]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
mod_dav, mod_dav_fs: Add opt-in support for controlling resource
authorJoe Orton <jorton@apache.org>
Fri, 7 Nov 2025 12:57:48 +0000 (12:57 +0000)
committerJoe Orton <jorton@apache.org>
Fri, 7 Nov 2025 12:57:48 +0000 (12:57 +0000)
modification times via the X-Oc-Mtime header in the PUT and MKCOL
method implementations.

* modules/dav/fs/mod_dav.h: Extend dav_hooks_repository struct
  with set_mtime function pointer.

* modules/dav/fs/repos.c (dav_fs_set_mtime): New function.

* modules/dav/main/mod_dav.c (dav_cmd_davhonormtimeheader,
  dav_parse_mtime): New functions.
  (dav_method_put, dav_method_mkcol): Add X-Oc-Mtime handling.
  (dav_cmds): Add DAVHonorMtimeHeader directive.

Submitted by: Leo <i hardrain980.com>
Github: closes #556

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1929581 13f79535-47bb-0310-9956-ffa450edef68

changes-entries/dav-mtime.txt [new file with mode: 0644]
modules/dav/fs/repos.c
modules/dav/main/mod_dav.c
modules/dav/main/mod_dav.h

diff --git a/changes-entries/dav-mtime.txt b/changes-entries/dav-mtime.txt
new file mode 100644 (file)
index 0000000..4c101a7
--- /dev/null
@@ -0,0 +1,6 @@
+  *) mod_dav: Add DAVHonorMtimeHeader directive, if enabled
+     the X-Oc-Mtime request header can be used to control
+     resource modification times.  [Leo <i hardrain980.com>]
+     
+  *) mod_dav_fs: Add support for changing resource modification times.
+     [Leo <i hardrain980.com>]
index 614154502e7aeb1dc227d69923af2eee48fff073..746681877f2321bcb7bded098b8952b00a219e08 100644 (file)
@@ -1164,6 +1164,24 @@ static dav_error * dav_fs_deliver(const dav_resource *resource,
 
 #endif /* DEBUG_GET_HANDLER */
 
+static dav_error * dav_fs_set_mtime(dav_resource *resource, apr_time_t mtime)
+{
+    apr_pool_t *pool;
+    apr_status_t status;
+
+    pool = resource->pool;
+    status = apr_file_mtime_set(resource->info->pathname, mtime, pool);
+
+    if (status != APR_SUCCESS) {
+        ap_log_perror(APLOG_MARK, APLOG_ERR, status, pool, APLOGNO(10543)
+                      "Failed setting modification time for file %s.",
+                      resource->info->pathname);
+        return dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, status,
+                             "Could not set modification time.");
+    }
+
+    return NULL;
+}
 
 static dav_error * dav_fs_create_collection(dav_resource *resource)
 {
@@ -1972,7 +1990,8 @@ static const dav_hooks_repository dav_hooks_repository_fs =
     dav_fs_getetag,
     NULL,
     dav_fs_get_request_rec,
-    dav_fs_pathname
+    dav_fs_pathname,
+    dav_fs_set_mtime
 };
 
 static dav_prop_insert dav_fs_insert_prop(const dav_resource *resource,
index fdfe2c609481b184a4041d1b4a1060500b10205d..69aa811a3e72f5d1e6cd0ac0e6c1f5abe312a5ef 100644 (file)
@@ -90,6 +90,7 @@ typedef struct {
     int allow_depthinfinity;
     int allow_lockdiscovery;
     int msext_opts;
+    int honor_mtime_header;
 } dav_dir_conf;
 
 /* per-server configuration */
@@ -213,6 +214,8 @@ static void *dav_merge_dir_config(apr_pool_t *p, void *base, void *overrides)
                                                      allow_depthinfinity);
     newconf->allow_lockdiscovery = DAV_INHERIT_VALUE(parent, child,
                                                      allow_lockdiscovery);
+    newconf->honor_mtime_header = DAV_INHERIT_VALUE(parent, child,
+                                                    honor_mtime_header);
     newconf->msext_opts = DAV_INHERIT_VALUE(parent, child,
                                             msext_opts);
 
@@ -304,6 +307,20 @@ static const char *dav_cmd_dav(cmd_parms *cmd, void *config, const char *arg1)
     return NULL;
 }
 
+/*
+ * Command handler for the DAVHonorMtimeHeader directive, which is FLAG.
+ */
+static const char *dav_cmd_davhonormtimeheader(cmd_parms *cmd, void *config, const int arg)
+{
+    dav_dir_conf *conf = (dav_dir_conf *)config;
+
+    if (arg)
+        conf->honor_mtime_header = DAV_ENABLED_ON;
+    else
+        conf->honor_mtime_header = DAV_ENABLED_OFF;
+    return NULL;
+}
+
 /*
  * Command handler for the DAVBasePath directive, which is TAKE1
  */
@@ -947,6 +964,37 @@ static int dav_parse_range(request_rec *r,
     return 1;
 }
 
+/**
+ * @return  1 if valid x-oc-mtime,
+ *          0 if no x-oc-mtime,
+ *         -1 if malformed x-oc-mtime
+ */
+static int dav_parse_mtime(request_rec *r, apr_time_t *mtime)
+{
+    const char *hdr;
+    char *endp;
+    apr_int64_t n;
+    apr_size_t i;
+
+    if ((hdr = apr_table_get(r->headers_in, "x-oc-mtime")) == NULL) {
+        return 0;
+    }
+
+    for (i = 0; i < strlen(hdr); i++) {
+        if (!apr_isdigit(hdr[i])) {
+            return -1;
+        }
+    }
+
+    n = apr_strtoi64(hdr, &endp, 10);
+    if (errno != 0 || endp == hdr) {
+        return -1;
+    }
+
+    *mtime = (apr_time_t) apr_time_from_sec(n);
+    return 1;
+}
+
 /* handle the GET method */
 static int dav_method_get(request_rec *r)
 {
@@ -1050,6 +1098,11 @@ static int dav_method_put(request_rec *r)
     apr_off_t range_start;
     apr_off_t range_end;
     int rc;
+    int mtime_ret;
+    apr_time_t mtime;
+
+    /* retrieve module config */
+    conf = ap_get_module_config(r->per_dir_config, &dav_module);
 
     /* Ask repository module to resolve the resource */
     err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
@@ -1114,6 +1167,17 @@ static int dav_method_put(request_rec *r)
         mode = DAV_MODE_WRITE_TRUNC;
     }
 
+    /* try parsing x-oc-mtime header */
+    mtime_ret = 0;
+    if (conf->honor_mtime_header == DAV_ENABLED_ON) {
+        if ((mtime_ret = dav_parse_mtime(r, &mtime)) == -1) {
+            body = apr_psprintf(r->pool,
+                                "Malformed X-OC-Mtime header for PUT %s.",
+                                ap_escape_html(r->pool, r->uri));
+            return dav_error_response(r, HTTP_BAD_REQUEST, body);
+        }
+    }
+
     /* make sure the resource can be modified (if versioning repository) */
     if ((err = dav_auto_checkout(r, resource,
                                  0 /* not parent_only */,
@@ -1208,6 +1272,19 @@ static int dav_method_put(request_rec *r)
         err2 = (*resource->hooks->close_stream)(stream,
                                                 err == NULL /* commit */);
         err = dav_join_error(err, err2);
+
+        if (err == NULL && mtime_ret == 1) {
+            if (*resource->hooks->set_mtime != NULL) {
+                err2 = (*resource->hooks->set_mtime)(resource, mtime);
+                err = dav_join_error(err, err2);
+                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                              "Setting modification time for file.");
+            }
+            else {
+                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                              "Provider does not support setting modification times.");
+            }
+        }
     }
 
     /*
@@ -1240,7 +1317,6 @@ static int dav_method_put(request_rec *r)
     }
 
     /* This performs MS-WDV PROPPATCH combined with PUT */
-    conf = ap_get_module_config(r->per_dir_config, &dav_module);
     if (conf->msext_opts & DAV_MSEXT_OPT_WDV)
         (void)dav_mswdv_postprocessing(r);
 
@@ -2732,6 +2808,12 @@ static int dav_method_mkcol(request_rec *r)
     int result;
     int rc;
     dav_response *multi_status;
+    dav_dir_conf *conf;
+    int mtime_ret;
+    apr_time_t mtime;
+
+    /* retrieve module config */
+    conf = ap_get_module_config(r->per_dir_config, &dav_module);
 
     /* handle the request body */
     /* ### this may move lower once we start processing bodies */
@@ -2759,6 +2841,17 @@ static int dav_method_mkcol(request_rec *r)
         return HTTP_METHOD_NOT_ALLOWED;
     }
 
+    /* try parsing x-oc-mtime header */
+    mtime_ret = 0;
+    if (conf->honor_mtime_header == DAV_ENABLED_ON) {
+        if ((mtime_ret = dav_parse_mtime(r, &mtime)) == -1) {
+            return dav_error_response(r, HTTP_BAD_REQUEST,
+                                      apr_psprintf(r->pool,
+                                                   "Malformed X-OC-Mtime header for MKCOL %s.",
+                                                   ap_escape_html(r->pool, r->uri)));
+        }
+    }
+
     resource_state = dav_get_resource_state(r, resource);
 
     /*
@@ -2839,6 +2932,25 @@ static int dav_method_mkcol(request_rec *r)
         }
     }
 
+    if (mtime_ret == 1) {
+        if (resource->hooks->set_mtime != NULL) {
+            err = (resource->hooks->set_mtime)(resource, mtime);
+            if (err != NULL) {
+                err = dav_push_error(r->pool, err->status, 0,
+                                     "The MKCOL was successful, but there "
+                                     "was a problem setting its modification time.",
+                                     err);
+                return dav_handle_err(r, err, NULL);
+            }
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                          "Setting modification time for directory.");
+        }
+        else {
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                          "Provider does not support setting modification times.");
+        }
+    }
+
     /* return an appropriate response (HTTP_CREATED) */
     rc = dav_created(r, NULL, "Collection", 0);
 
@@ -5345,6 +5457,11 @@ static const command_rec dav_cmds[] =
                  ACCESS_CONF|RSRC_CONF,
                  "allow lock discovery by PROPFIND requests"),
 
+    /* per directory/location */
+    AP_INIT_FLAG("DAVHonorMtimeHeader", dav_cmd_davhonormtimeheader, NULL,
+                 ACCESS_CONF,
+                 "Set modification time based on X-OC-Mtime header"),
+
     /* per directory/location, or per server */
     AP_INIT_ITERATE("DAVMSext", dav_cmd_davmsext, NULL,
                     ACCESS_CONF|RSRC_CONF,
index 6b42d8203fdc712c0874b586a5ce527d2762d1c0..8d9be2ad42af09f3d6825ebf24fb3f15ed59cd3b 100644 (file)
@@ -2166,6 +2166,10 @@ struct dav_hooks_repository
 
     /* Get the pathname for a resource */
     const char * (*get_pathname)(const dav_resource *resource);
+
+    /* Set modification time for a resource.  Can be NULL. */
+    dav_error * (*set_mtime)(dav_resource *resource, const apr_time_t mtime);
+
 };