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