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