Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
git-mirror
/
git
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
0
Pull requests
0
Actions
Projects
0
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Security
Insights
Files
9a8c2b6
Documentation
block-sha1
builtin
compat
contrib
ewah
git-gui
gitk-git
gitweb
mergetools
perl
po
ppc
t
templates
vcs-svn
xdiff
.gitattributes
.gitignore
.mailmap
COPYING
GIT-VERSION-GEN
INSTALL
LGPL-2.1
Makefile
README
RelNotes
abspath.c
aclocal.m4
advice.c
advice.h
alias.c
alloc.c
archive-tar.c
archive-zip.c
archive.c
archive.h
argv-array.c
argv-array.h
attr.c
attr.h
base85.c
bisect.c
bisect.h
blob.c
blob.h
branch.c
branch.h
builtin.h
bulk-checkin.c
bulk-checkin.h
bundle.c
bundle.h
cache-tree.c
cache-tree.h
cache.h
check-builtins.sh
check-racy.c
check_bindir
color.c
color.h
column.c
column.h
combine-diff.c
command-list.txt
commit-slab.h
commit.c
commit.h
config.c
config.mak.in
config.mak.uname
configure.ac
connect.c
connect.h
connected.c
connected.h
convert.c
convert.h
copy.c
credential-cache--daemon.c
credential-cache.c
credential-store.c
credential.c
credential.h
csum-file.c
csum-file.h
ctype.c
daemon.c
date.c
decorate.c
decorate.h
delta.h
diff-delta.c
diff-lib.c
diff-no-index.c
diff.c
diff.h
diffcore-break.c
diffcore-delta.c
diffcore-order.c
diffcore-pickaxe.c
diffcore-rename.c
diffcore.h
dir.c
dir.h
editor.c
entry.c
environment.c
exec_cmd.c
exec_cmd.h
fast-import.c
fetch-pack.c
fetch-pack.h
fmt-merge-msg.h
fsck.c
fsck.h
generate-cmdlist.sh
gettext.c
gettext.h
git-add--interactive.perl
git-am.sh
git-archimport.perl
git-bisect.sh
git-compat-util.h
git-cvsexportcommit.perl
git-cvsimport.perl
git-cvsserver.perl
git-difftool--helper.sh
git-difftool.perl
git-filter-branch.sh
git-instaweb.sh
git-merge-octopus.sh
git-merge-one-file.sh
git-merge-resolve.sh
git-mergetool--lib.sh
git-mergetool.sh
git-p4.py
git-parse-remote.sh
git-pull.sh
git-quiltimport.sh
git-rebase--am.sh
git-rebase--interactive.sh
git-rebase--merge.sh
git-rebase.sh
git-relink.perl
git-remote-testgit.sh
git-request-pull.sh
git-send-email.perl
git-sh-i18n.sh
git-sh-setup.sh
git-stash.sh
git-submodule.sh
git-svn.perl
git-web--browse.sh
git.c
git.rc
git.spec.in
gpg-interface.c
gpg-interface.h
graph.c
graph.h
grep.c
grep.h
hashmap.c
hashmap.h
help.c
help.h
hex.c
http-backend.c
http-fetch.c
http-push.c
http-walker.c
http.c
http.h
ident.c
imap-send.c
khash.h
kwset.c
kwset.h
levenshtein.c
levenshtein.h
line-log.c
line-log.h
line-range.c
line-range.h
list-objects.c
list-objects.h
ll-merge.c
ll-merge.h
lockfile.c
log-tree.c
log-tree.h
mailmap.c
mailmap.h
match-trees.c
merge-blobs.c
merge-blobs.h
merge-recursive.c
merge-recursive.h
merge.c
mergesort.c
mergesort.h
name-hash.c
notes-cache.c
notes-cache.h
notes-merge.c
notes-merge.h
notes-utils.c
notes-utils.h
notes.c
notes.h
object.c
object.h
pack-bitmap-write.c
pack-bitmap.c
pack-bitmap.h
pack-check.c
pack-objects.c
pack-objects.h
pack-revindex.c
pack-revindex.h
pack-write.c
pack.h
pager.c
parse-options-cb.c
parse-options.c
parse-options.h
patch-delta.c
patch-ids.c
patch-ids.h
path.c
pathspec.c
pathspec.h
pkt-line.c
pkt-line.h
preload-index.c
pretty.c
prio-queue.c
prio-queue.h
progress.c
progress.h
prompt.c
prompt.h
quote.c
quote.h
reachable.c
reachable.h
read-cache.c
reflog-walk.c
reflog-walk.h
refs.c
refs.h
remote-curl.c
remote-testsvn.c
remote.c
remote.h
replace_object.c
rerere.c
rerere.h
resolve-undo.c
resolve-undo.h
revision.c
revision.h
run-command.c
run-command.h
send-pack.c
send-pack.h
sequencer.c
sequencer.h
server-info.c
setup.c
sh-i18n--envsubst.c
sha1-array.c
sha1-array.h
sha1-lookup.c
sha1-lookup.h
sha1_file.c
sha1_name.c
shallow.c
shell.c
shortlog.h
show-index.c
sideband.c
sideband.h
sigchain.c
sigchain.h
strbuf.c
strbuf.h
streaming.c
streaming.h
string-list.c
string-list.h
submodule.c
submodule.h
symlinks.c
tag.c
tag.h
tar.h
test-chmtime.c
test-ctype.c
test-date.c
test-delta.c
test-dump-cache-tree.c
test-genrandom.c
test-hashmap.c
test-index-version.c
test-line-buffer.c
test-match-trees.c
test-mergesort.c
test-mktemp.c
test-parse-options.c
test-path-utils.c
test-prio-queue.c
test-read-cache.c
test-regex.c
test-revision-walking.c
test-run-command.c
test-scrap-cache-tree.c
test-sha1.c
test-sha1.sh
test-sigchain.c
test-string-list.c
test-subprocess.c
test-svn-fe.c
test-urlmatch-normalization.c
test-wildmatch.c
thread-utils.c
thread-utils.h
trace.c
transport-helper.c
transport.c
transport.h
tree-diff.c
tree-walk.c
tree-walk.h
tree.c
tree.h
unimplemented.sh
unix-socket.c
unix-socket.h
unpack-trees.c
unpack-trees.h
upload-pack.c
url.c
url.h
urlmatch.c
urlmatch.h
usage.c
userdiff.c
userdiff.h
utf8.c
utf8.h
varint.c
varint.h
version.c
version.h
versioncmp.c
walker.c
walker.h
wildmatch.c
wildmatch.h
wrap-for-bin.sh
wrapper.c
write_or_die.c
ws.c
wt-status.c
wt-status.h
xdiff-interface.c
xdiff-interface.h
zlib.c
Breadcrumbs
git
/
http-backend.c
Blame
Blame
Latest commit
History
History
630 lines (523 loc) · 13.9 KB
Breadcrumbs
git
/
http-backend.c
Top
File metadata and controls
Code
Blame
630 lines (523 loc) · 13.9 KB
Raw
#include "cache.h" #include "refs.h" #include "pkt-line.h" #include "object.h" #include "tag.h" #include "exec_cmd.h" #include "run-command.h" #include "string-list.h" #include "url.h" #include "argv-array.h" 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; struct rpc_service { const char *name; const char *config_name; signed enabled : 2; }; static struct rpc_service rpc_service[] = { { "upload-pack", "uploadpack", 1 }, { "receive-pack", "receivepack", -1 }, }; static struct string_list *get_parameters(void) { if (!query_params) { const char *query = getenv("QUERY_STRING"); query_params = xcalloc(1, sizeof(*query_params)); while (query && *query) { char *name = url_decode_parameter_name(&query); char *value = url_decode_parameter_value(&query); struct string_list_item *i; i = string_list_lookup(query_params, name); if (!i) i = string_list_insert(query_params, name); else free(i->util); i->util = value; } } return query_params; } static const char *get_parameter(const char *name) { struct string_list_item *i; i = string_list_lookup(get_parameters(), name); return i ? i->util : NULL; } __attribute__((format (printf, 2, 3))) static void format_write(int fd, const char *fmt, ...) { static char buffer[1024]; va_list args; unsigned n; va_start(args, fmt); n = vsnprintf(buffer, sizeof(buffer), fmt, args); va_end(args); if (n >= sizeof(buffer)) die("protocol error: impossibly long line"); write_or_die(fd, buffer, n); } static void http_status(unsigned code, const char *msg) { format_write(1, "Status: %u %s\r\n", code, msg); } 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, uintmax_t value) { format_write(1, "%s: %" PRIuMAX "\r\n", name, value); } static void hdr_date(const char *name, unsigned long when) { const char *value = show_date(when, 0, DATE_RFC2822); hdr_str(name, value); } static void hdr_nocache(void) { hdr_str("Expires", "Fri, 01 Jan 1980 00:00:00 GMT"); hdr_str("Pragma", "no-cache"); hdr_str("Cache-Control", "no-cache, max-age=0, must-revalidate"); } static void hdr_cache_forever(void) { unsigned long now = time(NULL); hdr_date("Date", now); hdr_date("Expires", now + 31536000); hdr_str("Cache-Control", "public, max-age=31536000"); } static void end_headers(void) { write_or_die(1, "\r\n", 2); } __attribute__((format (printf, 1, 2))) static NORETURN void not_found(const char *err, ...) { va_list params; http_status(404, "Not Found"); hdr_nocache(); end_headers(); va_start(params, err); if (err && *err) vfprintf(stderr, err, params); va_end(params); exit(0); } __attribute__((format (printf, 1, 2))) static NORETURN void forbidden(const char *err, ...) { va_list params; http_status(403, "Forbidden"); hdr_nocache(); end_headers(); va_start(params, err); if (err && *err) vfprintf(stderr, err, params); va_end(params); 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); hdr_str(content_type, type); end_headers(); write_or_die(1, buf->buf, buf->len); } 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; fd = open(p, O_RDONLY); if (fd < 0) not_found("Cannot open '%s': %s", p, strerror(errno)); if (fstat(fd, &sb) < 0) die_errno("Cannot stat '%s'", p); hdr_int(content_length, sb.st_size); hdr_str(content_type, the_type); hdr_date(last_modified, sb.st_mtime); end_headers(); for (;;) { ssize_t n = xread(fd, buf, buf_alloc); if (n < 0) die_errno("Cannot read '%s'", p); if (!n) break; write_or_die(1, buf, n); } close(fd); free(buf); } static void get_text_file(char *name) { select_getanyfile(); hdr_nocache(); send_local_file("text/plain", name); } static void get_loose_object(char *name) { select_getanyfile(); hdr_cache_forever(); send_local_file("application/x-git-loose-object", name); } static void get_pack_file(char *name) { select_getanyfile(); hdr_cache_forever(); send_local_file("application/x-git-packed-objects", name); } static void get_idx_file(char *name) { select_getanyfile(); hdr_cache_forever(); send_local_file("application/x-git-packed-objects-toc", name); } static int http_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "http.getanyfile")) { getanyfile = git_config_bool(var, value); return 0; } if (starts_with(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; } static struct rpc_service *select_service(const char *name) { struct rpc_service *svc = NULL; int i; if (!starts_with(name, "git-")) forbidden("Unsupported service: '%s'", name); for (i = 0; i < ARRAY_SIZE(rpc_service); i++) { struct rpc_service *s = &rpc_service[i]; if (!strcmp(s->name, name + 4)) { svc = s; break; } } if (!svc) forbidden("Unsupported service: '%s'", name); if (svc->enabled < 0) { const char *user = getenv("REMOTE_USER"); svc->enabled = (user && *user) ? 1 : 0; } if (!svc->enabled) forbidden("Service not enabled: '%s'", svc->name); return svc; } static void inflate_request(const char *prog_name, int out) { git_zstream stream; unsigned char in_buf[8192]; unsigned char out_buf[8192]; unsigned long cnt = 0; memset(&stream, 0, sizeof(stream)); git_inflate_init_gzip_only(&stream); while (1) { ssize_t n = xread(0, in_buf, sizeof(in_buf)); if (n <= 0) die("request ended in the middle of the gzip stream"); stream.next_in = in_buf; stream.avail_in = n; while (0 < stream.avail_in) { int ret; stream.next_out = out_buf; stream.avail_out = sizeof(out_buf); ret = git_inflate(&stream, Z_NO_FLUSH); if (ret != Z_OK && ret != Z_STREAM_END) die("zlib error inflating request, result %d", ret); n = stream.total_out - cnt; if (write_in_full(out, out_buf, n) != n) die("%s aborted reading request", prog_name); cnt += n; if (ret == Z_STREAM_END) goto done; } } done: git_inflate_end(&stream); close(out); } static void run_service(const char **argv) { const char *encoding = getenv("HTTP_CONTENT_ENCODING"); const char *user = getenv("REMOTE_USER"); const char *host = getenv("REMOTE_ADDR"); struct argv_array env = ARGV_ARRAY_INIT; int gzipped_request = 0; struct child_process cld; if (encoding && !strcmp(encoding, "gzip")) gzipped_request = 1; else if (encoding && !strcmp(encoding, "x-gzip")) gzipped_request = 1; if (!user || !*user) user = "anonymous"; if (!host || !*host) host = "(none)"; if (!getenv("GIT_COMMITTER_NAME")) argv_array_pushf(&env, "GIT_COMMITTER_NAME=%s", user); if (!getenv("GIT_COMMITTER_EMAIL")) argv_array_pushf(&env, "GIT_COMMITTER_EMAIL=%s@http.%s", user, host); memset(&cld, 0, sizeof(cld)); cld.argv = argv; cld.env = env.argv; if (gzipped_request) cld.in = -1; cld.git_cmd = 1; if (start_command(&cld)) exit(1); close(1); if (gzipped_request) inflate_request(argv[0], cld.in); else close(0); if (finish_command(&cld)) exit(1); argv_array_clear(&env); } static int show_text_ref(const char *name, const unsigned char *sha1, int flag, void *cb_data) { const char *name_nons = strip_namespace(name); struct strbuf *buf = cb_data; struct object *o = parse_object(sha1); if (!o) return 0; strbuf_addf(buf, "%s\t%s\n", sha1_to_hex(sha1), name_nons); if (o->type == OBJ_TAG) { o = deref_tag(o, name, 0); if (!o) return 0; strbuf_addf(buf, "%s\t%s^{}\n", sha1_to_hex(o->sha1), name_nons); } return 0; } static void get_info_refs(char *arg) { const char *service_name = get_parameter("service"); struct strbuf buf = STRBUF_INIT; hdr_nocache(); if (service_name) { const char *argv[] = {NULL /* service name */, "--stateless-rpc", "--advertise-refs", ".", NULL}; struct rpc_service *svc = select_service(service_name); strbuf_addf(&buf, "application/x-git-%s-advertisement", svc->name); hdr_str(content_type, buf.buf); end_headers(); packet_write(1, "# service=git-%s\n", svc->name); packet_flush(1); argv[0] = svc->name; run_service(argv); } else { select_getanyfile(); for_each_namespaced_ref(show_text_ref, &buf); send_strbuf("text/plain", &buf); } strbuf_release(&buf); } static int show_head_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { struct strbuf *buf = cb_data; if (flag & REF_ISSYMREF) { unsigned char unused[20]; const char *target = resolve_ref_unsafe(refname, unused, 1, NULL); const char *target_nons = strip_namespace(target); strbuf_addf(buf, "ref: %s\n", target_nons); } else { strbuf_addf(buf, "%s\n", sha1_to_hex(sha1)); } return 0; } static void get_head(char *arg) { struct strbuf buf = STRBUF_INIT; select_getanyfile(); head_ref_namespaced(show_head_ref, &buf); send_strbuf("text/plain", &buf); strbuf_release(&buf); } static void get_info_packs(char *arg) { size_t objdirlen = strlen(get_object_directory()); struct strbuf buf = STRBUF_INIT; 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) cnt++; } strbuf_grow(&buf, cnt * 53 + 2); for (p = packed_git; p; p = p->next) { if (p->pack_local) strbuf_addf(&buf, "P %s\n", p->pack_name + objdirlen + 6); } strbuf_addch(&buf, '\n'); hdr_nocache(); send_strbuf("text/plain; charset=utf-8", &buf); strbuf_release(&buf); } static void check_content_type(const char *accepted_type) { const char *actual_type = getenv("CONTENT_TYPE"); if (!actual_type) actual_type = ""; if (strcmp(actual_type, accepted_type)) { http_status(415, "Unsupported Media Type"); hdr_nocache(); end_headers(); format_write(1, "Expected POST with Content-Type '%s'," " but received '%s' instead.\n", accepted_type, actual_type); exit(0); } } static void service_rpc(char *service_name) { const char *argv[] = {NULL, "--stateless-rpc", ".", NULL}; struct rpc_service *svc = select_service(service_name); struct strbuf buf = STRBUF_INIT; strbuf_reset(&buf); strbuf_addf(&buf, "application/x-git-%s-request", svc->name); check_content_type(buf.buf); hdr_nocache(); strbuf_reset(&buf); strbuf_addf(&buf, "application/x-git-%s-result", svc->name); hdr_str(content_type, buf.buf); end_headers(); argv[0] = svc->name; run_service(argv); strbuf_release(&buf); } static NORETURN void die_webcgi(const char *err, va_list params) { static int dead; if (!dead) { dead = 1; http_status(500, "Internal Server Error"); hdr_nocache(); end_headers(); vreportf("fatal: ", err, params); } exit(0); /* we successfully reported a failure ;-) */ } 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); end_url_with_slash(&buf, root); 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; void (*imp)(char *); } services[] = { {"GET", "/HEAD$", get_head}, {"GET", "/info/refs$", get_info_refs}, {"GET", "/objects/info/alternates$", get_text_file}, {"GET", "/objects/info/http-alternates$", get_text_file}, {"GET", "/objects/info/packs$", get_info_packs}, {"GET", "/objects/[0-9a-f]{2}/[0-9a-f]{38}$", get_loose_object}, {"GET", "/objects/pack/pack-[0-9a-f]{40}\\.pack$", get_pack_file}, {"GET", "/objects/pack/pack-[0-9a-f]{40}\\.idx$", get_idx_file}, {"POST", "/git-upload-pack$", service_rpc}, {"POST", "/git-receive-pack$", service_rpc} }; int main(int argc, char **argv) { char *method = getenv("REQUEST_METHOD"); char *dir; struct service_cmd *cmd = NULL; char *cmd_arg = NULL; int i; git_setup_gettext(); git_extract_argv0_path(argv[0]); set_die_routine(die_webcgi); if (!method) die("No REQUEST_METHOD from server"); if (!strcmp(method, "HEAD")) method = "GET"; dir = getdir(); for (i = 0; i < ARRAY_SIZE(services); i++) { struct service_cmd *c = &services[i]; regex_t re; regmatch_t out[1]; 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; if (strcmp(method, c->method)) { const char *proto = getenv("SERVER_PROTOCOL"); if (proto && !strcmp(proto, "HTTP/1.1")) { http_status(405, "Method Not Allowed"); hdr_str("Allow", !strcmp(c->method, "GET") ? "GET, HEAD" : c->method); } else http_status(400, "Bad Request"); hdr_nocache(); end_headers(); return 0; } cmd = c; n = out[0].rm_eo - out[0].rm_so; cmd_arg = xmemdupz(dir + out[0].rm_so + 1, n - 1); dir[out[0].rm_so] = 0; break; } regfree(&re); } if (!cmd) not_found("Request not supported: '%s'", dir); setup_path(); if (!enter_repo(dir, 0)) not_found("Not a git repository: '%s'", dir); if (!getenv("GIT_HTTP_EXPORT_ALL") && access("git-daemon-export-ok", F_OK) ) not_found("Repository not exported: '%s'", dir); git_config(http_config, NULL); cmd->imp(cmd_arg); return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
You can’t perform that action at this time.