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