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