src/http/modules/ngx_http_dav_module.c - nginx-1.31.7 nginx/ @ 939334eff

Global variables defined

Data types defined

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>


  8. ‌#define NGX_HTTP_DAV_OFF             2


  9. ‌#define NGX_HTTP_DAV_NO_DEPTH        -3
  10. ‌#define NGX_HTTP_DAV_INVALID_DEPTH   -2
  11. ‌#define NGX_HTTP_DAV_INFINITY_DEPTH  -1


  12. ‌typedef struct {
  13.     ngx_uint_t  methods;
  14.     ngx_uint_t  access;
  15.     ngx_uint_t  min_delete_depth;
  16.     ngx_flag_t  create_full_put_path;
  17. ‌} ngx_http_dav_loc_conf_t;


  18. ‌typedef struct {
  19.     ngx_str_t   path;
  20.     size_t      len;
  21. ‌} ngx_http_dav_copy_ctx_t;


  22. static ngx_int_t ngx_http_dav_handler(ngx_http_request_t *r);

  23. static void ngx_http_dav_put_handler(ngx_http_request_t *r);

  24. static ngx_int_t ngx_http_dav_delete_handler(ngx_http_request_t *r);
  25. static ngx_int_t ngx_http_dav_delete_path(ngx_http_request_t *r,
  26.     ngx_str_t *path, ngx_uint_t dir);
  27. static ngx_int_t ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path);
  28. static ngx_int_t ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path);
  29. static ngx_int_t ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path);

  30. static ngx_int_t ngx_http_dav_mkcol_handler(ngx_http_request_t *r,
  31.     ngx_http_dav_loc_conf_t *dlcf);

  32. static ngx_int_t ngx_http_dav_copy_move_handler(ngx_http_request_t *r);
  33. static void ngx_http_dav_merge_slashes(ngx_str_t *path);
  34. static ngx_int_t ngx_http_dav_validate_paths(ngx_http_request_t *r,
  35.     ngx_str_t *src, ngx_str_t *dst, ngx_uint_t slash, ngx_table_elt_t *dest);
  36. static ngx_int_t ngx_http_dav_copy_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path);
  37. static ngx_int_t ngx_http_dav_copy_dir_time(ngx_tree_ctx_t *ctx,
  38.     ngx_str_t *path);
  39. static ngx_int_t ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx,
  40.     ngx_str_t *path);

  41. static ngx_int_t ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt);
  42. static ngx_int_t ngx_http_dav_error(ngx_log_t *log, ngx_err_t err,
  43.     ngx_int_t not_found, char *failed, u_char *path);
  44. static ngx_int_t ngx_http_dav_location(ngx_http_request_t *r);
  45. static void *ngx_http_dav_create_loc_conf(ngx_conf_t *cf);
  46. static char *ngx_http_dav_merge_loc_conf(ngx_conf_t *cf,
  47.     void *parent, void *child);
  48. static ngx_int_t ngx_http_dav_init(ngx_conf_t *cf);


  49. ‌static ngx_conf_bitmask_t  ngx_http_dav_methods_mask[] = {
  50.     { ngx_string("off"), NGX_HTTP_DAV_OFF },
  51.     { ngx_string("put"), NGX_HTTP_PUT },
  52.     { ngx_string("delete"), NGX_HTTP_DELETE },
  53.     { ngx_string("mkcol"), NGX_HTTP_MKCOL },
  54.     { ngx_string("copy"), NGX_HTTP_COPY },
  55.     { ngx_string("move"), NGX_HTTP_MOVE },
  56.     { ngx_null_string, 0 }
  57. };


  58. ‌static ngx_command_t  ngx_http_dav_commands[] = {

  59.     { ngx_string("dav_methods"),
  60.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
  61.       ngx_conf_set_bitmask_slot,
  62.       NGX_HTTP_LOC_CONF_OFFSET,
  63.       offsetof(ngx_http_dav_loc_conf_t, methods),
  64.       &ngx_http_dav_methods_mask },

  65.     { ngx_string("create_full_put_path"),
  66.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
  67.       ngx_conf_set_flag_slot,
  68.       NGX_HTTP_LOC_CONF_OFFSET,
  69.       offsetof(ngx_http_dav_loc_conf_t, create_full_put_path),
  70.       NULL },

  71.     { ngx_string("min_delete_depth"),
  72.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  73.       ngx_conf_set_num_slot,
  74.       NGX_HTTP_LOC_CONF_OFFSET,
  75.       offsetof(ngx_http_dav_loc_conf_t, min_delete_depth),
  76.       NULL },

  77.     { ngx_string("dav_access"),
  78.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
  79.       ngx_conf_set_access_slot,
  80.       NGX_HTTP_LOC_CONF_OFFSET,
  81.       offsetof(ngx_http_dav_loc_conf_t, access),
  82.       NULL },

  83.       ngx_null_command
  84. };


  85. ‌static ngx_http_module_t  ngx_http_dav_module_ctx = {
  86.     NULL,                                  /* preconfiguration */
  87.     ngx_http_dav_init,                     /* postconfiguration */

  88.     NULL,                                  /* create main configuration */
  89.     NULL,                                  /* init main configuration */

  90.     NULL,                                  /* create server configuration */
  91.     NULL,                                  /* merge server configuration */

  92.     ngx_http_dav_create_loc_conf,          /* create location configuration */
  93.     ngx_http_dav_merge_loc_conf            /* merge location configuration */
  94. };


  95. ‌ngx_module_t  ngx_http_dav_module = {
  96.     NGX_MODULE_V1,
  97.     &ngx_http_dav_module_ctx,              /* module context */
  98.     ngx_http_dav_commands,                 /* module directives */
  99.     NGX_HTTP_MODULE,                       /* module type */
  100.     NULL,                                  /* init master */
  101.     NULL,                                  /* init module */
  102.     NULL,                                  /* init process */
  103.     NULL,                                  /* init thread */
  104.     NULL,                                  /* exit thread */
  105.     NULL,                                  /* exit process */
  106.     NULL,                                  /* exit master */
  107.     NGX_MODULE_V1_PADDING
  108. };


  109. static ngx_int_t
  110. ‌ngx_http_dav_handler(ngx_http_request_t *r)
  111. {
  112.     ngx_int_t                 rc;
  113.     ngx_http_dav_loc_conf_t  *dlcf;

  114.     dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);

  115.     if (!(r->method & dlcf->methods)) {
  116.         return NGX_DECLINED;
  117.     }

  118.     switch (r->method) {

  119.     case NGX_HTTP_PUT:

  120.         if (r->uri.data[r->uri.len - 1] == '/') {
  121.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  122.                           "cannot PUT to a collection");
  123.             return NGX_HTTP_CONFLICT;
  124.         }

  125.         if (r->headers_in.content_range) {
  126.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  127.                           "PUT with range is unsupported");
  128.             return NGX_HTTP_NOT_IMPLEMENTED;
  129.         }

  130.         r->request_body_in_file_only = 1;
  131.         r->request_body_in_persistent_file = 1;
  132.         r->request_body_in_clean_file = 1;
  133.         r->request_body_file_group_access = 1;
  134.         r->request_body_file_log_level = 0;

  135.         rc = ngx_http_read_client_request_body(r, ngx_http_dav_put_handler);

  136.         if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
  137.             return rc;
  138.         }

  139.         return NGX_DONE;

  140.     case NGX_HTTP_DELETE:

  141.         return ngx_http_dav_delete_handler(r);

  142.     case NGX_HTTP_MKCOL:

  143.         return ngx_http_dav_mkcol_handler(r, dlcf);

  144.     case NGX_HTTP_COPY:

  145.         return ngx_http_dav_copy_move_handler(r);

  146.     case NGX_HTTP_MOVE:

  147.         return ngx_http_dav_copy_move_handler(r);
  148.     }

  149.     return NGX_DECLINED;
  150. }


  151. static void
  152. ‌ngx_http_dav_put_handler(ngx_http_request_t *r)
  153. {
  154.     size_t                    root;
  155.     time_t                    date;
  156.     ngx_str_t                *temp, path;
  157.     ngx_uint_t                status;
  158.     ngx_file_info_t           fi;
  159.     ngx_ext_rename_file_t     ext;
  160.     ngx_http_dav_loc_conf_t  *dlcf;

  161.     if (r->request_body == NULL) {
  162.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  163.                       "PUT request body is unavailable");
  164.         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
  165.         return;
  166.     }

  167.     if (r->request_body->temp_file == NULL) {
  168.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  169.                       "PUT request body must be in a file");
  170.         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
  171.         return;
  172.     }

  173.     if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
  174.         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
  175.         return;
  176.     }

  177.     path.len--;

  178.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  179.                    "http put filename: \"%s\"", path.data);

  180.     temp = &r->request_body->temp_file->file.name;

  181.     if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) {
  182.         status = NGX_HTTP_CREATED;

  183.     } else {
  184.         status = NGX_HTTP_NO_CONTENT;

  185.         if (ngx_is_dir(&fi)) {
  186.             ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,
  187.                           "\"%s\" could not be created", path.data);

  188.             if (ngx_delete_file(temp->data) == NGX_FILE_ERROR) {
  189.                 ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
  190.                               ngx_delete_file_n " \"%s\" failed",
  191.                               temp->data);
  192.             }

  193.             ngx_http_finalize_request(r, NGX_HTTP_CONFLICT);
  194.             return;
  195.         }
  196.     }

  197.     dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);

  198.     ext.access = dlcf->access;
  199.     ext.path_access = dlcf->access;
  200.     ext.time = -1;
  201.     ext.create_path = dlcf->create_full_put_path;
  202.     ext.delete_file = 1;
  203.     ext.log = r->connection->log;

  204.     if (r->headers_in.date) {
  205.         date = ngx_parse_http_time(r->headers_in.date->value.data,
  206.                                    r->headers_in.date->value.len);

  207.         if (date != NGX_ERROR) {
  208.             ext.time = date;
  209.             ext.fd = r->request_body->temp_file->file.fd;
  210.         }
  211.     }

  212.     if (ngx_ext_rename_file(temp, &path, &ext) != NGX_OK) {
  213.         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
  214.         return;
  215.     }

  216.     if (status == NGX_HTTP_CREATED) {
  217.         if (ngx_http_dav_location(r) != NGX_OK) {
  218.             ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
  219.             return;
  220.         }

  221.         r->headers_out.content_length_n = 0;
  222.     }

  223.     r->headers_out.status = status;
  224.     r->header_only = 1;

  225.     ngx_http_finalize_request(r, ngx_http_send_header(r));
  226.     return;
  227. }


  228. static ngx_int_t
  229. ‌ngx_http_dav_delete_handler(ngx_http_request_t *r)
  230. {
  231.     size_t                    root;
  232.     ngx_err_t                 err;
  233.     ngx_int_t                 rc, depth;
  234.     ngx_uint_t                i, d, dir;
  235.     ngx_str_t                 path;
  236.     ngx_file_info_t           fi;
  237.     ngx_http_dav_loc_conf_t  *dlcf;

  238.     if (r->headers_in.content_length_n > 0 || r->headers_in.chunked) {
  239.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  240.                       "DELETE with body is unsupported");
  241.         return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
  242.     }

  243.     dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);

  244.     if (dlcf->min_delete_depth) {
  245.         d = 0;

  246.         for (i = 0; i < r->uri.len; /* void */) {
  247.             if (r->uri.data[i++] == '/') {
  248.                 if (++d >= dlcf->min_delete_depth && i < r->uri.len) {
  249.                     goto ok;
  250.                 }
  251.             }
  252.         }

  253.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  254.                       "insufficient URI depth:%i to DELETE", d);
  255.         return NGX_HTTP_CONFLICT;
  256.     }

  257. ok:

  258.     if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
  259.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  260.     }

  261.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  262.                    "http delete filename: \"%s\"", path.data);

  263.     if (ngx_link_info(path.data, &fi) == NGX_FILE_ERROR) {
  264.         err = ngx_errno;

  265.         rc = (err == NGX_ENOTDIR) ? NGX_HTTP_CONFLICT : NGX_HTTP_NOT_FOUND;

  266.         return ngx_http_dav_error(r->connection->log, err,
  267.                                   rc, ngx_link_info_n, path.data);
  268.     }

  269.     if (ngx_is_dir(&fi)) {

  270.         if (r->uri.data[r->uri.len - 1] != '/') {
  271.             ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,
  272.                           "DELETE \"%s\" failed", path.data);
  273.             return NGX_HTTP_CONFLICT;
  274.         }

  275.         depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);

  276.         if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
  277.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  278.                           "\"Depth\" header must be infinity");
  279.             return NGX_HTTP_BAD_REQUEST;
  280.         }

  281.         path.len -= 2;  /* omit "/\0" */

  282.         dir = 1;

  283.     } else {

  284.         /*
  285.          * we do not need to test (r->uri.data[r->uri.len - 1] == '/')
  286.          * because ngx_link_info("/file/") returned NGX_ENOTDIR above
  287.          */

  288.         depth = ngx_http_dav_depth(r, 0);

  289.         if (depth != 0 && depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
  290.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  291.                           "\"Depth\" header must be 0 or infinity");
  292.             return NGX_HTTP_BAD_REQUEST;
  293.         }

  294.         dir = 0;
  295.     }

  296.     rc = ngx_http_dav_delete_path(r, &path, dir);

  297.     if (rc == NGX_OK) {
  298.         return NGX_HTTP_NO_CONTENT;
  299.     }

  300.     return rc;
  301. }


  302. static ngx_int_t
  303. ‌ngx_http_dav_delete_path(ngx_http_request_t *r, ngx_str_t *path, ngx_uint_t dir)
  304. {
  305.     char            *failed;
  306.     ngx_tree_ctx_t   tree;

  307.     if (dir) {

  308.         tree.init_handler = NULL;
  309.         tree.file_handler = ngx_http_dav_delete_file;
  310.         tree.pre_tree_handler = ngx_http_dav_noop;
  311.         tree.post_tree_handler = ngx_http_dav_delete_dir;
  312.         tree.spec_handler = ngx_http_dav_delete_file;
  313.         tree.data = NULL;
  314.         tree.alloc = 0;
  315.         tree.log = r->connection->log;

  316.         /* TODO: 207 */

  317.         if (ngx_walk_tree(&tree, path) != NGX_OK) {
  318.             return NGX_HTTP_INTERNAL_SERVER_ERROR;
  319.         }

  320.         if (ngx_delete_dir(path->data) != NGX_FILE_ERROR) {
  321.             return NGX_OK;
  322.         }

  323.         failed = ngx_delete_dir_n;

  324.     } else {

  325.         if (ngx_delete_file(path->data) != NGX_FILE_ERROR) {
  326.             return NGX_OK;
  327.         }

  328.         failed = ngx_delete_file_n;
  329.     }

  330.     return ngx_http_dav_error(r->connection->log, ngx_errno,
  331.                               NGX_HTTP_NOT_FOUND, failed, path->data);
  332. }


  333. static ngx_int_t
  334. ‌ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  335. {
  336.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
  337.                    "http delete dir: \"%s\"", path->data);

  338.     if (ngx_delete_dir(path->data) == NGX_FILE_ERROR) {

  339.         /* TODO: add to 207 */

  340.         (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_delete_dir_n,
  341.                                   path->data);
  342.     }

  343.     return NGX_OK;
  344. }


  345. static ngx_int_t
  346. ‌ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  347. {
  348.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
  349.                    "http delete file: \"%s\"", path->data);

  350.     if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {

  351.         /* TODO: add to 207 */

  352.         (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_delete_file_n,
  353.                                   path->data);
  354.     }

  355.     return NGX_OK;
  356. }


  357. static ngx_int_t
  358. ‌ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  359. {
  360.     return NGX_OK;
  361. }


  362. static ngx_int_t
  363. ‌ngx_http_dav_mkcol_handler(ngx_http_request_t *r, ngx_http_dav_loc_conf_t *dlcf)
  364. {
  365.     u_char    *p;
  366.     size_t     root;
  367.     ngx_str_t  path;

  368.     if (r->headers_in.content_length_n > 0 || r->headers_in.chunked) {
  369.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  370.                       "MKCOL with body is unsupported");
  371.         return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
  372.     }

  373.     if (r->uri.data[r->uri.len - 1] != '/') {
  374.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  375.                       "MKCOL can create a collection only");
  376.         return NGX_HTTP_CONFLICT;
  377.     }

  378.     p = ngx_http_map_uri_to_path(r, &path, &root, 0);
  379.     if (p == NULL) {
  380.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  381.     }

  382.     *(p - 1) = '\0';

  383.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  384.                    "http mkcol path: \"%s\"", path.data);

  385.     if (ngx_create_dir(path.data, ngx_dir_access(dlcf->access))
  386.         != NGX_FILE_ERROR)
  387.     {
  388.         if (ngx_http_dav_location(r) != NGX_OK) {
  389.             return NGX_HTTP_INTERNAL_SERVER_ERROR;
  390.         }

  391.         return NGX_HTTP_CREATED;
  392.     }

  393.     return ngx_http_dav_error(r->connection->log, ngx_errno,
  394.                               NGX_HTTP_CONFLICT, ngx_create_dir_n, path.data);
  395. }


  396. static ngx_int_t
  397. ‌ngx_http_dav_copy_move_handler(ngx_http_request_t *r)
  398. {
  399.     u_char                    *p, *host, *last, ch;
  400.     size_t                     len, root, alias;
  401.     ngx_err_t                  err;
  402.     ngx_int_t                  rc, depth;
  403.     ngx_uint_t                 overwrite, slash, dir, flags;
  404.     ngx_str_t                  path, uri, duri, args;
  405.     ngx_tree_ctx_t             tree;
  406.     ngx_copy_file_t            cf;
  407.     ngx_file_info_t            fi;
  408.     ngx_table_elt_t           *dest, *over;
  409.     ngx_ext_rename_file_t      ext;
  410.     ngx_http_dav_copy_ctx_t    copy;
  411.     ngx_http_dav_loc_conf_t   *dlcf;
  412.     ngx_http_core_loc_conf_t  *clcf;

  413.     if (r->headers_in.content_length_n > 0 || r->headers_in.chunked) {
  414.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  415.                       "COPY and MOVE with body are unsupported");
  416.         return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
  417.     }

  418.     dest = r->headers_in.destination;

  419.     if (dest == NULL) {
  420.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  421.                       "client sent no \"Destination\" header");
  422.         return NGX_HTTP_BAD_REQUEST;
  423.     }

  424.     p = dest->value.data;
  425.     /* there is always '\0' even after empty header value */
  426.     if (p[0] == '/') {
  427.         last = p + dest->value.len;
  428.         goto destination_done;
  429.     }

  430.     len = r->headers_in.server.len;

  431.     if (len == 0) {
  432.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  433.                       "client sent no \"Host\" header");
  434.         return NGX_HTTP_BAD_REQUEST;
  435.     }

  436. #if (NGX_HTTP_SSL)

  437.     if (r->connection->ssl) {
  438.         if (ngx_strncmp(dest->value.data, "https://", sizeof("https://") - 1)
  439.             != 0)
  440.         {
  441.             goto invalid_destination;
  442.         }

  443.         host = dest->value.data + sizeof("https://") - 1;

  444.     } else
  445. #endif
  446.     {
  447.         if (ngx_strncmp(dest->value.data, "http://", sizeof("http://") - 1)
  448.             != 0)
  449.         {
  450.             goto invalid_destination;
  451.         }

  452.         host = dest->value.data + sizeof("http://") - 1;
  453.     }

  454.     if (ngx_strncmp(host, r->headers_in.server.data, len) != 0) {
  455.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  456.                       "\"Destination\" URI \"%V\" is handled by "
  457.                       "different repository than the source URI",
  458.                       &dest->value);
  459.         return NGX_HTTP_BAD_REQUEST;
  460.     }

  461.     last = dest->value.data + dest->value.len;

  462.     for (p = host + len; p < last; p++) {
  463.         if (*p == '/') {
  464.             goto destination_done;
  465.         }
  466.     }

  467. invalid_destination:

  468.     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  469.                   "client sent invalid \"Destination\" header: \"%V\"",
  470.                   &dest->value);
  471.     return NGX_HTTP_BAD_REQUEST;

  472. destination_done:

  473.     duri.len = last - p;
  474.     duri.data = p;
  475.     flags = NGX_HTTP_LOG_UNSAFE;

  476.     if (ngx_http_parse_unsafe_uri(r, &duri, &args, &flags) != NGX_OK) {
  477.         goto invalid_destination;
  478.     }

  479.     if ((r->uri.data[r->uri.len - 1] == '/' && *(last - 1) != '/')
  480.         || (r->uri.data[r->uri.len - 1] != '/' && *(last - 1) == '/'))
  481.     {
  482.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  483.                       "both URI \"%V\" and \"Destination\" URI \"%V\" "
  484.                       "should be either collections or non-collections",
  485.                       &r->uri, &dest->value);
  486.         return NGX_HTTP_CONFLICT;
  487.     }

  488.     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
  489.     alias = clcf->alias;

  490.     if (alias && alias != NGX_MAX_SIZE_T_VALUE && r->valid_location) {

  491.         if (alias > duri.len
  492.             || ngx_filename_cmp(duri.data, r->uri.data, alias) != 0)
  493.         {
  494.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  495.                           "\"Destination\" URI \"%V\" must be "
  496.                           "within location prefix when using \"alias\"",
  497.                           &dest->value);
  498.             return NGX_HTTP_BAD_REQUEST;
  499.         }
  500.     }

  501.     depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);

  502.     if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {

  503.         if (r->method == NGX_HTTP_COPY) {
  504.             if (depth != 0) {
  505.                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  506.                               "\"Depth\" header must be 0 or infinity");
  507.                 return NGX_HTTP_BAD_REQUEST;
  508.             }

  509.         } else {
  510.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  511.                           "\"Depth\" header must be infinity");
  512.             return NGX_HTTP_BAD_REQUEST;
  513.         }
  514.     }

  515.     over = r->headers_in.overwrite;

  516.     if (over) {
  517.         if (over->value.len == 1) {
  518.             ch = over->value.data[0];

  519.             if (ch == 'T' || ch == 't') {
  520.                 overwrite = 1;
  521.                 goto overwrite_done;
  522.             }

  523.             if (ch == 'F' || ch == 'f') {
  524.                 overwrite = 0;
  525.                 goto overwrite_done;
  526.             }

  527.         }

  528.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  529.                       "client sent invalid \"Overwrite\" header: \"%V\"",
  530.                       &over->value);
  531.         return NGX_HTTP_BAD_REQUEST;
  532.     }

  533.     overwrite = 1;

  534. overwrite_done:

  535.     if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
  536.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  537.     }

  538.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  539.                    "http copy from: \"%s\"", path.data);

  540.     uri = r->uri;
  541.     r->uri = duri;

  542.     if (ngx_http_map_uri_to_path(r, &copy.path, &root, 0) == NULL) {
  543.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  544.     }

  545.     r->uri = uri;

  546.     ngx_http_dav_merge_slashes(&path);
  547.     ngx_http_dav_merge_slashes(&copy.path);

  548.     copy.path.len--;  /* omit "\0" */

  549.     if (copy.path.data[copy.path.len - 1] == '/') {
  550.         slash = 1;
  551.         copy.path.len--;
  552.         copy.path.data[copy.path.len] = '\0';

  553.     } else {
  554.         slash = 0;
  555.     }

  556.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  557.                    "http copy to: \"%s\"", copy.path.data);

  558.     if (ngx_http_dav_validate_paths(r, &path, &copy.path, slash, dest)
  559.         != NGX_OK)
  560.     {
  561.         return NGX_HTTP_FORBIDDEN;
  562.     }

  563.     if (ngx_link_info(copy.path.data, &fi) == NGX_FILE_ERROR) {
  564.         err = ngx_errno;

  565.         if (err != NGX_ENOENT) {
  566.             return ngx_http_dav_error(r->connection->log, err,
  567.                                       NGX_HTTP_NOT_FOUND, ngx_link_info_n,
  568.                                       copy.path.data);
  569.         }

  570.         /* destination does not exist */

  571.         overwrite = 0;
  572.         dir = 0;

  573.     } else {

  574.         /* destination exists */

  575.         if (ngx_is_dir(&fi) && !slash) {
  576.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  577.                           "\"%V\" could not be %Ved to collection \"%V\"",
  578.                           &r->uri, &r->method_name, &dest->value);
  579.             return NGX_HTTP_CONFLICT;
  580.         }

  581.         if (!overwrite) {
  582.             ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EEXIST,
  583.                           "\"%s\" could not be created", copy.path.data);
  584.             return NGX_HTTP_PRECONDITION_FAILED;
  585.         }

  586.         dir = ngx_is_dir(&fi);
  587.     }

  588.     if (ngx_link_info(path.data, &fi) == NGX_FILE_ERROR) {
  589.         return ngx_http_dav_error(r->connection->log, ngx_errno,
  590.                                   NGX_HTTP_NOT_FOUND, ngx_link_info_n,
  591.                                   path.data);
  592.     }

  593.     if (ngx_is_dir(&fi)) {

  594.         if (r->uri.data[r->uri.len - 1] != '/') {
  595.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  596.                           "\"%V\" is collection", &r->uri);
  597.             return NGX_HTTP_BAD_REQUEST;
  598.         }

  599.         if (overwrite) {
  600.             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  601.                            "http delete: \"%s\"", copy.path.data);

  602.             rc = ngx_http_dav_delete_path(r, &copy.path, dir);

  603.             if (rc != NGX_OK) {
  604.                 return rc;
  605.             }
  606.         }
  607.     }

  608.     if (ngx_is_dir(&fi)) {

  609.         path.len -= 2;  /* omit "/\0" */

  610.         if (r->method == NGX_HTTP_MOVE) {
  611.             if (ngx_rename_file(path.data, copy.path.data) != NGX_FILE_ERROR) {
  612.                 return NGX_HTTP_CREATED;
  613.             }
  614.         }

  615.         if (ngx_create_dir(copy.path.data, ngx_file_access(&fi))
  616.             == NGX_FILE_ERROR)
  617.         {
  618.             return ngx_http_dav_error(r->connection->log, ngx_errno,
  619.                                       NGX_HTTP_NOT_FOUND,
  620.                                       ngx_create_dir_n, copy.path.data);
  621.         }

  622.         copy.len = path.len;

  623.         tree.init_handler = NULL;
  624.         tree.file_handler = ngx_http_dav_copy_tree_file;
  625.         tree.pre_tree_handler = ngx_http_dav_copy_dir;
  626.         tree.post_tree_handler = ngx_http_dav_copy_dir_time;
  627.         tree.spec_handler = ngx_http_dav_noop;
  628.         tree.data = &copy;
  629.         tree.alloc = 0;
  630.         tree.log = r->connection->log;

  631.         if (ngx_walk_tree(&tree, &path) == NGX_OK) {

  632.             if (r->method == NGX_HTTP_MOVE) {
  633.                 rc = ngx_http_dav_delete_path(r, &path, 1);

  634.                 if (rc != NGX_OK) {
  635.                     return rc;
  636.                 }
  637.             }

  638.             return NGX_HTTP_CREATED;
  639.         }

  640.     } else {

  641.         if (r->method == NGX_HTTP_MOVE) {

  642.             dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);

  643.             ext.access = 0;
  644.             ext.path_access = dlcf->access;
  645.             ext.time = -1;
  646.             ext.create_path = 1;
  647.             ext.delete_file = 0;
  648.             ext.log = r->connection->log;

  649.             if (ngx_ext_rename_file(&path, &copy.path, &ext) == NGX_OK) {
  650.                 return NGX_HTTP_NO_CONTENT;
  651.             }

  652.             return NGX_HTTP_INTERNAL_SERVER_ERROR;
  653.         }

  654.         cf.size = ngx_file_size(&fi);
  655.         cf.buf_size = 0;
  656.         cf.access = ngx_file_access(&fi);
  657.         cf.time = ngx_file_mtime(&fi);
  658.         cf.log = r->connection->log;

  659.         if (ngx_copy_file(path.data, copy.path.data, &cf) == NGX_OK) {
  660.             return NGX_HTTP_NO_CONTENT;
  661.         }
  662.     }

  663.     return NGX_HTTP_INTERNAL_SERVER_ERROR;
  664. }


  665. static void
  666. ‌ngx_http_dav_merge_slashes(ngx_str_t *path)
  667. {
  668.     u_char  *p, *q;

  669.     p = path->data;
  670.     q = path->data;

  671.     while (*p) {
  672.         *q++ = *p;

  673.         if (*p++ == '/') {
  674.             while (*p == '/') {
  675.                 p++;
  676.             }
  677.         }
  678.     }

  679.     *q++ = '\0';
  680.     path->len = q - path->data;
  681. }


  682. static ngx_int_t
  683. ‌ngx_http_dav_validate_paths(ngx_http_request_t *r, ngx_str_t *src,
  684.     ngx_str_t *dst, ngx_uint_t slash, ngx_table_elt_t *dest)
  685. {
  686.     size_t  len;

  687.     len = src->len - 1;

  688.     if (len > 0 && src->data[len - 1] == '/') {
  689.         len--;
  690.     }

  691.     if (len == dst->len && ngx_strncmp(src->data, dst->data, len) == 0) {
  692.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  693.                       "both URI \"%V\" and \"Destination\" URI \"%V\" "
  694.                       "point to the same location",
  695.                       &r->uri, &dest->value);
  696.         return NGX_HTTP_FORBIDDEN;
  697.     }

  698.     if (slash
  699.         && ngx_strncmp(src->data, dst->data, ngx_min(len, dst->len)) == 0
  700.         && (len < dst->len
  701.             ? dst->data[len] == '/'
  702.             : src->data[dst->len] == '/'))
  703.     {
  704.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  705.                       "\"%V\" could not be %Ved to collection \"%V\"",
  706.                       &r->uri, &r->method_name, &dest->value);
  707.         return NGX_HTTP_FORBIDDEN;
  708.     }

  709.     return NGX_OK;
  710. }


  711. static ngx_int_t
  712. ‌ngx_http_dav_copy_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  713. {
  714.     u_char                   *p, *dir;
  715.     size_t                    len;
  716.     ngx_http_dav_copy_ctx_t  *copy;

  717.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
  718.                    "http copy dir: \"%s\"", path->data);

  719.     copy = ctx->data;

  720.     len = copy->path.len + path->len;

  721.     dir = ngx_alloc(len + 1, ctx->log);
  722.     if (dir == NULL) {
  723.         return NGX_ABORT;
  724.     }

  725.     p = ngx_cpymem(dir, copy->path.data, copy->path.len);
  726.     (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);

  727.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
  728.                    "http copy dir to: \"%s\"", dir);

  729.     if (ngx_create_dir(dir, ngx_dir_access(ctx->access)) == NGX_FILE_ERROR) {
  730.         (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_create_dir_n,
  731.                                   dir);
  732.     }

  733.     ngx_free(dir);

  734.     return NGX_OK;
  735. }


  736. static ngx_int_t
  737. ‌ngx_http_dav_copy_dir_time(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  738. {
  739.     u_char                   *p, *dir;
  740.     size_t                    len;
  741.     ngx_http_dav_copy_ctx_t  *copy;

  742.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
  743.                    "http copy dir time: \"%s\"", path->data);

  744.     copy = ctx->data;

  745.     len = copy->path.len + path->len;

  746.     dir = ngx_alloc(len + 1, ctx->log);
  747.     if (dir == NULL) {
  748.         return NGX_ABORT;
  749.     }

  750.     p = ngx_cpymem(dir, copy->path.data, copy->path.len);
  751.     (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);

  752.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
  753.                    "http copy dir time to: \"%s\"", dir);

  754. #if (NGX_WIN32)
  755.     {
  756.     ngx_fd_t  fd;

  757.     fd = ngx_open_file(dir, NGX_FILE_RDWR, NGX_FILE_OPEN, 0);

  758.     if (fd == NGX_INVALID_FILE) {
  759.         (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_open_file_n, dir);
  760.         goto failed;
  761.     }

  762.     if (ngx_set_file_time(NULL, fd, ctx->mtime) != NGX_OK) {
  763.         ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
  764.                       ngx_set_file_time_n " \"%s\" failed", dir);
  765.     }

  766.     if (ngx_close_file(fd) == NGX_FILE_ERROR) {
  767.         ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
  768.                       ngx_close_file_n " \"%s\" failed", dir);
  769.     }
  770.     }

  771. failed:

  772. #else

  773.     if (ngx_set_file_time(dir, 0, ctx->mtime) != NGX_OK) {
  774.         ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
  775.                       ngx_set_file_time_n " \"%s\" failed", dir);
  776.     }

  777. #endif

  778.     ngx_free(dir);

  779.     return NGX_OK;
  780. }


  781. static ngx_int_t
  782. ‌ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  783. {
  784.     u_char                   *p, *file;
  785.     size_t                    len;
  786.     ngx_copy_file_t           cf;
  787.     ngx_http_dav_copy_ctx_t  *copy;

  788.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
  789.                    "http copy file: \"%s\"", path->data);

  790.     copy = ctx->data;

  791.     len = copy->path.len + path->len;

  792.     file = ngx_alloc(len + 1, ctx->log);
  793.     if (file == NULL) {
  794.         return NGX_ABORT;
  795.     }

  796.     p = ngx_cpymem(file, copy->path.data, copy->path.len);
  797.     (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);

  798.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
  799.                    "http copy file to: \"%s\"", file);

  800.     cf.size = ctx->size;
  801.     cf.buf_size = 0;
  802.     cf.access = ctx->access;
  803.     cf.time = ctx->mtime;
  804.     cf.log = ctx->log;

  805.     (void) ngx_copy_file(path->data, file, &cf);

  806.     ngx_free(file);

  807.     return NGX_OK;
  808. }


  809. static ngx_int_t
  810. ‌ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt)
  811. {
  812.     ngx_table_elt_t  *depth;

  813.     depth = r->headers_in.depth;

  814.     if (depth == NULL) {
  815.         return dflt;
  816.     }

  817.     if (depth->value.len == 1) {

  818.         if (depth->value.data[0] == '0') {
  819.             return 0;
  820.         }

  821.         if (depth->value.data[0] == '1') {
  822.             return 1;
  823.         }

  824.     } else {

  825.         if (depth->value.len == sizeof("infinity") - 1
  826.             && ngx_strcmp(depth->value.data, "infinity") == 0)
  827.         {
  828.             return NGX_HTTP_DAV_INFINITY_DEPTH;
  829.         }
  830.     }

  831.     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  832.                   "client sent invalid \"Depth\" header: \"%V\"",
  833.                   &depth->value);

  834.     return NGX_HTTP_DAV_INVALID_DEPTH;
  835. }


  836. static ngx_int_t
  837. ‌ngx_http_dav_error(ngx_log_t *log, ngx_err_t err, ngx_int_t not_found,
  838.     char *failed, u_char *path)
  839. {
  840.     ngx_int_t   rc;
  841.     ngx_uint_t  level;

  842.     if (err == NGX_ENOENT || err == NGX_ENOTDIR || err == NGX_ENAMETOOLONG) {
  843.         level = NGX_LOG_ERR;
  844.         rc = not_found;

  845.     } else if (err == NGX_EACCES || err == NGX_EPERM) {
  846.         level = NGX_LOG_ERR;
  847.         rc = NGX_HTTP_FORBIDDEN;

  848.     } else if (err == NGX_EEXIST) {
  849.         level = NGX_LOG_ERR;
  850.         rc = NGX_HTTP_NOT_ALLOWED;

  851.     } else if (err == NGX_ENOSPC) {
  852.         level = NGX_LOG_CRIT;
  853.         rc = NGX_HTTP_INSUFFICIENT_STORAGE;

  854.     } else {
  855.         level = NGX_LOG_CRIT;
  856.         rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
  857.     }

  858.     ngx_log_error(level, log, err, "%s \"%s\" failed", failed, path);

  859.     return rc;
  860. }


  861. static ngx_int_t
  862. ‌ngx_http_dav_location(ngx_http_request_t *r)
  863. {
  864.     u_char     *p;
  865.     size_t      len;
  866.     uintptr_t   escape;

  867.     r->headers_out.location = ngx_list_push(&r->headers_out.headers);
  868.     if (r->headers_out.location == NULL) {
  869.         return NGX_ERROR;
  870.     }

  871.     r->headers_out.location->hash = 1;
  872.     r->headers_out.location->next = NULL;
  873.     ngx_str_set(&r->headers_out.location->key, "Location");

  874.     escape = 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len, NGX_ESCAPE_URI);

  875.     if (escape) {
  876.         len = r->uri.len + escape;

  877.         p = ngx_pnalloc(r->pool, len);
  878.         if (p == NULL) {
  879.             ngx_http_clear_location(r);
  880.             return NGX_ERROR;
  881.         }

  882.         r->headers_out.location->value.len = len;
  883.         r->headers_out.location->value.data = p;

  884.         ngx_escape_uri(p, r->uri.data, r->uri.len, NGX_ESCAPE_URI);

  885.     } else {
  886.         r->headers_out.location->value = r->uri;
  887.     }

  888.     return NGX_OK;
  889. }


  890. static void *
  891. ‌ngx_http_dav_create_loc_conf(ngx_conf_t *cf)
  892. {
  893.     ngx_http_dav_loc_conf_t  *conf;

  894.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dav_loc_conf_t));
  895.     if (conf == NULL) {
  896.         return NULL;
  897.     }

  898.     /*
  899.      * set by ngx_pcalloc():
  900.      *
  901.      *     conf->methods = 0;
  902.      */

  903.     conf->min_delete_depth = NGX_CONF_UNSET_UINT;
  904.     conf->access = NGX_CONF_UNSET_UINT;
  905.     conf->create_full_put_path = NGX_CONF_UNSET;

  906.     return conf;
  907. }


  908. static char *
  909. ‌ngx_http_dav_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
  910. {
  911.     ngx_http_dav_loc_conf_t  *prev = parent;
  912.     ngx_http_dav_loc_conf_t  *conf = child;

  913.     ngx_conf_merge_bitmask_value(conf->methods, prev->methods,
  914.                          (NGX_CONF_BITMASK_SET|NGX_HTTP_DAV_OFF));

  915.     ngx_conf_merge_uint_value(conf->min_delete_depth,
  916.                          prev->min_delete_depth, 0);

  917.     ngx_conf_merge_uint_value(conf->access, prev->access, 0600);

  918.     ngx_conf_merge_value(conf->create_full_put_path,
  919.                          prev->create_full_put_path, 0);

  920.     return NGX_CONF_OK;
  921. }


  922. static ngx_int_t
  923. ‌ngx_http_dav_init(ngx_conf_t *cf)
  924. {
  925.     ngx_http_handler_pt        *h;
  926.     ngx_http_core_main_conf_t  *cmcf;

  927.     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

  928.     h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
  929.     if (h == NULL) {
  930.         return NGX_ERROR;
  931.     }

  932.     *h = ngx_http_dav_handler;

  933.     return NGX_OK;
  934. }