Merge branch 'sk/tag-contains-wo-recursion'
[gitweb.git] / http.c
diff --git a/http.c b/http.c
index 94e1afdee7890825dfda0946b999f524265603fc..040f362a6a299618288c9249588ceb7aed6f3011 100644 (file)
--- a/http.c
+++ b/http.c
@@ -1,3 +1,4 @@
+#include "git-compat-util.h"
 #include "http.h"
 #include "pack.h"
 #include "sideband.h"
@@ -300,6 +301,9 @@ static CURL *get_curl_handle(void)
 {
        CURL *result = curl_easy_init();
 
+       if (!result)
+               die("curl_easy_init failed");
+
        if (!curl_ssl_verify) {
                curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0);
                curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 0);
@@ -399,7 +403,8 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
        git_config(urlmatch_config_entry, &config);
        free(normalized_url);
 
-       curl_global_init(CURL_GLOBAL_ALL);
+       if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
+               die("curl_global_init failed");
 
        http_proactive_auth = proactive_auth;
 
@@ -417,10 +422,8 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
        }
 
        curlm = curl_multi_init();
-       if (curlm == NULL) {
-               fprintf(stderr, "Error creating curl multi handle.\n");
-               exit(1);
-       }
+       if (!curlm)
+               die("curl_multi_init failed");
 #endif
 
        if (getenv("GIT_SSL_NO_VERIFY"))
@@ -906,6 +909,83 @@ static CURLcode curlinfo_strbuf(CURL *curl, CURLINFO info, struct strbuf *buf)
        return ret;
 }
 
+/*
+ * Check for and extract a content-type parameter. "raw"
+ * should be positioned at the start of the potential
+ * parameter, with any whitespace already removed.
+ *
+ * "name" is the name of the parameter. The value is appended
+ * to "out".
+ */
+static int extract_param(const char *raw, const char *name,
+                        struct strbuf *out)
+{
+       size_t len = strlen(name);
+
+       if (strncasecmp(raw, name, len))
+               return -1;
+       raw += len;
+
+       if (*raw != '=')
+               return -1;
+       raw++;
+
+       while (*raw && !isspace(*raw) && *raw != ';')
+               strbuf_addch(out, *raw++);
+       return 0;
+}
+
+/*
+ * Extract a normalized version of the content type, with any
+ * spaces suppressed, all letters lowercased, and no trailing ";"
+ * or parameters.
+ *
+ * Note that we will silently remove even invalid whitespace. For
+ * example, "text / plain" is specifically forbidden by RFC 2616,
+ * but "text/plain" is the only reasonable output, and this keeps
+ * our code simple.
+ *
+ * If the "charset" argument is not NULL, store the value of any
+ * charset parameter there.
+ *
+ * Example:
+ *   "TEXT/PLAIN; charset=utf-8" -> "text/plain", "utf-8"
+ *   "text / plain" -> "text/plain"
+ */
+static void extract_content_type(struct strbuf *raw, struct strbuf *type,
+                                struct strbuf *charset)
+{
+       const char *p;
+
+       strbuf_reset(type);
+       strbuf_grow(type, raw->len);
+       for (p = raw->buf; *p; p++) {
+               if (isspace(*p))
+                       continue;
+               if (*p == ';') {
+                       p++;
+                       break;
+               }
+               strbuf_addch(type, tolower(*p));
+       }
+
+       if (!charset)
+               return;
+
+       strbuf_reset(charset);
+       while (*p) {
+               while (isspace(*p) || *p == ';')
+                       p++;
+               if (!extract_param(p, "charset", charset))
+                       return;
+               while (*p && !isspace(*p))
+                       p++;
+       }
+
+       if (!charset->len && starts_with(type->buf, "text/"))
+               strbuf_addstr(charset, "ISO-8859-1");
+}
+
 /* http_request() targets */
 #define HTTP_REQUEST_STRBUF    0
 #define HTTP_REQUEST_FILE      1
@@ -957,9 +1037,13 @@ static int http_request(const char *url,
 
        ret = run_one_slot(slot, &results);
 
-       if (options && options->content_type)
-               curlinfo_strbuf(slot->curl, CURLINFO_CONTENT_TYPE,
-                               options->content_type);
+       if (options && options->content_type) {
+               struct strbuf raw = STRBUF_INIT;
+               curlinfo_strbuf(slot->curl, CURLINFO_CONTENT_TYPE, &raw);
+               extract_content_type(&raw, options->content_type,
+                                    options->charset);
+               strbuf_release(&raw);
+       }
 
        if (options && options->effective_url)
                curlinfo_strbuf(slot->curl, CURLINFO_EFFECTIVE_URL,
@@ -1006,11 +1090,10 @@ static int update_url_from_redirect(struct strbuf *base,
        if (!strcmp(asked, got->buf))
                return 0;
 
-       if (!starts_with(asked, base->buf))
+       if (!skip_prefix(asked, base->buf, &tail))
                die("BUG: update_url_from_redirect: %s is not a superset of %s",
                    asked, base->buf);
 
-       tail = asked + base->len;
        tail_len = strlen(tail);
 
        if (got->len < tail_len ||
@@ -1252,7 +1335,7 @@ int finish_http_pack_request(struct http_pack_request *preq)
        struct packed_git **lst;
        struct packed_git *p = preq->target;
        char *tmp_idx;
-       struct child_process ip;
+       struct child_process ip = CHILD_PROCESS_INIT;
        const char *ip_argv[8];
 
        close_pack_index(p);
@@ -1275,7 +1358,6 @@ int finish_http_pack_request(struct http_pack_request *preq)
        ip_argv[3] = preq->tmpfile;
        ip_argv[4] = NULL;
 
-       memset(&ip, 0, sizeof(ip));
        ip.argv = ip_argv;
        ip.git_cmd = 1;
        ip.no_stdin = 1;