rpm  5.2.1
rpmdav.c
Go to the documentation of this file.
1 /*@-modfilesys@*/
6 #include "system.h"
7 
8 #ifdef WITH_NEON
9 
10 #include "ne_alloc.h"
11 #include "ne_auth.h"
12 #include "ne_basic.h"
13 #include "ne_dates.h"
14 #include "ne_locks.h"
15 
16 #define NEONBLOWSCHUNKS
17 #ifndef NEONBLOWSCHUNKS
18 /* HACK: include ne_private.h to access sess->socket for now. */
19 #include "../neon/src/ne_private.h"
20 #endif
21 
22 #include "ne_props.h"
23 #include "ne_request.h"
24 #include "ne_socket.h"
25 #include "ne_string.h"
26 
27 #include "ne_utils.h"
28 #if !defined(HEADER_ERR_H)
29 /* cheats to avoid having to explicitly build against OpenSSL */
30 /*@-exportheader -redecl @*/
31 extern void ERR_remove_state(int foo);
32 extern void ENGINE_cleanup(void);
33 extern void CONF_modules_unload(int foo);
34 extern void ERR_free_strings(void);
35 extern void EVP_cleanup(void);
36 extern void CRYPTO_cleanup_all_ex_data(void);
37 extern void CRYPTO_mem_leaks(void * ptr);
38 /*@=exportheader =redecl @*/
39 #endif
40 
41 #include "ne_md5.h" /* for version detection only */
42 
43 /* poor-man's NEON version determination */
44 #if defined(NE_MD5_H)
45 #define WITH_NEON_MIN_VERSION 0x002700
46 #elif defined(NE_FEATURE_I18N)
47 #define WITH_NEON_MIN_VERSION 0x002600
48 #else
49 #define WITH_NEON_MIN_VERSION 0x002500
50 #endif
51 
52 /* XXX API changes for NEON 0.26 */
53 #if WITH_NEON_MIN_VERSION >= 0x002600
54 #define ne_propfind_set_private(_pfh, _create_item, NULL) \
55  ne_propfind_set_private(_pfh, _create_item, NULL, NULL)
56 #endif
57 
58 #endif /* WITH_NEON */
59 
60 #include <rpmio_internal.h>
61 
62 #include <rpmhash.h>
63 #include <rpmmacro.h> /* XXX rpmExpand */
64 #include <ugid.h>
65 
66 #define _RPMAV_INTERNAL
67 #define _RPMDAV_INTERNAL
68 #include <rpmdav.h>
69 #include <mire.h>
70 
71 #include "debug.h"
72 
73 /*@access DIR @*/
74 /*@access FD_t @*/
75 /*@access urlinfo @*/
76 /*@access miRE @*/
77 
78 /* HACK: reasonable value needed (wget uses 900 as default). */
79 #if 0
80 #define TIMEOUT_SECS 60
81 #else
82 #define TIMEOUT_SECS 5
83 #endif
84 
85 /*@unchecked@*/ /*@observer@*/
86 static const char _rpmioHttpUserAgent[] = PACKAGE "/" PACKAGE_VERSION;
87 
88 /*@unchecked@*/
89 static int rpmioHttpPersist = 1;
90 /*@unchecked@*/
92 /*@unchecked@*/
94 #ifdef NOTYET
95 int rpmioHttpRetries = 20;
96 int rpmioHttpRecurseMax = 5;
97 int rpmioHttpMaxRedirect = 20;
98 #endif
99 
100 /*@unchecked@*/ /*@null@*/
101 const char * rpmioHttpAccept;
102 /*@unchecked@*/ /*@null@*/
103 const char * rpmioHttpUserAgent;
104 
105 /* =============================================================== */
106 void * avContextDestroy(avContext ctx)
107 {
108  if (ctx == NULL)
109  return NULL;
110  if (ctx->av != NULL)
111  ctx->av = argvFree(ctx->av);
112  ctx->modes = _free(ctx->modes);
113  ctx->sizes = _free(ctx->sizes);
114  ctx->mtimes = _free(ctx->mtimes);
115  ctx->u = urlFree(ctx->u, "avContextDestroy");
116  ctx->uri = _free(ctx->uri);
117  memset(ctx, 0, sizeof(*ctx)); /* trash & burn */
118  ctx = _free(ctx);
119  return NULL;
120 }
121 
122 void * avContextCreate(const char *uri, struct stat *st)
123 {
124  avContext ctx;
125  urlinfo u;
126 
127 /*@-globs@*/ /* FIX: h_errno annoyance. */
128  if (urlSplit(uri, &u))
129  return NULL;
130 /*@=globs@*/
131 
132  ctx = xcalloc(1, sizeof(*ctx));
133  ctx->uri = xstrdup(uri);
134  ctx->u = urlLink(u, "avContextCreate");
135 /*@-temptrans@*/ /* XXX note the assignment */
136  if ((ctx->st = st) != NULL)
137  memset(ctx->st, 0, sizeof(*ctx->st));
138 /*@=temptrans@*/
139  return ctx;
140 }
141 
142 int avContextAdd(avContext ctx, const char * path,
143  mode_t mode, size_t size, time_t mtime)
144 {
145  int xx;
146 
147 if (_av_debug < 0)
148 fprintf(stderr, "*** avContextAdd(%p,\"%s\", %06o, 0x%x, 0x%x)\n", ctx, path, (unsigned)mode, (unsigned)size, (unsigned)mtime);
149 
150  xx = argvAdd(&ctx->av, path);
151 
152  while (ctx->ac >= ctx->nalloced) {
153  if (ctx->nalloced <= 0)
154  ctx->nalloced = 1;
155  ctx->nalloced *= 2;
156  ctx->modes = xrealloc(ctx->modes,
157  (sizeof(*ctx->modes) * ctx->nalloced));
158  ctx->sizes = xrealloc(ctx->sizes,
159  (sizeof(*ctx->sizes) * ctx->nalloced));
160  ctx->mtimes = xrealloc(ctx->mtimes,
161  (sizeof(*ctx->mtimes) * ctx->nalloced));
162  }
163 
164  ctx->modes[ctx->ac] = (rpmuint16_t)mode;
165  ctx->sizes[ctx->ac] = size;
166  ctx->mtimes[ctx->ac] = mtime;
167  ctx->ac++;
168  return 0;
169 }
170 
171 int avClosedir(/*@only@*/ DIR * dir)
172 {
173  AVDIR avdir = (AVDIR)dir;
174 
175 if (_av_debug)
176 fprintf(stderr, "*** avClosedir(%p)\n", avdir);
177 
178 #if defined(WITH_PTHREADS)
179 /*@-moduncon -noeffectuncon @*/
180  (void) pthread_mutex_destroy(&avdir->lock);
181 /*@=moduncon =noeffectuncon @*/
182 #endif
183 
184  avdir = _free(avdir);
185  return 0;
186 }
187 
188 struct dirent * avReaddir(DIR * dir)
189 {
190  AVDIR avdir = (AVDIR)dir;
191  struct dirent * dp;
192  const char ** av;
193  unsigned char * dt;
194  int ac;
195  int i;
196 
197  if (avdir == NULL || !ISAVMAGIC(avdir) || avdir->data == NULL) {
198  /* XXX TODO: EBADF errno. */
199  return NULL;
200  }
201 
202  dp = (struct dirent *) avdir->data;
203  av = (const char **) (dp + 1);
204  ac = (int)avdir->size;
205  dt = (unsigned char *) (av + (ac + 1));
206  i = avdir->offset + 1;
207 
208  if (i < 0 || i >= ac || av[i] == NULL)
209  return NULL;
210 
211  avdir->offset = i;
212 
213  /* XXX glob(3) uses REAL_DIR_ENTRY(dp) test on d_ino */
214 /*@-type@*/
215  /* Hash the name (starting with parent hash) for a d_ino analogue. */
216  dp->d_ino = hashFunctionString(avdir->filepos, dp->d_name, 0);
217 
218 #if !defined(__DragonFly__) && !defined(__CYGWIN__)
219  dp->d_reclen = 0; /* W2DO? */
220 #endif
221 
222 #if !(defined(hpux) || defined(__hpux) || defined(sun) || defined(RPM_OS_AIX) || defined(__CYGWIN__) || defined(__QNXNTO__))
223 #if !defined(__APPLE__) && !defined(__FreeBSD_kernel__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__DragonFly__) && !defined(__OpenBSD__)
224  dp->d_off = 0; /* W2DO? */
225 #endif
226  dp->d_type = dt[i];
227 #endif
228 /*@=type@*/
229 
230  strncpy(dp->d_name, av[i], sizeof(dp->d_name));
231 if (_av_debug)
232 fprintf(stderr, "*** avReaddir(%p) %p %s\n", (void *)avdir, dp, dp->d_name);
233 
234  return dp;
235 }
236 
237 DIR * avOpendir(const char * path, const char ** av, rpmuint16_t * modes)
238 {
239  AVDIR avdir;
240  struct dirent * dp;
241  size_t nb;
242  const char ** nav;
243  unsigned char * dt;
244  char * t;
245  int ac, nac;
246 
247 if (_av_debug)
248 fprintf(stderr, "*** avOpendir(%s, %p, %p)\n", path, av, modes);
249 
250  nb = 0;
251  ac = 0;
252  if (av != NULL)
253  while (av[ac] != NULL)
254  nb += strlen(av[ac++]) + 1;
255  ac += 2; /* for "." and ".." */
256  nb += sizeof(".") + sizeof("..");
257 
258  nb += sizeof(*avdir) + sizeof(*dp) + ((ac + 1) * sizeof(*av)) + (ac + 1);
259  avdir = xcalloc(1, nb);
260 /*@-abstract@*/
261  dp = (struct dirent *) (avdir + 1);
262  nav = (const char **) (dp + 1);
263  dt = (unsigned char *) (nav + (ac + 1));
264  t = (char *) (dt + ac + 1);
265 /*@=abstract@*/
266 
267  avdir->fd = avmagicdir;
268 /*@-usereleased@*/
269  avdir->data = (char *) dp;
270 /*@=usereleased@*/
271  avdir->allocation = nb;
272  avdir->size = ac;
273  avdir->offset = -1;
274  /* Hash the directory path for a d_ino analogue. */
275  avdir->filepos = hashFunctionString(0, path, 0);
276 
277 #if defined(WITH_PTHREADS)
278 /*@-moduncon -noeffectuncon -nullpass @*/
279  (void) pthread_mutex_init(&avdir->lock, NULL);
280 /*@=moduncon =noeffectuncon =nullpass @*/
281 #endif
282 
283  nac = 0;
284  /*@-dependenttrans -unrecog@*/
285  dt[nac] = (unsigned char)DT_DIR; nav[nac++] = t; t = stpcpy(t, "."); t++;
286  dt[nac] = (unsigned char)DT_DIR; nav[nac++] = t; t = stpcpy(t, ".."); t++;
287  /*@=dependenttrans =unrecog@*/
288 
289  /* Append av strings to DIR elements. */
290  ac = 0;
291  if (av != NULL)
292  while (av[ac] != NULL) {
293  if (modes != NULL)
294  switch (modes[ac] & S_IFMT) {
295  case S_IFIFO: dt[nac]=(unsigned char)DT_FIFO;/*@switchbreak@*/break;
296  case S_IFCHR: dt[nac]=(unsigned char)DT_CHR; /*@switchbreak@*/break;
297  case S_IFDIR: dt[nac]=(unsigned char)DT_DIR; /*@switchbreak@*/break;
298  case S_IFBLK: dt[nac]=(unsigned char)DT_BLK; /*@switchbreak@*/break;
299  case S_IFREG: dt[nac]=(unsigned char)DT_REG; /*@switchbreak@*/break;
300  case S_IFLNK: dt[nac]=(unsigned char)DT_LNK; /*@switchbreak@*/break;
301 /*@-unrecog@*/
302  case S_IFSOCK:dt[nac]=(unsigned char)DT_SOCK;/*@switchbreak@*/break;
303 /*@=unrecog@*/
304  default: dt[nac]=(unsigned char)DT_UNKNOWN;/*@switchbreak@*/break;
305  }
306  else
307  dt[nac] = (unsigned char)DT_UNKNOWN;
308 /*@-dependenttrans@*/
309  nav[nac++] = t;
310 /*@=dependenttrans@*/
311  t = stpcpy(t, av[ac++]);
312  t++; /* trailing \0 */
313  }
314  nav[nac] = NULL;
315 
316 /*@-kepttrans@*/
317  return (DIR *) avdir;
318 /*@=kepttrans@*/
319 }
320 
321 #ifdef WITH_NEON
322 /* =============================================================== */
323 /*@-mustmod@*/
324 int davDisconnect(/*@unused@*/ void * _u)
325 {
326 #ifdef NOTYET /* XXX not quite right yet. */
327  urlinfo u = (urlinfo)_u;
328  int rc;
329 
330 #if WITH_NEON_MIN_VERSION >= 0x002700
331  rc = (u->info.status == ne_status_sending || u->info.status == ne_status_recving);
332 #else
333  rc = 0; /* XXX W2DO? */
334 #endif
335  if (u != NULL && rc != 0) {
336  if (u->ctrl->req != NULL) {
337  if (u->ctrl && u->ctrl->req) {
338  ne_request_destroy(u->ctrl->req);
339  u->ctrl->req = NULL;
340  }
341  if (u->data && u->data->req) {
342  ne_request_destroy(u->data->req);
343  u->data->req = NULL;
344  }
345  }
346  }
347 if (_dav_debug < 0)
348 fprintf(stderr, "*** davDisconnect(%p) active %d\n", u, rc);
349  /* XXX return active state? */
350 #endif /* NOTYET */
351  return 0;
352 }
353 /*@=mustmod@*/
354 
355 int davFree(urlinfo u)
356 {
357  if (u != NULL) {
358  if (u->sess != NULL) {
359  ne_session_destroy(u->sess);
360  u->sess = NULL;
361  }
362  switch (u->urltype) {
363  default:
364  /*@notreached@*/ break;
365  case URL_IS_HTTPS:
366  case URL_IS_HTTP:
367  case URL_IS_HKP:
369  if (u->lockstore != NULL)
370  ne_lockstore_destroy(u->lockstore);
371  u->lockstore = NULL;
372  u->info.status = 0;
373  ne_sock_exit();
374  break;
375  }
376  }
377 if (_dav_debug < 0)
378 fprintf(stderr, "*** davFree(%p)\n", u);
379  return 0;
380 }
381 
382 void davDestroy(void)
383 {
384 #ifdef NE_FEATURE_SSL
385  if (ne_has_support(NE_FEATURE_SSL)) {
386 /* XXX http://www.nabble.com/Memory-Leaks-in-SSL_Library_init()-t3431875.html */
387  ENGINE_cleanup();
388  CRYPTO_cleanup_all_ex_data();
389  ERR_free_strings();
390  ERR_remove_state(0);
391  EVP_cleanup();
392  CRYPTO_mem_leaks(NULL);
393  CONF_modules_unload(1);
394  }
395 #endif
396 if (_dav_debug < 0)
397 fprintf(stderr, "*** davDestroy()\n");
398 }
399 
400 static void davProgress(void * userdata, off_t progress, off_t total)
401  /*@*/
402 {
403  urlinfo u = userdata;
404  ne_session * sess;
405 
406 assert(u != NULL);
407  sess = u->sess;
408 assert(sess != NULL);
409 /*@-sefuncon@*/
410 assert(u == ne_get_session_private(sess, "urlinfo"));
411 /*@=sefuncon@*/
412 
413  u->info.progress = progress;
414  u->info.total = total;
415 
416 if (_dav_debug < 0)
417 fprintf(stderr, "*** davProgress(%p,0x%x:0x%x) sess %p u %p\n", userdata, (unsigned int)progress, (unsigned int)total, sess, u);
418 }
419 
420 #if WITH_NEON_MIN_VERSION >= 0x002700
421 static void davNotify(void * userdata,
422  ne_session_status status, const ne_session_status_info *info)
423 #else
424 static void davNotify(void * userdata,
425  ne_conn_status status, const char * info)
426 #endif
427  /*@*/
428 {
429  char buf[64];
430  urlinfo u = userdata;
431  ne_session * sess;
432 
433 assert(u != NULL);
434  sess = u->sess;
435 assert(sess != NULL);
436 /*@-sefuncon@*/
437 assert(u == ne_get_session_private(sess, "urlinfo"));
438 /*@=sefuncon@*/
439 
440  u->info.hostname = NULL;
441  u->info.address = NULL;
442  u->info.progress = 0;
443  u->info.total = 0;
444 
445 #if WITH_NEON_MIN_VERSION >= 0x002700
446 #ifdef REFERENCE
447 typedef enum {
448  ne_status_lookup = 0, /* looking up hostname */
449  ne_status_connecting, /* connecting to host */
450  ne_status_connected, /* connected to host */
451  ne_status_sending, /* sending a request body */
452  ne_status_recving, /* receiving a response body */
453  ne_status_disconnected /* disconnected from host */
454 } ne_session_status;
455 #endif
456  switch (status) {
457  default:
458  break;
459  case ne_status_lookup: /* looking up hostname */
460  u->info.hostname = info->ci.hostname;
461  break;
462  case ne_status_connecting: /* connecting to host */
463  u->info.hostname = info->ci.hostname;
464  (void) ne_iaddr_print(info->ci.address, buf, sizeof(buf));
465  buf[sizeof(buf)-1] = '\0';
466  u->info.address = buf;
467  break;
468  case ne_status_connected: /* connected to host */
469  u->info.hostname = info->ci.hostname;
470  break;
471  case ne_status_sending: /* sending a request body */
472  u->info.progress = info->sr.progress;
473  u->info.total = info->sr.total;
474  break;
475  case ne_status_recving: /* receiving a response body */
476  u->info.progress = info->sr.progress;
477  u->info.total = info->sr.total;
478  break;
479  case ne_status_disconnected:
480  u->info.hostname = info->ci.hostname;
481  break;
482  }
483 
484  if (u->notify != NULL)
485  (void) (*u->notify) (u, status);
486 
487 #else
488 #ifdef REFERENCE
489 typedef enum {
490  ne_conn_namelookup, /* lookup up hostname (info = hostname) */
491  ne_conn_connecting, /* connecting to host (info = hostname) */
492  ne_conn_connected, /* connected to host (info = hostname) */
493  ne_conn_secure /* connection now secure (info = crypto level) */
494 } ne_conn_status;
495 #endif
496 
497 if (_dav_debug < 0) {
498 /*@observer@*/
499  static const char * connstates[] = {
500  "namelookup",
501  "connecting",
502  "connected",
503  "secure",
504  "unknown"
505  };
506 
507 fprintf(stderr, "*** davNotify(%p,%d,%p) sess %p u %p %s\n", userdata, status, info, sess, u, connstates[ (status < 4 ? status : 4)]);
508 }
509 #endif
510 
511  u->info.status = status;
512  u->info.hostname = NULL;
513  u->info.address = NULL;
514  u->info.progress = 0;
515  u->info.total = 0;
516 }
517 
518 static void davCreateRequest(ne_request * req, void * userdata,
519  const char * method, const char * uri)
520  /*@*/
521 {
522  urlinfo u = userdata;
523  ne_session * sess;
524  void * private = NULL;
525  const char * id = "urlinfo";
526 
527 assert(u != NULL);
528 assert(u->sess != NULL);
529 assert(req != NULL);
530  sess = ne_get_session(req);
531 assert(sess == u->sess);
532 /*@-sefuncon@*/
533 assert(u == ne_get_session_private(sess, "urlinfo"));
534 /*@=sefuncon@*/
535 
536 assert(sess != NULL);
537  private = ne_get_session_private(sess, id);
538 assert(u == private);
539 
540 if (_dav_debug < 0)
541 fprintf(stderr, "*** davCreateRequest(%p,%p,%s,%s) %s:%p\n", req, userdata, method, uri, id, private);
542 }
543 
544 static void davPreSend(ne_request * req, void * userdata, ne_buffer * buf)
545 {
546  urlinfo u = userdata;
547  ne_session * sess;
548  const char * id = "fd";
549  FD_t fd = NULL;
550 
551 /*@-modunconnomods@*/
552 assert(u != NULL);
553 assert(u->sess != NULL);
554 assert(req != NULL);
555  sess = ne_get_session(req);
556 assert(sess == u->sess);
557 /*@-sefuncon@*/
558 assert(u == ne_get_session_private(sess, "urlinfo"));
559 /*@=sefuncon@*/
560 
561  fd = ne_get_request_private(req, id);
562 /*@=modunconnomods@*/
563 
564 if (_dav_debug < 0)
565 fprintf(stderr, "*** davPreSend(%p,%p,%p) sess %p %s %p\n", req, userdata, buf, sess, id, fd);
566 if (_dav_debug)
567 fprintf(stderr, "-> %s\n", buf->data);
568 
569 }
570 
571 static int davPostSend(ne_request * req, void * userdata, const ne_status * status)
572  /*@*/
573 {
574  urlinfo u = userdata;
575  ne_session * sess;
576  const char * id = "fd";
577  FD_t fd = NULL;
578 
579 assert(u != NULL);
580 assert(u->sess != NULL);
581 assert(req != NULL);
582  sess = ne_get_session(req);
583 assert(sess == u->sess);
584 /*@-sefuncon@*/
585 assert(u == ne_get_session_private(sess, "urlinfo"));
586 /*@=sefuncon@*/
587 
588  fd = ne_get_request_private(req, id);
589 
590 /*@-evalorder@*/
591 if (_dav_debug < 0)
592 fprintf(stderr, "*** davPostSend(%p,%p,%p) sess %p %s %p %s\n", req, userdata, status, sess, id, fd, ne_get_error(sess));
593 /*@=evalorder@*/
594  return NE_OK;
595 }
596 
597 static void davDestroyRequest(ne_request * req, void * userdata)
598  /*@*/
599 {
600  urlinfo u = userdata;
601  ne_session * sess;
602  const char * id = "fd";
603  FD_t fd = NULL;
604 
605 assert(u != NULL);
606 assert(u->sess != NULL);
607 assert(req != NULL);
608  sess = ne_get_session(req);
609 assert(sess == u->sess);
610 /*@-sefuncon@*/
611 assert(u == ne_get_session_private(sess, "urlinfo"));
612 /*@=sefuncon@*/
613 
614  fd = ne_get_request_private(req, id);
615 
616 if (_dav_debug < 0)
617 fprintf(stderr, "*** davDestroyRequest(%p,%p) sess %p %s %p\n", req, userdata, sess, id, fd);
618 }
619 
620 static void davDestroySession(void * userdata)
621  /*@*/
622 {
623  urlinfo u = userdata;
624  ne_session * sess;
625  void * private = NULL;
626  const char * id = "urlinfo";
627 
628 assert(u != NULL);
629 assert(u->sess != NULL);
630  sess = u->sess;
631 /*@-sefuncon@*/
632 assert(u == ne_get_session_private(sess, "urlinfo"));
633 /*@=sefuncon@*/
634 
635 assert(sess != NULL);
636  private = ne_get_session_private(sess, id);
637 assert(u == private);
638 
639 if (_dav_debug < 0)
640 fprintf(stderr, "*** davDestroySession(%p) sess %p %s %p\n", userdata, sess, id, private);
641 }
642 
643 static int
644 davVerifyCert(void *userdata, int failures, const ne_ssl_certificate *cert)
645  /*@*/
646 {
647  const char *hostname = userdata;
648 
649 if (_dav_debug < 0)
650 fprintf(stderr, "*** davVerifyCert(%p,%d,%p) %s\n", userdata, failures, cert, hostname);
651 
652  return 0; /* HACK: trust all server certificates. */
653 }
654 
655 static int davConnect(urlinfo u)
656  /*@globals errno, internalState @*/
657  /*@modifies u, errno, internalState @*/
658 {
659  const char * path = NULL;
660  int rc;
661 
662  /* HACK: hkp:// has no steenkin' options */
663  if (!(u->urltype == URL_IS_HTTP || u->urltype == URL_IS_HTTPS))
664  return 0;
665 
666  /* HACK: where should server capabilities be read? */
667  (void) urlPath(u->url, &path);
668  if (path == NULL || *path == '\0')
669  path = "/";
670 
671 #ifdef NOTYET /* XXX too many new directories while recursing. */
672  /* Repeat OPTIONS for new directories. */
673  if (path != NULL && path[strlen(path)-1] == '/')
675 #endif
676  /* Have options been run? */
678  return 0;
679 
683 
684  /* HACK: perhaps capture Allow: tag, look for PUT permitted. */
685  /* XXX [hdr] Allow: GET,HEAD,POST,OPTIONS,TRACE */
686  rc = ne_options(u->sess, path, u->capabilities);
687  switch (rc) {
688  case NE_OK:
690  { ne_server_capabilities *cap = u->capabilities;
691  if (cap->dav_class1)
693  else
695  if (cap->dav_class2)
697  else
699  if (cap->dav_executable)
701  else
703  } break;
704  case NE_ERROR:
705  /* HACK: "501 Not Implemented" if OPTIONS not permitted. */
706  if (!strncmp("501 ", ne_get_error(u->sess), sizeof("501 ")-1)) {
708  rc = NE_OK;
709  break;
710  }
711  /* HACK: "301 Moved Permanently" on empty subdir. */
712  if (!strncmp("301 ", ne_get_error(u->sess), sizeof("301 ")-1))
713  break;
714 #ifdef HACK /* XXX need davHEAD changes here? */
715  /* HACK: "302 Found" if URI is missing pesky trailing '/'. */
716  if (!strncmp("302 ", ne_get_error(u->sess), sizeof("302 ")-1)) {
717  char * t;
718  if ((t = strchr(u->url, '\0')) != NULL)
719  *t = '/';
720  break;
721  }
722 #endif
723  errno = EIO; /* HACK: more precise errno. */
724  goto bottom;
725  case NE_LOOKUP:
726  errno = ENOENT; /* HACK: errno same as non-existent path. */
727  goto bottom;
728  case NE_CONNECT: /* HACK: errno set already? */
729  default:
730 bottom:
731 /*@-evalorderuncon@*/
732 if (_dav_debug)
733 fprintf(stderr, "*** Connect to %s:%d failed(%d):\n\t%s\n",
734  u->host, u->port, rc, ne_get_error(u->sess));
735 /*@=evalorderuncon@*/
736  break;
737  }
738 
739  /* HACK: sensitive to error returns? */
740  u->httpVersion = (ne_version_pre_http11(u->sess) ? 0 : 1);
741 
742  return rc;
743 }
744 
745 static int davInit(const char * url, urlinfo * uret)
746  /*@globals internalState @*/
747  /*@modifies *uret, internalState @*/
748 {
749  urlinfo u = NULL;
750  int rc = 0;
751 
752 /*@-globs@*/ /* FIX: h_errno annoyance. */
753  if (urlSplit(url, &u))
754  return -1; /* XXX error returns needed. */
755 /*@=globs@*/
756 
757  if (u->url != NULL && u->sess == NULL)
758  switch (u->urltype) {
759  default:
760  assert(u->urltype != u->urltype);
761  /*@notreached@*/ break;
762  case URL_IS_HTTPS:
763  case URL_IS_HTTP:
764  case URL_IS_HKP:
765  { ne_server_capabilities * capabilities;
766 
767  /* HACK: oneshots should be done Somewhere Else Instead. */
768 /*@-noeffect@*/
769  rc = ((_dav_debug < 0) ? NE_DBG_HTTP : 0);
770  ne_debug_init(stderr, rc); /* XXX oneshot? */
771 /*@=noeffect@*/
772  rc = ne_sock_init(); /* XXX oneshot? */
773 
774  u->lockstore = ne_lockstore_create(); /* XXX oneshot? */
775 
776  u->capabilities = capabilities = xcalloc(1, sizeof(*capabilities));
777  u->sess = ne_session_create(u->scheme, u->host, u->port);
778 
779  ne_lockstore_register(u->lockstore, u->sess);
780 
781  if (u->proxyh != NULL)
782  ne_session_proxy(u->sess, u->proxyh, u->proxyp);
783 
784 #if 0
785  { const ne_inet_addr ** addrs;
786  unsigned int n;
787  ne_set_addrlist(u->sess, addrs, n);
788  }
789 #endif
790 
791  ne_set_progress(u->sess, davProgress, u);
792 #if WITH_NEON_MIN_VERSION >= 0x002700
793  ne_set_notifier(u->sess, davNotify, u);
794 #else
795  ne_set_status(u->sess, davNotify, u);
796 #endif
797 
798 #if WITH_NEON_MIN_VERSION >= 0x002600
799  ne_set_session_flag(u->sess, NE_SESSFLAG_PERSIST, rpmioHttpPersist);
800 #else
801  ne_set_persist(u->sess, rpmioHttpPersist);
802 #endif
803  ne_set_read_timeout(u->sess, rpmioHttpReadTimeoutSecs);
804  ne_set_useragent(u->sess,
806 
807  /* XXX check that neon is ssl enabled. */
808  if (!strcasecmp(u->scheme, "https"))
809  ne_ssl_set_verify(u->sess, davVerifyCert, (char *)u->host);
810 
811  ne_set_session_private(u->sess, "urlinfo", u);
812 
813  ne_hook_destroy_session(u->sess, davDestroySession, u);
814 
815  ne_hook_create_request(u->sess, davCreateRequest, u);
816  ne_hook_pre_send(u->sess, davPreSend, u);
817  ne_hook_post_send(u->sess, davPostSend, u);
818  ne_hook_destroy_request(u->sess, davDestroyRequest, u);
819 
820  /* HACK: where should server capabilities be read? */
821  rc = davConnect(u);
822  if (rc)
823  goto exit;
824  } break;
825  }
826 
827 exit:
828  if (uret != NULL)
829  *uret = urlLink(u, "davInit");
830  u = urlFree(u, "urlSplit (davInit)");
831 
832  return rc;
833 }
834 
835 /* =============================================================== */
836 enum fetch_rtype_e {
837  resr_normal = 0,
838  resr_collection,
839  resr_reference,
840  resr_error
841 };
842 
843 struct fetch_resource_s {
844 /*@dependent@*/
845  struct fetch_resource_s *next;
846  char *uri;
847 /*@unused@*/
848  char *displayname;
849  enum fetch_rtype_e type;
850  size_t size;
851  time_t modtime;
852  int is_executable;
853  int is_vcr; /* Is version resource. 0: no vcr, 1 checkin 2 checkout */
854  char *error_reason; /* error string returned for this resource */
855  int error_status; /* error status returned for this resource */
856 };
857 
858 /*@null@*/
859 static void *fetch_destroy_item(/*@only@*/ struct fetch_resource_s *res)
860  /*@modifies res @*/
861 {
862  ne_free(res->uri);
863  ne_free(res->error_reason);
864  res = _free(res);
865  return NULL;
866 }
867 
868 #ifdef NOTUSED
869 /*@null@*/
870 static void *fetch_destroy_list(/*@only@*/ struct fetch_resource_s *res)
871  /*@modifies res @*/
872 {
873  struct fetch_resource_s *next;
874  for (; res != NULL; res = next) {
875  next = res->next;
876  res = fetch_destroy_item(res);
877  }
878  return NULL;
879 }
880 #endif
881 
882 #if WITH_NEON_MIN_VERSION >= 0x002600
883 static void *fetch_create_item(/*@unused@*/ void *userdata, /*@unused@*/ const ne_uri *uri)
884 #else
885 static void *fetch_create_item(/*@unused@*/ void *userdata, /*@unused@*/ const char *uri)
886 #endif
887  /*@*/
888 {
889  struct fetch_resource_s * res = ne_calloc(sizeof(*res));
890  return res;
891 }
892 
893 /* =============================================================== */
894 
895 /*@-nullassign -readonlytrans@*/
896 /*@unchecked@*/ /*@observer@*/
897 static const ne_propname fetch_props[] = {
898  { "DAV:", "getcontentlength" },
899  { "DAV:", "getlastmodified" },
900  { "http://apache.org/dav/props/", "executable" },
901  { "DAV:", "resourcetype" },
902  { "DAV:", "checked-in" },
903  { "DAV:", "checked-out" },
904  { NULL, NULL }
905 };
906 /*@=nullassign =readonlytrans@*/
907 
908 #define ELM_resourcetype (NE_PROPS_STATE_TOP + 1)
909 #define ELM_collection (NE_PROPS_STATE_TOP + 2)
910 
911 /*@-readonlytrans@*/
912 /*@unchecked@*/ /*@observer@*/
913 static const struct ne_xml_idmap fetch_idmap[] = {
914  { "DAV:", "resourcetype", ELM_resourcetype },
915  { "DAV:", "collection", ELM_collection }
916 };
917 /*@=readonlytrans@*/
918 
919 static int fetch_startelm(void *userdata, int parent,
920  const char *nspace, const char *name,
921  /*@unused@*/ const char **atts)
922  /*@*/
923 {
924  ne_propfind_handler *pfh = userdata;
925  struct fetch_resource_s *r = ne_propfind_current_private(pfh);
926 /*@-sizeoftype@*/
927  int state = ne_xml_mapid(fetch_idmap, NE_XML_MAPLEN(fetch_idmap),
928  nspace, name);
929 /*@=sizeoftype@*/
930 
931  if (r == NULL ||
932  !((parent == NE_207_STATE_PROP && state == ELM_resourcetype) ||
933  (parent == ELM_resourcetype && state == ELM_collection)))
934  return NE_XML_DECLINE;
935 
936  if (state == ELM_collection) {
937  r->type = resr_collection;
938  }
939 
940  return state;
941 }
942 
943 static int fetch_compare(const struct fetch_resource_s *r1,
944  const struct fetch_resource_s *r2)
945  /*@*/
946 {
947  /* Sort errors first, then collections, then alphabetically */
948  if (r1->type == resr_error) {
949  return -1;
950  } else if (r2->type == resr_error) {
951  return 1;
952  } else if (r1->type == resr_collection) {
953  if (r2->type != resr_collection) {
954  return -1;
955  } else {
956  return strcmp(r1->uri, r2->uri);
957  }
958  } else {
959  if (r2->type != resr_collection) {
960  return strcmp(r1->uri, r2->uri);
961  } else {
962  return 1;
963  }
964  }
965 }
966 
967 #if WITH_NEON_MIN_VERSION >= 0x002600
968 static void fetch_results(void *userdata, const ne_uri *uarg,
969  const ne_prop_result_set *set)
970 #else
971 static void fetch_results(void *userdata, void *uarg,
972  const ne_prop_result_set *set)
973 #endif
974  /*@*/
975 {
976  avContext ctx = userdata;
977  struct fetch_resource_s *current, *previous, *newres;
978  const char *clength, *modtime, *isexec;
979  const char *checkin, *checkout;
980  const ne_status *status = NULL;
981  const char * path = NULL;
982 
983 #if WITH_NEON_MIN_VERSION >= 0x002600
984  const ne_uri * uri = uarg;
985  (void) urlPath(uri->path, &path);
986 #else
987  const char * uri = uarg;
988  (void) urlPath(uri, &path);
989 #endif
990  if (path == NULL)
991  return;
992 
993  newres = ne_propset_private(set);
994 
995 if (_dav_debug < 0)
996 fprintf(stderr, "==> %s in uri %s\n", path, ctx->uri);
997 
998  if (ne_path_compare(ctx->uri, path) == 0) {
999  /* This is the target URI */
1000 if (_dav_debug < 0)
1001 fprintf(stderr, "==> %s skipping target resource.\n", path);
1002  /* Free the private structure. */
1003 /*@-dependenttrans -exposetrans@*/
1004  free(newres);
1005 /*@=dependenttrans =exposetrans@*/
1006  return;
1007  }
1008 
1009  newres->uri = ne_strdup(path);
1010 
1011  clength = ne_propset_value(set, &fetch_props[0]);
1012  modtime = ne_propset_value(set, &fetch_props[1]);
1013  isexec = ne_propset_value(set, &fetch_props[2]);
1014  checkin = ne_propset_value(set, &fetch_props[4]);
1015  checkout = ne_propset_value(set, &fetch_props[5]);
1016 
1017  if (clength == NULL)
1018  status = ne_propset_status(set, &fetch_props[0]);
1019  if (modtime == NULL)
1020  status = ne_propset_status(set, &fetch_props[1]);
1021 
1022  if (newres->type == resr_normal && status != NULL) {
1023  /* It's an error! */
1024  newres->error_status = status->code;
1025 
1026  /* Special hack for Apache 1.3/mod_dav */
1027  if (strcmp(status->reason_phrase, "status text goes here") == 0) {
1028  const char *desc;
1029  if (status->code == 401) {
1030  desc = _("Authorization Required");
1031  } else if (status->klass == 3) {
1032  desc = _("Redirect");
1033  } else if (status->klass == 5) {
1034  desc = _("Server Error");
1035  } else {
1036  desc = _("Unknown Error");
1037  }
1038  newres->error_reason = ne_strdup(desc);
1039  } else {
1040  newres->error_reason = ne_strdup(status->reason_phrase);
1041  }
1042  newres->type = resr_error;
1043  }
1044 
1045  if (isexec && strcasecmp(isexec, "T") == 0) {
1046  newres->is_executable = 1;
1047  } else {
1048  newres->is_executable = 0;
1049  }
1050 
1051  if (modtime)
1052  newres->modtime = ne_httpdate_parse(modtime);
1053 
1054  if (clength)
1055  newres->size = atoi(clength);
1056 
1057  /* is vcr */
1058  if (checkin) {
1059  newres->is_vcr = 1;
1060  } else if (checkout) {
1061  newres->is_vcr = 2;
1062  } else {
1063  newres->is_vcr = 0;
1064  }
1065 
1066  current = *(struct fetch_resource_s **)ctx->resrock;
1067  for (current = *ctx->resrock, previous = NULL; current != NULL;
1068  previous = current, current = current->next)
1069  {
1070  if (fetch_compare(current, newres) >= 0) {
1071  break;
1072  }
1073  }
1074  if (previous) {
1075  previous->next = newres;
1076  } else {
1077 /*@-dependenttrans @*/
1078  *(struct fetch_resource_s **)ctx->resrock = newres;
1079 /*@=dependenttrans @*/
1080  }
1081  newres->next = current;
1082 }
1083 
1084 static int davFetch(const urlinfo u, avContext ctx)
1085  /*@globals internalState @*/
1086  /*@modifies ctx, internalState @*/
1087 {
1088  const char * path = NULL;
1089  int depth = 1; /* XXX passed arg? */
1090  struct fetch_resource_s * resitem = NULL;
1091  ne_propfind_handler *pfh;
1092  struct fetch_resource_s *current, *next;
1093  mode_t st_mode;
1094  int rc = 0;
1095  int xx;
1096 
1097  (void) urlPath(u->url, &path);
1098  pfh = ne_propfind_create(u->sess, ctx->uri, depth);
1099 
1100  /* HACK: need to set RPMURL_SERVER_HASRANGE in u->allow here. */
1101 
1102  ctx->resrock = (void **) &resitem;
1103 
1104  ne_xml_push_handler(ne_propfind_get_parser(pfh),
1105  fetch_startelm, NULL, NULL, pfh);
1106 
1107  ne_propfind_set_private(pfh, fetch_create_item, NULL);
1108 
1109  rc = ne_propfind_named(pfh, fetch_props, fetch_results, ctx);
1110 
1111  ne_propfind_destroy(pfh);
1112 
1113  for (current = resitem; current != NULL; current = next) {
1114  const char *s, *se;
1115  char * val;
1116 
1117  next = current->next;
1118 
1119  /* Collections have trailing '/' that needs trim. */
1120  /* The top level collection is returned as well. */
1121  se = current->uri + strlen(current->uri);
1122  if (se[-1] == '/') {
1123  if (strlen(current->uri) <= strlen(path)) {
1124  current = fetch_destroy_item(current);
1125  continue;
1126  }
1127  se--;
1128  }
1129  s = se;
1130  while (s > current->uri && s[-1] != '/')
1131  s--;
1132 
1133  val = ne_strndup(s, (se - s));
1134 
1135 /*@-nullpass@*/
1136  val = ne_path_unescape(val);
1137 /*@=nullpass@*/
1138 
1139  switch (current->type) {
1140  case resr_normal:
1141  st_mode = S_IFREG;
1142  /*@switchbreak@*/ break;
1143  case resr_collection:
1144  st_mode = S_IFDIR;
1145  /*@switchbreak@*/ break;
1146  case resr_reference:
1147  case resr_error:
1148  default:
1149  st_mode = 0;
1150  /*@switchbreak@*/ break;
1151  }
1152 
1153  xx = avContextAdd(ctx, val, st_mode, current->size, current->modtime);
1154  ne_free(val);
1155 
1156  current = fetch_destroy_item(current);
1157  }
1158  ctx->resrock = NULL; /* HACK: avoid leaving stack reference. */
1159  /* HACK realloc to truncate modes/sizes/mtimes */
1160 
1161  return rc;
1162 }
1163 
1164 /* HACK davHEAD() should be rewritten to use davReq/davResp w callbacks. */
1165 static int davHEAD(urlinfo u, struct stat *st)
1166  /*@modifies u, *st @*/
1167 {
1168  ne_request *req;
1169  const ne_status *status = NULL;
1170  const char *htag;
1171  const char *value = NULL;
1172  int rc;
1173 int printing = 0;
1174 
1175  /* XXX HACK: URI's with pesky trailing '/' are directories. */
1176  { size_t nb = strlen(u->url);
1177  st->st_mode = (u->url[nb-1] == '/' ? S_IFDIR : S_IFREG);
1178  }
1179  st->st_blksize = 4 * 1024; /* HACK correct for linux ext */
1180  st->st_size = -1;
1181  st->st_atime = -1;
1182  st->st_mtime = -1;
1183  st->st_ctime = -1;
1184 
1185  req = ne_request_create(u->sess, "HEAD", u->url);
1186  if (rpmioHttpAccept != NULL)
1187  ne_add_request_header(req, "Accept", rpmioHttpAccept);
1188 
1189  /* XXX if !defined(HAVE_NEON_NE_GET_RESPONSE_HEADER) handlers? */
1190 
1191  rc = ne_request_dispatch(req);
1192  status = ne_get_status(req);
1193 
1194 /* XXX somewhere else instead? */
1195 if (_dav_debug) {
1196 fprintf(stderr, "HTTP request sent, awaiting response... %d %s\n", status->code, status->reason_phrase);
1197 }
1198 
1199  switch (rc) {
1200  default:
1201  goto exit;
1202  /*@notreached@*/ break;
1203  case NE_OK:
1204  if (status->klass != 2) /* XXX is this necessary? */
1205  rc = NE_ERROR;
1206  break;
1207  }
1208 
1209 #if defined(HAVE_NEON_NE_GET_RESPONSE_HEADER)
1210  htag = "ETag";
1211  value = ne_get_response_header(req, htag);
1212  if (value) {
1213  /* inode-size-mtime */
1214  u->etag = _free(u->etag);
1215  u->etag = xstrdup(value);
1216  }
1217 
1218  /* XXX limit to 3xx returns? */
1219  htag = "Location";
1220  value = ne_get_response_header(req, htag);
1221  if (value) {
1222  u->location = _free(u->location);
1223  u->location = xstrdup(value);
1224  }
1225 
1226  htag = "Content-Length";
1227  value = ne_get_response_header(req, htag);
1228  if (value) {
1229 /* XXX should wget's "... (1.2K)..." be added? */
1230 if (_dav_debug && ++printing)
1231 fprintf(stderr, "Length: %s", value);
1232 
1233 /*@-unrecog@*/ /* XXX LCLINT needs stdlib.h update. */
1234  st->st_size = strtoll(value, NULL, 10);
1235 /*@=unrecog@*/
1236  st->st_blocks = (st->st_size + 511)/512;
1237  }
1238 
1239  htag = "Content-Type";
1240  value = ne_get_response_header(req, htag);
1241  if (value) {
1242 if (_dav_debug && printing)
1243 fprintf(stderr, " [%s]", value);
1244  if (!strcmp(value, "text/html")
1245  || !strcmp(value, "application/xhtml+xml"))
1246  st->st_blksize = 2 * 1024;
1247  }
1248 
1249  htag = "Last-Modified";
1250  value = ne_get_response_header(req, htag);
1251  if (value) {
1252 if (_dav_debug && printing)
1253 fprintf(stderr, " [%s]", value);
1254  st->st_mtime = ne_httpdate_parse(value);
1255  st->st_atime = st->st_ctime = st->st_mtime; /* HACK */
1256  }
1257 
1258 if (_dav_debug && printing)
1259 fprintf(stderr, "\n");
1260 #endif
1261 
1262 exit:
1263  ne_request_destroy(req);
1264  return rc;
1265 }
1266 
1267 static int my_result(const char * msg, int ret, /*@null@*/ FILE * fp)
1268  /*@modifies *fp @*/
1269 {
1270  /* HACK: don't print unless debugging. */
1271  if (_dav_debug >= 0)
1272  return ret;
1273  if (fp == NULL)
1274  fp = stderr;
1275  if (msg != NULL)
1276  fprintf(fp, "*** %s: ", msg);
1277 
1278  /* HACK FTPERR_NE_FOO == -NE_FOO error impedance match */
1279 #ifdef HACK
1280  fprintf(fp, "%s: %s\n", ftpStrerror(-ret), ne_get_error(sess));
1281 #else
1282  fprintf(fp, "%s\n", ftpStrerror(-ret));
1283 #endif
1284  return ret;
1285 }
1286 
1287 /* XXX TODO move to rpmhtml.c */
1290 typedef struct rpmhtml_s * rpmhtml;
1291 
1294 struct rpmhtml_s {
1295 /*@kept@*/
1296  avContext ctx;
1297  ne_request *req;
1298 
1299 /*@observer@*/
1300  const char * pattern;
1301 /*@relnull@*/
1302  miRE mires;
1303  int nmires;
1304 
1305  char * buf;
1306  size_t nbuf;
1307 /*@null@*/
1308  char * b;
1309  size_t nb;
1310 };
1311 
1314 static /*@null@*/
1315 rpmhtml htmlFree(/*@only@*/ rpmhtml html)
1316  /*@modifies html @*/
1317 {
1318  if (html != NULL) {
1319  if (html->req != NULL) {
1320  ne_request_destroy(html->req);
1321  html->req = NULL;
1322  }
1323  html->buf = _free(html->buf);
1324  html->nbuf = 0;
1325  html->ctx = NULL;
1326  }
1327  return NULL;
1328 }
1329 
1332 static
1333 rpmhtml htmlNew(urlinfo u, /*@kept@*/ avContext ctx)
1334  /*@*/
1335 {
1336  rpmhtml html = xcalloc(1, sizeof(*html));
1337  html->ctx = ctx;
1338  html->nbuf = BUFSIZ; /* XXX larger buffer? */
1339  html->buf = xmalloc(html->nbuf + 1 + 1);
1340  html->req = ne_request_create(u->sess, "GET", u->url);
1341  return html;
1342 }
1343 
1346 static ssize_t htmlFill(rpmhtml html)
1347  /*@modifies html @*/
1348 {
1349  char * b = html->buf;
1350  size_t nb = html->nbuf;
1351  ssize_t rc;
1352 
1353 if (_dav_debug < 0)
1354 fprintf(stderr, "*** htmlFill(%p) %p[%u]\n", html, b, (unsigned)nb);
1355  if (html->b != NULL && html->nb > 0 && html->b > html->buf) {
1356  memmove(html->buf, html->b, html->nb);
1357  b += html->nb;
1358  nb -= html->nb;
1359  }
1360 
1361  /* XXX FIXME: "server awol" segfaults here. gud enuf atm ... */
1362  rc = ne_read_response_block(html->req, b, nb) ;
1363  if (rc > 0)
1364  html->nb += rc;
1365  html->b = html->buf;
1366 
1367  return rc;
1368 }
1369 
1375 static
1376 unsigned char nibble(char c)
1377  /*@*/
1378 {
1379  if (c >= '0' && c <= '9')
1380  return (unsigned char) (c - '0');
1381  if (c >= 'A' && c <= 'F')
1382  return (unsigned char)((int)(c - 'A') + 10);
1383  if (c >= 'a' && c <= 'f')
1384  return (unsigned char)((int)(c - 'a') + 10);
1385  return (unsigned char) '\0';
1386 }
1387 
1388 /*@observer@*/
1389 static const char * hrefpat = "(?i)<a(?:\\s+[a-z][a-z0-9_]*(?:=(?:\"[^\"]*\"|\\S+))?)*?\\s+href=(?:\"([^\"]*)\"|(\\S+))";
1390 
1393 static int htmlParse(rpmhtml html)
1394  /*@globals hrefpat, internalState @*/
1395  /*@modifies html, internalState @*/
1396 {
1397  miRE mire;
1398  int noffsets = 3;
1399  int offsets[3];
1400  ssize_t nr = (html->b != NULL ? (ssize_t)html->nb : htmlFill(html));
1401  int rc = 0;
1402  int xx;
1403 
1404 if (_dav_debug < 0)
1405 fprintf(stderr, "*** htmlParse(%p) %p[%u]\n", html, html->buf, (unsigned)html->nbuf);
1406  html->pattern = hrefpat;
1407  xx = mireAppend(RPMMIRE_PCRE, 0, html->pattern, NULL, &html->mires, &html->nmires);
1408  mire = html->mires;
1409 
1410  xx = mireSetEOptions(mire, offsets, noffsets);
1411 
1412  while (html->nb > 0) {
1413  char * gbn, * href;
1414  const char * hbn, * lpath;
1415  char * f, * fe;
1416  char * g, * ge;
1417  size_t ng;
1418  char * h, * he;
1419  size_t nh;
1420  char * t;
1421  mode_t st_mode;
1422  int ut;
1423 
1424 assert(html->b != NULL);
1425  offsets[0] = offsets[1] = -1;
1426  xx = mireRegexec(mire, html->b, html->nb);
1427  if (xx == 0 && offsets[0] != -1 && offsets[1] != -1) {
1428 
1429  /* [f:fe) contains |<a href="..."| match. */
1430  f = html->b + offsets[0];
1431  fe = html->b + offsets[1];
1432 
1433  he = fe;
1434  if (he[-1] == '"') he--;
1435  h = he;
1436  while (h > f && h[-1] != '"')
1437  h--;
1438  /* [h:he) contains the href. */
1439  nh = (size_t)(he - h);
1440  href = t = xmalloc(nh + 1 + 1);
1441  while (h < he) {
1442  char c = *h++;
1443  switch (c) {
1444  default:
1445  /*@switchbreak@*/ break;
1446  case '%':
1447  if (isxdigit((int)h[0]) && isxdigit((int)h[1])) {
1448  c = (char) (nibble(h[0]) << 4) | nibble(h[1]);
1449  h += 2;
1450  }
1451  /*@switchbreak@*/ break;
1452  }
1453  *t++ = c;
1454  }
1455  *t = '\0';
1456 
1457  /* Determine type of href. */
1458  switch ((ut = urlPath(href, &lpath))) {
1459  case URL_IS_UNKNOWN:
1460  default:
1461  /* XXX verify "same tree" as root URI. */
1462  if (href[nh-1] == '/') {
1463  st_mode = S_IFDIR | 0755;
1464  href[nh-1] = '\0';
1465  } else
1466  st_mode = S_IFREG | 0644;
1467  /*@switchbreak@*/ break;
1468  case URL_IS_FTP:
1469  case URL_IS_HTTPS:
1470  case URL_IS_HTTP:
1471 #ifdef NOTYET /* XXX avContext needs to save linktos first. */
1472  st_mode = S_IFLNK | 0755;
1473  /*@switchbreak@*/ break;
1474 #endif
1475  case URL_IS_PATH:
1476  case URL_IS_DASH:
1477  case URL_IS_HKP:
1478  href[0] = '\0';
1479  /*@switchbreak@*/ break;
1480  }
1481  if ((hbn = strrchr(href, '/')) != NULL)
1482  hbn++;
1483  else
1484  hbn = href;
1485 assert(hbn != NULL);
1486 
1487  /* Parse the URI path. */
1488  g = fe;
1489  while (*g != '>')
1490  g++;
1491  ge = ++g;
1492  while (*ge != '<')
1493  ge++;
1494  /* [g:ge) contains the URI basename. */
1495  ng = (size_t)(ge - g);
1496  gbn = t = xmalloc(ng + 1 + 1);
1497  while (g < ge && *g != '/') /* XXX prohibit '/' in gbn. */
1498  *t++ = *g++;
1499  *t = '\0';
1500 
1501 if (_dav_debug)
1502 if (*hbn != '\0' && *gbn != '\0' && strcasecmp(hbn, gbn))
1503 fprintf(stderr, "\t[%s] != [%s]\n", hbn, gbn);
1504 
1505  /*
1506  * Heuristics to identify HTML sub-directories:
1507  * Avoid empty strings.
1508  * Both "." and ".." will be added by avContext.
1509  *
1510  * Assume (case insensitive) basename(href) == basename(URI) is
1511  * a subdirectory.
1512  */
1513  if (*hbn != '\0' && *gbn != '\0')
1514  if (strcmp(hbn, ".") && strcmp(hbn, ".."))
1515  if (!strcasecmp(hbn, gbn)) {
1516  size_t _st_size = (size_t)0; /* XXX HACK */
1517  time_t _st_mtime = (time_t)0; /* XXX HACK */
1518  xx = avContextAdd(html->ctx, gbn, st_mode, _st_size, _st_mtime);
1519  }
1520 
1521  gbn = _free(gbn);
1522  href = _free(href);
1523 
1524  offsets[1] += (ge - fe);
1525  html->b += offsets[1];
1526  html->nb -= offsets[1];
1527  } else {
1528  size_t nb = html->nb;
1529  if (nr > 0) nb -= 128; /* XXX overlap a bit if filling. */
1530  html->b += nb;
1531  html->nb -= nb;
1532  }
1533 
1534  if (nr > 0)
1535  nr = htmlFill(html);
1536  }
1537 
1538  xx = mireSetEOptions(mire, NULL, 0);
1539 
1540  html->mires = mireFreeAll(html->mires, html->nmires);
1541  html->nmires = 0;
1542 
1543 if (_dav_debug < 0)
1544 fprintf(stderr, "*** htmlParse(%p) rc %d\n", html, rc);
1545  return rc;
1546 }
1547 
1548 /* HACK htmlNLST() should be rewritten to use davReq/davResp w callbacks. */
1549 /*@-mustmod@*/
1550 static int htmlNLST(urlinfo u, avContext ctx)
1551  /*@globals hrefpat, internalState @*/
1552  /*@modifies ctx, internalState @*/
1553 {
1554  rpmhtml html = htmlNew(u, ctx);
1555  int rc = 0;
1556 
1557 if (_dav_debug < 0)
1558 fprintf(stderr, "*** htmlNLST(%p, %p) html %p\n", u, ctx, html);
1559 
1560  do {
1561  rc = ne_begin_request(html->req);
1562  rc = my_result("ne_begin_req(html->req)", rc, NULL);
1563  if (rc != NE_OK) goto exit;
1564 
1565  (void) htmlParse(html); /* XXX error code needs handling. */
1566 
1567  rc = ne_end_request(html->req);
1568  rc = my_result("ne_end_req(html->req)", rc, NULL);
1569  } while (rc == NE_RETRY);
1570 
1571 exit:
1572  html = htmlFree(html);
1573  return rc;
1574 }
1575 /*@=mustmod@*/
1576 
1577 static int davNLST(avContext ctx)
1578  /*@globals hrefpat, internalState @*/
1579  /*@modifies ctx, internalState @*/
1580 {
1581  urlinfo u = NULL;
1582  int rc;
1583  int xx;
1584 
1585 retry:
1586  rc = davInit(ctx->uri, &u);
1587  if (rc || u == NULL)
1588  goto exit;
1589 
1590  /*
1591  * Do PROPFIND through davFetch iff server supports.
1592  * Otherwise, do HEAD to get Content-length/ETag/Last-Modified,
1593  * followed by GET through htmlNLST() to find the contained href's.
1594  */
1595  if (u->allow & RPMURL_SERVER_HASDAV)
1596  rc = davFetch(u, ctx); /* use PROPFIND to get contentLength */
1597  else {
1598 /*@-nullpass@*/ /* XXX annotate ctx->st correctly */
1599  rc = davHEAD(u, ctx->st); /* use HEAD to get contentLength */
1600 /*@=nullpass@*/
1601  /* Parse directory elements. */
1602  if (rc == NE_OK && S_ISDIR(ctx->st->st_mode))
1603  rc = htmlNLST(u, ctx);
1604  }
1605 
1606  switch (rc) {
1607  case NE_OK:
1608  break;
1609  case NE_ERROR:
1610  /* HACK: "405 Method Not Allowed" for PROPFIND on non-DAV servers. */
1611  /* XXX #206066 OPTIONS is ok, but PROPFIND from Stat() fails. */
1612  /* rpm -qp --rpmiodebug --davdebug http://people.freedesktop.org/~sandmann/metacity-2.16.0-2.fc6/i386/metacity-2.16.0-2.fc6.i386.rpm */
1613 
1614  /* HACK: "301 Moved Permanently" on empty subdir. */
1615  if (!strncmp("301 ", ne_get_error(u->sess), sizeof("301 ")-1))
1616  break;
1617 
1618  /* HACK: "302 Found" if URI is missing pesky trailing '/'. */
1619  if (!strncmp("302 ", ne_get_error(u->sess), sizeof("302 ")-1)) {
1620  const char * path = NULL;
1621  int ut = urlPath(u->url, &path);
1622  size_t nb = strlen(path);
1623  ut = ut; /* XXX keep gcc happy */
1624  if (u->location != NULL && !strncmp(path, u->location, nb)
1625  && u->location[nb] == '/' && u->location[nb+1] == '\0')
1626  {
1627  char * te = strchr(u->url, '\0');
1628  /* Append the pesky trailing '/'. */
1629  if (te != NULL && te[-1] != '/') {
1630  /* XXX u->uri malloc'd w room for +1b */
1631  *te++ = '/';
1632  *te = '\0';
1633  u->location = _free(u->location);
1634  /* XXX retry here needed iff ContentLength:. */
1635  xx = davFree(u);
1636  goto retry;
1637  /*@notreached@*/ break;
1638  }
1639  }
1640  }
1641  /*@fallthrough@*/
1642  default:
1643 /*@-evalorderuncon@*/
1644 if (_dav_debug)
1645 fprintf(stderr, "*** Fetch from %s:%d failed:\n\t%s\n",
1646  u->host, u->port, ne_get_error(u->sess));
1647 /*@=evalorderuncon@*/
1648  break;
1649  }
1650 
1651 exit:
1652  xx = davFree(u);
1653  return rc;
1654 }
1655 
1656 /* =============================================================== */
1657 /*@-mustmod@*/
1658 static void davAcceptRanges(void * userdata, /*@null@*/ const char * value)
1659  /*@modifies userdata @*/
1660 {
1661  urlinfo u = userdata;
1662 
1663  if (!(u != NULL && value != NULL)) return;
1664 if (_dav_debug < 0)
1665 fprintf(stderr, "*** u %p Accept-Ranges: %s\n", u, value);
1666  if (!strcmp(value, "bytes"))
1668  if (!strcmp(value, "none"))
1670 }
1671 /*@=mustmod@*/
1672 
1673 #if !defined(HAVE_NEON_NE_GET_RESPONSE_HEADER)
1674 static void davAllHeaders(void * userdata, const char * value)
1675 {
1676  FD_t ctrl = userdata;
1677 
1678  if (!(ctrl != NULL && value != NULL)) return;
1679 if (_dav_debug)
1680 fprintf(stderr, "<- %s\n", value);
1681 }
1682 #endif
1683 
1684 /*@-mustmod@*/
1685 static void davContentLength(void * userdata, /*@null@*/ const char * value)
1686  /*@modifies userdata @*/
1687 {
1688  FD_t ctrl = userdata;
1689 
1690  if (!(ctrl != NULL && value != NULL)) return;
1691 if (_dav_debug < 0)
1692 fprintf(stderr, "*** fd %p Content-Length: %s\n", ctrl, value);
1693 /*@-unrecog@*/
1694  ctrl->contentLength = strtoll(value, NULL, 10);
1695 /*@=unrecog@*/
1696 }
1697 /*@=mustmod@*/
1698 
1699 /*@-mustmod@*/
1700 static void davContentType(void * userdata, /*@null@*/ const char * value)
1701  /*@modifies userdata @*/
1702 {
1703  FD_t ctrl = userdata;
1704 
1705  if (!(ctrl != NULL && value != NULL)) return;
1706 if (_dav_debug < 0)
1707 fprintf(stderr, "*** fd %p Content-Type: %s\n", ctrl, value);
1708  ctrl->contentType = _free(ctrl->contentType);
1709  ctrl->contentType = xstrdup(value);
1710 }
1711 /*@=mustmod@*/
1712 
1713 /*@-mustmod@*/
1714 static void davContentDisposition(void * userdata, /*@null@*/ const char * value)
1715  /*@modifies userdata @*/
1716 {
1717  FD_t ctrl = userdata;
1718 
1719  if (!(ctrl != NULL && value != NULL)) return;
1720 if (_dav_debug < 0)
1721 fprintf(stderr, "*** fd %p Content-Disposition: %s\n", ctrl, value);
1723  ctrl->contentDisposition = xstrdup(value);
1724 }
1725 /*@=mustmod@*/
1726 
1727 /*@-mustmod@*/
1728 static void davLastModified(void * userdata, /*@null@*/ const char * value)
1729  /*@modifies userdata @*/
1730 {
1731  FD_t ctrl = userdata;
1732 
1733  if (!(ctrl != NULL && value != NULL)) return;
1734 if (_dav_debug < 0)
1735 fprintf(stderr, "*** fd %p Last-Modified: %s\n", ctrl, value);
1736 /*@-unrecog@*/
1737  ctrl->lastModified = ne_httpdate_parse(value);
1738 /*@=unrecog@*/
1739 }
1740 /*@=mustmod@*/
1741 
1742 /*@-mustmod@*/
1743 static void davConnection(void * userdata, /*@null@*/ const char * value)
1744  /*@modifies userdata @*/
1745 {
1746  FD_t ctrl = userdata;
1747 
1748  if (!(ctrl != NULL && value != NULL)) return;
1749 if (_dav_debug < 0)
1750 fprintf(stderr, "*** fd %p Connection: %s\n", ctrl, value);
1751  if (!strcasecmp(value, "close"))
1752  ctrl->persist = 0;
1753  else if (!strcasecmp(value, "Keep-Alive"))
1754  ctrl->persist = 1;
1755 }
1756 /*@=mustmod@*/
1757 
1758 /*@-mustmod@*/ /* HACK: stash error in *str. */
1759 int davResp(urlinfo u, FD_t ctrl, /*@unused@*/ char *const * str)
1760 {
1761  int rc = 0;
1762 
1763  rc = ne_begin_request(ctrl->req);
1764  rc = my_result("ne_begin_req(ctrl->req)", rc, NULL);
1765 
1766 if (_dav_debug < 0)
1767 fprintf(stderr, "*** davResp(%p,%p,%p) sess %p req %p rc %d\n", u, ctrl, str, u->sess, ctrl->req, rc);
1768 
1769  /* HACK FTPERR_NE_FOO == -NE_FOO error impedance match */
1770 /*@-observertrans@*/
1771  if (rc)
1772  fdSetSyserrno(ctrl, errno, ftpStrerror(-rc));
1773 /*@=observertrans@*/
1774 
1775  return rc;
1776 }
1777 /*@=mustmod@*/
1778 
1779 int davReq(FD_t ctrl, const char * httpCmd, const char * httpArg)
1780 {
1781  urlinfo u;
1782  int rc = 0;
1783 
1784 assert(ctrl != NULL);
1785  u = ctrl->url;
1786  URLSANE(u);
1787 
1788 if (_dav_debug < 0)
1789 fprintf(stderr, "*** davReq(%p,%s,\"%s\") entry sess %p req %p\n", ctrl, httpCmd, (httpArg ? httpArg : ""), u->sess, ctrl->req);
1790 
1791  ctrl->persist = (u->httpVersion > 0 ? 1 : 0);
1792  ctrl = fdLink(ctrl, "open ctrl (davReq)");
1793 assert(ctrl != NULL);
1794 
1795 assert(u->sess != NULL);
1796  /* XXX reset disconnected handle to NULL. should never happen ... */
1797  if (ctrl->req == (void *)-1)
1798  ctrl->req = NULL;
1799 /*@-nullderef@*/
1800 assert(ctrl->req == NULL);
1801 /*@=nullderef@*/
1802 /*@-nullpass@*/
1803  ctrl->req = ne_request_create(u->sess, httpCmd, httpArg);
1804 /*@=nullpass@*/
1805 assert(ctrl->req != NULL);
1806 
1807  ne_set_request_private(ctrl->req, "fd", ctrl);
1808 
1809 #if !defined(HAVE_NEON_NE_GET_RESPONSE_HEADER)
1810  ne_add_response_header_catcher(ctrl->req, davAllHeaders, ctrl);
1811 
1812  ne_add_response_header_handler(ctrl->req, "Content-Length",
1813  davContentLength, ctrl);
1814  ne_add_response_header_handler(ctrl->req, "Content-Type",
1815  davContentType, ctrl);
1816  ne_add_response_header_handler(ctrl->req, "Content-Disposition",
1817  davContentDisposition, ctrl);
1818  ne_add_response_header_handler(ctrl->req, "Last-Modified",
1819  davLastModified, ctrl);
1820  ne_add_response_header_handler(ctrl->req, "Connection",
1821  davConnection, ctrl);
1822 #endif
1823 
1824  if (!strcmp(httpCmd, "PUT")) {
1825 #if defined(HAVE_NEON_NE_SEND_REQUEST_CHUNK)
1826  ctrl->wr_chunked = 1;
1827  ne_add_request_header(ctrl->req, "Transfer-Encoding", "chunked");
1828  ne_set_request_chunked(ctrl->req, 1);
1829  /* HACK: no retries if/when chunking. */
1830  rc = davResp(u, ctrl, NULL);
1831 #else
1833 #endif
1834  } else {
1835  /* HACK: possible ETag: "inode-size-mtime" */
1836 #if !defined(HAVE_NEON_NE_GET_RESPONSE_HEADER)
1837  ne_add_response_header_handler(ctrl->req, "Accept-Ranges",
1838  davAcceptRanges, u);
1839 #endif
1840  /* HACK: possible Transfer-Encoding: on GET. */
1841 
1842  /* HACK: other errors may need retry too. */
1843  /* HACK: neon retries once, gud enuf. */
1844  /* HACK: retry counter? */
1845  do {
1846  rc = davResp(u, ctrl, NULL);
1847  } while (rc == NE_RETRY);
1848  }
1849 
1850 /* XXX somwhere else instead? */
1851 if (_dav_debug) {
1852  const ne_status *status = ne_get_status(ctrl->req);
1853 fprintf(stderr, "HTTP request sent, awaiting response... %d %s\n", status->code, status->reason_phrase);
1854 }
1855 
1856  if (rc)
1857  goto errxit;
1858 
1859 if (_dav_debug < 0)
1860 fprintf(stderr, "*** davReq(%p,%s,\"%s\") exit sess %p req %p rc %d\n", ctrl, httpCmd, (httpArg ? httpArg : ""), u->sess, ctrl->req, rc);
1861 
1862 #if defined(HAVE_NEON_NE_GET_RESPONSE_HEADER)
1863  davContentLength(ctrl,
1864  ne_get_response_header(ctrl->req, "Content-Length"));
1865  davContentType(ctrl,
1866  ne_get_response_header(ctrl->req, "Content-Type"));
1867  davContentDisposition(ctrl,
1868  ne_get_response_header(ctrl->req, "Content-Disposition"));
1869  davLastModified(ctrl,
1870  ne_get_response_header(ctrl->req, "Last-Modified"));
1871  davConnection(ctrl,
1872  ne_get_response_header(ctrl->req, "Connection"));
1873  if (strcmp(httpCmd, "PUT"))
1874  davAcceptRanges(u,
1875  ne_get_response_header(ctrl->req, "Accept-Ranges"));
1876 #endif
1877 
1878  ctrl = fdLink(ctrl, "open data (davReq)");
1879  return 0;
1880 
1881 errxit:
1882 /*@-observertrans@*/
1883  fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
1884 /*@=observertrans@*/
1885 
1886  /* HACK balance fd refs. ne_session_destroy to tear down non-keepalive? */
1887  ctrl = fdLink(ctrl, "error data (davReq)");
1888 
1889  return rc;
1890 }
1891 
1892 FD_t davOpen(const char * url, /*@unused@*/ int flags,
1893  /*@unused@*/ mode_t mode, /*@out@*/ urlinfo * uret)
1894 {
1895  const char * path = NULL;
1896  urltype urlType = urlPath(url, &path);
1897  urlinfo u = NULL;
1898  FD_t fd = NULL;
1899  int rc;
1900 
1901 #if 0 /* XXX makeTempFile() heartburn */
1902  assert(!(flags & O_RDWR));
1903 #endif
1904 
1905 if (_dav_debug < 0)
1906 fprintf(stderr, "*** davOpen(%s,0x%x,0%o,%p)\n", url, flags, (unsigned)mode, uret);
1907  rc = davInit(url, &u);
1908  if (rc || u == NULL || u->sess == NULL)
1909  goto exit;
1910 
1911  if (u->ctrl == NULL)
1912  u->ctrl = fdNew("persist ctrl (davOpen)");
1913  else {
1914  yarnLock use = u->ctrl->_item.use;
1915  yarnPossess(use);
1916  if (yarnPeekLock(use) > 2L && u->data == NULL)
1917  u->data = fdNew("persist data (davOpen)");
1918  yarnRelease(use);
1919  }
1920 
1921  if (u->ctrl->url == NULL)
1922  fd = u->ctrl = fdLink(u->ctrl, "grab ctrl (davOpen persist ctrl)");
1923  else if (u->data->url == NULL)
1924  fd = u->data = fdLink(u->data, "grab ctrl (davOpen persist data)");
1925  else
1926  fd = fdNew("grab ctrl (davOpen)");
1927 
1928  if (fd) {
1929  fdSetOpen(fd, url, flags, mode);
1930  fdSetIo(fd, ufdio);
1931 
1932  fd->ftpFileDoneNeeded = 0;
1934  fd->contentLength = fd->bytesRemain = -1;
1935 assert(urlType == URL_IS_HTTPS || urlType == URL_IS_HTTP || urlType == URL_IS_HKP);
1936  fd->urlType = urlType;
1937  fd->url = urlLink(u, "url (davOpen)");
1938  fd = fdLink(fd, "grab data (davOpen)");
1939  }
1940 
1941 exit:
1942  if (uret)
1943  *uret = u;
1944  /*@-refcounttrans@*/
1945  return fd;
1946  /*@=refcounttrans@*/
1947 }
1948 
1949 /*@-mustmod@*/
1950 ssize_t davRead(void * cookie, /*@out@*/ char * buf, size_t count)
1951 {
1952  FD_t fd = cookie;
1953  ssize_t rc;
1954 
1955 #if WITH_NEON_MIN_VERSION >= 0x002700
1956  { urlinfo u = NULL;
1957  u = urlLink(fd->url, "url (davRead)");
1958  if (u->info.status == ne_status_recving)
1959  rc = ne_read_response_block(fd->req, buf, count);
1960  else {
1961  /* If server has disconnected, then tear down the neon request. */
1962  if (u->info.status == ne_status_disconnected) {
1963  int xx;
1964  xx = ne_end_request(fd->req);
1965  xx = my_result("davRead: ne_end_request(req)", xx, NULL);
1966  ne_request_destroy(fd->req);
1967  fd->req = (void *)-1;
1968  }
1969  errno = EIO; /* XXX what to do? */
1970  rc = -1;
1971  }
1972  u = urlFree(u, "url (davRead)");
1973  }
1974 #else
1975  rc = ne_read_response_block(fd->req, buf, count);
1976 #endif
1977 
1978 if (_dav_debug < 0) {
1979 fprintf(stderr, "*** davRead(%p,%p,0x%x) rc 0x%x\n", cookie, buf, (unsigned)count, (unsigned)rc);
1980  }
1981 
1982  return rc;
1983 }
1984 /*@=mustmod@*/
1985 
1986 ssize_t davWrite(void * cookie, const char * buf, size_t count)
1987 {
1988 #if !defined(NEONBLOWSCHUNKS) || defined(HAVE_NEON_NE_SEND_REQUEST_CHUNK) || defined(__LCLINT__)
1989  FD_t fd = cookie;
1990 #endif
1991  ssize_t rc;
1992  int xx = -1;
1993 
1994 #if !defined(NEONBLOWSCHUNKS)
1995  ne_session * sess;
1996 
1997 assert(fd->req != NULL);
1998  sess = ne_get_session(fd->req);
1999 assert(sess != NULL);
2000 
2001  /* HACK: include ne_private.h to access sess->socket for now. */
2002  xx = ne_sock_fullwrite(sess->socket, buf, count);
2003 #else
2004 #if defined(HAVE_NEON_NE_SEND_REQUEST_CHUNK) || defined(__LCLINT__)
2005 assert(fd->req != NULL);
2006 /*@-unrecog@*/
2007  xx = ne_send_request_chunk(fd->req, buf, count);
2008 /*@=unrecog@*/
2009 #else
2010  errno = EIO; /* HACK */
2011  return -1;
2012 #endif
2013 #endif
2014 
2015  /* HACK: stupid error impedence matching. */
2016  rc = (xx == 0 ? (ssize_t)count : -1);
2017 
2018 if (_dav_debug < 0)
2019 fprintf(stderr, "*** davWrite(%p,%p,0x%x) rc 0x%x\n", cookie, buf, (unsigned)count, (unsigned)rc);
2020 
2021  return rc;
2022 }
2023 
2024 int davSeek(void * cookie, /*@unused@*/ _libio_pos_t pos, int whence)
2025 {
2026 if (_dav_debug < 0)
2027 fprintf(stderr, "*** davSeek(%p,pos,%d)\n", cookie, whence);
2028  return -1;
2029 }
2030 
2031 /*@-mustmod@*/ /* HACK: fd->req is modified. */
2032 int davClose(void * cookie)
2033 {
2034 /*@-onlytrans@*/
2035  FD_t fd = cookie;
2036 /*@=onlytrans@*/
2037  int rc = 0;
2038 
2039 assert(fd->req != NULL);
2040  if (fd->req != (void *)-1) {
2041  rc = ne_end_request(fd->req);
2042  rc = my_result("ne_end_request(req)", rc, NULL);
2043 
2044  ne_request_destroy(fd->req);
2045  }
2046  fd->req = NULL;
2047 
2048 if (_dav_debug < 0)
2049 fprintf(stderr, "*** davClose(%p) rc %d\n", fd, rc);
2050  return rc;
2051 }
2052 /*@=mustmod@*/
2053 
2054 /* =============================================================== */
2055 int davMkdir(const char * path, mode_t mode)
2056 {
2057  urlinfo u = NULL;
2058  const char * src = NULL;
2059  int rc;
2060 
2061  rc = davInit(path, &u);
2062  if (rc)
2063  goto exit;
2064 assert(u != NULL);
2065 
2066  (void) urlPath(path, &src);
2067 
2068  rc = ne_mkcol(u->sess, path);
2069 
2070  if (rc) rc = -1; /* XXX HACK: errno impedance match */
2071 
2072  /* XXX HACK: verify getrestype(remote) == resr_collection */
2073 
2074 exit:
2075 if (_dav_debug)
2076 fprintf(stderr, "*** davMkdir(%s,0%o) rc %d\n", path, (unsigned)mode, rc);
2077  return rc;
2078 }
2079 
2080 int davRmdir(const char * path)
2081 {
2082  urlinfo u = NULL;
2083  const char * src = NULL;
2084  int rc;
2085 
2086  rc = davInit(path, &u);
2087  if (rc)
2088  goto exit;
2089 assert(u != NULL);
2090 
2091  (void) urlPath(path, &src);
2092 
2093  /* XXX HACK: only getrestype(remote) == resr_collection */
2094 
2095  rc = ne_delete(u->sess, path);
2096 
2097  if (rc) rc = -1; /* XXX HACK: errno impedance match */
2098 
2099 exit:
2100 if (_dav_debug)
2101 fprintf(stderr, "*** davRmdir(%s) rc %d\n", path, rc);
2102  return rc;
2103 }
2104 
2105 int davRename(const char * oldpath, const char * newpath)
2106 {
2107  urlinfo u = NULL;
2108  const char * src = NULL;
2109  const char * dst = NULL;
2110  int overwrite = 1; /* HACK: set this correctly. */
2111  int rc;
2112 
2113  rc = davInit(oldpath, &u);
2114  if (rc)
2115  goto exit;
2116 assert(u != NULL);
2117 
2118  (void) urlPath(oldpath, &src);
2119  (void) urlPath(newpath, &dst);
2120 
2121  /* XXX HACK: only getrestype(remote) != resr_collection */
2122 
2123  rc = ne_move(u->sess, overwrite, src, dst);
2124 
2125  if (rc) rc = -1; /* XXX HACK: errno impedance match */
2126 
2127 exit:
2128 if (_dav_debug)
2129 fprintf(stderr, "*** davRename(%s,%s) rc %d\n", oldpath, newpath, rc);
2130  return rc;
2131 }
2132 
2133 int davUnlink(const char * path)
2134 {
2135  urlinfo u = NULL;
2136  const char * src = NULL;
2137  int rc;
2138 
2139  rc = davInit(path, &u);
2140  if (rc)
2141  goto exit;
2142 assert(u != NULL);
2143 
2144  (void) urlPath(path, &src);
2145 
2146  /* XXX HACK: only getrestype(remote) != resr_collection */
2147 
2148  rc = ne_delete(u->sess, src);
2149 
2150 exit:
2151  if (rc) rc = -1; /* XXX HACK: errno impedance match */
2152 
2153 if (_dav_debug)
2154 fprintf(stderr, "*** davUnlink(%s) rc %d\n", path, rc);
2155  return rc;
2156 }
2157 
2158 #ifdef NOTYET
2159 static int davChdir(const char * path)
2160  /*@globals h_errno, fileSystem, internalState @*/
2161  /*@modifies fileSystem, internalState @*/
2162 {
2163  return davCommand("CWD", path, NULL);
2164 }
2165 #endif /* NOTYET */
2166 
2167 /* =============================================================== */
2168 
2169 static const char * statstr(const struct stat * st,
2170  /*@returned@*/ /*@out@*/ char * buf)
2171  /*@modifies *buf @*/
2172 {
2173  sprintf(buf,
2174  "*** dev %x ino %x mode %0o nlink %d uid %d gid %d rdev %x size %x\n",
2175  (unsigned)st->st_dev,
2176  (unsigned)st->st_ino,
2177  (unsigned)st->st_mode,
2178  (unsigned)st->st_nlink,
2179  (unsigned)st->st_uid,
2180  (unsigned)st->st_gid,
2181  (unsigned)st->st_rdev,
2182  (unsigned)st->st_size);
2183  return buf;
2184 }
2185 
2186 int davStat(const char * path, /*@out@*/ struct stat *st)
2187  /*@globals hrefpat, fileSystem, internalState @*/
2188  /*@modifies *st, fileSystem, internalState @*/
2189 {
2190  avContext ctx = NULL;
2191  char buf[1024];
2192  int rc = -1;
2193 
2194  if (path == NULL || *path == '\0') {
2195  errno = ENOENT;
2196  goto exit;
2197  }
2198  ctx = avContextCreate(path, st);
2199  if (ctx == NULL) {
2200  errno = ENOENT; /* Note: ctx is NULL iff urlSplit() fails. */
2201  goto exit;
2202  }
2203  rc = davNLST(ctx);
2204  if (rc) {
2205 /* HACK: errno = ??? */
2206  goto exit;
2207  }
2208 
2209  if (st->st_mode == 0)
2210  st->st_mode = (ctx->ac > 1 ? S_IFDIR : S_IFREG);
2211  st->st_size = (ctx->sizes ? ctx->sizes[0] : (size_t)st->st_size);
2212  st->st_mtime = (ctx->mtimes ? ctx->mtimes[0] : st->st_mtime);
2213  st->st_atime = st->st_ctime = st->st_mtime; /* HACK */
2214  if (S_ISDIR(st->st_mode)) {
2215  st->st_nlink = 2;
2216  st->st_mode |= 0755;
2217  } else
2218  if (S_ISREG(st->st_mode)) {
2219  st->st_nlink = 1;
2220  st->st_mode |= 0644;
2221  }
2222 
2223  /* XXX Fts(3) needs/uses st_ino. */
2224  /* Hash the path to generate a st_ino analogue. */
2225  if (st->st_ino == 0)
2226  st->st_ino = hashFunctionString(0, path, 0);
2227 
2228 exit:
2229 if (_dav_debug < 0)
2230 fprintf(stderr, "*** davStat(%s) rc %d\n%s", path, rc, statstr(st, buf));
2231  ctx = avContextDestroy(ctx);
2232  return rc;
2233 }
2234 
2235 int davLstat(const char * path, /*@out@*/ struct stat *st)
2236  /*@globals hrefpat, fileSystem, internalState @*/
2237  /*@modifies *st, fileSystem, internalState @*/
2238 {
2239  avContext ctx = NULL;
2240  char buf[1024];
2241  int rc = -1;
2242 
2243  if (path == NULL || *path == '\0') {
2244  errno = ENOENT;
2245  goto exit;
2246  }
2247  ctx = avContextCreate(path, st);
2248  if (ctx == NULL) {
2249  errno = ENOENT; /* Note: ctx is NULL iff urlSplit() fails. */
2250  goto exit;
2251  }
2252  rc = davNLST(ctx);
2253  if (rc) {
2254 /* HACK: errno = ??? */
2255  goto exit;
2256  }
2257 
2258  if (st->st_mode == 0)
2259  st->st_mode = (ctx->ac > 1 ? S_IFDIR : S_IFREG);
2260  st->st_size = (ctx->sizes ? ctx->sizes[0] : (size_t)st->st_size);
2261  st->st_mtime = (ctx->mtimes ? ctx->mtimes[0] : st->st_mtime);
2262  st->st_atime = st->st_ctime = st->st_mtime; /* HACK */
2263  if (S_ISDIR(st->st_mode)) {
2264  st->st_nlink = 2;
2265  st->st_mode |= 0755;
2266  } else
2267  if (S_ISREG(st->st_mode)) {
2268  st->st_nlink = 1;
2269  st->st_mode |= 0644;
2270  }
2271 
2272  /* XXX fts(3) needs/uses st_ino. */
2273  /* Hash the path to generate a st_ino analogue. */
2274  if (st->st_ino == 0)
2275  st->st_ino = hashFunctionString(0, path, 0);
2276 
2277 if (_dav_debug < 0)
2278 fprintf(stderr, "*** davLstat(%s) rc %d\n%s\n", path, rc, statstr(st, buf));
2279 exit:
2280  ctx = avContextDestroy(ctx);
2281  return rc;
2282 }
2283 
2284 #ifdef NOTYET
2285 static int davReadlink(const char * path, /*@out@*/ char * buf, size_t bufsiz)
2286  /*@globals h_errno, fileSystem, internalState @*/
2287  /*@modifies *buf, fileSystem, internalState @*/
2288 {
2289  int rc;
2290  rc = davNLST(path, DO_FTP_READLINK, NULL, buf, bufsiz);
2291 if (_dav_debug < 0)
2292 fprintf(stderr, "*** davReadlink(%s) rc %d\n", path, rc);
2293  return rc;
2294 }
2295 #endif /* NOTYET */
2296 
2297 #endif /* WITH_NEON */
2298 
2299 /* =============================================================== */
2300 /*@unchecked@*/
2301 int avmagicdir = 0x3607113;
2302 
2303 #ifndef WITH_NEON
2304 /*@-nullstate@*/ /* FIX: u->{ctrl,data}->url undef after XurlLink. */
2305 FD_t httpOpen(const char * url, /*@unused@*/ int flags,
2306  /*@unused@*/ mode_t mode, /*@out@*/ urlinfo * uret)
2307  /*@globals internalState @*/
2308  /*@modifies *uret, internalState @*/
2309 {
2310  urlinfo u = NULL;
2311  FD_t fd = NULL;
2312 
2313 #if 0 /* XXX makeTempFile() heartburn */
2314  assert(!(flags & O_RDWR));
2315 #endif
2316  if (urlSplit(url, &u))
2317  goto exit;
2318 
2319  if (u->ctrl == NULL)
2320  u->ctrl = fdNew("persist ctrl (httpOpen)");
2321  if (u->ctrl != NULL) { /* XXX can't happen */
2322  yarnLock use = u->ctrl->_item.use;
2323  yarnPossess(use);
2324  if (yarnPeekLock(use) > 2L && u->data == NULL)
2325  u->data = fdNew("persist data (httpOpen)");
2326  yarnRelease(use);
2327  }
2328 
2329  if (u->ctrl->url == NULL)
2330  fd = fdLink(u->ctrl, "grab ctrl (httpOpen persist ctrl)");
2331  else if (u->data->url == NULL)
2332  fd = fdLink(u->data, "grab ctrl (httpOpen persist data)");
2333  else
2334  fd = fdNew("grab ctrl (httpOpen)");
2335 
2336  if (fd) {
2337  fdSetIo(fd, ufdio);
2338  fd->ftpFileDoneNeeded = 0;
2340  fd->contentLength = fd->bytesRemain = -1;
2341  fd->url = urlLink(u, "url (httpOpen)");
2342  fd = fdLink(fd, "grab data (httpOpen)");
2343  fd->urlType = URL_IS_HTTP;
2344  }
2345 
2346 exit:
2347  if (uret)
2348  *uret = u;
2349  /*@-refcounttrans@*/
2350  return fd;
2351  /*@=refcounttrans@*/
2352 }
2353 /*@=nullstate@*/
2354 #endif
2355 
2356 #ifdef WITH_NEON
2357 /* =============================================================== */
2358 int davClosedir(/*@only@*/ DIR * dir)
2359 {
2360  return avClosedir(dir);
2361 }
2362 
2363 struct dirent * davReaddir(DIR * dir)
2364 {
2365  return avReaddir(dir);
2366 }
2367 
2368 DIR * davOpendir(const char * path)
2369  /*@globals hrefpat @*/
2370 {
2371  AVDIR avdir = NULL;
2372  avContext ctx = NULL;
2373  struct stat sb, *st = &sb; /* XXX HACK: davHEAD needs ctx->st. */
2374  const char * uri = NULL;
2375  int rc;
2376 
2377 if (_dav_debug < 0)
2378 fprintf(stderr, "*** davOpendir(%s)\n", path);
2379 
2380  if (path == NULL || *path == '\0') {
2381  errno = ENOENT;
2382  goto exit;
2383  }
2384 
2385  /* Note: all Opendir(3) URI's need pesky trailing '/' */
2386 /*@-globs -mods@*/
2387  if (path[strlen(path)-1] != '/')
2388  uri = rpmExpand(path, "/", NULL);
2389  else
2390  uri = xstrdup(path);
2391 /*@=globs =mods@*/
2392 
2393  /* Load DAV collection into argv. */
2394  /* XXX HACK: davHEAD needs ctx->st. */
2395  ctx = avContextCreate(uri, st);
2396  if (ctx == NULL) {
2397  errno = ENOENT; /* Note: ctx is NULL iff urlSplit() fails. */
2398  goto exit;
2399  }
2400 
2401  rc = davNLST(ctx);
2402  if (rc) {
2403 /* HACK: errno = ??? */
2404  goto exit;
2405  } else
2406  avdir = (AVDIR) avOpendir(uri, ctx->av, ctx->modes);
2407 
2408 exit:
2409  uri = _free(uri);
2410  ctx = avContextDestroy(ctx);
2411 /*@-kepttrans@*/
2412  return (DIR *) avdir;
2413 /*@=kepttrans@*/
2414 }
2415 /*@=modfilesys@*/
2416 
2417 /*@-mustmod@*/
2418 char * davRealpath(const char * path, char * resolved_path)
2419 {
2420 assert(resolved_path == NULL); /* XXX no POSIXly broken realpath(3) here. */
2421  /* XXX TODO: handle redirects. For now, just dupe the path. */
2422  return xstrdup(path);
2423 }
2424 /*@=mustmod@*/
2425 
2426 #endif /* WITH_NEON */