1 /*
2    Basic HTTP and WebDAV methods
3    Copyright (C) 1999-2003, Joe Orton <joe@manyfish.co.uk>
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public
7    License as published by the Free Software Foundation; either
8    version 2 of the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14 
15    You should have received a copy of the GNU Library General Public
16    License along with this library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
18    MA 02111-1307, USA
19 
20 */
21 
22 #include "config.h"
23 
24 #include <sys/types.h>
25 
26 #ifdef HAVE_STRING_H
27 #include <string.h>
28 #endif
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #ifdef HAVE_STDLIB_H
33 #include <stdlib.h>
34 #endif
35 
36 #include <errno.h>
37 
38 #include "ne_request.h"
39 #include "ne_alloc.h"
40 #include "ne_utils.h"
41 #include "ne_basic.h"
42 #include "ne_207.h"
43 
44 #ifndef NEON_NODAV
45 #include "ne_uri.h"
46 #endif
47 
48 #ifdef USE_DAV_LOCKS
49 #include "ne_locks.h"
50 #endif
51 #include "ne_dates.h"
52 #include "ne_i18n.h"
53 
54 /* Header parser to retrieve Last-Modified date */
get_lastmodified(void * userdata,const char * value)55 static void get_lastmodified(void *userdata, const char *value) {
56     time_t *modtime = userdata;
57     *modtime = ne_httpdate_parse(value);
58 }
59 
ne_getmodtime(ne_session * sess,const char * uri,time_t * modtime)60 int ne_getmodtime(ne_session *sess, const char *uri, time_t *modtime)
61 {
62     ne_request *req = ne_request_create(sess, "HEAD", uri);
63     int ret;
64 
65     ne_add_response_header_handler(req, "Last-Modified", get_lastmodified,
66 				   modtime);
67 
68     *modtime = -1;
69 
70     ret = ne_request_dispatch(req);
71 
72     if (ret == NE_OK && ne_get_status(req)->klass != 2) {
73 	*modtime = -1;
74 	ret = NE_ERROR;
75     }
76 
77     ne_request_destroy(req);
78 
79     return ret;
80 }
81 
82 /* PUT's from fd to URI */
ne_put(ne_session * sess,const char * uri,int fd)83 int ne_put(ne_session *sess, const char *uri, int fd)
84 {
85     ne_request *req = ne_request_create(sess, "PUT", uri);
86     int ret;
87 
88 #ifdef USE_DAV_LOCKS
89     ne_lock_using_resource(req, uri, 0);
90     ne_lock_using_parent(req, uri);
91 #endif
92 
93     ne_set_request_body_fd(req, fd);
94 
95     ret = ne_request_dispatch(req);
96 
97     if (ret == NE_OK && ne_get_status(req)->klass != 2)
98 	ret = NE_ERROR;
99 
100     ne_request_destroy(req);
101 
102     return ret;
103 }
104 
105 /* Conditional HTTP put.
106  * PUTs from fd to uri, returning NE_FAILED if resource as URI has
107  * been modified more recently than 'since'.
108  */
109 int
ne_put_if_unmodified(ne_session * sess,const char * uri,int fd,time_t since)110 ne_put_if_unmodified(ne_session *sess, const char *uri, int fd,
111 		     time_t since)
112 {
113     ne_request *req;
114     char *date;
115     int ret;
116 
117     if (ne_version_pre_http11(sess)) {
118 	time_t modtime;
119 	/* Server is not minimally HTTP/1.1 compliant.  Do a HEAD to
120 	 * check the remote mod time. Of course, this makes the
121 	 * operation very non-atomic, but better than nothing. */
122 	ret = ne_getmodtime(sess, uri, &modtime);
123 	if (ret != NE_OK) return ret;
124 	if (modtime != since)
125 	    return NE_FAILED;
126     }
127 
128     req = ne_request_create(sess, "PUT", uri);
129 
130     date = ne_rfc1123_date(since);
131     /* Add in the conditionals */
132     ne_add_request_header(req, "If-Unmodified-Since", date);
133     ne_free(date);
134 
135 #ifdef USE_DAV_LOCKS
136     ne_lock_using_resource(req, uri, 0);
137     /* FIXME: this will give 412 if the resource doesn't exist, since
138      * PUT may modify the parent... does that matter?  */
139 #endif
140 
141     ne_set_request_body_fd(req, fd);
142 
143     ret = ne_request_dispatch(req);
144 
145     if (ret == NE_OK) {
146 	if (ne_get_status(req)->code == 412) {
147 	    ret = NE_FAILED;
148 	} else if (ne_get_status(req)->klass != 2) {
149 	    ret = NE_ERROR;
150 	}
151     }
152 
153     ne_request_destroy(req);
154 
155     return ret;
156 }
157 
158 struct get_context {
159     int error;
160     ne_session *session;
161     off_t total;
162     int fd; /* used in get_to_fd */
163     ne_content_range *range;
164 };
165 
get_to_fd(void * userdata,const char * block,size_t length)166 static void get_to_fd(void *userdata, const char *block, size_t length)
167 {
168     struct get_context *ctx = userdata;
169     ssize_t ret;
170 
171     if (!ctx->error) {
172 	while (length > 0) {
173 	    ret = write(ctx->fd, block, length);
174 	    if (ret < 0) {
175 		char err[200];
176 		ctx->error = 1;
177 		ne_strerror(errno, err, sizeof err);
178 		ne_set_error(ctx->session, _("Could not write to file: %s"),
179 			     err);
180 		break;
181 	    } else {
182 		length -= ret;
183 		block += ret;
184 	    }
185 	}
186     }
187 }
188 
accept_206(void * ud,ne_request * req,const ne_status * st)189 static int accept_206(void *ud, ne_request *req, const ne_status *st)
190 {
191     return (st->code == 206);
192 }
193 
clength_hdr_handler(void * ud,const char * value)194 static void clength_hdr_handler(void *ud, const char *value)
195 {
196     struct get_context *ctx = ud;
197     off_t len = strtol(value, NULL, 10);
198 
199     if (ctx->range->end == -1) {
200 	ctx->range->end = ctx->range->start + len - 1;
201 	ctx->range->total = len;
202     }
203     else if (len != ctx->total) {
204 	NE_DEBUG(NE_DBG_HTTP,
205 		 "Expecting %" NE_FMT_OFF_T " bytes, "
206 		 "got entity of length %" NE_FMT_OFF_T "\n",
207 		 ctx->total, len);
208 	ne_set_error(ctx->session, _("Response not of expected length"));
209 	ctx->error = 1;
210     }
211 }
212 
content_range_hdr_handler(void * ud,const char * value)213 static void content_range_hdr_handler(void *ud, const char *value)
214 {
215     struct get_context *ctx = ud;
216 
217     if (strncmp(value, "bytes ", 6) != 0) {
218 	ne_set_error(ctx->session, ("Response range using unrecognized unit"));
219 	ctx->error = 1;
220     }
221 
222     /* TODO: verify against requested range. */
223 }
224 
ne_get_range(ne_session * sess,const char * uri,ne_content_range * range,int fd)225 int ne_get_range(ne_session *sess, const char *uri,
226 		 ne_content_range *range, int fd)
227 {
228     ne_request *req = ne_request_create(sess, "GET", uri);
229     struct get_context ctx;
230     const ne_status *status;
231     int ret;
232 
233     if (range->end == -1) {
234 	ctx.total = -1;
235     }
236     else {
237 	ctx.total = (range->end - range->start) + 1;
238     }
239 
240     NE_DEBUG(NE_DBG_HTTP, "Range total: %" NE_FMT_OFF_T "\n", ctx.total);
241 
242     ctx.fd = fd;
243     ctx.error = 0;
244     ctx.range = range;
245     ctx.session = sess;
246 
247     ne_add_response_header_handler(req, "Content-Length",
248 				     clength_hdr_handler, &ctx);
249     ne_add_response_header_handler(req, "Content-Range",
250 				     content_range_hdr_handler,
251 				     &ctx);
252 
253     ne_add_response_body_reader(req, accept_206, get_to_fd, &ctx);
254 
255     if (range->end == -1) {
256 	ne_print_request_header(req, "Range", "bytes=%" NE_FMT_OFF_T "-",
257 				range->start);
258     }
259     else {
260 	ne_print_request_header(req, "Range",
261 				"bytes=%" NE_FMT_OFF_T "-%" NE_FMT_OFF_T,
262 				range->start, range->end);
263     }
264     ne_add_request_header(req, "Accept-Ranges", "bytes");
265 
266     ret = ne_request_dispatch(req);
267 
268     status = ne_get_status(req);
269 
270     if (ctx.error) {
271 	ret = NE_ERROR;
272     } else if (status && status->code == 416) {
273 	/* connection is terminated too early with Apache/1.3, so we check
274 	 * this even if ret == NE_ERROR... */
275 	ne_set_error(sess, _("Range is not satisfiable"));
276 	ret = NE_ERROR;
277     }
278     else if (ret == NE_OK) {
279 	if (status->klass == 2 && status->code != 206) {
280 	    ne_set_error(sess, _("Resource does not support ranged GETs."));
281 	    ret = NE_ERROR;
282 	}
283 	else if (status->klass != 2) {
284 	    ret = NE_ERROR;
285 	}
286     }
287 
288     ne_request_destroy(req);
289 
290     return ret;
291 }
292 
293 
294 /* Get to given fd */
ne_get(ne_session * sess,const char * uri,int fd)295 int ne_get(ne_session *sess, const char *uri, int fd)
296 {
297     ne_request *req = ne_request_create(sess, "GET", uri);
298     struct get_context ctx;
299     int ret;
300 
301     ctx.total = -1;
302     ctx.fd = fd;
303     ctx.error = 0;
304     ctx.session = sess;
305 
306     /* Read the value of the Content-Length header into ctx.total */
307     ne_add_response_header_handler(req, "Content-Length",
308 				     ne_handle_numeric_header,
309 				     &ctx.total);
310 
311     ne_add_response_body_reader(req, ne_accept_2xx, get_to_fd, &ctx);
312 
313     ret = ne_request_dispatch(req);
314 
315     if (ctx.error) {
316 	ret = NE_ERROR;
317     } else if (ret == NE_OK && ne_get_status(req)->klass != 2) {
318 	ret = NE_ERROR;
319     }
320 
321     ne_request_destroy(req);
322 
323     return ret;
324 }
325 
326 
327 /* Get to given fd */
ne_post(ne_session * sess,const char * uri,int fd,const char * buffer)328 int ne_post(ne_session *sess, const char *uri, int fd, const char *buffer)
329 {
330     ne_request *req = ne_request_create(sess, "POST", uri);
331     struct get_context ctx;
332     int ret;
333 
334     ctx.total = -1;
335     ctx.fd = fd;
336     ctx.error = 0;
337     ctx.session = sess;
338 
339     /* Read the value of the Content-Length header into ctx.total */
340     ne_add_response_header_handler(req, "Content-Length",
341 				     ne_handle_numeric_header, &ctx.total);
342 
343     ne_add_response_body_reader(req, ne_accept_2xx, get_to_fd, &ctx);
344 
345     ne_set_request_body_buffer(req, buffer, strlen(buffer));
346 
347     ret = ne_request_dispatch(req);
348 
349     if (ctx.error) {
350 	ret = NE_ERROR;
351     }
352     else if (ret == NE_OK && ne_get_status(req)->klass != 2) {
353 	ret = NE_ERROR;
354     }
355 
356     ne_request_destroy(req);
357 
358     return ret;
359 }
360 
ne_content_type_handler(void * userdata,const char * value)361 void ne_content_type_handler(void *userdata, const char *value)
362 {
363     ne_content_type *ct = userdata;
364     char *sep, *stype;
365 
366     ct->value = ne_strdup(value);
367 
368     stype = strchr(ct->value, '/');
369     if (!stype) {
370 	NE_FREE(ct->value);
371 	return;
372     }
373 
374     *stype++ = '\0';
375     ct->type = ct->value;
376 
377     sep = strchr(stype, ';');
378 
379     if (sep) {
380 	char *tok;
381 	/* look for the charset parameter. TODO; probably better to
382 	 * hand-carve a parser than use ne_token/strstr/shave here. */
383 	*sep++ = '\0';
384 	do {
385 	    tok = ne_qtoken(&sep, ';', "\"\'");
386 	    if (tok) {
387 		tok = strstr(tok, "charset=");
388 		if (tok)
389 		    ct->charset = ne_shave(tok+8, "\"\'");
390 	    } else {
391 		break;
392 	    }
393 	} while (sep != NULL);
394     }
395 
396     /* set subtype, losing any trailing whitespace */
397     ct->subtype = ne_shave(stype, " \t");
398 
399     /* 2616#3.7.1: subtypes of text/ default to charset ISO-8859-1. */
400     if (ct->charset == NULL && strcasecmp(ct->type, "text") == 0)
401 	ct->charset = "ISO-8859-1";
402 }
403 
dav_hdr_handler(void * userdata,const char * value)404 static void dav_hdr_handler(void *userdata, const char *value)
405 {
406     char *tokens = ne_strdup(value), *pnt = tokens;
407     ne_server_capabilities *caps = userdata;
408 
409     do {
410 	char *tok = ne_qtoken(&pnt, ',',  "\"'");
411 	if (!tok) break;
412 
413 	tok = ne_shave(tok, " \r\t\n");
414 
415 	if (strcmp(tok, "1") == 0) {
416 	    caps->dav_class1 = 1;
417 	} else if (strcmp(tok, "2") == 0) {
418 	    caps->dav_class2 = 1;
419 	} else if (strcmp(tok, "<http://apache.org/dav/propset/fs/1>") == 0) {
420 	    caps->dav_executable = 1;
421 	}
422     } while (pnt != NULL);
423 
424     ne_free(tokens);
425 
426 }
427 
ne_options(ne_session * sess,const char * uri,ne_server_capabilities * caps)428 int ne_options(ne_session *sess, const char *uri,
429 		  ne_server_capabilities *caps)
430 {
431     ne_request *req = ne_request_create(sess, "OPTIONS", uri);
432 
433     int ret;
434 
435     ne_add_response_header_handler(req, "DAV", dav_hdr_handler, caps);
436 
437     ret = ne_request_dispatch(req);
438 
439     if (ret == NE_OK && ne_get_status(req)->klass != 2) {
440 	ret = NE_ERROR;
441     }
442 
443     ne_request_destroy(req);
444 
445     return ret;
446 }
447 
448 #ifndef NEON_NODAV
449 
ne_add_depth_header(ne_request * req,int depth)450 void ne_add_depth_header(ne_request *req, int depth)
451 {
452     const char *value;
453     switch(depth) {
454     case NE_DEPTH_ZERO:
455 	value = "0";
456 	break;
457     case NE_DEPTH_ONE:
458 	value = "1";
459 	break;
460     default:
461 	value = "infinity";
462 	break;
463     }
464     ne_add_request_header(req, "Depth", value);
465 }
466 
copy_or_move(ne_session * sess,int is_move,int overwrite,int depth,const char * src,const char * dest)467 static int copy_or_move(ne_session *sess, int is_move, int overwrite,
468 			int depth, const char *src, const char *dest)
469 {
470     ne_request *req = ne_request_create( sess, is_move?"MOVE":"COPY", src );
471 
472     /* 2518 S8.9.2 says only use Depth: infinity with MOVE. */
473     if (!is_move) {
474 	ne_add_depth_header(req, depth);
475     }
476 
477 #ifdef USE_DAV_LOCKS
478     if (is_move) {
479 	ne_lock_using_resource(req, src, NE_DEPTH_INFINITE);
480     }
481     ne_lock_using_resource(req, dest, NE_DEPTH_INFINITE);
482     /* And we need to be able to add members to the destination's parent */
483     ne_lock_using_parent(req, dest);
484 #endif
485 
486     ne_print_request_header(req, "Destination", "%s://%s%s",
487 			      ne_get_scheme(sess),
488 			      ne_get_server_hostport(sess), dest);
489 
490     ne_add_request_header(req, "Overwrite", overwrite?"T":"F");
491 
492     return ne_simple_request(sess, req);
493 }
494 
ne_copy(ne_session * sess,int overwrite,int depth,const char * src,const char * dest)495 int ne_copy(ne_session *sess, int overwrite, int depth,
496 	     const char *src, const char *dest)
497 {
498     return copy_or_move(sess, 0, overwrite, depth, src, dest);
499 }
500 
ne_move(ne_session * sess,int overwrite,const char * src,const char * dest)501 int ne_move(ne_session *sess, int overwrite,
502 	     const char *src, const char *dest)
503 {
504     return copy_or_move(sess, 1, overwrite, 0, src, dest);
505 }
506 
507 /* Deletes the specified resource. (and in only two lines of code!) */
ne_delete(ne_session * sess,const char * uri)508 int ne_delete(ne_session *sess, const char *uri)
509 {
510     ne_request *req = ne_request_create(sess, "DELETE", uri);
511 
512 #ifdef USE_DAV_LOCKS
513     ne_lock_using_resource(req, uri, NE_DEPTH_INFINITE);
514     ne_lock_using_parent(req, uri);
515 #endif
516 
517     /* joe: I asked on the DAV WG list about whether we might get a
518      * 207 error back from a DELETE... conclusion, you shouldn't if
519      * you don't send the Depth header, since we might be an HTTP/1.1
520      * client and a 2xx response indicates success to them.  But
521      * it's all a bit unclear. In any case, DAV servers today do
522      * return 207 to DELETE even if we don't send the Depth header.
523      * So we handle 207 errors appropriately. */
524 
525     return ne_simple_request(sess, req);
526 }
527 
ne_mkcol(ne_session * sess,const char * uri)528 int ne_mkcol(ne_session *sess, const char *uri)
529 {
530     ne_request *req;
531     char *real_uri;
532     int ret;
533 
534     if (ne_path_has_trailing_slash(uri)) {
535 	real_uri = ne_strdup(uri);
536     } else {
537 	real_uri = ne_concat(uri, "/", NULL);
538     }
539 
540     req = ne_request_create(sess, "MKCOL", real_uri);
541 
542 #ifdef USE_DAV_LOCKS
543     ne_lock_using_resource(req, real_uri, 0);
544     ne_lock_using_parent(req, real_uri);
545 #endif
546 
547     ret = ne_simple_request(sess, req);
548 
549     ne_free(real_uri);
550 
551     return ret;
552 }
553 
554 #endif /* NEON_NODAV */
555