~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Nginx/http/modules/ngx_http_dav_module.c

Version: ~ [ nginx-1.4.1 ] ~ [ nginx-1.5.0 ] ~

  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, &copy.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, &copy.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 = &copy;
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, &copy.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] == '') {
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 }
1147 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.