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