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