Annotation of embedaddon/nginx/src/http/modules/ngx_http_dav_module.c, revision 1.1
1.1 ! misho 1:
! 2: /*
! 3: * Copyright (C) Igor Sysoev
! 4: * Copyright (C) Nginx, Inc.
! 5: */
! 6:
! 7:
! 8: #include <ngx_config.h>
! 9: #include <ngx_core.h>
! 10: #include <ngx_http.h>
! 11:
! 12:
! 13: #define NGX_HTTP_DAV_COPY_BLOCK 65536
! 14:
! 15: #define NGX_HTTP_DAV_OFF 2
! 16:
! 17:
! 18: #define NGX_HTTP_DAV_NO_DEPTH -3
! 19: #define NGX_HTTP_DAV_INVALID_DEPTH -2
! 20: #define NGX_HTTP_DAV_INFINITY_DEPTH -1
! 21:
! 22:
! 23: typedef struct {
! 24: ngx_uint_t methods;
! 25: ngx_uint_t access;
! 26: ngx_uint_t min_delete_depth;
! 27: ngx_flag_t create_full_put_path;
! 28: } ngx_http_dav_loc_conf_t;
! 29:
! 30:
! 31: typedef struct {
! 32: ngx_str_t path;
! 33: size_t len;
! 34: } ngx_http_dav_copy_ctx_t;
! 35:
! 36:
! 37: static ngx_int_t ngx_http_dav_handler(ngx_http_request_t *r);
! 38:
! 39: static void ngx_http_dav_put_handler(ngx_http_request_t *r);
! 40:
! 41: static ngx_int_t ngx_http_dav_delete_handler(ngx_http_request_t *r);
! 42: static ngx_int_t ngx_http_dav_delete_path(ngx_http_request_t *r,
! 43: ngx_str_t *path, ngx_uint_t dir);
! 44: static ngx_int_t ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path);
! 45: static ngx_int_t ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path);
! 46: static ngx_int_t ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path);
! 47:
! 48: static ngx_int_t ngx_http_dav_mkcol_handler(ngx_http_request_t *r,
! 49: ngx_http_dav_loc_conf_t *dlcf);
! 50:
! 51: static ngx_int_t ngx_http_dav_copy_move_handler(ngx_http_request_t *r);
! 52: static ngx_int_t ngx_http_dav_copy_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path);
! 53: static ngx_int_t ngx_http_dav_copy_dir_time(ngx_tree_ctx_t *ctx,
! 54: ngx_str_t *path);
! 55: static ngx_int_t ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx,
! 56: ngx_str_t *path);
! 57:
! 58: static ngx_int_t ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt);
! 59: static ngx_int_t ngx_http_dav_error(ngx_log_t *log, ngx_err_t err,
! 60: ngx_int_t not_found, char *failed, u_char *path);
! 61: static ngx_int_t ngx_http_dav_location(ngx_http_request_t *r, u_char *path);
! 62: static void *ngx_http_dav_create_loc_conf(ngx_conf_t *cf);
! 63: static char *ngx_http_dav_merge_loc_conf(ngx_conf_t *cf,
! 64: void *parent, void *child);
! 65: static ngx_int_t ngx_http_dav_init(ngx_conf_t *cf);
! 66:
! 67:
! 68: static ngx_conf_bitmask_t ngx_http_dav_methods_mask[] = {
! 69: { ngx_string("off"), NGX_HTTP_DAV_OFF },
! 70: { ngx_string("put"), NGX_HTTP_PUT },
! 71: { ngx_string("delete"), NGX_HTTP_DELETE },
! 72: { ngx_string("mkcol"), NGX_HTTP_MKCOL },
! 73: { ngx_string("copy"), NGX_HTTP_COPY },
! 74: { ngx_string("move"), NGX_HTTP_MOVE },
! 75: { ngx_null_string, 0 }
! 76: };
! 77:
! 78:
! 79: static ngx_command_t ngx_http_dav_commands[] = {
! 80:
! 81: { ngx_string("dav_methods"),
! 82: NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
! 83: ngx_conf_set_bitmask_slot,
! 84: NGX_HTTP_LOC_CONF_OFFSET,
! 85: offsetof(ngx_http_dav_loc_conf_t, methods),
! 86: &ngx_http_dav_methods_mask },
! 87:
! 88: { ngx_string("create_full_put_path"),
! 89: NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
! 90: ngx_conf_set_flag_slot,
! 91: NGX_HTTP_LOC_CONF_OFFSET,
! 92: offsetof(ngx_http_dav_loc_conf_t, create_full_put_path),
! 93: NULL },
! 94:
! 95: { ngx_string("min_delete_depth"),
! 96: NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
! 97: ngx_conf_set_num_slot,
! 98: NGX_HTTP_LOC_CONF_OFFSET,
! 99: offsetof(ngx_http_dav_loc_conf_t, min_delete_depth),
! 100: NULL },
! 101:
! 102: { ngx_string("dav_access"),
! 103: NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
! 104: ngx_conf_set_access_slot,
! 105: NGX_HTTP_LOC_CONF_OFFSET,
! 106: offsetof(ngx_http_dav_loc_conf_t, access),
! 107: NULL },
! 108:
! 109: ngx_null_command
! 110: };
! 111:
! 112:
! 113: static ngx_http_module_t ngx_http_dav_module_ctx = {
! 114: NULL, /* preconfiguration */
! 115: ngx_http_dav_init, /* postconfiguration */
! 116:
! 117: NULL, /* create main configuration */
! 118: NULL, /* init main configuration */
! 119:
! 120: NULL, /* create server configuration */
! 121: NULL, /* merge server configuration */
! 122:
! 123: ngx_http_dav_create_loc_conf, /* create location configuration */
! 124: ngx_http_dav_merge_loc_conf /* merge location configuration */
! 125: };
! 126:
! 127:
! 128: ngx_module_t ngx_http_dav_module = {
! 129: NGX_MODULE_V1,
! 130: &ngx_http_dav_module_ctx, /* module context */
! 131: ngx_http_dav_commands, /* module directives */
! 132: NGX_HTTP_MODULE, /* module type */
! 133: NULL, /* init master */
! 134: NULL, /* init module */
! 135: NULL, /* init process */
! 136: NULL, /* init thread */
! 137: NULL, /* exit thread */
! 138: NULL, /* exit process */
! 139: NULL, /* exit master */
! 140: NGX_MODULE_V1_PADDING
! 141: };
! 142:
! 143:
! 144: static ngx_int_t
! 145: ngx_http_dav_handler(ngx_http_request_t *r)
! 146: {
! 147: ngx_int_t rc;
! 148: ngx_http_dav_loc_conf_t *dlcf;
! 149:
! 150: dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
! 151:
! 152: if (!(r->method & dlcf->methods)) {
! 153: return NGX_DECLINED;
! 154: }
! 155:
! 156: switch (r->method) {
! 157:
! 158: case NGX_HTTP_PUT:
! 159:
! 160: if (r->uri.data[r->uri.len - 1] == '/') {
! 161: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 162: "cannot PUT to a collection");
! 163: return NGX_HTTP_CONFLICT;
! 164: }
! 165:
! 166: r->request_body_in_file_only = 1;
! 167: r->request_body_in_persistent_file = 1;
! 168: r->request_body_in_clean_file = 1;
! 169: r->request_body_file_group_access = 1;
! 170: r->request_body_file_log_level = 0;
! 171:
! 172: rc = ngx_http_read_client_request_body(r, ngx_http_dav_put_handler);
! 173:
! 174: if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
! 175: return rc;
! 176: }
! 177:
! 178: return NGX_DONE;
! 179:
! 180: case NGX_HTTP_DELETE:
! 181:
! 182: return ngx_http_dav_delete_handler(r);
! 183:
! 184: case NGX_HTTP_MKCOL:
! 185:
! 186: return ngx_http_dav_mkcol_handler(r, dlcf);
! 187:
! 188: case NGX_HTTP_COPY:
! 189:
! 190: return ngx_http_dav_copy_move_handler(r);
! 191:
! 192: case NGX_HTTP_MOVE:
! 193:
! 194: return ngx_http_dav_copy_move_handler(r);
! 195: }
! 196:
! 197: return NGX_DECLINED;
! 198: }
! 199:
! 200:
! 201: static void
! 202: ngx_http_dav_put_handler(ngx_http_request_t *r)
! 203: {
! 204: size_t root;
! 205: time_t date;
! 206: ngx_str_t *temp, path;
! 207: ngx_uint_t status;
! 208: ngx_file_info_t fi;
! 209: ngx_ext_rename_file_t ext;
! 210: ngx_http_dav_loc_conf_t *dlcf;
! 211:
! 212: if (r->request_body == NULL || r->request_body->temp_file == NULL) {
! 213: ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
! 214: return;
! 215: }
! 216:
! 217: ngx_http_map_uri_to_path(r, &path, &root, 0);
! 218:
! 219: path.len--;
! 220:
! 221: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
! 222: "http put filename: \"%s\"", path.data);
! 223:
! 224: temp = &r->request_body->temp_file->file.name;
! 225:
! 226: if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) {
! 227: status = NGX_HTTP_CREATED;
! 228:
! 229: } else {
! 230: status = NGX_HTTP_NO_CONTENT;
! 231:
! 232: if (ngx_is_dir(&fi)) {
! 233: ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,
! 234: "\"%s\" could not be created", path.data);
! 235:
! 236: if (ngx_delete_file(temp->data) == NGX_FILE_ERROR) {
! 237: ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
! 238: ngx_delete_file_n " \"%s\" failed",
! 239: temp->data);
! 240: }
! 241:
! 242: ngx_http_finalize_request(r, NGX_HTTP_CONFLICT);
! 243: return;
! 244: }
! 245: }
! 246:
! 247: dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
! 248:
! 249: ext.access = dlcf->access;
! 250: ext.path_access = dlcf->access;
! 251: ext.time = -1;
! 252: ext.create_path = dlcf->create_full_put_path;
! 253: ext.delete_file = 1;
! 254: ext.log = r->connection->log;
! 255:
! 256: if (r->headers_in.date) {
! 257: date = ngx_http_parse_time(r->headers_in.date->value.data,
! 258: r->headers_in.date->value.len);
! 259:
! 260: if (date != NGX_ERROR) {
! 261: ext.time = date;
! 262: ext.fd = r->request_body->temp_file->file.fd;
! 263: }
! 264: }
! 265:
! 266: if (ngx_ext_rename_file(temp, &path, &ext) != NGX_OK) {
! 267: ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
! 268: return;
! 269: }
! 270:
! 271: if (status == NGX_HTTP_CREATED) {
! 272: if (ngx_http_dav_location(r, path.data) != NGX_OK) {
! 273: ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
! 274: return;
! 275: }
! 276:
! 277: r->headers_out.content_length_n = 0;
! 278: }
! 279:
! 280: r->headers_out.status = status;
! 281: r->header_only = 1;
! 282:
! 283: ngx_http_finalize_request(r, ngx_http_send_header(r));
! 284: return;
! 285: }
! 286:
! 287:
! 288: static ngx_int_t
! 289: ngx_http_dav_delete_handler(ngx_http_request_t *r)
! 290: {
! 291: size_t root;
! 292: ngx_err_t err;
! 293: ngx_int_t rc, depth;
! 294: ngx_uint_t i, d, dir;
! 295: ngx_str_t path;
! 296: ngx_file_info_t fi;
! 297: ngx_http_dav_loc_conf_t *dlcf;
! 298:
! 299: if (r->headers_in.content_length_n > 0) {
! 300: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 301: "DELETE with body is unsupported");
! 302: return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
! 303: }
! 304:
! 305: dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
! 306:
! 307: if (dlcf->min_delete_depth) {
! 308: d = 0;
! 309:
! 310: for (i = 0; i < r->uri.len; /* void */) {
! 311: if (r->uri.data[i++] == '/') {
! 312: if (++d >= dlcf->min_delete_depth && i < r->uri.len) {
! 313: goto ok;
! 314: }
! 315: }
! 316: }
! 317:
! 318: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 319: "insufficient URI depth:%i to DELETE", d);
! 320: return NGX_HTTP_CONFLICT;
! 321: }
! 322:
! 323: ok:
! 324:
! 325: ngx_http_map_uri_to_path(r, &path, &root, 0);
! 326:
! 327: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
! 328: "http delete filename: \"%s\"", path.data);
! 329:
! 330: if (ngx_link_info(path.data, &fi) == NGX_FILE_ERROR) {
! 331: err = ngx_errno;
! 332:
! 333: rc = (err == NGX_ENOTDIR) ? NGX_HTTP_CONFLICT : NGX_HTTP_NOT_FOUND;
! 334:
! 335: return ngx_http_dav_error(r->connection->log, err,
! 336: rc, ngx_link_info_n, path.data);
! 337: }
! 338:
! 339: if (ngx_is_dir(&fi)) {
! 340:
! 341: if (r->uri.data[r->uri.len - 1] != '/') {
! 342: ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,
! 343: "DELETE \"%s\" failed", path.data);
! 344: return NGX_HTTP_CONFLICT;
! 345: }
! 346:
! 347: depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);
! 348:
! 349: if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
! 350: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 351: "\"Depth\" header must be infinity");
! 352: return NGX_HTTP_BAD_REQUEST;
! 353: }
! 354:
! 355: path.len -= 2; /* omit "/\0" */
! 356:
! 357: dir = 1;
! 358:
! 359: } else {
! 360:
! 361: /*
! 362: * we do not need to test (r->uri.data[r->uri.len - 1] == '/')
! 363: * because ngx_link_info("/file/") returned NGX_ENOTDIR above
! 364: */
! 365:
! 366: depth = ngx_http_dav_depth(r, 0);
! 367:
! 368: if (depth != 0 && depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
! 369: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 370: "\"Depth\" header must be 0 or infinity");
! 371: return NGX_HTTP_BAD_REQUEST;
! 372: }
! 373:
! 374: dir = 0;
! 375: }
! 376:
! 377: rc = ngx_http_dav_delete_path(r, &path, dir);
! 378:
! 379: if (rc == NGX_OK) {
! 380: return NGX_HTTP_NO_CONTENT;
! 381: }
! 382:
! 383: return rc;
! 384: }
! 385:
! 386:
! 387: static ngx_int_t
! 388: ngx_http_dav_delete_path(ngx_http_request_t *r, ngx_str_t *path, ngx_uint_t dir)
! 389: {
! 390: char *failed;
! 391: ngx_tree_ctx_t tree;
! 392:
! 393: if (dir) {
! 394:
! 395: tree.init_handler = NULL;
! 396: tree.file_handler = ngx_http_dav_delete_file;
! 397: tree.pre_tree_handler = ngx_http_dav_noop;
! 398: tree.post_tree_handler = ngx_http_dav_delete_dir;
! 399: tree.spec_handler = ngx_http_dav_delete_file;
! 400: tree.data = NULL;
! 401: tree.alloc = 0;
! 402: tree.log = r->connection->log;
! 403:
! 404: /* TODO: 207 */
! 405:
! 406: if (ngx_walk_tree(&tree, path) != NGX_OK) {
! 407: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 408: }
! 409:
! 410: if (ngx_delete_dir(path->data) != NGX_FILE_ERROR) {
! 411: return NGX_OK;
! 412: }
! 413:
! 414: failed = ngx_delete_dir_n;
! 415:
! 416: } else {
! 417:
! 418: if (ngx_delete_file(path->data) != NGX_FILE_ERROR) {
! 419: return NGX_OK;
! 420: }
! 421:
! 422: failed = ngx_delete_file_n;
! 423: }
! 424:
! 425: return ngx_http_dav_error(r->connection->log, ngx_errno,
! 426: NGX_HTTP_NOT_FOUND, failed, path->data);
! 427: }
! 428:
! 429:
! 430: static ngx_int_t
! 431: ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path)
! 432: {
! 433: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
! 434: "http delete dir: \"%s\"", path->data);
! 435:
! 436: if (ngx_delete_dir(path->data) == NGX_FILE_ERROR) {
! 437:
! 438: /* TODO: add to 207 */
! 439:
! 440: (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_delete_dir_n,
! 441: path->data);
! 442: }
! 443:
! 444: return NGX_OK;
! 445: }
! 446:
! 447:
! 448: static ngx_int_t
! 449: ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
! 450: {
! 451: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
! 452: "http delete file: \"%s\"", path->data);
! 453:
! 454: if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {
! 455:
! 456: /* TODO: add to 207 */
! 457:
! 458: (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_delete_file_n,
! 459: path->data);
! 460: }
! 461:
! 462: return NGX_OK;
! 463: }
! 464:
! 465:
! 466: static ngx_int_t
! 467: ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
! 468: {
! 469: return NGX_OK;
! 470: }
! 471:
! 472:
! 473: static ngx_int_t
! 474: ngx_http_dav_mkcol_handler(ngx_http_request_t *r, ngx_http_dav_loc_conf_t *dlcf)
! 475: {
! 476: u_char *p;
! 477: size_t root;
! 478: ngx_str_t path;
! 479:
! 480: if (r->headers_in.content_length_n > 0) {
! 481: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 482: "MKCOL with body is unsupported");
! 483: return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
! 484: }
! 485:
! 486: if (r->uri.data[r->uri.len - 1] != '/') {
! 487: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 488: "MKCOL can create a collection only");
! 489: return NGX_HTTP_CONFLICT;
! 490: }
! 491:
! 492: p = ngx_http_map_uri_to_path(r, &path, &root, 0);
! 493:
! 494: *(p - 1) = '\0';
! 495: r->uri.len--;
! 496:
! 497: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
! 498: "http mkcol path: \"%s\"", path.data);
! 499:
! 500: if (ngx_create_dir(path.data, ngx_dir_access(dlcf->access))
! 501: != NGX_FILE_ERROR)
! 502: {
! 503: if (ngx_http_dav_location(r, path.data) != NGX_OK) {
! 504: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 505: }
! 506:
! 507: return NGX_HTTP_CREATED;
! 508: }
! 509:
! 510: return ngx_http_dav_error(r->connection->log, ngx_errno,
! 511: NGX_HTTP_CONFLICT, ngx_create_dir_n, path.data);
! 512: }
! 513:
! 514:
! 515: static ngx_int_t
! 516: ngx_http_dav_copy_move_handler(ngx_http_request_t *r)
! 517: {
! 518: u_char *p, *host, *last, ch;
! 519: size_t len, root;
! 520: ngx_err_t err;
! 521: ngx_int_t rc, depth;
! 522: ngx_uint_t overwrite, slash, dir, flags;
! 523: ngx_str_t path, uri, duri, args;
! 524: ngx_tree_ctx_t tree;
! 525: ngx_copy_file_t cf;
! 526: ngx_file_info_t fi;
! 527: ngx_table_elt_t *dest, *over;
! 528: ngx_ext_rename_file_t ext;
! 529: ngx_http_dav_copy_ctx_t copy;
! 530: ngx_http_dav_loc_conf_t *dlcf;
! 531:
! 532: if (r->headers_in.content_length_n > 0) {
! 533: return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
! 534: }
! 535:
! 536: dest = r->headers_in.destination;
! 537:
! 538: if (dest == NULL) {
! 539: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 540: "client sent no \"Destination\" header");
! 541: return NGX_HTTP_BAD_REQUEST;
! 542: }
! 543:
! 544: p = dest->value.data;
! 545: /* there is always '\0' even after empty header value */
! 546: if (p[0] == '/') {
! 547: last = p + dest->value.len;
! 548: goto destination_done;
! 549: }
! 550:
! 551: len = r->headers_in.server.len;
! 552:
! 553: if (len == 0) {
! 554: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 555: "client sent no \"Host\" header");
! 556: return NGX_HTTP_BAD_REQUEST;
! 557: }
! 558:
! 559: #if (NGX_HTTP_SSL)
! 560:
! 561: if (r->connection->ssl) {
! 562: if (ngx_strncmp(dest->value.data, "https://", sizeof("https://") - 1)
! 563: != 0)
! 564: {
! 565: goto invalid_destination;
! 566: }
! 567:
! 568: host = dest->value.data + sizeof("https://") - 1;
! 569:
! 570: } else
! 571: #endif
! 572: {
! 573: if (ngx_strncmp(dest->value.data, "http://", sizeof("http://") - 1)
! 574: != 0)
! 575: {
! 576: goto invalid_destination;
! 577: }
! 578:
! 579: host = dest->value.data + sizeof("http://") - 1;
! 580: }
! 581:
! 582: if (ngx_strncmp(host, r->headers_in.server.data, len) != 0) {
! 583: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 584: "\"Destination\" URI \"%V\" is handled by "
! 585: "different repository than the source URI",
! 586: &dest->value);
! 587: return NGX_HTTP_BAD_REQUEST;
! 588: }
! 589:
! 590: last = dest->value.data + dest->value.len;
! 591:
! 592: for (p = host + len; p < last; p++) {
! 593: if (*p == '/') {
! 594: goto destination_done;
! 595: }
! 596: }
! 597:
! 598: invalid_destination:
! 599:
! 600: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 601: "client sent invalid \"Destination\" header: \"%V\"",
! 602: &dest->value);
! 603: return NGX_HTTP_BAD_REQUEST;
! 604:
! 605: destination_done:
! 606:
! 607: duri.len = last - p;
! 608: duri.data = p;
! 609: flags = 0;
! 610:
! 611: if (ngx_http_parse_unsafe_uri(r, &duri, &args, &flags) != NGX_OK) {
! 612: goto invalid_destination;
! 613: }
! 614:
! 615: if ((r->uri.data[r->uri.len - 1] == '/' && *(last - 1) != '/')
! 616: || (r->uri.data[r->uri.len - 1] != '/' && *(last - 1) == '/'))
! 617: {
! 618: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 619: "both URI \"%V\" and \"Destination\" URI \"%V\" "
! 620: "should be either collections or non-collections",
! 621: &r->uri, &dest->value);
! 622: return NGX_HTTP_CONFLICT;
! 623: }
! 624:
! 625: depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);
! 626:
! 627: if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
! 628:
! 629: if (r->method == NGX_HTTP_COPY) {
! 630: if (depth != 0) {
! 631: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 632: "\"Depth\" header must be 0 or infinity");
! 633: return NGX_HTTP_BAD_REQUEST;
! 634: }
! 635:
! 636: } else {
! 637: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 638: "\"Depth\" header must be infinity");
! 639: return NGX_HTTP_BAD_REQUEST;
! 640: }
! 641: }
! 642:
! 643: over = r->headers_in.overwrite;
! 644:
! 645: if (over) {
! 646: if (over->value.len == 1) {
! 647: ch = over->value.data[0];
! 648:
! 649: if (ch == 'T' || ch == 't') {
! 650: overwrite = 1;
! 651: goto overwrite_done;
! 652: }
! 653:
! 654: if (ch == 'F' || ch == 'f') {
! 655: overwrite = 0;
! 656: goto overwrite_done;
! 657: }
! 658:
! 659: }
! 660:
! 661: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 662: "client sent invalid \"Overwrite\" header: \"%V\"",
! 663: &over->value);
! 664: return NGX_HTTP_BAD_REQUEST;
! 665: }
! 666:
! 667: overwrite = 1;
! 668:
! 669: overwrite_done:
! 670:
! 671: ngx_http_map_uri_to_path(r, &path, &root, 0);
! 672:
! 673: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
! 674: "http copy from: \"%s\"", path.data);
! 675:
! 676: uri = r->uri;
! 677: r->uri = duri;
! 678:
! 679: ngx_http_map_uri_to_path(r, ©.path, &root, 0);
! 680:
! 681: r->uri = uri;
! 682:
! 683: copy.path.len--; /* omit "\0" */
! 684:
! 685: if (copy.path.data[copy.path.len - 1] == '/') {
! 686: slash = 1;
! 687: copy.path.len--;
! 688: copy.path.data[copy.path.len] = '\0';
! 689:
! 690: } else {
! 691: slash = 0;
! 692: }
! 693:
! 694: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
! 695: "http copy to: \"%s\"", copy.path.data);
! 696:
! 697: if (ngx_link_info(copy.path.data, &fi) == NGX_FILE_ERROR) {
! 698: err = ngx_errno;
! 699:
! 700: if (err != NGX_ENOENT) {
! 701: return ngx_http_dav_error(r->connection->log, err,
! 702: NGX_HTTP_NOT_FOUND, ngx_link_info_n,
! 703: copy.path.data);
! 704: }
! 705:
! 706: /* destination does not exist */
! 707:
! 708: overwrite = 0;
! 709: dir = 0;
! 710:
! 711: } else {
! 712:
! 713: /* destination exists */
! 714:
! 715: if (ngx_is_dir(&fi) && !slash) {
! 716: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 717: "\"%V\" could not be %Ved to collection \"%V\"",
! 718: &r->uri, &r->method_name, &dest->value);
! 719: return NGX_HTTP_CONFLICT;
! 720: }
! 721:
! 722: if (!overwrite) {
! 723: ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EEXIST,
! 724: "\"%s\" could not be created", copy.path.data);
! 725: return NGX_HTTP_PRECONDITION_FAILED;
! 726: }
! 727:
! 728: dir = ngx_is_dir(&fi);
! 729: }
! 730:
! 731: if (ngx_link_info(path.data, &fi) == NGX_FILE_ERROR) {
! 732: return ngx_http_dav_error(r->connection->log, ngx_errno,
! 733: NGX_HTTP_NOT_FOUND, ngx_link_info_n,
! 734: path.data);
! 735: }
! 736:
! 737: if (ngx_is_dir(&fi)) {
! 738:
! 739: if (r->uri.data[r->uri.len - 1] != '/') {
! 740: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 741: "\"%V\" is collection", &r->uri);
! 742: return NGX_HTTP_BAD_REQUEST;
! 743: }
! 744:
! 745: if (overwrite) {
! 746: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
! 747: "http delete: \"%s\"", copy.path.data);
! 748:
! 749: rc = ngx_http_dav_delete_path(r, ©.path, dir);
! 750:
! 751: if (rc != NGX_OK) {
! 752: return rc;
! 753: }
! 754: }
! 755: }
! 756:
! 757: if (ngx_is_dir(&fi)) {
! 758:
! 759: path.len -= 2; /* omit "/\0" */
! 760:
! 761: if (r->method == NGX_HTTP_MOVE) {
! 762: if (ngx_rename_file(path.data, copy.path.data) != NGX_FILE_ERROR) {
! 763: return NGX_HTTP_CREATED;
! 764: }
! 765: }
! 766:
! 767: if (ngx_create_dir(copy.path.data, ngx_file_access(&fi))
! 768: == NGX_FILE_ERROR)
! 769: {
! 770: return ngx_http_dav_error(r->connection->log, ngx_errno,
! 771: NGX_HTTP_NOT_FOUND,
! 772: ngx_create_dir_n, copy.path.data);
! 773: }
! 774:
! 775: copy.len = path.len;
! 776:
! 777: tree.init_handler = NULL;
! 778: tree.file_handler = ngx_http_dav_copy_tree_file;
! 779: tree.pre_tree_handler = ngx_http_dav_copy_dir;
! 780: tree.post_tree_handler = ngx_http_dav_copy_dir_time;
! 781: tree.spec_handler = ngx_http_dav_noop;
! 782: tree.data = ©
! 783: tree.alloc = 0;
! 784: tree.log = r->connection->log;
! 785:
! 786: if (ngx_walk_tree(&tree, &path) == NGX_OK) {
! 787:
! 788: if (r->method == NGX_HTTP_MOVE) {
! 789: rc = ngx_http_dav_delete_path(r, &path, 1);
! 790:
! 791: if (rc != NGX_OK) {
! 792: return rc;
! 793: }
! 794: }
! 795:
! 796: return NGX_HTTP_CREATED;
! 797: }
! 798:
! 799: } else {
! 800:
! 801: if (r->method == NGX_HTTP_MOVE) {
! 802:
! 803: dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
! 804:
! 805: ext.access = 0;
! 806: ext.path_access = dlcf->access;
! 807: ext.time = -1;
! 808: ext.create_path = 1;
! 809: ext.delete_file = 0;
! 810: ext.log = r->connection->log;
! 811:
! 812: if (ngx_ext_rename_file(&path, ©.path, &ext) == NGX_OK) {
! 813: return NGX_HTTP_NO_CONTENT;
! 814: }
! 815:
! 816: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 817: }
! 818:
! 819: dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
! 820:
! 821: cf.size = ngx_file_size(&fi);
! 822: cf.buf_size = 0;
! 823: cf.access = dlcf->access;
! 824: cf.time = ngx_file_mtime(&fi);
! 825: cf.log = r->connection->log;
! 826:
! 827: if (ngx_copy_file(path.data, copy.path.data, &cf) == NGX_OK) {
! 828: return NGX_HTTP_NO_CONTENT;
! 829: }
! 830: }
! 831:
! 832: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 833: }
! 834:
! 835:
! 836: static ngx_int_t
! 837: ngx_http_dav_copy_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path)
! 838: {
! 839: u_char *p, *dir;
! 840: size_t len;
! 841: ngx_http_dav_copy_ctx_t *copy;
! 842:
! 843: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
! 844: "http copy dir: \"%s\"", path->data);
! 845:
! 846: copy = ctx->data;
! 847:
! 848: len = copy->path.len + path->len;
! 849:
! 850: dir = ngx_alloc(len + 1, ctx->log);
! 851: if (dir == NULL) {
! 852: return NGX_ABORT;
! 853: }
! 854:
! 855: p = ngx_cpymem(dir, copy->path.data, copy->path.len);
! 856: (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);
! 857:
! 858: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
! 859: "http copy dir to: \"%s\"", dir);
! 860:
! 861: if (ngx_create_dir(dir, ngx_dir_access(ctx->access)) == NGX_FILE_ERROR) {
! 862: (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_create_dir_n,
! 863: dir);
! 864: }
! 865:
! 866: ngx_free(dir);
! 867:
! 868: return NGX_OK;
! 869: }
! 870:
! 871:
! 872: static ngx_int_t
! 873: ngx_http_dav_copy_dir_time(ngx_tree_ctx_t *ctx, ngx_str_t *path)
! 874: {
! 875: u_char *p, *dir;
! 876: size_t len;
! 877: ngx_http_dav_copy_ctx_t *copy;
! 878:
! 879: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
! 880: "http copy dir time: \"%s\"", path->data);
! 881:
! 882: copy = ctx->data;
! 883:
! 884: len = copy->path.len + path->len;
! 885:
! 886: dir = ngx_alloc(len + 1, ctx->log);
! 887: if (dir == NULL) {
! 888: return NGX_ABORT;
! 889: }
! 890:
! 891: p = ngx_cpymem(dir, copy->path.data, copy->path.len);
! 892: (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);
! 893:
! 894: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
! 895: "http copy dir time to: \"%s\"", dir);
! 896:
! 897: #if (NGX_WIN32)
! 898: {
! 899: ngx_fd_t fd;
! 900:
! 901: fd = ngx_open_file(dir, NGX_FILE_RDWR, NGX_FILE_OPEN, 0);
! 902:
! 903: if (fd == NGX_INVALID_FILE) {
! 904: (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_open_file_n, dir);
! 905: goto failed;
! 906: }
! 907:
! 908: if (ngx_set_file_time(NULL, fd, ctx->mtime) != NGX_OK) {
! 909: ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
! 910: ngx_set_file_time_n " \"%s\" failed", dir);
! 911: }
! 912:
! 913: if (ngx_close_file(fd) == NGX_FILE_ERROR) {
! 914: ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
! 915: ngx_close_file_n " \"%s\" failed", dir);
! 916: }
! 917: }
! 918:
! 919: failed:
! 920:
! 921: #else
! 922:
! 923: if (ngx_set_file_time(dir, 0, ctx->mtime) != NGX_OK) {
! 924: ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
! 925: ngx_set_file_time_n " \"%s\" failed", dir);
! 926: }
! 927:
! 928: #endif
! 929:
! 930: ngx_free(dir);
! 931:
! 932: return NGX_OK;
! 933: }
! 934:
! 935:
! 936: static ngx_int_t
! 937: ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
! 938: {
! 939: u_char *p, *file;
! 940: size_t len;
! 941: ngx_copy_file_t cf;
! 942: ngx_http_dav_copy_ctx_t *copy;
! 943:
! 944: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
! 945: "http copy file: \"%s\"", path->data);
! 946:
! 947: copy = ctx->data;
! 948:
! 949: len = copy->path.len + path->len;
! 950:
! 951: file = ngx_alloc(len + 1, ctx->log);
! 952: if (file == NULL) {
! 953: return NGX_ABORT;
! 954: }
! 955:
! 956: p = ngx_cpymem(file, copy->path.data, copy->path.len);
! 957: (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);
! 958:
! 959: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
! 960: "http copy file to: \"%s\"", file);
! 961:
! 962: cf.size = ctx->size;
! 963: cf.buf_size = 0;
! 964: cf.access = ctx->access;
! 965: cf.time = ctx->mtime;
! 966: cf.log = ctx->log;
! 967:
! 968: (void) ngx_copy_file(path->data, file, &cf);
! 969:
! 970: ngx_free(file);
! 971:
! 972: return NGX_OK;
! 973: }
! 974:
! 975:
! 976: static ngx_int_t
! 977: ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt)
! 978: {
! 979: ngx_table_elt_t *depth;
! 980:
! 981: depth = r->headers_in.depth;
! 982:
! 983: if (depth == NULL) {
! 984: return dflt;
! 985: }
! 986:
! 987: if (depth->value.len == 1) {
! 988:
! 989: if (depth->value.data[0] == '0') {
! 990: return 0;
! 991: }
! 992:
! 993: if (depth->value.data[0] == '1') {
! 994: return 1;
! 995: }
! 996:
! 997: } else {
! 998:
! 999: if (depth->value.len == sizeof("infinity") - 1
! 1000: && ngx_strcmp(depth->value.data, "infinity") == 0)
! 1001: {
! 1002: return NGX_HTTP_DAV_INFINITY_DEPTH;
! 1003: }
! 1004: }
! 1005:
! 1006: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 1007: "client sent invalid \"Depth\" header: \"%V\"",
! 1008: &depth->value);
! 1009:
! 1010: return NGX_HTTP_DAV_INVALID_DEPTH;
! 1011: }
! 1012:
! 1013:
! 1014: static ngx_int_t
! 1015: ngx_http_dav_error(ngx_log_t *log, ngx_err_t err, ngx_int_t not_found,
! 1016: char *failed, u_char *path)
! 1017: {
! 1018: ngx_int_t rc;
! 1019: ngx_uint_t level;
! 1020:
! 1021: if (err == NGX_ENOENT || err == NGX_ENOTDIR || err == NGX_ENAMETOOLONG) {
! 1022: level = NGX_LOG_ERR;
! 1023: rc = not_found;
! 1024:
! 1025: } else if (err == NGX_EACCES || err == NGX_EPERM) {
! 1026: level = NGX_LOG_ERR;
! 1027: rc = NGX_HTTP_FORBIDDEN;
! 1028:
! 1029: } else if (err == NGX_EEXIST) {
! 1030: level = NGX_LOG_ERR;
! 1031: rc = NGX_HTTP_NOT_ALLOWED;
! 1032:
! 1033: } else if (err == NGX_ENOSPC) {
! 1034: level = NGX_LOG_CRIT;
! 1035: rc = NGX_HTTP_INSUFFICIENT_STORAGE;
! 1036:
! 1037: } else {
! 1038: level = NGX_LOG_CRIT;
! 1039: rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
! 1040: }
! 1041:
! 1042: ngx_log_error(level, log, err, "%s \"%s\" failed", failed, path);
! 1043:
! 1044: return rc;
! 1045: }
! 1046:
! 1047:
! 1048: static ngx_int_t
! 1049: ngx_http_dav_location(ngx_http_request_t *r, u_char *path)
! 1050: {
! 1051: u_char *location;
! 1052: ngx_http_core_loc_conf_t *clcf;
! 1053:
! 1054: r->headers_out.location = ngx_palloc(r->pool, sizeof(ngx_table_elt_t));
! 1055: if (r->headers_out.location == NULL) {
! 1056: return NGX_ERROR;
! 1057: }
! 1058:
! 1059: clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
! 1060:
! 1061: if (!clcf->alias && clcf->root_lengths == NULL) {
! 1062: location = path + clcf->root.len;
! 1063:
! 1064: } else {
! 1065: location = ngx_pnalloc(r->pool, r->uri.len);
! 1066: if (location == NULL) {
! 1067: return NGX_ERROR;
! 1068: }
! 1069:
! 1070: ngx_memcpy(location, r->uri.data, r->uri.len);
! 1071: }
! 1072:
! 1073: /*
! 1074: * we do not need to set the r->headers_out.location->hash and
! 1075: * r->headers_out.location->key fields
! 1076: */
! 1077:
! 1078: r->headers_out.location->value.len = r->uri.len;
! 1079: r->headers_out.location->value.data = location;
! 1080:
! 1081: return NGX_OK;
! 1082: }
! 1083:
! 1084:
! 1085: static void *
! 1086: ngx_http_dav_create_loc_conf(ngx_conf_t *cf)
! 1087: {
! 1088: ngx_http_dav_loc_conf_t *conf;
! 1089:
! 1090: conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dav_loc_conf_t));
! 1091: if (conf == NULL) {
! 1092: return NULL;
! 1093: }
! 1094:
! 1095: /*
! 1096: * set by ngx_pcalloc():
! 1097: *
! 1098: * conf->methods = 0;
! 1099: */
! 1100:
! 1101: conf->min_delete_depth = NGX_CONF_UNSET_UINT;
! 1102: conf->access = NGX_CONF_UNSET_UINT;
! 1103: conf->create_full_put_path = NGX_CONF_UNSET;
! 1104:
! 1105: return conf;
! 1106: }
! 1107:
! 1108:
! 1109: static char *
! 1110: ngx_http_dav_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
! 1111: {
! 1112: ngx_http_dav_loc_conf_t *prev = parent;
! 1113: ngx_http_dav_loc_conf_t *conf = child;
! 1114:
! 1115: ngx_conf_merge_bitmask_value(conf->methods, prev->methods,
! 1116: (NGX_CONF_BITMASK_SET|NGX_HTTP_DAV_OFF));
! 1117:
! 1118: ngx_conf_merge_uint_value(conf->min_delete_depth,
! 1119: prev->min_delete_depth, 0);
! 1120:
! 1121: ngx_conf_merge_uint_value(conf->access, prev->access, 0600);
! 1122:
! 1123: ngx_conf_merge_value(conf->create_full_put_path,
! 1124: prev->create_full_put_path, 0);
! 1125:
! 1126: return NGX_CONF_OK;
! 1127: }
! 1128:
! 1129:
! 1130: static ngx_int_t
! 1131: ngx_http_dav_init(ngx_conf_t *cf)
! 1132: {
! 1133: ngx_http_handler_pt *h;
! 1134: ngx_http_core_main_conf_t *cmcf;
! 1135:
! 1136: cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
! 1137:
! 1138: h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
! 1139: if (h == NULL) {
! 1140: return NGX_ERROR;
! 1141: }
! 1142:
! 1143: *h = ngx_http_dav_handler;
! 1144:
! 1145: return NGX_OK;
! 1146: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>