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