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