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