{"thread":{"id":"34438","subject":"[PATCH v5 0/5] config: add support for http.<url>.* settings","startedAt":"2013-07-15T09:50:57Z","lastAt":"2013-07-16T10:01:37Z","messageCount":9,"participants":["Kyle J. McKay","Eric Sunshine"],"isPatch":true,"patchVersion":5,"patchTotal":5},"messages":[{"id":"223409","messageId":"506e5f642a838b95e0dd5b1f0fa1cfe@f74d39fa044aa309eaea14b9f57fe79","threadId":"34438","inReplyTo":null,"subject":"[PATCH v5 0/5] config: add support for http.<url>.* settings","fromName":"Kyle J. McKay","fromEmail":"mackyle@gmail.com","sentAt":"2013-07-15T09:50:57Z","receivedAt":"2013-07-15T09:50:57Z","isPatch":true,"sender":{"key":"mackyle@gmail.com","avatar":"https://avatars.githubusercontent.com/u/813346?v=4"},"body":"This patch series adds support for url-specific http.* settings.\n\nIt has been suggested that a preparatory patch to address the way the\nhttp.sslCertPasswordProtected variable is handled be included.\n\nI'm not sure if the intent was to make that a separate patch or include it\nin a patch series together with the http.<url>.* settings updates.  I have,\nhowever, included it here this time along with a second patch that does the\nsame thing for the GIT_SSL_CERT_PASSWORD_PROTECTED environment variable.\n\nThere is talk about possibly altering GIT_SSL_NO_VERIFY, GIT_CURL_FTP_NO_EPSV\nand GIT_CURL_VERBOSE to behave similarly.  No patch is included here for that.\n\nThe http.<url>.* settings support addresses various feedback.  To better\nsupport matching URLs that are equivalent but spelled differently, a\nurl_normalize function has been added.  Currently this patch leaves it in\nhttp.c as http_options_url_normalize as I am unclear whether it should go into\nurl.{h,c} at this time since only http.c uses.\n\nSince the url_normalize function's behavior is non-trivial, it is presented as\na separate patch on top of the basic http.<url>.* settings support.  A new test\nfor it has also been included as the final patch.  I am unclear on the proper\nnumber for this test, but have gone ahead and put it with the other http tests\nsince this patch series places the url_normalize function into http.c.\n\nJunio C Hamano (1):\n  http.c: fix parsing of http.sslCertPasswordProtected variable\n\nKyle J. McKay (4):\n  http.c: fix parsing of GIT_SSL_CERT_PASSWORD_PROTECTED variable\n  config: add support for http.<url>.* settings\n  config: improve support for http.<url>.* settings\n  tests: add new test for the url_normalize function\n\n .gitignore               |   1 +\n Documentation/config.txt |  14 ++\n Makefile                 |   5 +\n http.c                   | 397 ++++++++++++++++++++++++++++++++++++++++++++---\n t/t5200-url-normalize.sh | 109 +++++++++++++\n test-url-normalize.c     |  62 ++++++++\n 6 files changed, 565 insertions(+), 23 deletions(-)\n create mode 100755 t/t5200-url-normalize.sh\n create mode 100644 test-url-normalize.c\n\n-- \n1.8.3\n"},{"id":"223410","messageId":"49c213ce55978a1a655d8ea0d851974@f74d39fa044aa309eaea14b9f57fe79","threadId":"34438","inReplyTo":"506e5f642a838b95e0dd5b1f0fa1cfe@f74d39fa044aa309eaea14b9f57fe79","subject":"[PATCH v5 1/5] http.c: fix parsing of http.sslCertPasswordProtected variable","fromName":"Kyle J. McKay","fromEmail":"mackyle@gmail.com","sentAt":"2013-07-15T09:50:58Z","receivedAt":"2013-07-15T09:50:58Z","isPatch":true,"sender":{"key":"mackyle@gmail.com","avatar":"https://avatars.githubusercontent.com/u/813346?v=4"},"body":"From: Junio C Hamano <gitster@pobox.com>\n\nThe existing code triggers only when the configuration variable is\nset to true.  Once the variable is set to true in a more generic\nconfiguration file (e.g. ~/.gitconfig), it cannot be overriden to\nfalse in the repository specific one (e.g. .git/config).\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n http.c | 3 +--\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\ndiff --git a/http.c b/http.c\nindex 2d086ae..e65661e 100644\n--- a/http.c\n+++ b/http.c\n@@ -160,8 +160,7 @@ static int http_options(const char *var, const char *value, void *cb)\n \tif (!strcmp(\"http.sslcainfo\", var))\n \t\treturn git_config_string(&ssl_cainfo, var, value);\n \tif (!strcmp(\"http.sslcertpasswordprotected\", var)) {\n-\t\tif (git_config_bool(var, value))\n-\t\t\tssl_cert_password_required = 1;\n+\t\tssl_cert_password_required = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \tif (!strcmp(\"http.ssltry\", var)) {\n-- \n1.8.3\n"},{"id":"223411","messageId":"41e6c55e7d5ad8bcae6db7d53c846f3@f74d39fa044aa309eaea14b9f57fe79","threadId":"34438","inReplyTo":"506e5f642a838b95e0dd5b1f0fa1cfe@f74d39fa044aa309eaea14b9f57fe79","subject":"[PATCH v5 2/5] http.c: fix parsing of GIT_SSL_CERT_PASSWORD_PROTECTED variable","fromName":"Kyle J. McKay","fromEmail":"mackyle@gmail.com","sentAt":"2013-07-15T09:50:59Z","receivedAt":"2013-07-15T09:50:59Z","isPatch":true,"sender":{"key":"mackyle@gmail.com","avatar":"https://avatars.githubusercontent.com/u/813346?v=4"},"body":"The existing code triggers whenever GIT_SSL_CERT_PASSWORD_PROTECTED\nis defined.  Setting GIT_SSL_CERT_PASSWORD_PROTECTED to a false\nvalue could not be used to override the http.sslCertPasswordProtected\nsetting once it had been turned on.\n\nSigned-off-by: Kyle J. McKay <mackyle@gmail.com>\n---\n http.c | 7 +++----\n 1 file changed, 3 insertions(+), 4 deletions(-)\n\ndiff --git a/http.c b/http.c\nindex e65661e..9542a59 100644\n--- a/http.c\n+++ b/http.c\n@@ -403,11 +403,10 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)\n \t\tcurl_ftp_no_epsv = 1;\n \n \tif (url) {\n+\t\tint pwdreq = git_env_bool(\"GIT_SSL_CERT_PASSWORD_PROTECTED\", -1);\n \t\tcredential_from_url(&http_auth, url);\n-\t\tif (!ssl_cert_password_required &&\n-\t\t    getenv(\"GIT_SSL_CERT_PASSWORD_PROTECTED\") &&\n-\t\t    !prefixcmp(url, \"https://\"))\n-\t\t\tssl_cert_password_required = 1;\n+\t\tif (pwdreq != -1 && !prefixcmp(url, \"https://\"))\n+\t\t\tssl_cert_password_required = pwdreq;\n \t}\n \n #ifndef NO_CURL_EASY_DUPHANDLE\n-- \n1.8.3\n"},{"id":"223414","messageId":"ddd9d565c61c057e84871a9237b763c@f74d39fa044aa309eaea14b9f57fe79","threadId":"34438","inReplyTo":"506e5f642a838b95e0dd5b1f0fa1cfe@f74d39fa044aa309eaea14b9f57fe79","subject":"[PATCH v5 3/5] config: add support for http.<url>.* settings","fromName":"Kyle J. McKay","fromEmail":"mackyle@gmail.com","sentAt":"2013-07-15T09:51:00Z","receivedAt":"2013-07-15T09:51:00Z","isPatch":true,"sender":{"key":"mackyle@gmail.com","avatar":"https://avatars.githubusercontent.com/u/813346?v=4"},"body":"The credentials configuration values already support url-specific\nconfiguration items in the form credential.<url>.*.  This patch\nadds similar support for http configuration values.\n\nThe <url> value is considered a match to a url if the <url> value\nis either an exact match or a prefix of the url which ends on a\npath component boundary ('/').  So \"https://example.com/test\" will\nmatch \"https://example.com/test\" and \"https://example.com/test/too\"\nbut not \"https://example.com/testextra\".\n\nLonger matches take precedence over shorter matches with\nenvironment variable settings taking precedence over all.\n\nWith this configuration:\n\n[http]\n        useragent = other-agent\n        noEPSV = true\n[http \"https://example.com\"]\n        useragent = example-agent\n        sslVerify = false\n[http \"https://example.com/path\"]\n        useragent = path-agent\n\nThe \"https://other.example.com/\" url will have useragent\n\"other-agent\" and sslVerify will be on.\n\nThe \"https://example.com/\" url will have useragent\n\"example-agent\" and sslVerify will be off.\n\nThe \"https://example.com/path/sub\" url will have useragent\n\"path-agent\" and sslVerify will be off.\n\nAll three of the examples will have noEPSV enabled.\n\nSigned-off-by: Kyle J. McKay <mackyle@gmail.com>\n---\n Documentation/config.txt |  15 +++++\n http.c                   | 169 ++++++++++++++++++++++++++++++++++++++++++-----\n 2 files changed, 167 insertions(+), 17 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 81856dd..ceced99 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1531,6 +1531,21 @@ http.useragent::\n \tof common USER_AGENT strings (but not including those like git/1.7.1).\n \tCan be overridden by the 'GIT_HTTP_USER_AGENT' environment variable.\n \n+http.<url>.*::\n+\tAny of the http.* options above can be applied selectively to some urls.\n+\tFor example \"http.https://example.com.useragent\" would set the user\n+\tagent only for https connections to example.com.  The <url> value\n+\tmatches a url if it is an exact match or a prefix of the url matching\n+\tat a \"/\" boundary.  Longer <url> matches take precedence over shorter\n+\tones with the environment variable settings taking precedence over all.\n+\tNote that <url> must match the url passed to git exactly (other than\n+\tpossibly being a prefix).  This means any user, password and/or port\n+\tsetting that appears in a url as well as any %XX escapes that are\n+\tpresent must also appear in <url> to have a successful match.  The urls\n+\tthat are matched against are those given directly to git commands.  In\n+\tother words, use exactly the same url that was passed to git (possibly\n+\tshortened) for the <url> value of the config setting.\n+\n i18n.commitEncoding::\n \tCharacter encoding the commit messages are stored in; Git itself\n \tdoes not care per se, but this information is necessary e.g. when\ndiff --git a/http.c b/http.c\nindex 9542a59..758e5b1 100644\n--- a/http.c\n+++ b/http.c\n@@ -30,6 +30,34 @@ static CURL *curl_default;\n \n char curl_errorstr[CURL_ERROR_SIZE];\n \n+enum http_option_type {\n+\tOPT_POST_BUFFER,\n+\tOPT_MIN_SESSIONS,\n+\tOPT_SSL_VERIFY,\n+\tOPT_SSL_TRY,\n+\tOPT_SSL_CERT,\n+\tOPT_SSL_CAINFO,\n+\tOPT_LOW_SPEED,\n+\tOPT_LOW_TIME,\n+\tOPT_NO_EPSV,\n+\tOPT_HTTP_PROXY,\n+\tOPT_COOKIE_FILE,\n+\tOPT_USER_AGENT,\n+\tOPT_PASSWD_REQ,\n+#ifdef USE_CURL_MULTI\n+\tOPT_MAX_REQUESTS,\n+#endif\n+#if LIBCURL_VERSION_NUM >= 0x070903\n+\tOPT_SSL_KEY,\n+#endif\n+#if LIBCURL_VERSION_NUM >= 0x070908\n+\tOPT_SSL_CAPATH,\n+#endif\n+\tOPT_MAX\n+};\n+\n+static size_t http_option_max_matched_len[OPT_MAX];\n+\n static int curl_ssl_verify = -1;\n static int curl_ssl_try;\n static const char *ssl_cert;\n@@ -141,33 +169,121 @@ static void process_curl_messages(void)\n }\n #endif\n \n+static size_t http_options_url_match_prefix(const char *url,\n+\t\t\t\t\t    const char *url_prefix,\n+\t\t\t\t\t    size_t url_prefix_len)\n+{\n+\t/*\n+\t * url_prefix matches url if url_prefix is an exact match for url or it\n+\t * is a prefix of url and the match ends on a path component boundary.\n+\t * Both url and url_prefix are considered to have an implicit '/' on the\n+\t * end for matching purposes if they do not already.\n+\t *\n+\t * url must be NUL terminated.  url_prefix_len is the length of\n+\t * url_prefix which need not be NUL terminated.\n+\t *\n+\t * The return value is the length of the match in characters (excluding\n+\t * any final '/') or 0 for no match.  Passing \"/\" as url_prefix will\n+\t * always cause 0 to be returned.\n+\t */\n+\tsize_t url_len;\n+\tif (url_prefix_len && url_prefix[url_prefix_len - 1] == '/')\n+\t\turl_prefix_len--;\n+\tif (!url_prefix_len || strncmp(url, url_prefix, url_prefix_len))\n+\t\treturn 0;\n+\turl_len = strlen(url);\n+\tif ((url_len == url_prefix_len) || (url[url_prefix_len] == '/'))\n+\t\treturn url_prefix_len;\n+\treturn 0;\n+}\n+\n+static int new_match_is_shorter(size_t matchlen, enum http_option_type opt)\n+{\n+\t/*\n+\t * Compare matchlen to the last matched length of option opt and\n+\t * return true if matchlen is shorter than the last matched length\n+\t * (meaning the config setting should be ignored).  Upon seeing the\n+\t * _same_ key (i.e. new key has the same match length which is therefore\n+\t * not shorter) the new setting will override the previous setting.\n+\t * Otherwise return false and record matchlen as the current last\n+\t * matched length of option opt.\n+\t */\n+\tif (matchlen < http_option_max_matched_len[opt])\n+\t\treturn 1;\n+\thttp_option_max_matched_len[opt] = matchlen;\n+\treturn 0;\n+}\n+\n static int http_options(const char *var, const char *value, void *cb)\n {\n-\tif (!strcmp(\"http.sslverify\", var)) {\n+\tconst char *url = cb;\n+\tconst char *key, *dot;\n+\tsize_t matchlen = 0;\n+\n+\tkey = skip_prefix(var, \"http.\");\n+\tif (!key)\n+\t\treturn git_default_config(var, value, cb);\n+\n+\t/*\n+\t * If the part following the leading \"http.\" contains a '.' then check\n+\t * to see if the part between \"http.\" and the last '.' is a match to\n+\t * url.  If it's not then ignore the setting.  Otherwise set key to\n+\t * point to the option which is the part after the final '.' and\n+\t * use key in subsequent comparisons to determine the option type.\n+\t */\n+\tdot = strrchr(key, '.');\n+\tif (dot) {\n+\t\tmatchlen = http_options_url_match_prefix(url, key, dot - key);\n+\t\tif (!matchlen)\n+\t\t\treturn 0;\n+\t\tkey = dot + 1;\n+\t}\n+\n+\tif (!strcmp(\"sslverify\", key)) {\n+\t\tif (new_match_is_shorter(matchlen, OPT_SSL_VERIFY))\n+\t\t\treturn 0;\n \t\tcurl_ssl_verify = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n-\tif (!strcmp(\"http.sslcert\", var))\n+\tif (!strcmp(\"sslcert\", key)) {\n+\t\tif (new_match_is_shorter(matchlen, OPT_SSL_CERT))\n+\t\t\treturn 0;\n \t\treturn git_config_string(&ssl_cert, var, value);\n+\t}\n #if LIBCURL_VERSION_NUM >= 0x070903\n-\tif (!strcmp(\"http.sslkey\", var))\n+\tif (!strcmp(\"sslkey\", key)) {\n+\t\tif (new_match_is_shorter(matchlen, OPT_SSL_KEY))\n+\t\t\treturn 0;\n \t\treturn git_config_string(&ssl_key, var, value);\n+\t}\n #endif\n #if LIBCURL_VERSION_NUM >= 0x070908\n-\tif (!strcmp(\"http.sslcapath\", var))\n+\tif (!strcmp(\"sslcapath\", key)) {\n+\t\tif (new_match_is_shorter(matchlen, OPT_SSL_CAPATH))\n+\t\t\treturn 0;\n \t\treturn git_config_string(&ssl_capath, var, value);\n+\t}\n #endif\n-\tif (!strcmp(\"http.sslcainfo\", var))\n+\tif (!strcmp(\"sslcainfo\", key)) {\n+\t\tif (new_match_is_shorter(matchlen, OPT_SSL_CAINFO))\n+\t\t\treturn 0;\n \t\treturn git_config_string(&ssl_cainfo, var, value);\n-\tif (!strcmp(\"http.sslcertpasswordprotected\", var)) {\n+\t}\n+\tif (!strcmp(\"sslcertpasswordprotected\", key)) {\n+\t\tif (new_match_is_shorter(matchlen, OPT_PASSWD_REQ))\n+\t\t\treturn 0;\n \t\tssl_cert_password_required = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n-\tif (!strcmp(\"http.ssltry\", var)) {\n+\tif (!strcmp(\"ssltry\", key)) {\n+\t\tif (new_match_is_shorter(matchlen, OPT_SSL_TRY))\n+\t\t\treturn 0;\n \t\tcurl_ssl_try = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n-\tif (!strcmp(\"http.minsessions\", var)) {\n+\tif (!strcmp(\"minsessions\", key)) {\n+\t\tif (new_match_is_shorter(matchlen, OPT_MIN_SESSIONS))\n+\t\t\treturn 0;\n \t\tmin_curl_sessions = git_config_int(var, value);\n #ifndef USE_CURL_MULTI\n \t\tif (min_curl_sessions > 1)\n@@ -176,39 +292,58 @@ static int http_options(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n #ifdef USE_CURL_MULTI\n-\tif (!strcmp(\"http.maxrequests\", var)) {\n+\tif (!strcmp(\"maxrequests\", key)) {\n+\t\tif (new_match_is_shorter(matchlen, OPT_MAX_REQUESTS))\n+\t\t\treturn 0;\n \t\tmax_requests = git_config_int(var, value);\n \t\treturn 0;\n \t}\n #endif\n-\tif (!strcmp(\"http.lowspeedlimit\", var)) {\n+\tif (!strcmp(\"lowspeedlimit\", key)) {\n+\t\tif (new_match_is_shorter(matchlen, OPT_LOW_SPEED))\n+\t\t\treturn 0;\n \t\tcurl_low_speed_limit = (long)git_config_int(var, value);\n \t\treturn 0;\n \t}\n-\tif (!strcmp(\"http.lowspeedtime\", var)) {\n+\tif (!strcmp(\"lowspeedtime\", key)) {\n+\t\tif (new_match_is_shorter(matchlen, OPT_LOW_TIME))\n+\t\t\treturn 0;\n \t\tcurl_low_speed_time = (long)git_config_int(var, value);\n \t\treturn 0;\n \t}\n \n-\tif (!strcmp(\"http.noepsv\", var)) {\n+\tif (!strcmp(\"noepsv\", key)) {\n+\t\tif (new_match_is_shorter(matchlen, OPT_NO_EPSV))\n+\t\t\treturn 0;\n \t\tcurl_ftp_no_epsv = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n-\tif (!strcmp(\"http.proxy\", var))\n+\tif (!strcmp(\"proxy\", key)) {\n+\t\tif (new_match_is_shorter(matchlen, OPT_HTTP_PROXY))\n+\t\t\treturn 0;\n \t\treturn git_config_string(&curl_http_proxy, var, value);\n+\t}\n \n-\tif (!strcmp(\"http.cookiefile\", var))\n+\tif (!strcmp(\"cookiefile\", key)) {\n+\t\tif (new_match_is_shorter(matchlen, OPT_COOKIE_FILE))\n+\t\t\treturn 0;\n \t\treturn git_config_string(&curl_cookie_file, var, value);\n+\t}\n \n-\tif (!strcmp(\"http.postbuffer\", var)) {\n+\tif (!strcmp(\"postbuffer\", key)) {\n+\t\tif (new_match_is_shorter(matchlen, OPT_POST_BUFFER))\n+\t\t\treturn 0;\n \t\thttp_post_buffer = git_config_int(var, value);\n \t\tif (http_post_buffer < LARGE_PACKET_MAX)\n \t\t\thttp_post_buffer = LARGE_PACKET_MAX;\n \t\treturn 0;\n \t}\n \n-\tif (!strcmp(\"http.useragent\", var))\n+\tif (!strcmp(\"useragent\", key)) {\n+\t\tif (new_match_is_shorter(matchlen, OPT_USER_AGENT))\n+\t\t\treturn 0;\n \t\treturn git_config_string(&user_agent, var, value);\n+\t}\n \n \t/* Fall back on the default ones */\n \treturn git_default_config(var, value, cb);\n@@ -343,7 +478,7 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)\n \n \thttp_is_verbose = 0;\n \n-\tgit_config(http_options, NULL);\n+\tgit_config(http_options, (void *)url);\n \n \tcurl_global_init(CURL_GLOBAL_ALL);\n \n-- \n1.8.3\n"},{"id":"223412","messageId":"dcbaa11c8595f48814aa39a75ad18ea@f74d39fa044aa309eaea14b9f57fe79","threadId":"34438","inReplyTo":"506e5f642a838b95e0dd5b1f0fa1cfe@f74d39fa044aa309eaea14b9f57fe79","subject":"[PATCH v5 4/5] config: improve support for http.<url>.* settings","fromName":"Kyle J. McKay","fromEmail":"mackyle@gmail.com","sentAt":"2013-07-15T09:51:01Z","receivedAt":"2013-07-15T09:51:01Z","isPatch":true,"sender":{"key":"mackyle@gmail.com","avatar":"https://avatars.githubusercontent.com/u/813346?v=4"},"body":"Improve on the http.<url>.* url matching behavior by first\nnormalizing the urls before they are compared.\n\nWith this change, for example, the following configuration\nsection:\n\n[http \"https://example.com/path\"]\n        useragent = example-agent\n        sslVerify = false\n\nwill properly match a \"HTTPS://example.COM/p%61th\" url which\nis equivalent.\n\nThe normalization rules are based on RFC 3986 and should\nresult in any two equivalent urls being a match.\n\nSigned-off-by: Kyle J. McKay <mackyle@gmail.com>\n---\n Documentation/config.txt |  19 ++--\n http.c                   | 222 ++++++++++++++++++++++++++++++++++++++++++++++-\n 2 files changed, 229 insertions(+), 12 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex ceced99..98eee23 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1535,16 +1535,15 @@ http.<url>.*::\n \tAny of the http.* options above can be applied selectively to some urls.\n \tFor example \"http.https://example.com.useragent\" would set the user\n \tagent only for https connections to example.com.  The <url> value\n-\tmatches a url if it is an exact match or a prefix of the url matching\n-\tat a \"/\" boundary.  Longer <url> matches take precedence over shorter\n-\tones with the environment variable settings taking precedence over all.\n-\tNote that <url> must match the url passed to git exactly (other than\n-\tpossibly being a prefix).  This means any user, password and/or port\n-\tsetting that appears in a url as well as any %XX escapes that are\n-\tpresent must also appear in <url> to have a successful match.  The urls\n-\tthat are matched against are those given directly to git commands.  In\n-\tother words, use exactly the same url that was passed to git (possibly\n-\tshortened) for the <url> value of the config setting.\n+\tmatches a url if it is an exact match or if it is a prefix of the url\n+\tmatching at a \"/\" boundary.  Longer <url> matches take precedence over\n+\tshorter ones with the environment variable settings taking precedence\n+\tover all.  The urls are normalized before testing for a match.  Note,\n+\thowever, that any user, password and/or port setting that appears in a\n+\turl must also match that part of <url> to have a successful match.  The\n+\turls that are matched against are those given directly to git commands.\n+\tThis means any urls visited as a result of a redirection do not\n+\tparticipate in matching.\n \n i18n.commitEncoding::\n \tCharacter encoding the commit messages are stored in; Git itself\ndiff --git a/http.c b/http.c\nindex 758e5b1..d04386e 100644\n--- a/http.c\n+++ b/http.c\n@@ -169,6 +169,210 @@ static void process_curl_messages(void)\n }\n #endif\n \n+#define URL_ALPHA \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\"\n+#define URL_DIGIT \"0123456789\"\n+#define URL_HEXDIGIT URL_DIGIT \"ABCDEFabcdef\"\n+#define URL_ALPHADIGIT URL_ALPHA URL_DIGIT\n+#define URL_SCHEME_CHARS URL_ALPHADIGIT \"+.-\"\n+#define URL_HOST_CHARS URL_ALPHADIGIT \".-[:]\" /* IPv6 literals need [:] */\n+#define URL_UNSAFE_CHARS \" <>\\\"%{}|\\\\^`\" /* plus 0x00-0x1F,0x7F-0xFF */\n+#define URL_GEN_RESERVED \":/?#[]@\"\n+#define URL_SUB_RESERVED \"!$&'()*+,;=\"\n+#define URL_RESERVED URL_GEN_RESERVED URL_SUB_RESERVED /* only allowed delims */\n+\n+static int hex_digit_value(int ch)\n+{\n+\t/*\n+\t * Returns the hexadecimal digit value (0x00..0x0F) of character ch.\n+\t * If ch is not a hexadecimal digit, the return value is undefined.\n+\t */\n+\n+\tif ('0' <= ch && ch <= '9')\n+\t\treturn ch - '0';\n+\treturn toupper(ch) - 'A' + 10;\n+}\n+\n+static int append_normalized_escapes(struct strbuf *buf,\n+\t\t\t\t     const char *from,\n+\t\t\t\t     size_t from_len,\n+\t\t\t\t     const char *esc_extra,\n+\t\t\t\t     const char *esc_ok)\n+{\n+\t/*\n+\t * Append to strbuf buf characters from string from with length from_len\n+\t * while unescaping characters that do not need to be escaped and\n+\t * escaping characters that do.  The set of characters to escape (the\n+\t * complement of which is unescaped) starts out as the RFC 3986 unsafe\n+\t * characters (0x00-0x1F,0x7F-0xFF,\" <>\\\"#%{}|\\\\^`\").  If esc_extra\n+\t * is not NULL, those additional characters will also always be escaped.\n+\t * If esc_ok is not NULL, those characters will be left escaped if found\n+\t * that way, but will not be unescaped otherwise (used for delimiters).\n+\t * If a %-escape sequence is encountered that is not followed by 2\n+\t * hexadecimal digits, the sequence is invalid and false (0) will be\n+\t * returned.  Otherwise true (1) will be returned for success.\n+\t *\n+\t * Note that all %-escape sequences will be normalized to UPPERCASE\n+\t * as indicated in RFC 3986.  Unless included in esc_extra or esc_ok\n+\t * alphanumerics and \"-._~\" will always be unescaped as per RFC 3986.\n+\t */\n+\n+\twhile (from_len) {\n+\t\tint ch = *from++;\n+\t\tint was_esc = 0;\n+\n+\t\tfrom_len--;\n+\t\tif (ch == '%') {\n+\t\t\tif (from_len < 2 ||\n+\t\t\t    !strchr(URL_HEXDIGIT, from[0]) ||\n+\t\t\t    !strchr(URL_HEXDIGIT, from[1]))\n+\t\t\t\treturn 0;\n+\t\t\tch = hex_digit_value(*from++) << 4;\n+\t\t\tch |= hex_digit_value(*from++);\n+\t\t\tfrom_len -= 2;\n+\t\t\twas_esc = 1;\n+\t\t}\n+\t\tif (ch <= 0x1F || ch >= 0x7F || strchr(URL_UNSAFE_CHARS, ch) ||\n+\t\t    (esc_extra && strchr(esc_extra, ch)) ||\n+\t\t    (was_esc && strchr(esc_ok, ch)))\n+\t\t\tstrbuf_addf(buf, \"%%%02X\", ch);\n+\t\telse\n+\t\t\tstrbuf_addch(buf, ch);\n+\t}\n+\n+\treturn 1;\n+}\n+\n+static char *http_options_url_normalize(const char *url, size_t *out_len)\n+{\n+\t/*\n+\t * Normalize NUL-terminated url using the following rules:\n+\t *\n+\t * 1. Case-insensitive parts of url will be converted to lower case\n+\t * 2. %-encoded characters that do not need to be will be unencoded\n+\t * 3. Characters that are not %-encoded and must be will be encoded\n+\t * 4. All %-encodings will be converted to upper case hexadecimal\n+\t * 5. If the default port for the scheme is given it will be removed\n+\t * 6. If the user:password part is given and empty it will be removed\n+\t * 7. A path part (including empty) not starting with '/' has one added\n+\t * 8. IPv6 host literals are allowed (but not normalized or validated)\n+\t *\n+\t * The rules are based on information in RFC 3986.\n+\t *\n+\t * Please note this function requires a full URL including a scheme\n+\t * and host part (except for file: URLs which may have an empty host).\n+\t *\n+\t * The return value is a newly allocated string that must be freed\n+\t * or NULL if the url is not valid.\n+\t *\n+\t * If a non-NULL value is returned and out_len is not NULL then the\n+\t * length of the result will be stored in *out_len.\n+\t *\n+\t * This is NOT a URL validation function.  Full URL validation is NOT\n+\t * performed.  Some invalid host names are passed through this function\n+\t * undetected.  Invalid port numbers may also be passed through\n+\t * undetected.  However, most all other problems that make a URL invalid\n+\t * will be detected (including a missing host for non file: URLs).\n+\t */\n+\n+\tsize_t url_len = strlen(url);\n+\tstruct strbuf norm;\n+\tsize_t spanned;\n+\tconst char *slash_ptr, *at_ptr, *colon_ptr;\n+\n+\t/* Copy lowercased scheme and :// suffix, %-escapes are not allowed */\n+\tspanned = strspn(url, URL_SCHEME_CHARS);\n+\tif (!spanned || spanned + 3 > url_len || url[spanned] != ':' ||\n+\t    url[spanned+1] != '/' || url[spanned+2] != '/')\n+\t\treturn NULL; /* Bad scheme and/or missing \"://\" part */\n+\tstrbuf_init(&norm, url_len);\n+\tspanned += 3;\n+\turl_len -= spanned;\n+\twhile (spanned--)\n+\t\tstrbuf_addch(&norm, tolower(*url++));\n+\n+\t/* Copy any username:password if present normalizing %-escapes */\n+\tat_ptr = strchr(url, '@');\n+\tslash_ptr = url + strcspn(url, \"/?#\");\n+\tif (at_ptr && at_ptr < slash_ptr) {\n+\t\tif (at_ptr > url) {\n+\t\t\tif (!append_normalized_escapes(&norm, url, at_ptr - url,\n+\t\t\t\t\t\t       \"\", URL_RESERVED)) {\n+\t\t\t\tstrbuf_release(&norm);\n+\t\t\t\treturn NULL;\n+\t\t\t}\n+\t\t\tstrbuf_addch(&norm, '@');\n+\t\t}\n+\t\turl_len -= (++at_ptr - url);\n+\t\turl = at_ptr;\n+\t}\n+\n+\t/* Copy the host part excluding any port part, no %-escapes allowed */\n+\tif (!url_len || strchr(\":/?#\", *url)) {\n+\t\t/* Missing host invalid for all URL schemes except file */\n+\t\tif (strncmp(norm.buf, \"file:\", 5)) {\n+\t\t\tstrbuf_release(&norm);\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\tcolon_ptr = slash_ptr - 1;\n+\twhile (colon_ptr > url && *colon_ptr != ':' && *colon_ptr != ']')\n+\t\tcolon_ptr--;\n+\tif (*colon_ptr != ':')\n+\t\tcolon_ptr = slash_ptr;\n+\tspanned = strspn(url, URL_HOST_CHARS);\n+\tif (spanned < colon_ptr - url) {\n+\t\t/* Host name has invalid characters */\n+\t\tstrbuf_release(&norm);\n+\t\treturn NULL;\n+\t}\n+\twhile (url < colon_ptr) {\n+\t\tstrbuf_addch(&norm, tolower(*url++));\n+\t\turl_len--;\n+\t}\n+\n+\t/* Check the port part and copy if not the default, no %-escapes */\n+\tif (colon_ptr < slash_ptr) {\n+\t\tif (colon_ptr + 1 == slash_ptr) {\n+\t\t\t/* Skip \":\" port with no number, it's same as default */\n+\t\t} else if (slash_ptr - colon_ptr == 3 &&\n+\t\t\t   !strncmp(norm.buf, \"http:\", 5) &&\n+\t\t\t   !strncmp(url, \":80\", 3)) {\n+\t\t\t/* Skip http :80 as it's the default */\n+\t\t} else if (slash_ptr - colon_ptr == 4 &&\n+\t\t\t   !strncmp(norm.buf, \"https:\", 6) &&\n+\t\t\t   !strncmp(url, \":443\", 4)) {\n+\t\t\t/* Skip https :443 as it's the default */\n+\t\t} else {\n+\t\t\t/* Port number must be all digits */\n+\t\t\tspanned = strspn(url + 1, URL_DIGIT);\n+\t\t\tif (spanned + 1 < slash_ptr - colon_ptr) {\n+\t\t\t\t/* port number has invalid characters */\n+\t\t\t\tstrbuf_release(&norm);\n+\t\t\t\treturn NULL;\n+\t\t\t}\n+\t\t\tstrbuf_add(&norm, url, slash_ptr - colon_ptr);\n+\t\t}\n+\t\turl_len -= slash_ptr - colon_ptr;\n+\t\turl = slash_ptr;\n+\t}\n+\n+\t/*\n+\t * Now simply copy the rest, if any, only normalizing %-escapes and\n+\t * being careful to not corrupt the URL by unescaping any delimiters,\n+\t * but do add an initial '/' if it's missing\n+\t */\n+\tif (*url != '/')\n+\t\tstrbuf_addch(&norm, '/');\n+\tif (*url) {\n+\t\tif (!append_normalized_escapes(&norm, url, url_len, \"\", URL_RESERVED)) {\n+\t\t\tstrbuf_release(&norm);\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\n+\treturn strbuf_detach(&norm, out_len);\n+}\n+\n static size_t http_options_url_match_prefix(const char *url,\n \t\t\t\t\t    const char *url_prefix,\n \t\t\t\t\t    size_t url_prefix_len)\n@@ -185,8 +389,13 @@ static size_t http_options_url_match_prefix(const char *url,\n \t * The return value is the length of the match in characters (excluding\n \t * any final '/') or 0 for no match.  Passing \"/\" as url_prefix will\n \t * always cause 0 to be returned.\n+\t *\n+\t * Passing NULL as url and/or url_prefix will always cause 0 to be\n+\t * returned without causing any faults.\n \t */\n \tsize_t url_len;\n+\tif (!url || !url_prefix)\n+\t\treturn 0;\n \tif (url_prefix_len && url_prefix[url_prefix_len - 1] == '/')\n \t\turl_prefix_len--;\n \tif (!url_prefix_len || strncmp(url, url_prefix, url_prefix_len))\n@@ -233,7 +442,14 @@ static int http_options(const char *var, const char *value, void *cb)\n \t */\n \tdot = strrchr(key, '.');\n \tif (dot) {\n-\t\tmatchlen = http_options_url_match_prefix(url, key, dot - key);\n+\t\tchar *config_url = xmemdupz(key, dot - key);\n+\t\tsize_t norm_url_len;\n+\t\tchar *norm_url = http_options_url_normalize(config_url, &norm_url_len);\n+\t\tfree(config_url);\n+\t\tif (!norm_url)\n+\t\t\treturn 0;\n+\t\tmatchlen = http_options_url_match_prefix(url, norm_url, norm_url_len);\n+\t\tfree(norm_url);\n \t\tif (!matchlen)\n \t\t\treturn 0;\n \t\tkey = dot + 1;\n@@ -475,10 +691,12 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)\n {\n \tchar *low_speed_limit;\n \tchar *low_speed_time;\n+\tchar *norm_url = http_options_url_normalize(url, NULL);\n \n \thttp_is_verbose = 0;\n \n-\tgit_config(http_options, (void *)url);\n+\tgit_config(http_options, norm_url);\n+\tfree(norm_url);\n \n \tcurl_global_init(CURL_GLOBAL_ALL);\n \n-- \n1.8.3\n"},{"id":"223413","messageId":"62be3b26b7b98206f330b9e6f833523@f74d39fa044aa309eaea14b9f57fe79","threadId":"34438","inReplyTo":"506e5f642a838b95e0dd5b1f0fa1cfe@f74d39fa044aa309eaea14b9f57fe79","subject":"[PATCH v5 5/5] tests: add new test for the url_normalize function","fromName":"Kyle J. McKay","fromEmail":"mackyle@gmail.com","sentAt":"2013-07-15T09:51:02Z","receivedAt":"2013-07-15T09:51:02Z","isPatch":true,"sender":{"key":"mackyle@gmail.com","avatar":"https://avatars.githubusercontent.com/u/813346?v=4"},"body":"In order to perform sane URL matching for http.<url>.* options,\nhttp.c normalizes URLs before performing matches.\n\nA new test-url-normalize test program is introduced along with\na new t5200-url-normalize.sh script to run the tests.\n\nSince the url_normalize function currently lives in http.c this\ntest will be skipped if NO_CURL is defined since http.c is skipped\nin that case.\n\nSigned-off-by: Kyle J. McKay <mackyle@gmail.com>\n---\n .gitignore               |   1 +\n Makefile                 |   5 +++\n t/t5200-url-normalize.sh | 109 +++++++++++++++++++++++++++++++++++++++++++++++\n test-url-normalize.c     |  62 +++++++++++++++++++++++++++\n 4 files changed, 177 insertions(+)\n create mode 100755 t/t5200-url-normalize.sh\n create mode 100644 test-url-normalize.c\n\ndiff --git a/.gitignore b/.gitignore\nindex efa8db0..b60a9df 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -201,6 +201,7 @@\n /test-string-list\n /test-subprocess\n /test-svn-fe\n+/test-url-normalize\n /test-wildmatch\n /common-cmds.h\n *.tar.gz\ndiff --git a/Makefile b/Makefile\nindex 0600eb4..195c62a 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -580,6 +580,7 @@ TEST_PROGRAMS_NEED_X += test-sigchain\n TEST_PROGRAMS_NEED_X += test-string-list\n TEST_PROGRAMS_NEED_X += test-subprocess\n TEST_PROGRAMS_NEED_X += test-svn-fe\n+TEST_PROGRAMS_NEED_X += test-url-normalize\n TEST_PROGRAMS_NEED_X += test-wildmatch\n \n TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))\n@@ -2278,6 +2279,10 @@ test-parse-options$X: parse-options.o parse-options-cb.o\n \n test-svn-fe$X: vcs-svn/lib.a\n \n+test-url-normalize$X: test-url-normalize.o GIT-LDFLAGS $(GITLIBS)\n+\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n+\t\t$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)\n+\n .PRECIOUS: $(TEST_OBJS)\n \n test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS)\ndiff --git a/t/t5200-url-normalize.sh b/t/t5200-url-normalize.sh\nnew file mode 100755\nindex 0000000..40ebcd3\n--- /dev/null\n+++ b/t/t5200-url-normalize.sh\n@@ -0,0 +1,109 @@\n+#!/bin/sh\n+\n+test_description='url normalization'\n+. ./test-lib.sh\n+\n+if test -n \"$NO_CURL\"; then\n+\tskip_all='skipping test, git built without http support'\n+\ttest_done\n+fi\n+\n+# Note that only file: URLs should be allowed without a host\n+\n+test_expect_success 'url scheme' '\n+\t! test-url-normalize \"\" &&\n+\t! test-url-normalize \"_\" &&\n+\t! test-url-normalize \"scheme\" &&\n+\t! test-url-normalize \"scheme:\" &&\n+\t! test-url-normalize \"scheme:/\" &&\n+\t! test-url-normalize \"scheme://\" &&\n+\t! test-url-normalize \"file\" &&\n+\t! test-url-normalize \"file:\" &&\n+\t! test-url-normalize \"file:/\" &&\n+\ttest-url-normalize \"file://\" &&\n+\t! test-url-normalize \"://acme.co\" &&\n+\t! test-url-normalize \"x_test://acme.co\" &&\n+\t! test-url-normalize \"schem%6e://\" &&\n+\ttest-url-normalize \"x-Test+v1.0://acme.co\" &&\n+\ttest \"$(test-url-normalize -p \"AbCdeF://x.Y\")\" = \"abcdef://x.y/\"\n+'\n+\n+test_expect_success 'url authority' '\n+\t! test-url-normalize \"scheme://user:pass@\" &&\n+\t! test-url-normalize \"scheme://?\" &&\n+\t! test-url-normalize \"scheme://#\" &&\n+\t! test-url-normalize \"scheme:///\" &&\n+\t! test-url-normalize \"scheme://:\" &&\n+\t! test-url-normalize \"scheme://:555\" &&\n+\ttest-url-normalize \"file://user:pass@\" &&\n+\ttest-url-normalize \"file://?\" &&\n+\ttest-url-normalize \"file://#\" &&\n+\ttest-url-normalize \"file:///\" &&\n+\ttest-url-normalize \"file://:\" &&\n+\ttest-url-normalize \"file://:555\" &&\n+\ttest-url-normalize \"scheme://user:pass@host\" &&\n+\ttest-url-normalize \"scheme://@host\" &&\n+\ttest-url-normalize \"scheme://%00@host\" &&\n+\t! test-url-normalize \"scheme://%%@host\" &&\n+\t! test-url-normalize \"scheme://host_\" &&\n+\ttest-url-normalize \"scheme://user:pass@host/\" &&\n+\ttest-url-normalize \"scheme://@host/\" &&\n+\ttest-url-normalize \"scheme://host/\" &&\n+\ttest-url-normalize \"scheme://host?x\" &&\n+\ttest-url-normalize \"scheme://host#x\" &&\n+\ttest-url-normalize \"scheme://host/@\" &&\n+\ttest-url-normalize \"scheme://host?@x\" &&\n+\ttest-url-normalize \"scheme://host#@x\" &&\n+\ttest-url-normalize \"scheme://[::1]\" &&\n+\ttest-url-normalize \"scheme://[::1]/\" &&\n+\t! test-url-normalize \"scheme://hos%41/\" &&\n+\ttest-url-normalize \"scheme://[invalid....:/\" &&\n+\ttest-url-normalize \"scheme://invalid....:]/\" &&\n+\t! test-url-normalize \"scheme://invalid....:[/\" &&\n+\t! test-url-normalize \"scheme://invalid....:[\"\n+'\n+\n+test_expect_success 'url port' '\n+\ttest-url-normalize \"xyz://q@some.host:\" &&\n+\ttest-url-normalize \"xyz://q@some.host:456\" &&\n+\ttest-url-normalize \"xyz://q@some.host:0\" &&\n+\ttest-url-normalize \"xyz://q@some.host:99999\" &&\n+\ttest-url-normalize \"http://q@some.host:80\" &&\n+\ttest-url-normalize \"https://q@some.host:443\"\n+\t! test-url-normalize \"http://q@:8008\" &&\n+\t! test-url-normalize \"http://:8080\" &&\n+\t! test-url-normalize \"http://:\" &&\n+\ttest-url-normalize \"xyz://q@some.host:456/\" &&\n+\ttest-url-normalize \"xyz://[::1]:456/\" &&\n+\ttest-url-normalize \"xyz://[::1]:/\" &&\n+\ttest-url-normalize \"xyz://[::1]:000/\" &&\n+\t! test-url-normalize \"xyz://[::1]:0%300/\" &&\n+\t! test-url-normalize \"xyz://[::1]:030f/\"\n+'\n+\n+test_expect_success 'url general escapes' '\n+\t! test-url-normalize \"http://x.y?%fg\" &&\n+\ttest \"$(test-url-normalize -p \"X://W/%7e%41^%3a\")\" = \"x://w/~A%5E%3A\" &&\n+\ttest \"$(test-url-normalize -p \"X://W/:/?#[]@\")\" = \"x://w/:/?#[]@\" &&\n+\ttest \"$(test-url-normalize -p \"X://W/$&()*+,;=\")\" = \"x://w/$&()*+,;=\" &&\n+\ttest \"$(test-url-normalize -p \"X://W/'\\''\")\" = \"x://w/'\\''\" &&\n+\ttest \"$(test-url-normalize -p \"X://W?'\\!'\")\" = \"x://w/?'\\!'\"\n+';#'\n+\n+test_expect_success 'url username/password escapes' '\n+\ttest \"$(test-url-normalize -p \"x://%41%62(^):%70+d@foo\")\" = \"x://Ab(%5E):p+d@foo/\"\n+'\n+\n+test_expect_success 'url normalized lengths' '\n+\ttest \"$(test-url-normalize -l \"Http://%4d%65:%4d^%70@The.Host\")\" = 25 &&\n+\ttest \"$(test-url-normalize -l \"http://%41:%42@x.y/%61/\")\" = 17 &&\n+\ttest \"$(test-url-normalize -l \"http://@x.y/^\")\" = 14\n+'\n+\n+test_expect_success 'url equivalents' '\n+\ttest-url-normalize \"httP://x\" \"Http://X/\" &&\n+\ttest-url-normalize \"Http://%4d%65:%4d^%70@The.Host\" \"hTTP://Me:%4D^p@the.HOST:80/\" &&\n+\ttest-url-normalize \"https://@x.y/^\" \"httpS://x.y:443/^\"\n+'\n+\n+test_done\ndiff --git a/test-url-normalize.c b/test-url-normalize.c\nnew file mode 100644\nindex 0000000..1872964\n--- /dev/null\n+++ b/test-url-normalize.c\n@@ -0,0 +1,62 @@\n+#ifdef NO_CURL\n+\n+int main()\n+{\n+\treturn 125;\n+}\n+\n+#else /* !NO_CURL */\n+\n+#include \"http.c\"\n+\n+#define url_normalize(u,s) http_options_url_normalize(u,s)\n+\n+int main(int argc, char **argv)\n+{\n+\tconst char *usage = \"test-url-normalize [-p | -l] <url1> | <url1> <url2>\";\n+\tchar *url1, *url2;\n+\tint opt_p = 0, opt_l = 0;\n+\n+\t/*\n+\t * For one url, succeed if url_normalize succeeds on it, fail otherwise.\n+\t * For two urls, succeed only if url_normalize succeeds on both and\n+\t * the results compare equal with strcmp.  If -p is given (one url only)\n+\t * and url_normalize succeeds, print the result followed by \"\\n\".  If\n+\t * -l is given (one url only) and url_normalize succeeds, print the\n+\t * returned length in decimal followed by \"\\n\".\n+\t */\n+\n+\tif (argc > 1 && !strcmp(argv[1], \"-p\")) {\n+\t\topt_p = 1;\n+\t\targc--;\n+\t\targv++;\n+\t} else if (argc > 1 && !strcmp(argv[1], \"-l\")) {\n+\t\topt_l = 1;\n+\t\targc--;\n+\t\targv++;\n+\t}\n+\n+\tif (argc < 2 || argc > 3)\n+\t\tdie(usage);\n+\n+\tif (argc == 2) {\n+\t\tsize_t s;\n+\t\turl1 = url_normalize(argv[1], &s);\n+\t\tif (!url1)\n+\t\t\treturn 1;\n+\t\tif (opt_p)\n+\t\t\tprintf(\"%s\\n\", url1);\n+\t\tif (opt_l)\n+\t\t\tprintf(\"%u\\n\", (unsigned)s);\n+\t\treturn 0;\n+\t}\n+\n+\tif (opt_p || opt_l)\n+\t\tdie(usage);\n+\n+\turl1 = url_normalize(argv[1], NULL);\n+\turl2 = url_normalize(argv[2], NULL);\n+\treturn (url1 && url2 && !strcmp(url1, url2)) ? 0 : 1;\n+}\n+\n+#endif /* !NO_CURL */\n-- \n1.8.3\n"},{"id":"223491","messageId":"CAPig+cQTNXgPyD4qiQPyQeSHWY0Y=_Qnd2i9LfVqQDA1DJNDxg@mail.gmail.com","threadId":"34438","inReplyTo":"dcbaa11c8595f48814aa39a75ad18ea@f74d39fa044aa309eaea14b9f57fe79","subject":"Re: [PATCH v5 4/5] config: improve support for http.<url>.* settings","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-07-15T23:12:51Z","receivedAt":"2013-07-15T23:12:51Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Jul 15, 2013 at 5:51 AM, Kyle J. McKay <mackyle@gmail.com> wrote:\n> Improve on the http.<url>.* url matching behavior by first\n> normalizing the urls before they are compared.\n>\n> diff --git a/http.c b/http.c\n> index 758e5b1..d04386e 100644\n> --- a/http.c\n> +++ b/http.c\n> @@ -169,6 +169,210 @@ static void process_curl_messages(void)\n>  }\n>  #endif\n>\n> +static int append_normalized_escapes(struct strbuf *buf,\n> +                                    const char *from,\n> +                                    size_t from_len,\n> +                                    const char *esc_extra,\n> +                                    const char *esc_ok)\n> +{\n> +       /*\n> +        * Append to strbuf buf characters from string from with length from_len\n\ns/from string from/from string/\n\n> +        * while unescaping characters that do not need to be escaped and\n> +        * escaping characters that do.  The set of characters to escape (the\n> +        * complement of which is unescaped) starts out as the RFC 3986 unsafe\n> +        * characters (0x00-0x1F,0x7F-0xFF,\" <>\\\"#%{}|\\\\^`\").  If esc_extra\n> +        * is not NULL, those additional characters will also always be escaped.\n> +        * If esc_ok is not NULL, those characters will be left escaped if found\n> +        * that way, but will not be unescaped otherwise (used for delimiters).\n> +        * If a %-escape sequence is encountered that is not followed by 2\n> +        * hexadecimal digits, the sequence is invalid and false (0) will be\n> +        * returned.  Otherwise true (1) will be returned for success.\n> +        *\n> +        * Note that all %-escape sequences will be normalized to UPPERCASE\n> +        * as indicated in RFC 3986.  Unless included in esc_extra or esc_ok\n> +        * alphanumerics and \"-._~\" will always be unescaped as per RFC 3986.\n> +        */\n"},{"id":"223518","messageId":"4D163C69-2228-4895-AE47-377A5A71FD5E@gmail.com","threadId":"34438","inReplyTo":"CAPig+cQTNXgPyD4qiQPyQeSHWY0Y=_Qnd2i9LfVqQDA1DJNDxg@mail.gmail.com","subject":"Re: [PATCH v5 4/5] config: improve support for http.<url>.* settings","fromName":"Kyle J. McKay","fromEmail":"mackyle@gmail.com","sentAt":"2013-07-16T09:53:38Z","receivedAt":"2013-07-16T09:53:38Z","isPatch":true,"sender":{"key":"mackyle@gmail.com","avatar":"https://avatars.githubusercontent.com/u/813346?v=4"},"body":"On Jul 15, 2013, at 16:12, Eric Sunshine wrote:\n> On Mon, Jul 15, 2013 at 5:51 AM, Kyle J. McKay <mackyle@gmail.com>  \n> wrote:\n>> +static int append_normalized_escapes(struct strbuf *buf,\n>> +                                    const char *from,\n>> +                                    size_t from_len,\n>> +                                    const char *esc_extra,\n>> +                                    const char *esc_ok)\n>> +{\n>> +       /*\n>> +        * Append to strbuf buf characters from string from with  \n>> length from_len\n>\n> s/from string from/from string/\n\nHmmm.  Actually it's meant to say \"from string <parameter with name  \nfrom>\".\n\nDo quotes make it read better:\n\n   from string `from'\n\nOr do you think it needs to be:\n\n   from string parameter `from'\n"},{"id":"223519","messageId":"CAPig+cQnm6=YEKLd-LBhsrUH75Lnn9whjS3KeZMaNVRMWAS2RQ@mail.gmail.com","threadId":"34438","inReplyTo":"4D163C69-2228-4895-AE47-377A5A71FD5E@gmail.com","subject":"Re: [PATCH v5 4/5] config: improve support for http.<url>.* settings","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-07-16T10:01:37Z","receivedAt":"2013-07-16T10:01:37Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Jul 16, 2013 at 5:53 AM, Kyle J. McKay <mackyle@gmail.com> wrote:\n> On Jul 15, 2013, at 16:12, Eric Sunshine wrote:\n>>\n>> On Mon, Jul 15, 2013 at 5:51 AM, Kyle J. McKay <mackyle@gmail.com> wrote:\n>>>\n>>> +static int append_normalized_escapes(struct strbuf *buf,\n>>> +                                    const char *from,\n>>> +                                    size_t from_len,\n>>> +                                    const char *esc_extra,\n>>> +                                    const char *esc_ok)\n>>> +{\n>>> +       /*\n>>> +        * Append to strbuf buf characters from string from with length\n>>> from_len\n>>\n>>\n>> s/from string from/from string/\n>\n> Hmmm.  Actually it's meant to say \"from string <parameter with name from>\".\n>\n> Do quotes make it read better:\n>\n>   from string `from'\n>\n> Or do you think it needs to be:\n>\n>   from string parameter `from'\n\nAh, I see now. Thanks for clarifying. Quoting 'from' (and the other\narguments) probably would make it sufficiently readable.\n"}]}