Merge branch 'mk/unpack-careful'
[gitweb.git] / http.c
diff --git a/http.c b/http.c
index e4aa9c19dbe7fd2ed948d5cca255a7db44a9472f..256a5f15f40a8d9389560e1fb08e34a56e9f7140 100644 (file)
--- a/http.c
+++ b/http.c
@@ -4,61 +4,55 @@ int data_received;
 int active_requests = 0;
 
 #ifdef USE_CURL_MULTI
-int max_requests = -1;
-CURLM *curlm;
+static int max_requests = -1;
+static CURLM *curlm;
 #endif
 #ifndef NO_CURL_EASY_DUPHANDLE
-CURL *curl_default;
+static CURL *curl_default;
 #endif
 char curl_errorstr[CURL_ERROR_SIZE];
 
-int curl_ssl_verify = -1;
-char *ssl_cert = NULL;
+static int curl_ssl_verify = -1;
+static char *ssl_cert = NULL;
 #if LIBCURL_VERSION_NUM >= 0x070902
-char *ssl_key = NULL;
+static char *ssl_key = NULL;
 #endif
 #if LIBCURL_VERSION_NUM >= 0x070908
-char *ssl_capath = NULL;
+static char *ssl_capath = NULL;
 #endif
-char *ssl_cainfo = NULL;
-long curl_low_speed_limit = -1;
-long curl_low_speed_time = -1;
-int curl_ftp_no_epsv = 0;
-char *curl_http_proxy = NULL;
+static char *ssl_cainfo = NULL;
+static long curl_low_speed_limit = -1;
+static long curl_low_speed_time = -1;
+static int curl_ftp_no_epsv = 0;
+static char *curl_http_proxy = NULL;
 
-struct curl_slist *pragma_header;
+static struct curl_slist *pragma_header;
 
-struct active_request_slot *active_queue_head = NULL;
+static struct active_request_slot *active_queue_head = NULL;
 
 size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb,
                           struct buffer *buffer)
 {
        size_t size = eltsize * nmemb;
-       if (size > buffer->size - buffer->posn)
-               size = buffer->size - buffer->posn;
-       memcpy(ptr, (char *) buffer->buffer + buffer->posn, size);
+       if (size > buffer->buf.len - buffer->posn)
+               size = buffer->buf.len - buffer->posn;
+       memcpy(ptr, buffer->buf.buf + buffer->posn, size);
        buffer->posn += size;
+
        return size;
 }
 
 size_t fwrite_buffer(const void *ptr, size_t eltsize,
-                           size_t nmemb, struct buffer *buffer)
+                           size_t nmemb, struct strbuf *buffer)
 {
        size_t size = eltsize * nmemb;
-       if (size > buffer->size - buffer->posn) {
-               buffer->size = buffer->size * 3 / 2;
-               if (buffer->size < buffer->posn + size)
-                       buffer->size = buffer->posn + size;
-               buffer->buffer = xrealloc(buffer->buffer, buffer->size);
-       }
-       memcpy((char *) buffer->buffer + buffer->posn, ptr, size);
-       buffer->posn += size;
+       strbuf_add(buffer, ptr, size);
        data_received++;
        return size;
 }
 
 size_t fwrite_null(const void *ptr, size_t eltsize,
-                         size_t nmemb, struct buffer *buffer)
+                         size_t nmemb, struct strbuf *buffer)
 {
        data_received++;
        return eltsize * nmemb;
@@ -107,16 +101,18 @@ static int http_options(const char *var, const char *value)
 
        if (!strcmp("http.sslcert", var)) {
                if (ssl_cert == NULL) {
-                       ssl_cert = xmalloc(strlen(value)+1);
-                       strcpy(ssl_cert, value);
+                       if (!value)
+                               return config_error_nonbool(var);
+                       ssl_cert = xstrdup(value);
                }
                return 0;
        }
 #if LIBCURL_VERSION_NUM >= 0x070902
        if (!strcmp("http.sslkey", var)) {
                if (ssl_key == NULL) {
-                       ssl_key = xmalloc(strlen(value)+1);
-                       strcpy(ssl_key, value);
+                       if (!value)
+                               return config_error_nonbool(var);
+                       ssl_key = xstrdup(value);
                }
                return 0;
        }
@@ -124,16 +120,18 @@ static int http_options(const char *var, const char *value)
 #if LIBCURL_VERSION_NUM >= 0x070908
        if (!strcmp("http.sslcapath", var)) {
                if (ssl_capath == NULL) {
-                       ssl_capath = xmalloc(strlen(value)+1);
-                       strcpy(ssl_capath, value);
+                       if (!value)
+                               return config_error_nonbool(var);
+                       ssl_capath = xstrdup(value);
                }
                return 0;
        }
 #endif
        if (!strcmp("http.sslcainfo", var)) {
                if (ssl_cainfo == NULL) {
-                       ssl_cainfo = xmalloc(strlen(value)+1);
-                       strcpy(ssl_cainfo, value);
+                       if (!value)
+                               return config_error_nonbool(var);
+                       ssl_cainfo = xstrdup(value);
                }
                return 0;
        }
@@ -163,8 +161,9 @@ static int http_options(const char *var, const char *value)
        }
        if (!strcmp("http.proxy", var)) {
                if (curl_http_proxy == NULL) {
-                       curl_http_proxy = xmalloc(strlen(value)+1);
-                       strcpy(curl_http_proxy, value);
+                       if (!value)
+                               return config_error_nonbool(var);
+                       curl_http_proxy = xstrdup(value);
                }
                return 0;
        }
@@ -219,13 +218,16 @@ static CURL* get_curl_handle(void)
        return result;
 }
 
-void http_init(void)
+void http_init(struct remote *remote)
 {
        char *low_speed_limit;
        char *low_speed_time;
 
        curl_global_init(CURL_GLOBAL_ALL);
 
+       if (remote && remote->http_proxy)
+               curl_http_proxy = xstrdup(remote->http_proxy);
+
        pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
 
 #ifdef USE_CURL_MULTI
@@ -282,23 +284,15 @@ void http_init(void)
 void http_cleanup(void)
 {
        struct active_request_slot *slot = active_queue_head;
-#ifdef USE_CURL_MULTI
-       char *wait_url;
-#endif
 
        while (slot != NULL) {
                struct active_request_slot *next = slot->next;
+               if (slot->curl != NULL) {
 #ifdef USE_CURL_MULTI
-               if (slot->in_use) {
-                       curl_easy_getinfo(slot->curl,
-                                         CURLINFO_EFFECTIVE_URL,
-                                         &wait_url);
-                       fprintf(stderr, "Waiting for %s\n", wait_url);
-                       run_active_slot(slot);
-               }
+                       curl_multi_remove_handle(curlm, slot->curl);
 #endif
-               if (slot->curl != NULL)
                        curl_easy_cleanup(slot->curl);
+               }
                free(slot);
                slot = next;
        }
@@ -315,6 +309,11 @@ void http_cleanup(void)
 
        curl_slist_free_all(pragma_header);
        pragma_header = NULL;
+
+       if (curl_http_proxy) {
+               free(curl_http_proxy);
+               curl_http_proxy = NULL;
+       }
 }
 
 struct active_request_slot *get_active_slot(void)
@@ -370,7 +369,6 @@ struct active_request_slot *get_active_slot(void)
        slot->finished = NULL;
        slot->callback_data = NULL;
        slot->callback_func = NULL;
-       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
        curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
        curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL);
@@ -508,8 +506,8 @@ void run_active_slot(struct active_request_slot *slot)
 
 static void closedown_active_slot(struct active_request_slot *slot)
 {
-        active_requests--;
-        slot->in_use = 0;
+       active_requests--;
+       slot->in_use = 0;
 }
 
 void release_active_slot(struct active_request_slot *slot)
@@ -530,7 +528,7 @@ void release_active_slot(struct active_request_slot *slot)
 static void finish_active_slot(struct active_request_slot *slot)
 {
        closedown_active_slot(slot);
-        curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);
+       curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);
 
        if (slot->finished != NULL)
                (*slot->finished) = 1;
@@ -541,10 +539,10 @@ static void finish_active_slot(struct active_request_slot *slot)
                slot->results->http_code = slot->http_code;
        }
 
-        /* Run callback if appropriate */
-        if (slot->callback_func != NULL) {
-                slot->callback_func(slot->callback_data);
-        }
+       /* Run callback if appropriate */
+       if (slot->callback_func != NULL) {
+               slot->callback_func(slot->callback_data);
+       }
 }
 
 void finish_all_active_slots(void)
@@ -559,3 +557,85 @@ void finish_all_active_slots(void)
                        slot = slot->next;
                }
 }
+
+static inline int needs_quote(int ch)
+{
+       if (((ch >= 'A') && (ch <= 'Z'))
+                       || ((ch >= 'a') && (ch <= 'z'))
+                       || ((ch >= '0') && (ch <= '9'))
+                       || (ch == '/')
+                       || (ch == '-')
+                       || (ch == '.'))
+               return 0;
+       return 1;
+}
+
+static inline int hex(int v)
+{
+       if (v < 10) return '0' + v;
+       else return 'A' + v - 10;
+}
+
+static char *quote_ref_url(const char *base, const char *ref)
+{
+       const char *cp;
+       char *dp, *qref;
+       int len, baselen, ch;
+
+       baselen = strlen(base);
+       len = baselen + 7; /* "/refs/" + NUL */
+       for (cp = ref; (ch = *cp) != 0; cp++, len++)
+               if (needs_quote(ch))
+                       len += 2; /* extra two hex plus replacement % */
+       qref = xmalloc(len);
+       memcpy(qref, base, baselen);
+       memcpy(qref + baselen, "/refs/", 6);
+       for (cp = ref, dp = qref + baselen + 6; (ch = *cp) != 0; cp++) {
+               if (needs_quote(ch)) {
+                       *dp++ = '%';
+                       *dp++ = hex((ch >> 4) & 0xF);
+                       *dp++ = hex(ch & 0xF);
+               }
+               else
+                       *dp++ = ch;
+       }
+       *dp = 0;
+
+       return qref;
+}
+
+int http_fetch_ref(const char *base, const char *ref, unsigned char *sha1)
+{
+       char *url;
+       struct strbuf buffer = STRBUF_INIT;
+       struct active_request_slot *slot;
+       struct slot_results results;
+       int ret;
+
+       url = quote_ref_url(base, ref);
+       slot = get_active_slot();
+       slot->results = &results;
+       curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
+       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
+       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
+       curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+       if (start_active_slot(slot)) {
+               run_active_slot(slot);
+               if (results.curl_result == CURLE_OK) {
+                       strbuf_rtrim(&buffer);
+                       if (buffer.len == 40)
+                               ret = get_sha1_hex(buffer.buf, sha1);
+                       else
+                               ret = 1;
+               } else {
+                       ret = error("Couldn't get %s for %s\n%s",
+                                   url, ref, curl_errorstr);
+               }
+       } else {
+               ret = error("Unable to start request");
+       }
+
+       strbuf_release(&buffer);
+       free(url);
+       return ret;
+}