Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.13.12 ]​[ nginx-1.12.2 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_http.h>
0011 
0012 
0013 #define NGX_HTTP_DAV_OFF             2
0014 
0015 
0016 #define NGX_HTTP_DAV_NO_DEPTH        -3
0017 #define NGX_HTTP_DAV_INVALID_DEPTH   -2
0018 #define NGX_HTTP_DAV_INFINITY_DEPTH  -1
0019 
0020 
0021 typedef struct {
0022     ngx_uint_t  methods;
0023     ngx_uint_t  access;
0024     ngx_uint_t  min_delete_depth;
0025     ngx_flag_t  create_full_put_path;
0026 } ngx_http_dav_loc_conf_t;
0027 
0028 
0029 typedef struct {
0030     ngx_str_t   path;
0031     size_t      len;
0032 } ngx_http_dav_copy_ctx_t;
0033 
0034 
0035 static ngx_int_t ngx_http_dav_handler(ngx_http_request_t *r);
0036 
0037 static void ngx_http_dav_put_handler(ngx_http_request_t *r);
0038 
0039 static ngx_int_t ngx_http_dav_delete_handler(ngx_http_request_t *r);
0040 static ngx_int_t ngx_http_dav_delete_path(ngx_http_request_t *r,
0041     ngx_str_t *path, ngx_uint_t dir);
0042 static ngx_int_t ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path);
0043 static ngx_int_t ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path);
0044 static ngx_int_t ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path);
0045 
0046 static ngx_int_t ngx_http_dav_mkcol_handler(ngx_http_request_t *r,
0047     ngx_http_dav_loc_conf_t *dlcf);
0048 
0049 static ngx_int_t ngx_http_dav_copy_move_handler(ngx_http_request_t *r);
0050 static ngx_int_t ngx_http_dav_copy_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path);
0051 static ngx_int_t ngx_http_dav_copy_dir_time(ngx_tree_ctx_t *ctx,
0052     ngx_str_t *path);
0053 static ngx_int_t ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx,
0054     ngx_str_t *path);
0055 
0056 static ngx_int_t ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt);
0057 static ngx_int_t ngx_http_dav_error(ngx_log_t *log, ngx_err_t err,
0058     ngx_int_t not_found, char *failed, u_char *path);
0059 static ngx_int_t ngx_http_dav_location(ngx_http_request_t *r, u_char *path);
0060 static void *ngx_http_dav_create_loc_conf(ngx_conf_t *cf);
0061 static char *ngx_http_dav_merge_loc_conf(ngx_conf_t *cf,
0062     void *parent, void *child);
0063 static ngx_int_t ngx_http_dav_init(ngx_conf_t *cf);
0064 
0065 
0066 static ngx_conf_bitmask_t  ngx_http_dav_methods_mask[] = {
0067     { ngx_string("off"), NGX_HTTP_DAV_OFF },
0068     { ngx_string("put"), NGX_HTTP_PUT },
0069     { ngx_string("delete"), NGX_HTTP_DELETE },
0070     { ngx_string("mkcol"), NGX_HTTP_MKCOL },
0071     { ngx_string("copy"), NGX_HTTP_COPY },
0072     { ngx_string("move"), NGX_HTTP_MOVE },
0073     { ngx_null_string, 0 }
0074 };
0075 
0076 
0077 static ngx_command_t  ngx_http_dav_commands[] = {
0078 
0079     { ngx_string("dav_methods"),
0080       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
0081       ngx_conf_set_bitmask_slot,
0082       NGX_HTTP_LOC_CONF_OFFSET,
0083       offsetof(ngx_http_dav_loc_conf_t, methods),
0084       &ngx_http_dav_methods_mask },
0085 
0086     { ngx_string("create_full_put_path"),
0087       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
0088       ngx_conf_set_flag_slot,
0089       NGX_HTTP_LOC_CONF_OFFSET,
0090       offsetof(ngx_http_dav_loc_conf_t, create_full_put_path),
0091       NULL },
0092 
0093     { ngx_string("min_delete_depth"),
0094       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
0095       ngx_conf_set_num_slot,
0096       NGX_HTTP_LOC_CONF_OFFSET,
0097       offsetof(ngx_http_dav_loc_conf_t, min_delete_depth),
0098       NULL },
0099 
0100     { ngx_string("dav_access"),
0101       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
0102       ngx_conf_set_access_slot,
0103       NGX_HTTP_LOC_CONF_OFFSET,
0104       offsetof(ngx_http_dav_loc_conf_t, access),
0105       NULL },
0106 
0107       ngx_null_command
0108 };
0109 
0110 
0111 static ngx_http_module_t  ngx_http_dav_module_ctx = {
0112     NULL,                                  /* preconfiguration */
0113     ngx_http_dav_init,                     /* postconfiguration */
0114 
0115     NULL,                                  /* create main configuration */
0116     NULL,                                  /* init main configuration */
0117 
0118     NULL,                                  /* create server configuration */
0119     NULL,                                  /* merge server configuration */
0120 
0121     ngx_http_dav_create_loc_conf,          /* create location configuration */
0122     ngx_http_dav_merge_loc_conf            /* merge location configuration */
0123 };
0124 
0125 
0126 ngx_module_t  ngx_http_dav_module = {
0127     NGX_MODULE_V1,
0128     &ngx_http_dav_module_ctx,              /* module context */
0129     ngx_http_dav_commands,                 /* module directives */
0130     NGX_HTTP_MODULE,                       /* module type */
0131     NULL,                                  /* init master */
0132     NULL,                                  /* init module */
0133     NULL,                                  /* init process */
0134     NULL,                                  /* init thread */
0135     NULL,                                  /* exit thread */
0136     NULL,                                  /* exit process */
0137     NULL,                                  /* exit master */
0138     NGX_MODULE_V1_PADDING
0139 };
0140 
0141 
0142 static ngx_int_t
0143 ngx_http_dav_handler(ngx_http_request_t *r)
0144 {
0145     ngx_int_t                 rc;
0146     ngx_http_dav_loc_conf_t  *dlcf;
0147 
0148     dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
0149 
0150     if (!(r->method & dlcf->methods)) {
0151         return NGX_DECLINED;
0152     }
0153 
0154     switch (r->method) {
0155 
0156     case NGX_HTTP_PUT:
0157 
0158         if (r->uri.data[r->uri.len - 1] == '/') {
0159             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0160                           "cannot PUT to a collection");
0161             return NGX_HTTP_CONFLICT;
0162         }
0163 
0164         if (r->headers_in.content_range) {
0165             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0166                           "PUT with range is unsupported");
0167             return NGX_HTTP_NOT_IMPLEMENTED;
0168         }
0169 
0170         r->request_body_in_file_only = 1;
0171         r->request_body_in_persistent_file = 1;
0172         r->request_body_in_clean_file = 1;
0173         r->request_body_file_group_access = 1;
0174         r->request_body_file_log_level = 0;
0175 
0176         rc = ngx_http_read_client_request_body(r, ngx_http_dav_put_handler);
0177 
0178         if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
0179             return rc;
0180         }
0181 
0182         return NGX_DONE;
0183 
0184     case NGX_HTTP_DELETE:
0185 
0186         return ngx_http_dav_delete_handler(r);
0187 
0188     case NGX_HTTP_MKCOL:
0189 
0190         return ngx_http_dav_mkcol_handler(r, dlcf);
0191 
0192     case NGX_HTTP_COPY:
0193 
0194         return ngx_http_dav_copy_move_handler(r);
0195 
0196     case NGX_HTTP_MOVE:
0197 
0198         return ngx_http_dav_copy_move_handler(r);
0199     }
0200 
0201     return NGX_DECLINED;
0202 }
0203 
0204 
0205 static void
0206 ngx_http_dav_put_handler(ngx_http_request_t *r)
0207 {
0208     size_t                    root;
0209     time_t                    date;
0210     ngx_str_t                *temp, path;
0211     ngx_uint_t                status;
0212     ngx_file_info_t           fi;
0213     ngx_ext_rename_file_t     ext;
0214     ngx_http_dav_loc_conf_t  *dlcf;
0215 
0216     if (r->request_body == NULL || r->request_body->temp_file == NULL) {
0217         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
0218         return;
0219     }
0220 
0221     if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
0222         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
0223         return;
0224     }
0225 
0226     path.len--;
0227 
0228     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0229                    "http put filename: \"%s\"", path.data);
0230 
0231     temp = &r->request_body->temp_file->file.name;
0232 
0233     if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) {
0234         status = NGX_HTTP_CREATED;
0235 
0236     } else {
0237         status = NGX_HTTP_NO_CONTENT;
0238 
0239         if (ngx_is_dir(&fi)) {
0240             ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,
0241                           "\"%s\" could not be created", path.data);
0242 
0243             if (ngx_delete_file(temp->data) == NGX_FILE_ERROR) {
0244                 ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
0245                               ngx_delete_file_n " \"%s\" failed",
0246                               temp->data);
0247             }
0248 
0249             ngx_http_finalize_request(r, NGX_HTTP_CONFLICT);
0250             return;
0251         }
0252     }
0253 
0254     dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
0255 
0256     ext.access = dlcf->access;
0257     ext.path_access = dlcf->access;
0258     ext.time = -1;
0259     ext.create_path = dlcf->create_full_put_path;
0260     ext.delete_file = 1;
0261     ext.log = r->connection->log;
0262 
0263     if (r->headers_in.date) {
0264         date = ngx_parse_http_time(r->headers_in.date->value.data,
0265                                    r->headers_in.date->value.len);
0266 
0267         if (date != NGX_ERROR) {
0268             ext.time = date;
0269             ext.fd = r->request_body->temp_file->file.fd;
0270         }
0271     }
0272 
0273     if (ngx_ext_rename_file(temp, &path, &ext) != NGX_OK) {
0274         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
0275         return;
0276     }
0277 
0278     if (status == NGX_HTTP_CREATED) {
0279         if (ngx_http_dav_location(r, path.data) != NGX_OK) {
0280             ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
0281             return;
0282         }
0283 
0284         r->headers_out.content_length_n = 0;
0285     }
0286 
0287     r->headers_out.status = status;
0288     r->header_only = 1;
0289 
0290     ngx_http_finalize_request(r, ngx_http_send_header(r));
0291     return;
0292 }
0293 
0294 
0295 static ngx_int_t
0296 ngx_http_dav_delete_handler(ngx_http_request_t *r)
0297 {
0298     size_t                    root;
0299     ngx_err_t                 err;
0300     ngx_int_t                 rc, depth;
0301     ngx_uint_t                i, d, dir;
0302     ngx_str_t                 path;
0303     ngx_file_info_t           fi;
0304     ngx_http_dav_loc_conf_t  *dlcf;
0305 
0306     if (r->headers_in.content_length_n > 0) {
0307         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0308                       "DELETE with body is unsupported");
0309         return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
0310     }
0311 
0312     dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
0313 
0314     if (dlcf->min_delete_depth) {
0315         d = 0;
0316 
0317         for (i = 0; i < r->uri.len; /* void */) {
0318             if (r->uri.data[i++] == '/') {
0319                 if (++d >= dlcf->min_delete_depth && i < r->uri.len) {
0320                     goto ok;
0321                 }
0322             }
0323         }
0324 
0325         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0326                       "insufficient URI depth:%i to DELETE", d);
0327         return NGX_HTTP_CONFLICT;
0328     }
0329 
0330 ok:
0331 
0332     if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
0333         return NGX_HTTP_INTERNAL_SERVER_ERROR;
0334     }
0335 
0336     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0337                    "http delete filename: \"%s\"", path.data);
0338 
0339     if (ngx_link_info(path.data, &fi) == NGX_FILE_ERROR) {
0340         err = ngx_errno;
0341 
0342         rc = (err == NGX_ENOTDIR) ? NGX_HTTP_CONFLICT : NGX_HTTP_NOT_FOUND;
0343 
0344         return ngx_http_dav_error(r->connection->log, err,
0345                                   rc, ngx_link_info_n, path.data);
0346     }
0347 
0348     if (ngx_is_dir(&fi)) {
0349 
0350         if (r->uri.data[r->uri.len - 1] != '/') {
0351             ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,
0352                           "DELETE \"%s\" failed", path.data);
0353             return NGX_HTTP_CONFLICT;
0354         }
0355 
0356         depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);
0357 
0358         if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
0359             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0360                           "\"Depth\" header must be infinity");
0361             return NGX_HTTP_BAD_REQUEST;
0362         }
0363 
0364         path.len -= 2;  /* omit "/\0" */
0365 
0366         dir = 1;
0367 
0368     } else {
0369 
0370         /*
0371          * we do not need to test (r->uri.data[r->uri.len - 1] == '/')
0372          * because ngx_link_info("/file/") returned NGX_ENOTDIR above
0373          */
0374 
0375         depth = ngx_http_dav_depth(r, 0);
0376 
0377         if (depth != 0 && depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
0378             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0379                           "\"Depth\" header must be 0 or infinity");
0380             return NGX_HTTP_BAD_REQUEST;
0381         }
0382 
0383         dir = 0;
0384     }
0385 
0386     rc = ngx_http_dav_delete_path(r, &path, dir);
0387 
0388     if (rc == NGX_OK) {
0389         return NGX_HTTP_NO_CONTENT;
0390     }
0391 
0392     return rc;
0393 }
0394 
0395 
0396 static ngx_int_t
0397 ngx_http_dav_delete_path(ngx_http_request_t *r, ngx_str_t *path, ngx_uint_t dir)
0398 {
0399     char            *failed;
0400     ngx_tree_ctx_t   tree;
0401 
0402     if (dir) {
0403 
0404         tree.init_handler = NULL;
0405         tree.file_handler = ngx_http_dav_delete_file;
0406         tree.pre_tree_handler = ngx_http_dav_noop;
0407         tree.post_tree_handler = ngx_http_dav_delete_dir;
0408         tree.spec_handler = ngx_http_dav_delete_file;
0409         tree.data = NULL;
0410         tree.alloc = 0;
0411         tree.log = r->connection->log;
0412 
0413         /* TODO: 207 */
0414 
0415         if (ngx_walk_tree(&tree, path) != NGX_OK) {
0416             return NGX_HTTP_INTERNAL_SERVER_ERROR;
0417         }
0418 
0419         if (ngx_delete_dir(path->data) != NGX_FILE_ERROR) {
0420             return NGX_OK;
0421         }
0422 
0423         failed = ngx_delete_dir_n;
0424 
0425     } else {
0426 
0427         if (ngx_delete_file(path->data) != NGX_FILE_ERROR) {
0428             return NGX_OK;
0429         }
0430 
0431         failed = ngx_delete_file_n;
0432     }
0433 
0434     return ngx_http_dav_error(r->connection->log, ngx_errno,
0435                               NGX_HTTP_NOT_FOUND, failed, path->data);
0436 }
0437 
0438 
0439 static ngx_int_t
0440 ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path)
0441 {
0442     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
0443                    "http delete dir: \"%s\"", path->data);
0444 
0445     if (ngx_delete_dir(path->data) == NGX_FILE_ERROR) {
0446 
0447         /* TODO: add to 207 */
0448 
0449         (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_delete_dir_n,
0450                                   path->data);
0451     }
0452 
0453     return NGX_OK;
0454 }
0455 
0456 
0457 static ngx_int_t
0458 ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
0459 {
0460     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
0461                    "http delete file: \"%s\"", path->data);
0462 
0463     if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {
0464 
0465         /* TODO: add to 207 */
0466 
0467         (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_delete_file_n,
0468                                   path->data);
0469     }
0470 
0471     return NGX_OK;
0472 }
0473 
0474 
0475 static ngx_int_t
0476 ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
0477 {
0478     return NGX_OK;
0479 }
0480 
0481 
0482 static ngx_int_t
0483 ngx_http_dav_mkcol_handler(ngx_http_request_t *r, ngx_http_dav_loc_conf_t *dlcf)
0484 {
0485     u_char    *p;
0486     size_t     root;
0487     ngx_str_t  path;
0488 
0489     if (r->headers_in.content_length_n > 0) {
0490         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0491                       "MKCOL with body is unsupported");
0492         return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
0493     }
0494 
0495     if (r->uri.data[r->uri.len - 1] != '/') {
0496         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0497                       "MKCOL can create a collection only");
0498         return NGX_HTTP_CONFLICT;
0499     }
0500 
0501     p = ngx_http_map_uri_to_path(r, &path, &root, 0);
0502     if (p == NULL) {
0503         return NGX_HTTP_INTERNAL_SERVER_ERROR;
0504     }
0505 
0506     *(p - 1) = '\0';
0507     r->uri.len--;
0508 
0509     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0510                    "http mkcol path: \"%s\"", path.data);
0511 
0512     if (ngx_create_dir(path.data, ngx_dir_access(dlcf->access))
0513         != NGX_FILE_ERROR)
0514     {
0515         if (ngx_http_dav_location(r, path.data) != NGX_OK) {
0516             return NGX_HTTP_INTERNAL_SERVER_ERROR;
0517         }
0518 
0519         return NGX_HTTP_CREATED;
0520     }
0521 
0522     return ngx_http_dav_error(r->connection->log, ngx_errno,
0523                               NGX_HTTP_CONFLICT, ngx_create_dir_n, path.data);
0524 }
0525 
0526 
0527 static ngx_int_t
0528 ngx_http_dav_copy_move_handler(ngx_http_request_t *r)
0529 {
0530     u_char                   *p, *host, *last, ch;
0531     size_t                    len, root;
0532     ngx_err_t                 err;
0533     ngx_int_t                 rc, depth;
0534     ngx_uint_t                overwrite, slash, dir, flags;
0535     ngx_str_t                 path, uri, duri, args;
0536     ngx_tree_ctx_t            tree;
0537     ngx_copy_file_t           cf;
0538     ngx_file_info_t           fi;
0539     ngx_table_elt_t          *dest, *over;
0540     ngx_ext_rename_file_t     ext;
0541     ngx_http_dav_copy_ctx_t   copy;
0542     ngx_http_dav_loc_conf_t  *dlcf;
0543 
0544     if (r->headers_in.content_length_n > 0) {
0545         return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
0546     }
0547 
0548     dest = r->headers_in.destination;
0549 
0550     if (dest == NULL) {
0551         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0552                       "client sent no \"Destination\" header");
0553         return NGX_HTTP_BAD_REQUEST;
0554     }
0555 
0556     p = dest->value.data;
0557     /* there is always '\0' even after empty header value */
0558     if (p[0] == '/') {
0559         last = p + dest->value.len;
0560         goto destination_done;
0561     }
0562 
0563     len = r->headers_in.server.len;
0564 
0565     if (len == 0) {
0566         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0567                       "client sent no \"Host\" header");
0568         return NGX_HTTP_BAD_REQUEST;
0569     }
0570 
0571 #if (NGX_HTTP_SSL)
0572 
0573     if (r->connection->ssl) {
0574         if (ngx_strncmp(dest->value.data, "https://", sizeof("https://") - 1)
0575             != 0)
0576         {
0577             goto invalid_destination;
0578         }
0579 
0580         host = dest->value.data + sizeof("https://") - 1;
0581 
0582     } else
0583 #endif
0584     {
0585         if (ngx_strncmp(dest->value.data, "http://", sizeof("http://") - 1)
0586             != 0)
0587         {
0588             goto invalid_destination;
0589         }
0590 
0591         host = dest->value.data + sizeof("http://") - 1;
0592     }
0593 
0594     if (ngx_strncmp(host, r->headers_in.server.data, len) != 0) {
0595         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0596                       "\"Destination\" URI \"%V\" is handled by "
0597                       "different repository than the source URI",
0598                       &dest->value);
0599         return NGX_HTTP_BAD_REQUEST;
0600     }
0601 
0602     last = dest->value.data + dest->value.len;
0603 
0604     for (p = host + len; p < last; p++) {
0605         if (*p == '/') {
0606             goto destination_done;
0607         }
0608     }
0609 
0610 invalid_destination:
0611 
0612     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0613                   "client sent invalid \"Destination\" header: \"%V\"",
0614                   &dest->value);
0615     return NGX_HTTP_BAD_REQUEST;
0616 
0617 destination_done:
0618 
0619     duri.len = last - p;
0620     duri.data = p;
0621     flags = NGX_HTTP_LOG_UNSAFE;
0622 
0623     if (ngx_http_parse_unsafe_uri(r, &duri, &args, &flags) != NGX_OK) {
0624         goto invalid_destination;
0625     }
0626 
0627     if ((r->uri.data[r->uri.len - 1] == '/' && *(last - 1) != '/')
0628         || (r->uri.data[r->uri.len - 1] != '/' && *(last - 1) == '/'))
0629     {
0630         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0631                       "both URI \"%V\" and \"Destination\" URI \"%V\" "
0632                       "should be either collections or non-collections",
0633                       &r->uri, &dest->value);
0634         return NGX_HTTP_CONFLICT;
0635     }
0636 
0637     depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);
0638 
0639     if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
0640 
0641         if (r->method == NGX_HTTP_COPY) {
0642             if (depth != 0) {
0643                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0644                               "\"Depth\" header must be 0 or infinity");
0645                 return NGX_HTTP_BAD_REQUEST;
0646             }
0647 
0648         } else {
0649             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0650                           "\"Depth\" header must be infinity");
0651             return NGX_HTTP_BAD_REQUEST;
0652         }
0653     }
0654 
0655     over = r->headers_in.overwrite;
0656 
0657     if (over) {
0658         if (over->value.len == 1) {
0659             ch = over->value.data[0];
0660 
0661             if (ch == 'T' || ch == 't') {
0662                 overwrite = 1;
0663                 goto overwrite_done;
0664             }
0665 
0666             if (ch == 'F' || ch == 'f') {
0667                 overwrite = 0;
0668                 goto overwrite_done;
0669             }
0670 
0671         }
0672 
0673         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0674                       "client sent invalid \"Overwrite\" header: \"%V\"",
0675                       &over->value);
0676         return NGX_HTTP_BAD_REQUEST;
0677     }
0678 
0679     overwrite = 1;
0680 
0681 overwrite_done:
0682 
0683     if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
0684         return NGX_HTTP_INTERNAL_SERVER_ERROR;
0685     }
0686 
0687     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0688                    "http copy from: \"%s\"", path.data);
0689 
0690     uri = r->uri;
0691     r->uri = duri;
0692 
0693     if (ngx_http_map_uri_to_path(r, &copy.path, &root, 0) == NULL) {
0694         return NGX_HTTP_INTERNAL_SERVER_ERROR;
0695     }
0696 
0697     r->uri = uri;
0698 
0699     copy.path.len--;  /* omit "\0" */
0700 
0701     if (copy.path.data[copy.path.len - 1] == '/') {
0702         slash = 1;
0703         copy.path.len--;
0704         copy.path.data[copy.path.len] = '\0';
0705 
0706     } else {
0707         slash = 0;
0708     }
0709 
0710     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0711                    "http copy to: \"%s\"", copy.path.data);
0712 
0713     if (ngx_link_info(copy.path.data, &fi) == NGX_FILE_ERROR) {
0714         err = ngx_errno;
0715 
0716         if (err != NGX_ENOENT) {
0717             return ngx_http_dav_error(r->connection->log, err,
0718                                       NGX_HTTP_NOT_FOUND, ngx_link_info_n,
0719                                       copy.path.data);
0720         }
0721 
0722         /* destination does not exist */
0723 
0724         overwrite = 0;
0725         dir = 0;
0726 
0727     } else {
0728 
0729         /* destination exists */
0730 
0731         if (ngx_is_dir(&fi) && !slash) {
0732             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0733                           "\"%V\" could not be %Ved to collection \"%V\"",
0734                           &r->uri, &r->method_name, &dest->value);
0735             return NGX_HTTP_CONFLICT;
0736         }
0737 
0738         if (!overwrite) {
0739             ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EEXIST,
0740                           "\"%s\" could not be created", copy.path.data);
0741             return NGX_HTTP_PRECONDITION_FAILED;
0742         }
0743 
0744         dir = ngx_is_dir(&fi);
0745     }
0746 
0747     if (ngx_link_info(path.data, &fi) == NGX_FILE_ERROR) {
0748         return ngx_http_dav_error(r->connection->log, ngx_errno,
0749                                   NGX_HTTP_NOT_FOUND, ngx_link_info_n,
0750                                   path.data);
0751     }
0752 
0753     if (ngx_is_dir(&fi)) {
0754 
0755         if (r->uri.data[r->uri.len - 1] != '/') {
0756             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0757                           "\"%V\" is collection", &r->uri);
0758             return NGX_HTTP_BAD_REQUEST;
0759         }
0760 
0761         if (overwrite) {
0762             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0763                            "http delete: \"%s\"", copy.path.data);
0764 
0765             rc = ngx_http_dav_delete_path(r, &copy.path, dir);
0766 
0767             if (rc != NGX_OK) {
0768                 return rc;
0769             }
0770         }
0771     }
0772 
0773     if (ngx_is_dir(&fi)) {
0774 
0775         path.len -= 2;  /* omit "/\0" */
0776 
0777         if (r->method == NGX_HTTP_MOVE) {
0778             if (ngx_rename_file(path.data, copy.path.data) != NGX_FILE_ERROR) {
0779                 return NGX_HTTP_CREATED;
0780             }
0781         }
0782 
0783         if (ngx_create_dir(copy.path.data, ngx_file_access(&fi))
0784             == NGX_FILE_ERROR)
0785         {
0786             return ngx_http_dav_error(r->connection->log, ngx_errno,
0787                                       NGX_HTTP_NOT_FOUND,
0788                                       ngx_create_dir_n, copy.path.data);
0789         }
0790 
0791         copy.len = path.len;
0792 
0793         tree.init_handler = NULL;
0794         tree.file_handler = ngx_http_dav_copy_tree_file;
0795         tree.pre_tree_handler = ngx_http_dav_copy_dir;
0796         tree.post_tree_handler = ngx_http_dav_copy_dir_time;
0797         tree.spec_handler = ngx_http_dav_noop;
0798         tree.data = &copy;
0799         tree.alloc = 0;
0800         tree.log = r->connection->log;
0801 
0802         if (ngx_walk_tree(&tree, &path) == NGX_OK) {
0803 
0804             if (r->method == NGX_HTTP_MOVE) {
0805                 rc = ngx_http_dav_delete_path(r, &path, 1);
0806 
0807                 if (rc != NGX_OK) {
0808                     return rc;
0809                 }
0810             }
0811 
0812             return NGX_HTTP_CREATED;
0813         }
0814 
0815     } else {
0816 
0817         if (r->method == NGX_HTTP_MOVE) {
0818 
0819             dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
0820 
0821             ext.access = 0;
0822             ext.path_access = dlcf->access;
0823             ext.time = -1;
0824             ext.create_path = 1;
0825             ext.delete_file = 0;
0826             ext.log = r->connection->log;
0827 
0828             if (ngx_ext_rename_file(&path, &copy.path, &ext) == NGX_OK) {
0829                 return NGX_HTTP_NO_CONTENT;
0830             }
0831 
0832             return NGX_HTTP_INTERNAL_SERVER_ERROR;
0833         }
0834 
0835         dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
0836 
0837         cf.size = ngx_file_size(&fi);
0838         cf.buf_size = 0;
0839         cf.access = dlcf->access;
0840         cf.time = ngx_file_mtime(&fi);
0841         cf.log = r->connection->log;
0842 
0843         if (ngx_copy_file(path.data, copy.path.data, &cf) == NGX_OK) {
0844             return NGX_HTTP_NO_CONTENT;
0845         }
0846     }
0847 
0848     return NGX_HTTP_INTERNAL_SERVER_ERROR;
0849 }
0850 
0851 
0852 static ngx_int_t
0853 ngx_http_dav_copy_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path)
0854 {
0855     u_char                   *p, *dir;
0856     size_t                    len;
0857     ngx_http_dav_copy_ctx_t  *copy;
0858 
0859     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
0860                    "http copy dir: \"%s\"", path->data);
0861 
0862     copy = ctx->data;
0863 
0864     len = copy->path.len + path->len;
0865 
0866     dir = ngx_alloc(len + 1, ctx->log);
0867     if (dir == NULL) {
0868         return NGX_ABORT;
0869     }
0870 
0871     p = ngx_cpymem(dir, copy->path.data, copy->path.len);
0872     (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);
0873 
0874     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
0875                    "http copy dir to: \"%s\"", dir);
0876 
0877     if (ngx_create_dir(dir, ngx_dir_access(ctx->access)) == NGX_FILE_ERROR) {
0878         (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_create_dir_n,
0879                                   dir);
0880     }
0881 
0882     ngx_free(dir);
0883 
0884     return NGX_OK;
0885 }
0886 
0887 
0888 static ngx_int_t
0889 ngx_http_dav_copy_dir_time(ngx_tree_ctx_t *ctx, ngx_str_t *path)
0890 {
0891     u_char                   *p, *dir;
0892     size_t                    len;
0893     ngx_http_dav_copy_ctx_t  *copy;
0894 
0895     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
0896                    "http copy dir time: \"%s\"", path->data);
0897 
0898     copy = ctx->data;
0899 
0900     len = copy->path.len + path->len;
0901 
0902     dir = ngx_alloc(len + 1, ctx->log);
0903     if (dir == NULL) {
0904         return NGX_ABORT;
0905     }
0906 
0907     p = ngx_cpymem(dir, copy->path.data, copy->path.len);
0908     (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);
0909 
0910     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
0911                    "http copy dir time to: \"%s\"", dir);
0912 
0913 #if (NGX_WIN32)
0914     {
0915     ngx_fd_t  fd;
0916 
0917     fd = ngx_open_file(dir, NGX_FILE_RDWR, NGX_FILE_OPEN, 0);
0918 
0919     if (fd == NGX_INVALID_FILE) {
0920         (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_open_file_n, dir);
0921         goto failed;
0922     }
0923 
0924     if (ngx_set_file_time(NULL, fd, ctx->mtime) != NGX_OK) {
0925         ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
0926                       ngx_set_file_time_n " \"%s\" failed", dir);
0927     }
0928 
0929     if (ngx_close_file(fd) == NGX_FILE_ERROR) {
0930         ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
0931                       ngx_close_file_n " \"%s\" failed", dir);
0932     }
0933     }
0934 
0935 failed:
0936 
0937 #else
0938 
0939     if (ngx_set_file_time(dir, 0, ctx->mtime) != NGX_OK) {
0940         ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
0941                       ngx_set_file_time_n " \"%s\" failed", dir);
0942     }
0943 
0944 #endif
0945 
0946     ngx_free(dir);
0947 
0948     return NGX_OK;
0949 }
0950 
0951 
0952 static ngx_int_t
0953 ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
0954 {
0955     u_char                   *p, *file;
0956     size_t                    len;
0957     ngx_copy_file_t           cf;
0958     ngx_http_dav_copy_ctx_t  *copy;
0959 
0960     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
0961                    "http copy file: \"%s\"", path->data);
0962 
0963     copy = ctx->data;
0964 
0965     len = copy->path.len + path->len;
0966 
0967     file = ngx_alloc(len + 1, ctx->log);
0968     if (file == NULL) {
0969         return NGX_ABORT;
0970     }
0971 
0972     p = ngx_cpymem(file, copy->path.data, copy->path.len);
0973     (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);
0974 
0975     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
0976                    "http copy file to: \"%s\"", file);
0977 
0978     cf.size = ctx->size;
0979     cf.buf_size = 0;
0980     cf.access = ctx->access;
0981     cf.time = ctx->mtime;
0982     cf.log = ctx->log;
0983 
0984     (void) ngx_copy_file(path->data, file, &cf);
0985 
0986     ngx_free(file);
0987 
0988     return NGX_OK;
0989 }
0990 
0991 
0992 static ngx_int_t
0993 ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt)
0994 {
0995     ngx_table_elt_t  *depth;
0996 
0997     depth = r->headers_in.depth;
0998 
0999     if (depth == NULL) {
1000         return dflt;
1001     }
1002 
1003     if (depth->value.len == 1) {
1004 
1005         if (depth->value.data[0] == '0') {
1006             return 0;
1007         }
1008 
1009         if (depth->value.data[0] == '1') {
1010             return 1;
1011         }
1012 
1013     } else {
1014 
1015         if (depth->value.len == sizeof("infinity") - 1
1016             && ngx_strcmp(depth->value.data, "infinity") == 0)
1017         {
1018             return NGX_HTTP_DAV_INFINITY_DEPTH;
1019         }
1020     }
1021 
1022     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1023                   "client sent invalid \"Depth\" header: \"%V\"",
1024                   &depth->value);
1025 
1026     return NGX_HTTP_DAV_INVALID_DEPTH;
1027 }
1028 
1029 
1030 static ngx_int_t
1031 ngx_http_dav_error(ngx_log_t *log, ngx_err_t err, ngx_int_t not_found,
1032     char *failed, u_char *path)
1033 {
1034     ngx_int_t   rc;
1035     ngx_uint_t  level;
1036 
1037     if (err == NGX_ENOENT || err == NGX_ENOTDIR || err == NGX_ENAMETOOLONG) {
1038         level = NGX_LOG_ERR;
1039         rc = not_found;
1040 
1041     } else if (err == NGX_EACCES || err == NGX_EPERM) {
1042         level = NGX_LOG_ERR;
1043         rc = NGX_HTTP_FORBIDDEN;
1044 
1045     } else if (err == NGX_EEXIST) {
1046         level = NGX_LOG_ERR;
1047         rc = NGX_HTTP_NOT_ALLOWED;
1048 
1049     } else if (err == NGX_ENOSPC) {
1050         level = NGX_LOG_CRIT;
1051         rc = NGX_HTTP_INSUFFICIENT_STORAGE;
1052 
1053     } else {
1054         level = NGX_LOG_CRIT;
1055         rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
1056     }
1057 
1058     ngx_log_error(level, log, err, "%s \"%s\" failed", failed, path);
1059 
1060     return rc;
1061 }
1062 
1063 
1064 static ngx_int_t
1065 ngx_http_dav_location(ngx_http_request_t *r, u_char *path)
1066 {
1067     u_char                    *location;
1068     ngx_http_core_loc_conf_t  *clcf;
1069 
1070     r->headers_out.location = ngx_list_push(&r->headers_out.headers);
1071     if (r->headers_out.location == NULL) {
1072         return NGX_ERROR;
1073     }
1074 
1075     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
1076 
1077     if (!clcf->alias && clcf->root_lengths == NULL) {
1078         location = path + clcf->root.len;
1079 
1080     } else {
1081         location = ngx_pnalloc(r->pool, r->uri.len);
1082         if (location == NULL) {
1083             return NGX_ERROR;
1084         }
1085 
1086         ngx_memcpy(location, r->uri.data, r->uri.len);
1087     }
1088 
1089     r->headers_out.location->hash = 1;
1090     ngx_str_set(&r->headers_out.location->key, "Location");
1091     r->headers_out.location->value.len = r->uri.len;
1092     r->headers_out.location->value.data = location;
1093 
1094     return NGX_OK;
1095 }
1096 
1097 
1098 static void *
1099 ngx_http_dav_create_loc_conf(ngx_conf_t *cf)
1100 {
1101     ngx_http_dav_loc_conf_t  *conf;
1102 
1103     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dav_loc_conf_t));
1104     if (conf == NULL) {
1105         return NULL;
1106     }
1107 
1108     /*
1109      * set by ngx_pcalloc():
1110      *
1111      *     conf->methods = 0;
1112      */
1113 
1114     conf->min_delete_depth = NGX_CONF_UNSET_UINT;
1115     conf->access = NGX_CONF_UNSET_UINT;
1116     conf->create_full_put_path = NGX_CONF_UNSET;
1117 
1118     return conf;
1119 }
1120 
1121 
1122 static char *
1123 ngx_http_dav_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
1124 {
1125     ngx_http_dav_loc_conf_t  *prev = parent;
1126     ngx_http_dav_loc_conf_t  *conf = child;
1127 
1128     ngx_conf_merge_bitmask_value(conf->methods, prev->methods,
1129                          (NGX_CONF_BITMASK_SET|NGX_HTTP_DAV_OFF));
1130 
1131     ngx_conf_merge_uint_value(conf->min_delete_depth,
1132                          prev->min_delete_depth, 0);
1133 
1134     ngx_conf_merge_uint_value(conf->access, prev->access, 0600);
1135 
1136     ngx_conf_merge_value(conf->create_full_put_path,
1137                          prev->create_full_put_path, 0);
1138 
1139     return NGX_CONF_OK;
1140 }
1141 
1142 
1143 static ngx_int_t
1144 ngx_http_dav_init(ngx_conf_t *cf)
1145 {
1146     ngx_http_handler_pt        *h;
1147     ngx_http_core_main_conf_t  *cmcf;
1148 
1149     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
1150 
1151     h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
1152     if (h == NULL) {
1153         return NGX_ERROR;
1154     }
1155 
1156     *h = ngx_http_dav_handler;
1157 
1158     return NGX_OK;
1159 }