mirror of https://github.com/YGGverse/Yoda.git
yggverse
5 months ago
8 changed files with 652 additions and 350 deletions
@ -0,0 +1,123 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Abstract\Model; |
||||||
|
|
||||||
|
abstract class Connection implements \Yggverse\Yoda\Interface\Model\Connection |
||||||
|
{ |
||||||
|
// Status |
||||||
|
protected bool $_completed = false; |
||||||
|
|
||||||
|
// Response |
||||||
|
protected ?string $_title = null; |
||||||
|
protected ?string $_subtitle = null; |
||||||
|
protected ?string $_tooltip = null; |
||||||
|
protected ?string $_mime = null; |
||||||
|
protected ?string $_data = null; |
||||||
|
protected ?string $_redirect = null; |
||||||
|
protected ?array $_request = null; |
||||||
|
|
||||||
|
public function isCompleted(): bool |
||||||
|
{ |
||||||
|
return $this->_completed; |
||||||
|
} |
||||||
|
|
||||||
|
public function setCompleted( |
||||||
|
bool $completed |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->_completed = $completed; |
||||||
|
} |
||||||
|
|
||||||
|
public function getTitle(): ?string |
||||||
|
{ |
||||||
|
return $this->_title; |
||||||
|
} |
||||||
|
|
||||||
|
public function setTitle( |
||||||
|
?string $title = null |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->_title = $title; |
||||||
|
} |
||||||
|
|
||||||
|
public function getSubtitle(): ?string |
||||||
|
{ |
||||||
|
return $this->_subtitle; |
||||||
|
} |
||||||
|
|
||||||
|
public function setSubtitle( |
||||||
|
?string $subtitle = null |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->_subtitle = $subtitle; |
||||||
|
} |
||||||
|
|
||||||
|
public function getTooltip(): ?string |
||||||
|
{ |
||||||
|
return $this->_tooltip; |
||||||
|
} |
||||||
|
|
||||||
|
public function setTooltip( |
||||||
|
?string $tooltip = null |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->_tooltip = $tooltip; |
||||||
|
} |
||||||
|
|
||||||
|
public function getMime(): ?string |
||||||
|
{ |
||||||
|
return $this->_mime; |
||||||
|
} |
||||||
|
|
||||||
|
public function setMime( |
||||||
|
?string $mime = null |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->_mime = $mime; |
||||||
|
} |
||||||
|
|
||||||
|
public function getData(): ?string |
||||||
|
{ |
||||||
|
return $this->_data; |
||||||
|
} |
||||||
|
|
||||||
|
public function setData( |
||||||
|
?string $data = null |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->_data = $data; |
||||||
|
} |
||||||
|
|
||||||
|
public function getRedirect(): ?string |
||||||
|
{ |
||||||
|
return $this->_redirect; |
||||||
|
} |
||||||
|
|
||||||
|
public function setRedirect( |
||||||
|
?string $redirect = null |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->_redirect = $redirect; |
||||||
|
} |
||||||
|
|
||||||
|
public function getRequest(): ?array |
||||||
|
{ |
||||||
|
return $this->_request; |
||||||
|
} |
||||||
|
|
||||||
|
public function setRequest( |
||||||
|
?array $request = null |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->_request = $request; // @TODO |
||||||
|
} |
||||||
|
|
||||||
|
public function getLength(): ?int |
||||||
|
{ |
||||||
|
return mb_strlen( |
||||||
|
$this->_data |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Interface\Model; |
||||||
|
|
||||||
|
/* |
||||||
|
* Single API for multiple protocols |
||||||
|
* |
||||||
|
*/ |
||||||
|
interface Connection |
||||||
|
{ |
||||||
|
public function request( |
||||||
|
string $request, |
||||||
|
int $timeout = 5 |
||||||
|
): void; |
||||||
|
|
||||||
|
public const MIME_TEXT_GEMINI = 'text/gemini'; |
||||||
|
public const MIME_TEXT_PLAIN = 'text/plain'; |
||||||
|
|
||||||
|
public function isCompleted(): bool; |
||||||
|
|
||||||
|
public function setCompleted( |
||||||
|
bool $completed |
||||||
|
): void; |
||||||
|
|
||||||
|
public function getTitle(): ?string; |
||||||
|
|
||||||
|
public function setTitle( |
||||||
|
?string $title = null |
||||||
|
): void; |
||||||
|
|
||||||
|
public function getSubtitle(): ?string; |
||||||
|
|
||||||
|
public function setSubtitle( |
||||||
|
?string $subtitle = null |
||||||
|
): void; |
||||||
|
|
||||||
|
public function getTooltip(): ?string; |
||||||
|
|
||||||
|
public function setTooltip( |
||||||
|
?string $tooltip = null |
||||||
|
): void; |
||||||
|
|
||||||
|
public function getMime(): ?string; |
||||||
|
|
||||||
|
public function setMime( |
||||||
|
?string $mime = null |
||||||
|
): void; |
||||||
|
|
||||||
|
public function getData(): ?string; |
||||||
|
|
||||||
|
public function setData( |
||||||
|
?string $data = null |
||||||
|
): void; |
||||||
|
|
||||||
|
public function getRedirect(): ?string; |
||||||
|
|
||||||
|
public function setRedirect( |
||||||
|
?string $redirect = null |
||||||
|
): void; |
||||||
|
|
||||||
|
public function getRequest(): ?array; |
||||||
|
|
||||||
|
public function setRequest( |
||||||
|
?array $request = null |
||||||
|
): void; |
||||||
|
|
||||||
|
public function getLength(): ?int; |
||||||
|
} |
@ -0,0 +1,100 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Model; |
||||||
|
|
||||||
|
use \Yggverse\Net\Address; |
||||||
|
|
||||||
|
use \Yggverse\Yoda\Model\Connection\File; |
||||||
|
use \Yggverse\Yoda\Model\Connection\Gemini; |
||||||
|
use \Yggverse\Yoda\Model\Connection\Nex; |
||||||
|
|
||||||
|
class Connection extends \Yggverse\Yoda\Abstract\Model\Connection |
||||||
|
{ |
||||||
|
public function request( |
||||||
|
string $request, |
||||||
|
int $timeout = 5 |
||||||
|
): void |
||||||
|
{ |
||||||
|
// Build address instance |
||||||
|
$address = new Address( |
||||||
|
$request |
||||||
|
); |
||||||
|
|
||||||
|
// Detect protocol |
||||||
|
switch ($address->getScheme()) |
||||||
|
{ |
||||||
|
case 'file': |
||||||
|
|
||||||
|
(new File($this))->sync( |
||||||
|
$address |
||||||
|
); |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 'gemini': |
||||||
|
|
||||||
|
(new Gemini($this))->sync( |
||||||
|
$address, |
||||||
|
$timeout |
||||||
|
); |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 'nex': |
||||||
|
|
||||||
|
(new Nex($this))->sync( |
||||||
|
$address, |
||||||
|
$timeout |
||||||
|
); |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case null: // no scheme provided |
||||||
|
|
||||||
|
// Use gemini protocol by default |
||||||
|
$redirect = new Address( |
||||||
|
sprintf( |
||||||
|
'gemini://%s', |
||||||
|
$address->get() |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
// Hostname valid |
||||||
|
if (filter_var( |
||||||
|
$redirect->getHost(), |
||||||
|
FILTER_VALIDATE_DOMAIN, |
||||||
|
FILTER_FLAG_HOSTNAME |
||||||
|
) |
||||||
|
) { |
||||||
|
// Redirect |
||||||
|
$this->setRedirect( |
||||||
|
$redirect->get() |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
// Redirect to default search provider |
||||||
|
else |
||||||
|
{ |
||||||
|
// @TODO custom providers |
||||||
|
$this->setRedirect( |
||||||
|
sprintf( |
||||||
|
'gemini://tlgs.one/search?%s', |
||||||
|
urlencode( |
||||||
|
$request |
||||||
|
) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
return; |
||||||
|
|
||||||
|
default: |
||||||
|
|
||||||
|
throw new \Exception( |
||||||
|
_('Protocol not supported') |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,131 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Model\Connection; |
||||||
|
|
||||||
|
use \Yggverse\Net\Address; |
||||||
|
|
||||||
|
use \Yggverse\Yoda\Model\Connection; |
||||||
|
use \Yggverse\Yoda\Model\Filesystem; |
||||||
|
|
||||||
|
class File |
||||||
|
{ |
||||||
|
private Connection $_connection; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
Connection $connection |
||||||
|
) { |
||||||
|
$this->_connection = $connection; |
||||||
|
} |
||||||
|
|
||||||
|
public function sync( |
||||||
|
Address $address |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->_connection->setTitle( |
||||||
|
basename( |
||||||
|
$address->getPath() |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
$this->_connection->setSubtitle( |
||||||
|
$address->getPath() |
||||||
|
); |
||||||
|
|
||||||
|
$this->_connection->setTooltip( |
||||||
|
$address->getPath() |
||||||
|
); |
||||||
|
|
||||||
|
switch (true) |
||||||
|
{ |
||||||
|
case ( // is directory |
||||||
|
$list = Filesystem::getList( |
||||||
|
$address->getPath() |
||||||
|
) |
||||||
|
): |
||||||
|
$tree = []; |
||||||
|
|
||||||
|
foreach ($list as $item) |
||||||
|
{ |
||||||
|
$tree[] = trim( |
||||||
|
sprintf( |
||||||
|
'=> file://%s %s', |
||||||
|
$item['path'], |
||||||
|
$item['name'] . ( |
||||||
|
// append slash indicator |
||||||
|
$item['file'] ? null : '/' |
||||||
|
) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
$this->_connection->setMime( |
||||||
|
$this->_connection::MIME_TEXT_GEMINI |
||||||
|
); |
||||||
|
|
||||||
|
$this->_connection->setData( |
||||||
|
implode( |
||||||
|
PHP_EOL, |
||||||
|
$tree |
||||||
|
) . PHP_EOL |
||||||
|
); |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case file_exists( // is file |
||||||
|
$address->getPath() |
||||||
|
) && is_readable( |
||||||
|
$address->getPath() |
||||||
|
): |
||||||
|
$this->_connection->setData( |
||||||
|
strval( |
||||||
|
file_get_contents( |
||||||
|
$address->getPath() |
||||||
|
) |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
$this->_connection->setMime( |
||||||
|
strval( |
||||||
|
mime_content_type( |
||||||
|
$address->getPath() |
||||||
|
) |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
if ($this->_connection::MIME_TEXT_PLAIN == $this->_connection->getMime()) |
||||||
|
{ |
||||||
|
$extension = pathinfo( |
||||||
|
strval( |
||||||
|
$address->getPath() |
||||||
|
), |
||||||
|
PATHINFO_EXTENSION |
||||||
|
); |
||||||
|
|
||||||
|
if (in_array($extension, ['gmi', 'gemini'])) |
||||||
|
{ |
||||||
|
$this->_connection->setMime( |
||||||
|
$this->_connection::MIME_TEXT_GEMINI |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
|
||||||
|
$this->_connection->setTitle( |
||||||
|
_('Failure') |
||||||
|
); |
||||||
|
|
||||||
|
$this->_connection->setData( |
||||||
|
_('Could not open location') |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
$this->_connection->setCompleted( |
||||||
|
true |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,144 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Model\Connection; |
||||||
|
|
||||||
|
use \Yggverse\Gemini\Client\Request; |
||||||
|
use \Yggverse\Gemini\Client\Response; |
||||||
|
use \Yggverse\Net\Address; |
||||||
|
|
||||||
|
use \Yggverse\Yoda\Model\Connection; |
||||||
|
|
||||||
|
class Gemini |
||||||
|
{ |
||||||
|
private Connection $_connection; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
Connection $connection |
||||||
|
) { |
||||||
|
$this->_connection = $connection; |
||||||
|
} |
||||||
|
|
||||||
|
public function sync( |
||||||
|
Address $address, |
||||||
|
int $timeout = 5 |
||||||
|
): void |
||||||
|
{ |
||||||
|
$request = new Request( |
||||||
|
$address->get() |
||||||
|
); |
||||||
|
|
||||||
|
$response = new Response( |
||||||
|
$request->getResponse( |
||||||
|
$timeout |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
// Route status code |
||||||
|
// https://geminiprotocol.net/docs/protocol-specification.gmi#status-codes |
||||||
|
switch ($response->getCode()) |
||||||
|
{ |
||||||
|
case 10: // response expected |
||||||
|
case 11: // sensitive input |
||||||
|
|
||||||
|
$this->_connection->setMime( |
||||||
|
$this->_connection::MIME_TEXT_GEMINI |
||||||
|
); |
||||||
|
|
||||||
|
$this->_connection->setRequest( |
||||||
|
[ |
||||||
|
'placeholder' => $response->getMeta(), |
||||||
|
'visible' => 11 !== $response->getCode() |
||||||
|
] |
||||||
|
); |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 20: // ok |
||||||
|
|
||||||
|
$this->_connection->setData( |
||||||
|
$response->getBody() |
||||||
|
); |
||||||
|
|
||||||
|
switch (true) |
||||||
|
{ |
||||||
|
case str_contains( |
||||||
|
$response->getMeta(), |
||||||
|
self::MIME_TEXT_GEMINI |
||||||
|
): |
||||||
|
|
||||||
|
$this->_connection->setMime( |
||||||
|
$this->_connection::MIME_TEXT_GEMINI |
||||||
|
); |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case str_contains( |
||||||
|
$response->getMeta(), |
||||||
|
self::MIME_TEXT_PLAIN |
||||||
|
): |
||||||
|
|
||||||
|
$this->_connection->setMime( |
||||||
|
$this->_connection::MIME_TEXT_PLAIN |
||||||
|
); |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
|
||||||
|
throw new \Exception( |
||||||
|
sprintf( |
||||||
|
_('MIME type not implemented for %s'), |
||||||
|
$response->getMeta() |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 31: // redirect |
||||||
|
// show link, no follow |
||||||
|
|
||||||
|
$this->_connection->setTitle( |
||||||
|
_('Redirect!') |
||||||
|
); |
||||||
|
|
||||||
|
$this->_connection->setData( |
||||||
|
sprintf( |
||||||
|
'=> %s', |
||||||
|
$response->getMeta() |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
$this->_connection->setMime( |
||||||
|
$this->_connection::MIME_TEXT_GEMINI |
||||||
|
); |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
|
||||||
|
$this->_connection->setTitle( |
||||||
|
_('Oops!') |
||||||
|
); |
||||||
|
|
||||||
|
$this->_connection->setData( |
||||||
|
sprintf( |
||||||
|
'Could not open request (code: %d)', |
||||||
|
intval( |
||||||
|
$response->getCode() |
||||||
|
) |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
$this->_connection->setMime( |
||||||
|
$this->_connection::MIME_TEXT_GEMINI |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
$this->_connection->setCompleted( |
||||||
|
true |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Model\Connection; |
||||||
|
|
||||||
|
use \Yggverse\Net\Address; |
||||||
|
use \Yggverse\Nex\Client; |
||||||
|
|
||||||
|
use \Yggverse\Yoda\Model\Connection; |
||||||
|
|
||||||
|
class Nex |
||||||
|
{ |
||||||
|
private Connection $_connection; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
Connection $connection |
||||||
|
) { |
||||||
|
$this->_connection = $connection; |
||||||
|
} |
||||||
|
|
||||||
|
// @TODO |
||||||
|
public function sync( |
||||||
|
Address $address, |
||||||
|
int $timeout = 5 |
||||||
|
): void |
||||||
|
{ |
||||||
|
$response = (new \Yggverse\Nex\Client)->request( |
||||||
|
$address->get(), |
||||||
|
$timeout |
||||||
|
); |
||||||
|
|
||||||
|
if ($response) |
||||||
|
{ |
||||||
|
$this->_connection->setTitle( |
||||||
|
strval( |
||||||
|
$address->getHost() |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
$this->_connection->setData( |
||||||
|
$response |
||||||
|
); |
||||||
|
|
||||||
|
$this->_connection->setMime( |
||||||
|
$this->_connection::MIME_TEXT_PLAIN |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
else |
||||||
|
{ |
||||||
|
$this->_connection->setTitle( |
||||||
|
_('Oops!') |
||||||
|
); |
||||||
|
|
||||||
|
$this->_connection->setData( |
||||||
|
_('Could not open request') |
||||||
|
); |
||||||
|
|
||||||
|
$this->_connection->setMime( |
||||||
|
$this->_connection::MIME_TEXT_GEMINI |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
$this->_connection->setCompleted( |
||||||
|
true |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -1,338 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
declare(strict_types=1); |
|
||||||
|
|
||||||
namespace Yggverse\Yoda\Model; |
|
||||||
|
|
||||||
/* |
|
||||||
* Single response API for multiple protocol providers |
|
||||||
* |
|
||||||
*/ |
|
||||||
class Response |
|
||||||
{ |
|
||||||
// Subject |
|
||||||
private \Yggverse\Net\Address $_address; |
|
||||||
|
|
||||||
// Async status |
|
||||||
private bool $_completed = false; |
|
||||||
|
|
||||||
// Response |
|
||||||
private ?string $_title = null; |
|
||||||
private ?string $_subtitle = null; |
|
||||||
private ?string $_tooltip = null; |
|
||||||
private ?string $_mime = null; |
|
||||||
private ?string $_data = null; |
|
||||||
private ?string $_redirect = null; |
|
||||||
private ?array $_request = null; |
|
||||||
|
|
||||||
// Config |
|
||||||
public const MIME_TEXT_GEMINI = 'text/gemini'; |
|
||||||
public const MIME_TEXT_PLAIN = 'text/plain'; |
|
||||||
|
|
||||||
public function __construct( |
|
||||||
string $request, |
|
||||||
int $timeout = 5 |
|
||||||
) { |
|
||||||
// Build address instance |
|
||||||
$this->_address = new \Yggverse\Net\Address( |
|
||||||
$request |
|
||||||
); |
|
||||||
|
|
||||||
// Detect protocol |
|
||||||
switch ($this->_address->getScheme()) |
|
||||||
{ |
|
||||||
case 'file': |
|
||||||
|
|
||||||
$this->_title = basename( |
|
||||||
$this->_address->getPath() |
|
||||||
); |
|
||||||
|
|
||||||
$this->_subtitle = $this->_address->getPath(); |
|
||||||
$this->_tooltip = $this->_address->getPath(); |
|
||||||
|
|
||||||
switch (true) |
|
||||||
{ |
|
||||||
case ( |
|
||||||
$list = \Yggverse\Yoda\Model\Filesystem::getList( |
|
||||||
$this->_address->getPath() |
|
||||||
) |
|
||||||
): // directory |
|
||||||
$tree = []; |
|
||||||
|
|
||||||
foreach ($list as $item) |
|
||||||
{ |
|
||||||
$tree[] = trim( |
|
||||||
sprintf( |
|
||||||
'=> file://%s %s', |
|
||||||
$item['path'], |
|
||||||
$item['name'] . ( |
|
||||||
$item['file'] ? null : '/' |
|
||||||
) |
|
||||||
) |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
$this->_mime = self::MIME_TEXT_GEMINI; |
|
||||||
|
|
||||||
$this->_data = implode( |
|
||||||
PHP_EOL, |
|
||||||
$tree |
|
||||||
) . PHP_EOL; |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case file_exists( |
|
||||||
$this->_address->getPath() |
|
||||||
) && is_readable( |
|
||||||
$this->_address->getPath() |
|
||||||
): |
|
||||||
$this->_data = strval( |
|
||||||
file_get_contents( |
|
||||||
$this->_address->getPath() |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
$this->_mime = strval( |
|
||||||
mime_content_type( |
|
||||||
$this->_address->getPath() |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
if ($this->_mime == self::MIME_TEXT_PLAIN) |
|
||||||
{ |
|
||||||
$extension = pathinfo( |
|
||||||
strval( |
|
||||||
$this->_address->getPath() |
|
||||||
), |
|
||||||
PATHINFO_EXTENSION |
|
||||||
); |
|
||||||
|
|
||||||
if (in_array($extension, ['gmi', 'gemini'])) |
|
||||||
{ |
|
||||||
$this->_mime = self::MIME_TEXT_GEMINI; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
default: |
|
||||||
|
|
||||||
$this->_title = _( |
|
||||||
'Failure' |
|
||||||
); |
|
||||||
|
|
||||||
$this->_data = _( |
|
||||||
'Could not open location' |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
$this->_completed = true; |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case 'gemini': // @TODO async |
|
||||||
|
|
||||||
$request = new \Yggverse\Gemini\Client\Request( |
|
||||||
$this->_address->get() |
|
||||||
); |
|
||||||
|
|
||||||
$response = new \Yggverse\Gemini\Client\Response( |
|
||||||
$request->getResponse( |
|
||||||
$timeout |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
// Route status code |
|
||||||
// https://geminiprotocol.net/docs/protocol-specification.gmi#status-codes |
|
||||||
switch ($response->getCode()) |
|
||||||
{ |
|
||||||
case 10: // response expected |
|
||||||
case 11: // sensitive input |
|
||||||
|
|
||||||
$this->_mime = self::MIME_TEXT_GEMINI; |
|
||||||
|
|
||||||
$this->_request = |
|
||||||
[ |
|
||||||
'placeholder' => $response->getMeta(), |
|
||||||
'visible' => 11 !== $response->getCode() |
|
||||||
]; |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case 20: // ok |
|
||||||
|
|
||||||
$this->_data = $response->getBody(); |
|
||||||
|
|
||||||
switch (true) |
|
||||||
{ |
|
||||||
case str_contains( |
|
||||||
$response->getMeta(), |
|
||||||
self::MIME_TEXT_GEMINI |
|
||||||
): |
|
||||||
|
|
||||||
$this->_mime = self::MIME_TEXT_GEMINI; |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case str_contains( |
|
||||||
$response->getMeta(), |
|
||||||
self::MIME_TEXT_PLAIN |
|
||||||
): |
|
||||||
|
|
||||||
$this->_mime = self::MIME_TEXT_PLAIN; |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
default: |
|
||||||
|
|
||||||
throw new \Exception( |
|
||||||
sprintf( |
|
||||||
_('MIME type not implemented for %s'), |
|
||||||
$response->getMeta() |
|
||||||
) |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
$this->_completed = true; |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case 31: // redirect |
|
||||||
// show link, no follow |
|
||||||
|
|
||||||
$this->_data = sprintf( |
|
||||||
'=> %s', |
|
||||||
$response->getMeta() |
|
||||||
); |
|
||||||
|
|
||||||
$this->_mime = self::MIME_TEXT_GEMINI; |
|
||||||
|
|
||||||
$this->_completed = true; |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
default: |
|
||||||
|
|
||||||
$this->_title = _( |
|
||||||
'Oops!' |
|
||||||
); |
|
||||||
|
|
||||||
$this->_data = sprintf( |
|
||||||
'Could not open request (code: %d)', |
|
||||||
intval( |
|
||||||
$response->getCode() |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
$this->_mime = self::MIME_TEXT_GEMINI; |
|
||||||
|
|
||||||
$this->_completed = true; |
|
||||||
} |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case 'nex': // @TODO async |
|
||||||
|
|
||||||
$this->_data = ( |
|
||||||
new \Yggverse\Nex\Client |
|
||||||
)->request( |
|
||||||
$this->_address->get(), |
|
||||||
$timeout |
|
||||||
); |
|
||||||
|
|
||||||
$this->_mime = self::MIME_TEXT_PLAIN; // @TODO |
|
||||||
|
|
||||||
$this->_completed = true; |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case null: |
|
||||||
|
|
||||||
// Build gemini protocol address |
|
||||||
$address = new \Yggverse\Net\Address( |
|
||||||
sprintf( |
|
||||||
'gemini://%s', |
|
||||||
$this->_address->get() |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
// Validate hostname |
|
||||||
if (filter_var( |
|
||||||
$address->getHost(), |
|
||||||
FILTER_VALIDATE_DOMAIN, |
|
||||||
FILTER_FLAG_HOSTNAME |
|
||||||
) |
|
||||||
) { |
|
||||||
// Request redirect |
|
||||||
$this->_redirect = $address->get(); |
|
||||||
} |
|
||||||
|
|
||||||
// Request redirect to search provider |
|
||||||
else |
|
||||||
{ |
|
||||||
// @TODO custom providers |
|
||||||
$this->_redirect = sprintf( |
|
||||||
'gemini://tlgs.one/search?%s', |
|
||||||
urlencode( |
|
||||||
$request |
|
||||||
) |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
return; |
|
||||||
|
|
||||||
default: |
|
||||||
|
|
||||||
throw new \Exception( |
|
||||||
_('Protocol not supported') |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public function isCompleted(): bool |
|
||||||
{ |
|
||||||
return $this->_completed; |
|
||||||
} |
|
||||||
|
|
||||||
public function getTitle(): ?string |
|
||||||
{ |
|
||||||
return $this->_title; |
|
||||||
} |
|
||||||
|
|
||||||
public function getSubtitle(): ?string |
|
||||||
{ |
|
||||||
return $this->_subtitle; |
|
||||||
} |
|
||||||
|
|
||||||
public function getTooltip(): ?string |
|
||||||
{ |
|
||||||
return $this->_tooltip; |
|
||||||
} |
|
||||||
|
|
||||||
public function getMime(): ?string |
|
||||||
{ |
|
||||||
return $this->_mime; |
|
||||||
} |
|
||||||
|
|
||||||
public function getData(): ?string |
|
||||||
{ |
|
||||||
return $this->_data; |
|
||||||
} |
|
||||||
|
|
||||||
public function getRedirect(): ?string |
|
||||||
{ |
|
||||||
return $this->_redirect; |
|
||||||
} |
|
||||||
|
|
||||||
public function getRequest(): ?array |
|
||||||
{ |
|
||||||
return $this->_request; |
|
||||||
} |
|
||||||
|
|
||||||
public function getLength(): ?int |
|
||||||
{ |
|
||||||
return mb_strlen( |
|
||||||
$this->_data |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue