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