You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2019/10/25 13:27:13 UTC

svn commit: r1868930 [3/5] - in /httpd/httpd/branches/2.4.x: ./ build/ docs/manual/mod/ modules/md/

Modified: httpd/httpd/branches/2.4.x/modules/md/md_reg.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_reg.c?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_reg.c (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_reg.c Fri Oct 25 13:27:12 2019
@@ -46,8 +46,10 @@ struct md_reg_t {
     int can_https;
     const char *proxy_url;
     int domains_frozen;
-    const md_timeslice_t *renew_window;
-    const md_timeslice_t *warn_window;
+    md_timeslice_t *renew_window;
+    md_timeslice_t *warn_window;
+    md_job_notify_cb *notify;
+    void *notify_ctx;
 };
 
 /**************************************************************************************************/
@@ -292,11 +294,6 @@ md_t *md_reg_get(md_reg_t *reg, const ch
     return NULL;
 }
 
-apr_status_t md_reg_reinit_state(md_reg_t *reg, md_t *md, apr_pool_t *p)
-{
-    return state_init(reg, p, md);
-}
-
 typedef struct {
     const char *domain;
     md_t *md;
@@ -417,7 +414,7 @@ static apr_status_t p_md_update(void *ba
         return APR_ENOENT;
     }
     
-    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, ptemp, "update md %s", name);
+    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, ptemp, "md[%s]: update store", name);
     
     if (do_checks && APR_SUCCESS != (rv = check_values(reg, ptemp, updates, fields))) {
         return rv;
@@ -455,11 +452,11 @@ static apr_status_t p_md_update(void *ba
     }
     if (MD_UPD_RENEW_WINDOW & fields) {
         md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, ptemp, "update renew-window: %s", name);
-        nmd->renew_window = updates->renew_window;
+        *nmd->renew_window = *updates->renew_window;
     }
     if (MD_UPD_WARN_WINDOW & fields) {
         md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, ptemp, "update warn-window: %s", name);
-        nmd->warn_window = updates->warn_window;
+        *nmd->warn_window = *updates->warn_window;
     }
     if (MD_UPD_CA_CHALLENGES & fields) {
         md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, ptemp, "update ca challenges: %s", name);
@@ -489,6 +486,10 @@ static apr_status_t p_md_update(void *ba
         md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, ptemp, "update proto: %s", name);
         nmd->acme_tls_1_domains = updates->acme_tls_1_domains;
     }
+    if (MD_UPD_STAPLING & fields) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, ptemp, "update stapling: %s", name);
+        nmd->stapling = updates->stapling;
+    }
     
     if (fields && APR_SUCCESS == (rv = md_save(reg->store, p, MD_SG_DOMAINS, nmd, 0))) {
         rv = state_init(reg, ptemp, nmd);
@@ -496,17 +497,11 @@ static apr_status_t p_md_update(void *ba
     return rv;
 }
 
-static apr_status_t update_md(md_reg_t *reg, apr_pool_t *p, 
-                              const char *name, const md_t *md, 
-                              int fields, int do_checks)
-{
-    return md_util_pool_vdo(p_md_update, reg, p, name, md, fields, do_checks, NULL);
-}
-
 apr_status_t md_reg_update(md_reg_t *reg, apr_pool_t *p, 
-                           const char *name, const md_t *md, int fields)
+                           const char *name, const md_t *md, int fields, 
+                           int do_checks)
 {
-    return update_md(reg, p, name, md, fields, 1);
+    return md_util_pool_vdo(p_md_update, reg, p, name, md, fields, do_checks, NULL);
 }
 
 apr_status_t md_reg_delete_acct(md_reg_t *reg, apr_pool_t *p, const char *acct_id) 
@@ -612,16 +607,16 @@ apr_status_t md_reg_get_cred_files(const
     return APR_SUCCESS;
 }
 
-int md_reg_should_renew(md_reg_t *reg, const md_t *md, apr_pool_t *p) 
+apr_time_t md_reg_renew_at(md_reg_t *reg, const md_t *md, apr_pool_t *p)
 {
     const md_pubcert_t *pub;
     const md_cert_t *cert;
     md_timeperiod_t certlife, renewal;
     apr_status_t rv;
     
-    if (md->state == MD_S_INCOMPLETE) return 1;
+    if (md->state == MD_S_INCOMPLETE) return apr_time_now();
     rv = md_reg_get_pubcert(&pub, reg, md, p);
-    if (APR_STATUS_IS_ENOENT(rv)) return 1;
+    if (APR_STATUS_IS_ENOENT(rv)) return apr_time_now();
     if (APR_SUCCESS == rv) {
         cert = APR_ARRAY_IDX(pub->certs, 0, const md_cert_t*);
         certlife.start = md_cert_get_not_before(cert);
@@ -629,16 +624,24 @@ int md_reg_should_renew(md_reg_t *reg, c
 
         renewal = md_timeperiod_slice_before_end(&certlife, md->renew_window);
         if (md_log_is_level(p, MD_LOG_TRACE1)) {
-            md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, p, 
+            md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, 0, p, 
                           "md[%s]: cert-life[%s] renewal[%s]", md->name, 
                           md_timeperiod_print(p, &certlife),
                           md_timeperiod_print(p, &renewal));
         }
-        return md_timeperiod_has_started(&renewal, apr_time_now());
+        return renewal.start;
     }
     return 0;
 }
 
+int md_reg_should_renew(md_reg_t *reg, const md_t *md, apr_pool_t *p) 
+{
+    apr_time_t renew_at;
+    
+    renew_at = md_reg_renew_at(reg, md, p);
+    return renew_at && (renew_at <= apr_time_now());
+}
+
 int md_reg_should_warn(md_reg_t *reg, const md_t *md, apr_pool_t *p)
 {
     const md_pubcert_t *pub;
@@ -656,7 +659,7 @@ int md_reg_should_warn(md_reg_t *reg, co
 
         warn = md_timeperiod_slice_before_end(&certlife, md->warn_window);
         if (md_log_is_level(p, MD_LOG_TRACE1)) {
-            md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, p, 
+            md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, 0, p, 
                           "md[%s]: cert-life[%s] warn[%s]", md->name, 
                           md_timeperiod_print(p, &certlife),
                           md_timeperiod_print(p, &warn));
@@ -669,33 +672,6 @@ int md_reg_should_warn(md_reg_t *reg, co
 /**************************************************************************************************/
 /* synching */
 
-typedef struct {
-    apr_pool_t *p;
-    apr_array_header_t *store_mds;
-} sync_ctx;
-
-static int do_add_md(void *baton, md_store_t *store, md_t *md, apr_pool_t *ptemp)
-{
-    sync_ctx *ctx = baton;
-
-    (void)store;
-    (void)ptemp;
-    APR_ARRAY_PUSH(ctx->store_mds, const md_t*) = md_clone(ctx->p, md);
-    return 1;
-}
-
-static apr_status_t read_store_mds(md_reg_t *reg, sync_ctx *ctx)
-{
-    int rv;
-    
-    apr_array_clear(ctx->store_mds);
-    rv = md_store_md_iter(do_add_md, ctx, reg->store, ctx->p, MD_SG_DOMAINS, "*");
-    if (APR_STATUS_IS_ENOENT(rv) || APR_STATUS_IS_EINVAL(rv)) {
-        rv = APR_SUCCESS;
-    }
-    return rv;
-}
-
 apr_status_t md_reg_set_props(md_reg_t *reg, apr_pool_t *p, int can_http, int can_https)
 {
     if (reg->can_http != can_http || reg->can_https != can_https) {
@@ -714,192 +690,211 @@ apr_status_t md_reg_set_props(md_reg_t *
     return APR_SUCCESS;
 }
 
-static apr_status_t update_md(md_reg_t *reg, apr_pool_t *p, 
-                              const char *name, const md_t *md, 
-                              int fields, int do_checks);
- 
-/**
- * Procedure:
- * 1. Collect all defined "managed domains" (MD). It does not matter where a MD is defined. 
- *    All MDs need to be unique and have no overlaps in their domain names. 
- *    Fail the config otherwise. Also, if a vhost matches an MD, it
- *    needs to *only* have ServerAliases from that MD. There can be no more than one
- *    matching MD for a vhost. But an MD can apply to several vhosts.
- * 2. Synchronize with the persistent store. Iterate over all configured MDs and 
- *   a. create them in the store if they do not already exist, neither under the
- *      name or with a common domain.
- *   b. compare domain lists from store and config, if
- *      - store has dns name in other MD than from config, remove dns name from store def,
- *        issue WARNING.
- *      - store misses dns name from config, add dns name and update store
- *   c. compare MD acme url/protocol, update if changed
+static md_t *find_closest_match(apr_array_header_t *mds, const md_t *md)
+{
+    md_t *candidate, *m;
+    apr_size_t cand_n, n;
+    int i;
+    
+    candidate = md_get_by_name(mds, md->name);
+    if (!candidate) {
+        /* try to find an instance that contains all domain names from md */ 
+        for (i = 0; i < mds->nelts; ++i) {
+            m = APR_ARRAY_IDX(mds, i, md_t *);
+            if (md_contains_domains(m, md)) {
+                return m;
+            }
+        }
+        /* no matching name and no md in the list has all domains.
+         * We consider that managed domain as closest match that contains at least one
+         * domain name from md, ONLY if there is no other one that also has.
+         */
+        cand_n = 0;
+        for (i = 0; i < mds->nelts; ++i) {
+            m = APR_ARRAY_IDX(mds, i, md_t *);
+            n = md_common_name_count(md, m);
+            if (n > cand_n) {
+                candidate = m;
+                cand_n = n;
+            }
+        }
+    }
+    return candidate;
+}
+
+typedef struct {
+    apr_pool_t *p;
+    apr_array_header_t *master_mds;
+    apr_array_header_t *store_names;
+    apr_array_header_t *maybe_new_mds;
+    apr_array_header_t *new_mds;
+    apr_array_header_t *unassigned_mds;
+} sync_ctx_v2;
+
+static int iter_add_name(void *baton, const char *dir, const char *name, 
+                         md_store_vtype_t vtype, void *value, apr_pool_t *ptemp)
+{
+    sync_ctx_v2 *ctx = baton;
+    
+    (void)dir;
+    (void)value;
+    (void)ptemp;
+    (void)vtype;
+    APR_ARRAY_PUSH(ctx->store_names, const char*) = apr_pstrdup(ctx->p, name);
+    return APR_SUCCESS;
+}
+
+/* A better scaling version:
+ *  1. The consistency of the MDs in 'master_mds' has already been verified. E.g.
+ *     that no domain lists overlap etc.
+ *  2. All MD storage that exists will be overwritten by the settings we have.
+ *     And "exists" meaning that "store/MD_SG_DOMAINS/name" exists.
+ *  3. For MDs that have no directory in "store/MD_SG_DOMAINS", we load all MDs
+ *     outside the list of known names from MD_SG_DOMAINS. In this list, we
+ *     look for the MD with the most domain overlap. 
+ *      - if we find it, we assume this is a rename and move the old MD to the new name.
+ *      - if not, MD is completely new.
+ *  4. Any MD in store that does not match the "master_mds" will just be left as is. 
  */
-apr_status_t md_reg_sync(md_reg_t *reg, apr_pool_t *p, apr_pool_t *ptemp, 
-                         apr_array_header_t *master_mds) 
+apr_status_t md_reg_sync_start(md_reg_t *reg, apr_array_header_t *master_mds, apr_pool_t *p) 
 {
-    sync_ctx ctx;
+    sync_ctx_v2 ctx;
     apr_status_t rv;
-
-    ctx.p = ptemp;
-    ctx.store_mds = apr_array_make(ptemp, 100, sizeof(md_t *));
-    rv = read_store_mds(reg, &ctx);
+    md_t *md, *oldmd;
+    const char *name;
+    int i, idx;
     
-    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, 
-                  "sync: found %d mds in store", ctx.store_mds->nelts);
-    if (reg->domains_frozen) return APR_EACCES; 
-    if (APR_SUCCESS == rv) {
-        int i, fields;
-        md_t *md, *config_md, *smd, *omd;
-        const char *common;
-        
-        for (i = 0; i < master_mds->nelts; ++i) {
-            md = APR_ARRAY_IDX(master_mds, i, md_t *);
-            
-            /* find the store md that is closest match for the configured md */
-            smd = md_find_closest_match(ctx.store_mds, md);
-            if (smd) {
-                fields = 0;
-                
-                /* Did the name change? This happens when the order of names in configuration
-                 * changes or when the first name is removed. Use the name from the store, but
-                 * remember the original one. We try to align this later on. */
-                if (strcmp(md->name, smd->name)) {
-                    md->configured_name = md->name;
-                    md->name = apr_pstrdup(p, smd->name);
-                }
-                
-                /* Make the stored domain list *exactly* the same, even if
-                 * someone only changed upper/lowercase, we'd like to persist that. */
-                if (!md_equal_domains(md, smd, 1)) {
-                    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, 
-                                 "%s: domains changed", smd->name);
-                    smd->domains = md_array_str_clone(ptemp, md->domains);
-                    fields |= MD_UPD_DOMAINS;
-                }
-                
-                /* Look for other store mds which have domains now being part of smd */
-                while (APR_SUCCESS == rv && (omd = md_get_by_dns_overlap(ctx.store_mds, md))) {
-                    /* find the name now duplicate */
-                    common = md_common_name(md, omd);
-                    assert(common);
-                    
-                    /* Is this md still configured or has it been abandoned in the config? */
-                    config_md = md_get_by_name(master_mds, omd->name);
-                    if (config_md && md_contains(config_md, common, 0)) {
-                        /* domain used in two configured mds, not allowed */
-                        rv = APR_EINVAL;
-                        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, 
-                                      "domain %s used in md %s and %s", 
-                                      common, md->name, omd->name);
-                    }
-                    else {
-                        /* remove it from the other md and update store, or, if it
-                         * is now empty, move it into the archive */
-                        omd->domains = md_array_str_remove(ptemp, omd->domains, common, 0);
-                        if (apr_is_empty_array(omd->domains)) {
-                            md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, p, 
-                                          "All domains of the MD %s have moved elsewhere, "
-                                          " moving it to the archive. ", omd->name);
-                            md_reg_remove(reg, ptemp, omd->name, 1); /* best effort */
-                        }
-                        else {
-                            rv = update_md(reg, ptemp, omd->name, omd, MD_UPD_DOMAINS, 0);
-                        }
-                    }
-                }
-
-                /* If no CA url/proto is configured for the MD, take the default */
-                if (!md->ca_url) {
-                    md->ca_url = MD_ACME_DEF_URL;
-                    md->ca_proto = MD_PROTO_ACME; 
-                }
-                
-                if (MD_SVAL_UPDATE(md, smd, ca_url)) {
-                    smd->ca_url = md->ca_url;
-                    fields |= MD_UPD_CA_URL;
-                }
-                if (MD_SVAL_UPDATE(md, smd, ca_proto)) {
-                    smd->ca_proto = md->ca_proto;
-                    fields |= MD_UPD_CA_PROTO;
-                }
-                if (MD_SVAL_UPDATE(md, smd, ca_agreement)) {
-                    smd->ca_agreement = md->ca_agreement;
-                    fields |= MD_UPD_AGREEMENT;
-                }
-                if (MD_VAL_UPDATE(md, smd, transitive)) {
-                    smd->transitive = md->transitive;
-                    fields |= MD_UPD_TRANSITIVE;
-                }
-                if (MD_VAL_UPDATE(md, smd, renew_mode)) {
-                    smd->renew_mode = md->renew_mode;
-                    fields |= MD_UPD_DRIVE_MODE;
-                }
-                if (!apr_is_empty_array(md->contacts) 
-                    && !md_array_str_eq(md->contacts, smd->contacts, 0)) {
-                    smd->contacts = md->contacts;
-                    fields |= MD_UPD_CONTACTS;
-                }
-                if (!md_timeslice_eq(md->renew_window, smd->renew_window)) {
-                    smd->renew_window = md->renew_window;
-                    fields |= MD_UPD_RENEW_WINDOW;
-                }
-                if (!md_timeslice_eq(md->warn_window, smd->warn_window)) {
-                    smd->warn_window = md->warn_window;
-                    fields |= MD_UPD_WARN_WINDOW;
-                }
-                if (md->ca_challenges) {
-                    md->ca_challenges = md_array_str_compact(p, md->ca_challenges, 0);
-                    if (!smd->ca_challenges 
-                        || !md_array_str_eq(md->ca_challenges, smd->ca_challenges, 0)) {
-                        smd->ca_challenges = apr_array_copy(ptemp, md->ca_challenges);
-                        fields |= MD_UPD_CA_CHALLENGES;
-                    }
-                }
-                else if (smd->ca_challenges) {
-                    smd->ca_challenges = NULL;
-                    fields |= MD_UPD_CA_CHALLENGES;
-                }
-                if (!md_pkey_spec_eq(md->pkey_spec, smd->pkey_spec)) {
-                    fields |= MD_UPD_PKEY_SPEC;
-                    smd->pkey_spec = NULL;
-                    if (md->pkey_spec) {
-                        smd->pkey_spec = apr_pmemdup(p, md->pkey_spec, sizeof(md_pkey_spec_t));
-                    }
-                }
-                if (MD_VAL_UPDATE(md, smd, require_https)) {
-                    smd->require_https = md->require_https;
-                    fields |= MD_UPD_REQUIRE_HTTPS;
-                }
-                if (MD_VAL_UPDATE(md, smd, must_staple)) {
-                    smd->must_staple = md->must_staple;
-                    fields |= MD_UPD_MUST_STAPLE;
-                }
-                if (!md_array_str_eq(md->acme_tls_1_domains, smd->acme_tls_1_domains, 0)) {
-                    smd->acme_tls_1_domains = md->acme_tls_1_domains;
-                    fields |= MD_UPD_PROTO;
-                }
-                
-                if (fields) {
-                    rv = update_md(reg, ptemp, smd->name, smd, fields, 0);
-                    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "md %s updated", smd->name);
-                }
-            }
-            else {
-                /* new managed domain */
-                /* If no CA url/proto is configured for the MD, take the default */
-                if (!md->ca_url) {
-                    md->ca_url = MD_ACME_DEF_URL;
-                    md->ca_proto = MD_PROTO_ACME; 
-                }
-                rv = add_md(reg, md, ptemp, 0);
-                md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "new md %s added", md->name);
+    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, "sync MDs, start");
+     
+    ctx.p = p;
+    ctx.master_mds = master_mds;
+    ctx.store_names = apr_array_make(p, master_mds->nelts + 100, sizeof(const char*));
+    ctx.maybe_new_mds = apr_array_make(p, master_mds->nelts, sizeof(md_t*));
+    ctx.new_mds = apr_array_make(p, master_mds->nelts, sizeof(md_t*));
+    ctx.unassigned_mds = apr_array_make(p, master_mds->nelts, sizeof(md_t*));
+    
+    rv = md_store_iter_names(iter_add_name, &ctx, reg->store, p, MD_SG_DOMAINS, "*");
+    if (APR_SUCCESS != rv) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "listing existing store MD names"); 
+        goto leave;
+    }
+    
+    /* Get all MDs that are not already present in store */
+    for (i = 0; i < ctx.master_mds->nelts; ++i) {
+        md = APR_ARRAY_IDX(ctx.master_mds, i, md_t*);
+        idx = md_array_str_index(ctx.store_names, md->name, 0, 1);
+        if (idx < 0) {
+            APR_ARRAY_PUSH(ctx.maybe_new_mds, md_t*) = md;
+            md_array_remove_at(ctx.store_names, idx);
+        }
+    }
+    
+    if (ctx.maybe_new_mds->nelts == 0) goto leave; /* none new */
+    if (ctx.store_names->nelts == 0) goto leave;   /* all new */
+    
+    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, 
+                  "sync MDs, %d potentially new MDs detected, looking for renames among "
+                  "the %d unassigned store domains", (int)ctx.maybe_new_mds->nelts,
+                  (int)ctx.store_names->nelts);
+    for (i = 0; i < ctx.store_names->nelts; ++i) {
+        name = APR_ARRAY_IDX(ctx.store_names, i, const char*);
+        if (APR_SUCCESS == md_load(reg->store, MD_SG_DOMAINS, name, &md, p)) {
+            APR_ARRAY_PUSH(ctx.unassigned_mds, md_t*) = md;
+        } 
+    }
+    
+    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, 
+                  "sync MDs, %d MDs maybe new, checking store", (int)ctx.maybe_new_mds->nelts);
+    for (i = 0; i < ctx.maybe_new_mds->nelts; ++i) {
+        md = APR_ARRAY_IDX(ctx.maybe_new_mds, i, md_t*);
+        oldmd = find_closest_match(ctx.unassigned_mds, md);
+        if (oldmd) {
+            /* found the rename, move the domains and possible staging directory */
+            md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, 
+                          "sync MDs, found MD %s under previous name %s", md->name, oldmd->name);
+            rv = md_store_rename(reg->store, p, MD_SG_DOMAINS, oldmd->name, md->name);
+            if (APR_SUCCESS != rv) {
+                md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, 
+                              "sync MDs, renaming MD %s to %s failed", oldmd->name, md->name);
+                /* ignore it? */
             }
+            md_store_rename(reg->store, p, MD_SG_STAGING, oldmd->name, md->name);
+            md_array_remove(ctx.unassigned_mds, oldmd);
+        }
+        else {
+            APR_ARRAY_PUSH(ctx.new_mds, md_t*) = md;
         }
     }
-    else {
-        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "loading mds");
+
+leave:
+    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, 
+                  "sync MDs, %d existing, %d moved, %d new.", 
+                  (int)ctx.master_mds->nelts - ctx.maybe_new_mds->nelts,
+                  (int)ctx.maybe_new_mds->nelts - ctx.new_mds->nelts,
+                  (int)ctx.new_mds->nelts);
+    return rv;
+}
+
+/** 
+ * Finish synching an MD with the store. 
+ * 1. if there are changed properties (or if the MD is new), save it.
+ * 2. read any existing certificate and init the state of the memory MD
+ */
+apr_status_t md_reg_sync_finish(md_reg_t *reg, md_t *md, apr_pool_t *p, apr_pool_t *ptemp)
+{
+    md_t *old;
+    apr_status_t rv;
+    int changed = 1;
+    
+    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, ptemp, "sync MDs, finish start");
+    
+    if (!md->ca_url) {
+        md->ca_url = MD_ACME_DEF_URL;
+        md->ca_proto = MD_PROTO_ACME; 
     }
     
+    rv = state_init(reg, ptemp, md);
+    if (APR_SUCCESS != rv) goto leave;
+    
+    if (APR_SUCCESS == md_load(reg->store, MD_SG_DOMAINS, md->name, &old, ptemp)) {
+        /* Some parts are kept from old, lacking new values */
+        if ((!md->contacts || apr_is_empty_array(md->contacts)) && old->contacts) {
+            md->contacts = md_array_str_clone(p, old->contacts);
+        } 
+        if (md->ca_challenges && old->ca_challenges) {
+            if (!md_array_str_eq(md->ca_challenges, old->ca_challenges, 0)) {
+                md->ca_challenges = md_array_str_compact(p, md->ca_challenges, 0);
+            }
+        }
+        if (!md->ca_account && old->ca_account) {
+            md->ca_account = apr_pstrdup(p, old->ca_account);
+        }
+        
+        /* if everything remains the same, spare the write back */
+        if (!MD_VAL_UPDATE(md, old, state)
+            && !MD_SVAL_UPDATE(md, old, ca_url)
+            && !MD_SVAL_UPDATE(md, old, ca_proto)
+            && !MD_SVAL_UPDATE(md, old, ca_agreement)
+            && !MD_VAL_UPDATE(md, old, transitive)
+            && md_equal_domains(md, old, 1)
+            && !MD_VAL_UPDATE(md, old, renew_mode)
+            && md_timeslice_eq(md->renew_window, old->renew_window)
+            && md_timeslice_eq(md->warn_window, old->warn_window)
+            && md_pkey_spec_eq(md->pkey_spec, old->pkey_spec)
+            && !MD_VAL_UPDATE(md, old, require_https)
+            && !MD_VAL_UPDATE(md, old, must_staple)
+            && md_array_str_eq(md->acme_tls_1_domains, old->acme_tls_1_domains, 0)
+            && !MD_VAL_UPDATE(md, old, stapling)
+            && md_array_str_eq(md->contacts, old->contacts, 0)
+            && md_array_str_eq(md->ca_challenges, old->ca_challenges, 0)) {
+            changed = 0;
+        }
+    }
+    if (changed) {
+        rv = md_save(reg->store, ptemp, MD_SG_DOMAINS, md, 0);
+    }
+leave:
+    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, ptemp, "sync MDs, finish done");
     return rv;
 }
 
@@ -970,11 +965,14 @@ static apr_status_t run_init(void *baton
     md_proto_driver_t *driver, **pdriver;
     md_result_t *result;
     apr_table_t *env;
+    const char *s;
+    int preload;
     
     (void)p;
     va_start(ap, p);
     pdriver = va_arg(ap, md_proto_driver_t **);
     md = va_arg(ap, const md_t *);
+    preload = va_arg(ap, int);
     env = va_arg(ap, apr_table_t *);
     result = va_arg(ap, md_result_t *); 
     va_end(ap);
@@ -989,6 +987,11 @@ static apr_status_t run_init(void *baton
     driver->md = md;
     driver->can_http = reg->can_http;
     driver->can_https = reg->can_https;
+    
+    s = apr_table_get(driver->env, MD_KEY_ACTIVATION_DELAY);
+    if (!s || APR_SUCCESS != md_duration_parse(&driver->activation_delay, s, "d")) {
+        driver->activation_delay = apr_time_from_sec(MD_SECS_PER_DAY);
+    }
 
     if (!md->ca_proto) {
         md_result_printf(result, APR_EGENERAL, "CA protocol is not defined"); 
@@ -1002,7 +1005,12 @@ static apr_status_t run_init(void *baton
         goto leave;
     }
     
-    result->status = driver->proto->init(driver, result);
+    if (preload) {
+        result->status = driver->proto->init_preload(driver, result);
+    }
+    else {
+        result->status = driver->proto->init(driver, result);
+    }
 
 leave:
     if (APR_SUCCESS != result->status) {
@@ -1027,7 +1035,7 @@ static apr_status_t run_test_init(void *
     env = va_arg(ap, apr_table_t *);
     result = va_arg(ap, md_result_t *); 
 
-    return run_init(baton, ptemp, &driver, md, env, result, NULL);
+    return run_init(baton, ptemp, &driver, md, 0, env, result, NULL);
 }
 
 apr_status_t md_reg_test_init(md_reg_t *reg, const md_t *md, struct apr_table_t *env, 
@@ -1051,7 +1059,7 @@ static apr_status_t run_renew(void *bato
     reset = va_arg(ap, int); 
     result = va_arg(ap, md_result_t *); 
 
-    rv = run_init(baton, ptemp, &driver, md, env, result, NULL);
+    rv = run_init(baton, ptemp, &driver, md, 0, env, result, NULL);
     if (APR_SUCCESS == rv) { 
         md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, ptemp, "%s: run staging", md->name);
         driver->reset = reset;
@@ -1089,11 +1097,11 @@ static apr_status_t run_load_staging(voi
     result =  va_arg(ap, md_result_t*);
     
     if (APR_STATUS_IS_ENOENT(rv = md_load(reg->store, MD_SG_STAGING, md->name, NULL, ptemp))) {
-        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, rv, ptemp, "%s: nothing staged", md->name);
+        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, 0, ptemp, "%s: nothing staged", md->name);
         goto out;
     }
     
-    rv = run_init(baton, ptemp, &driver, md, env, result, NULL);
+    rv = run_init(baton, ptemp, &driver, md, 1, env, result, NULL);
     if (APR_SUCCESS != rv) goto out;
     
     apr_hash_set(reg->certs, md->name, (apr_ssize_t)strlen(md->name), NULL);
@@ -1102,9 +1110,10 @@ static apr_status_t run_load_staging(voi
     if (APR_SUCCESS != rv) goto out;
 
     /* If we had a job saved in STAGING, copy it over too */
-    job = md_job_make(ptemp, md->name);
-    if (APR_SUCCESS == md_job_load(job, reg, MD_SG_STAGING, ptemp)) {
-        md_job_save(job, reg, MD_SG_TMP, NULL, ptemp);
+    job = md_reg_job_make(reg, md->name, ptemp);
+    if (APR_SUCCESS == md_job_load(job)) {
+        md_job_set_group(job, MD_SG_TMP);
+        md_job_save(job, NULL, ptemp);
     }
     
     /* swap */
@@ -1118,9 +1127,13 @@ static apr_status_t run_load_staging(voi
     md_store_purge(reg->store, p, MD_SG_STAGING, md->name);
     md_store_purge(reg->store, p, MD_SG_CHALLENGES, md->name);
     md_result_set(result, APR_SUCCESS, "new certificate successfully saved in domains");
-
+    md_job_notify(job, "installed", result);
+    if (job->dirty) md_job_save(job, result, ptemp);
+    
 out:
-    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, ptemp, "%s: load done", md->name);
+    if (!APR_STATUS_IS_ENOENT(rv)) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, rv, ptemp, "%s: load done", md->name);
+    }
     return rv;
 }
 
@@ -1150,12 +1163,27 @@ leave:
     return rv;
 }
 
-void md_reg_set_renew_window_default(md_reg_t *reg, const md_timeslice_t *renew_window)
+void md_reg_set_renew_window_default(md_reg_t *reg, md_timeslice_t *renew_window)
 {
-    reg->renew_window = renew_window;
+    *reg->renew_window = *renew_window;
 }
 
-void md_reg_set_warn_window_default(md_reg_t *reg, const md_timeslice_t *warn_window)
+void md_reg_set_warn_window_default(md_reg_t *reg, md_timeslice_t *warn_window)
 {
-    reg->warn_window = warn_window;
+    *reg->warn_window = *warn_window;
+}
+
+void md_reg_set_notify_cb(md_reg_t *reg, md_job_notify_cb *cb, void *baton)
+{
+    reg->notify = cb;
+    reg->notify_ctx = baton;
+}
+
+md_job_t *md_reg_job_make(md_reg_t *reg, const char *mdomain, apr_pool_t *p)
+{
+    md_job_t *job;
+    
+    job = md_job_make(p, reg->store, MD_SG_STAGING, mdomain);
+    md_job_set_notify_cb(job, reg->notify, reg->notify_ctx);
+    return job;
 }

Modified: httpd/httpd/branches/2.4.x/modules/md/md_reg.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_reg.h?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_reg.h (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_reg.h Fri Oct 25 13:27:12 2019
@@ -19,11 +19,12 @@
 
 struct apr_hash_t;
 struct apr_array_header_t;
-struct md_store_t;
 struct md_pkey_t;
 struct md_cert_t;
 struct md_result_t;
 
+#include "md_store.h"
+
 /**
  * A registry for managed domains with a md_store_t as persistence.
  *
@@ -33,10 +34,10 @@ typedef struct md_reg_t md_reg_t;
 /**
  * Create the MD registry, using the pool and store.
  */
-apr_status_t md_reg_create(md_reg_t **preg, apr_pool_t *pm, struct md_store_t *store,
+apr_status_t md_reg_create(md_reg_t **preg, apr_pool_t *pm, md_store_t *store,
                            const char *proxy_url);
 
-struct md_store_t *md_reg_store_get(md_reg_t *reg);
+md_store_t *md_reg_store_get(md_reg_t *reg);
 
 apr_status_t md_reg_set_props(md_reg_t *reg, apr_pool_t *p, int can_http, int can_https);
 
@@ -66,11 +67,6 @@ md_t *md_reg_find_overlap(md_reg_t *reg,
 md_t *md_reg_get(md_reg_t *reg, const char *name, apr_pool_t *p);
 
 /**
- * Re-compute the state of the MD, given current store contents.
- */
-apr_status_t md_reg_reinit_state(md_reg_t *reg, md_t *md, apr_pool_t *p);
-
-/**
  * Callback invoked for every md in the registry. If 0 is returned, iteration stops.
  */
 typedef int md_reg_do_cb(void *baton, md_reg_t *reg, md_t *md);
@@ -85,21 +81,22 @@ int md_reg_do(md_reg_do_cb *cb, void *ba
 /**
  * Bitmask for fields that are updated.
  */
-#define MD_UPD_DOMAINS       0x0001
-#define MD_UPD_CA_URL        0x0002
-#define MD_UPD_CA_PROTO      0x0004
-#define MD_UPD_CA_ACCOUNT    0x0008
-#define MD_UPD_CONTACTS      0x0010
-#define MD_UPD_AGREEMENT     0x0020
-#define MD_UPD_DRIVE_MODE    0x0080
-#define MD_UPD_RENEW_WINDOW  0x0100
-#define MD_UPD_CA_CHALLENGES 0x0200
-#define MD_UPD_PKEY_SPEC     0x0400
-#define MD_UPD_REQUIRE_HTTPS 0x0800
-#define MD_UPD_TRANSITIVE    0x1000
-#define MD_UPD_MUST_STAPLE   0x2000
-#define MD_UPD_PROTO         0x4000
-#define MD_UPD_WARN_WINDOW   0x8000
+#define MD_UPD_DOMAINS       0x00001
+#define MD_UPD_CA_URL        0x00002
+#define MD_UPD_CA_PROTO      0x00004
+#define MD_UPD_CA_ACCOUNT    0x00008
+#define MD_UPD_CONTACTS      0x00010
+#define MD_UPD_AGREEMENT     0x00020
+#define MD_UPD_DRIVE_MODE    0x00080
+#define MD_UPD_RENEW_WINDOW  0x00100
+#define MD_UPD_CA_CHALLENGES 0x00200
+#define MD_UPD_PKEY_SPEC     0x00400
+#define MD_UPD_REQUIRE_HTTPS 0x00800
+#define MD_UPD_TRANSITIVE    0x01000
+#define MD_UPD_MUST_STAPLE   0x02000
+#define MD_UPD_PROTO         0x04000
+#define MD_UPD_WARN_WINDOW   0x08000
+#define MD_UPD_STAPLING      0x10000
 #define MD_UPD_ALL           0x7FFFFFFF
 
 /**
@@ -107,7 +104,8 @@ int md_reg_do(md_reg_do_cb *cb, void *ba
  * values from the given md, all other values remain unchanged.
  */
 apr_status_t md_reg_update(md_reg_t *reg, apr_pool_t *p, 
-                           const char *name, const md_t *md, int fields);
+                           const char *name, const md_t *md, 
+                           int fields, int check_consistency);
 
 /**
  * Get the chain of public certificates of the managed domain md, starting with the cert
@@ -127,8 +125,13 @@ apr_status_t md_reg_get_cred_files(const
 /**
  * Synchronise the give master mds with the store.
  */
-apr_status_t md_reg_sync(md_reg_t *reg, apr_pool_t *p, apr_pool_t *ptemp, 
-                         apr_array_header_t *master_mds);
+apr_status_t md_reg_sync_start(md_reg_t *reg, apr_array_header_t *master_mds, apr_pool_t *p);
+
+/**
+ * Re-compute the state of the MD, given current store contents.
+ */
+apr_status_t md_reg_sync_finish(md_reg_t *reg, md_t *md, apr_pool_t *p, apr_pool_t *ptemp);
+
 
 apr_status_t md_reg_remove(md_reg_t *reg, apr_pool_t *p, const char *name, int archive);
 
@@ -164,6 +167,12 @@ apr_status_t md_reg_freeze_domains(md_re
 int md_reg_should_renew(md_reg_t *reg, const md_t *md, apr_pool_t *p);
 
 /**
+ * Return the timestamp when the certificate should be renewed. A value of 0
+ * indicates that that renewal is not configured (see renew_mode).
+ */
+apr_time_t md_reg_renew_at(md_reg_t *reg, const md_t *md, apr_pool_t *p);
+
+/**
  * Return if a warning should be issued about the certificate expiration. 
  * This applies the configured warn window to the remaining lifetime of the 
  * current certiciate. If no certificate is present, this returns 0.
@@ -188,17 +197,19 @@ struct md_proto_driver_t {
     struct apr_table_t *env;
 
     md_reg_t *reg;
-    struct md_store_t *store;
+    md_store_t *store;
     const char *proxy_url;
     const md_t *md;
 
     int can_http;
     int can_https;
     int reset;
+    apr_interval_time_t activation_delay;
 };
 
 typedef apr_status_t md_proto_init_cb(md_proto_driver_t *driver, struct md_result_t *result);
 typedef apr_status_t md_proto_renew_cb(md_proto_driver_t *driver, struct md_result_t *result);
+typedef apr_status_t md_proto_init_preload_cb(md_proto_driver_t *driver, struct md_result_t *result);
 typedef apr_status_t md_proto_preload_cb(md_proto_driver_t *driver, 
                                          md_store_group_t group, struct md_result_t *result);
 
@@ -206,6 +217,7 @@ struct md_proto_t {
     const char *protocol;
     md_proto_init_cb *init;
     md_proto_renew_cb *renew;
+    md_proto_init_preload_cb *init_preload;
     md_proto_preload_cb *preload;
 };
 
@@ -239,7 +251,10 @@ apr_status_t md_reg_renew(md_reg_t *reg,
 apr_status_t md_reg_load_staging(md_reg_t *reg, const md_t *md, struct apr_table_t *env, 
                                  struct md_result_t *result, apr_pool_t *p);
 
-void md_reg_set_renew_window_default(md_reg_t *reg, const md_timeslice_t *renew_window);
-void md_reg_set_warn_window_default(md_reg_t *reg, const md_timeslice_t *warn_window);
+void md_reg_set_renew_window_default(md_reg_t *reg, md_timeslice_t *renew_window);
+void md_reg_set_warn_window_default(md_reg_t *reg, md_timeslice_t *warn_window);
+
+void md_reg_set_notify_cb(md_reg_t *reg, md_job_notify_cb *cb, void *baton);
+struct md_job_t *md_reg_job_make(md_reg_t *reg, const char *mdomain, apr_pool_t *p);
 
 #endif /* mod_md_md_reg_h */

Modified: httpd/httpd/branches/2.4.x/modules/md/md_result.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_result.c?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_result.c (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_result.c Fri Oct 25 13:27:12 2019
@@ -42,14 +42,15 @@ md_result_t *md_result_make(apr_pool_t *
     
     result = apr_pcalloc(p, sizeof(*result));
     result->p = p;
+    result->md_name = MD_OTHER;
     result->status = status;
     return result;
 }
 
-md_result_t *md_result_md_make(apr_pool_t *p, const struct md_t *md)
+md_result_t *md_result_md_make(apr_pool_t *p, const char *md_name)
 {
     md_result_t *result = md_result_make(p, APR_SUCCESS);
-    result->md = md;
+    result->md_name = md_name;
     return result;
 }
 
@@ -74,6 +75,7 @@ void md_result_activity_setn(md_result_t
 {
     result->activity = activity;
     result->problem = result->detail = NULL;
+    result->subproblems = NULL;
     on_change(result);
 }
 
@@ -91,15 +93,18 @@ void md_result_set(md_result_t *result,
     result->status = status;
     result->problem = NULL;
     result->detail = detail? apr_pstrdup(result->p, detail) : NULL;
+    result->subproblems = NULL;
     on_change(result);
 }
 
 void md_result_problem_set(md_result_t *result, apr_status_t status,
-                           const char *problem, const char *detail)
+                           const char *problem, const char *detail, 
+                           const md_json_t *subproblems)
 {
     result->status = status;
     result->problem = dup_trim(result->p, problem);
     result->detail = apr_pstrdup(result->p, detail);
+    result->subproblems = subproblems? md_json_clone(result->p, subproblems) : NULL;
     on_change(result);
 }
 
@@ -114,6 +119,7 @@ void md_result_problem_printf(md_result_
     va_start(ap, fmt);
     result->detail = apr_pvsprintf(result->p, fmt, ap);
     va_end(ap);
+    result->subproblems = NULL;
     on_change(result);
 }
 
@@ -125,6 +131,7 @@ void md_result_printf(md_result_t *resul
     va_start(ap, fmt);
     result->detail = apr_pvsprintf(result->p, fmt, ap);
     va_end(ap);
+    result->subproblems = NULL;
     on_change(result);
 }
 
@@ -146,7 +153,7 @@ md_result_t*md_result_from_json(const st
     result->activity = md_json_dups(p, json, MD_KEY_ACTIVITY, NULL);
     s = md_json_dups(p, json, MD_KEY_VALID_FROM, NULL);
     if (s && *s) result->ready_at = apr_date_parse_rfc(s);
-
+    result->subproblems = md_json_dupj(p, json, MD_KEY_SUBPROBLEMS, NULL);
     return result;
 }
 
@@ -169,6 +176,9 @@ struct md_json_t *md_result_to_json(cons
         apr_rfc822_date(ts, result->ready_at);
         md_json_sets(ts, json, MD_KEY_VALID_FROM, NULL);
     }
+    if (result->subproblems) {
+        md_json_setj(result->subproblems, json, MD_KEY_SUBPROBLEMS, NULL);
+    }
     return json;
 }
 
@@ -200,6 +210,7 @@ void md_result_assign(md_result_t *dest,
    dest->detail = src->detail;
    dest->activity = src->activity;
    dest->ready_at = src->ready_at;
+   dest->subproblems = src->subproblems;
 }
 
 void md_result_dup(md_result_t *dest, const md_result_t *src)
@@ -209,17 +220,18 @@ void md_result_dup(md_result_t *dest, co
    dest->detail = src->detail? apr_pstrdup(dest->p, src->detail) : NULL; 
    dest->activity = src->activity? apr_pstrdup(dest->p, src->activity) : NULL; 
    dest->ready_at = src->ready_at;
+   dest->subproblems = src->subproblems? md_json_clone(dest->p, src->subproblems) : NULL;
    on_change(dest);
 }
 
-void md_result_log(md_result_t *result, int level)
+void md_result_log(md_result_t *result, unsigned int level)
 {
     if (md_log_is_level(result->p, (md_log_level_t)level)) {
         const char *sep = "";
         const char *msg = "";
         
-        if (result->md) {
-            msg = apr_psprintf(result->p, "md[%s]", result->md->name);
+        if (result->md_name) {
+            msg = apr_psprintf(result->p, "md[%s]", result->md_name);
             sep = " ";
         }
         if (result->activity) {
@@ -234,6 +246,11 @@ void md_result_log(md_result_t *result,
             msg = apr_psprintf(result->p, "%s%sdetail[%s]", msg, sep, result->detail);
             sep = " ";
         }
+        if (result->subproblems) {
+            msg = apr_psprintf(result->p, "%s%ssubproblems[%s]", msg, sep, 
+                md_json_writep(result->subproblems, result->p, MD_JSON_FMT_COMPACT));
+            sep = " ";
+        }
         md_log_perror(MD_LOG_MARK, (md_log_level_t)level, result->status, result->p, "%s", msg);
     }
 }

Modified: httpd/httpd/branches/2.4.x/modules/md/md_result.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_result.h?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_result.h (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_result.h Fri Oct 25 13:27:12 2019
@@ -26,10 +26,11 @@ typedef void md_result_change_cb(md_resu
 
 struct md_result_t {
     apr_pool_t *p;
-    const struct md_t *md;
+    const char *md_name;
     apr_status_t status;
     const char *problem;
     const char *detail;
+    const struct md_json_t *subproblems;
     const char *activity;
     apr_time_t ready_at;
     md_result_change_cb *on_change;
@@ -37,7 +38,7 @@ struct md_result_t {
 };
 
 md_result_t *md_result_make(apr_pool_t *p, apr_status_t status);
-md_result_t *md_result_md_make(apr_pool_t *p, const struct md_t *md);
+md_result_t *md_result_md_make(apr_pool_t *p, const char *md_name);
 void md_result_reset(md_result_t *result);
 
 void md_result_activity_set(md_result_t *result, const char *activity);
@@ -46,7 +47,8 @@ void md_result_activity_printf(md_result
 
 void md_result_set(md_result_t *result, apr_status_t status, const char *detail);
 void md_result_problem_set(md_result_t *result, apr_status_t status, 
-                           const char *problem, const char *detail);
+                           const char *problem, const char *detail, 
+                           const struct md_json_t *subproblems);
 void md_result_problem_printf(md_result_t *result, apr_status_t status,
                               const char *problem, const char *fmt, ...);
 
@@ -64,7 +66,7 @@ int md_result_cmp(const md_result_t *r1,
 void md_result_assign(md_result_t *dest, const md_result_t *src);
 void md_result_dup(md_result_t *dest, const md_result_t *src);
 
-void md_result_log(md_result_t *result, int level);
+void md_result_log(md_result_t *result, unsigned int level);
 
 void md_result_on_change(md_result_t *result, md_result_change_cb *cb, void *data);
 

Modified: httpd/httpd/branches/2.4.x/modules/md/md_status.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_status.c?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_status.c (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_status.c Fri Oct 25 13:27:12 2019
@@ -27,6 +27,7 @@
 #include "md.h"
 #include "md_crypt.h"
 #include "md_log.h"
+#include "md_ocsp.h"
 #include "md_store.h"
 #include "md_result.h"
 #include "md_reg.h"
@@ -40,16 +41,15 @@
 
 static apr_status_t status_get_cert_json(md_json_t **pjson, const md_cert_t *cert, apr_pool_t *p)
 {
-    char ts[APR_RFC822_DATE_LEN];
     const char *finger;
     apr_status_t rv = APR_SUCCESS;
+    md_timeperiod_t valid;
     md_json_t *json;
     
     json = md_json_create(p);
-    apr_rfc822_date(ts, md_cert_get_not_before(cert));
-    md_json_sets(ts, json, MD_KEY_VALID_FROM, NULL);
-    apr_rfc822_date(ts, md_cert_get_not_after(cert));
-    md_json_sets(ts, json, MD_KEY_VALID_UNTIL, NULL);
+    valid.start = md_cert_get_not_before(cert);
+    valid.end = md_cert_get_not_after(cert);
+    md_json_set_timeperiod(&valid, json, MD_KEY_VALID, NULL);
     md_json_sets(md_cert_get_serial_number(cert, p), json, MD_KEY_SERIAL, NULL);
     if (APR_SUCCESS != (rv = md_cert_to_sha256_fingerprint(&finger, cert, p))) goto leave;
     md_json_sets(finger, json, MD_KEY_SHA256_FINGERPRINT, NULL);
@@ -102,7 +102,7 @@ static apr_status_t get_staging_cert_jso
         rv = APR_SUCCESS;
         goto leave;
     }
-    else if (APR_SUCCESS != rv) {
+    else if (APR_SUCCESS != rv || certs->nelts == 0) {
         goto leave;
     }
     cert = APR_ARRAY_IDX(certs, 0, md_cert_t *);
@@ -112,52 +112,88 @@ leave:
     return rv;
 }
 
-static apr_status_t job_loadj(md_json_t **pjson, const char *name, 
-                              struct md_reg_t *reg, apr_pool_t *p)
+static apr_status_t job_loadj(md_json_t **pjson, md_store_group_t group, const char *name, 
+                              struct md_reg_t *reg, int with_log, apr_pool_t *p)
 {
+    apr_status_t rv;
+    
     md_store_t *store = md_reg_store_get(reg);
-    return md_store_load_json(store, MD_SG_STAGING, name, MD_FN_JOB, pjson, p);
+    rv = md_store_load_json(store, group, name, MD_FN_JOB, pjson, p);
+    if (APR_SUCCESS == rv && !with_log) md_json_del(*pjson, MD_KEY_LOG, NULL);
+    return rv;
 }
 
-apr_status_t md_status_get_md_json(md_json_t **pjson, const md_t *md, 
-                                   md_reg_t *reg, apr_pool_t *p)
+static apr_status_t status_get_md_json(md_json_t **pjson, const md_t *md, 
+                                       md_reg_t *reg, md_ocsp_reg_t *ocsp, 
+                                       int with_logs, apr_pool_t *p)
 {
     md_json_t *mdj, *jobj, *certj;
     int renew;
     const md_pubcert_t *pubcert;
-    const md_cert_t *cert;
+    const md_cert_t *cert = NULL;
+    md_ocsp_cert_stat_t cert_stat;
+    md_timeperiod_t ocsp_valid; 
     apr_status_t rv = APR_SUCCESS;
+    apr_time_t renew_at;
 
     mdj = md_to_json(md, p);
     if (APR_SUCCESS == md_reg_get_pubcert(&pubcert, reg, md, p)) {
         cert = APR_ARRAY_IDX(pubcert->certs, 0, const md_cert_t*);
         if (APR_SUCCESS != (rv = status_get_cert_json(&certj, cert, p))) goto leave;
+        if (md->stapling && ocsp) {
+            rv = md_ocsp_get_meta(&cert_stat, &ocsp_valid, ocsp, cert, p, md);
+            if (APR_SUCCESS == rv) {
+                md_json_sets(md_ocsp_cert_stat_name(cert_stat), certj, MD_KEY_OCSP, MD_KEY_STATUS, NULL);
+                md_json_set_timeperiod(&ocsp_valid, certj, MD_KEY_OCSP, MD_KEY_VALID, NULL);
+            }
+            else if (!APR_STATUS_IS_ENOENT(rv)) goto leave;
+            rv = APR_SUCCESS;
+            if (APR_SUCCESS == job_loadj(&jobj, MD_SG_OCSP, md->name, reg, with_logs, p)) {
+                md_json_setj(jobj, certj, MD_KEY_OCSP, MD_KEY_RENEWAL, NULL);
+            }
+        }
         md_json_setj(certj, mdj, MD_KEY_CERT, NULL);
+
+        renew_at = md_reg_renew_at(reg, md, p);
+        if (renew_at) {
+            md_json_set_time(renew_at, mdj, MD_KEY_RENEW_AT, NULL);
+        }
     }
     
+    md_json_setb(md->stapling, mdj, MD_KEY_STAPLING, NULL);
+    md_json_setb(md->watched, mdj, MD_KEY_WATCHED, NULL);
     renew = md_reg_should_renew(reg, md, p);
-    md_json_setb(renew, mdj, MD_KEY_RENEW, NULL);
     if (renew) {
-        rv = job_loadj(&jobj, md->name, reg, p);
+        md_json_setb(renew, mdj, MD_KEY_RENEW, NULL);
+        rv = job_loadj(&jobj, MD_SG_STAGING, md->name, reg, with_logs, p);
         if (APR_SUCCESS == rv) {
-            rv = get_staging_cert_json(&certj, p, reg, md);
-            if (APR_SUCCESS != rv) goto leave;
-            if (certj) md_json_setj(certj, jobj, MD_KEY_CERT, NULL);
+            if (APR_SUCCESS == get_staging_cert_json(&certj, p, reg, md)) {
+                md_json_setj(certj, jobj, MD_KEY_CERT, NULL);
+            }
             md_json_setj(jobj, mdj, MD_KEY_RENEWAL, NULL);
         }
         else if (APR_STATUS_IS_ENOENT(rv)) rv = APR_SUCCESS;
         else goto leave;
     }
+    
 leave:
-    *pjson = (APR_SUCCESS == rv)? mdj : NULL;
+    if (APR_SUCCESS != rv) {
+        md_json_setl(rv, mdj, MD_KEY_ERROR, NULL);
+    }
+    *pjson = mdj;
     return rv;
 }
 
+apr_status_t md_status_get_md_json(md_json_t **pjson, const md_t *md, 
+                                   md_reg_t *reg, md_ocsp_reg_t *ocsp, apr_pool_t *p)
+{
+    return status_get_md_json(pjson, md, reg, ocsp, 1, p);
+}
+
 apr_status_t md_status_get_json(md_json_t **pjson, apr_array_header_t *mds, 
-                                md_reg_t *reg, apr_pool_t *p) 
+                                md_reg_t *reg, md_ocsp_reg_t *ocsp, apr_pool_t *p) 
 {
     md_json_t *json, *mdj;
-    apr_status_t rv = APR_SUCCESS;
     const md_t *md;
     int i;
     
@@ -165,33 +201,41 @@ apr_status_t md_status_get_json(md_json_
     md_json_sets(MOD_MD_VERSION, json, MD_KEY_VERSION, NULL);
     for (i = 0; i < mds->nelts; ++i) {
         md = APR_ARRAY_IDX(mds, i, const md_t *);
-        rv = md_status_get_md_json(&mdj, md, reg, p);
-        if (APR_SUCCESS != rv) goto leave;
+        status_get_md_json(&mdj, md, reg, ocsp, 0, p);
         md_json_addj(mdj, json, MD_KEY_MDS, NULL);
     }
-leave:
-    *pjson = (APR_SUCCESS == rv)? json : NULL;
-    return rv;
+    *pjson = json;
+    return APR_SUCCESS;
 }
 
 /**************************************************************************************************/
 /* drive job persistence */
 
-md_job_t *md_job_make(apr_pool_t *p, const char *name)
+md_job_t *md_job_make(apr_pool_t *p, md_store_t *store,
+                      md_store_group_t group, const char *name)
 {
     md_job_t *job = apr_pcalloc(p, sizeof(*job));
-    job->name = apr_pstrdup(p, name);
+    job->group = group;
+    job->mdomain = apr_pstrdup(p, name);
+    job->store = store;
     job->p = p;
+    job->max_log = 128;
     return job;
 }
 
+void md_job_set_group(md_job_t *job, md_store_group_t group)
+{
+    job->group = group;
+}
+
 static void md_job_from_json(md_job_t *job, md_json_t *json, apr_pool_t *p)
 {
     const char *s;
     
     /* not good, this is malloced from a temp pool */
-    /*job->name = md_json_gets(json, MD_KEY_NAME, NULL);*/
+    /*job->mdomain = md_json_gets(json, MD_KEY_NAME, NULL);*/
     job->finished = md_json_getb(json, MD_KEY_FINISHED, NULL);
+    job->notified = md_json_getb(json, MD_KEY_NOTIFIED, NULL);
     s = md_json_dups(p, json, MD_KEY_NEXT_RUN, NULL);
     if (s && *s) job->next_run = apr_date_parse_rfc(s);
     s = md_json_dups(p, json, MD_KEY_LAST_RUN, NULL);
@@ -210,8 +254,9 @@ static void job_to_json(md_json_t *json,
 {
     char ts[APR_RFC822_DATE_LEN];
 
-    md_json_sets(job->name, json, MD_KEY_NAME, NULL);
+    md_json_sets(job->mdomain, json, MD_KEY_NAME, NULL);
     md_json_setb(job->finished, json, MD_KEY_FINISHED, NULL);
+    md_json_setb(job->notified, json, MD_KEY_NOTIFIED, NULL);
     if (job->next_run > 0) {
         apr_rfc822_date(ts, job->next_run);
         md_json_sets(ts, json, MD_KEY_NEXT_RUN, NULL);
@@ -232,31 +277,27 @@ static void job_to_json(md_json_t *json,
     if (job->log) md_json_setj(job->log, json, MD_KEY_LOG, NULL);
 }
 
-apr_status_t md_job_load(md_job_t *job, md_reg_t *reg, 
-                         md_store_group_t group, apr_pool_t *p)
+apr_status_t md_job_load(md_job_t *job)
 {
-    md_store_t *store = md_reg_store_get(reg);
     md_json_t *jprops;
     apr_status_t rv;
     
-    rv = md_store_load_json(store, group, job->name, MD_FN_JOB, &jprops, p);
+    rv = md_store_load_json(job->store, job->group, job->mdomain, MD_FN_JOB, &jprops, job->p);
     if (APR_SUCCESS == rv) {
-        md_job_from_json(job, jprops, p);
+        md_job_from_json(job, jprops, job->p);
     }
     return rv;
 }
 
-apr_status_t md_job_save(md_job_t *job, struct md_reg_t *reg, 
-                         md_store_group_t group, md_result_t *result, 
-                         apr_pool_t *p)
+apr_status_t md_job_save(md_job_t *job, md_result_t *result, apr_pool_t *p)
 {
-    md_store_t *store = md_reg_store_get(reg);
     md_json_t *jprops;
     apr_status_t rv;
     
     jprops = md_json_create(p);
     job_to_json(jprops, job, result, p);
-    rv = md_store_save_json(store, p, group, job->name, MD_FN_JOB, jprops, 0);
+    rv = md_store_save_json(job->store, p, job->group, job->mdomain, MD_FN_JOB, jprops, 0);
+    if (APR_SUCCESS == rv) job->dirty = 0;
     return rv;
 }
 
@@ -274,6 +315,8 @@ void md_job_log_append(md_job_t *job, co
     if (detail) md_json_sets(detail, entry, MD_KEY_DETAIL, NULL);
     if (!job->log) job->log = md_json_create(job->p);
     md_json_insertj(entry, 0, job->log, MD_KEY_ENTRIES, NULL);
+    md_json_limita(job->max_log, job->log, MD_KEY_ENTRIES, NULL);
+    job->dirty = 1;
 }
 
 typedef struct {
@@ -301,9 +344,10 @@ md_json_t *md_job_log_get_latest(md_job_
 
 {
     log_find_ctx ctx;
+
+    memset(&ctx, 0, sizeof(ctx));
     ctx.job = job;
     ctx.type = type;
-    memset(&ctx, 0, sizeof(ctx));
     if (job->log) md_json_itera(find_first_log_entry, &ctx, job->log, MD_KEY_ENTRIES, NULL);
     return ctx.entry;
 }
@@ -325,7 +369,7 @@ void  md_status_take_stock(md_json_t **p
                            md_reg_t *reg, apr_pool_t *p)
 {
     const md_t *md;
-    md_job_t job;
+    md_job_t *job;
     int i, complete, renewing, errored, ready, total;
     md_json_t *json;
 
@@ -339,14 +383,13 @@ void  md_status_take_stock(md_json_t **p
             case MD_S_INCOMPLETE:
                 if (md_reg_should_renew(reg, md, p)) {
                     ++renewing;
-                    memset(&job, 0, sizeof(job));
-                    job.name = md->name;
-                    if (APR_SUCCESS == md_job_load(&job, reg, MD_SG_STAGING, p)) {
-                        if (job.error_runs > 0 
-                            || (job.last_result && job.last_result->status != APR_SUCCESS)) {
+                    job = md_reg_job_make(reg, md->name, p);
+                    if (APR_SUCCESS == md_job_load(job)) {
+                        if (job->error_runs > 0 
+                            || (job->last_result && job->last_result->status != APR_SUCCESS)) {
                             ++errored;
                         }
-                        else if (job.finished) {
+                        else if (job->finished) {
                             ++ready;
                         }
                     }
@@ -362,3 +405,142 @@ void  md_status_take_stock(md_json_t **p
     md_json_setl(ready, json, MD_KEY_READY, NULL);
     *pjson = json;
 }
+
+typedef struct {
+    apr_pool_t *p;
+    md_job_t *job;
+    md_store_t *store;
+    md_result_t *last;
+    apr_time_t last_save;
+} md_job_result_ctx;
+
+static void job_result_update(md_result_t *result, void *data)
+{
+    md_job_result_ctx *ctx = data;
+    apr_time_t now;
+    const char *msg, *sep;
+    
+    if (md_result_cmp(ctx->last, result)) {
+        now = apr_time_now();
+        md_result_assign(ctx->last, result);
+        if (result->activity || result->problem || result->detail) {
+            msg = sep = "";
+            if (result->activity) {
+                msg = apr_psprintf(result->p, "%s", result->activity);
+                sep = ": ";
+            }
+            if (result->detail) {
+                msg = apr_psprintf(result->p, "%s%s%s", msg, sep, result->detail);
+                sep = ", ";
+            }
+            if (result->problem) {
+                msg = apr_psprintf(result->p, "%s%sproblem: %s", msg, sep, result->problem);
+                sep = " ";
+            }
+            md_job_log_append(ctx->job, "progress", NULL, msg);
+
+            if (ctx->store && apr_time_as_msec(now - ctx->last_save) > 500) {
+                md_job_save(ctx->job, result, ctx->p);
+                ctx->last_save = now;
+            }
+        }
+    }
+}
+
+static void job_observation_start(md_job_t *job, md_result_t *result, md_store_t *store)
+{
+    md_job_result_ctx *ctx;
+
+    if (job->observing) md_result_on_change(job->observing, NULL, NULL);
+    job->observing = result;
+    
+    ctx = apr_pcalloc(result->p, sizeof(*ctx));
+    ctx->p = result->p;
+    ctx->job = job;
+    ctx->store = store;
+    ctx->last = md_result_md_make(result->p, APR_SUCCESS);
+    md_result_assign(ctx->last, result);
+    md_result_on_change(result, job_result_update, ctx);
+}
+
+static void job_observation_end(md_job_t *job)
+{
+    if (job->observing) md_result_on_change(job->observing, NULL, NULL);
+    job->observing = NULL; 
+} 
+
+void md_job_start_run(md_job_t *job, md_result_t *result, md_store_t *store)
+{
+    job->fatal_error = 0;
+    job->last_run = apr_time_now();
+    job_observation_start(job, result, store);
+    md_job_log_append(job, "starting", NULL, NULL);
+}
+
+apr_time_t md_job_delay_on_errors(int err_count)
+{
+    apr_time_t delay = 0;
+    
+    if (err_count > 0) {
+        /* back off duration, depending on the errors we encounter in a row */
+        delay = apr_time_from_sec(5 << (err_count - 1));
+        if (delay > apr_time_from_sec(60*60)) {
+            delay = apr_time_from_sec(60*60);
+        }
+    }
+    return delay;
+}
+
+void md_job_end_run(md_job_t *job, md_result_t *result)
+{
+    if (APR_SUCCESS == result->status) {
+        job->finished = 1;
+        job->valid_from = result->ready_at;
+        job->error_runs = 0;
+        job->dirty = 1;
+        md_job_log_append(job, "finished", NULL, NULL);
+    }
+    else {
+        ++job->error_runs;
+        job->dirty = 1;
+        job->next_run = apr_time_now() + md_job_delay_on_errors(job->error_runs);
+    }
+    job_observation_end(job);
+}
+
+void md_job_retry_at(md_job_t *job, apr_time_t later)
+{
+    job->next_run = later;
+    job->dirty = 1;
+}
+
+apr_status_t md_job_notify(md_job_t *job, const char *reason, md_result_t *result)
+{
+    if (job->notify) return job->notify(job, reason, result, job->p, job->notify_ctx);
+    job->dirty = 1;
+    if (APR_SUCCESS == result->status) {
+        job->notified = 1;
+        job->error_runs = 0;
+    }
+    else {
+        ++job->error_runs;
+        job->next_run = apr_time_now() + md_job_delay_on_errors(job->error_runs);
+    }
+    return result->status;
+}
+
+void md_job_holler(md_job_t *job, const char *reason)
+{
+    md_result_t *result;
+    
+    if (job->notify) {
+        result = md_result_make(job->p, APR_SUCCESS);
+        job->notify(job, reason, result, job->p, job->notify_ctx);
+    }
+}
+
+void md_job_set_notify_cb(md_job_t *job, md_job_notify_cb *cb, void *baton)
+{
+    job->notify = cb;
+    job->notify_ctx = baton;
+}

Modified: httpd/httpd/branches/2.4.x/modules/md/md_status.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_status.h?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_status.h (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_status.h Fri Oct 25 13:27:12 2019
@@ -20,18 +20,23 @@
 struct md_json_t;
 struct md_reg_t;
 struct md_result_t;
+struct md_ocsp_reg_t;
+
+#include "md_store.h"
 
 /** 
  * Get a JSON summary of the MD and its status (certificates, jobs, etc.).
  */
 apr_status_t md_status_get_md_json(struct md_json_t **pjson, const md_t *md, 
-                                   struct md_reg_t *reg, apr_pool_t *p);
+                                   struct md_reg_t *reg, struct md_ocsp_reg_t *ocsp,
+                                   apr_pool_t *p);
 
 /** 
  * Get a JSON summary of all MDs and their status.
  */
 apr_status_t md_status_get_json(struct md_json_t **pjson, apr_array_header_t *mds, 
-                                struct md_reg_t *reg, apr_pool_t *p);
+                                struct md_reg_t *reg, struct md_ocsp_reg_t *ocsp,
+                                apr_pool_t *p);
 
 /**
  * Take stock of all MDs given for a short overview. The JSON returned
@@ -41,38 +46,50 @@ apr_status_t md_status_get_json(struct m
 void  md_status_take_stock(struct md_json_t **pjson, apr_array_header_t *mds, 
                            struct md_reg_t *reg, apr_pool_t *p);
 
+
 typedef struct md_job_t md_job_t;
+
 struct md_job_t {
-    const char *name;      /* Name of the MD this job is about */
+    md_store_group_t group;/* group where job is persisted */
+    const char *mdomain;   /* Name of the MD this job is about */
+    md_store_t *store;     /* store where it is persisted */
     apr_pool_t *p;     
     apr_time_t next_run;   /* Time this job wants to be processed next */
     apr_time_t last_run;   /* Time this job ran last (or 0) */
     struct md_result_t *last_result; /* Result from last run */
     int finished;          /* true iff the job finished successfully */
+    int notified;          /* true iff notifications were handled successfully */
     apr_time_t valid_from; /* at which time the finished job results become valid, 0 if immediate */
     int error_runs;        /* Number of errored runs of an unfinished job */
+    int fatal_error;       /* a fatal error is remedied by retrying */
     md_json_t *log;        /* array of log objects with minimum fields
-                              MD_KEY_WHEN (timestamp) and MD_KEY_TYPE (string) */   
+                              MD_KEY_WHEN (timestamp) and MD_KEY_TYPE (string) */
+    apr_size_t max_log;    /* max number of log entries, new ones replace oldest */
+    int dirty;
+    struct md_result_t *observing;
+    
+    md_job_notify_cb *notify;
+    void *notify_ctx;
 };
 
 /**
- * Create a new job instance for the given MD name. Job load/save will work
- * on the MD_SG_STAGING for the name.
+ * Create a new job instance for the given MD name. 
+ * Job load/save will work using the name.
  */
-md_job_t *md_job_make(apr_pool_t *p, const char *name);
+md_job_t *md_job_make(apr_pool_t *p, md_store_t *store, 
+                      md_store_group_t group, const char *name);
+
+void md_job_set_group(md_job_t *job, md_store_group_t group);
 
 /**
- * Update the job from storage in <group>/job->name.
+ * Update the job from storage in <group>/job->mdomain.
  */
-apr_status_t md_job_load(md_job_t *job, struct md_reg_t *reg, 
-                         md_store_group_t group, apr_pool_t *p);
+apr_status_t md_job_load(md_job_t *job);
 
 /**
- * Update storage from job in <group>/job->name.
+ * Update storage from job in <group>/job->mdomain.
  */
-apr_status_t md_job_save(md_job_t *job, struct md_reg_t *reg, 
-                         md_store_group_t group, struct md_result_t *result, 
-                         apr_pool_t *p);
+apr_status_t md_job_save(md_job_t *job, struct md_result_t *result, apr_pool_t *p);
 
 /**
  * Append to the job's log. Timestamp is automatically added.
@@ -94,4 +111,16 @@ md_json_t *md_job_log_get_latest(md_job_
  */
 apr_time_t md_job_log_get_time_of_latest(md_job_t *job, const char *type);
 
+void md_job_start_run(md_job_t *job, struct md_result_t *result, md_store_t *store);
+void md_job_end_run(md_job_t *job, struct md_result_t *result);
+void md_job_retry_at(md_job_t *job, apr_time_t later);
+
+/* Given the number of errors encountered, recommend a delay for the next attempt */
+apr_time_t md_job_delay_on_errors(int err_count);
+
+void md_job_set_notify_cb(md_job_t *job, md_job_notify_cb *cb, void *baton);
+apr_status_t md_job_notify(md_job_t *job, const char *reason, struct md_result_t *result);
+/* Same as notify but without checks on success and no change to job */
+void md_job_holler(md_job_t *job, const char *reason);
+
 #endif /* md_status_h */

Modified: httpd/httpd/branches/2.4.x/modules/md/md_store.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_store.c?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_store.c (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_store.c Fri Oct 25 13:27:12 2019
@@ -55,22 +55,18 @@ static const char *GROUP_NAME[] = {
     "staging",
     "archive",
     "tmp",
+    "ocsp",
     NULL
 };
 
-const char *md_store_group_name(int group)
+const char *md_store_group_name(unsigned int group)
 {
-    if ((size_t)group < sizeof(GROUP_NAME)/sizeof(GROUP_NAME[0])) {
+    if (group < sizeof(GROUP_NAME)/sizeof(GROUP_NAME[0])) {
         return GROUP_NAME[group];
     }
     return "UNKNOWN";
 }
 
-void md_store_destroy(md_store_t *store)
-{
-    if (store->destroy) store->destroy(store);
-}
-
 apr_status_t md_store_load(md_store_t *store, md_store_group_t group, 
                            const char *name, const char *aspect, 
                            md_store_vtype_t vtype, void **pdata, 
@@ -145,12 +141,33 @@ int md_store_is_newer(md_store_t *store,
     return store->is_newer(store, group1, group2, name, aspect, p);
 }
 
+apr_time_t md_store_get_modified(md_store_t *store, md_store_group_t group,  
+                                 const char *name, const char *aspect, apr_pool_t *p)
+{
+    return store->get_modified(store, group, name, aspect, p);
+}
+
 apr_status_t md_store_iter_names(md_store_inspect *inspect, void *baton, md_store_t *store, 
                                  apr_pool_t *p, md_store_group_t group, const char *pattern)
 {
     return store->iterate_names(inspect, baton, store, p, group, pattern);
 }
 
+apr_status_t md_store_remove_not_modified_since(md_store_t *store, apr_pool_t *p, 
+                                                apr_time_t modified,
+                                                md_store_group_t group, 
+                                                const char *name, 
+                                                const char *aspect)
+{
+    return store->remove_nms(store, p, modified, group, name, aspect);
+}
+
+apr_status_t md_store_rename(md_store_t *store, apr_pool_t *p,
+                             md_store_group_t group, const char *name, const char *to)
+{
+    return store->rename(store, p, group, name, to);
+}
+
 /**************************************************************************************************/
 /* convenience */
 

Modified: httpd/httpd/branches/2.4.x/modules/md/md_store.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_store.h?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_store.h (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_store.h Fri Oct 25 13:27:12 2019
@@ -21,107 +21,183 @@ struct apr_array_header_t;
 struct md_cert_t;
 struct md_pkey_t;
 
-typedef struct md_store_t md_store_t;
-
-typedef void md_store_destroy_cb(md_store_t *store);
-
-const char *md_store_group_name(int group);
-
-
-typedef apr_status_t md_store_load_cb(md_store_t *store, md_store_group_t group, 
-                                      const char *name, const char *aspect, 
-                                      md_store_vtype_t vtype, void **pvalue, 
-                                      apr_pool_t *p);
-typedef apr_status_t md_store_save_cb(md_store_t *store, apr_pool_t *p, md_store_group_t group, 
-                                      const char *name, const char *aspect, 
-                                      md_store_vtype_t vtype, void *value, 
-                                      int create);
-typedef apr_status_t md_store_remove_cb(md_store_t *store, md_store_group_t group, 
-                                        const char *name, const char *aspect,  
-                                        apr_pool_t *p, int force);
-typedef apr_status_t md_store_purge_cb(md_store_t *store, apr_pool_t *p, md_store_group_t group, 
-                                        const char *name);
-
-typedef int md_store_inspect(void *baton, const char *name, const char *aspect, 
-                             md_store_vtype_t vtype, void *value, apr_pool_t *ptemp);
-
-typedef apr_status_t md_store_iter_cb(md_store_inspect *inspect, void *baton, md_store_t *store, 
-                                      apr_pool_t *p, md_store_group_t group, const char *pattern,
-                                      const char *aspect, md_store_vtype_t vtype);
-
-typedef apr_status_t md_store_names_iter_cb(md_store_inspect *inspect, void *baton, md_store_t *store, 
-                                            apr_pool_t *p, md_store_group_t group, const char *pattern);
+const char *md_store_group_name(unsigned int group);
 
-typedef apr_status_t md_store_move_cb(md_store_t *store, apr_pool_t *p, md_store_group_t from, 
-                                      md_store_group_t to, const char *name, int archive);
-
-typedef apr_status_t md_store_get_fname_cb(const char **pfname, 
-                                           md_store_t *store, md_store_group_t group, 
-                                           const char *name, const char *aspect, 
-                                           apr_pool_t *p);
-
-typedef int md_store_is_newer_cb(md_store_t *store, 
-                                 md_store_group_t group1, md_store_group_t group2,  
-                                 const char *name, const char *aspect, apr_pool_t *p);
-
-struct md_store_t {
-    md_store_destroy_cb *destroy;
-
-    md_store_save_cb *save;
-    md_store_load_cb *load;
-    md_store_remove_cb *remove;
-    md_store_move_cb *move;
-    md_store_iter_cb *iterate;
-    md_store_names_iter_cb *iterate_names;
-    md_store_purge_cb *purge;
-    md_store_get_fname_cb *get_fname;
-    md_store_is_newer_cb *is_newer;
-};
-
-void md_store_destroy(md_store_t *store);
+typedef struct md_store_t md_store_t;
 
+/**
+ * A store for domain related data.
+ *
+ * The Key for a piece of data is the set of 3 items
+ *   <group> + <domain> + <aspect>
+ *
+ * Examples:
+ * "domains" + "greenbytes.de" + "pubcert.pem"
+ * "ocsp" + "greenbytes.de" + "ocsp-XXXXX.json"
+ *
+ * Storage groups are pre-defined, domain and aspect names can be freely chosen.
+ *
+ * Groups reflect use cases and come with security restrictions. The groups 
+ * DOMAINS, ARCHIVE and NONE are only accessible during the startup 
+ * phase of httpd.
+ *
+ * Private key are stored unencrypted only in restricted groups. Meaning that certificate
+ * keys in group DOMAINS are not encrypted, but only readable at httpd start/reload.
+ * Keys in unrestricted groups are encrypted using a pass phrase generated once and stored
+ * in NONE.
+ */
+
+/** Value types handled by a store */
+typedef enum {
+    MD_SV_TEXT,         /* plain text, value is (char*) */
+    MD_SV_JSON,         /* JSON serialization, value is (md_json_t*) */
+    MD_SV_CERT,         /* PEM x509 certificate, value is (md_cert_t*) */
+    MD_SV_PKEY,         /* PEM private key, value is (md_pkey_t*) */
+    MD_SV_CHAIN,        /* list of PEM x509 certificates, value is 
+                           (apr_array_header_t*) of (md_cert*) */
+} md_store_vtype_t;
+
+/** Store storage groups */
+typedef enum {
+    MD_SG_NONE,         /* top level of store, name MUST be NULL in calls */
+    MD_SG_ACCOUNTS,     /* ACME accounts */
+    MD_SG_CHALLENGES,   /* challenge response data for a domain */ 
+    MD_SG_DOMAINS,      /* live certificates and settings for a domain */
+    MD_SG_STAGING,      /* staged set of certificate and settings, maybe incomplete */
+    MD_SG_ARCHIVE,      /* Archived live sets of a domain */
+    MD_SG_TMP,          /* temporary domain storage */
+    MD_SG_OCSP,         /* OCSP stapling related domain data */
+    MD_SG_COUNT,        /* number of storage groups, used in setups */
+} md_store_group_t;
+
+#define MD_FN_MD                "md.json"
+#define MD_FN_JOB               "job.json"
+#define MD_FN_PRIVKEY           "privkey.pem"
+#define MD_FN_PUBCERT           "pubcert.pem"
+#define MD_FN_CERT              "cert.pem"
+#define MD_FN_HTTPD_JSON        "httpd.json"
+
+#define MD_FN_FALLBACK_PKEY     "fallback-privkey.pem"
+#define MD_FN_FALLBACK_CERT     "fallback-cert.pem"
+
+/**
+ * Load the JSON value at key "group/name/aspect", allocated from pool p.
+ * @return APR_ENOENT if there is no such value
+ */
 apr_status_t md_store_load_json(md_store_t *store, md_store_group_t group, 
                                 const char *name, const char *aspect, 
                                 struct md_json_t **pdata, apr_pool_t *p);
+/**
+ * Save the JSON value at key "group/name/aspect". If create != 0, fail if there
+ * already is a value for this key.
+ */
 apr_status_t md_store_save_json(md_store_t *store, apr_pool_t *p, md_store_group_t group, 
                                 const char *name, const char *aspect, 
                                 struct md_json_t *data, int create);
 
-
+/**
+ * Load the value of type at key "group/name/aspect", allocated from pool p. Usually, the
+ * type is expected to be the same as used in saving the value. Some conversions will work,
+ * others will fail the format.
+ * @return APR_ENOENT if there is no such value
+ */
 apr_status_t md_store_load(md_store_t *store, md_store_group_t group, 
                            const char *name, const char *aspect, 
                            md_store_vtype_t vtype, void **pdata, 
                            apr_pool_t *p);
+/**
+ * Save the JSON value at key "group/name/aspect". If create != 0, fail if there
+ * already is a value for this key. The provided data MUST be of the correct type.
+ */
 apr_status_t md_store_save(md_store_t *store, apr_pool_t *p, md_store_group_t group, 
                            const char *name, const char *aspect, 
                            md_store_vtype_t vtype, void *data, 
                            int create);
+
+/**
+ * Remove the value stored at key "group/name/aspect". Unless force != 0, a missing
+ * value will cause the call to fail with APR_ENOENT.
+ */ 
 apr_status_t md_store_remove(md_store_t *store, md_store_group_t group, 
                              const char *name, const char *aspect, 
                              apr_pool_t *p, int force);
+/**
+ * Remove everything matching key "group/name".
+ */ 
 apr_status_t md_store_purge(md_store_t *store, apr_pool_t *p, 
                             md_store_group_t group, const char *name);
 
+/**
+ * Remove all items matching the name/aspect patterns that have not been
+ * modified since the given timestamp.
+ */
+apr_status_t md_store_remove_not_modified_since(md_store_t *store, apr_pool_t *p, 
+                                                apr_time_t modified,
+                                                md_store_group_t group, 
+                                                const char *name, 
+                                                const char *aspect);
+
+/**
+ * inspect callback function. Invoked for each matched value. Values allocated from
+ * ptemp may disappear any time after the call returned. If this function returns
+ * 0, the iteration is aborted. 
+ */
+typedef int md_store_inspect(void *baton, const char *name, const char *aspect, 
+                             md_store_vtype_t vtype, void *value, apr_pool_t *ptemp);
 
+/**
+ * Iterator over all existing values matching the name pattern. Patterns are evaluated
+ * using apr_fnmatch() without flags.
+ */
 apr_status_t md_store_iter(md_store_inspect *inspect, void *baton, md_store_t *store, 
                            apr_pool_t *p, md_store_group_t group, const char *pattern, 
                            const char *aspect, md_store_vtype_t vtype);
 
+/**
+ * Move everything matching key "from/name" from one group to another. If archive != 0,
+ * move any existing "to/name" into a new "archive/new_name" location.
+ */
 apr_status_t md_store_move(md_store_t *store, apr_pool_t *p,
                            md_store_group_t from, md_store_group_t to,
                            const char *name, int archive);
 
+/**
+ * Rename a group member.
+ */
+apr_status_t md_store_rename(md_store_t *store, apr_pool_t *p,
+                             md_store_group_t group, const char *name, const char *to);
+
+/**
+ * Get the filename of an item stored in "group/name/aspect". The item does
+ * not have to exist.
+ */
 apr_status_t md_store_get_fname(const char **pfname, 
                                 md_store_t *store, md_store_group_t group, 
                                 const char *name, const char *aspect, 
                                 apr_pool_t *p);
 
+/**
+ * Make a compare on the modification time of "group1/name/aspect" vs. "group2/name/aspect".
+ */
 int md_store_is_newer(md_store_t *store, md_store_group_t group1, md_store_group_t group2,  
                       const char *name, const char *aspect, apr_pool_t *p);
 
+/**
+ * Iterate over all names that exist in a group, e.g. there are items matching
+ * "group/pattern". The inspect function is called with the name and NULL aspect
+ * and value.
+ */
 apr_status_t md_store_iter_names(md_store_inspect *inspect, void *baton, md_store_t *store, 
                                  apr_pool_t *p, md_store_group_t group, const char *pattern);
 
+/**
+ * Get the modification time of the item store under "group/name/aspect".
+ * @return modification time or 0 if the item does not exist.
+ */
+apr_time_t md_store_get_modified(md_store_t *store, md_store_group_t group,  
+                                 const char *name, const char *aspect, apr_pool_t *p);
+
+
 
 /**************************************************************************************************/
 /* Storage handling utils */
@@ -153,5 +229,66 @@ apr_status_t md_pubcert_save(md_store_t
                              md_store_group_t group, const char *name, 
                              struct apr_array_header_t *pubcert, int create);
 
+/**************************************************************************************************/
+/* implementation interface */
+
+typedef apr_status_t md_store_load_cb(md_store_t *store, md_store_group_t group, 
+                                      const char *name, const char *aspect, 
+                                      md_store_vtype_t vtype, void **pvalue, 
+                                      apr_pool_t *p);
+typedef apr_status_t md_store_save_cb(md_store_t *store, apr_pool_t *p, md_store_group_t group, 
+                                      const char *name, const char *aspect, 
+                                      md_store_vtype_t vtype, void *value, 
+                                      int create);
+typedef apr_status_t md_store_remove_cb(md_store_t *store, md_store_group_t group, 
+                                        const char *name, const char *aspect,  
+                                        apr_pool_t *p, int force);
+typedef apr_status_t md_store_purge_cb(md_store_t *store, apr_pool_t *p, md_store_group_t group, 
+                                        const char *name);
+
+typedef apr_status_t md_store_iter_cb(md_store_inspect *inspect, void *baton, md_store_t *store, 
+                                      apr_pool_t *p, md_store_group_t group, const char *pattern,
+                                      const char *aspect, md_store_vtype_t vtype);
+
+typedef apr_status_t md_store_names_iter_cb(md_store_inspect *inspect, void *baton, md_store_t *store, 
+                                            apr_pool_t *p, md_store_group_t group, const char *pattern);
+
+typedef apr_status_t md_store_move_cb(md_store_t *store, apr_pool_t *p, md_store_group_t from, 
+                                      md_store_group_t to, const char *name, int archive);
+
+typedef apr_status_t md_store_rename_cb(md_store_t *store, apr_pool_t *p, md_store_group_t group, 
+                                        const char *from, const char *to);
+
+typedef apr_status_t md_store_get_fname_cb(const char **pfname, 
+                                           md_store_t *store, md_store_group_t group, 
+                                           const char *name, const char *aspect, 
+                                           apr_pool_t *p);
+
+typedef int md_store_is_newer_cb(md_store_t *store, 
+                                 md_store_group_t group1, md_store_group_t group2,  
+                                 const char *name, const char *aspect, apr_pool_t *p);
+
+typedef apr_time_t md_store_get_modified_cb(md_store_t *store, md_store_group_t group,  
+                                            const char *name, const char *aspect, apr_pool_t *p);
+
+typedef apr_status_t md_store_remove_nms_cb(md_store_t *store, apr_pool_t *p, 
+                                            apr_time_t modified, md_store_group_t group, 
+                                            const char *name, const char *aspect);
+
+struct md_store_t {
+    md_store_save_cb *save;
+    md_store_load_cb *load;
+    md_store_remove_cb *remove;
+    md_store_move_cb *move;
+    md_store_rename_cb *rename;
+    md_store_iter_cb *iterate;
+    md_store_names_iter_cb *iterate_names;
+    md_store_purge_cb *purge;
+    md_store_get_fname_cb *get_fname;
+    md_store_is_newer_cb *is_newer;
+    md_store_get_modified_cb *get_modified;
+    md_store_remove_nms_cb *remove_nms;
+};
+
 
 #endif /* mod_md_md_store_h */