| 1 | <?php |
|---|
| 2 | //HTTP Digest authentication code, modified from http://php.net/manual/en/features.http-auth.php |
|---|
| 3 | define('HTTP_DIGEST_REALM', 'Build Server'); |
|---|
| 4 | |
|---|
| 5 | //Function to challenge the user |
|---|
| 6 | function http_digest_challenge() { |
|---|
| 7 | header('HTTP/1.1 401 Unauthorized'); |
|---|
| 8 | header(sprintf('WWW-Authenticate: Digest realm="%s",qop="auth",nonce="%s",opaque="%s"', |
|---|
| 9 | HTTP_DIGEST_REALM, uniqid(), md5(HTTP_DIGEST_REALM))); |
|---|
| 10 | die('Authorisation required.'); |
|---|
| 11 | } |
|---|
| 12 | |
|---|
| 13 | //Function to parse the HTTP auth header |
|---|
| 14 | function http_digest_parse($txt) { |
|---|
| 15 | // protect against missing data |
|---|
| 16 | $needed_parts = array('nonce' => 1, |
|---|
| 17 | 'nc' => 1, |
|---|
| 18 | 'cnonce' => 1, |
|---|
| 19 | 'qop' => 1, |
|---|
| 20 | 'username' => 1, |
|---|
| 21 | 'uri' => 1, |
|---|
| 22 | 'response' => 1 |
|---|
| 23 | ); |
|---|
| 24 | $data = array(); |
|---|
| 25 | |
|---|
| 26 | preg_match_all('@(\w+)=(?:(?:\'([^\']+)\'|"([^"]+)")|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER); |
|---|
| 27 | |
|---|
| 28 | foreach ($matches as $m) |
|---|
| 29 | { |
|---|
| 30 | $data[$m[1]] = $m[2] ? $m[2] : ($m[3] ? $m[3] : $m[4]); |
|---|
| 31 | unset($needed_parts[$m[1]]); |
|---|
| 32 | } |
|---|
| 33 | |
|---|
| 34 | return $needed_parts ? false : $data; |
|---|
| 35 | } |
|---|
| 36 | |
|---|
| 37 | //Challenge the client if we did not receive the digest |
|---|
| 38 | if (empty($_SERVER['PHP_AUTH_DIGEST'])) |
|---|
| 39 | http_digest_challenge(); |
|---|
| 40 | |
|---|
| 41 | //Analyze the PHP_AUTH_DIGEST variable |
|---|
| 42 | $credentials = http_digest_parse($_SERVER['PHP_AUTH_DIGEST']); |
|---|
| 43 | if (!$credentials) |
|---|
| 44 | http_digest_challenge(); |
|---|
| 45 | |
|---|
| 46 | //Does the user exist? |
|---|
| 47 | require_once('Credentials.php'); |
|---|
| 48 | require_once('Database.php'); |
|---|
| 49 | $database = new Database(); |
|---|
| 50 | $count = $database->query(sprintf('SELECT COUNT(*) FROM build_slaves WHERE Username=%s', |
|---|
| 51 | $database->quote($credentials['username'])))->fetch(); |
|---|
| 52 | $count = $count[0]; |
|---|
| 53 | if (!$count) |
|---|
| 54 | http_digest_challenge(); |
|---|
| 55 | |
|---|
| 56 | //Check the response for the password. |
|---|
| 57 | $password = $database->query(sprintf('SELECT Password FROM build_slaves WHERE Username=%s', |
|---|
| 58 | $database->quote($credentials['username'])))->fetch(); |
|---|
| 59 | $password = $password['Password']; |
|---|
| 60 | $A1 = md5($credentials['username'] . ':' . HTTP_DIGEST_REALM . ':' . $password); |
|---|
| 61 | $A2 = md5($_SERVER['REQUEST_METHOD'] . ':' . $credentials['uri']); |
|---|
| 62 | $valid_response = md5($A1 . ':' . $credentials['nonce'] . ':' . $credentials['nc'] . ':' . |
|---|
| 63 | $credentials['cnonce'] . ':' . $credentials['qop'] . ':' . $A2); |
|---|
| 64 | if ($credentials['response'] != $valid_response) |
|---|
| 65 | http_digest_challenge(); |
|---|
| 66 | |
|---|
| 67 | require_once('Build.php'); |
|---|
| 68 | require_once('BuildUtil.php'); |
|---|
| 69 | require_once('BuildBranch.php'); |
|---|
| 70 | |
|---|
| 71 | try |
|---|
| 72 | { |
|---|
| 73 | //Check that we have all the necessary information |
|---|
| 74 | $branches = BuildBranch::Get(); |
|---|
| 75 | if (!is_numeric($_GET['revision']) || !is_numeric($_GET['filesize']) || empty($_GET['url']) || empty($_GET['branch'])) |
|---|
| 76 | throw new Exception('Invalid build information provided.'); |
|---|
| 77 | if (!array_key_exists($_GET['branch'], $branches)) |
|---|
| 78 | throw new Exception('The branch ' . $_GET['branch'] . ' does not exist.'); |
|---|
| 79 | |
|---|
| 80 | //Get the branch the notification is for |
|---|
| 81 | $branch = $branches[$_GET['branch']]; |
|---|
| 82 | |
|---|
| 83 | //Insert the build to the database. |
|---|
| 84 | ob_start(); |
|---|
| 85 | printf('Inserting build into database... '); |
|---|
| 86 | Build::CreateBuild($branch->ID, intval($_GET['revision']), intval($_GET['filesize']), $_GET['url']); |
|---|
| 87 | printf("Inserted.\n"); |
|---|
| 88 | |
|---|
| 89 | //Remove old builds |
|---|
| 90 | printf('Removing old builds from database...' . "\n"); |
|---|
| 91 | |
|---|
| 92 | $pdo = new Database(); |
|---|
| 93 | $statement = $pdo->prepare('UPDATE downloads SET Superseded=1 WHERE DownloadID=?'); |
|---|
| 94 | |
|---|
| 95 | $builds = Build::GetActive($branch->ID); |
|---|
| 96 | for ($i = 0, $j = count($builds) - 3; $i < $j; ++$i) |
|---|
| 97 | { |
|---|
| 98 | printf("\n\t" . 'Removing build %s' . "\n\t\t", $builds[$i]->Name); |
|---|
| 99 | |
|---|
| 100 | //Delete the copy on the SourceForge web server. |
|---|
| 101 | Delete(SHELL_WEB_ROOT . parse_url($builds[$i]->Link, PHP_URL_PATH), $sftp_username, |
|---|
| 102 | $sftp_password); |
|---|
| 103 | |
|---|
| 104 | //Remove from the database |
|---|
| 105 | $statement->execute(array($builds[$i]->ID)); |
|---|
| 106 | } |
|---|
| 107 | |
|---|
| 108 | header('content-type: text/plain'); |
|---|
| 109 | ob_end_flush(); |
|---|
| 110 | } |
|---|
| 111 | catch (Exception $e) |
|---|
| 112 | { |
|---|
| 113 | ob_end_clean(); |
|---|
| 114 | header('500 Internal Server Error'); |
|---|
| 115 | echo $e->getMessage(); |
|---|
| 116 | } |
|---|
| 117 | ?> |
|---|