2019-03-26 02:13:24 +00:00
|
|
|
/*
|
2020-05-20 05:06:13 +00:00
|
|
|
* mod_webdav - WEBDAV support for lighttpd
|
|
|
|
*
|
|
|
|
* Fully-rewritten from original
|
|
|
|
* Copyright(c) 2019 Glenn Strauss gstrauss()gluelogic.com All rights reserved
|
|
|
|
* License: BSD 3-clause (same as lighttpd)
|
2019-03-26 02:13:24 +00:00
|
|
|
*/
|
2016-03-19 15:14:35 +00:00
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
/*
|
|
|
|
* Note: This plugin is a basic implementation of [RFC4918] WebDAV
|
|
|
|
*
|
|
|
|
* Version Control System (VCS) backing WebDAV is recommended instead
|
|
|
|
* and Subversion (svn) is one such VCS supporting WebDAV.
|
|
|
|
*
|
|
|
|
* status: *** EXPERIMENTAL *** (and likely insecure encoding/decoding)
|
|
|
|
*
|
|
|
|
* future:
|
|
|
|
*
|
|
|
|
* TODO: moving props should delete any existing props instead of
|
|
|
|
* preserving any that are not overwritten with UPDATE OR REPLACE
|
|
|
|
* (and, if merging directories, be careful when doing so)
|
|
|
|
* TODO: add proper support for locks with "shared" lockscope
|
|
|
|
* (instead of treating match of any shared lock as sufficient,
|
|
|
|
* even when there are different lockroots)
|
|
|
|
* TODO: does libxml2 xml-decode (html-decode),
|
|
|
|
* or must I do so to normalize input?
|
|
|
|
* TODO: add strict enforcement of property names to be valid XML tags
|
|
|
|
* (or encode as such before putting into database)
|
|
|
|
* & " < >
|
|
|
|
* TODO: should we be using xmlNodeListGetString() or xmlBufNodeDump()
|
|
|
|
* and how does it handle encoding and entity-inlining of external refs?
|
|
|
|
* Must xml decode/encode (normalize) before storing user data in database
|
|
|
|
* if going to add that data verbatim to xml doc returned in queries
|
|
|
|
* TODO: when walking xml nodes, should add checks for "DAV:" namespace
|
|
|
|
* TODO: consider where it might be useful/informative to check
|
|
|
|
* SQLITE_OK != sqlite3_reset() or SQLITE_OK != sqlite3_bind_...() or ...
|
|
|
|
* (in some cases no rows returned is ok, while in other cases it is not)
|
2020-01-13 02:51:12 +00:00
|
|
|
* TODO: Unsupported: !r->conf.follow_symlink is not currently honored;
|
|
|
|
* symlinks are followed. Supporting !r->conf.follow_symlinks would
|
2019-03-26 02:13:24 +00:00
|
|
|
* require operating system support for POSIX.1-2008 *at() commands,
|
|
|
|
* and reworking the mod_webdav code to exclusively operate with *at()
|
|
|
|
* commands, for example, replacing unlink() with unlinkat().
|
|
|
|
*
|
|
|
|
* RFE: add config option whether or not to include locktoken and ownerinfo
|
|
|
|
* in PROPFIND lockdiscovery
|
|
|
|
*
|
|
|
|
* deficiencies
|
|
|
|
* - incomplete "shared" lock support
|
|
|
|
* - review code for proper decoding/encoding of elements from/to XML and db
|
|
|
|
* - preserve XML info in scope on dead properties, e.g. xml:lang
|
|
|
|
*
|
|
|
|
* [RFC4918] 4.3 Property Values
|
|
|
|
* Servers MUST preserve the following XML Information Items (using the
|
|
|
|
* terminology from [REC-XML-INFOSET]) in storage and transmission of dead
|
|
|
|
* properties: ...
|
|
|
|
* [RFC4918] 14.26 set XML Element
|
|
|
|
* The 'set' element MUST contain only a 'prop' element. The elements
|
|
|
|
* contained by the 'prop' element inside the 'set' element MUST specify the
|
|
|
|
* name and value of properties that are set on the resource identified by
|
|
|
|
* Request-URI. If a property already exists, then its value is replaced.
|
|
|
|
* Language tagging information appearing in the scope of the 'prop' element
|
|
|
|
* (in the "xml:lang" attribute, if present) MUST be persistently stored along
|
|
|
|
* with the property, and MUST be subsequently retrievable using PROPFIND.
|
|
|
|
* [RFC4918] F.2 Changes for Server Implementations
|
|
|
|
* Strengthened server requirements for storage of property values, in
|
|
|
|
* particular persistence of language information (xml:lang), whitespace, and
|
|
|
|
* XML namespace information (see Section 4.3).
|
|
|
|
*
|
|
|
|
* resource usage containment
|
|
|
|
* - filesystem I/O operations might take a non-trivial amount of time,
|
|
|
|
* blocking the server from responding to other requests during this time.
|
|
|
|
* Potential solution: have a thread dedicated to handling webdav requests
|
|
|
|
* and serialize such requests in each thread dedicated to handling webdav.
|
|
|
|
* (Limit number of such dedicated threads.) Remove write interest from
|
|
|
|
* connection during this period so that server will not trigger any timeout
|
|
|
|
* on the connection.
|
|
|
|
* - recursive directory operations are depth-first and may consume a large
|
|
|
|
* number of file descriptors if the directory hierarchy is deep.
|
|
|
|
* Potential solution: serialize webdav requests into dedicated thread (above)
|
|
|
|
* Potential solution: perform breadth-first directory traversal and pwrite()
|
|
|
|
* directory paths into a temporary file. After reading each directory,
|
|
|
|
* close() the dirhandle and pread() next directory from temporary file.
|
|
|
|
* (Keeping list of directories in memory might result in large memory usage)
|
|
|
|
* - flush response to client (or to intermediate temporary file) at regular
|
|
|
|
* intervals or triggers to avoid response consume large amount of memory
|
|
|
|
* during operations on large collection hierarchies (with lots of nested
|
|
|
|
* directories)
|
|
|
|
*
|
|
|
|
* beware of security concerns involved in enabling WebDAV
|
|
|
|
* on publicly accessible servers
|
|
|
|
* - (general) [RFC4918] 20 Security Considersations
|
|
|
|
* - (specifically) [RFC4918] 20.6 Implications of XML Entities
|
|
|
|
* - TODO review usage of xml libs for security, resource usage, containment
|
|
|
|
* libxml2 vs expat vs ...
|
|
|
|
* http://xmlbench.sourceforge.net/
|
|
|
|
* http://stackoverflow.com/questions/399704/xml-parser-for-c
|
|
|
|
* http://tibleiz.net/asm-xml/index.html
|
|
|
|
* http://dev.yorhel.nl/yxml
|
|
|
|
* - how might mod_webdav be affected by mod_openssl setting REMOTE_USER?
|
|
|
|
* - when encoding href in responses, also ensure proper XML encoding
|
|
|
|
* (do we need to ENCODING_REL_URI and then ENCODING_MINIMAL_XML?)
|
|
|
|
* - TODO: any (non-numeric) data that goes into database should be encoded
|
|
|
|
* before being sent back to user, not just href. Perhaps anything that
|
|
|
|
* is not an href should be stored in database in XML-encoded form.
|
|
|
|
*
|
|
|
|
* consider implementing a set of (reasonable) limits on such things as
|
|
|
|
* - max number of collections
|
|
|
|
* - max number of objects in a collection
|
|
|
|
* - max number of properties per object
|
|
|
|
* - max length of property name
|
|
|
|
* - max length of property value
|
|
|
|
* - max length of locktoken, lockroot, ownerinfo
|
|
|
|
* - max number of locks held by a client, or by an owner
|
|
|
|
* - max number of locks on a resource (shared locks)
|
|
|
|
* - ...
|
|
|
|
*
|
|
|
|
* robustness
|
|
|
|
* - should check return value from sqlite3_reset(stmt) for REPLACE, UPDATE,
|
|
|
|
* DELETE statements (which is when commit occurs and locks are obtained/fail)
|
|
|
|
* - handle SQLITE_BUSY (e.g. other processes modifying db and hold locks)
|
|
|
|
* https://www.sqlite.org/lang_transaction.html
|
|
|
|
* https://www.sqlite.org/rescode.html#busy
|
|
|
|
* https://www.sqlite.org/c3ref/busy_handler.html
|
|
|
|
* https://www.sqlite.org/c3ref/busy_timeout.html
|
|
|
|
* - periodically execute query to delete expired locks
|
|
|
|
* (MOD_WEBDAV_SQLITE_LOCKS_DELETE_EXPIRED)
|
|
|
|
* (should defend against removing locks protecting long-running operations
|
|
|
|
* that are in progress on the server)
|
|
|
|
* - having all requests go through database, including GET and HEAD would allow
|
|
|
|
* for better transactional semantics, instead of the current race conditions
|
|
|
|
* inherent in multiple (and many) filesystem operations. All queries would
|
|
|
|
* go through database, which would map to objects on disk, and copy and move
|
|
|
|
* would simply be database entries to objects with reference counts and
|
|
|
|
* copy-on-write semantics (instead of potential hard-links on disk).
|
|
|
|
* lstat() information could also be stored in database. Right now, if a file
|
|
|
|
* is copied or moved or deleted, the status of the property update in the db
|
|
|
|
* is discarded, whether it succeeds or not, since file operation succeeded.
|
|
|
|
* (Then again, it might also be okay if props do not exist on a given file.)
|
|
|
|
* On the other hand, if everything went through database, then etag could be
|
|
|
|
* stored in database and could be updated upon PUT (or MOVE/COPY/DELETE).
|
|
|
|
* There would also need to be a way to trigger a rescan of filesystem to
|
|
|
|
* bring the database into sync with any out-of-band changes.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* notes:
|
|
|
|
*
|
|
|
|
* - lstat() used instead of stat_cache_*() since the stat_cache might have
|
|
|
|
* expired data, as stat_cache is not invalidated by outside modifications,
|
|
|
|
* such as WebDAV PUT method (unless FAM is used)
|
|
|
|
*
|
|
|
|
* - SQLite database can be run in WAL mode (https://sqlite.org/wal.html)
|
|
|
|
* though mod_webdav does not provide a mechanism to configure WAL.
|
|
|
|
* Instead, once lighttpd starts up mod_webdav and creates the database,
|
|
|
|
* set WAL mode on the database from the command and then restart lighttpd.
|
|
|
|
*/
|
2009-10-11 14:31:42 +00:00
|
|
|
|
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
/* linkat() fstatat() unlinkat() fdopendir() NAME_MAX */
|
|
|
|
#if !defined(_XOPEN_SOURCE) || _XOPEN_SOURCE-0 < 700
|
|
|
|
#undef _XOPEN_SOURCE
|
|
|
|
#define _XOPEN_SOURCE 700
|
2020-10-24 01:24:26 +00:00
|
|
|
/* NetBSD dirent.h improperly hides fdopendir() (POSIX.1-2008) declaration
|
|
|
|
* which should be visible with _XOPEN_SOURCE 700 or _POSIX_C_SOURCE 200809L */
|
|
|
|
#ifdef __NetBSD__
|
|
|
|
#define _NETBSD_SOURCE
|
|
|
|
#endif
|
2019-03-26 02:13:24 +00:00
|
|
|
#endif
|
|
|
|
/* DT_UNKNOWN DTTOIF() */
|
|
|
|
#ifndef _GNU_SOURCE
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#endif
|
2009-10-11 14:31:42 +00:00
|
|
|
|
2019-11-09 05:27:58 +00:00
|
|
|
#include "first.h" /* first */
|
2009-10-11 14:31:42 +00:00
|
|
|
#include "sys-mmap.h"
|
2005-08-19 00:05:52 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
2020-11-22 07:41:11 +00:00
|
|
|
#include "sys-time.h"
|
2019-03-26 02:13:24 +00:00
|
|
|
#include <dirent.h>
|
2005-08-19 13:42:01 +00:00
|
|
|
#include <errno.h>
|
2005-08-19 14:40:19 +00:00
|
|
|
#include <fcntl.h>
|
2019-03-26 02:13:24 +00:00
|
|
|
#include <stdio.h> /* rename() */
|
|
|
|
#include <stdlib.h> /* strtol() */
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h> /* getpid() linkat() rmdir() unlinkat() */
|
2005-08-20 19:10:44 +00:00
|
|
|
|
2020-12-01 21:23:49 +00:00
|
|
|
#ifdef AT_FDCWD
|
2020-11-30 06:38:08 +00:00
|
|
|
#ifndef _ATFILE_SOURCE
|
|
|
|
#define _ATFILE_SOURCE
|
|
|
|
#endif
|
|
|
|
#endif
|
2020-12-01 21:23:49 +00:00
|
|
|
|
|
|
|
#ifndef AT_SYMLINK_NOFOLLOW
|
|
|
|
#define AT_SYMLINK_NOFOLLOW 0
|
2020-11-30 06:38:08 +00:00
|
|
|
#endif
|
|
|
|
|
2020-11-25 15:06:10 +00:00
|
|
|
/* Note: filesystem access race conditions exist without _ATFILE_SOURCE */
|
|
|
|
#ifndef _ATFILE_SOURCE
|
|
|
|
/*(trigger linkat() fail to fallback logic in mod_webdav.c)*/
|
|
|
|
#define linkat(odfd,opath,ndfd,npath,flags) -1
|
|
|
|
#endif
|
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
#ifndef _D_EXACT_NAMLEN
|
|
|
|
#ifdef _DIRENT_HAVE_D_NAMLEN
|
|
|
|
#define _D_EXACT_NAMLEN(d) ((d)->d_namlen)
|
|
|
|
#else
|
|
|
|
#define _D_EXACT_NAMLEN(d) (strlen ((d)->d_name))
|
|
|
|
#endif
|
|
|
|
#endif
|
2006-09-01 10:21:53 +00:00
|
|
|
|
2020-03-14 05:58:29 +00:00
|
|
|
#ifndef PATH_MAX
|
|
|
|
#define PATH_MAX 4096
|
|
|
|
#endif
|
|
|
|
|
2005-08-21 10:06:23 +00:00
|
|
|
#if defined(HAVE_LIBXML_H) && defined(HAVE_SQLITE3_H)
|
2019-03-26 02:13:24 +00:00
|
|
|
|
2005-08-21 10:06:23 +00:00
|
|
|
#define USE_PROPPATCH
|
2019-03-26 02:13:24 +00:00
|
|
|
/* minor: libxml2 includes stdlib.h in headers, too */
|
2005-08-21 10:06:23 +00:00
|
|
|
#include <libxml/tree.h>
|
|
|
|
#include <libxml/parser.h>
|
|
|
|
#include <sqlite3.h>
|
|
|
|
|
2019-02-08 12:10:58 +00:00
|
|
|
#if defined(HAVE_LIBUUID) && defined(HAVE_UUID_UUID_H)
|
2006-09-01 10:17:28 +00:00
|
|
|
#define USE_LOCKS
|
|
|
|
#include <uuid/uuid.h>
|
|
|
|
#endif
|
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
#endif /* defined(HAVE_LIBXML_H) && defined(HAVE_SQLITE3_H) */
|
2005-08-19 00:05:52 +00:00
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
#include "base.h"
|
|
|
|
#include "buffer.h"
|
|
|
|
#include "chunk.h"
|
|
|
|
#include "fdevent.h"
|
2021-04-01 15:25:42 +00:00
|
|
|
#include "http_chunk.h"
|
2020-11-22 07:41:11 +00:00
|
|
|
#include "http_date.h"
|
2020-12-25 08:56:39 +00:00
|
|
|
#include "http_etag.h"
|
2019-03-26 02:13:24 +00:00
|
|
|
#include "http_header.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "request.h"
|
|
|
|
#include "response.h" /* http_response_redirect_to_directory() */
|
|
|
|
#include "stat_cache.h" /* stat_cache_mimetype_by_ext() */
|
2005-08-19 00:05:52 +00:00
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
#include "plugin.h"
|
2005-08-19 00:05:52 +00:00
|
|
|
|
2020-08-04 02:54:17 +00:00
|
|
|
#if (defined(__linux__) || defined(__CYGWIN__)) && defined(O_TMPFILE)
|
|
|
|
static int has_proc_self_fd;
|
|
|
|
#endif
|
|
|
|
|
2020-01-13 02:51:12 +00:00
|
|
|
#define http_status_get(r) ((r)->http_status)
|
|
|
|
#define http_status_set_fin(r, code) ((r)->resp_body_finished = 1,\
|
|
|
|
(r)->handler_module = NULL, \
|
|
|
|
(r)->http_status = (code))
|
|
|
|
#define http_status_set(r, code) ((r)->http_status = (code))
|
|
|
|
#define http_status_unset(r) ((r)->http_status = 0)
|
|
|
|
#define http_status_is_set(r) (0 != (r)->http_status)
|
2019-03-26 02:13:24 +00:00
|
|
|
__attribute_cold__
|
|
|
|
__attribute_noinline__
|
2020-01-13 02:51:12 +00:00
|
|
|
static int http_status_set_error (request_st * const r, int status) {
|
|
|
|
return http_status_set_fin(r, status);
|
2019-03-26 02:13:24 +00:00
|
|
|
}
|
2005-08-20 19:10:44 +00:00
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
typedef physical physical_st;
|
|
|
|
|
|
|
|
INIT_FUNC(mod_webdav_init);
|
|
|
|
FREE_FUNC(mod_webdav_free);
|
|
|
|
SETDEFAULTS_FUNC(mod_webdav_set_defaults);
|
|
|
|
SERVER_FUNC(mod_webdav_worker_init);
|
|
|
|
URIHANDLER_FUNC(mod_webdav_uri_handler);
|
|
|
|
PHYSICALPATH_FUNC(mod_webdav_physical_handler);
|
|
|
|
SUBREQUEST_FUNC(mod_webdav_subrequest_handler);
|
2020-01-13 02:51:12 +00:00
|
|
|
REQUEST_FUNC(mod_webdav_handle_reset);
|
2019-03-26 02:13:24 +00:00
|
|
|
|
|
|
|
int mod_webdav_plugin_init(plugin *p);
|
|
|
|
int mod_webdav_plugin_init(plugin *p) {
|
|
|
|
p->version = LIGHTTPD_VERSION_ID;
|
2019-10-19 04:30:54 +00:00
|
|
|
p->name = "webdav";
|
2019-03-26 02:13:24 +00:00
|
|
|
|
|
|
|
p->init = mod_webdav_init;
|
|
|
|
p->cleanup = mod_webdav_free;
|
|
|
|
p->set_defaults = mod_webdav_set_defaults;
|
|
|
|
p->worker_init = mod_webdav_worker_init;
|
|
|
|
p->handle_uri_clean = mod_webdav_uri_handler;
|
|
|
|
p->handle_physical = mod_webdav_physical_handler;
|
|
|
|
p->handle_subrequest = mod_webdav_subrequest_handler;
|
2020-07-25 12:23:58 +00:00
|
|
|
p->handle_request_reset = mod_webdav_handle_reset;
|
2019-03-26 02:13:24 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define WEBDAV_FILE_MODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
|
|
|
|
#define WEBDAV_DIR_MODE S_IRWXU|S_IRWXG|S_IRWXO
|
|
|
|
|
|
|
|
#define WEBDAV_FLAG_LC_NAMES 0x01
|
|
|
|
#define WEBDAV_FLAG_OVERWRITE 0x02
|
|
|
|
#define WEBDAV_FLAG_MOVE_RENAME 0x04
|
|
|
|
#define WEBDAV_FLAG_COPY_LINK 0x08
|
|
|
|
#define WEBDAV_FLAG_MOVE_XDEV 0x10
|
|
|
|
#define WEBDAV_FLAG_COPY_XDEV 0x20
|
|
|
|
|
|
|
|
#define webdav_xmlstrcmp_fixed(s, fixed) \
|
|
|
|
strncmp((const char *)(s), (fixed), sizeof(fixed))
|
|
|
|
|
|
|
|
#include <ctype.h> /* isupper() tolower() */
|
2020-03-23 21:40:46 +00:00
|
|
|
__attribute_noinline__
|
2019-03-26 02:13:24 +00:00
|
|
|
static void
|
2020-03-23 21:40:46 +00:00
|
|
|
webdav_str_len_to_lower (char * const ss, const uint32_t len)
|
2019-03-26 02:13:24 +00:00
|
|
|
{
|
|
|
|
/*(caller must ensure that len not truncated to (int);
|
|
|
|
* for current intended use, NAME_MAX typically <= 255)*/
|
2020-03-23 21:40:46 +00:00
|
|
|
unsigned char * const restrict s = (unsigned char *)ss;
|
2019-03-26 02:13:24 +00:00
|
|
|
for (int i = 0; i < (int)len; ++i) {
|
|
|
|
if (isupper(s[i]))
|
|
|
|
s[i] = tolower(s[i]);
|
|
|
|
}
|
|
|
|
}
|
2005-08-19 00:05:52 +00:00
|
|
|
|
|
|
|
typedef struct {
|
2019-03-26 02:13:24 +00:00
|
|
|
#ifdef USE_PROPPATCH
|
|
|
|
sqlite3 *sqlh;
|
|
|
|
sqlite3_stmt *stmt_props_select_propnames;
|
|
|
|
sqlite3_stmt *stmt_props_select_props;
|
|
|
|
sqlite3_stmt *stmt_props_select_prop;
|
|
|
|
sqlite3_stmt *stmt_props_update_prop;
|
|
|
|
sqlite3_stmt *stmt_props_delete_prop;
|
|
|
|
|
|
|
|
sqlite3_stmt *stmt_props_copy;
|
|
|
|
sqlite3_stmt *stmt_props_move;
|
|
|
|
sqlite3_stmt *stmt_props_move_col;
|
|
|
|
sqlite3_stmt *stmt_props_delete;
|
|
|
|
|
|
|
|
sqlite3_stmt *stmt_locks_acquire;
|
|
|
|
sqlite3_stmt *stmt_locks_refresh;
|
|
|
|
sqlite3_stmt *stmt_locks_release;
|
|
|
|
sqlite3_stmt *stmt_locks_read;
|
|
|
|
sqlite3_stmt *stmt_locks_read_uri;
|
|
|
|
sqlite3_stmt *stmt_locks_read_uri_infinity;
|
|
|
|
sqlite3_stmt *stmt_locks_read_uri_members;
|
|
|
|
sqlite3_stmt *stmt_locks_delete_uri;
|
|
|
|
sqlite3_stmt *stmt_locks_delete_uri_col;
|
|
|
|
#else
|
|
|
|
int dummy;
|
|
|
|
#endif
|
|
|
|
} sql_config;
|
2006-09-01 10:17:28 +00:00
|
|
|
|
2020-06-12 01:55:47 +00:00
|
|
|
enum { /* opts bitflags */
|
|
|
|
MOD_WEBDAV_UNSAFE_PARTIAL_PUT_COMPAT = 0x1
|
2020-06-12 02:12:32 +00:00
|
|
|
,MOD_WEBDAV_UNSAFE_PROPFIND_FOLLOW_SYMLINK = 0x2
|
2020-06-12 02:28:12 +00:00
|
|
|
,MOD_WEBDAV_PROPFIND_DEPTH_INFINITY = 0x4
|
2020-06-12 01:55:47 +00:00
|
|
|
};
|
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
typedef struct {
|
|
|
|
unsigned short enabled;
|
|
|
|
unsigned short is_readonly;
|
|
|
|
unsigned short log_xml;
|
2020-06-12 01:55:47 +00:00
|
|
|
unsigned short opts;
|
2019-03-26 02:13:24 +00:00
|
|
|
|
|
|
|
sql_config *sql;
|
|
|
|
buffer *tmpb;
|
|
|
|
buffer *sqlite_db_name; /* not used after worker init */
|
|
|
|
} plugin_config;
|
2006-09-01 10:17:28 +00:00
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
typedef struct {
|
|
|
|
PLUGIN_DATA;
|
2019-11-09 05:27:58 +00:00
|
|
|
plugin_config defaults;
|
2005-08-19 00:05:52 +00:00
|
|
|
} plugin_data;
|
|
|
|
|
2016-11-28 17:39:37 +00:00
|
|
|
|
2005-08-19 00:05:52 +00:00
|
|
|
INIT_FUNC(mod_webdav_init) {
|
2019-03-26 02:13:24 +00:00
|
|
|
return calloc(1, sizeof(plugin_data));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-11-19 08:39:40 +00:00
|
|
|
FREE_FUNC(mod_webdav_free) {
|
|
|
|
plugin_data * const p = (plugin_data *)p_d;
|
2019-11-09 05:27:58 +00:00
|
|
|
if (NULL == p->cvlist) return;
|
|
|
|
/* (init i to 0 if global context; to 1 to skip empty global context) */
|
|
|
|
for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
|
|
|
|
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
|
|
|
|
for (; -1 != cpv->k_id; ++cpv) {
|
|
|
|
switch (cpv->k_id) {
|
|
|
|
#ifdef USE_PROPPATCH
|
|
|
|
case 0: /* webdav.sqlite-db-name */
|
|
|
|
if (cpv->vtype == T_CONFIG_LOCAL) {
|
|
|
|
sql_config * const sql = cpv->v.v;
|
|
|
|
if (!sql->sqlh) {
|
|
|
|
free(sql);
|
|
|
|
continue;
|
|
|
|
}
|
2019-03-26 02:13:24 +00:00
|
|
|
|
2019-11-09 05:27:58 +00:00
|
|
|
sqlite3_finalize(sql->stmt_props_select_propnames);
|
|
|
|
sqlite3_finalize(sql->stmt_props_select_props);
|
|
|
|
sqlite3_finalize(sql->stmt_props_select_prop);
|
|
|
|
sqlite3_finalize(sql->stmt_props_update_prop);
|
|
|
|
sqlite3_finalize(sql->stmt_props_delete_prop);
|
|
|
|
sqlite3_finalize(sql->stmt_props_copy);
|
|
|
|
sqlite3_finalize(sql->stmt_props_move);
|
|
|
|
sqlite3_finalize(sql->stmt_props_move_col);
|
|
|
|
sqlite3_finalize(sql->stmt_props_delete);
|
|
|
|
|
|
|
|
sqlite3_finalize(sql->stmt_locks_acquire);
|
|
|
|
sqlite3_finalize(sql->stmt_locks_refresh);
|
|
|
|
sqlite3_finalize(sql->stmt_locks_release);
|
|
|
|
sqlite3_finalize(sql->stmt_locks_read);
|
|
|
|
sqlite3_finalize(sql->stmt_locks_read_uri);
|
|
|
|
sqlite3_finalize(sql->stmt_locks_read_uri_infinity);
|
|
|
|
sqlite3_finalize(sql->stmt_locks_read_uri_members);
|
|
|
|
sqlite3_finalize(sql->stmt_locks_delete_uri);
|
|
|
|
sqlite3_finalize(sql->stmt_locks_delete_uri_col);
|
|
|
|
sqlite3_close(sql->sqlh);
|
|
|
|
free(sql);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
break;
|
2019-03-26 02:13:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-11-09 05:27:58 +00:00
|
|
|
}
|
2019-03-26 02:13:24 +00:00
|
|
|
|
|
|
|
|
2019-11-09 05:27:58 +00:00
|
|
|
static void mod_webdav_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
|
|
|
|
switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
|
|
|
|
case 0: /* webdav.sqlite-db-name */
|
|
|
|
if (cpv->vtype == T_CONFIG_LOCAL)
|
|
|
|
pconf->sql = cpv->v.v;
|
|
|
|
break;
|
|
|
|
case 1: /* webdav.activate */
|
|
|
|
pconf->enabled = (unsigned short)cpv->v.u;
|
|
|
|
break;
|
|
|
|
case 2: /* webdav.is-readonly */
|
|
|
|
pconf->is_readonly = (unsigned short)cpv->v.u;
|
|
|
|
break;
|
|
|
|
case 3: /* webdav.log-xml */
|
|
|
|
pconf->log_xml = (unsigned short)cpv->v.u;
|
|
|
|
break;
|
|
|
|
case 4: /* webdav.opts */
|
|
|
|
if (cpv->vtype == T_CONFIG_LOCAL)
|
2020-06-12 01:55:47 +00:00
|
|
|
pconf->opts = (unsigned short)cpv->v.u;
|
2019-11-09 05:27:58 +00:00
|
|
|
break;
|
|
|
|
default:/* should not happen */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void mod_webdav_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
|
|
|
|
do {
|
|
|
|
mod_webdav_merge_config_cpv(pconf, cpv);
|
|
|
|
} while ((++cpv)->k_id != -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-01-13 02:51:12 +00:00
|
|
|
static void mod_webdav_patch_config(request_st * const r, plugin_data * const p, plugin_config * const pconf) {
|
2019-11-09 05:27:58 +00:00
|
|
|
memcpy(pconf, &p->defaults, sizeof(plugin_config));
|
|
|
|
for (int i = 1, used = p->nconfig; i < used; ++i) {
|
2020-01-13 02:51:12 +00:00
|
|
|
if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
|
2019-11-09 05:27:58 +00:00
|
|
|
mod_webdav_merge_config(pconf, p->cvlist + p->cvlist[i].v.u2[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
__attribute_cold__
|
2019-11-09 05:27:58 +00:00
|
|
|
static int mod_webdav_sqlite3_init (const char * restrict s, log_error_st *errh);
|
2006-09-01 10:17:28 +00:00
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
SETDEFAULTS_FUNC(mod_webdav_set_defaults) {
|
2019-11-09 05:27:58 +00:00
|
|
|
static const config_plugin_keys_t cpk[] = {
|
|
|
|
{ CONST_STR_LEN("webdav.sqlite-db-name"),
|
|
|
|
T_CONFIG_STRING,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("webdav.activate"),
|
|
|
|
T_CONFIG_BOOL,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("webdav.is-readonly"),
|
|
|
|
T_CONFIG_BOOL,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("webdav.log-xml"),
|
|
|
|
T_CONFIG_BOOL,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("webdav.opts"),
|
2021-04-25 19:46:59 +00:00
|
|
|
T_CONFIG_ARRAY_KVANY,
|
2019-11-09 05:27:58 +00:00
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ NULL, 0,
|
|
|
|
T_CONFIG_UNSET,
|
|
|
|
T_CONFIG_SCOPE_UNSET }
|
|
|
|
};
|
|
|
|
|
|
|
|
plugin_data * const p = p_d;
|
|
|
|
if (!config_plugin_values_init(srv, p, cpk, "mod_webdav"))
|
|
|
|
return HANDLER_ERROR;
|
2006-09-01 10:17:28 +00:00
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
#ifdef USE_PROPPATCH
|
|
|
|
int sqlrc = sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
|
|
|
|
if (sqlrc != SQLITE_OK) {
|
|
|
|
log_error(srv->errh, __FILE__, __LINE__, "sqlite3_config(): %s",
|
|
|
|
sqlite3_errstr(sqlrc));
|
|
|
|
/*(performance option since our use is not threaded; not fatal)*/
|
|
|
|
/*return HANDLER_ERROR;*/
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-11-09 05:27:58 +00:00
|
|
|
/* process and validate config directives
|
|
|
|
* (init i to 0 if global context; to 1 to skip empty global context) */
|
|
|
|
for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
|
|
|
|
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
|
|
|
|
for (; -1 != cpv->k_id; ++cpv) {
|
|
|
|
switch (cpv->k_id) {
|
|
|
|
case 0: /* webdav.sqlite-db-name */
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
2021-06-09 02:57:36 +00:00
|
|
|
if (!buffer_is_blank(cpv->v.b)) {
|
2019-11-09 05:27:58 +00:00
|
|
|
if (!mod_webdav_sqlite3_init(cpv->v.b->ptr, srv->errh))
|
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 1: /* webdav.activate */
|
|
|
|
case 2: /* webdav.is-readonly */
|
|
|
|
case 3: /* webdav.log-xml */
|
|
|
|
break;
|
|
|
|
case 4: /* webdav.opts */
|
2020-06-12 01:55:47 +00:00
|
|
|
if (cpv->v.a->used) {
|
|
|
|
unsigned short opts = 0;
|
|
|
|
for (uint32_t j = 0, used = cpv->v.a->used; j < used; ++j) {
|
|
|
|
data_string *ds = (data_string *)cpv->v.a->data[j];
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
2021-06-09 02:57:36 +00:00
|
|
|
if (buffer_eq_slen(&ds->key,
|
2020-06-12 01:55:47 +00:00
|
|
|
CONST_STR_LEN("deprecated-unsafe-partial-put"))
|
2020-11-16 06:39:14 +00:00
|
|
|
&& config_plugin_value_tobool((data_unset *)ds,0)) {
|
2020-06-12 01:55:47 +00:00
|
|
|
opts |= MOD_WEBDAV_UNSAFE_PARTIAL_PUT_COMPAT;
|
|
|
|
continue;
|
|
|
|
}
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
2021-06-09 02:57:36 +00:00
|
|
|
if (buffer_eq_slen(&ds->key,
|
2020-06-12 02:28:12 +00:00
|
|
|
CONST_STR_LEN("propfind-depth-infinity"))
|
2020-11-16 06:39:14 +00:00
|
|
|
&& config_plugin_value_tobool((data_unset *)ds,0)) {
|
2020-06-12 02:28:12 +00:00
|
|
|
opts |= MOD_WEBDAV_PROPFIND_DEPTH_INFINITY;
|
|
|
|
continue;
|
|
|
|
}
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
2021-06-09 02:57:36 +00:00
|
|
|
if (buffer_eq_slen(&ds->key,
|
2020-06-12 02:12:32 +00:00
|
|
|
CONST_STR_LEN("unsafe-propfind-follow-symlink"))
|
2020-11-16 06:39:14 +00:00
|
|
|
&& config_plugin_value_tobool((data_unset *)ds,0)) {
|
2020-06-12 02:12:32 +00:00
|
|
|
opts |= MOD_WEBDAV_UNSAFE_PROPFIND_FOLLOW_SYMLINK;
|
|
|
|
continue;
|
|
|
|
}
|
2020-06-12 01:55:47 +00:00
|
|
|
log_error(srv->errh, __FILE__, __LINE__,
|
2021-06-16 21:33:59 +00:00
|
|
|
"unrecognized webdav.opts: %s", ds->key.ptr);
|
2020-06-12 01:55:47 +00:00
|
|
|
return HANDLER_ERROR;
|
2019-11-09 05:27:58 +00:00
|
|
|
}
|
2020-06-12 01:55:47 +00:00
|
|
|
cpv->v.u = opts;
|
|
|
|
cpv->vtype = T_CONFIG_LOCAL;
|
2019-11-09 05:27:58 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:/* should not happen */
|
|
|
|
break;
|
2019-04-17 03:41:27 +00:00
|
|
|
}
|
|
|
|
}
|
2019-03-26 02:13:24 +00:00
|
|
|
}
|
2006-09-01 10:17:28 +00:00
|
|
|
|
2019-11-09 05:27:58 +00:00
|
|
|
p->defaults.tmpb = srv->tmp_buf;
|
2006-09-01 10:17:28 +00:00
|
|
|
|
2019-11-09 05:27:58 +00:00
|
|
|
/* initialize p->defaults from global config context */
|
|
|
|
if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
|
|
|
|
const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
|
|
|
|
if (-1 != cpv->k_id)
|
|
|
|
mod_webdav_merge_config(&p->defaults, cpv);
|
2019-03-26 02:13:24 +00:00
|
|
|
}
|
2019-11-09 05:27:58 +00:00
|
|
|
|
2020-08-04 02:54:17 +00:00
|
|
|
#if (defined(__linux__) || defined(__CYGWIN__)) && defined(O_TMPFILE)
|
|
|
|
struct stat st;
|
|
|
|
has_proc_self_fd = (0 == stat("/proc/self/fd", &st));
|
|
|
|
#endif
|
|
|
|
|
2019-11-09 05:27:58 +00:00
|
|
|
return HANDLER_GO_ON;
|
2005-08-19 00:05:52 +00:00
|
|
|
}
|
|
|
|
|
2006-09-01 10:17:28 +00:00
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
URIHANDLER_FUNC(mod_webdav_uri_handler)
|
|
|
|
{
|
2020-01-13 02:51:12 +00:00
|
|
|
if (r->http_method != HTTP_METHOD_OPTIONS)
|
2019-03-26 02:13:24 +00:00
|
|
|
return HANDLER_GO_ON;
|
|
|
|
|
|
|
|
plugin_config pconf;
|
2020-01-13 02:51:12 +00:00
|
|
|
mod_webdav_patch_config(r, (plugin_data *)p_d, &pconf);
|
2019-03-26 02:13:24 +00:00
|
|
|
if (!pconf.enabled) return HANDLER_GO_ON;
|
|
|
|
|
|
|
|
/* [RFC4918] 18 DAV Compliance Classes */
|
2020-01-13 02:51:12 +00:00
|
|
|
http_header_response_set(r, HTTP_HEADER_OTHER,
|
2019-03-26 02:13:24 +00:00
|
|
|
CONST_STR_LEN("DAV"),
|
|
|
|
#ifdef USE_LOCKS
|
|
|
|
CONST_STR_LEN("1,2,3")
|
|
|
|
#else
|
|
|
|
CONST_STR_LEN("1,3")
|
|
|
|
#endif
|
|
|
|
);
|
|
|
|
|
|
|
|
/* instruct MS Office Web Folders to use DAV
|
|
|
|
* (instead of MS FrontPage Extensions)
|
|
|
|
* http://www.zorched.net/2006/03/01/more-webdav-tips-tricks-and-bugs/ */
|
2020-01-13 02:51:12 +00:00
|
|
|
http_header_response_set(r, HTTP_HEADER_OTHER,
|
2019-03-26 02:13:24 +00:00
|
|
|
CONST_STR_LEN("MS-Author-Via"),
|
|
|
|
CONST_STR_LEN("DAV"));
|
|
|
|
|
|
|
|
if (pconf.is_readonly)
|
2020-09-13 02:23:16 +00:00
|
|
|
http_header_response_append(r, HTTP_HEADER_ALLOW,
|
2019-03-26 02:13:24 +00:00
|
|
|
CONST_STR_LEN("Allow"),
|
|
|
|
CONST_STR_LEN("PROPFIND"));
|
|
|
|
else
|
2020-09-13 02:23:16 +00:00
|
|
|
http_header_response_append(r, HTTP_HEADER_ALLOW,
|
2019-03-26 02:13:24 +00:00
|
|
|
CONST_STR_LEN("Allow"),
|
|
|
|
#ifdef USE_PROPPATCH
|
|
|
|
#ifdef USE_LOCKS
|
|
|
|
CONST_STR_LEN(
|
|
|
|
"PROPFIND, DELETE, MKCOL, PUT, MOVE, COPY, PROPPATCH, LOCK, UNLOCK")
|
|
|
|
#else
|
|
|
|
CONST_STR_LEN(
|
|
|
|
"PROPFIND, DELETE, MKCOL, PUT, MOVE, COPY, PROPPATCH")
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
CONST_STR_LEN(
|
|
|
|
"PROPFIND, DELETE, MKCOL, PUT, MOVE, COPY")
|
|
|
|
#endif
|
|
|
|
);
|
2005-08-19 00:05:52 +00:00
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
return HANDLER_GO_ON;
|
|
|
|
}
|
2006-09-01 10:17:28 +00:00
|
|
|
|
2005-08-19 08:37:21 +00:00
|
|
|
|
2021-04-01 15:25:42 +00:00
|
|
|
static void
|
|
|
|
webdav_double_buffer (request_st * const r, buffer * const b)
|
|
|
|
{
|
|
|
|
/* send parts of XML to r->write_queue; surrounding XML tags added later.
|
|
|
|
* http_chunk_append_buffer() is safe to use here since r->resp_body_started
|
|
|
|
* has not been set, so r->resp_send_chunked can not be set yet */
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
2021-06-09 02:57:36 +00:00
|
|
|
if (buffer_clen(b) > 60000) {
|
2021-04-01 15:25:42 +00:00
|
|
|
http_chunk_append_buffer(r, b); /*(might move/steal/reset buffer)*/
|
2021-07-24 04:53:13 +00:00
|
|
|
/*buffer_clear(b);*//*http_chunk_append_buffer() clears*/
|
2021-04-01 15:25:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
#ifdef USE_LOCKS
|
|
|
|
|
2019-10-13 08:59:57 +00:00
|
|
|
typedef struct webdav_lockdata_wr {
|
|
|
|
buffer locktoken;
|
|
|
|
buffer lockroot;
|
|
|
|
buffer ownerinfo;
|
|
|
|
buffer *owner; /* NB: caller must provide writable storage */
|
|
|
|
const buffer *lockscope; /* future: might use enum, store int in db */
|
|
|
|
const buffer *locktype; /* future: might use enum, store int in db */
|
|
|
|
int depth;
|
|
|
|
int timeout; /* offset from now, not absolute time_t */
|
|
|
|
} webdav_lockdata_wr;
|
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
typedef struct webdav_lockdata {
|
|
|
|
buffer locktoken;
|
|
|
|
buffer lockroot;
|
|
|
|
buffer ownerinfo;
|
2019-10-13 08:59:57 +00:00
|
|
|
const buffer *owner;
|
2019-03-26 02:13:24 +00:00
|
|
|
const buffer *lockscope; /* future: might use enum, store int in db */
|
|
|
|
const buffer *locktype; /* future: might use enum, store int in db */
|
|
|
|
int depth;
|
|
|
|
int timeout; /* offset from now, not absolute time_t */
|
|
|
|
} webdav_lockdata;
|
|
|
|
|
|
|
|
typedef struct { const char *ptr; uint32_t used; uint32_t size; } tagb;
|
|
|
|
|
|
|
|
static const tagb lockscope_exclusive =
|
|
|
|
{ "exclusive", sizeof("exclusive"), 0 };
|
|
|
|
static const tagb lockscope_shared =
|
|
|
|
{ "shared", sizeof("shared"), 0 };
|
|
|
|
static const tagb locktype_write =
|
|
|
|
{ "write", sizeof("write"), 0 };
|
2006-09-01 10:17:28 +00:00
|
|
|
|
|
|
|
#endif
|
2005-08-29 10:43:29 +00:00
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
typedef struct {
|
|
|
|
const char *ns;
|
|
|
|
const char *name;
|
|
|
|
uint32_t nslen;
|
|
|
|
uint32_t namelen;
|
|
|
|
} webdav_property_name;
|
2006-09-01 10:17:28 +00:00
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
typedef struct {
|
|
|
|
webdav_property_name *ptr;
|
|
|
|
int used;
|
|
|
|
int size;
|
|
|
|
} webdav_property_names;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* http://www.w3.org/TR/1998/NOTE-XML-data-0105/
|
|
|
|
* The datatype attribute "dt" is defined in the namespace named
|
|
|
|
* "urn:uuid:C2F41010-65B3-11d1-A29F-00AA00C14882/".
|
|
|
|
* (See the XML Namespaces Note at the W3C site for details of namespaces.)
|
|
|
|
* The full URN of the attribute is
|
|
|
|
* "urn:uuid:C2F41010-65B3-11d1-A29F-00AA00C14882/dt".
|
|
|
|
* http://www.w3.org/TR/1998/NOTE-xml-names-0119
|
|
|
|
* http://www.w3.org/TR/1998/WD-xml-names-19980327
|
|
|
|
* http://lists.xml.org/archives/xml-dev/200101/msg00924.html
|
|
|
|
* http://lists.xml.org/archives/xml-dev/200101/msg00929.html
|
|
|
|
* http://lists.xml.org/archives/xml-dev/200101/msg00930.html
|
|
|
|
* (Microsoft) Namespace Guidelines
|
|
|
|
* https://msdn.microsoft.com/en-us/library/ms879470%28v=exchg.65%29.aspx
|
|
|
|
* (Microsoft) XML Persistence Format
|
|
|
|
* https://msdn.microsoft.com/en-us/library/ms676547%28v=vs.85%29.aspx
|
|
|
|
* http://www.xml.com/pub/a/2002/06/26/vocabularies.html
|
|
|
|
* The "Uuid" namespaces is the namespace
|
|
|
|
* "uuid:C2F41010-65B3-11d1-A29F-00AA00C14882",
|
|
|
|
* mainly found in association with the MS Office
|
|
|
|
* namespace on the http://www.omg.org website.
|
|
|
|
* http://www.data2type.de/en/xml-xslt-xslfo/wordml/wordml-introduction/the-root-element/
|
|
|
|
* xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
|
|
|
|
* By using the prefix dt, the namespace declares an attribute which
|
|
|
|
* determines the data type of a value. The name of the underlying schema
|
|
|
|
* is dt.xsd and it can be found in the folder for Excel schemas.
|
|
|
|
*/
|
|
|
|
#define MOD_WEBDAV_XMLNS_NS0 "xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\""
|
2006-09-01 10:17:28 +00:00
|
|
|
|
|
|
|
|
2021-04-01 00:16:10 +00:00
|
|
|
static char *
|
|
|
|
webdav_mmap_file_chunk (chunk * const c);
|
|
|
|
|
|
|
|
__attribute_cold__
|
|
|
|
__attribute_noinline__
|
|
|
|
static void
|
|
|
|
webdav_xml_log_response (request_st * const r)
|
|
|
|
{
|
|
|
|
chunkqueue * const cq = &r->write_queue;
|
|
|
|
log_error_st * const errh = r->conf.errh;
|
|
|
|
if (chunkqueue_length(cq) <= 65536)
|
|
|
|
chunkqueue_read_squash(cq, errh);
|
|
|
|
char *s;
|
|
|
|
uint32_t len;
|
|
|
|
for (chunk *c = cq->first; c; c = c->next) {
|
|
|
|
switch (c->type) {
|
|
|
|
case MEM_CHUNK:
|
|
|
|
s = c->mem->ptr + c->offset;
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
2021-06-09 02:57:36 +00:00
|
|
|
len = buffer_clen(c->mem) - (uint32_t)c->offset;
|
2021-04-01 00:16:10 +00:00
|
|
|
break;
|
|
|
|
case FILE_CHUNK:
|
|
|
|
/*(safe to mmap tempfiles from response XML)*/
|
|
|
|
s = webdav_mmap_file_chunk(c);
|
|
|
|
len = (uint32_t)c->file.length;
|
|
|
|
if (s == NULL) continue;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
log_error(errh, __FILE__, __LINE__, "XML-response-body: %.*s",
|
|
|
|
(int)len, s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
static void
|
2020-01-13 02:51:12 +00:00
|
|
|
webdav_xml_doctype (buffer * const b, request_st * const r)
|
2019-03-26 02:13:24 +00:00
|
|
|
{
|
2020-01-13 02:51:12 +00:00
|
|
|
http_header_response_set(r, HTTP_HEADER_CONTENT_TYPE,
|
2019-03-26 02:13:24 +00:00
|
|
|
CONST_STR_LEN("Content-Type"),
|
|
|
|
CONST_STR_LEN("application/xml; charset=\"utf-8\""));
|
2006-09-01 10:17:28 +00:00
|
|
|
|
2019-03-26 02:13:24 +00:00
|
|
|
buffer_copy_string_len(b, CONST_STR_LEN(
|
|
|
|
|