rpm 5.2.1
|
00001 /*@-modfilesys@*/ 00006 #include "system.h" 00007 00008 #ifdef WITH_NEON 00009 00010 #include "ne_alloc.h" 00011 #include "ne_auth.h" 00012 #include "ne_basic.h" 00013 #include "ne_dates.h" 00014 #include "ne_locks.h" 00015 00016 #define NEONBLOWSCHUNKS 00017 #ifndef NEONBLOWSCHUNKS 00018 /* HACK: include ne_private.h to access sess->socket for now. */ 00019 #include "../neon/src/ne_private.h" 00020 #endif 00021 00022 #include "ne_props.h" 00023 #include "ne_request.h" 00024 #include "ne_socket.h" 00025 #include "ne_string.h" 00026 00027 #include "ne_utils.h" 00028 #if !defined(HEADER_ERR_H) 00029 /* cheats to avoid having to explicitly build against OpenSSL */ 00030 /*@-exportheader -redecl @*/ 00031 extern void ERR_remove_state(int foo); 00032 extern void ENGINE_cleanup(void); 00033 extern void CONF_modules_unload(int foo); 00034 extern void ERR_free_strings(void); 00035 extern void EVP_cleanup(void); 00036 extern void CRYPTO_cleanup_all_ex_data(void); 00037 extern void CRYPTO_mem_leaks(void * ptr); 00038 /*@=exportheader =redecl @*/ 00039 #endif 00040 00041 #include "ne_md5.h" /* for version detection only */ 00042 00043 /* poor-man's NEON version determination */ 00044 #if defined(NE_MD5_H) 00045 #define WITH_NEON_MIN_VERSION 0x002700 00046 #elif defined(NE_FEATURE_I18N) 00047 #define WITH_NEON_MIN_VERSION 0x002600 00048 #else 00049 #define WITH_NEON_MIN_VERSION 0x002500 00050 #endif 00051 00052 /* XXX API changes for NEON 0.26 */ 00053 #if WITH_NEON_MIN_VERSION >= 0x002600 00054 #define ne_propfind_set_private(_pfh, _create_item, NULL) \ 00055 ne_propfind_set_private(_pfh, _create_item, NULL, NULL) 00056 #endif 00057 00058 #endif /* WITH_NEON */ 00059 00060 #include <rpmio_internal.h> 00061 00062 #include <rpmhash.h> 00063 #include <rpmmacro.h> /* XXX rpmExpand */ 00064 #include <ugid.h> 00065 00066 #define _RPMAV_INTERNAL 00067 #define _RPMDAV_INTERNAL 00068 #include <rpmdav.h> 00069 #include <mire.h> 00070 00071 #include "debug.h" 00072 00073 /*@access DIR @*/ 00074 /*@access FD_t @*/ 00075 /*@access urlinfo @*/ 00076 /*@access miRE @*/ 00077 00078 /* HACK: reasonable value needed (wget uses 900 as default). */ 00079 #if 0 00080 #define TIMEOUT_SECS 60 00081 #else 00082 #define TIMEOUT_SECS 5 00083 #endif 00084 00085 /*@unchecked@*/ /*@observer@*/ 00086 static const char _rpmioHttpUserAgent[] = PACKAGE "/" PACKAGE_VERSION; 00087 00088 /*@unchecked@*/ 00089 static int rpmioHttpPersist = 1; 00090 /*@unchecked@*/ 00091 int rpmioHttpReadTimeoutSecs = TIMEOUT_SECS; 00092 /*@unchecked@*/ 00093 int rpmioHttpConnectTimeoutSecs = TIMEOUT_SECS; 00094 #ifdef NOTYET 00095 int rpmioHttpRetries = 20; 00096 int rpmioHttpRecurseMax = 5; 00097 int rpmioHttpMaxRedirect = 20; 00098 #endif 00099 00100 /*@unchecked@*/ /*@null@*/ 00101 const char * rpmioHttpAccept; 00102 /*@unchecked@*/ /*@null@*/ 00103 const char * rpmioHttpUserAgent; 00104 00105 /* =============================================================== */ 00106 void * avContextDestroy(avContext ctx) 00107 { 00108 if (ctx == NULL) 00109 return NULL; 00110 if (ctx->av != NULL) 00111 ctx->av = argvFree(ctx->av); 00112 ctx->modes = _free(ctx->modes); 00113 ctx->sizes = _free(ctx->sizes); 00114 ctx->mtimes = _free(ctx->mtimes); 00115 ctx->u = urlFree(ctx->u, "avContextDestroy"); 00116 ctx->uri = _free(ctx->uri); 00117 memset(ctx, 0, sizeof(*ctx)); /* trash & burn */ 00118 ctx = _free(ctx); 00119 return NULL; 00120 } 00121 00122 void * avContextCreate(const char *uri, struct stat *st) 00123 { 00124 avContext ctx; 00125 urlinfo u; 00126 00127 /*@-globs@*/ /* FIX: h_errno annoyance. */ 00128 if (urlSplit(uri, &u)) 00129 return NULL; 00130 /*@=globs@*/ 00131 00132 ctx = xcalloc(1, sizeof(*ctx)); 00133 ctx->uri = xstrdup(uri); 00134 ctx->u = urlLink(u, "avContextCreate"); 00135 /*@-temptrans@*/ /* XXX note the assignment */ 00136 if ((ctx->st = st) != NULL) 00137 memset(ctx->st, 0, sizeof(*ctx->st)); 00138 /*@=temptrans@*/ 00139 return ctx; 00140 } 00141 00142 int avContextAdd(avContext ctx, const char * path, 00143 mode_t mode, size_t size, time_t mtime) 00144 { 00145 int xx; 00146 00147 if (_av_debug < 0) 00148 fprintf(stderr, "*** avContextAdd(%p,\"%s\", %06o, 0x%x, 0x%x)\n", ctx, path, (unsigned)mode, (unsigned)size, (unsigned)mtime); 00149 00150 xx = argvAdd(&ctx->av, path); 00151 00152 while (ctx->ac >= ctx->nalloced) { 00153 if (ctx->nalloced <= 0) 00154 ctx->nalloced = 1; 00155 ctx->nalloced *= 2; 00156 ctx->modes = xrealloc(ctx->modes, 00157 (sizeof(*ctx->modes) * ctx->nalloced)); 00158 ctx->sizes = xrealloc(ctx->sizes, 00159 (sizeof(*ctx->sizes) * ctx->nalloced)); 00160 ctx->mtimes = xrealloc(ctx->mtimes, 00161 (sizeof(*ctx->mtimes) * ctx->nalloced)); 00162 } 00163 00164 ctx->modes[ctx->ac] = (rpmuint16_t)mode; 00165 ctx->sizes[ctx->ac] = size; 00166 ctx->mtimes[ctx->ac] = mtime; 00167 ctx->ac++; 00168 return 0; 00169 } 00170 00171 int avClosedir(/*@only@*/ DIR * dir) 00172 { 00173 AVDIR avdir = (AVDIR)dir; 00174 00175 if (_av_debug) 00176 fprintf(stderr, "*** avClosedir(%p)\n", avdir); 00177 00178 #if defined(WITH_PTHREADS) 00179 /*@-moduncon -noeffectuncon @*/ 00180 (void) pthread_mutex_destroy(&avdir->lock); 00181 /*@=moduncon =noeffectuncon @*/ 00182 #endif 00183 00184 avdir = _free(avdir); 00185 return 0; 00186 } 00187 00188 struct dirent * avReaddir(DIR * dir) 00189 { 00190 AVDIR avdir = (AVDIR)dir; 00191 struct dirent * dp; 00192 const char ** av; 00193 unsigned char * dt; 00194 int ac; 00195 int i; 00196 00197 if (avdir == NULL || !ISAVMAGIC(avdir) || avdir->data == NULL) { 00198 /* XXX TODO: EBADF errno. */ 00199 return NULL; 00200 } 00201 00202 dp = (struct dirent *) avdir->data; 00203 av = (const char **) (dp + 1); 00204 ac = (int)avdir->size; 00205 dt = (unsigned char *) (av + (ac + 1)); 00206 i = avdir->offset + 1; 00207 00208 if (i < 0 || i >= ac || av[i] == NULL) 00209 return NULL; 00210 00211 avdir->offset = i; 00212 00213 /* XXX glob(3) uses REAL_DIR_ENTRY(dp) test on d_ino */ 00214 /*@-type@*/ 00215 /* Hash the name (starting with parent hash) for a d_ino analogue. */ 00216 dp->d_ino = hashFunctionString(avdir->filepos, dp->d_name, 0); 00217 00218 #if !defined(__DragonFly__) && !defined(__CYGWIN__) 00219 dp->d_reclen = 0; /* W2DO? */ 00220 #endif 00221 00222 #if !(defined(hpux) || defined(__hpux) || defined(sun) || defined(RPM_OS_AIX) || defined(__CYGWIN__) || defined(__QNXNTO__)) 00223 #if !defined(__APPLE__) && !defined(__FreeBSD_kernel__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__DragonFly__) && !defined(__OpenBSD__) 00224 dp->d_off = 0; /* W2DO? */ 00225 #endif 00226 dp->d_type = dt[i]; 00227 #endif 00228 /*@=type@*/ 00229 00230 strncpy(dp->d_name, av[i], sizeof(dp->d_name)); 00231 if (_av_debug) 00232 fprintf(stderr, "*** avReaddir(%p) %p %s\n", (void *)avdir, dp, dp->d_name); 00233 00234 return dp; 00235 } 00236 00237 DIR * avOpendir(const char * path, const char ** av, rpmuint16_t * modes) 00238 { 00239 AVDIR avdir; 00240 struct dirent * dp; 00241 size_t nb; 00242 const char ** nav; 00243 unsigned char * dt; 00244 char * t; 00245 int ac, nac; 00246 00247 if (_av_debug) 00248 fprintf(stderr, "*** avOpendir(%s, %p, %p)\n", path, av, modes); 00249 00250 nb = 0; 00251 ac = 0; 00252 if (av != NULL) 00253 while (av[ac] != NULL) 00254 nb += strlen(av[ac++]) + 1; 00255 ac += 2; /* for "." and ".." */ 00256 nb += sizeof(".") + sizeof(".."); 00257 00258 nb += sizeof(*avdir) + sizeof(*dp) + ((ac + 1) * sizeof(*av)) + (ac + 1); 00259 avdir = xcalloc(1, nb); 00260 /*@-abstract@*/ 00261 dp = (struct dirent *) (avdir + 1); 00262 nav = (const char **) (dp + 1); 00263 dt = (unsigned char *) (nav + (ac + 1)); 00264 t = (char *) (dt + ac + 1); 00265 /*@=abstract@*/ 00266 00267 avdir->fd = avmagicdir; 00268 /*@-usereleased@*/ 00269 avdir->data = (char *) dp; 00270 /*@=usereleased@*/ 00271 avdir->allocation = nb; 00272 avdir->size = ac; 00273 avdir->offset = -1; 00274 /* Hash the directory path for a d_ino analogue. */ 00275 avdir->filepos = hashFunctionString(0, path, 0); 00276 00277 #if defined(WITH_PTHREADS) 00278 /*@-moduncon -noeffectuncon -nullpass @*/ 00279 (void) pthread_mutex_init(&avdir->lock, NULL); 00280 /*@=moduncon =noeffectuncon =nullpass @*/ 00281 #endif 00282 00283 nac = 0; 00284 /*@-dependenttrans -unrecog@*/ 00285 dt[nac] = (unsigned char)DT_DIR; nav[nac++] = t; t = stpcpy(t, "."); t++; 00286 dt[nac] = (unsigned char)DT_DIR; nav[nac++] = t; t = stpcpy(t, ".."); t++; 00287 /*@=dependenttrans =unrecog@*/ 00288 00289 /* Append av strings to DIR elements. */ 00290 ac = 0; 00291 if (av != NULL) 00292 while (av[ac] != NULL) { 00293 if (modes != NULL) 00294 switch (modes[ac] & S_IFMT) { 00295 case S_IFIFO: dt[nac]=(unsigned char)DT_FIFO;/*@switchbreak@*/break; 00296 case S_IFCHR: dt[nac]=(unsigned char)DT_CHR; /*@switchbreak@*/break; 00297 case S_IFDIR: dt[nac]=(unsigned char)DT_DIR; /*@switchbreak@*/break; 00298 case S_IFBLK: dt[nac]=(unsigned char)DT_BLK; /*@switchbreak@*/break; 00299 case S_IFREG: dt[nac]=(unsigned char)DT_REG; /*@switchbreak@*/break; 00300 case S_IFLNK: dt[nac]=(unsigned char)DT_LNK; /*@switchbreak@*/break; 00301 /*@-unrecog@*/ 00302 case S_IFSOCK:dt[nac]=(unsigned char)DT_SOCK;/*@switchbreak@*/break; 00303 /*@=unrecog@*/ 00304 default: dt[nac]=(unsigned char)DT_UNKNOWN;/*@switchbreak@*/break; 00305 } 00306 else 00307 dt[nac] = (unsigned char)DT_UNKNOWN; 00308 /*@-dependenttrans@*/ 00309 nav[nac++] = t; 00310 /*@=dependenttrans@*/ 00311 t = stpcpy(t, av[ac++]); 00312 t++; /* trailing \0 */ 00313 } 00314 nav[nac] = NULL; 00315 00316 /*@-kepttrans@*/ 00317 return (DIR *) avdir; 00318 /*@=kepttrans@*/ 00319 } 00320 00321 #ifdef WITH_NEON 00322 /* =============================================================== */ 00323 /*@-mustmod@*/ 00324 int davDisconnect(/*@unused@*/ void * _u) 00325 { 00326 #ifdef NOTYET /* XXX not quite right yet. */ 00327 urlinfo u = (urlinfo)_u; 00328 int rc; 00329 00330 #if WITH_NEON_MIN_VERSION >= 0x002700 00331 rc = (u->info.status == ne_status_sending || u->info.status == ne_status_recving); 00332 #else 00333 rc = 0; /* XXX W2DO? */ 00334 #endif 00335 if (u != NULL && rc != 0) { 00336 if (u->ctrl->req != NULL) { 00337 if (u->ctrl && u->ctrl->req) { 00338 ne_request_destroy(u->ctrl->req); 00339 u->ctrl->req = NULL; 00340 } 00341 if (u->data && u->data->req) { 00342 ne_request_destroy(u->data->req); 00343 u->data->req = NULL; 00344 } 00345 } 00346 } 00347 if (_dav_debug < 0) 00348 fprintf(stderr, "*** davDisconnect(%p) active %d\n", u, rc); 00349 /* XXX return active state? */ 00350 #endif /* NOTYET */ 00351 return 0; 00352 } 00353 /*@=mustmod@*/ 00354 00355 int davFree(urlinfo u) 00356 { 00357 if (u != NULL) { 00358 if (u->sess != NULL) { 00359 ne_session_destroy(u->sess); 00360 u->sess = NULL; 00361 } 00362 switch (u->urltype) { 00363 default: 00364 /*@notreached@*/ break; 00365 case URL_IS_HTTPS: 00366 case URL_IS_HTTP: 00367 case URL_IS_HKP: 00368 u->capabilities = _free(u->capabilities); 00369 if (u->lockstore != NULL) 00370 ne_lockstore_destroy(u->lockstore); 00371 u->lockstore = NULL; 00372 u->info.status = 0; 00373 ne_sock_exit(); 00374 break; 00375 } 00376 } 00377 if (_dav_debug < 0) 00378 fprintf(stderr, "*** davFree(%p)\n", u); 00379 return 0; 00380 } 00381 00382 void davDestroy(void) 00383 { 00384 #ifdef NE_FEATURE_SSL 00385 if (ne_has_support(NE_FEATURE_SSL)) { 00386 /* XXX http://www.nabble.com/Memory-Leaks-in-SSL_Library_init()-t3431875.html */ 00387 ENGINE_cleanup(); 00388 CRYPTO_cleanup_all_ex_data(); 00389 ERR_free_strings(); 00390 ERR_remove_state(0); 00391 EVP_cleanup(); 00392 CRYPTO_mem_leaks(NULL); 00393 CONF_modules_unload(1); 00394 } 00395 #endif 00396 if (_dav_debug < 0) 00397 fprintf(stderr, "*** davDestroy()\n"); 00398 } 00399 00400 static void davProgress(void * userdata, off_t progress, off_t total) 00401 /*@*/ 00402 { 00403 urlinfo u = userdata; 00404 ne_session * sess; 00405 00406 assert(u != NULL); 00407 sess = u->sess; 00408 assert(sess != NULL); 00409 /*@-sefuncon@*/ 00410 assert(u == ne_get_session_private(sess, "urlinfo")); 00411 /*@=sefuncon@*/ 00412 00413 u->info.progress = progress; 00414 u->info.total = total; 00415 00416 if (_dav_debug < 0) 00417 fprintf(stderr, "*** davProgress(%p,0x%x:0x%x) sess %p u %p\n", userdata, (unsigned int)progress, (unsigned int)total, sess, u); 00418 } 00419 00420 #if WITH_NEON_MIN_VERSION >= 0x002700 00421 static void davNotify(void * userdata, 00422 ne_session_status status, const ne_session_status_info *info) 00423 #else 00424 static void davNotify(void * userdata, 00425 ne_conn_status status, const char * info) 00426 #endif 00427 /*@*/ 00428 { 00429 char buf[64]; 00430 urlinfo u = userdata; 00431 ne_session * sess; 00432 00433 assert(u != NULL); 00434 sess = u->sess; 00435 assert(sess != NULL); 00436 /*@-sefuncon@*/ 00437 assert(u == ne_get_session_private(sess, "urlinfo")); 00438 /*@=sefuncon@*/ 00439 00440 u->info.hostname = NULL; 00441 u->info.address = NULL; 00442 u->info.progress = 0; 00443 u->info.total = 0; 00444 00445 #if WITH_NEON_MIN_VERSION >= 0x002700 00446 #ifdef REFERENCE 00447 typedef enum { 00448 ne_status_lookup = 0, /* looking up hostname */ 00449 ne_status_connecting, /* connecting to host */ 00450 ne_status_connected, /* connected to host */ 00451 ne_status_sending, /* sending a request body */ 00452 ne_status_recving, /* receiving a response body */ 00453 ne_status_disconnected /* disconnected from host */ 00454 } ne_session_status; 00455 #endif 00456 switch (status) { 00457 default: 00458 break; 00459 case ne_status_lookup: /* looking up hostname */ 00460 u->info.hostname = info->ci.hostname; 00461 break; 00462 case ne_status_connecting: /* connecting to host */ 00463 u->info.hostname = info->ci.hostname; 00464 (void) ne_iaddr_print(info->ci.address, buf, sizeof(buf)); 00465 buf[sizeof(buf)-1] = '\0'; 00466 u->info.address = buf; 00467 break; 00468 case ne_status_connected: /* connected to host */ 00469 u->info.hostname = info->ci.hostname; 00470 break; 00471 case ne_status_sending: /* sending a request body */ 00472 u->info.progress = info->sr.progress; 00473 u->info.total = info->sr.total; 00474 break; 00475 case ne_status_recving: /* receiving a response body */ 00476 u->info.progress = info->sr.progress; 00477 u->info.total = info->sr.total; 00478 break; 00479 case ne_status_disconnected: 00480 u->info.hostname = info->ci.hostname; 00481 break; 00482 } 00483 00484 if (u->notify != NULL) 00485 (void) (*u->notify) (u, status); 00486 00487 #else 00488 #ifdef REFERENCE 00489 typedef enum { 00490 ne_conn_namelookup, /* lookup up hostname (info = hostname) */ 00491 ne_conn_connecting, /* connecting to host (info = hostname) */ 00492 ne_conn_connected, /* connected to host (info = hostname) */ 00493 ne_conn_secure /* connection now secure (info = crypto level) */ 00494 } ne_conn_status; 00495 #endif 00496 00497 if (_dav_debug < 0) { 00498 /*@observer@*/ 00499 static const char * connstates[] = { 00500 "namelookup", 00501 "connecting", 00502 "connected", 00503 "secure", 00504 "unknown" 00505 }; 00506 00507 fprintf(stderr, "*** davNotify(%p,%d,%p) sess %p u %p %s\n", userdata, status, info, sess, u, connstates[ (status < 4 ? status : 4)]); 00508 } 00509 #endif 00510 00511 u->info.status = status; 00512 u->info.hostname = NULL; 00513 u->info.address = NULL; 00514 u->info.progress = 0; 00515 u->info.total = 0; 00516 } 00517 00518 static void davCreateRequest(ne_request * req, void * userdata, 00519 const char * method, const char * uri) 00520 /*@*/ 00521 { 00522 urlinfo u = userdata; 00523 ne_session * sess; 00524 void * private = NULL; 00525 const char * id = "urlinfo"; 00526 00527 assert(u != NULL); 00528 assert(u->sess != NULL); 00529 assert(req != NULL); 00530 sess = ne_get_session(req); 00531 assert(sess == u->sess); 00532 /*@-sefuncon@*/ 00533 assert(u == ne_get_session_private(sess, "urlinfo")); 00534 /*@=sefuncon@*/ 00535 00536 assert(sess != NULL); 00537 private = ne_get_session_private(sess, id); 00538 assert(u == private); 00539 00540 if (_dav_debug < 0) 00541 fprintf(stderr, "*** davCreateRequest(%p,%p,%s,%s) %s:%p\n", req, userdata, method, uri, id, private); 00542 } 00543 00544 static void davPreSend(ne_request * req, void * userdata, ne_buffer * buf) 00545 { 00546 urlinfo u = userdata; 00547 ne_session * sess; 00548 const char * id = "fd"; 00549 FD_t fd = NULL; 00550 00551 /*@-modunconnomods@*/ 00552 assert(u != NULL); 00553 assert(u->sess != NULL); 00554 assert(req != NULL); 00555 sess = ne_get_session(req); 00556 assert(sess == u->sess); 00557 /*@-sefuncon@*/ 00558 assert(u == ne_get_session_private(sess, "urlinfo")); 00559 /*@=sefuncon@*/ 00560 00561 fd = ne_get_request_private(req, id); 00562 /*@=modunconnomods@*/ 00563 00564 if (_dav_debug < 0) 00565 fprintf(stderr, "*** davPreSend(%p,%p,%p) sess %p %s %p\n", req, userdata, buf, sess, id, fd); 00566 if (_dav_debug) 00567 fprintf(stderr, "-> %s\n", buf->data); 00568 00569 } 00570 00571 static int davPostSend(ne_request * req, void * userdata, const ne_status * status) 00572 /*@*/ 00573 { 00574 urlinfo u = userdata; 00575 ne_session * sess; 00576 const char * id = "fd"; 00577 FD_t fd = NULL; 00578 00579 assert(u != NULL); 00580 assert(u->sess != NULL); 00581 assert(req != NULL); 00582 sess = ne_get_session(req); 00583 assert(sess == u->sess); 00584 /*@-sefuncon@*/ 00585 assert(u == ne_get_session_private(sess, "urlinfo")); 00586 /*@=sefuncon@*/ 00587 00588 fd = ne_get_request_private(req, id); 00589 00590 /*@-evalorder@*/ 00591 if (_dav_debug < 0) 00592 fprintf(stderr, "*** davPostSend(%p,%p,%p) sess %p %s %p %s\n", req, userdata, status, sess, id, fd, ne_get_error(sess)); 00593 /*@=evalorder@*/ 00594 return NE_OK; 00595 } 00596 00597 static void davDestroyRequest(ne_request * req, void * userdata) 00598 /*@*/ 00599 { 00600 urlinfo u = userdata; 00601 ne_session * sess; 00602 const char * id = "fd"; 00603 FD_t fd = NULL; 00604 00605 assert(u != NULL); 00606 assert(u->sess != NULL); 00607 assert(req != NULL); 00608 sess = ne_get_session(req); 00609 assert(sess == u->sess); 00610 /*@-sefuncon@*/ 00611 assert(u == ne_get_session_private(sess, "urlinfo")); 00612 /*@=sefuncon@*/ 00613 00614 fd = ne_get_request_private(req, id); 00615 00616 if (_dav_debug < 0) 00617 fprintf(stderr, "*** davDestroyRequest(%p,%p) sess %p %s %p\n", req, userdata, sess, id, fd); 00618 } 00619 00620 static void davDestroySession(void * userdata) 00621 /*@*/ 00622 { 00623 urlinfo u = userdata; 00624 ne_session * sess; 00625 void * private = NULL; 00626 const char * id = "urlinfo"; 00627 00628 assert(u != NULL); 00629 assert(u->sess != NULL); 00630 sess = u->sess; 00631 /*@-sefuncon@*/ 00632 assert(u == ne_get_session_private(sess, "urlinfo")); 00633 /*@=sefuncon@*/ 00634 00635 assert(sess != NULL); 00636 private = ne_get_session_private(sess, id); 00637 assert(u == private); 00638 00639 if (_dav_debug < 0) 00640 fprintf(stderr, "*** davDestroySession(%p) sess %p %s %p\n", userdata, sess, id, private); 00641 } 00642 00643 static int 00644 davVerifyCert(void *userdata, int failures, const ne_ssl_certificate *cert) 00645 /*@*/ 00646 { 00647 const char *hostname = userdata; 00648 00649 if (_dav_debug < 0) 00650 fprintf(stderr, "*** davVerifyCert(%p,%d,%p) %s\n", userdata, failures, cert, hostname); 00651 00652 return 0; /* HACK: trust all server certificates. */ 00653 } 00654 00655 static int davConnect(urlinfo u) 00656 /*@globals errno, internalState @*/ 00657 /*@modifies u, errno, internalState @*/ 00658 { 00659 const char * path = NULL; 00660 int rc; 00661 00662 /* HACK: hkp:// has no steenkin' options */ 00663 if (!(u->urltype == URL_IS_HTTP || u->urltype == URL_IS_HTTPS)) 00664 return 0; 00665 00666 /* HACK: where should server capabilities be read? */ 00667 (void) urlPath(u->url, &path); 00668 if (path == NULL || *path == '\0') 00669 path = "/"; 00670 00671 #ifdef NOTYET /* XXX too many new directories while recursing. */ 00672 /* Repeat OPTIONS for new directories. */ 00673 if (path != NULL && path[strlen(path)-1] == '/') 00674 u->allow &= ~RPMURL_SERVER_OPTIONSDONE; 00675 #endif 00676 /* Have options been run? */ 00677 if (u->allow & RPMURL_SERVER_OPTIONSDONE) 00678 return 0; 00679 00680 u->allow &= ~(RPMURL_SERVER_HASDAVCLASS1 | 00681 RPMURL_SERVER_HASDAVCLASS2 | 00682 RPMURL_SERVER_HASDAVEXEC); 00683 00684 /* HACK: perhaps capture Allow: tag, look for PUT permitted. */ 00685 /* XXX [hdr] Allow: GET,HEAD,POST,OPTIONS,TRACE */ 00686 rc = ne_options(u->sess, path, u->capabilities); 00687 switch (rc) { 00688 case NE_OK: 00689 u->allow |= RPMURL_SERVER_OPTIONSDONE; 00690 { ne_server_capabilities *cap = u->capabilities; 00691 if (cap->dav_class1) 00692 u->allow |= RPMURL_SERVER_HASDAVCLASS1; 00693 else 00694 u->allow &= ~RPMURL_SERVER_HASDAVCLASS1; 00695 if (cap->dav_class2) 00696 u->allow |= RPMURL_SERVER_HASDAVCLASS2; 00697 else 00698 u->allow &= ~RPMURL_SERVER_HASDAVCLASS2; 00699 if (cap->dav_executable) 00700 u->allow |= RPMURL_SERVER_HASDAVEXEC; 00701 else 00702 u->allow &= ~RPMURL_SERVER_HASDAVEXEC; 00703 } break; 00704 case NE_ERROR: 00705 /* HACK: "501 Not Implemented" if OPTIONS not permitted. */ 00706 if (!strncmp("501 ", ne_get_error(u->sess), sizeof("501 ")-1)) { 00707 u->allow |= RPMURL_SERVER_OPTIONSDONE; 00708 rc = NE_OK; 00709 break; 00710 } 00711 /* HACK: "301 Moved Permanently" on empty subdir. */ 00712 if (!strncmp("301 ", ne_get_error(u->sess), sizeof("301 ")-1)) 00713 break; 00714 #ifdef HACK /* XXX need davHEAD changes here? */ 00715 /* HACK: "302 Found" if URI is missing pesky trailing '/'. */ 00716 if (!strncmp("302 ", ne_get_error(u->sess), sizeof("302 ")-1)) { 00717 char * t; 00718 if ((t = strchr(u->url, '\0')) != NULL) 00719 *t = '/'; 00720 break; 00721 } 00722 #endif 00723 errno = EIO; /* HACK: more precise errno. */ 00724 goto bottom; 00725 case NE_LOOKUP: 00726 errno = ENOENT; /* HACK: errno same as non-existent path. */ 00727 goto bottom; 00728 case NE_CONNECT: /* HACK: errno set already? */ 00729 default: 00730 bottom: 00731 /*@-evalorderuncon@*/ 00732 if (_dav_debug) 00733 fprintf(stderr, "*** Connect to %s:%d failed(%d):\n\t%s\n", 00734 u->host, u->port, rc, ne_get_error(u->sess)); 00735 /*@=evalorderuncon@*/ 00736 break; 00737 } 00738 00739 /* HACK: sensitive to error returns? */ 00740 u->httpVersion = (ne_version_pre_http11(u->sess) ? 0 : 1); 00741 00742 return rc; 00743 } 00744 00745 static int davInit(const char * url, urlinfo * uret) 00746 /*@globals internalState @*/ 00747 /*@modifies *uret, internalState @*/ 00748 { 00749 urlinfo u = NULL; 00750 int rc = 0; 00751 00752 /*@-globs@*/ /* FIX: h_errno annoyance. */ 00753 if (urlSplit(url, &u)) 00754 return -1; /* XXX error returns needed. */ 00755 /*@=globs@*/ 00756 00757 if (u->url != NULL && u->sess == NULL) 00758 switch (u->urltype) { 00759 default: 00760 assert(u->urltype != u->urltype); 00761 /*@notreached@*/ break; 00762 case URL_IS_HTTPS: 00763 case URL_IS_HTTP: 00764 case URL_IS_HKP: 00765 { ne_server_capabilities * capabilities; 00766 00767 /* HACK: oneshots should be done Somewhere Else Instead. */ 00768 /*@-noeffect@*/ 00769 rc = ((_dav_debug < 0) ? NE_DBG_HTTP : 0); 00770 ne_debug_init(stderr, rc); /* XXX oneshot? */ 00771 /*@=noeffect@*/ 00772 rc = ne_sock_init(); /* XXX oneshot? */ 00773 00774 u->lockstore = ne_lockstore_create(); /* XXX oneshot? */ 00775 00776 u->capabilities = capabilities = xcalloc(1, sizeof(*capabilities)); 00777 u->sess = ne_session_create(u->scheme, u->host, u->port); 00778 00779 ne_lockstore_register(u->lockstore, u->sess); 00780 00781 if (u->proxyh != NULL) 00782 ne_session_proxy(u->sess, u->proxyh, u->proxyp); 00783 00784 #if 0 00785 { const ne_inet_addr ** addrs; 00786 unsigned int n; 00787 ne_set_addrlist(u->sess, addrs, n); 00788 } 00789 #endif 00790 00791 ne_set_progress(u->sess, davProgress, u); 00792 #if WITH_NEON_MIN_VERSION >= 0x002700 00793 ne_set_notifier(u->sess, davNotify, u); 00794 #else 00795 ne_set_status(u->sess, davNotify, u); 00796 #endif 00797 00798 #if WITH_NEON_MIN_VERSION >= 0x002600 00799 ne_set_session_flag(u->sess, NE_SESSFLAG_PERSIST, rpmioHttpPersist); 00800 #else 00801 ne_set_persist(u->sess, rpmioHttpPersist); 00802 #endif 00803 ne_set_read_timeout(u->sess, rpmioHttpReadTimeoutSecs); 00804 ne_set_useragent(u->sess, 00805 (rpmioHttpUserAgent ? rpmioHttpUserAgent : _rpmioHttpUserAgent)); 00806 00807 /* XXX check that neon is ssl enabled. */ 00808 if (!strcasecmp(u->scheme, "https")) 00809 ne_ssl_set_verify(u->sess, davVerifyCert, (char *)u->host); 00810 00811 ne_set_session_private(u->sess, "urlinfo", u); 00812 00813 ne_hook_destroy_session(u->sess, davDestroySession, u); 00814 00815 ne_hook_create_request(u->sess, davCreateRequest, u); 00816 ne_hook_pre_send(u->sess, davPreSend, u); 00817 ne_hook_post_send(u->sess, davPostSend, u); 00818 ne_hook_destroy_request(u->sess, davDestroyRequest, u); 00819 00820 /* HACK: where should server capabilities be read? */ 00821 rc = davConnect(u); 00822 if (rc) 00823 goto exit; 00824 } break; 00825 } 00826 00827 exit: 00828 if (uret != NULL) 00829 *uret = urlLink(u, "davInit"); 00830 u = urlFree(u, "urlSplit (davInit)"); 00831 00832 return rc; 00833 } 00834 00835 /* =============================================================== */ 00836 enum fetch_rtype_e { 00837 resr_normal = 0, 00838 resr_collection, 00839 resr_reference, 00840 resr_error 00841 }; 00842 00843 struct fetch_resource_s { 00844 /*@dependent@*/ 00845 struct fetch_resource_s *next; 00846 char *uri; 00847 /*@unused@*/ 00848 char *displayname; 00849 enum fetch_rtype_e type; 00850 size_t size; 00851 time_t modtime; 00852 int is_executable; 00853 int is_vcr; /* Is version resource. 0: no vcr, 1 checkin 2 checkout */ 00854 char *error_reason; /* error string returned for this resource */ 00855 int error_status; /* error status returned for this resource */ 00856 }; 00857 00858 /*@null@*/ 00859 static void *fetch_destroy_item(/*@only@*/ struct fetch_resource_s *res) 00860 /*@modifies res @*/ 00861 { 00862 ne_free(res->uri); 00863 ne_free(res->error_reason); 00864 res = _free(res); 00865 return NULL; 00866 } 00867 00868 #ifdef NOTUSED 00869 /*@null@*/ 00870 static void *fetch_destroy_list(/*@only@*/ struct fetch_resource_s *res) 00871 /*@modifies res @*/ 00872 { 00873 struct fetch_resource_s *next; 00874 for (; res != NULL; res = next) { 00875 next = res->next; 00876 res = fetch_destroy_item(res); 00877 } 00878 return NULL; 00879 } 00880 #endif 00881 00882 #if WITH_NEON_MIN_VERSION >= 0x002600 00883 static void *fetch_create_item(/*@unused@*/ void *userdata, /*@unused@*/ const ne_uri *uri) 00884 #else 00885 static void *fetch_create_item(/*@unused@*/ void *userdata, /*@unused@*/ const char *uri) 00886 #endif 00887 /*@*/ 00888 { 00889 struct fetch_resource_s * res = ne_calloc(sizeof(*res)); 00890 return res; 00891 } 00892 00893 /* =============================================================== */ 00894 00895 /*@-nullassign -readonlytrans@*/ 00896 /*@unchecked@*/ /*@observer@*/ 00897 static const ne_propname fetch_props[] = { 00898 { "DAV:", "getcontentlength" }, 00899 { "DAV:", "getlastmodified" }, 00900 { "http://apache.org/dav/props/", "executable" }, 00901 { "DAV:", "resourcetype" }, 00902 { "DAV:", "checked-in" }, 00903 { "DAV:", "checked-out" }, 00904 { NULL, NULL } 00905 }; 00906 /*@=nullassign =readonlytrans@*/ 00907 00908 #define ELM_resourcetype (NE_PROPS_STATE_TOP + 1) 00909 #define ELM_collection (NE_PROPS_STATE_TOP + 2) 00910 00911 /*@-readonlytrans@*/ 00912 /*@unchecked@*/ /*@observer@*/ 00913 static const struct ne_xml_idmap fetch_idmap[] = { 00914 { "DAV:", "resourcetype", ELM_resourcetype }, 00915 { "DAV:", "collection", ELM_collection } 00916 }; 00917 /*@=readonlytrans@*/ 00918 00919 static int fetch_startelm(void *userdata, int parent, 00920 const char *nspace, const char *name, 00921 /*@unused@*/ const char **atts) 00922 /*@*/ 00923 { 00924 ne_propfind_handler *pfh = userdata; 00925 struct fetch_resource_s *r = ne_propfind_current_private(pfh); 00926 /*@-sizeoftype@*/ 00927 int state = ne_xml_mapid(fetch_idmap, NE_XML_MAPLEN(fetch_idmap), 00928 nspace, name); 00929 /*@=sizeoftype@*/ 00930 00931 if (r == NULL || 00932 !((parent == NE_207_STATE_PROP && state == ELM_resourcetype) || 00933 (parent == ELM_resourcetype && state == ELM_collection))) 00934 return NE_XML_DECLINE; 00935 00936 if (state == ELM_collection) { 00937 r->type = resr_collection; 00938 } 00939 00940 return state; 00941 } 00942 00943 static int fetch_compare(const struct fetch_resource_s *r1, 00944 const struct fetch_resource_s *r2) 00945 /*@*/ 00946 { 00947 /* Sort errors first, then collections, then alphabetically */ 00948 if (r1->type == resr_error) { 00949 return -1; 00950 } else if (r2->type == resr_error) { 00951 return 1; 00952 } else if (r1->type == resr_collection) { 00953 if (r2->type != resr_collection) { 00954 return -1; 00955 } else { 00956 return strcmp(r1->uri, r2->uri); 00957 } 00958 } else { 00959 if (r2->type != resr_collection) { 00960 return strcmp(r1->uri, r2->uri); 00961 } else { 00962 return 1; 00963 } 00964 } 00965 } 00966 00967 #if WITH_NEON_MIN_VERSION >= 0x002600 00968 static void fetch_results(void *userdata, const ne_uri *uarg, 00969 const ne_prop_result_set *set) 00970 #else 00971 static void fetch_results(void *userdata, void *uarg, 00972 const ne_prop_result_set *set) 00973 #endif 00974 /*@*/ 00975 { 00976 avContext ctx = userdata; 00977 struct fetch_resource_s *current, *previous, *newres; 00978 const char *clength, *modtime, *isexec; 00979 const char *checkin, *checkout; 00980 const ne_status *status = NULL; 00981 const char * path = NULL; 00982 00983 #if WITH_NEON_MIN_VERSION >= 0x002600 00984 const ne_uri * uri = uarg; 00985 (void) urlPath(uri->path, &path); 00986 #else 00987 const char * uri = uarg; 00988 (void) urlPath(uri, &path); 00989 #endif 00990 if (path == NULL) 00991 return; 00992 00993 newres = ne_propset_private(set); 00994 00995 if (_dav_debug < 0) 00996 fprintf(stderr, "==> %s in uri %s\n", path, ctx->uri); 00997 00998 if (ne_path_compare(ctx->uri, path) == 0) { 00999 /* This is the target URI */ 01000 if (_dav_debug < 0) 01001 fprintf(stderr, "==> %s skipping target resource.\n", path); 01002 /* Free the private structure. */ 01003 /*@-dependenttrans -exposetrans@*/ 01004 free(newres); 01005 /*@=dependenttrans =exposetrans@*/ 01006 return; 01007 } 01008 01009 newres->uri = ne_strdup(path); 01010 01011 clength = ne_propset_value(set, &fetch_props[0]); 01012 modtime = ne_propset_value(set, &fetch_props[1]); 01013 isexec = ne_propset_value(set, &fetch_props[2]); 01014 checkin = ne_propset_value(set, &fetch_props[4]); 01015 checkout = ne_propset_value(set, &fetch_props[5]); 01016 01017 if (clength == NULL) 01018 status = ne_propset_status(set, &fetch_props[0]); 01019 if (modtime == NULL) 01020 status = ne_propset_status(set, &fetch_props[1]); 01021 01022 if (newres->type == resr_normal && status != NULL) { 01023 /* It's an error! */ 01024 newres->error_status = status->code; 01025 01026 /* Special hack for Apache 1.3/mod_dav */ 01027 if (strcmp(status->reason_phrase, "status text goes here") == 0) { 01028 const char *desc; 01029 if (status->code == 401) { 01030 desc = _("Authorization Required"); 01031 } else if (status->klass == 3) { 01032 desc = _("Redirect"); 01033 } else if (status->klass == 5) { 01034 desc = _("Server Error"); 01035 } else { 01036 desc = _("Unknown Error"); 01037 } 01038 newres->error_reason = ne_strdup(desc); 01039 } else { 01040 newres->error_reason = ne_strdup(status->reason_phrase); 01041 } 01042 newres->type = resr_error; 01043 } 01044 01045 if (isexec && strcasecmp(isexec, "T") == 0) { 01046 newres->is_executable = 1; 01047 } else { 01048 newres->is_executable = 0; 01049 } 01050 01051 if (modtime) 01052 newres->modtime = ne_httpdate_parse(modtime); 01053 01054 if (clength) 01055 newres->size = atoi(clength); 01056 01057 /* is vcr */ 01058 if (checkin) { 01059 newres->is_vcr = 1; 01060 } else if (checkout) { 01061 newres->is_vcr = 2; 01062 } else { 01063 newres->is_vcr = 0; 01064 } 01065 01066 current = *(struct fetch_resource_s **)ctx->resrock; 01067 for (current = *ctx->resrock, previous = NULL; current != NULL; 01068 previous = current, current = current->next) 01069 { 01070 if (fetch_compare(current, newres) >= 0) { 01071 break; 01072 } 01073 } 01074 if (previous) { 01075 previous->next = newres; 01076 } else { 01077 /*@-dependenttrans @*/ 01078 *(struct fetch_resource_s **)ctx->resrock = newres; 01079 /*@=dependenttrans @*/ 01080 } 01081 newres->next = current; 01082 } 01083 01084 static int davFetch(const urlinfo u, avContext ctx) 01085 /*@globals internalState @*/ 01086 /*@modifies ctx, internalState @*/ 01087 { 01088 const char * path = NULL; 01089 int depth = 1; /* XXX passed arg? */ 01090 struct fetch_resource_s * resitem = NULL; 01091 ne_propfind_handler *pfh; 01092 struct fetch_resource_s *current, *next; 01093 mode_t st_mode; 01094 int rc = 0; 01095 int xx; 01096 01097 (void) urlPath(u->url, &path); 01098 pfh = ne_propfind_create(u->sess, ctx->uri, depth); 01099 01100 /* HACK: need to set RPMURL_SERVER_HASRANGE in u->allow here. */ 01101 01102 ctx->resrock = (void **) &resitem; 01103 01104 ne_xml_push_handler(ne_propfind_get_parser(pfh), 01105 fetch_startelm, NULL, NULL, pfh); 01106 01107 ne_propfind_set_private(pfh, fetch_create_item, NULL); 01108 01109 rc = ne_propfind_named(pfh, fetch_props, fetch_results, ctx); 01110 01111 ne_propfind_destroy(pfh); 01112 01113 for (current = resitem; current != NULL; current = next) { 01114 const char *s, *se; 01115 char * val; 01116 01117 next = current->next; 01118 01119 /* Collections have trailing '/' that needs trim. */ 01120 /* The top level collection is returned as well. */ 01121 se = current->uri + strlen(current->uri); 01122 if (se[-1] == '/') { 01123 if (strlen(current->uri) <= strlen(path)) { 01124 current = fetch_destroy_item(current); 01125 continue; 01126 } 01127 se--; 01128 } 01129 s = se; 01130 while (s > current->uri && s[-1] != '/') 01131 s--; 01132 01133 val = ne_strndup(s, (se - s)); 01134 01135 /*@-nullpass@*/ 01136 val = ne_path_unescape(val); 01137 /*@=nullpass@*/ 01138 01139 switch (current->type) { 01140 case resr_normal: 01141 st_mode = S_IFREG; 01142 /*@switchbreak@*/ break; 01143 case resr_collection: 01144 st_mode = S_IFDIR; 01145 /*@switchbreak@*/ break; 01146 case resr_reference: 01147 case resr_error: 01148 default: 01149 st_mode = 0; 01150 /*@switchbreak@*/ break; 01151 } 01152 01153 xx = avContextAdd(ctx, val, st_mode, current->size, current->modtime); 01154 ne_free(val); 01155 01156 current = fetch_destroy_item(current); 01157 } 01158 ctx->resrock = NULL; /* HACK: avoid leaving stack reference. */ 01159 /* HACK realloc to truncate modes/sizes/mtimes */ 01160 01161 return rc; 01162 } 01163 01164 /* HACK davHEAD() should be rewritten to use davReq/davResp w callbacks. */ 01165 static int davHEAD(urlinfo u, struct stat *st) 01166 /*@modifies u, *st @*/ 01167 { 01168 ne_request *req; 01169 const ne_status *status = NULL; 01170 const char *htag; 01171 const char *value = NULL; 01172 int rc; 01173 int printing = 0; 01174 01175 /* XXX HACK: URI's with pesky trailing '/' are directories. */ 01176 { size_t nb = strlen(u->url); 01177 st->st_mode = (u->url[nb-1] == '/' ? S_IFDIR : S_IFREG); 01178 } 01179 st->st_blksize = 4 * 1024; /* HACK correct for linux ext */ 01180 st->st_size = -1; 01181 st->st_atime = -1; 01182 st->st_mtime = -1; 01183 st->st_ctime = -1; 01184 01185 req = ne_request_create(u->sess, "HEAD", u->url); 01186 if (rpmioHttpAccept != NULL) 01187 ne_add_request_header(req, "Accept", rpmioHttpAccept); 01188 01189 /* XXX if !defined(HAVE_NEON_NE_GET_RESPONSE_HEADER) handlers? */ 01190 01191 rc = ne_request_dispatch(req); 01192 status = ne_get_status(req); 01193 01194 /* XXX somewhere else instead? */ 01195 if (_dav_debug) { 01196 fprintf(stderr, "HTTP request sent, awaiting response... %d %s\n", status->code, status->reason_phrase); 01197 } 01198 01199 switch (rc) { 01200 default: 01201 goto exit; 01202 /*@notreached@*/ break; 01203 case NE_OK: 01204 if (status->klass != 2) /* XXX is this necessary? */ 01205 rc = NE_ERROR; 01206 break; 01207 } 01208 01209 #if defined(HAVE_NEON_NE_GET_RESPONSE_HEADER) 01210 htag = "ETag"; 01211 value = ne_get_response_header(req, htag); 01212 if (value) { 01213 /* inode-size-mtime */ 01214 u->etag = _free(u->etag); 01215 u->etag = xstrdup(value); 01216 } 01217 01218 /* XXX limit to 3xx returns? */ 01219 htag = "Location"; 01220 value = ne_get_response_header(req, htag); 01221 if (value) { 01222 u->location = _free(u->location); 01223 u->location = xstrdup(value); 01224 } 01225 01226 htag = "Content-Length"; 01227 value = ne_get_response_header(req, htag); 01228 if (value) { 01229 /* XXX should wget's "... (1.2K)..." be added? */ 01230 if (_dav_debug && ++printing) 01231 fprintf(stderr, "Length: %s", value); 01232 01233 /*@-unrecog@*/ /* XXX LCLINT needs stdlib.h update. */ 01234 st->st_size = strtoll(value, NULL, 10); 01235 /*@=unrecog@*/ 01236 st->st_blocks = (st->st_size + 511)/512; 01237 } 01238 01239 htag = "Content-Type"; 01240 value = ne_get_response_header(req, htag); 01241 if (value) { 01242 if (_dav_debug && printing) 01243 fprintf(stderr, " [%s]", value); 01244 if (!strcmp(value, "text/html") 01245 || !strcmp(value, "application/xhtml+xml")) 01246 st->st_blksize = 2 * 1024; 01247 } 01248 01249 htag = "Last-Modified"; 01250 value = ne_get_response_header(req, htag); 01251 if (value) { 01252 if (_dav_debug && printing) 01253 fprintf(stderr, " [%s]", value); 01254 st->st_mtime = ne_httpdate_parse(value); 01255 st->st_atime = st->st_ctime = st->st_mtime; /* HACK */ 01256 } 01257 01258 if (_dav_debug && printing) 01259 fprintf(stderr, "\n"); 01260 #endif 01261 01262 exit: 01263 ne_request_destroy(req); 01264 return rc; 01265 } 01266 01267 static int my_result(const char * msg, int ret, /*@null@*/ FILE * fp) 01268 /*@modifies *fp @*/ 01269 { 01270 /* HACK: don't print unless debugging. */ 01271 if (_dav_debug >= 0) 01272 return ret; 01273 if (fp == NULL) 01274 fp = stderr; 01275 if (msg != NULL) 01276 fprintf(fp, "*** %s: ", msg); 01277 01278 /* HACK FTPERR_NE_FOO == -NE_FOO error impedance match */ 01279 #ifdef HACK 01280 fprintf(fp, "%s: %s\n", ftpStrerror(-ret), ne_get_error(sess)); 01281 #else 01282 fprintf(fp, "%s\n", ftpStrerror(-ret)); 01283 #endif 01284 return ret; 01285 } 01286 01287 /* XXX TODO move to rpmhtml.c */ 01290 typedef struct rpmhtml_s * rpmhtml; 01291 01294 struct rpmhtml_s { 01295 /*@kept@*/ 01296 avContext ctx; 01297 ne_request *req; 01298 01299 /*@observer@*/ 01300 const char * pattern; 01301 /*@relnull@*/ 01302 miRE mires; 01303 int nmires; 01304 01305 char * buf; 01306 size_t nbuf; 01307 /*@null@*/ 01308 char * b; 01309 size_t nb; 01310 }; 01311 01314 static /*@null@*/ 01315 rpmhtml htmlFree(/*@only@*/ rpmhtml html) 01316 /*@modifies html @*/ 01317 { 01318 if (html != NULL) { 01319 if (html->req != NULL) { 01320 ne_request_destroy(html->req); 01321 html->req = NULL; 01322 } 01323 html->buf = _free(html->buf); 01324 html->nbuf = 0; 01325 html->ctx = NULL; 01326 } 01327 return NULL; 01328 } 01329 01332 static 01333 rpmhtml htmlNew(urlinfo u, /*@kept@*/ avContext ctx) 01334 /*@*/ 01335 { 01336 rpmhtml html = xcalloc(1, sizeof(*html)); 01337 html->ctx = ctx; 01338 html->nbuf = BUFSIZ; /* XXX larger buffer? */ 01339 html->buf = xmalloc(html->nbuf + 1 + 1); 01340 html->req = ne_request_create(u->sess, "GET", u->url); 01341 return html; 01342 } 01343 01346 static ssize_t htmlFill(rpmhtml html) 01347 /*@modifies html @*/ 01348 { 01349 char * b = html->buf; 01350 size_t nb = html->nbuf; 01351 ssize_t rc; 01352 01353 if (_dav_debug < 0) 01354 fprintf(stderr, "*** htmlFill(%p) %p[%u]\n", html, b, (unsigned)nb); 01355 if (html->b != NULL && html->nb > 0 && html->b > html->buf) { 01356 memmove(html->buf, html->b, html->nb); 01357 b += html->nb; 01358 nb -= html->nb; 01359 } 01360 01361 /* XXX FIXME: "server awol" segfaults here. gud enuf atm ... */ 01362 rc = ne_read_response_block(html->req, b, nb) ; 01363 if (rc > 0) 01364 html->nb += rc; 01365 html->b = html->buf; 01366 01367 return rc; 01368 } 01369 01375 static 01376 unsigned char nibble(char c) 01377 /*@*/ 01378 { 01379 if (c >= '0' && c <= '9') 01380 return (unsigned char) (c - '0'); 01381 if (c >= 'A' && c <= 'F') 01382 return (unsigned char)((int)(c - 'A') + 10); 01383 if (c >= 'a' && c <= 'f') 01384 return (unsigned char)((int)(c - 'a') + 10); 01385 return (unsigned char) '\0'; 01386 } 01387 01388 /*@observer@*/ 01389 static const char * hrefpat = "(?i)<a(?:\\s+[a-z][a-z0-9_]*(?:=(?:\"[^\"]*\"|\\S+))?)*?\\s+href=(?:\"([^\"]*)\"|(\\S+))"; 01390 01393 static int htmlParse(rpmhtml html) 01394 /*@globals hrefpat, internalState @*/ 01395 /*@modifies html, internalState @*/ 01396 { 01397 miRE mire; 01398 int noffsets = 3; 01399 int offsets[3]; 01400 ssize_t nr = (html->b != NULL ? (ssize_t)html->nb : htmlFill(html)); 01401 int rc = 0; 01402 int xx; 01403 01404 if (_dav_debug < 0) 01405 fprintf(stderr, "*** htmlParse(%p) %p[%u]\n", html, html->buf, (unsigned)html->nbuf); 01406 html->pattern = hrefpat; 01407 xx = mireAppend(RPMMIRE_PCRE, 0, html->pattern, NULL, &html->mires, &html->nmires); 01408 mire = html->mires; 01409 01410 xx = mireSetEOptions(mire, offsets, noffsets); 01411 01412 while (html->nb > 0) { 01413 char * gbn, * href; 01414 const char * hbn, * lpath; 01415 char * f, * fe; 01416 char * g, * ge; 01417 size_t ng; 01418 char * h, * he; 01419 size_t nh; 01420 char * t; 01421 mode_t st_mode; 01422 int ut; 01423 01424 assert(html->b != NULL); 01425 offsets[0] = offsets[1] = -1; 01426 xx = mireRegexec(mire, html->b, html->nb); 01427 if (xx == 0 && offsets[0] != -1 && offsets[1] != -1) { 01428 01429 /* [f:fe) contains |<a href="..."| match. */ 01430 f = html->b + offsets[0]; 01431 fe = html->b + offsets[1]; 01432 01433 he = fe; 01434 if (he[-1] == '"') he--; 01435 h = he; 01436 while (h > f && h[-1] != '"') 01437 h--; 01438 /* [h:he) contains the href. */ 01439 nh = (size_t)(he - h); 01440 href = t = xmalloc(nh + 1 + 1); 01441 while (h < he) { 01442 char c = *h++; 01443 switch (c) { 01444 default: 01445 /*@switchbreak@*/ break; 01446 case '%': 01447 if (isxdigit((int)h[0]) && isxdigit((int)h[1])) { 01448 c = (char) (nibble(h[0]) << 4) | nibble(h[1]); 01449 h += 2; 01450 } 01451 /*@switchbreak@*/ break; 01452 } 01453 *t++ = c; 01454 } 01455 *t = '\0'; 01456 01457 /* Determine type of href. */ 01458 switch ((ut = urlPath(href, &lpath))) { 01459 case URL_IS_UNKNOWN: 01460 default: 01461 /* XXX verify "same tree" as root URI. */ 01462 if (href[nh-1] == '/') { 01463 st_mode = S_IFDIR | 0755; 01464 href[nh-1] = '\0'; 01465 } else 01466 st_mode = S_IFREG | 0644; 01467 /*@switchbreak@*/ break; 01468 case URL_IS_FTP: 01469 case URL_IS_HTTPS: 01470 case URL_IS_HTTP: 01471 #ifdef NOTYET /* XXX avContext needs to save linktos first. */ 01472 st_mode = S_IFLNK | 0755; 01473 /*@switchbreak@*/ break; 01474 #endif 01475 case URL_IS_PATH: 01476 case URL_IS_DASH: 01477 case URL_IS_HKP: 01478 href[0] = '\0'; 01479 /*@switchbreak@*/ break; 01480 } 01481 if ((hbn = strrchr(href, '/')) != NULL) 01482 hbn++; 01483 else 01484 hbn = href; 01485 assert(hbn != NULL); 01486 01487 /* Parse the URI path. */ 01488 g = fe; 01489 while (*g != '>') 01490 g++; 01491 ge = ++g; 01492 while (*ge != '<') 01493 ge++; 01494 /* [g:ge) contains the URI basename. */ 01495 ng = (size_t)(ge - g); 01496 gbn = t = xmalloc(ng + 1 + 1); 01497 while (g < ge && *g != '/') /* XXX prohibit '/' in gbn. */ 01498 *t++ = *g++; 01499 *t = '\0'; 01500 01501 if (_dav_debug) 01502 if (*hbn != '\0' && *gbn != '\0' && strcasecmp(hbn, gbn)) 01503 fprintf(stderr, "\t[%s] != [%s]\n", hbn, gbn); 01504 01505 /* 01506 * Heuristics to identify HTML sub-directories: 01507 * Avoid empty strings. 01508 * Both "." and ".." will be added by avContext. 01509 * 01510 * Assume (case insensitive) basename(href) == basename(URI) is 01511 * a subdirectory. 01512 */ 01513 if (*hbn != '\0' && *gbn != '\0') 01514 if (strcmp(hbn, ".") && strcmp(hbn, "..")) 01515 if (!strcasecmp(hbn, gbn)) { 01516 size_t _st_size = (size_t)0; /* XXX HACK */ 01517 time_t _st_mtime = (time_t)0; /* XXX HACK */ 01518 xx = avContextAdd(html->ctx, gbn, st_mode, _st_size, _st_mtime); 01519 } 01520 01521 gbn = _free(gbn); 01522 href = _free(href); 01523 01524 offsets[1] += (ge - fe); 01525 html->b += offsets[1]; 01526 html->nb -= offsets[1]; 01527 } else { 01528 size_t nb = html->nb; 01529 if (nr > 0) nb -= 128; /* XXX overlap a bit if filling. */ 01530 html->b += nb; 01531 html->nb -= nb; 01532 } 01533 01534 if (nr > 0) 01535 nr = htmlFill(html); 01536 } 01537 01538 xx = mireSetEOptions(mire, NULL, 0); 01539 01540 html->mires = mireFreeAll(html->mires, html->nmires); 01541 html->nmires = 0; 01542 01543 if (_dav_debug < 0) 01544 fprintf(stderr, "*** htmlParse(%p) rc %d\n", html, rc); 01545 return rc; 01546 } 01547 01548 /* HACK htmlNLST() should be rewritten to use davReq/davResp w callbacks. */ 01549 /*@-mustmod@*/ 01550 static int htmlNLST(urlinfo u, avContext ctx) 01551 /*@globals hrefpat, internalState @*/ 01552 /*@modifies ctx, internalState @*/ 01553 { 01554 rpmhtml html = htmlNew(u, ctx); 01555 int rc = 0; 01556 01557 if (_dav_debug < 0) 01558 fprintf(stderr, "*** htmlNLST(%p, %p) html %p\n", u, ctx, html); 01559 01560 do { 01561 rc = ne_begin_request(html->req); 01562 rc = my_result("ne_begin_req(html->req)", rc, NULL); 01563 if (rc != NE_OK) goto exit; 01564 01565 (void) htmlParse(html); /* XXX error code needs handling. */ 01566 01567 rc = ne_end_request(html->req); 01568 rc = my_result("ne_end_req(html->req)", rc, NULL); 01569 } while (rc == NE_RETRY); 01570 01571 exit: 01572 html = htmlFree(html); 01573 return rc; 01574 } 01575 /*@=mustmod@*/ 01576 01577 static int davNLST(avContext ctx) 01578 /*@globals hrefpat, internalState @*/ 01579 /*@modifies ctx, internalState @*/ 01580 { 01581 urlinfo u = NULL; 01582 int rc; 01583 int xx; 01584 01585 retry: 01586 rc = davInit(ctx->uri, &u); 01587 if (rc || u == NULL) 01588 goto exit; 01589 01590 /* 01591 * Do PROPFIND through davFetch iff server supports. 01592 * Otherwise, do HEAD to get Content-length/ETag/Last-Modified, 01593 * followed by GET through htmlNLST() to find the contained href's. 01594 */ 01595 if (u->allow & RPMURL_SERVER_HASDAV) 01596 rc = davFetch(u, ctx); /* use PROPFIND to get contentLength */ 01597 else { 01598 /*@-nullpass@*/ /* XXX annotate ctx->st correctly */ 01599 rc = davHEAD(u, ctx->st); /* use HEAD to get contentLength */ 01600 /*@=nullpass@*/ 01601 /* Parse directory elements. */ 01602 if (rc == NE_OK && S_ISDIR(ctx->st->st_mode)) 01603 rc = htmlNLST(u, ctx); 01604 } 01605 01606 switch (rc) { 01607 case NE_OK: 01608 break; 01609 case NE_ERROR: 01610 /* HACK: "405 Method Not Allowed" for PROPFIND on non-DAV servers. */ 01611 /* XXX #206066 OPTIONS is ok, but PROPFIND from Stat() fails. */ 01612 /* rpm -qp --rpmiodebug --davdebug http://people.freedesktop.org/~sandmann/metacity-2.16.0-2.fc6/i386/metacity-2.16.0-2.fc6.i386.rpm */ 01613 01614 /* HACK: "301 Moved Permanently" on empty subdir. */ 01615 if (!strncmp("301 ", ne_get_error(u->sess), sizeof("301 ")-1)) 01616 break; 01617 01618 /* HACK: "302 Found" if URI is missing pesky trailing '/'. */ 01619 if (!strncmp("302 ", ne_get_error(u->sess), sizeof("302 ")-1)) { 01620 const char * path = NULL; 01621 int ut = urlPath(u->url, &path); 01622 size_t nb = strlen(path); 01623 ut = ut; /* XXX keep gcc happy */ 01624 if (u->location != NULL && !strncmp(path, u->location, nb) 01625 && u->location[nb] == '/' && u->location[nb+1] == '\0') 01626 { 01627 char * te = strchr(u->url, '\0'); 01628 /* Append the pesky trailing '/'. */ 01629 if (te != NULL && te[-1] != '/') { 01630 /* XXX u->uri malloc'd w room for +1b */ 01631 *te++ = '/'; 01632 *te = '\0'; 01633 u->location = _free(u->location); 01634 /* XXX retry here needed iff ContentLength:. */ 01635 xx = davFree(u); 01636 goto retry; 01637 /*@notreached@*/ break; 01638 } 01639 } 01640 } 01641 /*@fallthrough@*/ 01642 default: 01643 /*@-evalorderuncon@*/ 01644 if (_dav_debug) 01645 fprintf(stderr, "*** Fetch from %s:%d failed:\n\t%s\n", 01646 u->host, u->port, ne_get_error(u->sess)); 01647 /*@=evalorderuncon@*/ 01648 break; 01649 } 01650 01651 exit: 01652 xx = davFree(u); 01653 return rc; 01654 } 01655 01656 /* =============================================================== */ 01657 /*@-mustmod@*/ 01658 static void davAcceptRanges(void * userdata, /*@null@*/ const char * value) 01659 /*@modifies userdata @*/ 01660 { 01661 urlinfo u = userdata; 01662 01663 if (!(u != NULL && value != NULL)) return; 01664 if (_dav_debug < 0) 01665 fprintf(stderr, "*** u %p Accept-Ranges: %s\n", u, value); 01666 if (!strcmp(value, "bytes")) 01667 u->allow |= RPMURL_SERVER_HASRANGE; 01668 if (!strcmp(value, "none")) 01669 u->allow &= ~RPMURL_SERVER_HASRANGE; 01670 } 01671 /*@=mustmod@*/ 01672 01673 #if !defined(HAVE_NEON_NE_GET_RESPONSE_HEADER) 01674 static void davAllHeaders(void * userdata, const char * value) 01675 { 01676 FD_t ctrl = userdata; 01677 01678 if (!(ctrl != NULL && value != NULL)) return; 01679 if (_dav_debug) 01680 fprintf(stderr, "<- %s\n", value); 01681 } 01682 #endif 01683 01684 /*@-mustmod@*/ 01685 static void davContentLength(void * userdata, /*@null@*/ const char * value) 01686 /*@modifies userdata @*/ 01687 { 01688 FD_t ctrl = userdata; 01689 01690 if (!(ctrl != NULL && value != NULL)) return; 01691 if (_dav_debug < 0) 01692 fprintf(stderr, "*** fd %p Content-Length: %s\n", ctrl, value); 01693 /*@-unrecog@*/ 01694 ctrl->contentLength = strtoll(value, NULL, 10); 01695 /*@=unrecog@*/ 01696 } 01697 /*@=mustmod@*/ 01698 01699 /*@-mustmod@*/ 01700 static void davContentType(void * userdata, /*@null@*/ const char * value) 01701 /*@modifies userdata @*/ 01702 { 01703 FD_t ctrl = userdata; 01704 01705 if (!(ctrl != NULL && value != NULL)) return; 01706 if (_dav_debug < 0) 01707 fprintf(stderr, "*** fd %p Content-Type: %s\n", ctrl, value); 01708 ctrl->contentType = _free(ctrl->contentType); 01709 ctrl->contentType = xstrdup(value); 01710 } 01711 /*@=mustmod@*/ 01712 01713 /*@-mustmod@*/ 01714 static void davContentDisposition(void * userdata, /*@null@*/ const char * value) 01715 /*@modifies userdata @*/ 01716 { 01717 FD_t ctrl = userdata; 01718 01719 if (!(ctrl != NULL && value != NULL)) return; 01720 if (_dav_debug < 0) 01721 fprintf(stderr, "*** fd %p Content-Disposition: %s\n", ctrl, value); 01722 ctrl->contentDisposition = _free(ctrl->contentDisposition); 01723 ctrl->contentDisposition = xstrdup(value); 01724 } 01725 /*@=mustmod@*/ 01726 01727 /*@-mustmod@*/ 01728 static void davLastModified(void * userdata, /*@null@*/ const char * value) 01729 /*@modifies userdata @*/ 01730 { 01731 FD_t ctrl = userdata; 01732 01733 if (!(ctrl != NULL && value != NULL)) return; 01734 if (_dav_debug < 0) 01735 fprintf(stderr, "*** fd %p Last-Modified: %s\n", ctrl, value); 01736 /*@-unrecog@*/ 01737 ctrl->lastModified = ne_httpdate_parse(value); 01738 /*@=unrecog@*/ 01739 } 01740 /*@=mustmod@*/ 01741 01742 /*@-mustmod@*/ 01743 static void davConnection(void * userdata, /*@null@*/ const char * value) 01744 /*@modifies userdata @*/ 01745 { 01746 FD_t ctrl = userdata; 01747 01748 if (!(ctrl != NULL && value != NULL)) return; 01749 if (_dav_debug < 0) 01750 fprintf(stderr, "*** fd %p Connection: %s\n", ctrl, value); 01751 if (!strcasecmp(value, "close")) 01752 ctrl->persist = 0; 01753 else if (!strcasecmp(value, "Keep-Alive")) 01754 ctrl->persist = 1; 01755 } 01756 /*@=mustmod@*/ 01757 01758 /*@-mustmod@*/ /* HACK: stash error in *str. */ 01759 int davResp(urlinfo u, FD_t ctrl, /*@unused@*/ char *const * str) 01760 { 01761 int rc = 0; 01762 01763 rc = ne_begin_request(ctrl->req); 01764 rc = my_result("ne_begin_req(ctrl->req)", rc, NULL); 01765 01766 if (_dav_debug < 0) 01767 fprintf(stderr, "*** davResp(%p,%p,%p) sess %p req %p rc %d\n", u, ctrl, str, u->sess, ctrl->req, rc); 01768 01769 /* HACK FTPERR_NE_FOO == -NE_FOO error impedance match */ 01770 /*@-observertrans@*/ 01771 if (rc) 01772 fdSetSyserrno(ctrl, errno, ftpStrerror(-rc)); 01773 /*@=observertrans@*/ 01774 01775 return rc; 01776 } 01777 /*@=mustmod@*/ 01778 01779 int davReq(FD_t ctrl, const char * httpCmd, const char * httpArg) 01780 { 01781 urlinfo u; 01782 int rc = 0; 01783 01784 assert(ctrl != NULL); 01785 u = ctrl->url; 01786 URLSANE(u); 01787 01788 if (_dav_debug < 0) 01789 fprintf(stderr, "*** davReq(%p,%s,\"%s\") entry sess %p req %p\n", ctrl, httpCmd, (httpArg ? httpArg : ""), u->sess, ctrl->req); 01790 01791 ctrl->persist = (u->httpVersion > 0 ? 1 : 0); 01792 ctrl = fdLink(ctrl, "open ctrl (davReq)"); 01793 assert(ctrl != NULL); 01794 01795 assert(u->sess != NULL); 01796 /* XXX reset disconnected handle to NULL. should never happen ... */ 01797 if (ctrl->req == (void *)-1) 01798 ctrl->req = NULL; 01799 /*@-nullderef@*/ 01800 assert(ctrl->req == NULL); 01801 /*@=nullderef@*/ 01802 /*@-nullpass@*/ 01803 ctrl->req = ne_request_create(u->sess, httpCmd, httpArg); 01804 /*@=nullpass@*/ 01805 assert(ctrl->req != NULL); 01806 01807 ne_set_request_private(ctrl->req, "fd", ctrl); 01808 01809 #if !defined(HAVE_NEON_NE_GET_RESPONSE_HEADER) 01810 ne_add_response_header_catcher(ctrl->req, davAllHeaders, ctrl); 01811 01812 ne_add_response_header_handler(ctrl->req, "Content-Length", 01813 davContentLength, ctrl); 01814 ne_add_response_header_handler(ctrl->req, "Content-Type", 01815 davContentType, ctrl); 01816 ne_add_response_header_handler(ctrl->req, "Content-Disposition", 01817 davContentDisposition, ctrl); 01818 ne_add_response_header_handler(ctrl->req, "Last-Modified", 01819 davLastModified, ctrl); 01820 ne_add_response_header_handler(ctrl->req, "Connection", 01821 davConnection, ctrl); 01822 #endif 01823 01824 if (!strcmp(httpCmd, "PUT")) { 01825 #if defined(HAVE_NEON_NE_SEND_REQUEST_CHUNK) 01826 ctrl->wr_chunked = 1; 01827 ne_add_request_header(ctrl->req, "Transfer-Encoding", "chunked"); 01828 ne_set_request_chunked(ctrl->req, 1); 01829 /* HACK: no retries if/when chunking. */ 01830 rc = davResp(u, ctrl, NULL); 01831 #else 01832 rc = FTPERR_SERVER_IO_ERROR; 01833 #endif 01834 } else { 01835 /* HACK: possible ETag: "inode-size-mtime" */ 01836 #if !defined(HAVE_NEON_NE_GET_RESPONSE_HEADER) 01837 ne_add_response_header_handler(ctrl->req, "Accept-Ranges", 01838 davAcceptRanges, u); 01839 #endif 01840 /* HACK: possible Transfer-Encoding: on GET. */ 01841 01842 /* HACK: other errors may need retry too. */ 01843 /* HACK: neon retries once, gud enuf. */ 01844 /* HACK: retry counter? */ 01845 do { 01846 rc = davResp(u, ctrl, NULL); 01847 } while (rc == NE_RETRY); 01848 } 01849 01850 /* XXX somwhere else instead? */ 01851 if (_dav_debug) { 01852 const ne_status *status = ne_get_status(ctrl->req); 01853 fprintf(stderr, "HTTP request sent, awaiting response... %d %s\n", status->code, status->reason_phrase); 01854 } 01855 01856 if (rc) 01857 goto errxit; 01858 01859 if (_dav_debug < 0) 01860 fprintf(stderr, "*** davReq(%p,%s,\"%s\") exit sess %p req %p rc %d\n", ctrl, httpCmd, (httpArg ? httpArg : ""), u->sess, ctrl->req, rc); 01861 01862 #if defined(HAVE_NEON_NE_GET_RESPONSE_HEADER) 01863 davContentLength(ctrl, 01864 ne_get_response_header(ctrl->req, "Content-Length")); 01865 davContentType(ctrl, 01866 ne_get_response_header(ctrl->req, "Content-Type")); 01867 davContentDisposition(ctrl, 01868 ne_get_response_header(ctrl->req, "Content-Disposition")); 01869 davLastModified(ctrl, 01870 ne_get_response_header(ctrl->req, "Last-Modified")); 01871 davConnection(ctrl, 01872 ne_get_response_header(ctrl->req, "Connection")); 01873 if (strcmp(httpCmd, "PUT")) 01874 davAcceptRanges(u, 01875 ne_get_response_header(ctrl->req, "Accept-Ranges")); 01876 #endif 01877 01878 ctrl = fdLink(ctrl, "open data (davReq)"); 01879 return 0; 01880 01881 errxit: 01882 /*@-observertrans@*/ 01883 fdSetSyserrno(ctrl, errno, ftpStrerror(rc)); 01884 /*@=observertrans@*/ 01885 01886 /* HACK balance fd refs. ne_session_destroy to tear down non-keepalive? */ 01887 ctrl = fdLink(ctrl, "error data (davReq)"); 01888 01889 return rc; 01890 } 01891 01892 FD_t davOpen(const char * url, /*@unused@*/ int flags, 01893 /*@unused@*/ mode_t mode, /*@out@*/ urlinfo * uret) 01894 { 01895 const char * path = NULL; 01896 urltype urlType = urlPath(url, &path); 01897 urlinfo u = NULL; 01898 FD_t fd = NULL; 01899 int rc; 01900 01901 #if 0 /* XXX makeTempFile() heartburn */ 01902 assert(!(flags & O_RDWR)); 01903 #endif 01904 01905 if (_dav_debug < 0) 01906 fprintf(stderr, "*** davOpen(%s,0x%x,0%o,%p)\n", url, flags, (unsigned)mode, uret); 01907 rc = davInit(url, &u); 01908 if (rc || u == NULL || u->sess == NULL) 01909 goto exit; 01910 01911 if (u->ctrl == NULL) 01912 u->ctrl = fdNew("persist ctrl (davOpen)"); 01913 else { 01914 yarnLock use = u->ctrl->_item.use; 01915 yarnPossess(use); 01916 if (yarnPeekLock(use) > 2L && u->data == NULL) 01917 u->data = fdNew("persist data (davOpen)"); 01918 yarnRelease(use); 01919 } 01920 01921 if (u->ctrl->url == NULL) 01922 fd = u->ctrl = fdLink(u->ctrl, "grab ctrl (davOpen persist ctrl)"); 01923 else if (u->data->url == NULL) 01924 fd = u->data = fdLink(u->data, "grab ctrl (davOpen persist data)"); 01925 else 01926 fd = fdNew("grab ctrl (davOpen)"); 01927 01928 if (fd) { 01929 fdSetOpen(fd, url, flags, mode); 01930 fdSetIo(fd, ufdio); 01931 01932 fd->ftpFileDoneNeeded = 0; 01933 fd->rd_timeoutsecs = rpmioHttpReadTimeoutSecs; 01934 fd->contentLength = fd->bytesRemain = -1; 01935 assert(urlType == URL_IS_HTTPS || urlType == URL_IS_HTTP || urlType == URL_IS_HKP); 01936 fd->urlType = urlType; 01937 fd->url = urlLink(u, "url (davOpen)"); 01938 fd = fdLink(fd, "grab data (davOpen)"); 01939 } 01940 01941 exit: 01942 if (uret) 01943 *uret = u; 01944 /*@-refcounttrans@*/ 01945 return fd; 01946 /*@=refcounttrans@*/ 01947 } 01948 01949 /*@-mustmod@*/ 01950 ssize_t davRead(void * cookie, /*@out@*/ char * buf, size_t count) 01951 { 01952 FD_t fd = cookie; 01953 ssize_t rc; 01954 01955 #if WITH_NEON_MIN_VERSION >= 0x002700 01956 { urlinfo u = NULL; 01957 u = urlLink(fd->url, "url (davRead)"); 01958 if (u->info.status == ne_status_recving) 01959 rc = ne_read_response_block(fd->req, buf, count); 01960 else { 01961 /* If server has disconnected, then tear down the neon request. */ 01962 if (u->info.status == ne_status_disconnected) { 01963 int xx; 01964 xx = ne_end_request(fd->req); 01965 xx = my_result("davRead: ne_end_request(req)", xx, NULL); 01966 ne_request_destroy(fd->req); 01967 fd->req = (void *)-1; 01968 } 01969 errno = EIO; /* XXX what to do? */ 01970 rc = -1; 01971 } 01972 u = urlFree(u, "url (davRead)"); 01973 } 01974 #else 01975 rc = ne_read_response_block(fd->req, buf, count); 01976 #endif 01977 01978 if (_dav_debug < 0) { 01979 fprintf(stderr, "*** davRead(%p,%p,0x%x) rc 0x%x\n", cookie, buf, (unsigned)count, (unsigned)rc); 01980 } 01981 01982 return rc; 01983 } 01984 /*@=mustmod@*/ 01985 01986 ssize_t davWrite(void * cookie, const char * buf, size_t count) 01987 { 01988 #if !defined(NEONBLOWSCHUNKS) || defined(HAVE_NEON_NE_SEND_REQUEST_CHUNK) || defined(__LCLINT__) 01989 FD_t fd = cookie; 01990 #endif 01991 ssize_t rc; 01992 int xx = -1; 01993 01994 #if !defined(NEONBLOWSCHUNKS) 01995 ne_session * sess; 01996 01997 assert(fd->req != NULL); 01998 sess = ne_get_session(fd->req); 01999 assert(sess != NULL); 02000 02001 /* HACK: include ne_private.h to access sess->socket for now. */ 02002 xx = ne_sock_fullwrite(sess->socket, buf, count); 02003 #else 02004 #if defined(HAVE_NEON_NE_SEND_REQUEST_CHUNK) || defined(__LCLINT__) 02005 assert(fd->req != NULL); 02006 /*@-unrecog@*/ 02007 xx = ne_send_request_chunk(fd->req, buf, count); 02008 /*@=unrecog@*/ 02009 #else 02010 errno = EIO; /* HACK */ 02011 return -1; 02012 #endif 02013 #endif 02014 02015 /* HACK: stupid error impedence matching. */ 02016 rc = (xx == 0 ? (ssize_t)count : -1); 02017 02018 if (_dav_debug < 0) 02019 fprintf(stderr, "*** davWrite(%p,%p,0x%x) rc 0x%x\n", cookie, buf, (unsigned)count, (unsigned)rc); 02020 02021 return rc; 02022 } 02023 02024 int davSeek(void * cookie, /*@unused@*/ _libio_pos_t pos, int whence) 02025 { 02026 if (_dav_debug < 0) 02027 fprintf(stderr, "*** davSeek(%p,pos,%d)\n", cookie, whence); 02028 return -1; 02029 } 02030 02031 /*@-mustmod@*/ /* HACK: fd->req is modified. */ 02032 int davClose(void * cookie) 02033 { 02034 /*@-onlytrans@*/ 02035 FD_t fd = cookie; 02036 /*@=onlytrans@*/ 02037 int rc = 0; 02038 02039 assert(fd->req != NULL); 02040 if (fd->req != (void *)-1) { 02041 rc = ne_end_request(fd->req); 02042 rc = my_result("ne_end_request(req)", rc, NULL); 02043 02044 ne_request_destroy(fd->req); 02045 } 02046 fd->req = NULL; 02047 02048 if (_dav_debug < 0) 02049 fprintf(stderr, "*** davClose(%p) rc %d\n", fd, rc); 02050 return rc; 02051 } 02052 /*@=mustmod@*/ 02053 02054 /* =============================================================== */ 02055 int davMkdir(const char * path, mode_t mode) 02056 { 02057 urlinfo u = NULL; 02058 const char * src = NULL; 02059 int rc; 02060 02061 rc = davInit(path, &u); 02062 if (rc) 02063 goto exit; 02064 assert(u != NULL); 02065 02066 (void) urlPath(path, &src); 02067 02068 rc = ne_mkcol(u->sess, path); 02069 02070 if (rc) rc = -1; /* XXX HACK: errno impedance match */ 02071 02072 /* XXX HACK: verify getrestype(remote) == resr_collection */ 02073 02074 exit: 02075 if (_dav_debug) 02076 fprintf(stderr, "*** davMkdir(%s,0%o) rc %d\n", path, (unsigned)mode, rc); 02077 return rc; 02078 } 02079 02080 int davRmdir(const char * path) 02081 { 02082 urlinfo u = NULL; 02083 const char * src = NULL; 02084 int rc; 02085 02086 rc = davInit(path, &u); 02087 if (rc) 02088 goto exit; 02089 assert(u != NULL); 02090 02091 (void) urlPath(path, &src); 02092 02093 /* XXX HACK: only getrestype(remote) == resr_collection */ 02094 02095 rc = ne_delete(u->sess, path); 02096 02097 if (rc) rc = -1; /* XXX HACK: errno impedance match */ 02098 02099 exit: 02100 if (_dav_debug) 02101 fprintf(stderr, "*** davRmdir(%s) rc %d\n", path, rc); 02102 return rc; 02103 } 02104 02105 int davRename(const char * oldpath, const char * newpath) 02106 { 02107 urlinfo u = NULL; 02108 const char * src = NULL; 02109 const char * dst = NULL; 02110 int overwrite = 1; /* HACK: set this correctly. */ 02111 int rc; 02112 02113 rc = davInit(oldpath, &u); 02114 if (rc) 02115 goto exit; 02116 assert(u != NULL); 02117 02118 (void) urlPath(oldpath, &src); 02119 (void) urlPath(newpath, &dst); 02120 02121 /* XXX HACK: only getrestype(remote) != resr_collection */ 02122 02123 rc = ne_move(u->sess, overwrite, src, dst); 02124 02125 if (rc) rc = -1; /* XXX HACK: errno impedance match */ 02126 02127 exit: 02128 if (_dav_debug) 02129 fprintf(stderr, "*** davRename(%s,%s) rc %d\n", oldpath, newpath, rc); 02130 return rc; 02131 } 02132 02133 int davUnlink(const char * path) 02134 { 02135 urlinfo u = NULL; 02136 const char * src = NULL; 02137 int rc; 02138 02139 rc = davInit(path, &u); 02140 if (rc) 02141 goto exit; 02142 assert(u != NULL); 02143 02144 (void) urlPath(path, &src); 02145 02146 /* XXX HACK: only getrestype(remote) != resr_collection */ 02147 02148 rc = ne_delete(u->sess, src); 02149 02150 exit: 02151 if (rc) rc = -1; /* XXX HACK: errno impedance match */ 02152 02153 if (_dav_debug) 02154 fprintf(stderr, "*** davUnlink(%s) rc %d\n", path, rc); 02155 return rc; 02156 } 02157 02158 #ifdef NOTYET 02159 static int davChdir(const char * path) 02160 /*@globals h_errno, fileSystem, internalState @*/ 02161 /*@modifies fileSystem, internalState @*/ 02162 { 02163 return davCommand("CWD", path, NULL); 02164 } 02165 #endif /* NOTYET */ 02166 02167 /* =============================================================== */ 02168 02169 static const char * statstr(const struct stat * st, 02170 /*@returned@*/ /*@out@*/ char * buf) 02171 /*@modifies *buf @*/ 02172 { 02173 sprintf(buf, 02174 "*** dev %x ino %x mode %0o nlink %d uid %d gid %d rdev %x size %x\n", 02175 (unsigned)st->st_dev, 02176 (unsigned)st->st_ino, 02177 (unsigned)st->st_mode, 02178 (unsigned)st->st_nlink, 02179 (unsigned)st->st_uid, 02180 (unsigned)st->st_gid, 02181 (unsigned)st->st_rdev, 02182 (unsigned)st->st_size); 02183 return buf; 02184 } 02185 02186 int davStat(const char * path, /*@out@*/ struct stat *st) 02187 /*@globals hrefpat, fileSystem, internalState @*/ 02188 /*@modifies *st, fileSystem, internalState @*/ 02189 { 02190 avContext ctx = NULL; 02191 char buf[1024]; 02192 int rc = -1; 02193 02194 if (path == NULL || *path == '\0') { 02195 errno = ENOENT; 02196 goto exit; 02197 } 02198 ctx = avContextCreate(path, st); 02199 if (ctx == NULL) { 02200 errno = ENOENT; /* Note: ctx is NULL iff urlSplit() fails. */ 02201 goto exit; 02202 } 02203 rc = davNLST(ctx); 02204 if (rc) { 02205 /* HACK: errno = ??? */ 02206 goto exit; 02207 } 02208 02209 if (st->st_mode == 0) 02210 st->st_mode = (ctx->ac > 1 ? S_IFDIR : S_IFREG); 02211 st->st_size = (ctx->sizes ? ctx->sizes[0] : (size_t)st->st_size); 02212 st->st_mtime = (ctx->mtimes ? ctx->mtimes[0] : st->st_mtime); 02213 st->st_atime = st->st_ctime = st->st_mtime; /* HACK */ 02214 if (S_ISDIR(st->st_mode)) { 02215 st->st_nlink = 2; 02216 st->st_mode |= 0755; 02217 } else 02218 if (S_ISREG(st->st_mode)) { 02219 st->st_nlink = 1; 02220 st->st_mode |= 0644; 02221 } 02222 02223 /* XXX Fts(3) needs/uses st_ino. */ 02224 /* Hash the path to generate a st_ino analogue. */ 02225 if (st->st_ino == 0) 02226 st->st_ino = hashFunctionString(0, path, 0); 02227 02228 exit: 02229 if (_dav_debug < 0) 02230 fprintf(stderr, "*** davStat(%s) rc %d\n%s", path, rc, statstr(st, buf)); 02231 ctx = avContextDestroy(ctx); 02232 return rc; 02233 } 02234 02235 int davLstat(const char * path, /*@out@*/ struct stat *st) 02236 /*@globals hrefpat, fileSystem, internalState @*/ 02237 /*@modifies *st, fileSystem, internalState @*/ 02238 { 02239 avContext ctx = NULL; 02240 char buf[1024]; 02241 int rc = -1; 02242 02243 if (path == NULL || *path == '\0') { 02244 errno = ENOENT; 02245 goto exit; 02246 } 02247 ctx = avContextCreate(path, st); 02248 if (ctx == NULL) { 02249 errno = ENOENT; /* Note: ctx is NULL iff urlSplit() fails. */ 02250 goto exit; 02251 } 02252 rc = davNLST(ctx); 02253 if (rc) { 02254 /* HACK: errno = ??? */ 02255 goto exit; 02256 } 02257 02258 if (st->st_mode == 0) 02259 st->st_mode = (ctx->ac > 1 ? S_IFDIR : S_IFREG); 02260 st->st_size = (ctx->sizes ? ctx->sizes[0] : (size_t)st->st_size); 02261 st->st_mtime = (ctx->mtimes ? ctx->mtimes[0] : st->st_mtime); 02262 st->st_atime = st->st_ctime = st->st_mtime; /* HACK */ 02263 if (S_ISDIR(st->st_mode)) { 02264 st->st_nlink = 2; 02265 st->st_mode |= 0755; 02266 } else 02267 if (S_ISREG(st->st_mode)) { 02268 st->st_nlink = 1; 02269 st->st_mode |= 0644; 02270 } 02271 02272 /* XXX fts(3) needs/uses st_ino. */ 02273 /* Hash the path to generate a st_ino analogue. */ 02274 if (st->st_ino == 0) 02275 st->st_ino = hashFunctionString(0, path, 0); 02276 02277 if (_dav_debug < 0) 02278 fprintf(stderr, "*** davLstat(%s) rc %d\n%s\n", path, rc, statstr(st, buf)); 02279 exit: 02280 ctx = avContextDestroy(ctx); 02281 return rc; 02282 } 02283 02284 #ifdef NOTYET 02285 static int davReadlink(const char * path, /*@out@*/ char * buf, size_t bufsiz) 02286 /*@globals h_errno, fileSystem, internalState @*/ 02287 /*@modifies *buf, fileSystem, internalState @*/ 02288 { 02289 int rc; 02290 rc = davNLST(path, DO_FTP_READLINK, NULL, buf, bufsiz); 02291 if (_dav_debug < 0) 02292 fprintf(stderr, "*** davReadlink(%s) rc %d\n", path, rc); 02293 return rc; 02294 } 02295 #endif /* NOTYET */ 02296 02297 #endif /* WITH_NEON */ 02298 02299 /* =============================================================== */ 02300 /*@unchecked@*/ 02301 int avmagicdir = 0x3607113; 02302 02303 #ifndef WITH_NEON 02304 /*@-nullstate@*/ /* FIX: u->{ctrl,data}->url undef after XurlLink. */ 02305 FD_t httpOpen(const char * url, /*@unused@*/ int flags, 02306 /*@unused@*/ mode_t mode, /*@out@*/ urlinfo * uret) 02307 /*@globals internalState @*/ 02308 /*@modifies *uret, internalState @*/ 02309 { 02310 urlinfo u = NULL; 02311 FD_t fd = NULL; 02312 02313 #if 0 /* XXX makeTempFile() heartburn */ 02314 assert(!(flags & O_RDWR)); 02315 #endif 02316 if (urlSplit(url, &u)) 02317 goto exit; 02318 02319 if (u->ctrl == NULL) 02320 u->ctrl = fdNew("persist ctrl (httpOpen)"); 02321 if (u->ctrl != NULL) { /* XXX can't happen */ 02322 yarnLock use = u->ctrl->_item.use; 02323 yarnPossess(use); 02324 if (yarnPeekLock(use) > 2L && u->data == NULL) 02325 u->data = fdNew("persist data (httpOpen)"); 02326 yarnRelease(use); 02327 } 02328 02329 if (u->ctrl->url == NULL) 02330 fd = fdLink(u->ctrl, "grab ctrl (httpOpen persist ctrl)"); 02331 else if (u->data->url == NULL) 02332 fd = fdLink(u->data, "grab ctrl (httpOpen persist data)"); 02333 else 02334 fd = fdNew("grab ctrl (httpOpen)"); 02335 02336 if (fd) { 02337 fdSetIo(fd, ufdio); 02338 fd->ftpFileDoneNeeded = 0; 02339 fd->rd_timeoutsecs = rpmioHttpReadTimeoutSecs; 02340 fd->contentLength = fd->bytesRemain = -1; 02341 fd->url = urlLink(u, "url (httpOpen)"); 02342 fd = fdLink(fd, "grab data (httpOpen)"); 02343 fd->urlType = URL_IS_HTTP; 02344 } 02345 02346 exit: 02347 if (uret) 02348 *uret = u; 02349 /*@-refcounttrans@*/ 02350 return fd; 02351 /*@=refcounttrans@*/ 02352 } 02353 /*@=nullstate@*/ 02354 #endif 02355 02356 #ifdef WITH_NEON 02357 /* =============================================================== */ 02358 int davClosedir(/*@only@*/ DIR * dir) 02359 { 02360 return avClosedir(dir); 02361 } 02362 02363 struct dirent * davReaddir(DIR * dir) 02364 { 02365 return avReaddir(dir); 02366 } 02367 02368 DIR * davOpendir(const char * path) 02369 /*@globals hrefpat @*/ 02370 { 02371 AVDIR avdir = NULL; 02372 avContext ctx = NULL; 02373 struct stat sb, *st = &sb; /* XXX HACK: davHEAD needs ctx->st. */ 02374 const char * uri = NULL; 02375 int rc; 02376 02377 if (_dav_debug < 0) 02378 fprintf(stderr, "*** davOpendir(%s)\n", path); 02379 02380 if (path == NULL || *path == '\0') { 02381 errno = ENOENT; 02382 goto exit; 02383 } 02384 02385 /* Note: all Opendir(3) URI's need pesky trailing '/' */ 02386 /*@-globs -mods@*/ 02387 if (path[strlen(path)-1] != '/') 02388 uri = rpmExpand(path, "/", NULL); 02389 else 02390 uri = xstrdup(path); 02391 /*@=globs =mods@*/ 02392 02393 /* Load DAV collection into argv. */ 02394 /* XXX HACK: davHEAD needs ctx->st. */ 02395 ctx = avContextCreate(uri, st); 02396 if (ctx == NULL) { 02397 errno = ENOENT; /* Note: ctx is NULL iff urlSplit() fails. */ 02398 goto exit; 02399 } 02400 02401 rc = davNLST(ctx); 02402 if (rc) { 02403 /* HACK: errno = ??? */ 02404 goto exit; 02405 } else 02406 avdir = (AVDIR) avOpendir(uri, ctx->av, ctx->modes); 02407 02408 exit: 02409 uri = _free(uri); 02410 ctx = avContextDestroy(ctx); 02411 /*@-kepttrans@*/ 02412 return (DIR *) avdir; 02413 /*@=kepttrans@*/ 02414 } 02415 /*@=modfilesys@*/ 02416 02417 /*@-mustmod@*/ 02418 char * davRealpath(const char * path, char * resolved_path) 02419 { 02420 assert(resolved_path == NULL); /* XXX no POSIXly broken realpath(3) here. */ 02421 /* XXX TODO: handle redirects. For now, just dupe the path. */ 02422 return xstrdup(path); 02423 } 02424 /*@=mustmod@*/ 02425 02426 #endif /* WITH_NEON */