1c1b921aa1aa6d3e2368eccd9a344fe010ea6850
   1#include "cache.h"
   2#include "pkt-line.h"
   3
   4static const char send_pack_usage[] = "git-send-pack [--exec=other] destination [heads]*";
   5
   6static const char *exec = "git-receive-pack";
   7
   8struct ref {
   9        struct ref *next;
  10        unsigned char old_sha1[20];
  11        unsigned char new_sha1[20];
  12        char name[0];
  13};
  14
  15static struct ref *ref_list = NULL;
  16
  17static int read_ref(const char *ref, unsigned char *sha1)
  18{
  19        int fd, ret;
  20        static char pathname[PATH_MAX];
  21        char buffer[60];
  22        const char *git_dir = gitenv(GIT_DIR_ENVIRONMENT) ? : DEFAULT_GIT_DIR_ENVIRONMENT;
  23
  24        snprintf(pathname, sizeof(pathname), "%s/%s", git_dir, ref);
  25        fd = open(pathname, O_RDONLY);
  26        if (fd < 0)
  27                return -1;
  28        ret = -1;
  29        if (read(fd, buffer, sizeof(buffer)) >= 40)
  30                ret = get_sha1_hex(buffer, sha1);
  31        close(fd);
  32        return ret;
  33}
  34
  35static int send_pack(int in, int out)
  36{
  37        for (;;) {
  38                unsigned char old_sha1[20];
  39                unsigned char new_sha1[20];
  40                static char buffer[1000];
  41                char *name;
  42                struct ref *n;
  43                int len;
  44
  45                len = packet_read_line(in, buffer, sizeof(buffer));
  46                if (!len)
  47                        break;
  48                if (buffer[len-1] == '\n')
  49                        buffer[--len] = 0;
  50
  51                if (len < 42 || get_sha1_hex(buffer, old_sha1) || buffer[40] != ' ')
  52                        die("protocol error: expected sha/ref, got '%s'", buffer);
  53                name = buffer + 41;
  54                if (read_ref(name, new_sha1) < 0) {
  55                        fprintf(stderr, "no such local reference '%s'\n", name);
  56                        continue;
  57                }
  58                if (!has_sha1_file(old_sha1)) {
  59                        fprintf(stderr, "remote '%s' points to object I don't have\n", name);
  60                        continue;
  61                }
  62                if (!memcmp(old_sha1, new_sha1, 20)) {
  63                        fprintf(stderr, "'%s' unchanged\n", name);
  64                } else {
  65                        char new_hex[60];
  66                        strcpy(new_hex, sha1_to_hex(new_sha1));
  67                        fprintf(stderr, "%s: updating from %s to %s\n", name, sha1_to_hex(old_sha1), new_hex);
  68                }
  69                n = xmalloc(sizeof(*n) + len - 40);
  70                memcpy(n->old_sha1, old_sha1, 20);
  71                memcpy(n->new_sha1, new_sha1, 20);
  72                memcpy(n->name, buffer + 41, len - 40);
  73                n->next = ref_list;
  74                ref_list = n;
  75        }
  76        packet_flush(out);
  77        close(out);
  78        return 0;
  79}
  80
  81/*
  82 * First, make it shell-safe.  We do this by just disallowing any
  83 * special characters. Somebody who cares can do escaping and let
  84 * through the rest. But since we're doing to feed this to ssh as
  85 * a command line, we're going to be pretty damn anal for now.
  86 */
  87static char *shell_safe(char *url)
  88{
  89        char *n = url;
  90        unsigned char c;
  91        static const char flags[256] = {
  92                ['0'...'9'] = 1,
  93                ['a'...'z'] = 1,
  94                ['A'...'Z'] = 1,
  95                ['.'] = 1, ['/'] = 1,
  96                ['-'] = 1, ['+'] = 1,
  97                [':'] = 1
  98        };
  99
 100        while ((c = *n++) != 0) {
 101                if (flags[c] != 1)
 102                        die("I don't like '%c'. Sue me.", c);
 103        }
 104        return url;
 105}
 106
 107/*
 108 * Yeah, yeah, fixme. Need to pass in the heads etc.
 109 */
 110static int setup_connection(int fd[2], char *url, char **heads)
 111{
 112        char command[1024];
 113        const char *host, *path;
 114        char *colon;
 115        int pipefd[2][2];
 116
 117        url = shell_safe(url);
 118        host = NULL;
 119        path = url;
 120        colon = strchr(url, ':');
 121        if (colon) {
 122                *colon = 0;
 123                host = url;
 124                path = colon+1;
 125        }
 126        snprintf(command, sizeof(command), "%s %s", exec, path);
 127        if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0)
 128                die("unable to create pipe pair for communication");
 129        if (!fork()) {
 130                dup2(pipefd[1][0], 0);
 131                dup2(pipefd[0][1], 1);
 132                close(pipefd[0][0]);
 133                close(pipefd[0][1]);
 134                close(pipefd[1][0]);
 135                close(pipefd[1][1]);
 136                if (host)
 137                        execlp("ssh", "ssh", host, command, NULL);
 138                else
 139                        execlp("sh", "sh", "-c", command, NULL);
 140                die("exec failed");
 141        }               
 142        fd[0] = pipefd[0][0];
 143        fd[1] = pipefd[1][1];
 144        close(pipefd[0][1]);
 145        close(pipefd[1][0]);
 146        return 0;
 147}
 148
 149int main(int argc, char **argv)
 150{
 151        int i, nr_heads = 0;
 152        char *dest = NULL;
 153        char **heads = NULL;
 154        int fd[2];
 155
 156        argv++;
 157        for (i = 1; i < argc; i++) {
 158                char *arg = *argv++;
 159
 160                if (*arg == '-') {
 161                        if (!strncmp(arg, "--exec=", 7)) {
 162                                exec = arg + 7;
 163                                continue;
 164                        }
 165                        usage(send_pack_usage);
 166                }
 167                dest = arg;
 168                heads = argv;
 169                nr_heads = argc - i -1;
 170                break;
 171        }
 172        if (!dest)
 173                usage(send_pack_usage);
 174        if (setup_connection(fd, dest, heads))
 175                return 1;
 176        return send_pack(fd[0], fd[1]);
 177}