Annotation of embedaddon/nginx/src/http/modules/ngx_http_dav_module.c, revision 1.1.1.1
1.1 misho 1:
2: /*
3: * Copyright (C) Igor Sysoev
4: * Copyright (C) Nginx, Inc.
5: */
6:
7:
8: #include <ngx_config.h>
9: #include <ngx_core.h>
10: #include <ngx_http.h>
11:
12:
13: #define NGX_HTTP_DAV_COPY_BLOCK 65536
14:
15: #define NGX_HTTP_DAV_OFF 2
16:
17:
18: #define NGX_HTTP_DAV_NO_DEPTH -3
19: #define NGX_HTTP_DAV_INVALID_DEPTH -2
20: #define NGX_HTTP_DAV_INFINITY_DEPTH -1
21:
22:
23: typedef struct {
24: ngx_uint_t methods;
25: ngx_uint_t access;
26: ngx_uint_t min_delete_depth;
27: ngx_flag_t create_full_put_path;
28: } ngx_http_dav_loc_conf_t;
29:
30:
31: typedef struct {
32: ngx_str_t path;
33: size_t len;
34: } ngx_http_dav_copy_ctx_t;
35:
36:
37: static ngx_int_t ngx_http_dav_handler(ngx_http_request_t *r);
38:
39: static void ngx_http_dav_put_handler(ngx_http_request_t *r);
40:
41: static ngx_int_t ngx_http_dav_delete_handler(ngx_http_request_t *r);
42: static ngx_int_t ngx_http_dav_delete_path(ngx_http_request_t *r,
43: ngx_str_t *path, ngx_uint_t dir);
44: static ngx_int_t ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path);
45: static ngx_int_t ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path);
46: static ngx_int_t ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path);
47:
48: static ngx_int_t ngx_http_dav_mkcol_handler(ngx_http_request_t *r,
49: ngx_http_dav_loc_conf_t *dlcf);
50:
51: static ngx_int_t ngx_http_dav_copy_move_handler(ngx_http_request_t *r);
52: static ngx_int_t ngx_http_dav_copy_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path);
53: static ngx_int_t ngx_http_dav_copy_dir_time(ngx_tree_ctx_t *ctx,
54: ngx_str_t *path);
55: static ngx_int_t ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx,
56: ngx_str_t *path);
57:
58: static ngx_int_t ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt);
59: static ngx_int_t ngx_http_dav_error(ngx_log_t *log, ngx_err_t err,
60: ngx_int_t not_found, char *failed, u_char *path);
61: static ngx_int_t ngx_http_dav_location(ngx_http_request_t *r, u_char *path);
62: static void *ngx_http_dav_create_loc_conf(ngx_conf_t *cf);
63: static char *ngx_http_dav_merge_loc_conf(ngx_conf_t *cf,
64: void *parent, void *child);
65: static ngx_int_t ngx_http_dav_init(ngx_conf_t *cf);
66:
67:
68: static ngx_conf_bitmask_t ngx_http_dav_methods_mask[] = {
69: { ngx_string("off"), NGX_HTTP_DAV_OFF },
70: { ngx_string("put"), NGX_HTTP_PUT },
71: { ngx_string("delete"), NGX_HTTP_DELETE },
72: { ngx_string("mkcol"), NGX_HTTP_MKCOL },
73: { ngx_string("copy"), NGX_HTTP_COPY },
74: { ngx_string("move"), NGX_HTTP_MOVE },
75: { ngx_null_string, 0 }
76: };
77:
78:
79: static ngx_command_t ngx_http_dav_commands[] = {
80:
81: { ngx_string("dav_methods"),
82: NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
83: ngx_conf_set_bitmask_slot,
84: NGX_HTTP_LOC_CONF_OFFSET,
85: offsetof(ngx_http_dav_loc_conf_t, methods),
86: &ngx_http_dav_methods_mask },
87:
88: { ngx_string("create_full_put_path"),
89: NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
90: ngx_conf_set_flag_slot,
91: NGX_HTTP_LOC_CONF_OFFSET,
92: offsetof(ngx_http_dav_loc_conf_t, create_full_put_path),
93: NULL },
94:
95: { ngx_string("min_delete_depth"),
96: NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
97: ngx_conf_set_num_slot,
98: NGX_HTTP_LOC_CONF_OFFSET,
99: offsetof(ngx_http_dav_loc_conf_t, min_delete_depth),
100: NULL },
101:
102: { ngx_string("dav_access"),
103: NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
104: ngx_conf_set_access_slot,
105: NGX_HTTP_LOC_CONF_OFFSET,
106: offsetof(ngx_http_dav_loc_conf_t, access),
107: NULL },
108:
109: ngx_null_command
110: };
111:
112:
113: static ngx_http_module_t ngx_http_dav_module_ctx = {
114: NULL, /* preconfiguration */
115: ngx_http_dav_init, /* postconfiguration */
116:
117: NULL, /* create main configuration */
118: NULL, /* init main configuration */
119:
120: NULL, /* create server configuration */
121: NULL, /* merge server configuration */
122:
123: ngx_http_dav_create_loc_conf, /* create location configuration */
124: ngx_http_dav_merge_loc_conf /* merge location configuration */
125: };
126:
127:
128: ngx_module_t ngx_http_dav_module = {
129: NGX_MODULE_V1,
130: &ngx_http_dav_module_ctx, /* module context */
131: ngx_http_dav_commands, /* module directives */
132: NGX_HTTP_MODULE, /* module type */
133: NULL, /* init master */
134: NULL, /* init module */
135: NULL, /* init process */
136: NULL, /* init thread */
137: NULL, /* exit thread */
138: NULL, /* exit process */
139: NULL, /* exit master */
140: NGX_MODULE_V1_PADDING
141: };
142:
143:
144: static ngx_int_t
145: ngx_http_dav_handler(ngx_http_request_t *r)
146: {
147: ngx_int_t rc;
148: ngx_http_dav_loc_conf_t *dlcf;
149:
150: dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
151:
152: if (!(r->method & dlcf->methods)) {
153: return NGX_DECLINED;
154: }
155:
156: switch (r->method) {
157:
158: case NGX_HTTP_PUT:
159:
160: if (r->uri.data[r->uri.len - 1] == '/') {
161: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
162: "cannot PUT to a collection");
163: return NGX_HTTP_CONFLICT;
164: }
165:
166: r->request_body_in_file_only = 1;
167: r->request_body_in_persistent_file = 1;
168: r->request_body_in_clean_file = 1;
169: r->request_body_file_group_access = 1;
170: r->request_body_file_log_level = 0;
171:
172: rc = ngx_http_read_client_request_body(r, ngx_http_dav_put_handler);
173:
174: if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
175: return rc;
176: }
177:
178: return NGX_DONE;
179:
180: case NGX_HTTP_DELETE:
181:
182: return ngx_http_dav_delete_handler(r);
183:
184: case NGX_HTTP_MKCOL:
185:
186: return ngx_http_dav_mkcol_handler(r, dlcf);
187:
188: case NGX_HTTP_COPY:
189:
190: return ngx_http_dav_copy_move_handler(r);
191:
192: case NGX_HTTP_MOVE:
193:
194: return ngx_http_dav_copy_move_handler(r);
195: }
196:
197: return NGX_DECLINED;
198: }
199:
200:
201: static void
202: ngx_http_dav_put_handler(ngx_http_request_t *r)
203: {
204: size_t root;
205: time_t date;
206: ngx_str_t *temp, path;
207: ngx_uint_t status;
208: ngx_file_info_t fi;
209: ngx_ext_rename_file_t ext;
210: ngx_http_dav_loc_conf_t *dlcf;
211:
212: if (r->request_body == NULL || r->request_body->temp_file == NULL) {
213: ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
214: return;
215: }
216:
217: ngx_http_map_uri_to_path(r, &path, &root, 0);
218:
219: path.len--;
220:
221: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
222: "http put filename: \"%s\"", path.data);
223:
224: temp = &r->request_body->temp_file->file.name;
225:
226: if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) {
227: status = NGX_HTTP_CREATED;
228:
229: } else {
230: status = NGX_HTTP_NO_CONTENT;
231:
232: if (ngx_is_dir(&fi)) {
233: ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,
234: "\"%s\" could not be created", path.data);
235:
236: if (ngx_delete_file(temp->data) == NGX_FILE_ERROR) {
237: ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
238: ngx_delete_file_n " \"%s\" failed",
239: temp->data);
240: }
241:
242: ngx_http_finalize_request(r, NGX_HTTP_CONFLICT);
243: return;
244: }
245: }
246:
247: dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
248:
249: ext.access = dlcf->access;
250: ext.path_access = dlcf->access;
251: ext.time = -1;
252: ext.create_path = dlcf->create_full_put_path;
253: ext.delete_file = 1;
254: ext.log = r->connection->log;
255:
256: if (r->headers_in.date) {
257: date = ngx_http_parse_time(r->headers_in.date->value.data,
258: r->headers_in.date->value.len);
259:
260: if (date != NGX_ERROR) {
261: ext.time = date;
262: ext.fd = r->request_body->temp_file->file.fd;
263: }
264: }
265:
266: if (ngx_ext_rename_file(temp, &path, &ext) != NGX_OK) {
267: ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
268: return;
269: }
270:
271: if (status == NGX_HTTP_CREATED) {
272: if (ngx_http_dav_location(r, path.data) != NGX_OK) {
273: ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
274: return;
275: }
276:
277: r->headers_out.content_length_n = 0;
278: }
279:
280: r->headers_out.status = status;
281: r->header_only = 1;
282:
283: ngx_http_finalize_request(r, ngx_http_send_header(r));
284: return;
285: }
286:
287:
288: static ngx_int_t
289: ngx_http_dav_delete_handler(ngx_http_request_t *r)
290: {
291: size_t root;
292: ngx_err_t err;
293: ngx_int_t rc, depth;
294: ngx_uint_t i, d, dir;
295: ngx_str_t path;
296: ngx_file_info_t fi;
297: ngx_http_dav_loc_conf_t *dlcf;
298:
299: if (r->headers_in.content_length_n > 0) {
300: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
301: "DELETE with body is unsupported");
302: return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
303: }
304:
305: dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
306:
307: if (dlcf->min_delete_depth) {
308: d = 0;
309:
310: for (i = 0; i < r->uri.len; /* void */) {
311: if (r->uri.data[i++] == '/') {
312: if (++d >= dlcf->min_delete_depth && i < r->uri.len) {
313: goto ok;
314: }
315: }
316: }
317:
318: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
319: "insufficient URI depth:%i to DELETE", d);
320: return NGX_HTTP_CONFLICT;
321: }
322:
323: ok:
324:
325: ngx_http_map_uri_to_path(r, &path, &root, 0);
326:
327: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
328: "http delete filename: \"%s\"", path.data);
329:
330: if (ngx_link_info(path.data, &fi) == NGX_FILE_ERROR) {
331: err = ngx_errno;
332:
333: rc = (err == NGX_ENOTDIR) ? NGX_HTTP_CONFLICT : NGX_HTTP_NOT_FOUND;
334:
335: return ngx_http_dav_error(r->connection->log, err,
336: rc, ngx_link_info_n, path.data);
337: }
338:
339: if (ngx_is_dir(&fi)) {
340:
341: if (r->uri.data[r->uri.len - 1] != '/') {
342: ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,
343: "DELETE \"%s\" failed", path.data);
344: return NGX_HTTP_CONFLICT;
345: }
346:
347: depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);
348:
349: if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
350: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
351: "\"Depth\" header must be infinity");
352: return NGX_HTTP_BAD_REQUEST;
353: }
354:
355: path.len -= 2; /* omit "/\0" */
356:
357: dir = 1;
358:
359: } else {
360:
361: /*
362: * we do not need to test (r->uri.data[r->uri.len - 1] == '/')
363: * because ngx_link_info("/file/") returned NGX_ENOTDIR above
364: */
365:
366: depth = ngx_http_dav_depth(r, 0);
367:
368: if (depth != 0 && depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
369: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
370: "\"Depth\" header must be 0 or infinity");
371: return NGX_HTTP_BAD_REQUEST;
372: }
373:
374: dir = 0;
375: }
376:
377: rc = ngx_http_dav_delete_path(r, &path, dir);
378:
379: if (rc == NGX_OK) {
380: return NGX_HTTP_NO_CONTENT;
381: }
382:
383: return rc;
384: }
385:
386:
387: static ngx_int_t
388: ngx_http_dav_delete_path(ngx_http_request_t *r, ngx_str_t *path, ngx_uint_t dir)
389: {
390: char *failed;
391: ngx_tree_ctx_t tree;
392:
393: if (dir) {
394:
395: tree.init_handler = NULL;
396: tree.file_handler = ngx_http_dav_delete_file;
397: tree.pre_tree_handler = ngx_http_dav_noop;
398: tree.post_tree_handler = ngx_http_dav_delete_dir;
399: tree.spec_handler = ngx_http_dav_delete_file;
400: tree.data = NULL;
401: tree.alloc = 0;
402: tree.log = r->connection->log;
403:
404: /* TODO: 207 */
405:
406: if (ngx_walk_tree(&tree, path) != NGX_OK) {
407: return NGX_HTTP_INTERNAL_SERVER_ERROR;
408: }
409:
410: if (ngx_delete_dir(path->data) != NGX_FILE_ERROR) {
411: return NGX_OK;
412: }
413:
414: failed = ngx_delete_dir_n;
415:
416: } else {
417:
418: if (ngx_delete_file(path->data) != NGX_FILE_ERROR) {
419: return NGX_OK;
420: }
421:
422: failed = ngx_delete_file_n;
423: }
424:
425: return ngx_http_dav_error(r->connection->log, ngx_errno,
426: NGX_HTTP_NOT_FOUND, failed, path->data);
427: }
428:
429:
430: static ngx_int_t
431: ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path)
432: {
433: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
434: "http delete dir: \"%s\"", path->data);
435:
436: if (ngx_delete_dir(path->data) == NGX_FILE_ERROR) {
437:
438: /* TODO: add to 207 */
439:
440: (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_delete_dir_n,
441: path->data);
442: }
443:
444: return NGX_OK;
445: }
446:
447:
448: static ngx_int_t
449: ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
450: {
451: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
452: "http delete file: \"%s\"", path->data);
453:
454: if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {
455:
456: /* TODO: add to 207 */
457:
458: (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_delete_file_n,
459: path->data);
460: }
461:
462: return NGX_OK;
463: }
464:
465:
466: static ngx_int_t
467: ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
468: {
469: return NGX_OK;
470: }
471:
472:
473: static ngx_int_t
474: ngx_http_dav_mkcol_handler(ngx_http_request_t *r, ngx_http_dav_loc_conf_t *dlcf)
475: {
476: u_char *p;
477: size_t root;
478: ngx_str_t path;
479:
480: if (r->headers_in.content_length_n > 0) {
481: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
482: "MKCOL with body is unsupported");
483: return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
484: }
485:
486: if (r->uri.data[r->uri.len - 1] != '/') {
487: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
488: "MKCOL can create a collection only");
489: return NGX_HTTP_CONFLICT;
490: }
491:
492: p = ngx_http_map_uri_to_path(r, &path, &root, 0);
493:
494: *(p - 1) = '\0';
495: r->uri.len--;
496:
497: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
498: "http mkcol path: \"%s\"", path.data);
499:
500: if (ngx_create_dir(path.data, ngx_dir_access(dlcf->access))
501: != NGX_FILE_ERROR)
502: {
503: if (ngx_http_dav_location(r, path.data) != NGX_OK) {
504: return NGX_HTTP_INTERNAL_SERVER_ERROR;
505: }
506:
507: return NGX_HTTP_CREATED;
508: }
509:
510: return ngx_http_dav_error(r->connection->log, ngx_errno,
511: NGX_HTTP_CONFLICT, ngx_create_dir_n, path.data);
512: }
513:
514:
515: static ngx_int_t
516: ngx_http_dav_copy_move_handler(ngx_http_request_t *r)
517: {
518: u_char *p, *host, *last, ch;
519: size_t len, root;
520: ngx_err_t err;
521: ngx_int_t rc, depth;
522: ngx_uint_t overwrite, slash, dir, flags;
523: ngx_str_t path, uri, duri, args;
524: ngx_tree_ctx_t tree;
525: ngx_copy_file_t cf;
526: ngx_file_info_t fi;
527: ngx_table_elt_t *dest, *over;
528: ngx_ext_rename_file_t ext;
529: ngx_http_dav_copy_ctx_t copy;
530: ngx_http_dav_loc_conf_t *dlcf;
531:
532: if (r->headers_in.content_length_n > 0) {
533: return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
534: }
535:
536: dest = r->headers_in.destination;
537:
538: if (dest == NULL) {
539: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
540: "client sent no \"Destination\" header");
541: return NGX_HTTP_BAD_REQUEST;
542: }
543:
544: p = dest->value.data;
545: /* there is always '\0' even after empty header value */
546: if (p[0] == '/') {
547: last = p + dest->value.len;
548: goto destination_done;
549: }
550:
551: len = r->headers_in.server.len;
552:
553: if (len == 0) {
554: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
555: "client sent no \"Host\" header");
556: return NGX_HTTP_BAD_REQUEST;
557: }
558:
559: #if (NGX_HTTP_SSL)
560:
561: if (r->connection->ssl) {
562: if (ngx_strncmp(dest->value.data, "https://", sizeof("https://") - 1)
563: != 0)
564: {
565: goto invalid_destination;
566: }
567:
568: host = dest->value.data + sizeof("https://") - 1;
569:
570: } else
571: #endif
572: {
573: if (ngx_strncmp(dest->value.data, "http://", sizeof("http://") - 1)
574: != 0)
575: {
576: goto invalid_destination;
577: }
578:
579: host = dest->value.data + sizeof("http://") - 1;
580: }
581:
582: if (ngx_strncmp(host, r->headers_in.server.data, len) != 0) {
583: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
584: "\"Destination\" URI \"%V\" is handled by "
585: "different repository than the source URI",
586: &dest->value);
587: return NGX_HTTP_BAD_REQUEST;
588: }
589:
590: last = dest->value.data + dest->value.len;
591:
592: for (p = host + len; p < last; p++) {
593: if (*p == '/') {
594: goto destination_done;
595: }
596: }
597:
598: invalid_destination:
599:
600: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
601: "client sent invalid \"Destination\" header: \"%V\"",
602: &dest->value);
603: return NGX_HTTP_BAD_REQUEST;
604:
605: destination_done:
606:
607: duri.len = last - p;
608: duri.data = p;
609: flags = 0;
610:
611: if (ngx_http_parse_unsafe_uri(r, &duri, &args, &flags) != NGX_OK) {
612: goto invalid_destination;
613: }
614:
615: if ((r->uri.data[r->uri.len - 1] == '/' && *(last - 1) != '/')
616: || (r->uri.data[r->uri.len - 1] != '/' && *(last - 1) == '/'))
617: {
618: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
619: "both URI \"%V\" and \"Destination\" URI \"%V\" "
620: "should be either collections or non-collections",
621: &r->uri, &dest->value);
622: return NGX_HTTP_CONFLICT;
623: }
624:
625: depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);
626:
627: if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
628:
629: if (r->method == NGX_HTTP_COPY) {
630: if (depth != 0) {
631: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
632: "\"Depth\" header must be 0 or infinity");
633: return NGX_HTTP_BAD_REQUEST;
634: }
635:
636: } else {
637: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
638: "\"Depth\" header must be infinity");
639: return NGX_HTTP_BAD_REQUEST;
640: }
641: }
642:
643: over = r->headers_in.overwrite;
644:
645: if (over) {
646: if (over->value.len == 1) {
647: ch = over->value.data[0];
648:
649: if (ch == 'T' || ch == 't') {
650: overwrite = 1;
651: goto overwrite_done;
652: }
653:
654: if (ch == 'F' || ch == 'f') {
655: overwrite = 0;
656: goto overwrite_done;
657: }
658:
659: }
660:
661: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
662: "client sent invalid \"Overwrite\" header: \"%V\"",
663: &over->value);
664: return NGX_HTTP_BAD_REQUEST;
665: }
666:
667: overwrite = 1;
668:
669: overwrite_done:
670:
671: ngx_http_map_uri_to_path(r, &path, &root, 0);
672:
673: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
674: "http copy from: \"%s\"", path.data);
675:
676: uri = r->uri;
677: r->uri = duri;
678:
679: ngx_http_map_uri_to_path(r, ©.path, &root, 0);
680:
681: r->uri = uri;
682:
683: copy.path.len--; /* omit "\0" */
684:
685: if (copy.path.data[copy.path.len - 1] == '/') {
686: slash = 1;
687: copy.path.len--;
688: copy.path.data[copy.path.len] = '\0';
689:
690: } else {
691: slash = 0;
692: }
693:
694: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
695: "http copy to: \"%s\"", copy.path.data);
696:
697: if (ngx_link_info(copy.path.data, &fi) == NGX_FILE_ERROR) {
698: err = ngx_errno;
699:
700: if (err != NGX_ENOENT) {
701: return ngx_http_dav_error(r->connection->log, err,
702: NGX_HTTP_NOT_FOUND, ngx_link_info_n,
703: copy.path.data);
704: }
705:
706: /* destination does not exist */
707:
708: overwrite = 0;
709: dir = 0;
710:
711: } else {
712:
713: /* destination exists */
714:
715: if (ngx_is_dir(&fi) && !slash) {
716: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
717: "\"%V\" could not be %Ved to collection \"%V\"",
718: &r->uri, &r->method_name, &dest->value);
719: return NGX_HTTP_CONFLICT;
720: }
721:
722: if (!overwrite) {
723: ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EEXIST,
724: "\"%s\" could not be created", copy.path.data);
725: return NGX_HTTP_PRECONDITION_FAILED;
726: }
727:
728: dir = ngx_is_dir(&fi);
729: }
730:
731: if (ngx_link_info(path.data, &fi) == NGX_FILE_ERROR) {
732: return ngx_http_dav_error(r->connection->log, ngx_errno,
733: NGX_HTTP_NOT_FOUND, ngx_link_info_n,
734: path.data);
735: }
736:
737: if (ngx_is_dir(&fi)) {
738:
739: if (r->uri.data[r->uri.len - 1] != '/') {
740: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
741: "\"%V\" is collection", &r->uri);
742: return NGX_HTTP_BAD_REQUEST;
743: }
744:
745: if (overwrite) {
746: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
747: "http delete: \"%s\"", copy.path.data);
748:
749: rc = ngx_http_dav_delete_path(r, ©.path, dir);
750:
751: if (rc != NGX_OK) {
752: return rc;
753: }
754: }
755: }
756:
757: if (ngx_is_dir(&fi)) {
758:
759: path.len -= 2; /* omit "/\0" */
760:
761: if (r->method == NGX_HTTP_MOVE) {
762: if (ngx_rename_file(path.data, copy.path.data) != NGX_FILE_ERROR) {
763: return NGX_HTTP_CREATED;
764: }
765: }
766:
767: if (ngx_create_dir(copy.path.data, ngx_file_access(&fi))
768: == NGX_FILE_ERROR)
769: {
770: return ngx_http_dav_error(r->connection->log, ngx_errno,
771: NGX_HTTP_NOT_FOUND,
772: ngx_create_dir_n, copy.path.data);
773: }
774:
775: copy.len = path.len;
776:
777: tree.init_handler = NULL;
778: tree.file_handler = ngx_http_dav_copy_tree_file;
779: tree.pre_tree_handler = ngx_http_dav_copy_dir;
780: tree.post_tree_handler = ngx_http_dav_copy_dir_time;
781: tree.spec_handler = ngx_http_dav_noop;
782: tree.data = ©
783: tree.alloc = 0;
784: tree.log = r->connection->log;
785:
786: if (ngx_walk_tree(&tree, &path) == NGX_OK) {
787:
788: if (r->method == NGX_HTTP_MOVE) {
789: rc = ngx_http_dav_delete_path(r, &path, 1);
790:
791: if (rc != NGX_OK) {
792: return rc;
793: }
794: }
795:
796: return NGX_HTTP_CREATED;
797: }
798:
799: } else {
800:
801: if (r->method == NGX_HTTP_MOVE) {
802:
803: dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
804:
805: ext.access = 0;
806: ext.path_access = dlcf->access;
807: ext.time = -1;
808: ext.create_path = 1;
809: ext.delete_file = 0;
810: ext.log = r->connection->log;
811:
812: if (ngx_ext_rename_file(&path, ©.path, &ext) == NGX_OK) {
813: return NGX_HTTP_NO_CONTENT;
814: }
815:
816: return NGX_HTTP_INTERNAL_SERVER_ERROR;
817: }
818:
819: dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
820:
821: cf.size = ngx_file_size(&fi);
822: cf.buf_size = 0;
823: cf.access = dlcf->access;
824: cf.time = ngx_file_mtime(&fi);
825: cf.log = r->connection->log;
826:
827: if (ngx_copy_file(path.data, copy.path.data, &cf) == NGX_OK) {
828: return NGX_HTTP_NO_CONTENT;
829: }
830: }
831:
832: return NGX_HTTP_INTERNAL_SERVER_ERROR;
833: }
834:
835:
836: static ngx_int_t
837: ngx_http_dav_copy_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path)
838: {
839: u_char *p, *dir;
840: size_t len;
841: ngx_http_dav_copy_ctx_t *copy;
842:
843: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
844: "http copy dir: \"%s\"", path->data);
845:
846: copy = ctx->data;
847:
848: len = copy->path.len + path->len;
849:
850: dir = ngx_alloc(len + 1, ctx->log);
851: if (dir == NULL) {
852: return NGX_ABORT;
853: }
854:
855: p = ngx_cpymem(dir, copy->path.data, copy->path.len);
856: (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);
857:
858: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
859: "http copy dir to: \"%s\"", dir);
860:
861: if (ngx_create_dir(dir, ngx_dir_access(ctx->access)) == NGX_FILE_ERROR) {
862: (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_create_dir_n,
863: dir);
864: }
865:
866: ngx_free(dir);
867:
868: return NGX_OK;
869: }
870:
871:
872: static ngx_int_t
873: ngx_http_dav_copy_dir_time(ngx_tree_ctx_t *ctx, ngx_str_t *path)
874: {
875: u_char *p, *dir;
876: size_t len;
877: ngx_http_dav_copy_ctx_t *copy;
878:
879: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
880: "http copy dir time: \"%s\"", path->data);
881:
882: copy = ctx->data;
883:
884: len = copy->path.len + path->len;
885:
886: dir = ngx_alloc(len + 1, ctx->log);
887: if (dir == NULL) {
888: return NGX_ABORT;
889: }
890:
891: p = ngx_cpymem(dir, copy->path.data, copy->path.len);
892: (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);
893:
894: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
895: "http copy dir time to: \"%s\"", dir);
896:
897: #if (NGX_WIN32)
898: {
899: ngx_fd_t fd;
900:
901: fd = ngx_open_file(dir, NGX_FILE_RDWR, NGX_FILE_OPEN, 0);
902:
903: if (fd == NGX_INVALID_FILE) {
904: (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_open_file_n, dir);
905: goto failed;
906: }
907:
908: if (ngx_set_file_time(NULL, fd, ctx->mtime) != NGX_OK) {
909: ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
910: ngx_set_file_time_n " \"%s\" failed", dir);
911: }
912:
913: if (ngx_close_file(fd) == NGX_FILE_ERROR) {
914: ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
915: ngx_close_file_n " \"%s\" failed", dir);
916: }
917: }
918:
919: failed:
920:
921: #else
922:
923: if (ngx_set_file_time(dir, 0, ctx->mtime) != NGX_OK) {
924: ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
925: ngx_set_file_time_n " \"%s\" failed", dir);
926: }
927:
928: #endif
929:
930: ngx_free(dir);
931:
932: return NGX_OK;
933: }
934:
935:
936: static ngx_int_t
937: ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
938: {
939: u_char *p, *file;
940: size_t len;
941: ngx_copy_file_t cf;
942: ngx_http_dav_copy_ctx_t *copy;
943:
944: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
945: "http copy file: \"%s\"", path->data);
946:
947: copy = ctx->data;
948:
949: len = copy->path.len + path->len;
950:
951: file = ngx_alloc(len + 1, ctx->log);
952: if (file == NULL) {
953: return NGX_ABORT;
954: }
955:
956: p = ngx_cpymem(file, copy->path.data, copy->path.len);
957: (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);
958:
959: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
960: "http copy file to: \"%s\"", file);
961:
962: cf.size = ctx->size;
963: cf.buf_size = 0;
964: cf.access = ctx->access;
965: cf.time = ctx->mtime;
966: cf.log = ctx->log;
967:
968: (void) ngx_copy_file(path->data, file, &cf);
969:
970: ngx_free(file);
971:
972: return NGX_OK;
973: }
974:
975:
976: static ngx_int_t
977: ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt)
978: {
979: ngx_table_elt_t *depth;
980:
981: depth = r->headers_in.depth;
982:
983: if (depth == NULL) {
984: return dflt;
985: }
986:
987: if (depth->value.len == 1) {
988:
989: if (depth->value.data[0] == '0') {
990: return 0;
991: }
992:
993: if (depth->value.data[0] == '1') {
994: return 1;
995: }
996:
997: } else {
998:
999: if (depth->value.len == sizeof("infinity") - 1
1000: && ngx_strcmp(depth->value.data, "infinity") == 0)
1001: {
1002: return NGX_HTTP_DAV_INFINITY_DEPTH;
1003: }
1004: }
1005:
1006: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1007: "client sent invalid \"Depth\" header: \"%V\"",
1008: &depth->value);
1009:
1010: return NGX_HTTP_DAV_INVALID_DEPTH;
1011: }
1012:
1013:
1014: static ngx_int_t
1015: ngx_http_dav_error(ngx_log_t *log, ngx_err_t err, ngx_int_t not_found,
1016: char *failed, u_char *path)
1017: {
1018: ngx_int_t rc;
1019: ngx_uint_t level;
1020:
1021: if (err == NGX_ENOENT || err == NGX_ENOTDIR || err == NGX_ENAMETOOLONG) {
1022: level = NGX_LOG_ERR;
1023: rc = not_found;
1024:
1025: } else if (err == NGX_EACCES || err == NGX_EPERM) {
1026: level = NGX_LOG_ERR;
1027: rc = NGX_HTTP_FORBIDDEN;
1028:
1029: } else if (err == NGX_EEXIST) {
1030: level = NGX_LOG_ERR;
1031: rc = NGX_HTTP_NOT_ALLOWED;
1032:
1033: } else if (err == NGX_ENOSPC) {
1034: level = NGX_LOG_CRIT;
1035: rc = NGX_HTTP_INSUFFICIENT_STORAGE;
1036:
1037: } else {
1038: level = NGX_LOG_CRIT;
1039: rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
1040: }
1041:
1042: ngx_log_error(level, log, err, "%s \"%s\" failed", failed, path);
1043:
1044: return rc;
1045: }
1046:
1047:
1048: static ngx_int_t
1049: ngx_http_dav_location(ngx_http_request_t *r, u_char *path)
1050: {
1051: u_char *location;
1052: ngx_http_core_loc_conf_t *clcf;
1053:
1054: r->headers_out.location = ngx_palloc(r->pool, sizeof(ngx_table_elt_t));
1055: if (r->headers_out.location == NULL) {
1056: return NGX_ERROR;
1057: }
1058:
1059: clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
1060:
1061: if (!clcf->alias && clcf->root_lengths == NULL) {
1062: location = path + clcf->root.len;
1063:
1064: } else {
1065: location = ngx_pnalloc(r->pool, r->uri.len);
1066: if (location == NULL) {
1067: return NGX_ERROR;
1068: }
1069:
1070: ngx_memcpy(location, r->uri.data, r->uri.len);
1071: }
1072:
1073: /*
1074: * we do not need to set the r->headers_out.location->hash and
1075: * r->headers_out.location->key fields
1076: */
1077:
1078: r->headers_out.location->value.len = r->uri.len;
1079: r->headers_out.location->value.data = location;
1080:
1081: return NGX_OK;
1082: }
1083:
1084:
1085: static void *
1086: ngx_http_dav_create_loc_conf(ngx_conf_t *cf)
1087: {
1088: ngx_http_dav_loc_conf_t *conf;
1089:
1090: conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dav_loc_conf_t));
1091: if (conf == NULL) {
1092: return NULL;
1093: }
1094:
1095: /*
1096: * set by ngx_pcalloc():
1097: *
1098: * conf->methods = 0;
1099: */
1100:
1101: conf->min_delete_depth = NGX_CONF_UNSET_UINT;
1102: conf->access = NGX_CONF_UNSET_UINT;
1103: conf->create_full_put_path = NGX_CONF_UNSET;
1104:
1105: return conf;
1106: }
1107:
1108:
1109: static char *
1110: ngx_http_dav_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
1111: {
1112: ngx_http_dav_loc_conf_t *prev = parent;
1113: ngx_http_dav_loc_conf_t *conf = child;
1114:
1115: ngx_conf_merge_bitmask_value(conf->methods, prev->methods,
1116: (NGX_CONF_BITMASK_SET|NGX_HTTP_DAV_OFF));
1117:
1118: ngx_conf_merge_uint_value(conf->min_delete_depth,
1119: prev->min_delete_depth, 0);
1120:
1121: ngx_conf_merge_uint_value(conf->access, prev->access, 0600);
1122:
1123: ngx_conf_merge_value(conf->create_full_put_path,
1124: prev->create_full_put_path, 0);
1125:
1126: return NGX_CONF_OK;
1127: }
1128:
1129:
1130: static ngx_int_t
1131: ngx_http_dav_init(ngx_conf_t *cf)
1132: {
1133: ngx_http_handler_pt *h;
1134: ngx_http_core_main_conf_t *cmcf;
1135:
1136: cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
1137:
1138: h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
1139: if (h == NULL) {
1140: return NGX_ERROR;
1141: }
1142:
1143: *h = ngx_http_dav_handler;
1144:
1145: return NGX_OK;
1146: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>