rpm 5.2.1

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

- File List
- Globals

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@*/
91 int rpmioHttpReadTimeoutSecs = TIMEOUT_SECS;
92 /*@unchecked@*/
93 int rpmioHttpConnectTimeoutSecs = TIMEOUT_SECS;
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:
368 u->capabilities = _free(u->capabilities);
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] == '/')
674 u->allow &= ~RPMURL_SERVER_OPTIONSDONE;
675 #endif
676 /* Have options been run? */
677 if (u->allow & RPMURL_SERVER_OPTIONSDONE)
678 return 0;
679
680 u->allow &= ~(RPMURL_SERVER_HASDAVCLASS1 |
681 RPMURL_SERVER_HASDAVCLASS2 |
682 RPMURL_SERVER_HASDAVEXEC);
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:
689 u->allow |= RPMURL_SERVER_OPTIONSDONE;
690 { ne_server_capabilities *cap = u->capabilities;
691 if (cap->dav_class1)
692 u->allow |= RPMURL_SERVER_HASDAVCLASS1;
693 else
694 u->allow &= ~RPMURL_SERVER_HASDAVCLASS1;
695 if (cap->dav_class2)
696 u->allow |= RPMURL_SERVER_HASDAVCLASS2;
697 else
698 u->allow &= ~RPMURL_SERVER_HASDAVCLASS2;
699 if (cap->dav_executable)
700 u->allow |= RPMURL_SERVER_HASDAVEXEC;
701 else
702 u->allow &= ~RPMURL_SERVER_HASDAVEXEC;
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)) {
707 u->allow |= RPMURL_SERVER_OPTIONSDONE;
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,
805 (rpmioHttpUserAgent ? rpmioHttpUserAgent : _rpmioHttpUserAgent));
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"))
1667 u->allow |= RPMURL_SERVER_HASRANGE;
1668 if (!strcmp(value, "none"))
1669 u->allow &= ~RPMURL_SERVER_HASRANGE;
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);
1722 ctrl->contentDisposition = _free(ctrl->contentDisposition);
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
1832 rc = FTPERR_SERVER_IO_ERROR;
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;
1933 fd->rd_timeoutsecs = rpmioHttpReadTimeoutSecs;
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;
2339 fd->rd_timeoutsecs = rpmioHttpReadTimeoutSecs;
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 */