http-push.con commit Support remote references with slashes in their names (0772b9a)
   1#include "cache.h"
   2#include "commit.h"
   3#include "pack.h"
   4#include "fetch.h"
   5#include "tag.h"
   6#include "blob.h"
   7
   8#include <curl/curl.h>
   9#include <curl/easy.h>
  10#include "expat.h"
  11
  12static const char http_push_usage[] =
  13"git-http-push [--complete] [--force] [--verbose] <url> <ref> [<ref>...]\n";
  14
  15#if LIBCURL_VERSION_NUM >= 0x070908
  16#define USE_CURL_MULTI
  17#define DEFAULT_MAX_REQUESTS 5
  18#endif
  19
  20#if LIBCURL_VERSION_NUM < 0x070704
  21#define curl_global_cleanup() do { /* nothing */ } while(0)
  22#endif
  23#if LIBCURL_VERSION_NUM < 0x070800
  24#define curl_global_init(a) do { /* nothing */ } while(0)
  25#endif
  26
  27#if LIBCURL_VERSION_NUM < 0x070c04
  28#define NO_CURL_EASY_DUPHANDLE
  29#endif
  30
  31#define RANGE_HEADER_SIZE 30
  32
  33/* DAV method names and request body templates */
  34#define DAV_LOCK "LOCK"
  35#define DAV_MKCOL "MKCOL"
  36#define DAV_MOVE "MOVE"
  37#define DAV_PROPFIND "PROPFIND"
  38#define DAV_PUT "PUT"
  39#define DAV_UNLOCK "UNLOCK"
  40#define PROPFIND_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:prop xmlns:R=\"%s\">\n<D:supportedlock/>\n</D:prop>\n</D:propfind>"
  41#define LOCK_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:lockinfo xmlns:D=\"DAV:\">\n<D:lockscope><D:exclusive/></D:lockscope>\n<D:locktype><D:write/></D:locktype>\n<D:owner>\n<D:href>mailto:%s</D:href>\n</D:owner>\n</D:lockinfo>"
  42
  43static int active_requests = 0;
  44static int data_received;
  45static int pushing = 0;
  46static int aborted = 0;
  47
  48#ifdef USE_CURL_MULTI
  49static int max_requests = -1;
  50static CURLM *curlm;
  51#endif
  52#ifndef NO_CURL_EASY_DUPHANDLE
  53static CURL *curl_default;
  54#endif
  55static struct curl_slist *no_pragma_header;
  56static struct curl_slist *default_headers;
  57static char curl_errorstr[CURL_ERROR_SIZE];
  58static char *lock_token = NULL;
  59
  60static int push_verbosely = 0;
  61static int push_all = 0;
  62static int force_all = 0;
  63
  64struct buffer
  65{
  66        size_t posn;
  67        size_t size;
  68        void *buffer;
  69};
  70
  71struct repo
  72{
  73        char *url;
  74        struct packed_git *packs;
  75};
  76
  77static struct repo *remote = NULL;
  78
  79enum transfer_state {
  80        NEED_CHECK,
  81        RUN_HEAD,
  82        NEED_PUSH,
  83        RUN_MKCOL,
  84        RUN_PUT,
  85        RUN_MOVE,
  86        ABORTED,
  87        COMPLETE,
  88};
  89
  90struct transfer_request
  91{
  92        unsigned char sha1[20];
  93        char *url;
  94        char *dest;
  95        char *lock_token;
  96        struct curl_slist *headers;
  97        struct buffer buffer;
  98        char filename[PATH_MAX];
  99        char tmpfile[PATH_MAX];
 100        enum transfer_state state;
 101        CURLcode curl_result;
 102        char errorstr[CURL_ERROR_SIZE];
 103        long http_code;
 104        unsigned char real_sha1[20];
 105        SHA_CTX c;
 106        z_stream stream;
 107        int zret;
 108        int rename;
 109        struct active_request_slot *slot;
 110        struct transfer_request *next;
 111};
 112
 113struct active_request_slot
 114{
 115        CURL *curl;
 116        FILE *local;
 117        int in_use;
 118        int done;
 119        CURLcode curl_result;
 120        long http_code;
 121        struct active_request_slot *next;
 122};
 123
 124static struct transfer_request *request_queue_head = NULL;
 125static struct active_request_slot *active_queue_head = NULL;
 126
 127static int curl_ssl_verify = -1;
 128static char *ssl_cert = NULL;
 129#if LIBCURL_VERSION_NUM >= 0x070902
 130static char *ssl_key = NULL;
 131#endif
 132#if LIBCURL_VERSION_NUM >= 0x070908
 133static char *ssl_capath = NULL;
 134#endif
 135static char *ssl_cainfo = NULL;
 136static long curl_low_speed_limit = -1;
 137static long curl_low_speed_time = -1;
 138
 139struct lockprop
 140{
 141        int supported_lock;
 142        int lock_entry;
 143        int lock_scope;
 144        int lock_type;
 145        int lock_exclusive;
 146        int lock_exclusive_write;
 147};
 148
 149static int http_options(const char *var, const char *value)
 150{
 151        if (!strcmp("http.sslverify", var)) {
 152                if (curl_ssl_verify == -1) {
 153                        curl_ssl_verify = git_config_bool(var, value);
 154                }
 155                return 0;
 156        }
 157
 158        if (!strcmp("http.sslcert", var)) {
 159                if (ssl_cert == NULL) {
 160                        ssl_cert = xmalloc(strlen(value)+1);
 161                        strcpy(ssl_cert, value);
 162                }
 163                return 0;
 164        }
 165#if LIBCURL_VERSION_NUM >= 0x070902
 166        if (!strcmp("http.sslkey", var)) {
 167                if (ssl_key == NULL) {
 168                        ssl_key = xmalloc(strlen(value)+1);
 169                        strcpy(ssl_key, value);
 170                }
 171                return 0;
 172        }
 173#endif
 174#if LIBCURL_VERSION_NUM >= 0x070908
 175        if (!strcmp("http.sslcapath", var)) {
 176                if (ssl_capath == NULL) {
 177                        ssl_capath = xmalloc(strlen(value)+1);
 178                        strcpy(ssl_capath, value);
 179                }
 180                return 0;
 181        }
 182#endif
 183        if (!strcmp("http.sslcainfo", var)) {
 184                if (ssl_cainfo == NULL) {
 185                        ssl_cainfo = xmalloc(strlen(value)+1);
 186                        strcpy(ssl_cainfo, value);
 187                }
 188                return 0;
 189        }
 190
 191#ifdef USE_CURL_MULTI   
 192        if (!strcmp("http.maxrequests", var)) {
 193                if (max_requests == -1)
 194                        max_requests = git_config_int(var, value);
 195                return 0;
 196        }
 197#endif
 198
 199        if (!strcmp("http.lowspeedlimit", var)) {
 200                if (curl_low_speed_limit == -1)
 201                        curl_low_speed_limit = (long)git_config_int(var, value);
 202                return 0;
 203        }
 204        if (!strcmp("http.lowspeedtime", var)) {
 205                if (curl_low_speed_time == -1)
 206                        curl_low_speed_time = (long)git_config_int(var, value);
 207                return 0;
 208        }
 209
 210        /* Fall back on the default ones */
 211        return git_default_config(var, value);
 212}
 213
 214static size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb,
 215                           struct buffer *buffer)
 216{
 217        size_t size = eltsize * nmemb;
 218        if (size > buffer->size - buffer->posn)
 219                size = buffer->size - buffer->posn;
 220        memcpy(ptr, buffer->buffer + buffer->posn, size);
 221        buffer->posn += size;
 222        return size;
 223}
 224
 225static size_t fwrite_buffer_dynamic(const void *ptr, size_t eltsize,
 226                                    size_t nmemb, struct buffer *buffer)
 227{
 228        size_t size = eltsize * nmemb;
 229        if (size > buffer->size - buffer->posn) {
 230                buffer->size = buffer->size * 3 / 2;
 231                if (buffer->size < buffer->posn + size)
 232                        buffer->size = buffer->posn + size;
 233                buffer->buffer = xrealloc(buffer->buffer, buffer->size);
 234        }
 235        memcpy(buffer->buffer + buffer->posn, ptr, size);
 236        buffer->posn += size;
 237        data_received++;
 238        return size;
 239}
 240
 241static size_t fwrite_null(const void *ptr, size_t eltsize,
 242                          size_t nmemb, struct buffer *buffer)
 243{
 244        data_received++;
 245        return eltsize * nmemb;
 246}
 247
 248#ifdef USE_CURL_MULTI
 249static void process_curl_messages(void);
 250static void process_request_queue(void);
 251#endif
 252
 253static CURL* get_curl_handle(void)
 254{
 255        CURL* result = curl_easy_init();
 256
 257        curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, curl_ssl_verify);
 258#if LIBCURL_VERSION_NUM >= 0x070907
 259        curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
 260#endif
 261
 262        if (ssl_cert != NULL)
 263                curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
 264#if LIBCURL_VERSION_NUM >= 0x070902
 265        if (ssl_key != NULL)
 266                curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
 267#endif
 268#if LIBCURL_VERSION_NUM >= 0x070908
 269        if (ssl_capath != NULL)
 270                curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);
 271#endif
 272        if (ssl_cainfo != NULL)
 273                curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
 274        curl_easy_setopt(result, CURLOPT_FAILONERROR, 1);
 275
 276        if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
 277                curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,
 278                                 curl_low_speed_limit);
 279                curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME,
 280                                 curl_low_speed_time);
 281        }
 282
 283        return result;
 284}
 285
 286static struct active_request_slot *get_active_slot(void)
 287{
 288        struct active_request_slot *slot = active_queue_head;
 289        struct active_request_slot *newslot;
 290
 291#ifdef USE_CURL_MULTI
 292        int num_transfers;
 293
 294        /* Wait for a slot to open up if the queue is full */
 295        while (active_requests >= max_requests) {
 296                curl_multi_perform(curlm, &num_transfers);
 297                if (num_transfers < active_requests) {
 298                        process_curl_messages();
 299                }
 300        }
 301#endif
 302
 303        while (slot != NULL && slot->in_use) {
 304                slot = slot->next;
 305        }
 306        if (slot == NULL) {
 307                newslot = xmalloc(sizeof(*newslot));
 308                newslot->curl = NULL;
 309                newslot->in_use = 0;
 310                newslot->next = NULL;
 311
 312                slot = active_queue_head;
 313                if (slot == NULL) {
 314                        active_queue_head = newslot;
 315                } else {
 316                        while (slot->next != NULL) {
 317                                slot = slot->next;
 318                        }
 319                        slot->next = newslot;
 320                }
 321                slot = newslot;
 322        }
 323
 324        if (slot->curl == NULL) {
 325#ifdef NO_CURL_EASY_DUPHANDLE
 326                slot->curl = get_curl_handle();
 327#else
 328                slot->curl = curl_easy_duphandle(curl_default);
 329#endif
 330        }
 331
 332        active_requests++;
 333        slot->in_use = 1;
 334        slot->done = 0;
 335        slot->local = NULL;
 336        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, default_headers);
 337        curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
 338
 339        return slot;
 340}
 341
 342static int start_active_slot(struct active_request_slot *slot)
 343{
 344#ifdef USE_CURL_MULTI
 345        CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl);
 346
 347        if (curlm_result != CURLM_OK &&
 348            curlm_result != CURLM_CALL_MULTI_PERFORM) {
 349                active_requests--;
 350                slot->in_use = 0;
 351                return 0;
 352        }
 353#endif
 354        return 1;
 355}
 356
 357static void run_active_slot(struct active_request_slot *slot)
 358{
 359#ifdef USE_CURL_MULTI
 360        int num_transfers;
 361        long last_pos = 0;
 362        long current_pos;
 363        fd_set readfds;
 364        fd_set writefds;
 365        fd_set excfds;
 366        int max_fd;
 367        struct timeval select_timeout;
 368        CURLMcode curlm_result;
 369
 370        while (!slot->done) {
 371                data_received = 0;
 372                do {
 373                        curlm_result = curl_multi_perform(curlm,
 374                                                          &num_transfers);
 375                } while (curlm_result == CURLM_CALL_MULTI_PERFORM);
 376                if (num_transfers < active_requests) {
 377                        process_curl_messages();
 378                        process_request_queue();
 379                }
 380
 381                if (!data_received && slot->local != NULL) {
 382                        current_pos = ftell(slot->local);
 383                        if (current_pos > last_pos)
 384                                data_received++;
 385                        last_pos = current_pos;
 386                }
 387
 388                if (!slot->done && !data_received) {
 389                        max_fd = 0;
 390                        FD_ZERO(&readfds);
 391                        FD_ZERO(&writefds);
 392                        FD_ZERO(&excfds);
 393                        select_timeout.tv_sec = 0;
 394                        select_timeout.tv_usec = 50000;
 395                        select(max_fd, &readfds, &writefds,
 396                               &excfds, &select_timeout);
 397                }
 398        }
 399#else
 400        slot->curl_result = curl_easy_perform(slot->curl);
 401        active_requests--;
 402#endif
 403}
 404
 405static void start_check(struct transfer_request *request)
 406{
 407        char *hex = sha1_to_hex(request->sha1);
 408        struct active_request_slot *slot;
 409        char *posn;
 410
 411        request->url = xmalloc(strlen(remote->url) + 55);
 412        strcpy(request->url, remote->url);
 413        posn = request->url + strlen(remote->url);
 414        strcpy(posn, "objects/");
 415        posn += 8;
 416        memcpy(posn, hex, 2);
 417        posn += 2;
 418        *(posn++) = '/';
 419        strcpy(posn, hex + 2);
 420
 421        slot = get_active_slot();
 422        curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);
 423        curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
 424        curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
 425
 426        if (start_active_slot(slot)) {
 427                request->slot = slot;
 428                request->state = RUN_HEAD;
 429        } else {
 430                request->state = ABORTED;
 431                free(request->url);
 432        }
 433}
 434
 435static void start_mkcol(struct transfer_request *request)
 436{
 437        char *hex = sha1_to_hex(request->sha1);
 438        struct active_request_slot *slot;
 439        char *posn;
 440
 441        request->url = xmalloc(strlen(remote->url) + 13);
 442        strcpy(request->url, remote->url);
 443        posn = request->url + strlen(remote->url);
 444        strcpy(posn, "objects/");
 445        posn += 8;
 446        memcpy(posn, hex, 2);
 447        posn += 2;
 448        strcpy(posn, "/");
 449
 450        slot = get_active_slot();
 451        curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */
 452        curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
 453        curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);
 454        curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MKCOL);
 455        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
 456
 457        if (start_active_slot(slot)) {
 458                request->slot = slot;
 459                request->state = RUN_MKCOL;
 460        } else {
 461                request->state = ABORTED;
 462                free(request->url);
 463        }
 464}
 465
 466static void start_put(struct transfer_request *request)
 467{
 468        char *hex = sha1_to_hex(request->sha1);
 469        struct active_request_slot *slot;
 470        char *posn;
 471        char type[20];
 472        char hdr[50];
 473        void *unpacked;
 474        unsigned long len;
 475        int hdrlen;
 476        ssize_t size;
 477        z_stream stream;
 478
 479        unpacked = read_sha1_file(request->sha1, type, &len);
 480        hdrlen = sprintf(hdr, "%s %lu", type, len) + 1;
 481
 482        /* Set it up */
 483        memset(&stream, 0, sizeof(stream));
 484        deflateInit(&stream, Z_BEST_COMPRESSION);
 485        size = deflateBound(&stream, len + hdrlen);
 486        request->buffer.buffer = xmalloc(size);
 487
 488        /* Compress it */
 489        stream.next_out = request->buffer.buffer;
 490        stream.avail_out = size;
 491
 492        /* First header.. */
 493        stream.next_in = (void *)hdr;
 494        stream.avail_in = hdrlen;
 495        while (deflate(&stream, 0) == Z_OK)
 496                /* nothing */;
 497
 498        /* Then the data itself.. */
 499        stream.next_in = unpacked;
 500        stream.avail_in = len;
 501        while (deflate(&stream, Z_FINISH) == Z_OK)
 502                /* nothing */;
 503        deflateEnd(&stream);
 504        free(unpacked);
 505
 506        request->buffer.size = stream.total_out;
 507        request->buffer.posn = 0;
 508
 509        if (request->url != NULL)
 510                free(request->url);
 511        request->url = xmalloc(strlen(remote->url) + 
 512                               strlen(request->lock_token) + 51);
 513        strcpy(request->url, remote->url);
 514        posn = request->url + strlen(remote->url);
 515        strcpy(posn, "objects/");
 516        posn += 8;
 517        memcpy(posn, hex, 2);
 518        posn += 2;
 519        *(posn++) = '/';
 520        strcpy(posn, hex + 2);
 521        request->dest = xmalloc(strlen(request->url) + 14);
 522        sprintf(request->dest, "Destination: %s", request->url);
 523        posn += 38;
 524        *(posn++) = '.';
 525        strcpy(posn, request->lock_token);
 526
 527        slot = get_active_slot();
 528        curl_easy_setopt(slot->curl, CURLOPT_INFILE, &request->buffer);
 529        curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, request->buffer.size);
 530        curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
 531        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
 532        curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
 533        curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
 534        curl_easy_setopt(slot->curl, CURLOPT_PUT, 1);
 535        curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
 536        curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
 537
 538        if (start_active_slot(slot)) {
 539                request->slot = slot;
 540                request->state = RUN_PUT;
 541        } else {
 542                request->state = ABORTED;
 543                free(request->url);
 544        }
 545}
 546
 547static void start_move(struct transfer_request *request)
 548{
 549        struct active_request_slot *slot;
 550        struct curl_slist *dav_headers = NULL;
 551
 552        slot = get_active_slot();
 553        curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */
 554        curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MOVE);
 555        dav_headers = curl_slist_append(dav_headers, request->dest);
 556        dav_headers = curl_slist_append(dav_headers, "Overwrite: T");
 557        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
 558        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
 559        curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
 560
 561        if (start_active_slot(slot)) {
 562                request->slot = slot;
 563                request->state = RUN_MOVE;
 564        } else {
 565                request->state = ABORTED;
 566                free(request->url);
 567        }
 568}
 569
 570static void finish_request(struct transfer_request *request)
 571{
 572        request->curl_result =  request->slot->curl_result;
 573        request->http_code = request->slot->http_code;
 574        request->slot = NULL;
 575        if (request->headers != NULL)
 576                curl_slist_free_all(request->headers);
 577        if (request->state == RUN_HEAD) {
 578                if (request->http_code == 404) {
 579                        request->state = NEED_PUSH;
 580                } else if (request->curl_result == CURLE_OK) {
 581                        request->state = COMPLETE;
 582                } else {
 583                        fprintf(stderr, "HEAD %s failed, aborting (%d/%ld)\n",
 584                                sha1_to_hex(request->sha1),
 585                                request->curl_result, request->http_code);
 586                        request->state = ABORTED;
 587                        aborted = 1;
 588                }
 589        } else if (request->state == RUN_MKCOL) {
 590                if (request->curl_result == CURLE_OK ||
 591                    request->http_code == 405) {
 592                        start_put(request);
 593                } else {
 594                        fprintf(stderr, "MKCOL %s failed, aborting (%d/%ld)\n",
 595                                sha1_to_hex(request->sha1),
 596                                request->curl_result, request->http_code);
 597                        request->state = ABORTED;
 598                        aborted = 1;
 599                }
 600        } else if (request->state == RUN_PUT) {
 601                if (request->curl_result == CURLE_OK) {
 602                        start_move(request);
 603                } else {
 604                        fprintf(stderr, "PUT %s failed, aborting (%d/%ld)\n",
 605                                sha1_to_hex(request->sha1),
 606                                request->curl_result, request->http_code);
 607                        request->state = ABORTED;
 608                        aborted = 1;
 609                }
 610        } else if (request->state == RUN_MOVE) {
 611                if (request->curl_result == CURLE_OK) {
 612                        if (push_verbosely)
 613                                fprintf(stderr,
 614                                        "sent %s\n",
 615                                        sha1_to_hex(request->sha1));
 616                        request->state = COMPLETE;
 617                } else {
 618                        fprintf(stderr, "MOVE %s failed, aborting (%d/%ld)\n",
 619                                sha1_to_hex(request->sha1),
 620                                request->curl_result, request->http_code);
 621                        request->state = ABORTED;
 622                        aborted = 1;
 623                }
 624        }
 625}
 626
 627static void release_request(struct transfer_request *request)
 628{
 629        struct transfer_request *entry = request_queue_head;
 630
 631        if (request == request_queue_head) {
 632                request_queue_head = request->next;
 633        } else {
 634                while (entry->next != NULL && entry->next != request)
 635                        entry = entry->next;
 636                if (entry->next == request)
 637                        entry->next = entry->next->next;
 638        }
 639
 640        free(request->url);
 641        free(request);
 642}
 643
 644#ifdef USE_CURL_MULTI
 645void process_curl_messages(void)
 646{
 647        int num_messages;
 648        struct active_request_slot *slot;
 649        struct transfer_request *request = NULL;
 650        CURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages);
 651
 652        while (curl_message != NULL) {
 653                if (curl_message->msg == CURLMSG_DONE) {
 654                        slot = active_queue_head;
 655                        while (slot != NULL &&
 656                               slot->curl != curl_message->easy_handle)
 657                                slot = slot->next;
 658                        if (slot != NULL) {
 659                                curl_multi_remove_handle(curlm, slot->curl);
 660                                active_requests--;
 661                                slot->done = 1;
 662                                slot->in_use = 0;
 663                                slot->curl_result = curl_message->data.result;
 664                                curl_easy_getinfo(slot->curl,
 665                                                  CURLINFO_HTTP_CODE,
 666                                                  &slot->http_code);
 667                                request = request_queue_head;
 668                                while (request != NULL &&
 669                                       request->slot != slot)
 670                                        request = request->next;
 671                                if (request != NULL)
 672                                        finish_request(request);
 673                        } else {
 674                                fprintf(stderr, "Received DONE message for unknown request!\n");
 675                        }
 676                } else {
 677                        fprintf(stderr, "Unknown CURL message received: %d\n",
 678                                (int)curl_message->msg);
 679                }
 680                curl_message = curl_multi_info_read(curlm, &num_messages);
 681        }
 682}
 683
 684void process_request_queue(void)
 685{
 686        struct transfer_request *request = request_queue_head;
 687        struct active_request_slot *slot = active_queue_head;
 688        int num_transfers;
 689
 690        if (aborted)
 691                return;
 692
 693        while (active_requests < max_requests && request != NULL) {
 694                if (!pushing && request->state == NEED_CHECK) {
 695                        start_check(request);
 696                        curl_multi_perform(curlm, &num_transfers);
 697                } else if (pushing && request->state == NEED_PUSH) {
 698                        start_mkcol(request);
 699                        curl_multi_perform(curlm, &num_transfers);
 700                }
 701                request = request->next;
 702        }
 703
 704        while (slot != NULL) {
 705                if (!slot->in_use && slot->curl != NULL) {
 706                        curl_easy_cleanup(slot->curl);
 707                        slot->curl = NULL;
 708                }
 709                slot = slot->next;
 710        }                               
 711}
 712#endif
 713
 714void process_waiting_requests(void)
 715{
 716        struct active_request_slot *slot = active_queue_head;
 717
 718        while (slot != NULL)
 719                if (slot->in_use) {
 720                        run_active_slot(slot);
 721                        slot = active_queue_head;
 722                } else {
 723                        slot = slot->next;
 724                }
 725}
 726
 727void add_request(unsigned char *sha1, char *lock_token)
 728{
 729        struct transfer_request *request = request_queue_head;
 730        struct packed_git *target;
 731        
 732        while (request != NULL && memcmp(request->sha1, sha1, 20))
 733                request = request->next;
 734        if (request != NULL)
 735                return;
 736
 737        target = find_sha1_pack(sha1, remote->packs);
 738        if (target)
 739                return;
 740
 741        request = xmalloc(sizeof(*request));
 742        memcpy(request->sha1, sha1, 20);
 743        request->url = NULL;
 744        request->lock_token = lock_token;
 745        request->headers = NULL;
 746        request->state = NEED_CHECK;
 747        request->next = request_queue_head;
 748        request_queue_head = request;
 749#ifdef USE_CURL_MULTI
 750        process_request_queue();
 751        process_curl_messages();
 752#endif
 753}
 754
 755static int fetch_index(unsigned char *sha1)
 756{
 757        char *hex = sha1_to_hex(sha1);
 758        char *filename;
 759        char *url;
 760        char tmpfile[PATH_MAX];
 761        long prev_posn = 0;
 762        char range[RANGE_HEADER_SIZE];
 763        struct curl_slist *range_header = NULL;
 764
 765        FILE *indexfile;
 766        struct active_request_slot *slot;
 767
 768        /* Don't use the index if the pack isn't there */
 769        url = xmalloc(strlen(remote->url) + 65);
 770        sprintf(url, "%s/objects/pack/pack-%s.pack", remote->url, hex);
 771        slot = get_active_slot();
 772        curl_easy_setopt(slot->curl, CURLOPT_URL, url);
 773        curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
 774        if (start_active_slot(slot)) {
 775                run_active_slot(slot);
 776                if (slot->curl_result != CURLE_OK) {
 777                        free(url);
 778                        return error("Unable to verify pack %s is available",
 779                                     hex);
 780                }
 781        } else {
 782                return error("Unable to start request");
 783        }
 784
 785        if (has_pack_index(sha1))
 786                return 0;
 787
 788        if (push_verbosely)
 789                fprintf(stderr, "Getting index for pack %s\n", hex);
 790        
 791        sprintf(url, "%s/objects/pack/pack-%s.idx", remote->url, hex);
 792        
 793        filename = sha1_pack_index_name(sha1);
 794        snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
 795        indexfile = fopen(tmpfile, "a");
 796        if (!indexfile)
 797                return error("Unable to open local file %s for pack index",
 798                             filename);
 799
 800        slot = get_active_slot();
 801        curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
 802        curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
 803        curl_easy_setopt(slot->curl, CURLOPT_FILE, indexfile);
 804        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
 805        curl_easy_setopt(slot->curl, CURLOPT_URL, url);
 806        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
 807        slot->local = indexfile;
 808
 809        /* If there is data present from a previous transfer attempt,
 810           resume where it left off */
 811        prev_posn = ftell(indexfile);
 812        if (prev_posn>0) {
 813                if (push_verbosely)
 814                        fprintf(stderr,
 815                                "Resuming fetch of index for pack %s at byte %ld\n",
 816                                hex, prev_posn);
 817                sprintf(range, "Range: bytes=%ld-", prev_posn);
 818                range_header = curl_slist_append(range_header, range);
 819                curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
 820        }
 821
 822        if (start_active_slot(slot)) {
 823                run_active_slot(slot);
 824                if (slot->curl_result != CURLE_OK) {
 825                        free(url);
 826                        fclose(indexfile);
 827                        return error("Unable to get pack index %s\n%s", url,
 828                                     curl_errorstr);
 829                }
 830        } else {
 831                free(url);
 832                return error("Unable to start request");
 833        }
 834
 835        free(url);
 836        fclose(indexfile);
 837
 838        return move_temp_to_file(tmpfile, filename);
 839}
 840
 841static int setup_index(unsigned char *sha1)
 842{
 843        struct packed_git *new_pack;
 844
 845        if (fetch_index(sha1))
 846                return -1;
 847
 848        new_pack = parse_pack_index(sha1);
 849        new_pack->next = remote->packs;
 850        remote->packs = new_pack;
 851        return 0;
 852}
 853
 854static int fetch_indices()
 855{
 856        unsigned char sha1[20];
 857        char *url;
 858        struct buffer buffer;
 859        char *data;
 860        int i = 0;
 861
 862        struct active_request_slot *slot;
 863
 864        data = xmalloc(4096);
 865        memset(data, 0, 4096);
 866        buffer.size = 4096;
 867        buffer.posn = 0;
 868        buffer.buffer = data;
 869
 870        if (push_verbosely)
 871                fprintf(stderr, "Getting pack list\n");
 872        
 873        url = xmalloc(strlen(remote->url) + 21);
 874        sprintf(url, "%s/objects/info/packs", remote->url);
 875
 876        slot = get_active_slot();
 877        curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
 878        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
 879                         fwrite_buffer_dynamic);
 880        curl_easy_setopt(slot->curl, CURLOPT_URL, url);
 881        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
 882        if (start_active_slot(slot)) {
 883                run_active_slot(slot);
 884                if (slot->curl_result != CURLE_OK) {
 885                        free(buffer.buffer);
 886                        free(url);
 887                        if (slot->http_code == 404)
 888                                return 0;
 889                        else
 890                                return error("%s", curl_errorstr);
 891                }
 892        } else {
 893                free(buffer.buffer);
 894                free(url);
 895                return error("Unable to start request");
 896        }
 897        free(url);
 898
 899        data = buffer.buffer;
 900        while (i < buffer.posn) {
 901                switch (data[i]) {
 902                case 'P':
 903                        i++;
 904                        if (i + 52 < buffer.posn &&
 905                            !strncmp(data + i, " pack-", 6) &&
 906                            !strncmp(data + i + 46, ".pack\n", 6)) {
 907                                get_sha1_hex(data + i + 6, sha1);
 908                                setup_index(sha1);
 909                                i += 51;
 910                                break;
 911                        }
 912                default:
 913                        while (data[i] != '\n')
 914                                i++;
 915                }
 916                i++;
 917        }
 918
 919        free(buffer.buffer);
 920        return 0;
 921}
 922
 923static inline int needs_quote(int ch)
 924{
 925        switch (ch) {
 926        case '/': case '-': case '.':
 927        case 'A'...'Z': case 'a'...'z': case '0'...'9':
 928                return 0;
 929        default:
 930                return 1;
 931        }
 932}
 933
 934static inline int hex(int v)
 935{
 936        if (v < 10) return '0' + v;
 937        else return 'A' + v - 10;
 938}
 939
 940static char *quote_ref_url(const char *base, const char *ref)
 941{
 942        const char *cp;
 943        char *dp, *qref;
 944        int len, baselen, ch;
 945
 946        baselen = strlen(base);
 947        len = baselen + 12; /* "refs/heads/" + NUL */
 948        for (cp = ref; (ch = *cp) != 0; cp++, len++)
 949                if (needs_quote(ch))
 950                        len += 2; /* extra two hex plus replacement % */
 951        qref = xmalloc(len);
 952        memcpy(qref, base, baselen);
 953        memcpy(qref + baselen, "refs/heads/", 11);
 954        for (cp = ref, dp = qref + baselen + 11; (ch = *cp) != 0; cp++) {
 955                if (needs_quote(ch)) {
 956                        *dp++ = '%';
 957                        *dp++ = hex((ch >> 4) & 0xF);
 958                        *dp++ = hex(ch & 0xF);
 959                }
 960                else
 961                        *dp++ = ch;
 962        }
 963        *dp = 0;
 964
 965        return qref;
 966}
 967
 968int fetch_ref(char *ref, unsigned char *sha1)
 969{
 970        char *url;
 971        char hex[42];
 972        struct buffer buffer;
 973        char *base = remote->url;
 974        struct active_request_slot *slot;
 975        buffer.size = 41;
 976        buffer.posn = 0;
 977        buffer.buffer = hex;
 978        hex[41] = '\0';
 979        
 980        url = quote_ref_url(base, ref);
 981        slot = get_active_slot();
 982        curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
 983        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
 984                         fwrite_buffer_dynamic);
 985        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
 986        curl_easy_setopt(slot->curl, CURLOPT_URL, url);
 987        if (start_active_slot(slot)) {
 988                run_active_slot(slot);
 989                if (slot->curl_result != CURLE_OK)
 990                        return error("Couldn't get %s for %s\n%s",
 991                                     url, ref, curl_errorstr);
 992        } else {
 993                return error("Unable to start request");
 994        }
 995
 996        hex[40] = '\0';
 997        get_sha1_hex(hex, sha1);
 998        return 0;
 999}
1000
1001static void
1002start_lockprop_element(void *userData, const char *name, const char **atts)
1003{
1004        struct lockprop *prop = (struct lockprop *)userData;
1005
1006        if (prop->lock_type && !strcmp(name, "D:write")) {
1007                if (prop->lock_exclusive) {
1008                        prop->lock_exclusive_write = 1;
1009                }
1010        } else if (prop->lock_scope && !strcmp(name, "D:exclusive")) {
1011                prop->lock_exclusive = 1;
1012        } else if (prop->lock_entry) {
1013                if (!strcmp(name, "D:lockscope")) {
1014                        prop->lock_scope = 1;
1015                } else if (!strcmp(name, "D:locktype")) {
1016                        prop->lock_type = 1;
1017                }
1018        } else if (prop->supported_lock) {
1019                if (!strcmp(name, "D:lockentry")) {
1020                        prop->lock_entry = 1;
1021                }
1022        } else if (!strcmp(name, "D:supportedlock")) {
1023                prop->supported_lock = 1;
1024        }
1025}
1026
1027static void
1028end_lockprop_element(void *userData, const char *name)
1029{
1030        struct lockprop *prop = (struct lockprop *)userData;
1031
1032        if (!strcmp(name, "D:lockentry")) {
1033                prop->lock_entry = 0;
1034                prop->lock_scope = 0;
1035                prop->lock_type = 0;
1036                prop->lock_exclusive = 0;
1037        } else if (!strcmp(name, "D:supportedlock")) {
1038                prop->supported_lock = 0;
1039        }
1040}
1041
1042size_t process_lock_header( void *ptr, size_t size, size_t nmemb, void *stream)
1043{
1044        size_t header_size = size*nmemb;
1045        char *start;
1046        char *end;
1047
1048        if (!strncmp(ptr, "Lock-Token: <opaquelocktoken:", 29)) {
1049                start = ptr + 29;
1050                for (end = ptr + header_size;
1051                     *(end - 1) == '\r' || *(end - 1) == '\n' || *(end - 1) == '>';
1052                     end--) {}
1053                if (end > start) {
1054                        lock_token = xmalloc(end - start + 1);
1055                        memcpy(lock_token, start, end - start);
1056                        lock_token[end - start] = 0;
1057                }
1058        }
1059
1060        return header_size;
1061}
1062
1063char *lock_remote(char *file, int timeout)
1064{
1065        struct active_request_slot *slot;
1066        struct buffer out_buffer;
1067        char *out_data;
1068        char *url;
1069        char *ep;
1070        char timeout_header[25];
1071        struct curl_slist *dav_headers = NULL;
1072
1073        if (lock_token != NULL)
1074                free(lock_token);
1075
1076        url = xmalloc(strlen(remote->url) + strlen(file) + 1);
1077        sprintf(url, "%s%s", remote->url, file);
1078
1079        /* Make sure leading directories exist for the remote ref */
1080        ep = strchr(url + strlen(remote->url) + 11, '/');
1081        while (ep) {
1082                *ep = 0;
1083                slot = get_active_slot();
1084                curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
1085                curl_easy_setopt(slot->curl, CURLOPT_URL, url);
1086                curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MKCOL);
1087                curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
1088                if (start_active_slot(slot)) {
1089                        run_active_slot(slot);
1090                        if (slot->curl_result != CURLE_OK &&
1091                            slot->http_code != 405) {
1092                                fprintf(stderr,
1093                                        "Unable to create branch path %s\n",
1094                                        url);
1095                                free(url);
1096                                return NULL;
1097                        }
1098                } else {
1099                        fprintf(stderr, "Unable to start request\n");
1100                        free(url);
1101                        return NULL;
1102                }
1103                *ep = '/';
1104                ep = strchr(ep + 1, '/');
1105        }
1106
1107        out_buffer.size = strlen(LOCK_REQUEST) + strlen(git_default_email) - 2;
1108        out_data = xmalloc(out_buffer.size + 1);
1109        snprintf(out_data, out_buffer.size + 1, LOCK_REQUEST, git_default_email);
1110        out_buffer.posn = 0;
1111        out_buffer.buffer = out_data;
1112
1113        sprintf(timeout_header, "Timeout: Second-%d", timeout);
1114        dav_headers = curl_slist_append(dav_headers, timeout_header);
1115        dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
1116
1117        slot = get_active_slot();
1118        curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
1119        curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
1120        curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
1121        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
1122        curl_easy_setopt(slot->curl, CURLOPT_HEADERFUNCTION,
1123                process_lock_header);
1124        curl_easy_setopt(slot->curl, CURLOPT_URL, url);
1125        curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
1126        curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK);
1127        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
1128
1129        if (start_active_slot(slot)) {
1130                run_active_slot(slot);
1131                if (slot->curl_result != CURLE_OK) {
1132                        fprintf(stderr, "Got HTTP error %ld\n", slot->http_code);
1133                        free(url);
1134                        free(out_data);
1135                        return NULL;
1136                }
1137        } else {
1138                free(url);
1139                free(out_data);
1140                fprintf(stderr, "Unable to start request\n");
1141                return NULL;
1142        }
1143
1144        free(url);
1145        free(out_data);
1146
1147        return strdup(lock_token);
1148}
1149
1150int unlock_remote(char *file, char *lock_token)
1151{
1152        struct active_request_slot *slot;
1153        char *url;
1154        char *lock_token_header;
1155        struct curl_slist *dav_headers = NULL;
1156        int rc = 0;
1157
1158        if (lock_token == NULL) {
1159                fprintf(stderr, "Unable to unlock, no lock token");
1160                return 0;
1161        }
1162
1163        lock_token_header = xmalloc(strlen(lock_token) + 31);
1164        sprintf(lock_token_header, "Lock-Token: <opaquelocktoken:%s>",
1165                lock_token);
1166        url = xmalloc(strlen(remote->url) + strlen(file) + 1);
1167        sprintf(url, "%s%s", remote->url, file);
1168        dav_headers = curl_slist_append(dav_headers, lock_token_header);
1169
1170        slot = get_active_slot();
1171        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
1172        curl_easy_setopt(slot->curl, CURLOPT_URL, url);
1173        curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_UNLOCK);
1174        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
1175
1176        if (start_active_slot(slot)) {
1177                run_active_slot(slot);
1178                if (slot->curl_result == CURLE_OK)
1179                        rc = 1;
1180                else
1181                        fprintf(stderr, "Got HTTP error %ld\n",
1182                                slot->http_code);
1183        } else {
1184                fprintf(stderr, "Unable to start request\n");
1185        }
1186
1187        curl_slist_free_all(dav_headers);
1188        free(lock_token_header);
1189        free(url);
1190
1191        return rc;
1192}
1193
1194int check_locking()
1195{
1196        struct active_request_slot *slot;
1197        struct buffer in_buffer;
1198        struct buffer out_buffer;
1199        char *in_data;
1200        char *out_data;
1201        XML_Parser parser = XML_ParserCreate(NULL);
1202        enum XML_Status result;
1203        struct lockprop supported_lock;
1204        struct curl_slist *dav_headers = NULL;
1205
1206        out_buffer.size = strlen(PROPFIND_REQUEST) + strlen(remote->url) - 2;
1207        out_data = xmalloc(out_buffer.size + 1);
1208        snprintf(out_data, out_buffer.size + 1, PROPFIND_REQUEST, remote->url);
1209        out_buffer.posn = 0;
1210        out_buffer.buffer = out_data;
1211
1212        in_buffer.size = 4096;
1213        in_data = xmalloc(in_buffer.size);
1214        in_buffer.posn = 0;
1215        in_buffer.buffer = in_data;
1216
1217        dav_headers = curl_slist_append(dav_headers, "Depth: 0");
1218        dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
1219        
1220        slot = get_active_slot();
1221        curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
1222        curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
1223        curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
1224        curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
1225        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
1226                         fwrite_buffer_dynamic);
1227        curl_easy_setopt(slot->curl, CURLOPT_URL, remote->url);
1228        curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
1229        curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);
1230        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
1231
1232        if (start_active_slot(slot)) {
1233                run_active_slot(slot);
1234                free(out_data);
1235                if (slot->curl_result != CURLE_OK) {
1236                        free(in_buffer.buffer);
1237                        return -1;
1238                }
1239
1240                XML_SetUserData(parser, &supported_lock);
1241                XML_SetElementHandler(parser, start_lockprop_element,
1242                                      end_lockprop_element);
1243                result = XML_Parse(parser, in_buffer.buffer, in_buffer.posn, 1);
1244                free(in_buffer.buffer);
1245                if (result != XML_STATUS_OK)
1246                        return error("%s", XML_ErrorString(
1247                                             XML_GetErrorCode(parser)));
1248        } else {
1249                free(out_data);
1250                free(in_buffer.buffer);
1251                return error("Unable to start request");
1252        }
1253
1254        if (supported_lock.lock_exclusive_write)
1255                return 0;
1256        else
1257                return 1;
1258}
1259
1260int is_ancestor(unsigned char *sha1, struct commit *commit)
1261{
1262        struct commit_list *parents;
1263
1264        if (parse_commit(commit))
1265                return 0;
1266        parents = commit->parents;
1267        for (; parents; parents = parents->next) {
1268                if (!memcmp(sha1, parents->item->object.sha1, 20)) {
1269                        return 1;
1270                } else if (parents->item->object.type == commit_type) {
1271                        if (is_ancestor(
1272                                    sha1,
1273                                    (struct commit *)&parents->item->object
1274                                    ))
1275                                return 1;
1276                }
1277        }
1278        return 0;
1279}
1280
1281void get_delta(unsigned char *sha1, struct object *obj, char *lock_token)
1282{
1283        struct commit *commit;
1284        struct commit_list *parents;
1285        struct tree *tree;
1286        struct tree_entry_list *entry;
1287
1288        if (sha1 && !memcmp(sha1, obj->sha1, 20))
1289                return;
1290
1291        if (aborted)
1292                return;
1293
1294        if (obj->type == commit_type) {
1295                if (push_verbosely)
1296                        fprintf(stderr, "walk %s\n", sha1_to_hex(obj->sha1));
1297                add_request(obj->sha1, lock_token);
1298                commit = (struct commit *)obj;
1299                if (parse_commit(commit)) {
1300                        fprintf(stderr, "Error parsing commit %s\n",
1301                                sha1_to_hex(obj->sha1));
1302                        aborted = 1;
1303                        return;
1304                }
1305                parents = commit->parents;
1306                for (; parents; parents = parents->next)
1307                        if (sha1 == NULL ||
1308                            memcmp(sha1, parents->item->object.sha1, 20))
1309                                get_delta(sha1, &parents->item->object,
1310                                          lock_token);
1311                get_delta(sha1, &commit->tree->object, lock_token);
1312        } else if (obj->type == tree_type) {
1313                if (push_verbosely)
1314                        fprintf(stderr, "walk %s\n", sha1_to_hex(obj->sha1));
1315                add_request(obj->sha1, lock_token);
1316                tree = (struct tree *)obj;
1317                if (parse_tree(tree)) {
1318                        fprintf(stderr, "Error parsing tree %s\n",
1319                                sha1_to_hex(obj->sha1));
1320                        aborted = 1;
1321                        return;
1322                }
1323                entry = tree->entries;
1324                tree->entries = NULL;
1325                while (entry) {
1326                        struct tree_entry_list *next = entry->next;
1327                        get_delta(sha1, entry->item.any, lock_token);
1328                        free(entry->name);
1329                        free(entry);
1330                        entry = next;
1331                }
1332        } else if (obj->type == blob_type || obj->type == tag_type) {
1333                add_request(obj->sha1, lock_token);
1334        }
1335}
1336
1337int update_remote(char *remote_path, unsigned char *sha1, char *lock_token)
1338{
1339        struct active_request_slot *slot;
1340        char *url;
1341        char *out_data;
1342        char *if_header;
1343        struct buffer out_buffer;
1344        struct curl_slist *dav_headers = NULL;
1345        int i;
1346
1347        url = xmalloc(strlen(remote->url) + strlen(remote_path) + 1);
1348        sprintf(url, "%s%s", remote->url, remote_path);
1349
1350        if_header = xmalloc(strlen(lock_token) + 25);
1351        sprintf(if_header, "If: (<opaquelocktoken:%s>)", lock_token);
1352        dav_headers = curl_slist_append(dav_headers, if_header);
1353
1354        out_buffer.size = 41;
1355        out_data = xmalloc(out_buffer.size + 1);
1356        i = snprintf(out_data, out_buffer.size + 1, "%s\n", sha1_to_hex(sha1));
1357        if (i != out_buffer.size) {
1358                fprintf(stderr, "Unable to initialize PUT request body\n");
1359                return 0;
1360        }
1361        out_buffer.posn = 0;
1362        out_buffer.buffer = out_data;
1363
1364        slot = get_active_slot();
1365        curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
1366        curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
1367        curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
1368        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
1369        curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
1370        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
1371        curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
1372        curl_easy_setopt(slot->curl, CURLOPT_PUT, 1);
1373        curl_easy_setopt(slot->curl, CURLOPT_URL, url);
1374
1375        if (start_active_slot(slot)) {
1376                run_active_slot(slot);
1377                free(out_data);
1378                free(if_header);
1379                free(url);
1380                if (slot->curl_result != CURLE_OK) {
1381                        fprintf(stderr,
1382                                "PUT error: curl result=%d, HTTP code=%ld\n",
1383                                slot->curl_result, slot->http_code);
1384                        /* We should attempt recovery? */
1385                        return 0;
1386                }
1387        } else {
1388                free(out_data);
1389                free(if_header);
1390                free(url);
1391                fprintf(stderr, "Unable to start PUT request\n");
1392                return 0;
1393        }
1394
1395        return 1;
1396}
1397
1398int main(int argc, char **argv)
1399{
1400        struct active_request_slot *slot;
1401        struct active_request_slot *next_slot;
1402        struct transfer_request *request;
1403        struct transfer_request *next_request;
1404        int nr_refspec = 0;
1405        char **refspec = NULL;
1406        int do_remote_update;
1407        int new_branch;
1408        int force_this;
1409        char *local_ref;
1410        unsigned char local_sha1[20];
1411        struct object *local_object = NULL;
1412        char *remote_ref = NULL;
1413        unsigned char remote_sha1[20];
1414        char *remote_lock = NULL;
1415        char *remote_path = NULL;
1416        char *low_speed_limit;
1417        char *low_speed_time;
1418        int rc = 0;
1419        int i;
1420
1421        setup_ident();
1422
1423        remote = xmalloc(sizeof(*remote));
1424        remote->url = NULL;
1425        remote->packs = NULL;
1426
1427        argv++;
1428        for (i = 1; i < argc; i++, argv++) {
1429                char *arg = *argv;
1430
1431                if (*arg == '-') {
1432                        if (!strcmp(arg, "--complete")) {
1433                                push_all = 1;
1434                                continue;
1435                        }
1436                        if (!strcmp(arg, "--force")) {
1437                                force_all = 1;
1438                                continue;
1439                        }
1440                        if (!strcmp(arg, "--verbose")) {
1441                                push_verbosely = 1;
1442                                continue;
1443                        }
1444                        usage(http_push_usage);
1445                }
1446                if (!remote->url) {
1447                        remote->url = arg;
1448                        continue;
1449                }
1450                refspec = argv;
1451                nr_refspec = argc - i;
1452                break;
1453        }
1454
1455        curl_global_init(CURL_GLOBAL_ALL);
1456
1457#ifdef USE_CURL_MULTI
1458        {
1459                char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS");
1460                if (http_max_requests != NULL)
1461                        max_requests = atoi(http_max_requests);
1462        }
1463
1464        curlm = curl_multi_init();
1465        if (curlm == NULL) {
1466                fprintf(stderr, "Error creating curl multi handle.\n");
1467                return 1;
1468        }
1469#endif
1470
1471        if (getenv("GIT_SSL_NO_VERIFY"))
1472                curl_ssl_verify = 0;
1473
1474        ssl_cert = getenv("GIT_SSL_CERT");
1475#if LIBCURL_VERSION_NUM >= 0x070902
1476        ssl_key = getenv("GIT_SSL_KEY");
1477#endif
1478#if LIBCURL_VERSION_NUM >= 0x070908
1479        ssl_capath = getenv("GIT_SSL_CAPATH");
1480#endif
1481        ssl_cainfo = getenv("GIT_SSL_CAINFO");
1482
1483        low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT");
1484        if (low_speed_limit != NULL)
1485                curl_low_speed_limit = strtol(low_speed_limit, NULL, 10);
1486        low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME");
1487        if (low_speed_time != NULL)
1488                curl_low_speed_time = strtol(low_speed_time, NULL, 10);
1489
1490        git_config(http_options);
1491
1492        if (curl_ssl_verify == -1)
1493                curl_ssl_verify = 1;
1494
1495#ifdef USE_CURL_MULTI
1496        if (max_requests < 1)
1497                max_requests = DEFAULT_MAX_REQUESTS;
1498#endif
1499
1500        no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
1501        default_headers = curl_slist_append(default_headers, "Range:");
1502        default_headers = curl_slist_append(default_headers, "Destination:");
1503        default_headers = curl_slist_append(default_headers, "If:");
1504        default_headers = curl_slist_append(default_headers,
1505                                            "Pragma: no-cache");
1506
1507#ifndef NO_CURL_EASY_DUPHANDLE
1508        curl_default = get_curl_handle();
1509#endif
1510
1511        /* Verify DAV compliance/lock support */
1512        if (check_locking() != 0) {
1513                fprintf(stderr, "Error: no DAV locking support on remote repo %s\n", remote->url);
1514                rc = 1;
1515                goto cleanup;
1516        }
1517
1518        /* Process each refspec */
1519        for (i = 0; i < nr_refspec; i++) {
1520                char *ep;
1521                force_this = 0;
1522                do_remote_update = 0;
1523                new_branch = 0;
1524                local_ref = refspec[i];
1525                if (*local_ref == '+') {
1526                        force_this = 1;
1527                        local_ref++;
1528                }
1529                ep = strchr(local_ref, ':');
1530                if (ep) {
1531                        remote_ref = ep + 1;
1532                        *ep = 0;
1533                }
1534                else
1535                        remote_ref = local_ref;
1536
1537                /* Lock remote branch ref */
1538                if (remote_path)
1539                        free(remote_path);
1540                remote_path = xmalloc(strlen(remote_ref) + 12);
1541                sprintf(remote_path, "refs/heads/%s", remote_ref);
1542                remote_lock = lock_remote(remote_path, 3600);
1543                if (remote_lock == NULL) {
1544                        fprintf(stderr, "Unable to lock remote branch %s\n",
1545                                remote_ref);
1546                        rc = 1;
1547                        continue;
1548                }
1549
1550                /* Resolve local and remote refs */
1551                if (fetch_ref(remote_ref, remote_sha1) != 0) {
1552                        fprintf(stderr,
1553                                "Remote branch %s does not exist on %s\n",
1554                                remote_ref, remote->url);
1555                        new_branch = 1;
1556                }
1557                if (get_sha1(local_ref, local_sha1) != 0) {
1558                        fprintf(stderr, "Error resolving local branch %s\n",
1559                                local_ref);
1560                        rc = 1;
1561                        goto unlock;
1562                }
1563        
1564                /* Find relationship between local and remote */
1565                local_object = parse_object(local_sha1);
1566                if (!local_object) {
1567                        fprintf(stderr, "Unable to parse local object %s\n",
1568                                sha1_to_hex(local_sha1));
1569                        rc = 1;
1570                        goto unlock;
1571                } else if (new_branch) {
1572                        do_remote_update = 1;
1573                } else {
1574                        if (!memcmp(local_sha1, remote_sha1, 20)) {
1575                                fprintf(stderr,
1576                                        "* %s: same as branch '%s' of %s\n",
1577                                        local_ref, remote_ref, remote->url);
1578                        } else if (is_ancestor(remote_sha1,
1579                                               (struct commit *)local_object)) {
1580                                fprintf(stderr,
1581                                        "Remote %s will fast-forward to local %s\n",
1582                                        remote_ref, local_ref);
1583                                do_remote_update = 1;
1584                        } else if (force_all || force_this) {
1585                                fprintf(stderr,
1586                                        "* %s on %s does not fast forward to local branch '%s', overwriting\n",
1587                                        remote_ref, remote->url, local_ref);
1588                                do_remote_update = 1;
1589                        } else {
1590                                fprintf(stderr,
1591                                        "* %s on %s does not fast forward to local branch '%s'\n",
1592                                        remote_ref, remote->url, local_ref);
1593                                rc = 1;
1594                                goto unlock;
1595                        }
1596                }
1597
1598                /* Generate and check list of required objects */
1599                pushing = 0;
1600                if (do_remote_update || push_all)
1601                        fetch_indices();
1602                get_delta(push_all ? NULL : remote_sha1,
1603                          local_object, remote_lock);
1604                process_waiting_requests();
1605
1606                /* Push missing objects to remote, this would be a
1607                   convenient time to pack them first if appropriate. */
1608                pushing = 1;
1609                process_request_queue();
1610                process_waiting_requests();
1611
1612                /* Update the remote branch if all went well */
1613                if (do_remote_update) {
1614                        if (!aborted && update_remote(remote_path,
1615                                                      local_sha1,
1616                                                      remote_lock)) {
1617                                fprintf(stderr, "%s remote branch %s\n",
1618                                        new_branch ? "Created" : "Updated",
1619                                        remote_ref);
1620                        } else {
1621                                fprintf(stderr,
1622                                        "Unable to %s remote branch %s\n",
1623                                        new_branch ? "create" : "update",
1624                                        remote_ref);
1625                                rc = 1;
1626                                goto unlock;
1627                        }
1628                }
1629
1630        unlock:
1631                unlock_remote(remote_path, remote_lock);
1632                free(remote_path);
1633                free(remote_lock);
1634        }
1635
1636 cleanup:
1637        free(remote);
1638
1639        curl_slist_free_all(no_pragma_header);
1640        curl_slist_free_all(default_headers);
1641
1642        slot = active_queue_head;
1643        while (slot != NULL) {
1644                next_slot = slot->next;
1645                if (slot->curl != NULL)
1646                        curl_easy_cleanup(slot->curl);
1647                free(slot);
1648                slot = next_slot;
1649        }
1650
1651        request = request_queue_head;
1652        while (request != NULL) {
1653                next_request = request->next;
1654                release_request(request);
1655                request = next_request;
1656        }
1657
1658#ifndef NO_CURL_EASY_DUPHANDLE
1659        curl_easy_cleanup(curl_default);
1660#endif
1661#ifdef USE_CURL_MULTI
1662        curl_multi_cleanup(curlm);
1663#endif
1664        curl_global_cleanup();
1665        return rc;
1666}