rpm 5.3.7

rpmio/rpmdav.c

Go to the documentation of this file.
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 */