X-Git-Url: http://git.pjr.cc/?p=gwvp-mini.git;a=blobdiff_plain;f=gwvpmini%2Fgwvpmini_gitbackend.php;h=5fe188ab3bd5ef3de2bc58404cf09039e9c12487;hp=17b578808bd313b7f28430575a41987c3e153a11;hb=1801e714ec1393dfe1a8a35aa22429eb8483ef4a;hpb=4d6a42b605b8c52c5621fb704b284e4c12672755 diff --git a/gwvpmini/gwvpmini_gitbackend.php b/gwvpmini/gwvpmini_gitbackend.php index 17b5788..5fe188a 100644 --- a/gwvpmini/gwvpmini_gitbackend.php +++ b/gwvpmini/gwvpmini_gitbackend.php @@ -26,10 +26,49 @@ function gwvpmini_gitControlCallMe() } +function gwvpmini_CreateRepoHooks($repopath, $cmdpath, $reponame) +{ + $fp = fopen("$repopath/hooks/pre-receive", "w"); + + if(!$fp) error_log("could not create pre-receive hook"); + + // TODO: think about this one + //$script = '#!/bin/bash'."\n\n".'DCOMMIT=`cat`'."\n".'START=`echo $DCOMMIT|cut -d " " -f 1`'."\n".'END=`echo $DCOMMIT|cut -d " " -f 2`'."\n".'REF=`echo $DCOMMIT|cut -d " " -f 3`'."\n\n"; + $script = "#!/bin/bash\n\nDCOMMIT=".'`cat`'."\n\nphp $cmdpath $reponame \$REMOTE_USER pre-receive \$DCOMMIT\n\n"; + fwrite($fp, $script); + + fclose($fp); + + chmod("$repopath/hooks/pre-receive", 0755); + + + $fp = fopen("$repopath/hooks/update", "w"); + + if(!$fp) error_log("could not create update hook"); + + // TODO: think about this one + unset($script); + $script = "#!/bin/bash\n\nphp $cmdpath $reponame \$REMOTE_USER update \$1 \$2 \$3\n\n"; + fwrite($fp, $script); + + fclose($fp); + + chmod("$repopath/hooks/update", 0755); +} + function gwvpmini_gitBackendInterface() { // and this is where i re-code the git backend interface from scratch - global $BASE_URL; + global $BASE_URL, $cmd_line_tool; + + header_remove("Pragma"); + header_remove("Cache-Control"); + header_remove("Set-Cookie"); + header_remove("Expires"); + header_remove("X-Powered-By"); + header_remove("Vary"); + //header("Content-Encoding: none"); + $repo_base = gwvpmini_getConfigVal("repodir"); @@ -37,16 +76,16 @@ function gwvpmini_gitBackendInterface() /* bizare git problem that ignores 403's or continues on with a push despite them - error_log("FLAP for ".$_SERVER["REQUEST_URI"]); + // error_log("FLAP for ".$_SERVER["REQUEST_URI"]); if(isset($_REQUEST)) { $dump = print_r($_REQUEST, true); - error_log("FLAP, $dump"); + // error_log("FLAP, $dump"); } if(isset($_SERVER["PHP_AUTH_USER"])) { - error_log("FLAP: donut hole"); + // error_log("FLAP: donut hole"); }*/ - + error_log("REQUESTINBACKEND: ".print_r($_REQUEST, true)); $repo = ""; $repoid = false; @@ -69,195 +108,122 @@ function gwvpmini_gitBackendInterface() // we do an update server cause its weird and i cant figure out when it actually needs to happen chdir("$repo_base/$repo.git"); - exec("/usr/bin/git update-server-info"); + // dont believe i have to do this + //exec("/usr/bin/git update-server-info"); + + if(!file_exists("$repo_base/$repo.git/hooks/pre-receive") || !file_exists("$repo_base/$repo.git/hooks/update")) { + // error_log("WRITING HOOKS"); + gwvpmini_CreateRepoHooks("$repo_base/$repo.git", $cmd_line_tool, $repo); + } // so now we have the repo // next we determine if this is a read or a write + + // TODO: WE NEED TO FIX THIS, IT DOESNT ALWAYS DETECT a "WRITE" $write = false; if(isset($_REQUEST["service"])) { if($_REQUEST["service"] == "git-receive-pack") { - error_log("got write as receivepack in post"); + // error_log("got write as receivepack in post"); $write = true; } } - if($_SERVER["REQUEST_METHOD"] == "POST") { - $write = true; - } + if(preg_match("/.*git-receive-pack$/", $_REQUEST["q"])) $write = true; + //$write = true; // THIS MAY CAUSE ISSUES LATER ON but we do it cause the git client ignores our 403 when it uses git-receive-pack after an auth // no, this isnt a solution cause auth'd read attempts will come up as writes... //if(isset($_SERVER["PHP_AUTH_USER"])) { //$write = true; //} - $perms = 5; - // if its a write, we push for authentication - if($write) { - error_log("is write attempt, ask for login"); - $person = gwvpmini_checkBasicAuthLogin(); - if($person == false) { - gwvpmini_AskForBasicAuth(); - return; - } else { - error_log("checking perms for $person against $repoid for repo $repo"); - // here we pass to the git backend - error_log("perms are $perms and im allowed"); - gwvpmini_callGitBackend($person["username"], $repo); - } - return; - } + $person = gwvpmini_checkBasicAuthLogin(); + //$write = true; + // next, figure out permissions for repo + $rid = gwvpmini_GetRepoId($repo); + $uid = -1; + // error_log("AT THIS POINT WE HAVE $uid, $rid, $repo $person"); - - // if they're less then read, we need to then check the user auth permissions - if($perms < 2) { - // we ask for auth - $person = gwvpmini_checkBasicAuthLogin(); - if($person == false) { + if(!$person) { + if($write) { + // error_log("ASK FOR BASIC AUTH"); gwvpmini_AskForBasicAuth(); return; } else { - } - } - - // if we made it this far, we a read and we have permissions to do so, just search the file from the repo - if(file_exists("$repo_base/$repo.git/$newloc")) { - error_log("would ask $repo for $repo.git/$newloc from $repo_base/$repo.git/$newloc"); - $fh = fopen("$repo_base/$repo.git/$newloc", "rb"); - - error_log("pushing file"); - while(!feof($fh)) { - echo fread($fh, 8192); + $perm = gwvpmini_GetRepoPerm($rid, "a"); + if($perm < 1) { + // error_log("ASK FOR BASIC AUTH 2"); + gwvpmini_AskForBasicAuth(); + return; + } } } else { - //echo "would ask $repo,$actual_repo_name for $repo/$newloc from $repo_base/$repo/$newloc, NE"; - gwvpmini_fourZeroFour(); - return; - } - -} - - -function gwvpmini_gitBackendInterface_old() -{ - global $BASE_URL; - - $repo_base = gwvpmini_getConfigVal("repodir"); - - $repo = ""; - $newloc = "/"; - if(isset($_REQUEST["q"])) { - $query = $_REQUEST["q"]; - $qspl = explode("/", $query); - $repo = $qspl[1]; - for($i=2; $i < count($qspl); $i++) { - $newloc .= "/".$qspl[$i]; - } - } - - $actual_repo_name = preg_replace("/\.git$/", "", $repo); - - $user = gwvpmini_checkBasicAuthLogin(); - - if(!$user) { - error_log("User is set to false, so its anonymouse"); - } else { - error_log("user is $user"); - } - - // must remember that $user of false is anonymous when we code gwvpmini_repoPerm'sCheck() - if(!gwvpmini_repoPermissionCheck($actual_repo_name, $user)) { - error_log("perms check fails - start auth"); - if(isset($_SERVER["PHP_AUTH_USER"])) { - error_log("have auth - push 403"); - gwvpmini_fourZeroThree(); + $uid = gwvpmini_GetUserId($person); + $perm = gwvpmini_GetRepoPerm($rid, $uid); + if($write) { + if($perm < 2) { + // error_log("SEND FOFF"); + gwvpmini_fourZeroThree(); + return; + } } else { - error_log("push auth"); - gwvpmini_AskForBasicAuth(); - return; - } - } - - // we need to quite a bit of parsing in here. The "repo" will always be /git/repo.git - // but if we get here from a browser, we need to forward back to a normal repo viewer - // the only way i can think of doing this is to check the useragent for the word "git" - - /* - * here we need to - * 1) figure out the repo its acessing - * 2) figure out the perms on the repo - * 3) determine if its a pull or a push - * - if its a pull, we just serve straight from the fs - * - if its a push, we go thru git-http-backend - * 4) if it requiers auth, we push to auth - * - */ - $agent = "git-unknown"; - $isgitagent = false; - - // tested the user agent bit with jgit from eclipse and normal git... seems to work - if(isset($_SERVER["HTTP_USER_AGENT"])) { - $agent = $_SERVER["HTTP_USER_AGENT"]; - error_log("in git backend with user agent $agent"); - if(stristr($agent, "git")!==false) { - $isgitagent = true; + if($perm < 1) { + gwvpmini_fourZeroThree(); + return; + } } } + // if its a write, we push for authentication - - /* dont need this code right now - if($isgitagent) echo "GIT: i am a git backened interface for a repo $repo, agent $agent"; - else echo "NOT GIT: i am a git backened interface for a repo $repo, agent $agent"; - */ - - // now we need to rebuild the actual request or do we? - //$basegit = "$BASE_URL/git/something.git"; - //$newloc = preg_replace("/^$basegit/", "", $_SERVER["REQUEST_URI"]); - chdir("$repo_base/$repo"); - exec("/usr/bin/git update-server-info"); - - if($_SERVER["REQUEST_METHOD"] == "POST") { - gwvpmini_AskForBasicAuth(); - gwvpmini_callGitBackend($repo); - return; + //if($write) { + if(!$person) { + $person = "anonymous"; } - if(isset($_REQUEST["service"])) { - if($_REQUEST["service"] == "git-receive-pack") { - // we are a write call - we need auth and we're going to the backend proper - gwvpmini_AskForBasicAuth(); - gwvpmini_callGitBackend($repo); - return; - } + // if its a write, we check (before and after) the branch/tag info to see if they were updated + if($write) { + error_log("REQUESTINBACKEND: processed as write"); + } else { + error_log("REQUESTINBACKEND: processed as read"); } + gwvpmini_callGitBackend($person, $repo); - if(file_exists("$repo_base/$repo/$newloc")) { - error_log("would ask $repo,$actual_repo_name for $repo/$newloc from $repo_base/$repo/$newloc"); - $fh = fopen("$repo_base/$repo/$newloc", "rb"); + //if($write) { + //} + return; + //} + + // if we made it this far, we a read and we have permissions to do so, just search the file from the repo + /*if(file_exists("$repo_base/$repo.git/$newloc")) { + // error_log("would ask $repo for $repo.git/$newloc from $repo_base/$repo.git/$newloc"); + $fh = fopen("$repo_base/$repo.git/$newloc", "rb"); - error_log("pushing file"); + // error_log("pushing file"); while(!feof($fh)) { echo fread($fh, 8192); } } else { - echo "would ask $repo,$actual_repo_name for $repo/$newloc from $repo_base/$repo/$newloc, NE"; - header('HTTP/1.0 404 No Such Thing'); + // error_log("would ask $repo for $repo/$newloc from $repo_base/$repo/$newloc, NE"); + gwvpmini_fourZeroFour(); return; - } + }*/ + } function gwvpmini_canManageRepo($userid, $repoid) { // only the owner or an admin can do these tasks - error_log("Checking repoid, $repoid against userid $userid"); + // error_log("Checking repoid, $repoid against userid $userid"); if(gwvpmini_IsUserAdmin(null, null, $userid)) return true; if(gwvpmini_IsRepoOwner($userid, $repoid)) return true; return false; } + +// TODO: this whole bit needs a re-write - seriously, like totally! function gwvpmini_callGitBackend($username, $repo) { // this is where things become a nightmare @@ -278,7 +244,7 @@ function gwvpmini_callGitBackend($username, $repo) $qs = ""; foreach($_REQUEST as $key => $var) { if($key != "q") { - //error_log("adding, $var from $key"); + //// error_log("adding, $var from $key"); if($qs == "") $qs.="$key=$var"; else $qs.="&$key=$var"; } @@ -286,7 +252,7 @@ function gwvpmini_callGitBackend($username, $repo) //sleep(2); - + $userdets = gwvpmini_getUser($username); // this is where the fun, it ends. $myoutput = ""; @@ -299,6 +265,8 @@ function gwvpmini_callGitBackend($username, $repo) $procenv["GATEWAY_INTERFACE"] = "CGI/1.1"; $procenv["PATH_TRANSLATED"] = "/$repo_base/$repo.git/$euri"; $procenv["REQUEST_METHOD"] = "$rmeth"; + $procenv["GIT_COMMITTER_NAME"] = $userdets["fullname"]; + $procenv["GIT_COMMITTER_EMAIL"] = $userdets["email"]; $procenv["GIT_HTTP_EXPORT_ALL"] = "1"; $procenv["QUERY_STRING"] = "$qs"; $procenv["HTTP_USER_AGENT"] = "git/1.7.1"; @@ -306,6 +274,8 @@ function gwvpmini_callGitBackend($username, $repo) $procenv["REMOTE_ADDR"] = $_SERVER["REMOTE_ADDR"]; $procenv["AUTH_TYPE"] = "Basic"; + //// error_log("PROCENV: ".print_r($procenv,true)); + if(isset($_SERVER["CONTENT_TYPE"])) { $procenv["CONTENT_TYPE"] = $_SERVER["CONTENT_TYPE"]; } else { @@ -314,12 +284,11 @@ function gwvpmini_callGitBackend($username, $repo) if(isset($_SERVER["CONTENT_LENGTH"])) { $procenv["CONTENT_LENGTH"] = $_SERVER["CONTENT_LENGTH"]; } + + $headers = getallheaders(); - error_log("path trans'd is /$repo_base/$repo.git/$euri from $ruri with ".$_REQUEST["q"]." $strrem"); - - + //error_log("path trans'd is /$repo_base/$repo.git/$euri from $ruri with ".$_REQUEST["q"]." $strrem pre is ".print_r($_REQUEST,true)." and foff ".print_r($headers, true)); - $pwd = "/$repo_base/"; $proc = proc_open("/usr/lib/git-core/git-http-backend", array(array("pipe","rb"),array("pipe","wb"),array("file","/tmp/err", "a")), $pipes, $pwd, $procenv); @@ -328,13 +297,13 @@ function gwvpmini_callGitBackend($username, $repo) while(!$untilblank&&!feof($pipes[1])) { $lines_t = fgets($pipes[1]); $lines = trim($lines_t); - error_log("got line: $lines"); + // error_log("got line: $lines"); if($lines_t == "\r\n") { $untilblank = true; - error_log("now blank"); + // error_log("now blank"); } else header($lines); if($lines === false) { - error_log("got an unexpexted exit..."); + // error_log("got an unexpexted exit..."); exit(0); } @@ -345,27 +314,57 @@ function gwvpmini_callGitBackend($username, $repo) $continue = true; if(!stream_set_blocking($fh,0)) { - error_log("cant set input non-blocking"); + // error_log("cant set input non-blocking"); } if(!stream_set_blocking($pipes[1],0)) { - error_log("cant set pipe1 non-blocking"); + // error_log("cant set pipe1 non-blocking"); } + + $stlimit = 0; + //$fp = fopen("/tmp/gitup.".rand(0,4000000), "w"); + $from_client_data = ""; // i was going to use stream_select, but i feel this works better like this while($continue) { // do client + + // lets start again.... + if(!feof($fh)) { - $from_client_data = fread($fh,8192); - if($from_client_data !== false) fwrite($pipes[0], $from_client_data); + if(isset($headers["Content-Length"])) { + $cl = $headers["Content-Length"]; + while(strlen($from_client_data) < $cl) { + error_log("re-read ".strlen($from_client_data)); + $from_client_data .= fread($fh, $cl); + } + } else $from_client_data = fread($fh,8192); + + // TODO: BIG TODO: must rewrite this bit. its ugly as sin, it works, but it probably shouldnt. + + if(isset($headers["Content-Encoding"])) { + if($headers["Content-Encoding"] == "gzip") { + //error_log("DAM YOU GIT CLIENT and your retarded gzip encoding"); + $from_client_data = gzinflate(substr($from_client_data, 10)); + //$from_client_data = gzdecode($from_client_data); + } + } + if($from_client_data !== false) { + fwrite($pipes[0], $from_client_data); + //fwrite($fp, $from_client_data); + } fflush($pipes[0]); //fwrite($fl, $from_client_data); $client_len = strlen($from_client_data); } else { - error_log("client end"); + //error_log("client end"); $client_len = 0; + //$continue = false; } + + + // do cgi // sometimes, we get a \r\n from the cgi, i do not know why she swallowed the fly, // but i do know that the fgets for the headers above should have comsued that @@ -382,45 +381,57 @@ function gwvpmini_callGitBackend($username, $repo) // or something the git-http-backend thing is doing.. // TODO: find out why this happens $from_cgi_data = preg_replace("/^\r\n/", "", $from_cgi_data_t); + /* if(strlen($from_cgi_data)!=strlen($from_cgi_data_t)) { error_log("MOOOKS - we did trunc"); } else { error_log("MOOOKS - we did not trunc"); - } + }*/ $firstline = false; } } - if($from_cgi_data !== false) { + if($from_cgi_data !== false && $from_cgi_data != "") { + //error_log("send to client of size ".strlen($from_cgi_data)); echo $from_cgi_data; + ob_flush(); flush(); } $cgi_len = strlen($from_cgi_data); } else { - error_log("cgi end"); + // error_log("cgi end"); $cgi_len = 0; } + flush(); + if(feof($pipes[1])) $continue = false; else { if($client_len == 0 && $cgi_len == 0) { usleep(200000); - error_log("sleep tick"); + //error_log("sleep tick"); + $stlimit++; + if($stlimit > 300) $continue = false; // if we get no output from git backend after 1 minute, we close..... something went wrong } else { - error_log("sizes: $client_len, $cgi_len"); + $stlimit = 0; + //error_log("sizes: $client_len, $cgi_len"); if($cgi_len > 0) { - error_log("from cgi: \"$from_cgi_data\""); + //error_log("from cgi: \"$from_cgi_data\""); } } } - } + flush(); + error_log("and im done..."); + //fclose($fl); fclose($fh); fclose($pipes[1]); fclose($pipes[0]); + + exit(0); } @@ -437,18 +448,40 @@ function gwvpmini_repoExists($name) // 0 - anyone can clone/read, only owner can write // 1 - noone can clone/read, repo is visible (i.e. name), only owner can read/write repo // 2 - only owner can see anything -function gwvpmini_createGitRepo($name, $ownerid, $desc) +function gwvpmini_createGitRepo($name, $ownerid, $desc, $defperms, $clonefrom, $isremoteclone) { + global $cmd_line_tool; + $repo_base = gwvpmini_getConfigVal("repodir"); + if($clonefrom !== false) { + error_log("how did i end up in clonefrom? $clonefrom"); + if(!$isremoteclone) { + exec("/usr/bin/git clone --bare $repo_base/$clonefrom.git $repo_base/$name.git >> /tmp/gitlog 2>&1"); + gwvpmini_AddRepo($name, $desc, $ownerid, $defperms, $clonefrom); + } else { + // we do this from an outside call in the background + $cmd = "/usr/bin/php $cmd_line_tool $clonefrom $name backgroundclone >> /tmp/gitlog 2>&1 &"; + error_log("cmd called as $cmd"); + exec($cmd); + gwvpmini_AddRepo($name, $desc, $ownerid, $defperms, $clonefrom); + $rn = gwvpmini_getRepo(null, $name, null); + $rid = $rn["id"]; + gwvpmini_SetRepoCloning($rid); + gwvpmini_SendMessageByDb("info", "Background clone initiated for $name ($rid) from $clonefrom... your repo will be available once the background clone is finished", $ownerid); + return false; + } + } else { + // phew, this works, but i tell you this - bundles arent quite as nice as they should be - error_log("would create $repo_base/$name.git"); - exec("/usr/bin/git init $repo_base/$name.git --bare > /tmp/gitlog 2>&1"); - chdir("$repo_base/$name.git"); - exec("/usr/bin/git update-server-info"); - - // gwvpmini_AddRepo($reponame, $repodesc, $repoowner, $defaultperms = 0) - gwvpmini_AddRepo($name, $desc, $ownerid); + // error_log("would create $repo_base/$name.git"); + exec("/usr/bin/git init $repo_base/$name.git --bare >> /tmp/gitlog 2>&1"); + chdir("$repo_base/$name.git"); + exec("/usr/bin/git update-server-info"); + + // gwvpmini_AddRepo($reponame, $repodesc, $repoowner, $defaultperms = 0) + gwvpmini_AddRepo($name, $desc, $ownerid, $defperms, $clonefrom); + } return true; }