src/http/modules/ngx_http_dav_module.c - nginx

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;
  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.     if (clcf->alias
  490.         && clcf->alias != NGX_MAX_SIZE_T_VALUE
  491.         && duri.len < clcf->alias)
  492.     {
  493.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  494.                       "client sent invalid \"Destination\" header: \"%V\"",
  495.                       &dest->value);
  496.         return NGX_HTTP_BAD_REQUEST;
  497.     }

  498.     depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);

  499.     if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {

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

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

  512.     over = r->headers_in.overwrite;

  513.     if (over) {
  514.         if (over->value.len == 1) {
  515.             ch = over->value.data[0];

  516.             if (ch == 'T' || ch == 't') {
  517.                 overwrite = 1;
  518.                 goto overwrite_done;
  519.             }

  520.             if (ch == 'F' || ch == 'f') {
  521.                 overwrite = 0;
  522.                 goto overwrite_done;
  523.             }

  524.         }

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

  530.     overwrite = 1;

  531. overwrite_done:

  532.     if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
  533.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  534.     }

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

  537.     uri = r->uri;
  538.     r->uri = duri;

  539.     if (ngx_http_map_uri_to_path(r, &copy.path, &root, 0) == NULL) {
  540.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  541.     }

  542.     r->uri = uri;

  543.     ngx_http_dav_merge_slashes(&path);
  544.     ngx_http_dav_merge_slashes(&copy.path);

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

  546.     if (copy.path.data[copy.path.len - 1] == '/') {
  547.         slash = 1;
  548.         copy.path.len--;
  549.         copy.path.data[copy.path.len] = '\0';

  550.     } else {
  551.         slash = 0;
  552.     }

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

  555.     if (ngx_http_dav_validate_paths(r, &path, &copy.path, slash, dest)
  556.         != NGX_OK)
  557.     {
  558.         return NGX_HTTP_FORBIDDEN;
  559.     }

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

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

  567.         /* destination does not exist */

  568.         overwrite = 0;
  569.         dir = 0;

  570.     } else {

  571.         /* destination exists */

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

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

  583.         dir = ngx_is_dir(&fi);
  584.     }

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

  590.     if (ngx_is_dir(&fi)) {

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

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

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

  600.             if (rc != NGX_OK) {
  601.                 return rc;
  602.             }
  603.         }
  604.     }

  605.     if (ngx_is_dir(&fi)) {

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

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

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

  619.         copy.len = path.len;

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

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

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

  631.                 if (rc != NGX_OK) {
  632.                     return rc;
  633.                 }
  634.             }

  635.             return NGX_HTTP_CREATED;
  636.         }

  637.     } else {

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

  639.             dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);

  640.             ext.access = 0;
  641.             ext.path_access = dlcf->access;
  642.             ext.time = -1;
  643.             ext.create_path = 1;
  644.             ext.delete_file = 0;
  645.             ext.log = r->connection->log;

  646.             if (ngx_ext_rename_file(&path, &copy.path, &ext) == NGX_OK) {
  647.                 return NGX_HTTP_NO_CONTENT;
  648.             }

  649.             return NGX_HTTP_INTERNAL_SERVER_ERROR;
  650.         }

  651.         cf.size = ngx_file_size(&fi);
  652.         cf.buf_size = 0;
  653.         cf.access = ngx_file_access(&fi);
  654.         cf.time = ngx_file_mtime(&fi);
  655.         cf.log = r->connection->log;

  656.         if (ngx_copy_file(path.data, copy.path.data, &cf) == NGX_OK) {
  657.             return NGX_HTTP_NO_CONTENT;
  658.         }
  659.     }

  660.     return NGX_HTTP_INTERNAL_SERVER_ERROR;
  661. }


  662. static void
  663. ngx_http_dav_merge_slashes(ngx_str_t *path)
  664. {
  665.     u_char  *p, *q;

  666.     p = path->data;
  667.     q = path->data;

  668.     while (*p) {
  669.         *q++ = *p;

  670.         if (*p++ == '/') {
  671.             while (*p == '/') {
  672.                 p++;
  673.             }
  674.         }
  675.     }

  676.     *q++ = '\0';
  677.     path->len = q - path->data;
  678. }


  679. static ngx_int_t
  680. ngx_http_dav_validate_paths(ngx_http_request_t *r, ngx_str_t *src,
  681.     ngx_str_t *dst, ngx_uint_t slash, ngx_table_elt_t *dest)
  682. {
  683.     size_t  len;

  684.     len = src->len - 1;

  685.     if (len > 0 && src->data[len - 1] == '/') {
  686.         len--;
  687.     }

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

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

  706.     return NGX_OK;
  707. }


  708. static ngx_int_t
  709. ngx_http_dav_copy_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  710. {
  711.     u_char                   *p, *dir;
  712.     size_t                    len;
  713.     ngx_http_dav_copy_ctx_t  *copy;

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

  716.     copy = ctx->data;

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

  718.     dir = ngx_alloc(len + 1, ctx->log);
  719.     if (dir == NULL) {
  720.         return NGX_ABORT;
  721.     }

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

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

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

  730.     ngx_free(dir);

  731.     return NGX_OK;
  732. }


  733. static ngx_int_t
  734. ngx_http_dav_copy_dir_time(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  735. {
  736.     u_char                   *p, *dir;
  737.     size_t                    len;
  738.     ngx_http_dav_copy_ctx_t  *copy;

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

  741.     copy = ctx->data;

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

  743.     dir = ngx_alloc(len + 1, ctx->log);
  744.     if (dir == NULL) {
  745.         return NGX_ABORT;
  746.     }

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

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

  751. #if (NGX_WIN32)
  752.     {
  753.     ngx_fd_t  fd;

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

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

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

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

  768. failed:

  769. #else

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

  774. #endif

  775.     ngx_free(dir);

  776.     return NGX_OK;
  777. }


  778. static ngx_int_t
  779. ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  780. {
  781.     u_char                   *p, *file;
  782.     size_t                    len;
  783.     ngx_copy_file_t           cf;
  784.     ngx_http_dav_copy_ctx_t  *copy;

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

  787.     copy = ctx->data;

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

  789.     file = ngx_alloc(len + 1, ctx->log);
  790.     if (file == NULL) {
  791.         return NGX_ABORT;
  792.     }

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

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

  797.     cf.size = ctx->size;
  798.     cf.buf_size = 0;
  799.     cf.access = ctx->access;
  800.     cf.time = ctx->mtime;
  801.     cf.log = ctx->log;

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

  803.     ngx_free(file);

  804.     return NGX_OK;
  805. }


  806. static ngx_int_t
  807. ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt)
  808. {
  809.     ngx_table_elt_t  *depth;

  810.     depth = r->headers_in.depth;

  811.     if (depth == NULL) {
  812.         return dflt;
  813.     }

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

  815.         if (depth->value.data[0] == '0') {
  816.             return 0;
  817.         }

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

  821.     } else {

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

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

  831.     return NGX_HTTP_DAV_INVALID_DEPTH;
  832. }


  833. static ngx_int_t
  834. ngx_http_dav_error(ngx_log_t *log, ngx_err_t err, ngx_int_t not_found,
  835.     char *failed, u_char *path)
  836. {
  837.     ngx_int_t   rc;
  838.     ngx_uint_t  level;

  839.     if (err == NGX_ENOENT || err == NGX_ENOTDIR || err == NGX_ENAMETOOLONG) {
  840.         level = NGX_LOG_ERR;
  841.         rc = not_found;

  842.     } else if (err == NGX_EACCES || err == NGX_EPERM) {
  843.         level = NGX_LOG_ERR;
  844.         rc = NGX_HTTP_FORBIDDEN;

  845.     } else if (err == NGX_EEXIST) {
  846.         level = NGX_LOG_ERR;
  847.         rc = NGX_HTTP_NOT_ALLOWED;

  848.     } else if (err == NGX_ENOSPC) {
  849.         level = NGX_LOG_CRIT;
  850.         rc = NGX_HTTP_INSUFFICIENT_STORAGE;

  851.     } else {
  852.         level = NGX_LOG_CRIT;
  853.         rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
  854.     }

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

  856.     return rc;
  857. }


  858. static ngx_int_t
  859. ngx_http_dav_location(ngx_http_request_t *r)
  860. {
  861.     u_char     *p;
  862.     size_t      len;
  863.     uintptr_t   escape;

  864.     r->headers_out.location = ngx_list_push(&r->headers_out.headers);
  865.     if (r->headers_out.location == NULL) {
  866.         return NGX_ERROR;
  867.     }

  868.     r->headers_out.location->hash = 1;
  869.     r->headers_out.location->next = NULL;
  870.     ngx_str_set(&r->headers_out.location->key, "Location");

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

  872.     if (escape) {
  873.         len = r->uri.len + escape;

  874.         p = ngx_pnalloc(r->pool, len);
  875.         if (p == NULL) {
  876.             ngx_http_clear_location(r);
  877.             return NGX_ERROR;
  878.         }

  879.         r->headers_out.location->value.len = len;
  880.         r->headers_out.location->value.data = p;

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

  882.     } else {
  883.         r->headers_out.location->value = r->uri;
  884.     }

  885.     return NGX_OK;
  886. }


  887. static void *
  888. ngx_http_dav_create_loc_conf(ngx_conf_t *cf)
  889. {
  890.     ngx_http_dav_loc_conf_t  *conf;

  891.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dav_loc_conf_t));
  892.     if (conf == NULL) {
  893.         return NULL;
  894.     }

  895.     /*
  896.      * set by ngx_pcalloc():
  897.      *
  898.      *     conf->methods = 0;
  899.      */

  900.     conf->min_delete_depth = NGX_CONF_UNSET_UINT;
  901.     conf->access = NGX_CONF_UNSET_UINT;
  902.     conf->create_full_put_path = NGX_CONF_UNSET;

  903.     return conf;
  904. }


  905. static char *
  906. ngx_http_dav_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
  907. {
  908.     ngx_http_dav_loc_conf_t  *prev = parent;
  909.     ngx_http_dav_loc_conf_t  *conf = child;

  910.     ngx_conf_merge_bitmask_value(conf->methods, prev->methods,
  911.                          (NGX_CONF_BITMASK_SET|NGX_HTTP_DAV_OFF));

  912.     ngx_conf_merge_uint_value(conf->min_delete_depth,
  913.                          prev->min_delete_depth, 0);

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

  915.     ngx_conf_merge_value(conf->create_full_put_path,
  916.                          prev->create_full_put_path, 0);

  917.     return NGX_CONF_OK;
  918. }


  919. static ngx_int_t
  920. ngx_http_dav_init(ngx_conf_t *cf)
  921. {
  922.     ngx_http_handler_pt        *h;
  923.     ngx_http_core_main_conf_t  *cmcf;

  924.     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

  925.     h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
  926.     if (h == NULL) {
  927.         return NGX_ERROR;
  928.     }

  929.     *h = ngx_http_dav_handler;

  930.     return NGX_OK;
  931. }