@@ -17,6 +17,8 @@ public function willBeginExecution() {
17
17
if (preg_match ($ regex , (string )$ uri , $ matches )) {
18
18
$ vcs = null ;
19
19
20
+ $ content_type = $ request ->getHTTPHeader ('Content-Type ' );
21
+
20
22
if ($ request ->getExists ('__vcs__ ' )) {
21
23
// This is magic to make it easier for us to debug stuff by telling
22
24
// users to run:
@@ -26,8 +28,13 @@ public function willBeginExecution() {
26
28
// ...to get a human-readable error.
27
29
$ vcs = $ request ->getExists ('__vcs__ ' );
28
30
} else if ($ request ->getExists ('service ' )) {
31
+ $ service = $ request ->getStr ('service ' );
32
+ // We get this initially for `info/refs`.
29
33
// Git also gives us a User-Agent like "git/1.8.2.3".
30
34
$ vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT ;
35
+ } else if ($ content_type == 'application/x-git-upload-pack-request ' ) {
36
+ // We get this for `git-upload-pack`.
37
+ $ vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT ;
31
38
} else if ($ request ->getExists ('cmd ' )) {
32
39
// Mercurial also sends an Accept header like
33
40
// "application/mercurial-0.1", and a User-Agent like
@@ -125,6 +132,13 @@ private function processVCSRequest($callsign) {
125
132
pht ('This repository is not available over HTTP. ' ));
126
133
}
127
134
135
+ switch ($ repository ->getVersionControlSystem ()) {
136
+ case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT :
137
+ return $ this ->serveGitRequest ($ repository );
138
+ default :
139
+ break ;
140
+ }
141
+
128
142
return new PhabricatorVCSResponse (
129
143
999 ,
130
144
pht ('TODO: Implement meaningful responses. ' ));
@@ -133,22 +147,28 @@ private function processVCSRequest($callsign) {
133
147
private function isReadOnlyRequest (
134
148
PhabricatorRepository $ repository ) {
135
149
$ request = $ this ->getRequest ();
150
+ $ method = $ _SERVER ['REQUEST_METHOD ' ];
136
151
137
152
// TODO: This implementation is safe by default, but very incomplete.
138
153
139
154
switch ($ repository ->getVersionControlSystem ()) {
140
155
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT :
141
156
$ service = $ request ->getStr ('service ' );
157
+ $ path = $ this ->getRequestDirectoryPath ();
142
158
// NOTE: Service names are the reverse of what you might expect, as they
143
159
// are from the point of view of the server. The main read service is
144
160
// "git-upload-pack", and the main write service is "git-receive-pack".
145
- switch ($ service ) {
146
- case 'git-upload-pack ' :
147
- return true ;
148
- case 'git-receive-pack ' :
149
- default :
150
- return false ;
161
+
162
+ if ($ method == 'GET ' &&
163
+ $ path == '/info/refs ' &&
164
+ $ service == 'git-upload-pack ' ) {
165
+ return true ;
166
+ }
167
+
168
+ if ($ path == '/git-upload-pack ' ) {
169
+ return true ;
151
170
}
171
+
152
172
break ;
153
173
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL :
154
174
$ cmd = $ request ->getStr ('cmd ' );
@@ -375,5 +395,52 @@ protected function renderPathLinks(DiffusionRequest $drequest, $action) {
375
395
return $ links ;
376
396
}
377
397
398
+ /**
399
+ * @phutil-external-symbol class PhabricatorStartup
400
+ */
401
+ private function serveGitRequest (PhabricatorRepository $ repository ) {
402
+ $ request = $ this ->getRequest ();
403
+
404
+ $ request_path = $ this ->getRequestDirectoryPath ();
405
+ $ repository_root = $ repository ->getLocalPath ();
406
+
407
+ // Rebuild the query string to strip `__magic__` parameters and prevent
408
+ // issues where we might interpret inputs like "service=read&service=write"
409
+ // differently than the server does and pass it an unsafe command.
410
+ $ query_data = $ request ->getPassthroughRequestParameters ();
411
+ $ query_string = http_build_query ($ query_data , '' , '& ' );
412
+
413
+ // We're about to wipe out PATH with the rest of the environment, so
414
+ // resolve the binary first.
415
+ $ bin = Filesystem::resolveBinary ('git-http-backend ' );
416
+ if (!$ bin ) {
417
+ throw new Exception ("Unable to find `git-http-backend` in PATH! " );
418
+ }
419
+
420
+ $ env = array (
421
+ 'REQUEST_METHOD ' => $ _SERVER ['REQUEST_METHOD ' ],
422
+ 'QUERY_STRING ' => $ query_string ,
423
+ 'CONTENT_TYPE ' => $ _SERVER ['CONTENT_TYPE ' ],
424
+ 'REMOTE_USER ' => '' ,
425
+ 'REMOTE_ADDR ' => $ _SERVER ['REMOTE_ADDR ' ],
426
+ 'GIT_PROJECT_ROOT ' => $ repository_root ,
427
+ 'GIT_HTTP_EXPORT_ALL ' => '1 ' ,
428
+ 'PATH_INFO ' => $ request_path ,
429
+ );
430
+
431
+ list ($ stdout ) = id (new ExecFuture ('%s ' , $ bin ))
432
+ ->setEnv ($ env , true )
433
+ ->write (PhabricatorStartup::getRawInput ())
434
+ ->resolvex ();
435
+
436
+ return id (new DiffusionGitResponse ())->setGitData ($ stdout );
437
+ }
438
+
439
+ private function getRequestDirectoryPath () {
440
+ $ request = $ this ->getRequest ();
441
+ $ request_path = $ request ->getRequestURI ()->getPath ();
442
+ return preg_replace ('@^/diffusion/[A-Z]+@ ' , '' , $ request_path );
443
+ }
444
+
378
445
}
379
446
0 commit comments