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