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