rpm 5.2.1

- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

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 */