| /****************************************************************************** |
| Copyright (c) 2012, Roman Arutyunyan (arut@qip.ru) |
| All rights reserved. |
| |
| Redistribution and use in source and binary forms, with or without modification, |
| are permitted provided that the following conditions are met: |
| |
| 1. Redistributions of source code must retain the above copyright notice, |
| this list of conditions and the following disclaimer. |
| |
| 2. Redistributions in binary form must reproduce the above copyright notice, |
| this list of conditions and the following disclaimer in the documentation |
| and/or other materials provided with the distribution. |
| |
| THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED |
| WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
| SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
| IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
| OF SUCH DAMAGE. |
| *******************************************************************************/ |
| |
| /* |
| |
| NGINX missing WebDAV commands support |
| |
| *PROPFIND & OPTIONS* |
| |
| */ |
| |
| #include <ngx_config.h> |
| #include <ngx_core.h> |
| #include <ngx_http.h> |
| |
| #include <sys/types.h> |
| #include <dirent.h> |
| #include <time.h> |
| |
| #include <expat.h> |
| |
| #define NGX_HTTP_DAV_EXT_OFF 2 |
| |
| typedef struct { |
| |
| ngx_uint_t methods; |
| |
| } ngx_http_dav_ext_loc_conf_t; |
| |
| static ngx_int_t ngx_http_dav_ext_init(ngx_conf_t *cf); |
| static void * ngx_http_dav_ext_create_loc_conf(ngx_conf_t *cf); |
| static char * ngx_http_dav_ext_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); |
| |
| static ngx_conf_bitmask_t ngx_http_dav_ext_methods_mask[] = { |
| |
| { ngx_string("off"), NGX_HTTP_DAV_EXT_OFF }, |
| { ngx_string("propfind"), NGX_HTTP_PROPFIND }, |
| { ngx_string("options"), NGX_HTTP_OPTIONS }, |
| { ngx_null_string, 0 } |
| |
| }; |
| |
| static ngx_command_t ngx_http_dav_ext_commands[] = { |
| |
| { ngx_string("dav_ext_methods"), |
| NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, |
| ngx_conf_set_bitmask_slot, |
| NGX_HTTP_LOC_CONF_OFFSET, |
| offsetof(ngx_http_dav_ext_loc_conf_t, methods), |
| &ngx_http_dav_ext_methods_mask }, |
| |
| ngx_null_command |
| }; |
| |
| static ngx_http_module_t ngx_http_dav_ext_module_ctx = { |
| NULL, /* preconfiguration */ |
| ngx_http_dav_ext_init, /* postconfiguration */ |
| |
| NULL, /* create main configuration */ |
| NULL, /* init main configuration */ |
| |
| NULL, /* create server configuration */ |
| NULL, /* merge server configuration */ |
| |
| ngx_http_dav_ext_create_loc_conf, /* create location configuration */ |
| ngx_http_dav_ext_merge_loc_conf, /* merge location configuration */ |
| }; |
| |
| |
| ngx_module_t ngx_http_dav_ext_module = { |
| NGX_MODULE_V1, |
| &ngx_http_dav_ext_module_ctx, /* module context */ |
| ngx_http_dav_ext_commands, /* module directives */ |
| NGX_HTTP_MODULE, /* module type */ |
| NULL, /* init master */ |
| NULL, /* init module */ |
| NULL, /* init process */ |
| NULL, /* init thread */ |
| NULL, /* exit thread */ |
| NULL, /* exit process */ |
| NULL, /* exit master */ |
| NGX_MODULE_V1_PADDING |
| }; |
| |
| #define NGX_HTTP_DAV_EXT_NODE_propfind 0x001 |
| #define NGX_HTTP_DAV_EXT_NODE_prop 0x002 |
| #define NGX_HTTP_DAV_EXT_NODE_propname 0x004 |
| #define NGX_HTTP_DAV_EXT_NODE_allprop 0x008 |
| |
| #define NGX_HTTP_DAV_EXT_PROP_creationdate 0x001 |
| #define NGX_HTTP_DAV_EXT_PROP_displayname 0x002 |
| #define NGX_HTTP_DAV_EXT_PROP_getcontentlanguage 0x004 |
| #define NGX_HTTP_DAV_EXT_PROP_getcontentlength 0x008 |
| #define NGX_HTTP_DAV_EXT_PROP_getcontenttype 0x010 |
| #define NGX_HTTP_DAV_EXT_PROP_getetag 0x020 |
| #define NGX_HTTP_DAV_EXT_PROP_getlastmodified 0x040 |
| #define NGX_HTTP_DAV_EXT_PROP_lockdiscovery 0x080 |
| #define NGX_HTTP_DAV_EXT_PROP_resourcetype 0x100 |
| #define NGX_HTTP_DAV_EXT_PROP_source 0x200 |
| #define NGX_HTTP_DAV_EXT_PROP_supportedlock 0x400 |
| |
| #define NGX_HTTP_DAV_EXT_PROPFIND_SELECTED 1 |
| #define NGX_HTTP_DAV_EXT_PROPFIND_NAMES 2 |
| #define NGX_HTTP_DAV_EXT_PROPFIND_ALL 3 |
| |
| typedef struct { |
| |
| ngx_uint_t nodes; |
| |
| ngx_uint_t props; |
| |
| ngx_uint_t propfind; |
| |
| } ngx_http_dav_ext_ctx_t; |
| |
| static int ngx_http_dav_ext_xmlcmp(const char *xname, const char *sname) { |
| |
| const char *c; |
| |
| c = strrchr(xname, ':'); |
| |
| return strcmp(c ? c + 1 : xname, sname); |
| } |
| |
| static void ngx_http_dav_ext_start_xml_elt(void *user_data, |
| const XML_Char *name, const XML_Char **atts) |
| { |
| ngx_http_dav_ext_ctx_t *ctx = user_data; |
| |
| #define NGX_HTTP_DAV_EXT_SET_NODE(nm) \ |
| if (!ngx_http_dav_ext_xmlcmp(name, #nm)) \ |
| ctx->nodes ^= NGX_HTTP_DAV_EXT_NODE_##nm |
| |
| NGX_HTTP_DAV_EXT_SET_NODE(propfind); |
| NGX_HTTP_DAV_EXT_SET_NODE(prop); |
| NGX_HTTP_DAV_EXT_SET_NODE(propname); |
| NGX_HTTP_DAV_EXT_SET_NODE(allprop); |
| |
| } |
| |
| static void ngx_http_dav_ext_end_xml_elt(void *user_data, const XML_Char *name) |
| { |
| ngx_http_dav_ext_ctx_t *ctx = user_data; |
| |
| if (ctx->nodes & NGX_HTTP_DAV_EXT_NODE_propfind) { |
| |
| if (ctx->nodes & NGX_HTTP_DAV_EXT_NODE_prop) { |
| |
| ctx->propfind = NGX_HTTP_DAV_EXT_PROPFIND_SELECTED; |
| |
| #define NGX_HTTP_DAV_EXT_SET_PROP(nm) \ |
| if (!ngx_http_dav_ext_xmlcmp(name, #nm)) \ |
| ctx->props |= NGX_HTTP_DAV_EXT_PROP_##nm |
| |
| NGX_HTTP_DAV_EXT_SET_PROP(creationdate); |
| NGX_HTTP_DAV_EXT_SET_PROP(displayname); |
| NGX_HTTP_DAV_EXT_SET_PROP(getcontentlanguage); |
| NGX_HTTP_DAV_EXT_SET_PROP(getcontentlength); |
| NGX_HTTP_DAV_EXT_SET_PROP(getcontenttype); |
| NGX_HTTP_DAV_EXT_SET_PROP(getetag); |
| NGX_HTTP_DAV_EXT_SET_PROP(getlastmodified); |
| NGX_HTTP_DAV_EXT_SET_PROP(lockdiscovery); |
| NGX_HTTP_DAV_EXT_SET_PROP(resourcetype); |
| NGX_HTTP_DAV_EXT_SET_PROP(source); |
| NGX_HTTP_DAV_EXT_SET_PROP(supportedlock); |
| |
| } |
| |
| if (ctx->nodes & NGX_HTTP_DAV_EXT_NODE_propname) { |
| |
| ctx->propfind = NGX_HTTP_DAV_EXT_PROPFIND_NAMES; |
| |
| } |
| |
| if (ctx->nodes & NGX_HTTP_DAV_EXT_NODE_allprop) { |
| |
| ctx->propfind = NGX_HTTP_DAV_EXT_PROPFIND_ALL; |
| |
| } |
| |
| } |
| |
| ngx_http_dav_ext_start_xml_elt(user_data, name, NULL); |
| } |
| |
| #define NGX_HTTP_DAV_EXT_COPY 0x01 |
| #define NGX_HTTP_DAV_EXT_ESCAPE 0x02 |
| |
| static void |
| ngx_http_dav_ext_output(ngx_http_request_t *r, ngx_chain_t **ll, |
| ngx_int_t flags, u_char *data, ngx_uint_t len) |
| { |
| ngx_chain_t *cl; |
| ngx_buf_t *b; |
| |
| if (!len) { |
| return; |
| } |
| |
| if (flags & NGX_HTTP_DAV_EXT_ESCAPE) { |
| |
| b = ngx_create_temp_buf(r->pool, len + ngx_escape_html(NULL, data, len)); |
| b->last = (u_char*)ngx_escape_html(b->pos, data, len); |
| |
| } else if (flags & NGX_HTTP_DAV_EXT_COPY) { |
| |
| b = ngx_create_temp_buf(r->pool, len); |
| b->last = ngx_cpymem(b->pos, data, len); |
| |
| } else { |
| |
| b = ngx_calloc_buf(r->pool); |
| b->memory = 1; |
| b->pos = data; |
| b->start = data; |
| b->last = b->pos + len; |
| b->end = b->last; |
| } |
| |
| cl = ngx_alloc_chain_link(r->pool); |
| cl->buf = b; |
| cl->next = NULL; |
| |
| if (*ll != NULL) { |
| cl->next = (*ll)->next; |
| (*ll)->next = cl; |
| *ll = cl; |
| } else { |
| *ll = cl; |
| cl->next = cl; |
| } |
| } |
| |
| static void |
| ngx_http_dav_ext_flush(ngx_http_request_t *r, ngx_chain_t **ll) |
| { |
| ngx_chain_t *cl; |
| |
| cl = (*ll)->next; |
| (*ll)->next = NULL; |
| ngx_http_output_filter(r, cl); |
| *ll = NULL; |
| } |
| |
| /* output shortcuts |
| |
| NB: these shortcuts assume 2 variables exist in current context: |
| r - request ptr |
| ll - chain ptr ptr |
| |
| output chains are buffered in circular list & flushed on demand |
| */ |
| |
| /* output buffer copy */ |
| #define NGX_HTTP_DAV_EXT_OUTCB(data, len) \ |
| ngx_http_dav_ext_output(r, ll, NGX_HTTP_DAV_EXT_COPY, (data), (len)) |
| |
| /* output string (no copy) */ |
| #define NGX_HTTP_DAV_EXT_OUTS(s) \ |
| ngx_http_dav_ext_output(r, ll, 0, (s)->data, (s)->len) |
| |
| /* output escaped string */ |
| #define NGX_HTTP_DAV_EXT_OUTES(s) \ |
| ngx_http_dav_ext_output(r, ll, NGX_HTTP_DAV_EXT_ESCAPE, (s)->data, (s)->len) |
| |
| /* output literal */ |
| #define NGX_HTTP_DAV_EXT_OUTL(s) \ |
| ngx_http_dav_ext_output(r, ll, 0, (u_char*)(s), sizeof(s) - 1) |
| |
| static ngx_int_t |
| ngx_http_dav_ext_send_propfind_atts(ngx_http_request_t *r, |
| char *path, ngx_str_t *uri, ngx_chain_t **ll, ngx_uint_t props) |
| { |
| struct stat st; |
| struct tm stm; |
| u_char buf[256]; |
| ngx_str_t name; |
| |
| if (stat(path, &st)) { |
| |
| ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, |
| "dav_ext stat failed on '%s'", path); |
| |
| return NGX_HTTP_NOT_FOUND; |
| } |
| |
| if (props & NGX_HTTP_DAV_EXT_PROP_creationdate) { |
| |
| /* output file ctime (attr change time) as creation time */ |
| if (gmtime_r(&st.st_ctime, &stm) == NULL) |
| return NGX_ERROR; |
| |
| /* ISO 8601 time format |
| 2012-02-20T16:15:00Z */ |
| NGX_HTTP_DAV_EXT_OUTCB(buf, strftime((char*)buf, sizeof(buf), |
| "<D:creationdate>" |
| "%Y-%m-%dT%TZ" |
| "</D:creationdate>\n", |
| |
| &stm)); |
| } |
| |
| if (props & NGX_HTTP_DAV_EXT_PROP_displayname) { |
| NGX_HTTP_DAV_EXT_OUTL( |
| "<D:displayname>" |
| ); |
| |
| if (uri->len) { |
| |
| for(name.data = uri->data + uri->len; |
| name.data >= uri->data + 1 && name.data[-1] != '/'; |
| --name.data); |
| |
| name.len = uri->data + uri->len - name.data; |
| |
| NGX_HTTP_DAV_EXT_OUTES(&name); |
| } |
| |
| NGX_HTTP_DAV_EXT_OUTL( |
| "</D:displayname>\n" |
| ); |
| } |
| |
| if (props & NGX_HTTP_DAV_EXT_PROP_getcontentlanguage) { |
| NGX_HTTP_DAV_EXT_OUTL( |
| "<D:getcontentlanguage/>\n" |
| ); |
| } |
| |
| if (props & NGX_HTTP_DAV_EXT_PROP_getcontentlength) { |
| NGX_HTTP_DAV_EXT_OUTCB(buf, ngx_snprintf(buf, sizeof(buf), |
| "<D:getcontentlength>" |
| "%O" |
| "</D:getcontentlength>\n", |
| |
| st.st_size) - buf); |
| } |
| |
| if (props & NGX_HTTP_DAV_EXT_PROP_getcontenttype) { |
| NGX_HTTP_DAV_EXT_OUTL( |
| "<D:getcontenttype/>\n" |
| ); |
| } |
| |
| if (props & NGX_HTTP_DAV_EXT_PROP_getetag) { |
| NGX_HTTP_DAV_EXT_OUTL( |
| "<D:getetag/>\n" |
| ); |
| } |
| |
| if (props & NGX_HTTP_DAV_EXT_PROP_getlastmodified) { |
| |
| if (gmtime_r(&st.st_mtime, &stm) == NULL) |
| return NGX_ERROR; |
| |
| /* RFC 2822 time format */ |
| NGX_HTTP_DAV_EXT_OUTCB(buf, strftime((char*)buf, sizeof(buf), |
| "<D:getlastmodified>" |
| "%a, %d %b %Y %T GMT" |
| "</D:getlastmodified>\n", |
| |
| &stm)); |
| } |
| |
| if (props & NGX_HTTP_DAV_EXT_PROP_lockdiscovery) { |
| NGX_HTTP_DAV_EXT_OUTL( |
| "<D:lockdiscovery/>\n" |
| ); |
| } |
| |
| if (props & NGX_HTTP_DAV_EXT_PROP_resourcetype) { |
| if (S_ISDIR(st.st_mode)) { |
| NGX_HTTP_DAV_EXT_OUTL( |
| "<D:resourcetype>" |
| "<D:collection/>" |
| "</D:resourcetype>\n" |
| ); |
| } else { |
| NGX_HTTP_DAV_EXT_OUTL( |
| "<D:resourcetype/>\n" |
| ); |
| } |
| } |
| |
| if (props & NGX_HTTP_DAV_EXT_PROP_source) { |
| NGX_HTTP_DAV_EXT_OUTL( |
| "<D:source/>\n" |
| ); |
| } |
| |
| if (props & NGX_HTTP_DAV_EXT_PROP_supportedlock) { |
| NGX_HTTP_DAV_EXT_OUTL( |
| "<D:supportedlock/>\n" |
| ); |
| } |
| |
| return NGX_OK; |
| } |
| |
| static ngx_int_t |
| ngx_http_dav_ext_send_propfind_item(ngx_http_request_t *r, |
| char *path, ngx_str_t *uri) |
| { |
| ngx_http_dav_ext_ctx_t *ctx; |
| ngx_chain_t *l = NULL, **ll = &l; |
| u_char vbuf[8]; |
| ngx_str_t status_line = ngx_string("200 OK"); |
| |
| ctx = ngx_http_get_module_ctx(r, ngx_http_dav_ext_module); |
| |
| NGX_HTTP_DAV_EXT_OUTL( |
| "<D:response>\n" |
| "<D:href>" |
| ); |
| |
| NGX_HTTP_DAV_EXT_OUTES(uri); |
| |
| NGX_HTTP_DAV_EXT_OUTL( |
| "</D:href>\n" |
| "<D:propstat>\n" |
| "<D:prop>\n" |
| ); |
| |
| if (ctx->propfind == NGX_HTTP_DAV_EXT_PROPFIND_NAMES) { |
| |
| NGX_HTTP_DAV_EXT_OUTL( |
| "<D:creationdate/>\n" |
| "<D:displayname/>\n" |
| "<D:getcontentlanguage/>\n" |
| "<D:getcontentlength/>\n" |
| "<D:getcontenttype/>\n" |
| "<D:getetag/>\n" |
| "<D:getlastmodified/>\n" |
| "<D:lockdiscovery/>\n" |
| "<D:resourcetype/>\n" |
| "<D:source/>\n" |
| "<D:supportedlock/>\n" |
| ); |
| |
| } else { |
| |
| switch (ngx_http_dav_ext_send_propfind_atts(r, path, uri, ll, |
| ctx->propfind == NGX_HTTP_DAV_EXT_PROPFIND_SELECTED ? |
| ctx->props : (ngx_uint_t)-1)) |
| { |
| case NGX_HTTP_NOT_FOUND: |
| ngx_str_set(&status_line, "404 Not Found"); |
| break; |
| |
| case NGX_OK: |
| case NGX_HTTP_OK: |
| break; |
| |
| default: |
| ngx_str_set(&status_line, "500 Internal Server Error"); |
| } |
| } |
| |
| NGX_HTTP_DAV_EXT_OUTL( |
| "</D:prop>\n" |
| "<D:status>HTTP/" |
| ); |
| |
| NGX_HTTP_DAV_EXT_OUTCB(vbuf, ngx_snprintf(vbuf, sizeof(vbuf), "%d.%d ", |
| r->http_major, r->http_minor) - vbuf); |
| |
| NGX_HTTP_DAV_EXT_OUTS(&status_line); |
| |
| NGX_HTTP_DAV_EXT_OUTL( |
| "</D:status>\n" |
| "</D:propstat>\n" |
| "</D:response>\n" |
| |
| ); |
| |
| ngx_http_dav_ext_flush(r, ll); |
| |
| return NGX_OK; |
| } |
| |
| /* path returned by this function is terminated |
| with a hidden (out-of-len) null */ |
| static void ngx_http_dav_ext_make_child(ngx_pool_t *pool, ngx_str_t *parent, |
| u_char *child, size_t chlen, ngx_str_t *path) |
| { |
| u_char *s; |
| |
| path->data = ngx_palloc(pool, parent->len + 2 + chlen); |
| s = path->data; |
| s = ngx_cpymem(s, parent->data, parent->len); |
| if (parent->len > 0 && s[-1] != '/') |
| *s++ = '/'; |
| s = ngx_cpymem(s, child, chlen); |
| path->len = s - path->data; |
| *s = 0; |
| } |
| |
| #define DAV_EXT_INFINITY (-1) |
| |
| static ngx_int_t |
| ngx_http_dav_ext_send_propfind(ngx_http_request_t *r) |
| { |
| size_t root; |
| ngx_str_t path, spath, ruri, suri; |
| ngx_chain_t *l = NULL, **ll = &l; |
| DIR *dir; |
| int depth; |
| struct dirent *de; |
| size_t len, uc_len; |
| ngx_http_variable_value_t vv; |
| ngx_str_t depth_name = ngx_string("depth"); |
| u_char *p, *uc; |
| |
| if (ngx_http_variable_unknown_header(&vv, &depth_name, |
| &r->headers_in.headers.part, 0) == NGX_OK |
| && vv.valid) |
| { |
| if (vv.len == sizeof("infinity") -1 |
| && !ngx_strncasecmp(vv.data, (u_char*)"infinity", vv.len)) |
| { |
| depth = DAV_EXT_INFINITY; |
| } else { |
| depth = ngx_atoi(vv.data, vv.len); |
| } |
| |
| } else { |
| depth = DAV_EXT_INFINITY; |
| } |
| |
| p = ngx_http_map_uri_to_path(r, &path, &root, 0); |
| |
| if (p == NULL || !path.len) { |
| |
| ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, |
| "dav_ext error mapping uri to path"); |
| |
| return NGX_ERROR; |
| } |
| |
| path.len = p - path.data; |
| *p = 0; |
| |
| ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
| "http propfind path: \"%V\"", &path); |
| |
| NGX_HTTP_DAV_EXT_OUTL( |
| "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" |
| "<D:multistatus xmlns:D=\"DAV:\">\n" |
| ); |
| |
| ngx_http_dav_ext_flush(r, ll); |
| /* |
| ruri.data = ngx_palloc(r->pool, r->uri.len + 2 * ngx_escape_uri(NULL, |
| r->uri.data, r->uri.len, NGX_ESCAPE_URI)); |
| if (ruri.data == NULL) { |
| return NGX_ERROR; |
| } |
| |
| ruri.len = (u_char *) ngx_escape_uri(ruri.data, r->uri.data, r->uri.len, |
| NGX_ESCAPE_URI) - ruri.data; |
| */ |
| ruri = r->unparsed_uri; |
| |
| ngx_http_dav_ext_send_propfind_item(r, (char*)path.data, &ruri); |
| |
| if (depth) { |
| |
| /* treat infinite depth as 1 for performance reasons */ |
| |
| if ((dir = opendir((char*)path.data))) { |
| |
| while((de = readdir(dir))) { |
| |
| if (!strcmp(de->d_name, ".") |
| || !strcmp(de->d_name, "..")) |
| { |
| continue; |
| } |
| |
| len = strlen(de->d_name); |
| |
| ngx_http_dav_ext_make_child(r->pool, &path, |
| (u_char*)de->d_name, len, &spath); |
| |
| /* escape uri component */ |
| |
| uc = ngx_palloc(r->pool, len + 2 * ngx_escape_uri(NULL, |
| (u_char *) de->d_name, len, NGX_ESCAPE_URI_COMPONENT)); |
| if (uc == NULL) { |
| return NGX_ERROR; |
| } |
| |
| uc_len = (u_char*)ngx_escape_uri(uc, (u_char *) de->d_name, len, |
| NGX_ESCAPE_URI_COMPONENT) - uc; |
| |
| ngx_http_dav_ext_make_child(r->pool, &ruri, uc, uc_len, &suri); |
| |
| ngx_http_dav_ext_send_propfind_item(r, (char*)spath.data, &suri); |
| |
| } |
| |
| closedir(dir); |
| } |
| |
| } |
| |
| NGX_HTTP_DAV_EXT_OUTL( |
| "</D:multistatus>\n" |
| ); |
| |
| if (*ll && (*ll)->buf) { |
| (*ll)->buf->last_buf = 1; |
| } |
| |
| ngx_http_dav_ext_flush(r, ll); |
| |
| return NGX_OK; |
| } |
| |
| static void |
| ngx_http_dav_ext_propfind_handler(ngx_http_request_t *r) |
| { |
| ngx_chain_t *c; |
| ngx_buf_t *b; |
| XML_Parser parser; |
| ngx_uint_t status; |
| ngx_http_dav_ext_ctx_t *ctx; |
| |
| ctx = ngx_http_get_module_ctx(r, ngx_http_dav_ext_module); |
| |
| if (ctx == NULL) { |
| ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_dav_ext_ctx_t)); |
| ngx_http_set_ctx(r, ctx, ngx_http_dav_ext_module); |
| } |
| |
| c = r->request_body->bufs; |
| |
| status = NGX_OK; |
| |
| parser = XML_ParserCreate(NULL); |
| |
| XML_SetUserData(parser, ctx); |
| |
| XML_SetElementHandler(parser, |
| ngx_http_dav_ext_start_xml_elt, |
| ngx_http_dav_ext_end_xml_elt); |
| |
| for(; c != NULL && c->buf != NULL && !c->buf->last_buf; c = c->next) { |
| |
| b = c ->buf; |
| |
| if (!XML_Parse(parser, (const char*)b->pos, b->last - b->pos, b->last_buf)) { |
| |
| ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, |
| "dav_ext propfind XML error"); |
| |
| status = NGX_ERROR; |
| |
| break; |
| } |
| |
| } |
| |
| XML_ParserFree(parser); |
| |
| if (status == NGX_OK) { |
| |
| r->headers_out.status = 207; |
| |
| ngx_str_set(&r->headers_out.status_line, "207 Multi-Status"); |
| |
| ngx_http_send_header(r); |
| |
| ngx_http_finalize_request(r, ngx_http_dav_ext_send_propfind(r)); |
| |
| } else { |
| |
| r->headers_out.status = NGX_HTTP_INTERNAL_SERVER_ERROR; |
| |
| r->header_only = 1; |
| |
| r->headers_out.content_length_n = 0; |
| |
| ngx_http_finalize_request(r, ngx_http_send_header(r)); |
| |
| } |
| |
| } |
| |
| static ngx_int_t |
| ngx_http_dav_ext_handler(ngx_http_request_t *r) |
| { |
| ngx_int_t rc; |
| ngx_table_elt_t *h; |
| ngx_http_dav_ext_loc_conf_t *delcf; |
| |
| delcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_ext_module); |
| |
| if (!(r->method & delcf->methods)) { |
| return NGX_DECLINED; |
| } |
| |
| switch (r->method) { |
| |
| case NGX_HTTP_PROPFIND: |
| |
| ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
| "dav_ext propfind"); |
| |
| rc = ngx_http_read_client_request_body(r, |
| ngx_http_dav_ext_propfind_handler); |
| |
| if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { |
| return rc; |
| } |
| |
| return NGX_DONE; |
| |
| case NGX_HTTP_OPTIONS: |
| |
| ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
| "dav_ext options"); |
| |
| h = ngx_list_push(&r->headers_out.headers); |
| |
| if (h == NULL) { |
| return NGX_HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| ngx_str_set(&h->key, "DAV"); |
| ngx_str_set(&h->value, "1"); |
| h->hash = 1; |
| |
| h = ngx_list_push(&r->headers_out.headers); |
| |
| if (h == NULL) { |
| return NGX_HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| /* FIXME: it looks so ugly because I cannot access nginx dav module */ |
| ngx_str_set(&h->key, "Allow"); |
| ngx_str_set(&h->value, "GET,HEAD,PUT,DELETE,MKCOL,COPY,MOVE,PROPFIND,OPTIONS"); |
| h->hash = 1; |
| |
| r->headers_out.status = NGX_HTTP_OK; |
| r->header_only = 1; |
| r->headers_out.content_length_n = 0; |
| |
| ngx_http_send_header(r); |
| |
| return NGX_OK; |
| |
| } |
| |
| return NGX_DECLINED; |
| } |
| |
| static void * |
| ngx_http_dav_ext_create_loc_conf(ngx_conf_t *cf) |
| { |
| ngx_http_dav_ext_loc_conf_t *conf; |
| |
| conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dav_ext_loc_conf_t)); |
| if (conf == NULL) { |
| return NULL; |
| } |
| |
| return conf; |
| } |
| |
| static char * |
| ngx_http_dav_ext_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) |
| { |
| ngx_http_dav_ext_loc_conf_t *prev = parent; |
| ngx_http_dav_ext_loc_conf_t *conf = child; |
| |
| ngx_conf_merge_bitmask_value(conf->methods, prev->methods, |
| (NGX_CONF_BITMASK_SET|NGX_HTTP_DAV_EXT_OFF)); |
| |
| return NGX_CONF_OK; |
| } |
| |
| static ngx_int_t |
| ngx_http_dav_ext_init(ngx_conf_t *cf) |
| { |
| ngx_http_handler_pt *h; |
| ngx_http_core_main_conf_t *cmcf; |
| |
| cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); |
| |
| h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); |
| |
| if (h == NULL) { |
| return NGX_ERROR; |
| } |
| |
| *h = ngx_http_dav_ext_handler; |
| |
| return NGX_OK; |
| } |
| |