Created
August 30, 2023 15:27
-
-
Save live627/68345dfb22756a9d16dc81aa800780a1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class HttpException extends Exception | |
{ | |
/** | |
* List of HTTP status codes | |
* | |
* From http://en.wikipedia.org/wiki/List_of_HTTP_status_codes | |
* | |
* @var array | |
*/ | |
private $status = [ | |
400 => 'Bad Request', | |
401 => 'Unauthorized', | |
402 => 'Payment Required', | |
403 => 'Forbidden', | |
404 => 'Not Found', | |
405 => 'Method Not Allowed', | |
406 => 'Not Acceptable', | |
407 => 'Proxy Authentication Required', | |
408 => 'Request Timeout', | |
409 => 'Conflict', | |
410 => 'Gone', | |
411 => 'Length Required', | |
412 => 'Precondition Failed', | |
413 => 'Request Entity Too Large', | |
414 => 'Request-URI Too Long', | |
415 => 'Unsupported Media Type', | |
416 => 'Requested Range Not Satisfiable', | |
417 => 'Expectation Failed', | |
418 => 'I\'m a teapot', // RFC 2324 | |
426 => 'Upgrade Required', // RFC 2817 | |
428 => 'Precondition Required', // RFC 6585 | |
]; | |
/** | |
* @param int $status Defaults to 400 | |
* @param ?string $statusText If null, will use the default status phrase | |
*/ | |
public function __construct(int $status = 400, string $statusText = null) | |
{ | |
if ($statusText === null && isset($this->status[$status])) | |
$statusText = $this->status[$status]; | |
parent::__construct($statusText, $status); | |
} | |
} | |
class HttpResponse | |
{ | |
/** | |
* List of HTTP status codes | |
* | |
* From http://en.wikipedia.org/wiki/List_of_HTTP_status_codes | |
* | |
* @var array | |
*/ | |
private $status = [ | |
200 => 'OK', | |
201 => 'Created', | |
202 => 'Accepted', | |
203 => 'Non-Authoritative Information', | |
204 => 'No Content', | |
205 => 'Reset Content', | |
206 => 'Partial Content', | |
207 => 'Multi-Status', | |
300 => 'Multiple Choices', | |
301 => 'Moved Permanently', | |
302 => 'Found', | |
303 => 'See Other', | |
304 => 'Not Modified', | |
307 => 'Temporary Redirect', | |
308 => 'Permanent Redirect', | |
400 => 'Bad Request', | |
401 => 'Unauthorized', | |
402 => 'Payment Required', | |
403 => 'Forbidden', | |
404 => 'Not Found', | |
405 => 'Method Not Allowed', | |
406 => 'Not Acceptable', | |
407 => 'Proxy Authentication Required', | |
408 => 'Request Timeout', | |
409 => 'Conflict', | |
410 => 'Gone', | |
411 => 'Length Required', | |
412 => 'Precondition Failed', | |
413 => 'Request Entity Too Large', | |
414 => 'Request-URI Too Long', | |
415 => 'Unsupported Media Type', | |
416 => 'Requested Range Not Satisfiable', | |
417 => 'Expectation Failed', | |
418 => 'I\'m a teapot', // RFC 2324 | |
426 => 'Upgrade Required', // RFC 2817 | |
428 => 'Precondition Required', // RFC 6585 | |
]; | |
/** | |
* @param int $status Defaults to 400 | |
* @param ?string $statusText If null, will use the default status phrase | |
* @param ?array $headers List of additional headers | |
* @param ?array $data Data to output JSON-encoded | |
*/ | |
public function __construct( | |
int $status = 400, | |
?string $statusText = null, | |
?array $headers = null, | |
?array $data = null | |
) | |
{ | |
if ($statusText === null && isset($this->status[$status])) | |
$statusText = $this->status[$status]; | |
header(sprintf('HTTP/1.1 %d %s', $status, $statusText)); | |
if ($headers !== null) | |
foreach ($headers as $key => $header) | |
{ | |
if (!is_int($key)) | |
$header = $key . ': ' . $header; | |
header($header); | |
} | |
echo $data === null ? [] : json_encode($data); | |
} | |
} | |
function parse_headers($headers) | |
{ | |
$head = array(); | |
foreach ($headers as $k => $v) | |
{ | |
$t = explode(':', $v, 2); | |
if (isset($t[1])) | |
$head[trim($t[0])] = trim($t[1]); | |
else | |
{ | |
$head[] = $v; | |
if (preg_match('/HTTP\/[0-9\.]+\s+([0-9]+)/', $v, $out)) | |
$head['reponse_code'] = intval($out[1]); | |
} | |
} | |
return $head; | |
} | |
try | |
{ | |
header('Allow: POST'); | |
header('Content-Type: application/json'); | |
header('Accept: application/json;application/x-www-form-urlencoded'); | |
if ($_SERVER['REQUEST_METHOD'] != 'POST') | |
throw new HttpException(405); | |
if (!isset($_SERVER['CONTENT_TYPE'])) | |
throw new HttpException(400, "Missing HTTP 'Content-Type' header."); | |
elseif (!isset($_SERVER['HTTP_X_GITHUB_EVENT'])) | |
throw new HttpException(400, "Missing HTTP 'X-Github-Event' header."); | |
if ($_SERVER['HTTP_X_GITHUB_EVENT'] == 'ping') | |
new HttpResponse(200); | |
else | |
{ | |
$algo = strtok($_SERVER['HTTP_X_HUB_SIGNATURE'], '='); | |
$signature = strtok('='); | |
if ($signature === false) | |
throw new HttpException(400, 'signature has invalid format'); | |
if (!isset($_SERVER['HTTP_X_HUB_SIGNATURE'])) | |
throw new HttpException(400, "HTTP header 'X-Hub-Signature' is missing."); | |
elseif (!extension_loaded('hash')) | |
throw new HttpException(400, "Missing 'hash' extension to check the secret code validity."); | |
if (!in_array($algo, hash_algos(), true)) | |
throw new HttpException(400, "Hash algorithm '$algo' is not supported."); | |
$raw_post = file_get_contents('php://input'); | |
if (!hash_equals($signature, hash_hmac($algo, $raw_post, '110eccfe5a43afec70e99fa1c91889c408ecc7b7'))) | |
throw new HttpException(400, 'Hook secret does not match.'); | |
switch ($_SERVER['CONTENT_TYPE']) | |
{ | |
case 'application/json': | |
$json = $raw_post ?: file_get_contents('php://input'); | |
break; | |
case 'application/x-www-form-urlencoded': | |
$json = $_POST['payload']; | |
break; | |
default: | |
throw new HttpException(406, "Unsupported content type: $_SERVER[CONTENT_TYPE]"); | |
} | |
# Payload structure depends on triggered event | |
# https://developer.github.com/v3/activity/events/types/ | |
$payload = json_decode($json); | |
require_once './SSI.php'; | |
require_once $sourcedir . '/Subs-Post.php'; | |
$request = $smcFunc['db_query']('', ' | |
SELECT t.id_topic, t.id_board, m.subject | |
FROM {db_prefix}topics AS t | |
LEFT JOIN {db_prefix}messages AS m ON (t.id_last_msg = m.id_msg) | |
WHERE t.id_topic = 61'); | |
$repo = $smcFunc['db_fetch_assoc']($request); | |
$stream = stream_context_create(array('http' => array('user_agent' => 'Wedge'))); | |
$repo_url = $payload->repository->full_name; | |
$topic_ids = array(); | |
$body = array(); | |
foreach ($payload->commits as $commit) | |
{ | |
$item = file_get_contents('https://api.github.com/repos/' . $repo_url . '/commits/' . $commit->id, false, $stream); | |
if ($item === false) | |
continue; | |
print_r(parse_headers($http_response_header)); | |
$item = json_decode($item); | |
$signed_off = strpos($commit->message, "Signed-off-by: ") !== false; | |
// A lovely series of regex to turn the ugly changelog layout into Audrey Hepburn. | |
// In order: Fix, Comment, Addition, Deletion, Modification. | |
$log = '[list]' . str_replace( | |
array('[cli=!', '[cli=@', '[cli=+', '[cli=-', '[cli=*', '[/cli]\n\n[cli'), | |
array('[cli=f', '[cli=c', '[cli=a', '[cli=r', '[cli=m', '[/cli]\n[cli'), | |
preg_replace( | |
array( | |
'~^([*+@!-]) ([^\v]+)*~m', | |
'~^([^\v[].*+)$~m', | |
), | |
array( | |
'[cli=$1]$2[/cli]', | |
'[li]$1[/li]', | |
), | |
preg_replace('~\n\nSigned-off-by: [^\v]+~', '', $item->commit->message) | |
) | |
) . '[/list]'; | |
$body[] = "[Commit revision " . substr($item->sha, 0, 7) . "][table]\n[tr]\n[td]\n[img height=50]" . $item->author->avatar_url . "[/img][size=10pt]" | |
. "[b]Author[/b]: [url=" . $item->author->html_url . "]" . $item->author->login . "[/url]" . ($signed_off ? " (Signed-off)" : "") . "\n" | |
. "[b]Date[/b]: " . date('r', strtotime($item->commit->author->date)) . "\n" | |
. "[b]Stats[/b]: [url=https://github.com/" . $repo_url . "/commit/" . $item->sha . "]" . count($item->files) | |
. " file" . (count($item->files) > 1 ? 's' : '') . " changed[/url]; +" . $item->stats->additions | |
. " (insertion" . ($item->stats->additions > 1 ? "s" : "") . "), -" . $item->stats->deletions | |
. " (deletion" . ($item->stats->deletions > 1 ? "s" : "") . ")[/size][/td]\n[/tr]\n[/table]\n" | |
. preg_replace('~\n\nSigned-off-by: [^\v]+~', '', $log) . "\n"; | |
} | |
$msgOptions = array( | |
'subject' => $repo['subject'], | |
'body' => implode($body, "[hr]\n"), | |
'icon' => 'xx', | |
'smileys_enabled' => 1, | |
); | |
$topicOptions = array( | |
'id' => $repo['id_topic'], | |
'board' => $repo['id_board'], | |
); | |
$posterOptions = array( | |
'id' => 1, | |
'update_post_count' => true, | |
); | |
createPost($msgOptions, $topicOptions, $posterOptions); | |
} | |
} | |
catch (JsonException $exception) | |
{ | |
new HttpResponse(400, null, null, ['errors' => ['code' => 400, 'message' => $exception->getMessage()]]); | |
} | |
catch (HttpException $exception) | |
{ | |
new HttpResponse($exception->getCode(), null, null, ['errors' => ['code' => $exception->getCode(), 'message' => $exception->getMessage()]]); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment