Use $(git rev-parse --show-toplevel) in cd_to_toplevel().
[gitweb.git] / http-backend.c
index bfce52063f2dd31b56fb421acf6c4b4f856749d6..f729488fc5f787c0f4997aacdd1bb14732d717f7 100644 (file)
@@ -10,6 +10,7 @@
 static const char content_type[] = "Content-Type";
 static const char content_length[] = "Content-Length";
 static const char last_modified[] = "Last-Modified";
+static int getanyfile = 1;
 
 static struct string_list *query_params;
 
@@ -107,6 +108,7 @@ static const char *get_parameter(const char *name)
        return i ? i->util : NULL;
 }
 
+__attribute__((format (printf, 2, 3)))
 static void format_write(int fd, const char *fmt, ...)
 {
        static char buffer[1024];
@@ -133,7 +135,7 @@ static void hdr_str(const char *name, const char *value)
        format_write(1, "%s: %s\r\n", name, value);
 }
 
-static void hdr_int(const char *name, size_t value)
+static void hdr_int(const char *name, uintmax_t value)
 {
        format_write(1, "%s: %" PRIuMAX "\r\n", name, value);
 }
@@ -164,6 +166,7 @@ static void end_headers(void)
        safe_write(1, "\r\n", 2);
 }
 
+__attribute__((format (printf, 1, 2)))
 static NORETURN void not_found(const char *err, ...)
 {
        va_list params;
@@ -179,6 +182,7 @@ static NORETURN void not_found(const char *err, ...)
        exit(0);
 }
 
+__attribute__((format (printf, 1, 2)))
 static NORETURN void forbidden(const char *err, ...)
 {
        va_list params;
@@ -194,6 +198,12 @@ static NORETURN void forbidden(const char *err, ...)
        exit(0);
 }
 
+static void select_getanyfile(void)
+{
+       if (!getanyfile)
+               forbidden("Unsupported service: getanyfile");
+}
+
 static void send_strbuf(const char *type, struct strbuf *buf)
 {
        hdr_int(content_length, buf->len);
@@ -202,14 +212,13 @@ static void send_strbuf(const char *type, struct strbuf *buf)
        safe_write(1, buf->buf, buf->len);
 }
 
-static void send_file(const char *the_type, const char *name)
+static void send_local_file(const char *the_type, const char *name)
 {
        const char *p = git_path("%s", name);
        size_t buf_alloc = 8192;
        char *buf = xmalloc(buf_alloc);
        int fd;
        struct stat sb;
-       size_t size;
 
        fd = open(p, O_RDONLY);
        if (fd < 0)
@@ -217,14 +226,12 @@ static void send_file(const char *the_type, const char *name)
        if (fstat(fd, &sb) < 0)
                die_errno("Cannot stat '%s'", p);
 
-       size = xsize_t(sb.st_size);
-
-       hdr_int(content_length, size);
+       hdr_int(content_length, sb.st_size);
        hdr_str(content_type, the_type);
        hdr_date(last_modified, sb.st_mtime);
        end_headers();
 
-       while (size) {
+       for (;;) {
                ssize_t n = xread(fd, buf, buf_alloc);
                if (n < 0)
                        die_errno("Cannot read '%s'", p);
@@ -238,38 +245,51 @@ static void send_file(const char *the_type, const char *name)
 
 static void get_text_file(char *name)
 {
+       select_getanyfile();
        hdr_nocache();
-       send_file("text/plain", name);
+       send_local_file("text/plain", name);
 }
 
 static void get_loose_object(char *name)
 {
+       select_getanyfile();
        hdr_cache_forever();
-       send_file("application/x-git-loose-object", name);
+       send_local_file("application/x-git-loose-object", name);
 }
 
 static void get_pack_file(char *name)
 {
+       select_getanyfile();
        hdr_cache_forever();
-       send_file("application/x-git-packed-objects", name);
+       send_local_file("application/x-git-packed-objects", name);
 }
 
 static void get_idx_file(char *name)
 {
+       select_getanyfile();
        hdr_cache_forever();
-       send_file("application/x-git-packed-objects-toc", name);
+       send_local_file("application/x-git-packed-objects-toc", name);
 }
 
 static int http_config(const char *var, const char *value, void *cb)
 {
-       struct rpc_service *svc = cb;
-
-       if (!prefixcmp(var, "http.") &&
-           !strcmp(var + 5, svc->config_name)) {
-               svc->enabled = git_config_bool(var, value);
+       if (!strcmp(var, "http.getanyfile")) {
+               getanyfile = git_config_bool(var, value);
                return 0;
        }
 
+       if (!prefixcmp(var, "http.")) {
+               int i;
+
+               for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
+                       struct rpc_service *svc = &rpc_service[i];
+                       if (!strcmp(var + 5, svc->config_name)) {
+                               svc->enabled = git_config_bool(var, value);
+                               return 0;
+                       }
+               }
+       }
+
        /* we are not interested in parsing any other configuration here */
        return 0;
 }
@@ -293,7 +313,6 @@ static struct rpc_service *select_service(const char *name)
        if (!svc)
                forbidden("Unsupported service: '%s'", name);
 
-       git_config(http_config, svc);
        if (svc->enabled < 0) {
                const char *user = getenv("REMOTE_USER");
                svc->enabled = (user && *user) ? 1 : 0;
@@ -442,6 +461,7 @@ static void get_info_refs(char *arg)
                run_service(argv);
 
        } else {
+               select_getanyfile();
                for_each_ref(show_text_ref, &buf);
                send_strbuf("text/plain", &buf);
        }
@@ -455,6 +475,7 @@ static void get_info_packs(char *arg)
        struct packed_git *p;
        size_t cnt = 0;
 
+       select_getanyfile();
        prepare_packed_git();
        for (p = packed_git; p; p = p->next) {
                if (p->pack_local)
@@ -528,6 +549,32 @@ static NORETURN void die_webcgi(const char *err, va_list params)
        exit(0);
 }
 
+static char* getdir(void)
+{
+       struct strbuf buf = STRBUF_INIT;
+       char *pathinfo = getenv("PATH_INFO");
+       char *root = getenv("GIT_PROJECT_ROOT");
+       char *path = getenv("PATH_TRANSLATED");
+
+       if (root && *root) {
+               if (!pathinfo || !*pathinfo)
+                       die("GIT_PROJECT_ROOT is set but PATH_INFO is not");
+               if (daemon_avoid_alias(pathinfo))
+                       die("'%s': aliased", pathinfo);
+               strbuf_addstr(&buf, root);
+               if (buf.buf[buf.len - 1] != '/')
+                       strbuf_addch(&buf, '/');
+               if (pathinfo[0] == '/')
+                       pathinfo++;
+               strbuf_addstr(&buf, pathinfo);
+               return strbuf_detach(&buf, NULL);
+       } else if (path && *path) {
+               return xstrdup(path);
+       } else
+               die("No GIT_PROJECT_ROOT or PATH_TRANSLATED from server");
+       return NULL;
+}
+
 static struct service_cmd {
        const char *method;
        const char *pattern;
@@ -549,7 +596,7 @@ static struct service_cmd {
 int main(int argc, char **argv)
 {
        char *method = getenv("REQUEST_METHOD");
-       char *dir = getenv("PATH_TRANSLATED");
+       char *dir;
        struct service_cmd *cmd = NULL;
        char *cmd_arg = NULL;
        int i;
@@ -561,8 +608,7 @@ int main(int argc, char **argv)
                die("No REQUEST_METHOD from server");
        if (!strcmp(method, "HEAD"))
                method = "GET";
-       if (!dir)
-               die("No PATH_TRANSLATED from server");
+       dir = getdir();
 
        for (i = 0; i < ARRAY_SIZE(services); i++) {
                struct service_cmd *c = &services[i];
@@ -572,7 +618,7 @@ int main(int argc, char **argv)
                if (regcomp(&re, c->pattern, REG_EXTENDED))
                        die("Bogus regex in service table: %s", c->pattern);
                if (!regexec(&re, dir, 1, out, 0)) {
-                       size_t n = out[0].rm_eo - out[0].rm_so;
+                       size_t n;
 
                        if (strcmp(method, c->method)) {
                                const char *proto = getenv("SERVER_PROTOCOL");
@@ -586,9 +632,10 @@ int main(int argc, char **argv)
                        }
 
                        cmd = c;
+                       n = out[0].rm_eo - out[0].rm_so;
                        cmd_arg = xmalloc(n);
-                       strncpy(cmd_arg, dir + out[0].rm_so + 1, n);
-                       cmd_arg[n] = '\0';
+                       memcpy(cmd_arg, dir + out[0].rm_so + 1, n-1);
+                       cmd_arg[n-1] = '\0';
                        dir[out[0].rm_so] = 0;
                        break;
                }
@@ -602,6 +649,7 @@ int main(int argc, char **argv)
        if (!enter_repo(dir, 0))
                not_found("Not a git repository: '%s'", dir);
 
+       git_config(http_config, NULL);
        cmd->imp(cmd_arg);
        return 0;
 }