http.con commit diff -p: squelch "diff --git" header for stat-dirty paths (b3f01ff)
   1#include "http.h"
   2#include "pack.h"
   3#include "sideband.h"
   4#include "run-command.h"
   5
   6int data_received;
   7int active_requests;
   8int http_is_verbose;
   9size_t http_post_buffer = 16 * LARGE_PACKET_MAX;
  10
  11#if LIBCURL_VERSION_NUM >= 0x070a06
  12#define LIBCURL_CAN_HANDLE_AUTH_ANY
  13#endif
  14
  15static int min_curl_sessions = 1;
  16static int curl_session_count;
  17#ifdef USE_CURL_MULTI
  18static int max_requests = -1;
  19static CURLM *curlm;
  20#endif
  21#ifndef NO_CURL_EASY_DUPHANDLE
  22static CURL *curl_default;
  23#endif
  24
  25#define PREV_BUF_SIZE 4096
  26#define RANGE_HEADER_SIZE 30
  27
  28char curl_errorstr[CURL_ERROR_SIZE];
  29
  30static int curl_ssl_verify = -1;
  31static const char *ssl_cert;
  32#if LIBCURL_VERSION_NUM >= 0x070903
  33static const char *ssl_key;
  34#endif
  35#if LIBCURL_VERSION_NUM >= 0x070908
  36static const char *ssl_capath;
  37#endif
  38static const char *ssl_cainfo;
  39static long curl_low_speed_limit = -1;
  40static long curl_low_speed_time = -1;
  41static int curl_ftp_no_epsv;
  42static const char *curl_http_proxy;
  43static char *user_name, *user_pass;
  44static const char *user_agent;
  45
  46#if LIBCURL_VERSION_NUM >= 0x071700
  47/* Use CURLOPT_KEYPASSWD as is */
  48#elif LIBCURL_VERSION_NUM >= 0x070903
  49#define CURLOPT_KEYPASSWD CURLOPT_SSLKEYPASSWD
  50#else
  51#define CURLOPT_KEYPASSWD CURLOPT_SSLCERTPASSWD
  52#endif
  53
  54static char *ssl_cert_password;
  55static int ssl_cert_password_required;
  56
  57static struct curl_slist *pragma_header;
  58static struct curl_slist *no_pragma_header;
  59
  60static struct active_request_slot *active_queue_head;
  61
  62size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
  63{
  64        size_t size = eltsize * nmemb;
  65        struct buffer *buffer = buffer_;
  66
  67        if (size > buffer->buf.len - buffer->posn)
  68                size = buffer->buf.len - buffer->posn;
  69        memcpy(ptr, buffer->buf.buf + buffer->posn, size);
  70        buffer->posn += size;
  71
  72        return size;
  73}
  74
  75#ifndef NO_CURL_IOCTL
  76curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp)
  77{
  78        struct buffer *buffer = clientp;
  79
  80        switch (cmd) {
  81        case CURLIOCMD_NOP:
  82                return CURLIOE_OK;
  83
  84        case CURLIOCMD_RESTARTREAD:
  85                buffer->posn = 0;
  86                return CURLIOE_OK;
  87
  88        default:
  89                return CURLIOE_UNKNOWNCMD;
  90        }
  91}
  92#endif
  93
  94size_t fwrite_buffer(const void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
  95{
  96        size_t size = eltsize * nmemb;
  97        struct strbuf *buffer = buffer_;
  98
  99        strbuf_add(buffer, ptr, size);
 100        data_received++;
 101        return size;
 102}
 103
 104size_t fwrite_null(const void *ptr, size_t eltsize, size_t nmemb, void *strbuf)
 105{
 106        data_received++;
 107        return eltsize * nmemb;
 108}
 109
 110#ifdef USE_CURL_MULTI
 111static void process_curl_messages(void)
 112{
 113        int num_messages;
 114        struct active_request_slot *slot;
 115        CURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages);
 116
 117        while (curl_message != NULL) {
 118                if (curl_message->msg == CURLMSG_DONE) {
 119                        int curl_result = curl_message->data.result;
 120                        slot = active_queue_head;
 121                        while (slot != NULL &&
 122                               slot->curl != curl_message->easy_handle)
 123                                slot = slot->next;
 124                        if (slot != NULL) {
 125                                curl_multi_remove_handle(curlm, slot->curl);
 126                                slot->curl_result = curl_result;
 127                                finish_active_slot(slot);
 128                        } else {
 129                                fprintf(stderr, "Received DONE message for unknown request!\n");
 130                        }
 131                } else {
 132                        fprintf(stderr, "Unknown CURL message received: %d\n",
 133                                (int)curl_message->msg);
 134                }
 135                curl_message = curl_multi_info_read(curlm, &num_messages);
 136        }
 137}
 138#endif
 139
 140static int http_options(const char *var, const char *value, void *cb)
 141{
 142        if (!strcmp("http.sslverify", var)) {
 143                curl_ssl_verify = git_config_bool(var, value);
 144                return 0;
 145        }
 146        if (!strcmp("http.sslcert", var))
 147                return git_config_string(&ssl_cert, var, value);
 148#if LIBCURL_VERSION_NUM >= 0x070903
 149        if (!strcmp("http.sslkey", var))
 150                return git_config_string(&ssl_key, var, value);
 151#endif
 152#if LIBCURL_VERSION_NUM >= 0x070908
 153        if (!strcmp("http.sslcapath", var))
 154                return git_config_string(&ssl_capath, var, value);
 155#endif
 156        if (!strcmp("http.sslcainfo", var))
 157                return git_config_string(&ssl_cainfo, var, value);
 158        if (!strcmp("http.sslcertpasswordprotected", var)) {
 159                if (git_config_bool(var, value))
 160                        ssl_cert_password_required = 1;
 161                return 0;
 162        }
 163        if (!strcmp("http.minsessions", var)) {
 164                min_curl_sessions = git_config_int(var, value);
 165#ifndef USE_CURL_MULTI
 166                if (min_curl_sessions > 1)
 167                        min_curl_sessions = 1;
 168#endif
 169                return 0;
 170        }
 171#ifdef USE_CURL_MULTI
 172        if (!strcmp("http.maxrequests", var)) {
 173                max_requests = git_config_int(var, value);
 174                return 0;
 175        }
 176#endif
 177        if (!strcmp("http.lowspeedlimit", var)) {
 178                curl_low_speed_limit = (long)git_config_int(var, value);
 179                return 0;
 180        }
 181        if (!strcmp("http.lowspeedtime", var)) {
 182                curl_low_speed_time = (long)git_config_int(var, value);
 183                return 0;
 184        }
 185
 186        if (!strcmp("http.noepsv", var)) {
 187                curl_ftp_no_epsv = git_config_bool(var, value);
 188                return 0;
 189        }
 190        if (!strcmp("http.proxy", var))
 191                return git_config_string(&curl_http_proxy, var, value);
 192
 193        if (!strcmp("http.postbuffer", var)) {
 194                http_post_buffer = git_config_int(var, value);
 195                if (http_post_buffer < LARGE_PACKET_MAX)
 196                        http_post_buffer = LARGE_PACKET_MAX;
 197                return 0;
 198        }
 199
 200        if (!strcmp("http.useragent", var))
 201                return git_config_string(&user_agent, var, value);
 202
 203        /* Fall back on the default ones */
 204        return git_default_config(var, value, cb);
 205}
 206
 207static void init_curl_http_auth(CURL *result)
 208{
 209        if (user_name) {
 210                struct strbuf up = STRBUF_INIT;
 211                if (!user_pass)
 212                        user_pass = xstrdup(git_getpass("Password: "));
 213                strbuf_addf(&up, "%s:%s", user_name, user_pass);
 214                curl_easy_setopt(result, CURLOPT_USERPWD,
 215                                 strbuf_detach(&up, NULL));
 216        }
 217}
 218
 219static int has_cert_password(void)
 220{
 221        if (ssl_cert_password != NULL)
 222                return 1;
 223        if (ssl_cert == NULL || ssl_cert_password_required != 1)
 224                return 0;
 225        /* Only prompt the user once. */
 226        ssl_cert_password_required = -1;
 227        ssl_cert_password = git_getpass("Certificate Password: ");
 228        if (ssl_cert_password != NULL) {
 229                ssl_cert_password = xstrdup(ssl_cert_password);
 230                return 1;
 231        } else
 232                return 0;
 233}
 234
 235static CURL *get_curl_handle(void)
 236{
 237        CURL *result = curl_easy_init();
 238
 239        if (!curl_ssl_verify) {
 240                curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0);
 241                curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 0);
 242        } else {
 243                /* Verify authenticity of the peer's certificate */
 244                curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 1);
 245                /* The name in the cert must match whom we tried to connect */
 246                curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 2);
 247        }
 248
 249#if LIBCURL_VERSION_NUM >= 0x070907
 250        curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
 251#endif
 252#ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
 253        curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
 254#endif
 255
 256        init_curl_http_auth(result);
 257
 258        if (ssl_cert != NULL)
 259                curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
 260        if (has_cert_password())
 261                curl_easy_setopt(result, CURLOPT_KEYPASSWD, ssl_cert_password);
 262#if LIBCURL_VERSION_NUM >= 0x070903
 263        if (ssl_key != NULL)
 264                curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
 265#endif
 266#if LIBCURL_VERSION_NUM >= 0x070908
 267        if (ssl_capath != NULL)
 268                curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);
 269#endif
 270        if (ssl_cainfo != NULL)
 271                curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
 272        curl_easy_setopt(result, CURLOPT_FAILONERROR, 1);
 273
 274        if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
 275                curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,
 276                                 curl_low_speed_limit);
 277                curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME,
 278                                 curl_low_speed_time);
 279        }
 280
 281        curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
 282
 283        if (getenv("GIT_CURL_VERBOSE"))
 284                curl_easy_setopt(result, CURLOPT_VERBOSE, 1);
 285
 286        curl_easy_setopt(result, CURLOPT_USERAGENT,
 287                user_agent ? user_agent : GIT_HTTP_USER_AGENT);
 288
 289        if (curl_ftp_no_epsv)
 290                curl_easy_setopt(result, CURLOPT_FTP_USE_EPSV, 0);
 291
 292        if (curl_http_proxy)
 293                curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
 294
 295        return result;
 296}
 297
 298static void http_auth_init(const char *url)
 299{
 300        char *at, *colon, *cp, *slash;
 301        int len;
 302
 303        cp = strstr(url, "://");
 304        if (!cp)
 305                return;
 306
 307        /*
 308         * Ok, the URL looks like "proto://something".  Which one?
 309         * "proto://<user>:<pass>@<host>/...",
 310         * "proto://<user>@<host>/...", or just
 311         * "proto://<host>/..."?
 312         */
 313        cp += 3;
 314        at = strchr(cp, '@');
 315        colon = strchr(cp, ':');
 316        slash = strchrnul(cp, '/');
 317        if (!at || slash <= at)
 318                return; /* No credentials */
 319        if (!colon || at <= colon) {
 320                /* Only username */
 321                len = at - cp;
 322                user_name = xmalloc(len + 1);
 323                memcpy(user_name, cp, len);
 324                user_name[len] = '\0';
 325                user_pass = NULL;
 326        } else {
 327                len = colon - cp;
 328                user_name = xmalloc(len + 1);
 329                memcpy(user_name, cp, len);
 330                user_name[len] = '\0';
 331                len = at - (colon + 1);
 332                user_pass = xmalloc(len + 1);
 333                memcpy(user_pass, colon + 1, len);
 334                user_pass[len] = '\0';
 335        }
 336}
 337
 338static void set_from_env(const char **var, const char *envname)
 339{
 340        const char *val = getenv(envname);
 341        if (val)
 342                *var = val;
 343}
 344
 345void http_init(struct remote *remote)
 346{
 347        char *low_speed_limit;
 348        char *low_speed_time;
 349
 350        http_is_verbose = 0;
 351
 352        git_config(http_options, NULL);
 353
 354        curl_global_init(CURL_GLOBAL_ALL);
 355
 356        if (remote && remote->http_proxy)
 357                curl_http_proxy = xstrdup(remote->http_proxy);
 358
 359        pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
 360        no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
 361
 362#ifdef USE_CURL_MULTI
 363        {
 364                char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS");
 365                if (http_max_requests != NULL)
 366                        max_requests = atoi(http_max_requests);
 367        }
 368
 369        curlm = curl_multi_init();
 370        if (curlm == NULL) {
 371                fprintf(stderr, "Error creating curl multi handle.\n");
 372                exit(1);
 373        }
 374#endif
 375
 376        if (getenv("GIT_SSL_NO_VERIFY"))
 377                curl_ssl_verify = 0;
 378
 379        set_from_env(&ssl_cert, "GIT_SSL_CERT");
 380#if LIBCURL_VERSION_NUM >= 0x070903
 381        set_from_env(&ssl_key, "GIT_SSL_KEY");
 382#endif
 383#if LIBCURL_VERSION_NUM >= 0x070908
 384        set_from_env(&ssl_capath, "GIT_SSL_CAPATH");
 385#endif
 386        set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");
 387
 388        set_from_env(&user_agent, "GIT_HTTP_USER_AGENT");
 389
 390        low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT");
 391        if (low_speed_limit != NULL)
 392                curl_low_speed_limit = strtol(low_speed_limit, NULL, 10);
 393        low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME");
 394        if (low_speed_time != NULL)
 395                curl_low_speed_time = strtol(low_speed_time, NULL, 10);
 396
 397        if (curl_ssl_verify == -1)
 398                curl_ssl_verify = 1;
 399
 400        curl_session_count = 0;
 401#ifdef USE_CURL_MULTI
 402        if (max_requests < 1)
 403                max_requests = DEFAULT_MAX_REQUESTS;
 404#endif
 405
 406        if (getenv("GIT_CURL_FTP_NO_EPSV"))
 407                curl_ftp_no_epsv = 1;
 408
 409        if (remote && remote->url && remote->url[0]) {
 410                http_auth_init(remote->url[0]);
 411                if (!ssl_cert_password_required &&
 412                    getenv("GIT_SSL_CERT_PASSWORD_PROTECTED") &&
 413                    !prefixcmp(remote->url[0], "https://"))
 414                        ssl_cert_password_required = 1;
 415        }
 416
 417#ifndef NO_CURL_EASY_DUPHANDLE
 418        curl_default = get_curl_handle();
 419#endif
 420}
 421
 422void http_cleanup(void)
 423{
 424        struct active_request_slot *slot = active_queue_head;
 425
 426        while (slot != NULL) {
 427                struct active_request_slot *next = slot->next;
 428                if (slot->curl != NULL) {
 429#ifdef USE_CURL_MULTI
 430                        curl_multi_remove_handle(curlm, slot->curl);
 431#endif
 432                        curl_easy_cleanup(slot->curl);
 433                }
 434                free(slot);
 435                slot = next;
 436        }
 437        active_queue_head = NULL;
 438
 439#ifndef NO_CURL_EASY_DUPHANDLE
 440        curl_easy_cleanup(curl_default);
 441#endif
 442
 443#ifdef USE_CURL_MULTI
 444        curl_multi_cleanup(curlm);
 445#endif
 446        curl_global_cleanup();
 447
 448        curl_slist_free_all(pragma_header);
 449        pragma_header = NULL;
 450
 451        curl_slist_free_all(no_pragma_header);
 452        no_pragma_header = NULL;
 453
 454        if (curl_http_proxy) {
 455                free((void *)curl_http_proxy);
 456                curl_http_proxy = NULL;
 457        }
 458
 459        if (ssl_cert_password != NULL) {
 460                memset(ssl_cert_password, 0, strlen(ssl_cert_password));
 461                free(ssl_cert_password);
 462                ssl_cert_password = NULL;
 463        }
 464        ssl_cert_password_required = 0;
 465}
 466
 467struct active_request_slot *get_active_slot(void)
 468{
 469        struct active_request_slot *slot = active_queue_head;
 470        struct active_request_slot *newslot;
 471
 472#ifdef USE_CURL_MULTI
 473        int num_transfers;
 474
 475        /* Wait for a slot to open up if the queue is full */
 476        while (active_requests >= max_requests) {
 477                curl_multi_perform(curlm, &num_transfers);
 478                if (num_transfers < active_requests)
 479                        process_curl_messages();
 480        }
 481#endif
 482
 483        while (slot != NULL && slot->in_use)
 484                slot = slot->next;
 485
 486        if (slot == NULL) {
 487                newslot = xmalloc(sizeof(*newslot));
 488                newslot->curl = NULL;
 489                newslot->in_use = 0;
 490                newslot->next = NULL;
 491
 492                slot = active_queue_head;
 493                if (slot == NULL) {
 494                        active_queue_head = newslot;
 495                } else {
 496                        while (slot->next != NULL)
 497                                slot = slot->next;
 498                        slot->next = newslot;
 499                }
 500                slot = newslot;
 501        }
 502
 503        if (slot->curl == NULL) {
 504#ifdef NO_CURL_EASY_DUPHANDLE
 505                slot->curl = get_curl_handle();
 506#else
 507                slot->curl = curl_easy_duphandle(curl_default);
 508#endif
 509                curl_session_count++;
 510        }
 511
 512        active_requests++;
 513        slot->in_use = 1;
 514        slot->local = NULL;
 515        slot->results = NULL;
 516        slot->finished = NULL;
 517        slot->callback_data = NULL;
 518        slot->callback_func = NULL;
 519        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
 520        curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
 521        curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL);
 522        curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, NULL);
 523        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, NULL);
 524        curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);
 525        curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
 526
 527        return slot;
 528}
 529
 530int start_active_slot(struct active_request_slot *slot)
 531{
 532#ifdef USE_CURL_MULTI
 533        CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl);
 534        int num_transfers;
 535
 536        if (curlm_result != CURLM_OK &&
 537            curlm_result != CURLM_CALL_MULTI_PERFORM) {
 538                active_requests--;
 539                slot->in_use = 0;
 540                return 0;
 541        }
 542
 543        /*
 544         * We know there must be something to do, since we just added
 545         * something.
 546         */
 547        curl_multi_perform(curlm, &num_transfers);
 548#endif
 549        return 1;
 550}
 551
 552#ifdef USE_CURL_MULTI
 553struct fill_chain {
 554        void *data;
 555        int (*fill)(void *);
 556        struct fill_chain *next;
 557};
 558
 559static struct fill_chain *fill_cfg;
 560
 561void add_fill_function(void *data, int (*fill)(void *))
 562{
 563        struct fill_chain *new = xmalloc(sizeof(*new));
 564        struct fill_chain **linkp = &fill_cfg;
 565        new->data = data;
 566        new->fill = fill;
 567        new->next = NULL;
 568        while (*linkp)
 569                linkp = &(*linkp)->next;
 570        *linkp = new;
 571}
 572
 573void fill_active_slots(void)
 574{
 575        struct active_request_slot *slot = active_queue_head;
 576
 577        while (active_requests < max_requests) {
 578                struct fill_chain *fill;
 579                for (fill = fill_cfg; fill; fill = fill->next)
 580                        if (fill->fill(fill->data))
 581                                break;
 582
 583                if (!fill)
 584                        break;
 585        }
 586
 587        while (slot != NULL) {
 588                if (!slot->in_use && slot->curl != NULL
 589                        && curl_session_count > min_curl_sessions) {
 590                        curl_easy_cleanup(slot->curl);
 591                        slot->curl = NULL;
 592                        curl_session_count--;
 593                }
 594                slot = slot->next;
 595        }
 596}
 597
 598void step_active_slots(void)
 599{
 600        int num_transfers;
 601        CURLMcode curlm_result;
 602
 603        do {
 604                curlm_result = curl_multi_perform(curlm, &num_transfers);
 605        } while (curlm_result == CURLM_CALL_MULTI_PERFORM);
 606        if (num_transfers < active_requests) {
 607                process_curl_messages();
 608                fill_active_slots();
 609        }
 610}
 611#endif
 612
 613void run_active_slot(struct active_request_slot *slot)
 614{
 615#ifdef USE_CURL_MULTI
 616        long last_pos = 0;
 617        long current_pos;
 618        fd_set readfds;
 619        fd_set writefds;
 620        fd_set excfds;
 621        int max_fd;
 622        struct timeval select_timeout;
 623        int finished = 0;
 624
 625        slot->finished = &finished;
 626        while (!finished) {
 627                data_received = 0;
 628                step_active_slots();
 629
 630                if (!data_received && slot->local != NULL) {
 631                        current_pos = ftell(slot->local);
 632                        if (current_pos > last_pos)
 633                                data_received++;
 634                        last_pos = current_pos;
 635                }
 636
 637                if (slot->in_use && !data_received) {
 638                        max_fd = 0;
 639                        FD_ZERO(&readfds);
 640                        FD_ZERO(&writefds);
 641                        FD_ZERO(&excfds);
 642                        select_timeout.tv_sec = 0;
 643                        select_timeout.tv_usec = 50000;
 644                        select(max_fd, &readfds, &writefds,
 645                               &excfds, &select_timeout);
 646                }
 647        }
 648#else
 649        while (slot->in_use) {
 650                slot->curl_result = curl_easy_perform(slot->curl);
 651                finish_active_slot(slot);
 652        }
 653#endif
 654}
 655
 656static void closedown_active_slot(struct active_request_slot *slot)
 657{
 658        active_requests--;
 659        slot->in_use = 0;
 660}
 661
 662static void release_active_slot(struct active_request_slot *slot)
 663{
 664        closedown_active_slot(slot);
 665        if (slot->curl && curl_session_count > min_curl_sessions) {
 666#ifdef USE_CURL_MULTI
 667                curl_multi_remove_handle(curlm, slot->curl);
 668#endif
 669                curl_easy_cleanup(slot->curl);
 670                slot->curl = NULL;
 671                curl_session_count--;
 672        }
 673#ifdef USE_CURL_MULTI
 674        fill_active_slots();
 675#endif
 676}
 677
 678void finish_active_slot(struct active_request_slot *slot)
 679{
 680        closedown_active_slot(slot);
 681        curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);
 682
 683        if (slot->finished != NULL)
 684                (*slot->finished) = 1;
 685
 686        /* Store slot results so they can be read after the slot is reused */
 687        if (slot->results != NULL) {
 688                slot->results->curl_result = slot->curl_result;
 689                slot->results->http_code = slot->http_code;
 690        }
 691
 692        /* Run callback if appropriate */
 693        if (slot->callback_func != NULL)
 694                slot->callback_func(slot->callback_data);
 695}
 696
 697void finish_all_active_slots(void)
 698{
 699        struct active_request_slot *slot = active_queue_head;
 700
 701        while (slot != NULL)
 702                if (slot->in_use) {
 703                        run_active_slot(slot);
 704                        slot = active_queue_head;
 705                } else {
 706                        slot = slot->next;
 707                }
 708}
 709
 710/* Helpers for modifying and creating URLs */
 711static inline int needs_quote(int ch)
 712{
 713        if (((ch >= 'A') && (ch <= 'Z'))
 714                        || ((ch >= 'a') && (ch <= 'z'))
 715                        || ((ch >= '0') && (ch <= '9'))
 716                        || (ch == '/')
 717                        || (ch == '-')
 718                        || (ch == '.'))
 719                return 0;
 720        return 1;
 721}
 722
 723static inline int hex(int v)
 724{
 725        if (v < 10)
 726                return '0' + v;
 727        else
 728                return 'A' + v - 10;
 729}
 730
 731void end_url_with_slash(struct strbuf *buf, const char *url)
 732{
 733        strbuf_addstr(buf, url);
 734        if (buf->len && buf->buf[buf->len - 1] != '/')
 735                strbuf_addstr(buf, "/");
 736}
 737
 738static char *quote_ref_url(const char *base, const char *ref)
 739{
 740        struct strbuf buf = STRBUF_INIT;
 741        const char *cp;
 742        int ch;
 743
 744        end_url_with_slash(&buf, base);
 745
 746        for (cp = ref; (ch = *cp) != 0; cp++)
 747                if (needs_quote(ch))
 748                        strbuf_addf(&buf, "%%%02x", ch);
 749                else
 750                        strbuf_addch(&buf, *cp);
 751
 752        return strbuf_detach(&buf, NULL);
 753}
 754
 755void append_remote_object_url(struct strbuf *buf, const char *url,
 756                              const char *hex,
 757                              int only_two_digit_prefix)
 758{
 759        end_url_with_slash(buf, url);
 760
 761        strbuf_addf(buf, "objects/%.*s/", 2, hex);
 762        if (!only_two_digit_prefix)
 763                strbuf_addf(buf, "%s", hex+2);
 764}
 765
 766char *get_remote_object_url(const char *url, const char *hex,
 767                            int only_two_digit_prefix)
 768{
 769        struct strbuf buf = STRBUF_INIT;
 770        append_remote_object_url(&buf, url, hex, only_two_digit_prefix);
 771        return strbuf_detach(&buf, NULL);
 772}
 773
 774/* http_request() targets */
 775#define HTTP_REQUEST_STRBUF     0
 776#define HTTP_REQUEST_FILE       1
 777
 778static int http_request(const char *url, void *result, int target, int options)
 779{
 780        struct active_request_slot *slot;
 781        struct slot_results results;
 782        struct curl_slist *headers = NULL;
 783        struct strbuf buf = STRBUF_INIT;
 784        int ret;
 785
 786        slot = get_active_slot();
 787        slot->results = &results;
 788        curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
 789
 790        if (result == NULL) {
 791                curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
 792        } else {
 793                curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
 794                curl_easy_setopt(slot->curl, CURLOPT_FILE, result);
 795
 796                if (target == HTTP_REQUEST_FILE) {
 797                        long posn = ftell(result);
 798                        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
 799                                         fwrite);
 800                        if (posn > 0) {
 801                                strbuf_addf(&buf, "Range: bytes=%ld-", posn);
 802                                headers = curl_slist_append(headers, buf.buf);
 803                                strbuf_reset(&buf);
 804                        }
 805                        slot->local = result;
 806                } else
 807                        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
 808                                         fwrite_buffer);
 809        }
 810
 811        strbuf_addstr(&buf, "Pragma:");
 812        if (options & HTTP_NO_CACHE)
 813                strbuf_addstr(&buf, " no-cache");
 814
 815        headers = curl_slist_append(headers, buf.buf);
 816
 817        curl_easy_setopt(slot->curl, CURLOPT_URL, url);
 818        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
 819
 820        if (start_active_slot(slot)) {
 821                run_active_slot(slot);
 822                if (results.curl_result == CURLE_OK)
 823                        ret = HTTP_OK;
 824                else if (missing_target(&results))
 825                        ret = HTTP_MISSING_TARGET;
 826                else if (results.http_code == 401) {
 827                        if (user_name) {
 828                                ret = HTTP_NOAUTH;
 829                        } else {
 830                                /*
 831                                 * git_getpass is needed here because its very likely stdin/stdout are
 832                                 * pipes to our parent process.  So we instead need to use /dev/tty,
 833                                 * but that is non-portable.  Using git_getpass() can at least be stubbed
 834                                 * on other platforms with a different implementation if/when necessary.
 835                                 */
 836                                user_name = xstrdup(git_getpass("Username: "));
 837                                init_curl_http_auth(slot->curl);
 838                                ret = HTTP_REAUTH;
 839                        }
 840                } else
 841                        ret = HTTP_ERROR;
 842        } else {
 843                error("Unable to start HTTP request for %s", url);
 844                ret = HTTP_START_FAILED;
 845        }
 846
 847        slot->local = NULL;
 848        curl_slist_free_all(headers);
 849        strbuf_release(&buf);
 850
 851        return ret;
 852}
 853
 854int http_get_strbuf(const char *url, struct strbuf *result, int options)
 855{
 856        int http_ret = http_request(url, result, HTTP_REQUEST_STRBUF, options);
 857        if (http_ret == HTTP_REAUTH) {
 858                http_ret = http_request(url, result, HTTP_REQUEST_STRBUF, options);
 859        }
 860        return http_ret;
 861}
 862
 863/*
 864 * Downloads an url and stores the result in the given file.
 865 *
 866 * If a previous interrupted download is detected (i.e. a previous temporary
 867 * file is still around) the download is resumed.
 868 */
 869static int http_get_file(const char *url, const char *filename, int options)
 870{
 871        int ret;
 872        struct strbuf tmpfile = STRBUF_INIT;
 873        FILE *result;
 874
 875        strbuf_addf(&tmpfile, "%s.temp", filename);
 876        result = fopen(tmpfile.buf, "a");
 877        if (! result) {
 878                error("Unable to open local file %s", tmpfile.buf);
 879                ret = HTTP_ERROR;
 880                goto cleanup;
 881        }
 882
 883        ret = http_request(url, result, HTTP_REQUEST_FILE, options);
 884        fclose(result);
 885
 886        if ((ret == HTTP_OK) && move_temp_to_file(tmpfile.buf, filename))
 887                ret = HTTP_ERROR;
 888cleanup:
 889        strbuf_release(&tmpfile);
 890        return ret;
 891}
 892
 893int http_error(const char *url, int ret)
 894{
 895        /* http_request has already handled HTTP_START_FAILED. */
 896        if (ret != HTTP_START_FAILED)
 897                error("%s while accessing %s\n", curl_errorstr, url);
 898
 899        return ret;
 900}
 901
 902int http_fetch_ref(const char *base, struct ref *ref)
 903{
 904        char *url;
 905        struct strbuf buffer = STRBUF_INIT;
 906        int ret = -1;
 907
 908        url = quote_ref_url(base, ref->name);
 909        if (http_get_strbuf(url, &buffer, HTTP_NO_CACHE) == HTTP_OK) {
 910                strbuf_rtrim(&buffer);
 911                if (buffer.len == 40)
 912                        ret = get_sha1_hex(buffer.buf, ref->old_sha1);
 913                else if (!prefixcmp(buffer.buf, "ref: ")) {
 914                        ref->symref = xstrdup(buffer.buf + 5);
 915                        ret = 0;
 916                }
 917        }
 918
 919        strbuf_release(&buffer);
 920        free(url);
 921        return ret;
 922}
 923
 924/* Helpers for fetching packs */
 925static char *fetch_pack_index(unsigned char *sha1, const char *base_url)
 926{
 927        char *url, *tmp;
 928        struct strbuf buf = STRBUF_INIT;
 929
 930        if (http_is_verbose)
 931                fprintf(stderr, "Getting index for pack %s\n", sha1_to_hex(sha1));
 932
 933        end_url_with_slash(&buf, base_url);
 934        strbuf_addf(&buf, "objects/pack/pack-%s.idx", sha1_to_hex(sha1));
 935        url = strbuf_detach(&buf, NULL);
 936
 937        strbuf_addf(&buf, "%s.temp", sha1_pack_index_name(sha1));
 938        tmp = strbuf_detach(&buf, NULL);
 939
 940        if (http_get_file(url, tmp, 0) != HTTP_OK) {
 941                error("Unable to get pack index %s\n", url);
 942                free(tmp);
 943                tmp = NULL;
 944        }
 945
 946        free(url);
 947        return tmp;
 948}
 949
 950static int fetch_and_setup_pack_index(struct packed_git **packs_head,
 951        unsigned char *sha1, const char *base_url)
 952{
 953        struct packed_git *new_pack;
 954        char *tmp_idx = NULL;
 955        int ret;
 956
 957        if (has_pack_index(sha1)) {
 958                new_pack = parse_pack_index(sha1, NULL);
 959                if (!new_pack)
 960                        return -1; /* parse_pack_index() already issued error message */
 961                goto add_pack;
 962        }
 963
 964        tmp_idx = fetch_pack_index(sha1, base_url);
 965        if (!tmp_idx)
 966                return -1;
 967
 968        new_pack = parse_pack_index(sha1, tmp_idx);
 969        if (!new_pack) {
 970                unlink(tmp_idx);
 971                free(tmp_idx);
 972
 973                return -1; /* parse_pack_index() already issued error message */
 974        }
 975
 976        ret = verify_pack_index(new_pack);
 977        if (!ret) {
 978                close_pack_index(new_pack);
 979                ret = move_temp_to_file(tmp_idx, sha1_pack_index_name(sha1));
 980        }
 981        free(tmp_idx);
 982        if (ret)
 983                return -1;
 984
 985add_pack:
 986        new_pack->next = *packs_head;
 987        *packs_head = new_pack;
 988        return 0;
 989}
 990
 991int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
 992{
 993        int ret = 0, i = 0;
 994        char *url, *data;
 995        struct strbuf buf = STRBUF_INIT;
 996        unsigned char sha1[20];
 997
 998        end_url_with_slash(&buf, base_url);
 999        strbuf_addstr(&buf, "objects/info/packs");
1000        url = strbuf_detach(&buf, NULL);
1001
1002        ret = http_get_strbuf(url, &buf, HTTP_NO_CACHE);
1003        if (ret != HTTP_OK)
1004                goto cleanup;
1005
1006        data = buf.buf;
1007        while (i < buf.len) {
1008                switch (data[i]) {
1009                case 'P':
1010                        i++;
1011                        if (i + 52 <= buf.len &&
1012                            !prefixcmp(data + i, " pack-") &&
1013                            !prefixcmp(data + i + 46, ".pack\n")) {
1014                                get_sha1_hex(data + i + 6, sha1);
1015                                fetch_and_setup_pack_index(packs_head, sha1,
1016                                                      base_url);
1017                                i += 51;
1018                                break;
1019                        }
1020                default:
1021                        while (i < buf.len && data[i] != '\n')
1022                                i++;
1023                }
1024                i++;
1025        }
1026
1027cleanup:
1028        free(url);
1029        return ret;
1030}
1031
1032void release_http_pack_request(struct http_pack_request *preq)
1033{
1034        if (preq->packfile != NULL) {
1035                fclose(preq->packfile);
1036                preq->packfile = NULL;
1037                preq->slot->local = NULL;
1038        }
1039        if (preq->range_header != NULL) {
1040                curl_slist_free_all(preq->range_header);
1041                preq->range_header = NULL;
1042        }
1043        preq->slot = NULL;
1044        free(preq->url);
1045}
1046
1047int finish_http_pack_request(struct http_pack_request *preq)
1048{
1049        struct packed_git **lst;
1050        struct packed_git *p = preq->target;
1051        char *tmp_idx;
1052        struct child_process ip;
1053        const char *ip_argv[8];
1054
1055        close_pack_index(p);
1056
1057        fclose(preq->packfile);
1058        preq->packfile = NULL;
1059        preq->slot->local = NULL;
1060
1061        lst = preq->lst;
1062        while (*lst != p)
1063                lst = &((*lst)->next);
1064        *lst = (*lst)->next;
1065
1066        tmp_idx = xstrdup(preq->tmpfile);
1067        strcpy(tmp_idx + strlen(tmp_idx) - strlen(".pack.temp"),
1068               ".idx.temp");
1069
1070        ip_argv[0] = "index-pack";
1071        ip_argv[1] = "-o";
1072        ip_argv[2] = tmp_idx;
1073        ip_argv[3] = preq->tmpfile;
1074        ip_argv[4] = NULL;
1075
1076        memset(&ip, 0, sizeof(ip));
1077        ip.argv = ip_argv;
1078        ip.git_cmd = 1;
1079        ip.no_stdin = 1;
1080        ip.no_stdout = 1;
1081
1082        if (run_command(&ip)) {
1083                unlink(preq->tmpfile);
1084                unlink(tmp_idx);
1085                free(tmp_idx);
1086                return -1;
1087        }
1088
1089        unlink(sha1_pack_index_name(p->sha1));
1090
1091        if (move_temp_to_file(preq->tmpfile, sha1_pack_name(p->sha1))
1092         || move_temp_to_file(tmp_idx, sha1_pack_index_name(p->sha1))) {
1093                free(tmp_idx);
1094                return -1;
1095        }
1096
1097        install_packed_git(p);
1098        free(tmp_idx);
1099        return 0;
1100}
1101
1102struct http_pack_request *new_http_pack_request(
1103        struct packed_git *target, const char *base_url)
1104{
1105        long prev_posn = 0;
1106        char range[RANGE_HEADER_SIZE];
1107        struct strbuf buf = STRBUF_INIT;
1108        struct http_pack_request *preq;
1109
1110        preq = xmalloc(sizeof(*preq));
1111        preq->target = target;
1112        preq->range_header = NULL;
1113
1114        end_url_with_slash(&buf, base_url);
1115        strbuf_addf(&buf, "objects/pack/pack-%s.pack",
1116                sha1_to_hex(target->sha1));
1117        preq->url = strbuf_detach(&buf, NULL);
1118
1119        snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp",
1120                sha1_pack_name(target->sha1));
1121        preq->packfile = fopen(preq->tmpfile, "a");
1122        if (!preq->packfile) {
1123                error("Unable to open local file %s for pack",
1124                      preq->tmpfile);
1125                goto abort;
1126        }
1127
1128        preq->slot = get_active_slot();
1129        preq->slot->local = preq->packfile;
1130        curl_easy_setopt(preq->slot->curl, CURLOPT_FILE, preq->packfile);
1131        curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
1132        curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url);
1133        curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
1134                no_pragma_header);
1135
1136        /*
1137         * If there is data present from a previous transfer attempt,
1138         * resume where it left off
1139         */
1140        prev_posn = ftell(preq->packfile);
1141        if (prev_posn>0) {
1142                if (http_is_verbose)
1143                        fprintf(stderr,
1144                                "Resuming fetch of pack %s at byte %ld\n",
1145                                sha1_to_hex(target->sha1), prev_posn);
1146                sprintf(range, "Range: bytes=%ld-", prev_posn);
1147                preq->range_header = curl_slist_append(NULL, range);
1148                curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
1149                        preq->range_header);
1150        }
1151
1152        return preq;
1153
1154abort:
1155        free(preq->url);
1156        free(preq);
1157        return NULL;
1158}
1159
1160/* Helpers for fetching objects (loose) */
1161static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
1162                               void *data)
1163{
1164        unsigned char expn[4096];
1165        size_t size = eltsize * nmemb;
1166        int posn = 0;
1167        struct http_object_request *freq =
1168                (struct http_object_request *)data;
1169        do {
1170                ssize_t retval = xwrite(freq->localfile,
1171                                        (char *) ptr + posn, size - posn);
1172                if (retval < 0)
1173                        return posn;
1174                posn += retval;
1175        } while (posn < size);
1176
1177        freq->stream.avail_in = size;
1178        freq->stream.next_in = ptr;
1179        do {
1180                freq->stream.next_out = expn;
1181                freq->stream.avail_out = sizeof(expn);
1182                freq->zret = git_inflate(&freq->stream, Z_SYNC_FLUSH);
1183                git_SHA1_Update(&freq->c, expn,
1184                                sizeof(expn) - freq->stream.avail_out);
1185        } while (freq->stream.avail_in && freq->zret == Z_OK);
1186        data_received++;
1187        return size;
1188}
1189
1190struct http_object_request *new_http_object_request(const char *base_url,
1191        unsigned char *sha1)
1192{
1193        char *hex = sha1_to_hex(sha1);
1194        char *filename;
1195        char prevfile[PATH_MAX];
1196        int prevlocal;
1197        unsigned char prev_buf[PREV_BUF_SIZE];
1198        ssize_t prev_read = 0;
1199        long prev_posn = 0;
1200        char range[RANGE_HEADER_SIZE];
1201        struct curl_slist *range_header = NULL;
1202        struct http_object_request *freq;
1203
1204        freq = xmalloc(sizeof(*freq));
1205        hashcpy(freq->sha1, sha1);
1206        freq->localfile = -1;
1207
1208        filename = sha1_file_name(sha1);
1209        snprintf(freq->tmpfile, sizeof(freq->tmpfile),
1210                 "%s.temp", filename);
1211
1212        snprintf(prevfile, sizeof(prevfile), "%s.prev", filename);
1213        unlink_or_warn(prevfile);
1214        rename(freq->tmpfile, prevfile);
1215        unlink_or_warn(freq->tmpfile);
1216
1217        if (freq->localfile != -1)
1218                error("fd leakage in start: %d", freq->localfile);
1219        freq->localfile = open(freq->tmpfile,
1220                               O_WRONLY | O_CREAT | O_EXCL, 0666);
1221        /*
1222         * This could have failed due to the "lazy directory creation";
1223         * try to mkdir the last path component.
1224         */
1225        if (freq->localfile < 0 && errno == ENOENT) {
1226                char *dir = strrchr(freq->tmpfile, '/');
1227                if (dir) {
1228                        *dir = 0;
1229                        mkdir(freq->tmpfile, 0777);
1230                        *dir = '/';
1231                }
1232                freq->localfile = open(freq->tmpfile,
1233                                       O_WRONLY | O_CREAT | O_EXCL, 0666);
1234        }
1235
1236        if (freq->localfile < 0) {
1237                error("Couldn't create temporary file %s: %s",
1238                      freq->tmpfile, strerror(errno));
1239                goto abort;
1240        }
1241
1242        memset(&freq->stream, 0, sizeof(freq->stream));
1243
1244        git_inflate_init(&freq->stream);
1245
1246        git_SHA1_Init(&freq->c);
1247
1248        freq->url = get_remote_object_url(base_url, hex, 0);
1249
1250        /*
1251         * If a previous temp file is present, process what was already
1252         * fetched.
1253         */
1254        prevlocal = open(prevfile, O_RDONLY);
1255        if (prevlocal != -1) {
1256                do {
1257                        prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
1258                        if (prev_read>0) {
1259                                if (fwrite_sha1_file(prev_buf,
1260                                                     1,
1261                                                     prev_read,
1262                                                     freq) == prev_read) {
1263                                        prev_posn += prev_read;
1264                                } else {
1265                                        prev_read = -1;
1266                                }
1267                        }
1268                } while (prev_read > 0);
1269                close(prevlocal);
1270        }
1271        unlink_or_warn(prevfile);
1272
1273        /*
1274         * Reset inflate/SHA1 if there was an error reading the previous temp
1275         * file; also rewind to the beginning of the local file.
1276         */
1277        if (prev_read == -1) {
1278                memset(&freq->stream, 0, sizeof(freq->stream));
1279                git_inflate_init(&freq->stream);
1280                git_SHA1_Init(&freq->c);
1281                if (prev_posn>0) {
1282                        prev_posn = 0;
1283                        lseek(freq->localfile, 0, SEEK_SET);
1284                        if (ftruncate(freq->localfile, 0) < 0) {
1285                                error("Couldn't truncate temporary file %s: %s",
1286                                          freq->tmpfile, strerror(errno));
1287                                goto abort;
1288                        }
1289                }
1290        }
1291
1292        freq->slot = get_active_slot();
1293
1294        curl_easy_setopt(freq->slot->curl, CURLOPT_FILE, freq);
1295        curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
1296        curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);
1297        curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url);
1298        curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
1299
1300        /*
1301         * If we have successfully processed data from a previous fetch
1302         * attempt, only fetch the data we don't already have.
1303         */
1304        if (prev_posn>0) {
1305                if (http_is_verbose)
1306                        fprintf(stderr,
1307                                "Resuming fetch of object %s at byte %ld\n",
1308                                hex, prev_posn);
1309                sprintf(range, "Range: bytes=%ld-", prev_posn);
1310                range_header = curl_slist_append(range_header, range);
1311                curl_easy_setopt(freq->slot->curl,
1312                                 CURLOPT_HTTPHEADER, range_header);
1313        }
1314
1315        return freq;
1316
1317abort:
1318        free(filename);
1319        free(freq->url);
1320        free(freq);
1321        return NULL;
1322}
1323
1324void process_http_object_request(struct http_object_request *freq)
1325{
1326        if (freq->slot == NULL)
1327                return;
1328        freq->curl_result = freq->slot->curl_result;
1329        freq->http_code = freq->slot->http_code;
1330        freq->slot = NULL;
1331}
1332
1333int finish_http_object_request(struct http_object_request *freq)
1334{
1335        struct stat st;
1336
1337        close(freq->localfile);
1338        freq->localfile = -1;
1339
1340        process_http_object_request(freq);
1341
1342        if (freq->http_code == 416) {
1343                warning("requested range invalid; we may already have all the data.");
1344        } else if (freq->curl_result != CURLE_OK) {
1345                if (stat(freq->tmpfile, &st) == 0)
1346                        if (st.st_size == 0)
1347                                unlink_or_warn(freq->tmpfile);
1348                return -1;
1349        }
1350
1351        git_inflate_end(&freq->stream);
1352        git_SHA1_Final(freq->real_sha1, &freq->c);
1353        if (freq->zret != Z_STREAM_END) {
1354                unlink_or_warn(freq->tmpfile);
1355                return -1;
1356        }
1357        if (hashcmp(freq->sha1, freq->real_sha1)) {
1358                unlink_or_warn(freq->tmpfile);
1359                return -1;
1360        }
1361        freq->rename =
1362                move_temp_to_file(freq->tmpfile, sha1_file_name(freq->sha1));
1363
1364        return freq->rename;
1365}
1366
1367void abort_http_object_request(struct http_object_request *freq)
1368{
1369        unlink_or_warn(freq->tmpfile);
1370
1371        release_http_object_request(freq);
1372}
1373
1374void release_http_object_request(struct http_object_request *freq)
1375{
1376        if (freq->localfile != -1) {
1377                close(freq->localfile);
1378                freq->localfile = -1;
1379        }
1380        if (freq->url != NULL) {
1381                free(freq->url);
1382                freq->url = NULL;
1383        }
1384        if (freq->slot != NULL) {
1385                freq->slot->callback_func = NULL;
1386                freq->slot->callback_data = NULL;
1387                release_active_slot(freq->slot);
1388                freq->slot = NULL;
1389        }
1390}