Refactor into Orbit namespace
This commit is contained in:
parent
fc00fdbd63
commit
1a48642baa
122
bin/orbit
122
bin/orbit
|
@ -1,105 +1,27 @@
|
||||||
#!/usr/bin/env php
|
#!/usr/bin/env php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
// Certificate data:
|
require_once dirname(__DIR__) . "/vendor/autoload.php";
|
||||||
$dn = array(
|
|
||||||
"countryName" => "UK",
|
// Define command line args for this client
|
||||||
"stateOrProvinceName" => "Somerset",
|
// And fetch args passed in by invocation
|
||||||
"localityName" => "Glastonbury",
|
$args = new \Qi_Console_ArgV(
|
||||||
"organizationName" => "The Brain Room Limited",
|
$argv,
|
||||||
"organizationalUnitName" => "PHP Documentation Team",
|
array(
|
||||||
"commonName" => "Wez Furlong",
|
'host|h:' => 'Set host/ip address to listen on (default 0.0.0.0)',
|
||||||
"emailAddress" => "wez@example.com"
|
'port|p:' => 'Set port to listen on (default 1965)',
|
||||||
|
'pem-cert:' => 'Set cert PEM file to use (default ./server.pem)',
|
||||||
|
'log:' => 'Set log filename (default orbit.log)',
|
||||||
|
'help|h' => 'Show help',
|
||||||
|
'verbose|v' => 'Include more verbose output',
|
||||||
|
'quiet|q' => 'Print less messages',
|
||||||
|
'no-color' => 'Don\'t use color output',
|
||||||
|
'version' => 'Show version and exit',
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
$terminal = new \Qi_Console_Terminal();
|
||||||
|
$error_handler = new \Qi_Console_ExceptionHandler($terminal, true);
|
||||||
|
$console = new \Orbit\Console($args, $terminal);
|
||||||
|
|
||||||
// Generate certificate
|
$value = $console->execute();
|
||||||
$privkey = openssl_pkey_new();
|
exit($value);
|
||||||
$cert = openssl_csr_new($dn, $privkey);
|
|
||||||
$cert = openssl_csr_sign($cert, null, $privkey, 365);
|
|
||||||
|
|
||||||
// Generate PEM file
|
|
||||||
# Optionally change the passphrase from 'comet' to whatever you want, or leave it empty for no passphrase
|
|
||||||
$pem_passphrase = 'comet';
|
|
||||||
$pem = array();
|
|
||||||
openssl_x509_export($cert, $pem[0]);
|
|
||||||
openssl_pkey_export($privkey, $pem[1], $pem_passphrase);
|
|
||||||
$pem = implode($pem);
|
|
||||||
|
|
||||||
// Save PEM file
|
|
||||||
$pemfile = './server.pem';
|
|
||||||
file_put_contents($pemfile, $pem);
|
|
||||||
|
|
||||||
$context = stream_context_create();
|
|
||||||
|
|
||||||
// local_cert must be in PEM format
|
|
||||||
stream_context_set_option($context, 'ssl', 'local_cert', $pemfile);
|
|
||||||
// Pass Phrase (password) of private key
|
|
||||||
stream_context_set_option($context, 'ssl', 'passphrase', $pem_passphrase);
|
|
||||||
|
|
||||||
stream_context_set_option($context, 'ssl', 'allow_self_signed', true);
|
|
||||||
stream_context_set_option($context, 'ssl', 'verify_peer', false);
|
|
||||||
|
|
||||||
// Create the server socket
|
|
||||||
$server = stream_socket_server('ssl://0.0.0.0:1965', $errno, $errstr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
//$buffer = '';
|
|
||||||
print "waiting...";
|
|
||||||
$client = @stream_socket_accept($server);
|
|
||||||
|
|
||||||
if ($client) {
|
|
||||||
print "accepted " . stream_socket_get_name($client, true) . "\n";
|
|
||||||
$request = stream_get_line($client, 1024, "\r\n");
|
|
||||||
// Read until CRLF
|
|
||||||
//while(!preg_match('/\r\n/', $buffer) )
|
|
||||||
// $buffer .= fread($client, 2046);
|
|
||||||
print(View::hexView($request));
|
|
||||||
|
|
||||||
// Respond to client
|
|
||||||
$response = implode("\r\n", ['20 text/gemini', 'Thanks for all the fish']);
|
|
||||||
fwrite($client, $response);
|
|
||||||
fclose($client);
|
|
||||||
} else {
|
|
||||||
print "error.\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class View
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* View hex chars of string
|
|
||||||
*
|
|
||||||
* Outputs a listing of hexidecimal values in 16 byte rows
|
|
||||||
*
|
|
||||||
* @param mixed $text Input text
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function hexView($text)
|
|
||||||
{
|
|
||||||
$num = 16;
|
|
||||||
$outStr = '';
|
|
||||||
$printableChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
||||||
. 'abcdefghijklmnopqrstuvwxyz'
|
|
||||||
. '0123456789~!@#$%^&*()_+-={}|[]\:";\'<>?,./';
|
|
||||||
|
|
||||||
$charCount = strlen($text);
|
|
||||||
for ($i = 0; $i < $charCount; $i += $num) {
|
|
||||||
$printStr = '';
|
|
||||||
for ($j = 0; $j < $num; $j++) {
|
|
||||||
$char = substr($text, $i+$j, 1);
|
|
||||||
|
|
||||||
$outStr .= sprintf("%02X", ord($char)) . " ";
|
|
||||||
|
|
||||||
if (ord($char) >= 32 && ord($char) < 127) {
|
|
||||||
$printStr .= $char;
|
|
||||||
} else {
|
|
||||||
$printStr .= ".";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$outStr .= " | " . $printStr . "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $outStr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
20
composer.json
Normal file
20
composer.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "sumpygump/orbit",
|
||||||
|
"description": "Server for gemini protocol",
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Jansen Price",
|
||||||
|
"email": "sumpygump@tilde.tam"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"sumpygump/qi-console": "^1.3",
|
||||||
|
"monolog/monolog": "^2.1"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4" : {
|
||||||
|
"Orbit\\" : "src/Orbit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
202
composer.lock
generated
Normal file
202
composer.lock
generated
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
{
|
||||||
|
"_readme": [
|
||||||
|
"This file locks the dependencies of your project to a known state",
|
||||||
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
|
"This file is @generated automatically"
|
||||||
|
],
|
||||||
|
"content-hash": "f2158d38eb2be637af2512ebe8b515b2",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "monolog/monolog",
|
||||||
|
"version": "2.1.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/Seldaek/monolog.git",
|
||||||
|
"reference": "f9eee5cec93dfb313a38b6b288741e84e53f02d5"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/f9eee5cec93dfb313a38b6b288741e84e53f02d5",
|
||||||
|
"reference": "f9eee5cec93dfb313a38b6b288741e84e53f02d5",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.2",
|
||||||
|
"psr/log": "^1.0.1"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"psr/log-implementation": "1.0.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"aws/aws-sdk-php": "^2.4.9 || ^3.0",
|
||||||
|
"doctrine/couchdb": "~1.0@dev",
|
||||||
|
"elasticsearch/elasticsearch": "^6.0",
|
||||||
|
"graylog2/gelf-php": "^1.4.2",
|
||||||
|
"php-amqplib/php-amqplib": "~2.4",
|
||||||
|
"php-console/php-console": "^3.1.3",
|
||||||
|
"php-parallel-lint/php-parallel-lint": "^1.0",
|
||||||
|
"phpspec/prophecy": "^1.6.1",
|
||||||
|
"phpunit/phpunit": "^8.5",
|
||||||
|
"predis/predis": "^1.1",
|
||||||
|
"rollbar/rollbar": "^1.3",
|
||||||
|
"ruflin/elastica": ">=0.90 <3.0",
|
||||||
|
"swiftmailer/swiftmailer": "^5.3|^6.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
|
||||||
|
"doctrine/couchdb": "Allow sending log messages to a CouchDB server",
|
||||||
|
"elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
|
||||||
|
"ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
|
||||||
|
"ext-mbstring": "Allow to work properly with unicode symbols",
|
||||||
|
"ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
|
||||||
|
"graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
|
||||||
|
"mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
|
||||||
|
"php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
|
||||||
|
"php-console/php-console": "Allow sending log messages to Google Chrome",
|
||||||
|
"rollbar/rollbar": "Allow sending log messages to Rollbar",
|
||||||
|
"ruflin/elastica": "Allow sending log messages to an Elastic Search server"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "2.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Monolog\\": "src/Monolog"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Jordi Boggiano",
|
||||||
|
"email": "j.boggiano@seld.be",
|
||||||
|
"homepage": "http://seld.be"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Sends your logs to files, sockets, inboxes, databases and various web services",
|
||||||
|
"homepage": "http://github.com/Seldaek/monolog",
|
||||||
|
"keywords": [
|
||||||
|
"log",
|
||||||
|
"logging",
|
||||||
|
"psr-3"
|
||||||
|
],
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/Seldaek",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2020-07-23T08:41:23+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "psr/log",
|
||||||
|
"version": "1.1.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-fig/log.git",
|
||||||
|
"reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
|
||||||
|
"reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.1.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Psr\\Log\\": "Psr/Log/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "http://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Common interface for logging libraries",
|
||||||
|
"homepage": "https://github.com/php-fig/log",
|
||||||
|
"keywords": [
|
||||||
|
"log",
|
||||||
|
"psr",
|
||||||
|
"psr-3"
|
||||||
|
],
|
||||||
|
"time": "2020-03-23T09:12:05+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sumpygump/qi-console",
|
||||||
|
"version": "1.3.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/sumpygump/qi-console.git",
|
||||||
|
"reference": "f3afecb5fe238799a09f38082379a3cd9057eb07"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/sumpygump/qi-console/zipball/f3afecb5fe238799a09f38082379a3cd9057eb07",
|
||||||
|
"reference": "f3afecb5fe238799a09f38082379a3cd9057eb07",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "7.5.*"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-0": {
|
||||||
|
"Qi_Console_": "lib/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Jansen Price",
|
||||||
|
"email": "jansen.price@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Console utilities",
|
||||||
|
"homepage": "http://quantalideas.com/code/qi-console",
|
||||||
|
"keywords": [
|
||||||
|
"console",
|
||||||
|
"terminal",
|
||||||
|
"terminfo"
|
||||||
|
],
|
||||||
|
"time": "2020-08-26T20:40:44+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": [],
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": [],
|
||||||
|
"platform-dev": [],
|
||||||
|
"plugin-api-version": "1.1.0"
|
||||||
|
}
|
13
src/Orbit/Config.php
Normal file
13
src/Orbit/Config.php
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Orbit;
|
||||||
|
|
||||||
|
class Config
|
||||||
|
{
|
||||||
|
public $host = "0.0.0.0";
|
||||||
|
public $port = 1965;
|
||||||
|
public $log_file = "./orbit.log";
|
||||||
|
|
||||||
|
public $quiet = false;
|
||||||
|
public $verbose = false;
|
||||||
|
}
|
119
src/Orbit/Console.php
Normal file
119
src/Orbit/Console.php
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Orbit;
|
||||||
|
|
||||||
|
use Monolog\Logger;
|
||||||
|
use Monolog\Formatter\LineFormatter;
|
||||||
|
use Monolog\Handler\StreamHandler;
|
||||||
|
|
||||||
|
class Console extends \Qi_Console_Client
|
||||||
|
{
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
if ($this->_args->version) {
|
||||||
|
$this->showVersion();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->_args->help) {
|
||||||
|
$this->showHelp();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = new Config();
|
||||||
|
|
||||||
|
if ($this->_args->host) {
|
||||||
|
$config->host = $this->_args->host;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->_args->port) {
|
||||||
|
$config->port = $this->_args->port;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->_args->log) {
|
||||||
|
$config->log_file = $this->_args->log;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->_args->quiet) {
|
||||||
|
$config->quiet = $this->_args->quiet;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->_args->verbose) {
|
||||||
|
$config->verbose = $this->_args->verbose;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$config->quiet) {
|
||||||
|
print "Orbit // Gemini server software\n";
|
||||||
|
}
|
||||||
|
$server = new Server($config);
|
||||||
|
|
||||||
|
$server->setLogger($this->makeLogger($config));
|
||||||
|
$server->listen();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function makeLogger($config)
|
||||||
|
{
|
||||||
|
$pid = getmypid();
|
||||||
|
$output = "[%datetime%] $pid %channel%.%level_name%: %message% %context%\n";
|
||||||
|
$formatter = new LineFormatter($output, 'Y-m-d\TH:i:s');
|
||||||
|
|
||||||
|
$logger = new Logger('orbit');
|
||||||
|
|
||||||
|
$level = Logger::INFO;
|
||||||
|
if ($config->verbose) {
|
||||||
|
$level = Logger::DEBUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
$log_stream = new StreamHandler($config->log_file, $level);
|
||||||
|
$log_stream->setFormatter($formatter);
|
||||||
|
$logger->pushHandler($log_stream);
|
||||||
|
|
||||||
|
if (!$config->quiet) {
|
||||||
|
$std_stream = new StreamHandler('php://stdout', $level);
|
||||||
|
$std_stream->setFormatter($formatter);
|
||||||
|
$logger->pushHandler($std_stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function showVersion()
|
||||||
|
{
|
||||||
|
print "Orbit " . Server::$version . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function showHelp()
|
||||||
|
{
|
||||||
|
$this->showVersion();
|
||||||
|
|
||||||
|
$out = $this->_terminal->do_op()
|
||||||
|
. "Usage: orbit [options]\n"
|
||||||
|
. "\n"
|
||||||
|
. $this->_terminal->do_setaf(3)
|
||||||
|
. "Options:\n"
|
||||||
|
. $this->_terminal->do_op();
|
||||||
|
|
||||||
|
foreach ($this->_args->getHelp() as $key => $value) {
|
||||||
|
if (strpos($key, "|")) {
|
||||||
|
$parts = explode("|", $key);
|
||||||
|
$left = sprintf("-%s, --%s", $parts[0], $parts[1]);
|
||||||
|
} else {
|
||||||
|
$left = sprintf(" --%s", $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($left[-1] == ":") {
|
||||||
|
$left = substr($left, 0, -1) . " <arg>";
|
||||||
|
}
|
||||||
|
|
||||||
|
$out .= sprintf(
|
||||||
|
" %s%s %s%s\n",
|
||||||
|
$this->_terminal->do_setaf(2),
|
||||||
|
str_pad($left, 20),
|
||||||
|
$this->_terminal->do_op(),
|
||||||
|
$value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
print $out;
|
||||||
|
}
|
||||||
|
}
|
26
src/Orbit/Request.php
Normal file
26
src/Orbit/Request.php
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Orbit;
|
||||||
|
|
||||||
|
class Request
|
||||||
|
{
|
||||||
|
public $url = '';
|
||||||
|
public $scheme;
|
||||||
|
public $host;
|
||||||
|
public $port;
|
||||||
|
public $user;
|
||||||
|
public $pass;
|
||||||
|
public $path;
|
||||||
|
public $query;
|
||||||
|
public $fragment;
|
||||||
|
|
||||||
|
public function __construct($request_input)
|
||||||
|
{
|
||||||
|
$this->url = $request_input;
|
||||||
|
$data = parse_url($request_input);
|
||||||
|
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
$this->{$key} = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
154
src/Orbit/Server.php
Normal file
154
src/Orbit/Server.php
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Orbit;
|
||||||
|
|
||||||
|
use Monolog\Logger;
|
||||||
|
use Monolog\Handler\StreamHandler;
|
||||||
|
|
||||||
|
class Server
|
||||||
|
{
|
||||||
|
public static $version = "0.2";
|
||||||
|
|
||||||
|
public $config;
|
||||||
|
public $pemfile = "./server.pem";
|
||||||
|
public $pem_passphrase = "";
|
||||||
|
public $timeout = 60;
|
||||||
|
|
||||||
|
private $ssl_context;
|
||||||
|
private $logger;
|
||||||
|
|
||||||
|
public function __construct(Config $config = null)
|
||||||
|
{
|
||||||
|
if ($config == null) {
|
||||||
|
$this->config = new Config();
|
||||||
|
} else {
|
||||||
|
$this->config = $config;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->timeout = ini_get("default_socket_timeout");
|
||||||
|
|
||||||
|
if (file_exists($this->pemfile)) {
|
||||||
|
$this->log(Logger::DEBUG, "Using existing cert.");
|
||||||
|
} else {
|
||||||
|
$this->log(Logger::DEBUG, "Generating new cert.");
|
||||||
|
$this->generateCert();
|
||||||
|
}
|
||||||
|
$this->ssl_context = $this->createSslContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLogger(Logger $logger)
|
||||||
|
{
|
||||||
|
$this->logger = $logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLogger()
|
||||||
|
{
|
||||||
|
if (!$this->logger) {
|
||||||
|
$this->logger = new Logger('orbit');
|
||||||
|
$this->logger->pushHandler(new StreamHandler($this->config->log_file, Logger::INFO));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listen()
|
||||||
|
{
|
||||||
|
$server = stream_socket_server(
|
||||||
|
$this->getListenAddress(),
|
||||||
|
$errno, $errstr,
|
||||||
|
STREAM_SERVER_BIND|STREAM_SERVER_LISTEN,
|
||||||
|
$this->ssl_context
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!$server) {
|
||||||
|
throw new \Exception("Error " . $errno . ": " . $errstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
$protocol = "gemini";
|
||||||
|
$name = stream_socket_get_name($server, false);
|
||||||
|
$this->log(Logger::NOTICE, "Listening on $protocol://$name ...");
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
# This is to swallow up the `timeout` warning
|
||||||
|
set_error_handler([$this, 'onWarning']);
|
||||||
|
$client = stream_socket_accept($server, $this->timeout, $client_name);
|
||||||
|
restore_error_handler();
|
||||||
|
|
||||||
|
if ($client) {
|
||||||
|
$this->log(Logger::DEBUG, "$client_name Accepted");
|
||||||
|
$request_buffer = stream_get_line($client, 1024, "\r\n");
|
||||||
|
$this->log(Logger::INFO, "REQ: $request_buffer", ["client" => $client_name]);
|
||||||
|
$request = new Request($request_buffer);
|
||||||
|
|
||||||
|
// Respond to client
|
||||||
|
$response = implode("\r\n", ['20 text/gemini', "Thanks for all the fish\n"]);
|
||||||
|
fwrite($client, $response);
|
||||||
|
fclose($client);
|
||||||
|
$this->log(Logger::DEBUG, "$client_name Closed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function log($level, $message, $context = [])
|
||||||
|
{
|
||||||
|
$this->getLogger()->log($level, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onWarning($id, $message)
|
||||||
|
{
|
||||||
|
if (strpos($message, "Operation timed out") !== false) {
|
||||||
|
// Do nothing
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Something else happened.
|
||||||
|
throw new \Exception($id . ' ' . $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getListenAddress()
|
||||||
|
{
|
||||||
|
return sprintf('tls://%s:%s', $this->config->host, $this->config->port);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateCert()
|
||||||
|
{
|
||||||
|
// Certificate data
|
||||||
|
$dn = [
|
||||||
|
"countryName" => "UK",
|
||||||
|
"stateOrProvinceName" => "X",
|
||||||
|
"localityName" => "X",
|
||||||
|
"organizationName" => "X",
|
||||||
|
"organizationalUnitName" => "X",
|
||||||
|
"commonName" => "orbit1.0",
|
||||||
|
"emailAddress" => "X"
|
||||||
|
];
|
||||||
|
|
||||||
|
// Generate certificate
|
||||||
|
$privkey = openssl_pkey_new();
|
||||||
|
$cert = openssl_csr_new($dn, $privkey);
|
||||||
|
$cert = openssl_csr_sign($cert, null, $privkey, 365);
|
||||||
|
|
||||||
|
// Generate PEM file
|
||||||
|
$pem = [];
|
||||||
|
openssl_x509_export($cert, $pem[0]);
|
||||||
|
openssl_pkey_export($privkey, $pem[1], $this->pem_passphrase);
|
||||||
|
$pem = implode($pem);
|
||||||
|
|
||||||
|
// Save PEM file
|
||||||
|
file_put_contents($this->pemfile, $pem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createSslContext()
|
||||||
|
{
|
||||||
|
$context = stream_context_create();
|
||||||
|
|
||||||
|
// local_cert must be in PEM format
|
||||||
|
stream_context_set_option($context, 'ssl', 'local_cert', $this->pemfile);
|
||||||
|
stream_context_set_option($context, 'ssl', 'passphrase', $this->pem_passphrase);
|
||||||
|
|
||||||
|
stream_context_set_option($context, 'ssl', 'allow_self_signed', true);
|
||||||
|
stream_context_set_option($context, 'ssl', 'verify_peer', false);
|
||||||
|
|
||||||
|
return $context;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue