Blob Blame History Raw
diff --git a/src/plugins/apache-httpd/src/mod_lcgdm_disk/checksum.c b/src/plugins/apache-httpd/src/mod_lcgdm_disk/checksum.c
index 6189da03..61454e81 100644
--- a/src/plugins/apache-httpd/src/mod_lcgdm_disk/checksum.c
+++ b/src/plugins/apache-httpd/src/mod_lcgdm_disk/checksum.c
@@ -32,16 +32,17 @@
 dav_error* dav_disk_digest_header(request_rec *r, const dav_resource *resource,
         char* output, size_t outsize)
 {
-    const char *want_digest;
-    char digest_name[32], digest[64], full_digest_name[64];
+    char sorted_digests[1024], *digest_name, *first_digest_name = NULL;
+    char digest[1024], full_digest_name[64];
+    int chksum_timeout = -1; // negative time can be interpreted as a request for already calculated checksum value
 
-    want_digest = apr_table_get(r->headers_in, "Want-Digest");
-
-    if (!resource->info->fd || !want_digest) {
+    dav_shared_sorted_digests(resource->info->request, sorted_digests, 1024);
+    if (strlen(sorted_digests) == 0) {
         return NULL;
-    }
+    };
 
-    while (dav_shared_next_digest(&want_digest, digest_name, sizeof(digest_name))) {
+    digest_name = strtok(sorted_digests, ",");
+    while (digest_name) {
         snprintf(full_digest_name, sizeof(full_digest_name), "checksum.%s", digest_name);
 
         int ret = dmlite_getchecksum(resource->info->ctx,
@@ -49,7 +50,7 @@ dav_error* dav_disk_digest_header(request_rec *r, const dav_resource *resource,
             full_digest_name, digest, sizeof(digest),
             resource->info->loc.chunks[0].url.path, // replica
             0, // 0 = do not recalculate
-            0 // 1800 seconds wait, otherwise return EAGAIN
+            chksum_timeout // negative time can be interpreted as a request for already calculated checksum value
             );
 
         if (ret == 0 && digest[0] != '\0') {
@@ -79,8 +80,26 @@ dav_error* dav_disk_digest_header(request_rec *r, const dav_resource *resource,
             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, resource->info->request,
                 "Failed to get the checksum %s: %s", digest_name, dmlite_error(resource->info->ctx));
         } else {
-            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, resource->info->request,
-                "Failed to get the checksum %s: empty value", digest_name);
+            if (!first_digest_name) {
+                // first checksum with empty value signalize it may be necessary
+                // to calculate missing checksum in case we don't find any other
+                // that was already calculated and it is now stored in database
+                first_digest_name = digest_name;
+            } else if (!(chksum_timeout < 0)) {
+                // only report problems in case of an attempt to calculate missing checksum
+                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, resource->info->request,
+                    "Failed to get the checksum %s: empty value", digest_name);
+            }
+        }
+
+        // forced checksum calculation is the last step after we already processed all digest names
+        if (!(chksum_timeout < 0))
+            break;
+
+        digest_name = strtok(NULL, ",");
+        if (!digest_name && first_digest_name) {
+            digest_name = first_digest_name;
+            chksum_timeout = 0; // calculate missing checksum with default timeout
         }
     }
 
diff --git a/src/plugins/apache-httpd/src/mod_lcgdm_disk/repository.c b/src/plugins/apache-httpd/src/mod_lcgdm_disk/repository.c
index d92b5cf5..343209cd 100644
--- a/src/plugins/apache-httpd/src/mod_lcgdm_disk/repository.c
+++ b/src/plugins/apache-httpd/src/mod_lcgdm_disk/repository.c
@@ -122,7 +122,7 @@ static int dav_finish_writing(dav_resource_private* info)
         dmlite_fclose(info->fd);
         info->fd = NULL;
     }
-    apr_table_unset(info->request->connection->notes, "dav_disk_info");
+    apr_table_unset(info->request->connection->notes, info->d_conf->info_key);
     return e;
 }
 
@@ -199,7 +199,7 @@ static dav_error *dav_disk_internal_get_resource(request_rec *r,
     /* (Re?)Initialize info */
     info->request = r;
     info->method = r->method_number;
-    info->query_str = apr_pstrdup(r->pool, r->parsed_uri.query);
+    info->query_str = apr_pstrdup(r->connection->pool, r->parsed_uri.query);
     info->loc.nchunks = 1;
     strncpy(info->loc.chunks[0].url.path, pfn, PATH_MAX);
     info->loc.chunks[0].url.path[PATH_MAX - 1] = '\0';
@@ -267,7 +267,7 @@ static dav_error *dav_disk_internal_get_resource(request_rec *r,
     /* Namespace URL */
     sfn = apr_table_get(query, "dav_sfn");
     if (sfn != NULL ) {
-        info->namespace_path = apr_pstrdup(r->pool, sfn);
+        info->namespace_path = apr_pstrdup(r->connection->pool, sfn);
         ap_unescape_url(info->namespace_path);
         // this should be passed to dmlite according to LCGDM-1508
         //apr_table_unset(query, "dav_sfn");
diff --git a/src/plugins/apache-httpd/src/mod_lcgdm_ns/checksum.c b/src/plugins/apache-httpd/src/mod_lcgdm_ns/checksum.c
index 98f80604..c9645aa1 100644
--- a/src/plugins/apache-httpd/src/mod_lcgdm_ns/checksum.c
+++ b/src/plugins/apache-httpd/src/mod_lcgdm_ns/checksum.c
@@ -31,16 +31,17 @@
 dav_error* dav_ns_digest_header(request_rec *r, const dav_resource *resource,
         char* output, size_t outsize)
 {
-    const char *want_digest;
-    char digest_name[32], digest[1024], full_digest_name[64];
+    char sorted_digests[1024], *digest_name, *first_digest_name = NULL;
+    char digest[1024], full_digest_name[64];
+    int chksum_timeout = -1; // negative time can be interpreted as a request for already calculated checksum value
 
-    want_digest = apr_table_get(r->headers_in, "Want-Digest");
-
-    if (!want_digest) {
+    dav_shared_sorted_digests(resource->info->request, sorted_digests, 1024);
+    if (strlen(sorted_digests) == 0) {
         return NULL;
-    }
+    };
 
-    while (dav_shared_next_digest(&want_digest, digest_name, sizeof(digest_name))) {
+    digest_name = strtok(sorted_digests, ",");
+    while (digest_name) {
         snprintf(full_digest_name, sizeof(full_digest_name), "checksum.%s", digest_name);
 
         int ret = dmlite_getchecksum(resource->info->ctx,
@@ -48,7 +49,7 @@ dav_error* dav_ns_digest_header(request_rec *r, const dav_resource *resource,
             full_digest_name, digest, sizeof(digest),
             NULL, // replica
             0, // 0 = do not recalculate
-            0 // 1800 seconds wait, otherwise return EAGAIN
+            chksum_timeout // negative time can be interpreted as a request for already calculated checksum value
             );
 
         if (ret == DMLITE_RDR_ON_CHECKSUM) {
@@ -89,8 +90,26 @@ dav_error* dav_ns_digest_header(request_rec *r, const dav_resource *resource,
             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, resource->info->request,
                 "Failed to get the checksum %s: %s", digest_name, dmlite_error(resource->info->ctx));
         } else {
-            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, resource->info->request,
-                "Failed to get the checksum %s: empty value", digest_name);
+            if (!first_digest_name) {
+                // first checksum with empty value signalize it may be necessary
+                // to calculate missing checksum in case we don't find any other
+                // that was already calculated and it is now stored in database
+                first_digest_name = digest_name;
+            } else if (!(chksum_timeout < 0)) {
+                // only report problems in case of an attempt to calculate missing checksum
+                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, resource->info->request,
+                    "Failed to get the checksum %s: empty value", digest_name);
+            }
+        }
+
+        // forced checksum calculation is the last step after we already processed all digest names
+        if (!(chksum_timeout < 0))
+            break;
+
+        digest_name = strtok(NULL, ",");
+        if (!digest_name && first_digest_name) {
+            digest_name = first_digest_name;
+            chksum_timeout = 0; // calculate missing checksum with default timeout
         }
     }
 
diff --git a/src/plugins/apache-httpd/src/shared/checksum.c b/src/plugins/apache-httpd/src/shared/checksum.c
index 23d98c43..02da1f53 100644
--- a/src/plugins/apache-httpd/src/shared/checksum.c
+++ b/src/plugins/apache-httpd/src/shared/checksum.c
@@ -32,7 +32,15 @@
 #include <openssl/bio.h>
 #include <openssl/buffer.h>
 
-#define RFC3230_REGEXP "^([[:alnum:]]+)(;q=[[:digit:]]*)?([,]?)"
+#define RFC3230_REGEXP "^([[:alnum:]-]+)(;q=[[:digit:]]+\\.?[[:digit:]]*)?([,]?)"
+#define RFC3230_REGEXP_SINGLE "^[[:space:]]*([[:alnum:]-]+)(;q=([[:digit:]]+\\.?[[:digit:]]*))?[[:space:]]*$"
+
+ struct digest_list {
+    char *digest;
+    double q;
+    struct digest_list *next;
+};
+
 
 static char* dav_shared_strcpy_lower(char *out, const char *in, size_t max)
 {
@@ -78,6 +86,107 @@ int dav_shared_next_digest(const char **want_digest, char *output, size_t outsiz
 }
 
 
+void dav_shared_sorted_digests(request_rec *r, char *output, size_t max)
+{
+    char *supported_digests[] = {
+        "md5", "crc32", "adler32", NULL
+    };
+
+    const char *want_digest;
+    char *s, *token;
+    struct digest_list *dl = NULL;
+
+    int status;
+    regex_t regex;
+    regmatch_t matches[4];
+
+    output[0] = '\0';
+    want_digest = apr_table_get(r->headers_in, "Want-Digest");
+
+    if (!want_digest) {
+        return;
+    }
+
+    if (regcomp(&regex, RFC3230_REGEXP_SINGLE, REG_EXTENDED) != 0)
+        abort();
+
+    // split want-digest string by comma
+    s = strdup(want_digest);
+    token = strtok(s, ",");
+
+    while (token) {
+        // parse checksum type and preference
+        status = regexec(&regex, token, 4, matches, 0);
+
+        if (status == 0) {
+            size_t digest_len = matches[1].rm_eo - matches[1].rm_so;
+            char *digest;
+            double q = 1;
+            int supported = 0;
+            int i;
+
+            digest = malloc(digest_len+1);
+            dav_shared_strcpy_lower(digest, token+matches[1].rm_so, digest_len);
+            digest[digest_len] = '\0';
+
+            for (i = 0; supported_digests[i]; ++i) {
+                if (strcmp(supported_digests[i], digest) == 0) {
+                    supported = 1;
+                    break;
+                }
+            }
+
+            if (supported) {
+                if (matches[3].rm_so < matches[3].rm_eo) {
+                    const char *q_start = token + matches[3].rm_so;
+                    token[matches[3].rm_eo] = '\0';
+                    q = atof(q_start);
+                }
+                if (q < 0) q = 0;
+                if (q > 1) q = 1;
+
+                // add new checksum time in a list sorted by preference
+                struct digest_list *d = (struct digest_list*) malloc(sizeof(struct digest_list));
+                d->digest = digest;
+                d->q = q;
+                d->next = NULL;
+
+                if (dl) {
+                    struct digest_list *p = dl;
+                    while (p->q >= q && p->next) p = p->next;
+                    d->next = p->next;
+                    p->next = d;
+                } else {
+                  dl = d;
+                }
+
+            } else {
+                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Skipping unsupported checksum type %s", token);
+            }
+
+        } else {
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Skipping unparsable checksum algorithm %s", token);
+        }
+
+        token = strtok(NULL, ",");
+    }
+
+    regfree(&regex);
+    free(s);
+
+    while (dl) {
+        if (strlen(output) + strlen(dl->digest) + 1 < max) {
+            if (output[0] != '\0') strcat(output, ",");
+            strcat(output, dl->digest);
+        } else {
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Skipping digest %s because it exceeds max digest list size %lu", dl->digest, (unsigned long) max);
+        }
+        free(dl->digest);
+        dl = dl->next;
+    }
+}
+
+
 static int
 char_to_int(char c)
 {
diff --git a/src/plugins/apache-httpd/src/shared/utils.h b/src/plugins/apache-httpd/src/shared/utils.h
index f8ff0a9a..658f973f 100644
--- a/src/plugins/apache-httpd/src/shared/utils.h
+++ b/src/plugins/apache-httpd/src/shared/utils.h
@@ -140,6 +140,14 @@ apr_table_t* dav_lcgdm_parse_cookies(apr_pool_t* pool, const char* cookie_str);
  */
 int dav_shared_next_digest(const char **want_digest, char *output, size_t outsize);
 
+/**
+ * Return comma separated list of supported checksum types sorted by priority.
+ * @param r         The request thar originated the error.
+ * @param output    Pre-allocated char array for output sorted list of comma separated chksum types.
+ * @param max       Length of pre-allocated output buffer.
+ */
+void dav_shared_sorted_digests(request_rec *r, char *output, size_t max);
+
 /**
 * Decode a hex digest array to base64 if needed. Returns the
 * length of the resulting string, 0 if error