config: add generic callback wrapper to parse section.<url>.key

Existing configuration parsing functions (e.g. http_options() in http.c) know how to parse two-level configuration variable names. We would like to exploit them and parse something like this: [http] sslVerify = true [http "https://weak.example.com"] sslVerify = false and pretend as if http.sslVerify were set to false when talking to "https://weak.example.com/path". Introduce `urlmatch_config_entry()` wrapper that: - is called with the target URL (e.g. "https://weak.example.com/path"), and the two-level variable parser (e.g. `http_options`); - uses `url_normalize()` and `match_urls()` to see if configuration data matches the target URL; and - calls the traditional two-level configuration variable parser only for the configuration data whose <url> part matches the target URL (and if there are multiple matches, only do so if the current match is a better match than the ones previously seen). Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jul 31, 2013 at 10:42 UTC 836b6fb5a56338dc28165284875b257ccfd6c224
2 files changed +85
urlmatch.c
+67
@@ -466,3 +466,70 @@ int match_urls(const struct url_info *url,
466 *exactusermatch = usermatched;
467 return pathmatchlen;
468 }
469 +
470 +int urlmatch_config_entry(const char *var, const char *value, void *cb)
471 +{
472 + struct string_list_item *item;
473 + struct urlmatch_config *collect = cb;
474 + struct urlmatch_item *matched;
475 + struct url_info *url = &collect->url;
476 + const char *key, *dot;
477 + struct strbuf synthkey = STRBUF_INIT;
478 + size_t matched_len = 0;
479 + int user_matched = 0;
480 + int retval;
481 +
482 + key = skip_prefix(var, collect->section);
483 + if (!key || *(key++) != '.') {
484 + if (collect->cascade_fn)
485 + return collect->cascade_fn(var, value, cb);
486 + return 0; /* not interested */
487 + }
488 + dot = strrchr(key, '.');
489 + if (dot) {
490 + char *config_url, *norm_url;
491 + struct url_info norm_info;
492 +
493 + config_url = xmemdupz(key, dot - key);
494 + norm_url = url_normalize(config_url, &norm_info);
495 + free(config_url);
496 + if (!norm_url)
497 + return 0;
498 + matched_len = match_urls(url, &norm_info, &user_matched);
499 + free(norm_url);
500 + if (!matched_len)
501 + return 0;
502 + key = dot + 1;
503 + }
504 +
505 + if (collect->key && strcmp(key, collect->key))
506 + return 0;
507 +
508 + item = string_list_insert(&collect->vars, key);
509 + if (!item->util) {
510 + matched = xcalloc(1, sizeof(*matched));
511 + item->util = matched;
512 + } else {
513 + matched = item->util;
514 + /*
515 + * Is our match shorter? Is our match the same
516 + * length, and without user while the current
517 + * candidate is with user? Then we cannot use it.
518 + */
519 + if (matched_len < matched->matched_len ||
520 + ((matched_len == matched->matched_len) &&
521 + (!user_matched && matched->user_matched)))
522 + return 0;
523 + /* Otherwise, replace it with this one. */
524 + }
525 +
526 + matched->matched_len = matched_len;
527 + matched->user_matched = user_matched;
528 + strbuf_addstr(&synthkey, collect->section);
529 + strbuf_addch(&synthkey, '.');
530 + strbuf_addstr(&synthkey, key);
531 + retval = collect->collect_fn(synthkey.buf, value, collect->cb);
532 +
533 + strbuf_release(&synthkey);
534 + return retval;
535 +}
urlmatch.h
+18
@@ -33,4 +33,22 @@ struct url_info {
33 extern char *url_normalize(const char *, struct url_info *);
34 extern int match_urls(const struct url_info *url, const struct url_info *url_prefix, int *exactusermatch);
35
36 +struct urlmatch_item {
37 + size_t matched_len;
38 + char user_matched;
39 +};
40 +
41 +struct urlmatch_config {
42 + struct string_list vars;
43 + struct url_info url;
44 + const char *section;
45 + const char *key;
46 +
47 + void *cb;
48 + int (*collect_fn)(const char *var, const char *value, void *cb);
49 + int (*cascade_fn)(const char *var, const char *value, void *cb);
50 +};
51 +
52 +extern int urlmatch_config_entry(const char *var, const char *value, void *cb);
53 +
54 #endif /* URL_MATCH_H */