migrate therinaldos.com data
Build & Deploy to DigitalOcean Space / build (push) Failing after 2m38s

This commit is contained in:
2024-05-05 15:50:45 -04:00
commit ef1ff240d4
23182 changed files with 3801898 additions and 0 deletions
@@ -0,0 +1,432 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2022
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Tools\Traits;
use Exception;
use JsonSerializable;
use OCA\Circles\Tools\Exceptions\ArrayNotFoundException;
use OCA\Circles\Tools\Exceptions\ItemNotFoundException;
use OCA\Circles\Tools\Exceptions\MalformedArrayException;
use OCA\Circles\Tools\Exceptions\UnknownTypeException;
trait TArrayTools {
public static $TYPE_NULL = 'Null';
public static $TYPE_STRING = 'String';
public static $TYPE_ARRAY = 'Array';
public static $TYPE_BOOLEAN = 'Boolean';
public static $TYPE_INTEGER = 'Integer';
public static $TYPE_SERIALIZABLE = 'Serializable';
/**
* @param string $k
* @param array $arr
* @param string $default
*
* @return string
*/
protected function get(string $k, array $arr, string $default = ''): string {
if (!array_key_exists($k, $arr)) {
$subs = explode('.', $k, 2);
if (sizeof($subs) > 1) {
if (!array_key_exists($subs[0], $arr)) {
return $default;
}
$r = $arr[$subs[0]];
if (!is_array($r)) {
return $default;
}
return $this->get($subs[1], $r, $default);
} else {
return $default;
}
}
if ($arr[$k] === null || !is_string($arr[$k]) && (!is_int($arr[$k]))) {
return $default;
}
return (string)$arr[$k];
}
/**
* @param string $k
* @param array $arr
* @param int $default
*
* @return int
*/
protected function getInt(string $k, array $arr, int $default = 0): int {
if (!array_key_exists($k, $arr)) {
$subs = explode('.', $k, 2);
if (sizeof($subs) > 1) {
if (!array_key_exists($subs[0], $arr)) {
return $default;
}
$r = $arr[$subs[0]];
if (!is_array($r)) {
return $default;
}
return $this->getInt($subs[1], $r, $default);
} else {
return $default;
}
}
if ($arr[$k] === null) {
return $default;
}
return intval($arr[$k]);
}
/**
* @param string $k
* @param array $arr
* @param float $default
*
* @return float
*/
protected function getFloat(string $k, array $arr, float $default = 0): float {
if (!array_key_exists($k, $arr)) {
$subs = explode('.', $k, 2);
if (sizeof($subs) > 1) {
if (!array_key_exists($subs[0], $arr)) {
return $default;
}
$r = $arr[$subs[0]];
if (!is_array($r)) {
return $default;
}
return $this->getFloat($subs[1], $r, $default);
} else {
return $default;
}
}
if ($arr[$k] === null) {
return $default;
}
return intval($arr[$k]);
}
/**
* @param string $k
* @param array $arr
* @param bool $default
*
* @return bool
*/
protected function getBool(string $k, array $arr, bool $default = false): bool {
if (!array_key_exists($k, $arr)) {
$subs = explode('.', $k, 2);
if (sizeof($subs) > 1) {
if (!array_key_exists($subs[0], $arr)) {
return $default;
}
return $this->getBool($subs[1], $arr[$subs[0]], $default);
} else {
return $default;
}
}
if ($arr[$k] === null) {
return $default;
}
if (is_bool($arr[$k])) {
return $arr[$k];
}
$sk = (string)$arr[$k];
if ($sk === '1' || strtolower($sk) === 'true') {
return true;
}
if ($sk === '0' || strtolower($sk) === 'false') {
return false;
}
return $default;
}
/**
* @param string $k
* @param array $arr
* @param JsonSerializable|null $default
*
* @return mixed
*/
protected function getObj(string $k, array $arr, ?JsonSerializable $default = null): ?JsonSerializable {
if (!array_key_exists($k, $arr)) {
$subs = explode('.', $k, 2);
if (sizeof($subs) > 1) {
if (!array_key_exists($subs[0], $arr)) {
return $default;
}
return $this->getObj($subs[1], $arr[$subs[0]], $default);
} else {
return $default;
}
}
return $arr[$k];
}
/**
* @param string $k
* @param array $arr
* @param array $default
*
* @return array
*/
protected function getArray(string $k, array $arr, array $default = []): array {
if (!array_key_exists($k, $arr)) {
$subs = explode('.', $k, 2);
if (sizeof($subs) > 1) {
if (!array_key_exists($subs[0], $arr)) {
return $default;
}
$r = $arr[$subs[0]];
if (!is_array($r)) {
return $default;
}
return $this->getArray($subs[1], $r, $default);
} else {
return $default;
}
}
$r = $arr[$k];
if (!is_array($r) && !is_string($r)) {
return $default;
}
if (is_string($r)) {
$r = json_decode($r, true);
}
if (!is_array($r)) {
return $default;
}
return $r;
}
/**
* @param string $k
* @param array $arr
*
* @return bool
*/
public function validKey(string $k, array $arr): bool {
if (array_key_exists($k, $arr)) {
return true;
}
$subs = explode('.', $k, 2);
if (sizeof($subs) > 1) {
if (!array_key_exists($subs[0], $arr)) {
return false;
}
$r = $arr[$subs[0]];
if (!is_array($r)) {
return false;
}
return $this->validKey($subs[1], $r);
}
return false;
}
/**
* @param string $k
* @param array $arr
* @param array $import
* @param array $default
*
* @return array
*/
protected function getList(string $k, array $arr, array $import, array $default = []): array {
$list = $this->getArray($k, $arr, $default);
$r = [];
[$obj, $method] = $import;
foreach ($list as $item) {
try {
$o = new $obj();
$o->$method($item);
$r[] = $o;
} catch (Exception $e) {
}
}
return $r;
}
/**
* @param string $k
* @param string $value
* @param array $list
*
* @return mixed
* @throws ArrayNotFoundException
*/
protected function extractArray(string $k, string $value, array $list) {
foreach ($list as $arr) {
if (!array_key_exists($k, $arr)) {
continue;
}
if ($arr[$k] === $value) {
return $arr;
}
}
throw new ArrayNotFoundException();
}
/**
* @param string $key
* @param array $arr
* @param bool $root
*
* @return string
* @throws ItemNotFoundException
* @throws UnknownTypeException
*/
public function typeOf(string $key, array $arr, bool $root = true): string {
if (array_key_exists($key, $arr)) {
$item = $arr[$key];
if (is_null($item)) {
return self::$TYPE_NULL;
}
if (is_string($item)) {
return self::$TYPE_STRING;
}
if (is_array($item)) {
return self::$TYPE_ARRAY;
}
if (is_bool($item)) {
return self::$TYPE_BOOLEAN;
}
if (is_int($item)) {
return self::$TYPE_INTEGER;
}
if ($item instanceof JsonSerializable) {
return self::$TYPE_SERIALIZABLE;
}
throw new UnknownTypeException();
}
$subs = explode('.', $key, 2);
if (sizeof($subs) > 1) {
if (!array_key_exists($subs[0], $arr)) {
throw new ItemNotFoundException();
}
$r = $arr[$subs[0]];
if (is_array($r)) {
return $this->typeOf($subs[1], $r);
}
}
throw new ItemNotFoundException();
}
/**
* @param array $keys
* @param array $arr
*
* @throws MalformedArrayException
*/
protected function mustContains(array $keys, array $arr) {
foreach ($keys as $key) {
if (!array_key_exists($key, $arr)) {
throw new MalformedArrayException(
'source: ' . json_encode($arr) . ' - missing key: ' . $key
);
}
}
}
/**
* @param array $arr
*/
protected function cleanArray(array &$arr) {
$arr = array_filter(
$arr,
function ($v) {
if (is_string($v)) {
return ($v !== '');
}
if (is_array($v)) {
return !empty($v);
}
return true;
}
);
}
}
@@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2022
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Tools\Traits;
use JsonSerializable;
trait TAsync {
use TNCSetup;
/** @var string */
public static $SETUP_TIME_LIMIT = 'async_time_limit';
/**
* Hacky way to async the rest of the process without keeping client on hold.
*
* @param string $result
*/
public function async(string $result = ''): void {
if (ob_get_contents() !== false) {
ob_end_clean();
}
header('Connection: close');
header('Content-Encoding: none');
ignore_user_abort();
$timeLimit = $this->setupInt(self::$SETUP_TIME_LIMIT);
set_time_limit(($timeLimit > 0) ? $timeLimit : 0);
ob_start();
echo($result);
$size = ob_get_length();
header('Content-Length: ' . $size);
ob_end_flush();
flush();
}
/**
* @param JsonSerializable $obj
*/
public function asyncObj(JsonSerializable $obj): void {
$this->async(json_encode($obj));
}
}
@@ -0,0 +1,141 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2022
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Tools\Traits;
use OCA\Circles\Tools\Model\TreeNode;
use Symfony\Component\Console\Output\ConsoleOutput;
trait TConsoleTree {
/**
* @param TreeNode $root
* @param callable $method
* @param array $config
*/
public function drawTree(
TreeNode $root,
callable $method,
array $config = [
'height' => 1,
'node-spacing' => 0,
'item-spacing' => 0,
]
): void {
$config = array_merge(
[
'height' => 1,
'node-spacing' => 0,
'item-spacing' => 0
], $config
);
$output = new ConsoleOutput();
$prec = 0;
while (true) {
$node = $root->current();
if ($node === null) {
return;
}
$path = $node->getPath();
array_pop($path);
$line = $empty = $spacing = '';
$p = 0;
foreach ($path as $k => $i) {
$line .= ' ';
$empty .= ' ';
if ($k === array_key_last($path)) {
if ($i->haveNext()) {
$line .= '├';
$empty .= '│';
} else {
$line .= '└';
$empty .= ' ';
}
$line .= '── ';
$empty .= ' ';
} else {
if ($i->haveNext()) {
$line .= '│';
$empty .= '│';
} else {
$line .= ' ';
$empty .= ' ';
}
$line .= ' ';
$empty .= ' ';
}
$p++;
}
if ($p < $prec) {
for ($i = 0; $i < $config['node-spacing']; $i++) {
$spacing = substr($empty, 0, -3);
if (substr($spacing, -1) === ' ') {
$spacing = substr($spacing, 0, -1) . '│';
}
$output->writeln($spacing);
}
}
$prec = $p;
for ($i = 1; $i <= $config['height']; $i++) {
$draw = $method($node->getItem(), $i);
if ($draw === '') {
continue;
}
if ($i === 1) {
$output->write($line);
} else {
$output->write($empty);
}
$output->writeln($draw);
}
if ($node->haveNext()) {
$empty .= ' │';
}
if (!$node->isSplited() && $node->haveNext()) {
for ($i = 0; $i < $config['node-spacing']; $i++) {
$output->writeln($empty);
}
}
for ($i = 0; $i < $config['item-spacing']; $i++) {
$output->writeln($empty);
}
}
}
}
@@ -0,0 +1,141 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2022
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Tools\Traits;
use JsonSerializable;
use OCA\Circles\Tools\Exceptions\InvalidItemException;
use OCA\Circles\Tools\IDeserializable;
trait TDeserialize {
/**
* @param JsonSerializable $model
*
* @return array
*/
public function serialize(JsonSerializable $model): array {
return json_decode(json_encode($model), true);
}
/**
* @param array $data
*
* @return array
*/
public function serializeArray(array $data): array {
return json_decode(json_encode($data), true);
}
/**
* @param array $data
* @param string $class
*
* @return IDeserializable
* @throws InvalidItemException
*/
public function deserialize(array $data, string $class): IDeserializable {
if ($class instanceof IDeserializable) {
throw new InvalidItemException(get_class($class) . ' does not implement IDeserializable');
}
/** @var IDeserializable $item */
$item = new $class;
$item->import($data);
return $item;
}
/**
* @param array $data
* @param string $class
* @param bool $associative
*
* @return IDeserializable[]
*/
public function deserializeArray(array $data, string $class, bool $associative = false): array {
$arr = [];
foreach ($data as $key => $entry) {
if (!is_array($entry)) {
continue;
}
try {
if ($associative) {
$arr[$key] = $this->deserialize($entry, $class);
} else {
$arr[] = $this->deserialize($entry, $class);
}
} catch (InvalidItemException $e) {
}
}
return $arr;
}
/**
* @param string $json
* @param string $class
*
* @return IDeserializable[]
* @throws InvalidItemException
*/
public function deserializeList(string $json, string $class): array {
$arr = [];
$data = json_decode($json, true);
if (!is_array($data)) {
return $arr;
}
foreach ($data as $entry) {
$arr[] = $this->deserialize($entry, $class);
}
return $arr;
}
/**
* @param string $json
* @param string $class
*
* @return IDeserializable
* @throws InvalidItemException
*/
public function deserializeJson(string $json, string $class): IDeserializable {
$data = json_decode($json, true);
return $this->deserialize($data, $class);
}
}
@@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2022
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Tools\Traits;
use OC;
use OCA\Circles\Tools\Exceptions\SignatoryException;
use OCA\Circles\Tools\Model\NCSignatory;
use OCP\IConfig;
trait TNCLocalSignatory {
use TNCSignatory;
public static $SIGNATORIES_APP = 'signatories';
/**
* @param NCSignatory $signatory
* @param bool $generate
*
* @throws SignatoryException
*/
public function fillSimpleSignatory(NCSignatory $signatory, bool $generate = false): void {
$app = $this->setup('app', '', self::$SIGNATORIES_APP);
$signatories = json_decode(OC::$server->get(IConfig::class)->getAppValue($app, 'key_pairs'), true);
if (!is_array($signatories)) {
$signatories = [];
}
$sign = $this->getArray($signatory->getId(), $signatories);
if (!empty($sign)) {
$signatory->setKeyId($this->get('keyId', $sign))
->setKeyOwner($this->get('keyOwner', $sign))
->setPublicKey($this->get('publicKey', $sign))
->setPrivateKey($this->get('privateKey', $sign));
return;
}
if (!$generate) {
throw new SignatoryException('signatory not found');
}
$this->createSimpleSignatory($signatory);
}
/**
* @param NCSignatory $signatory
*/
public function createSimpleSignatory(NCSignatory $signatory): void {
$app = $this->setup('app', '', self::$SIGNATORIES_APP);
$signatory->setKeyId($signatory->getId() . '#main-key');
$signatory->setKeyOwner($signatory->getId());
$this->generateKeys($signatory);
$signatories =
json_decode(OC::$server->get(IConfig::class)->getAppValue($app, 'key_pairs', '[]'), true);
$signatories[$signatory->getId()] = [
'keyId' => $signatory->getKeyId(),
'keyOwner' => $signatory->getKeyOwner(),
'publicKey' => $signatory->getPublicKey(),
'privateKey' => $signatory->getPrivateKey()
];
OC::$server->get(IConfig::class)->setAppValue($app, 'key_pairs', json_encode($signatories));
}
/**
* @param NCSignatory $signatory
*/
public function removeSimpleSignatory(NCSignatory $signatory): void {
$app = $this->setup('app', '', self::$SIGNATORIES_APP);
$signatories = json_decode(OC::$server->get(IConfig::class)->getAppValue($app, 'key_pairs'), true);
if (!is_array($signatories)) {
$signatories = [];
}
unset($signatories[$signatory->getId()]);
OC::$server->get(IConfig::class)->setAppValue($app, 'key_pairs', json_encode($signatories));
}
}
@@ -0,0 +1,201 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2022
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Tools\Traits;
use Exception;
use OC;
use OC\HintException;
use Psr\Log\LoggerInterface;
use Throwable;
trait TNCLogger {
use TNCSetup;
public static $EMERGENCY = 4;
public static $ALERT = 3;
public static $CRITICAL = 3;
public static $ERROR = 3;
public static $WARNING = 2;
public static $NOTICE = 1;
public static $INFO = 1;
public static $DEBUG = 0;
/**
* @param Throwable $t
* @param array $serializable
*/
public function t(Throwable $t, array $serializable = []): void {
$this->throwable($t, self::$ERROR, $serializable);
}
/**
* @param Throwable $t
* @param int $level
* @param array $serializable
*/
public function throwable(Throwable $t, int $level = 3, array $serializable = []): void {
$message = '';
if (!empty($serializable)) {
$message = json_encode($serializable);
}
$this->logger()
->log(
$level,
$message,
[
'app' => $this->setup('app'),
'exception' => $t
]
);
}
/**
* @param Exception $e
* @param array $serializable
*/
public function e(Exception $e, array $serializable = []): void {
$this->exception($e, self::$ERROR, $serializable);
}
/**
* @param Exception $e
* @param int|array $level
* @param array $serializable
*/
public function exception(Exception $e, $level = 3, array $serializable = []): void {
if (is_array($level) && empty($serializable)) {
$serializable = $level;
$level = 3;
}
$message = '';
if (!empty($serializable)) {
$message = json_encode($serializable);
}
if ($level === self::$DEBUG) {
$level = (int)$this->appConfig('debug_level');
}
$this->logger()
->log(
$level,
$message,
[
'app' => $this->setup('app'),
'exception' => $e
]
);
}
/**
* @param string $message
* @param bool $trace
* @param array $serializable
*/
public function emergency(string $message, bool $trace = false, array $serializable = []): void {
$this->log(self::$EMERGENCY, '[emergency] ' . $message, $trace, $serializable);
}
/**
* @param string $message
* @param bool $trace
* @param array $serializable
*/
public function alert(string $message, bool $trace = false, array $serializable = []): void {
$this->log(self::$ALERT, '[alert] ' . $message, $trace, $serializable);
}
/**
* @param string $message
* @param bool $trace
* @param array $serializable
*/
public function warning(string $message, bool $trace = false, array $serializable = []): void {
$this->log(self::$WARNING, '[warning] ' . $message, $trace, $serializable);
}
/**
* @param string $message
* @param bool $trace
* @param array $serializable
*/
public function notice(string $message, bool $trace = false, array $serializable = []): void {
$this->log(self::$NOTICE, '[notice] ' . $message, $trace, $serializable);
}
/**
* @param string $message
* @param array $serializable
*/
public function debug(string $message, array $serializable = []): void {
$message = '[debug] ' . $message;
$debugLevel = (int)$this->appConfig('debug_level');
$this->log($debugLevel, $message, ($this->appConfig('debug_trace') === '1'), $serializable);
}
/**
* @param int $level
* @param string $message
* @param bool $trace
* @param array $serializable
*/
public function log(int $level, string $message, bool $trace = false, array $serializable = []): void {
$opts = ['app' => $this->setup('app')];
if ($trace) {
$opts['exception'] = new HintException($message, json_encode($serializable));
} elseif (!empty($serializable)) {
$message .= ' -- ' . json_encode($serializable);
}
$this->logger()
->log($level, $message, $opts);
}
/**
* @return LoggerInterface
*/
public function logger(): LoggerInterface {
if (isset($this->logger) && $this->logger instanceof LoggerInterface) {
return $this->logger;
} else {
return OC::$server->get(LoggerInterface::class);
}
}
}
@@ -0,0 +1,178 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2022
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Tools\Traits;
use OCA\Circles\Tools\Model\NCRequest;
use Exception;
use GuzzleHttp\Exception\ClientException;
use OC;
use OCA\Circles\Tools\Exceptions\RequestNetworkException;
use OCA\Circles\Tools\Model\NCRequestResult;
use OCA\Circles\Tools\Model\Request;
use OCP\Http\Client\IClientService;
use OCP\Http\Client\IResponse;
trait TNCRequest {
use TNCLogger;
/**
* @param int $size
*/
public function setMaxDownloadSize(int $size) {
}
/**
* @param NCRequest $request
*
* @return array
* @throws RequestNetworkException
*/
public function retrieveJson(NCRequest $request): array {
$this->doRequest($request);
$requestResult = $request->getResult();
return $requestResult->getAsArray();
}
/**
* @param NCRequest $request
* @param bool $exceptionOnIssue
*
* @throws RequestNetworkException
*/
public function doRequest(NCRequest $request, bool $exceptionOnIssue = true): void {
$request->setClient(
$this->clientService()
->newClient()
);
$this->generationClientOptions($request);
$this->debug('doRequest initiated', ['request' => $request]);
foreach ($request->getProtocols() as $protocol) {
$request->setUsedProtocol($protocol);
try {
$response = $this->useClient($request);
$request->setResult(new NCRequestResult($response));
break;
} catch (ClientException $e) {
$request->setResult(new NCRequestResult(null, $e));
} catch (Exception $e) {
$this->exception($e, self::$DEBUG, ['request' => $request]);
}
}
$this->debug('doRequest done', ['request' => $request]);
if ($exceptionOnIssue && (!$request->hasResult() || $request->getResult()->hasException())) {
throw new RequestNetworkException();
}
}
/**
* @return IClientService
*/
public function clientService(): IClientService {
if (isset($this->clientService) && $this->clientService instanceof IClientService) {
return $this->clientService;
} else {
return OC::$server->get(IClientService::class);
}
}
/**
* @param NCRequest $request
*/
private function generationClientOptions(NCRequest $request) {
$options = [
'headers' => $request->getHeaders(),
'cookies' => $request->getCookies(),
'verify' => $request->isVerifyPeer(),
'timeout' => $request->getTimeout(),
'http_errors' => !$request->isHttpErrorsAllowed()
];
if (!empty($request->getData())) {
$options['body'] = $request->getDataBody();
}
if (!empty($request->getParams())) {
$options['form_params'] = $request->getParams();
}
if ($request->isLocalAddressAllowed()) {
$options['nextcloud']['allow_local_address'] = true;
}
if ($request->isFollowLocation()) {
$options['allow_redirects'] = [
'max' => 10,
'strict' => true,
'referer' => true,
];
} else {
$options['allow_redirects'] = false;
}
$request->setClientOptions($options);
}
/**
* @param NCRequest $request
*
* @return IResponse
* @throws Exception
*/
private function useClient(NCRequest $request): IResponse {
$client = $request->getClient();
switch ($request->getType()) {
case Request::TYPE_POST:
return $client->post($request->getCompleteUrl(), $request->getClientOptions());
case Request::TYPE_PUT:
return $client->put($request->getCompleteUrl(), $request->getClientOptions());
case Request::TYPE_DELETE:
return $client->delete($request->getCompleteUrl(), $request->getClientOptions());
case Request::TYPE_GET:
return $client->get(
$request->getCompleteUrl() . $request->getQueryString(), $request->getClientOptions()
);
default:
throw new Exception('unknown request type ' . json_encode($request));
}
}
}
@@ -0,0 +1,107 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2022
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Tools\Traits;
use OC;
use OCP\IConfig;
trait TNCSetup {
use TArrayTools;
/** @var array */
private $_setup = [];
/**
* @param string $key
* @param string $value
*
* @param string $default
*
* @return string
*/
public function setup(string $key, string $value = '', string $default = ''): string {
if ($value !== '') {
$this->_setup[$key] = $value;
}
return $this->get($key, $this->_setup, $default);
}
/**
* @param string $key
* @param array $value
* @param array $default
*
* @return array
*/
public function setupArray(string $key, array $value = [], array $default = []): array {
if (!empty($value)) {
$this->_setup[$key] = $value;
}
return $this->getArray($key, $this->_setup, $default);
}
/**
* @param string $key
* @param int $value
* @param int $default
*
* @return int
*/
public function setupInt(string $key, int $value = -999, int $default = 0): int {
if ($value !== -999) {
$this->_setup[$key] = $value;
}
return $this->getInt($key, $this->_setup, $default);
}
/**
* @param string $key
*
* @return string
*/
public function appConfig(string $key): string {
$app = $this->setup('app');
if ($app === '') {
return '';
}
/** @var IConfig $config */
$config = OC::$server->get(IConfig::class);
return $config->getAppValue($app, $key, '');
}
}
@@ -0,0 +1,207 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2022
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Tools\Traits;
use OCA\Circles\Tools\Exceptions\InvalidOriginException;
use OCA\Circles\Tools\Exceptions\RequestNetworkException;
use OCA\Circles\Tools\Exceptions\SignatoryException;
use OCA\Circles\Tools\Exceptions\SignatureException;
use OCA\Circles\Tools\Model\NCRequest;
use OCA\Circles\Tools\Model\NCSignatory;
trait TNCSignatory {
use TNCRequest;
/**
* return Signatory by its Id from cache or from direct request.
* Should be overwritten.
*
* @param string $keyId
* @param bool $refresh
*
* @return NCSignatory
* @throws SignatoryException
*/
public function retrieveSignatory(string $keyId, bool $refresh = false): NCSignatory {
if (!$refresh) {
throw new SignatoryException();
}
$signatory = new NCSignatory($keyId);
$this->downloadSignatory($signatory, $keyId);
return $signatory;
}
/**
* @param NCSignatory $signatory
* @param string $keyId
* @param array $params
* @param NCRequest|null $request
*
* @throws SignatoryException
*/
public function downloadSignatory(
NCSignatory $signatory,
string $keyId = '',
array $params = [],
?NCRequest $request = null
): void {
if (is_null($request)) {
$request = new NCRequest();
$request->setFollowLocation(true);
$request->setTimeout(5);
}
$request->basedOnUrl(($keyId !== '') ? $keyId : $signatory->getId());
$request->setParams($params);
$request->addHeader('Accept', 'application/ld+json');
try {
$this->updateSignatory($signatory, $this->retrieveJson($request), $keyId);
} catch (RequestNetworkException $e) {
$this->debug('network issue while downloading Signatory', ['request' => $request]);
throw new SignatoryException('network issue: ' . $e->getMessage());
}
}
/**
* @param NCSignatory $signatory
* @param array $json
* @param string $keyId
*
* @throws SignatoryException
*/
public function updateSignatory(NCSignatory $signatory, array $json, string $keyId = ''): void {
$signatory->setOrigData($json)
->import($json);
if ($keyId === '') {
$keyId = $signatory->getKeyId();
}
try {
if (($signatory->getId() !== $keyId && $signatory->getKeyId() !== $keyId)
|| $signatory->getId() !== $signatory->getKeyOwner()
|| $this->getKeyOrigin($signatory->getKeyId()) !== $this->getKeyOrigin($signatory->getId())
|| $signatory->getPublicKey() === '') {
$this->debug('invalid format', ['signatory' => $signatory, 'keyId' => $keyId]);
throw new SignatoryException('invalid format');
}
} catch (InvalidOriginException $e) {
throw new SignatoryException('invalid origin');
}
}
/**
* @param string $keyId
*
* @return string
* @throws InvalidOriginException
*/
public function getKeyOrigin(string $keyId) {
$host = parse_url($keyId, PHP_URL_HOST);
if (is_string($host) && ($host !== '')) {
return $host;
}
throw new InvalidOriginException('cannot retrieve origin from ' . $keyId);
}
/**
* @param NCSignatory $signatory
* @param string $digest
* @param int $bits
* @param int $type
*/
public function generateKeys(
NCSignatory $signatory,
string $digest = 'rsa',
int $bits = 2048,
int $type = OPENSSL_KEYTYPE_RSA
) {
$res = openssl_pkey_new(
[
'digest_alg' => $digest,
'private_key_bits' => $bits,
'private_key_type' => $type,
]
);
openssl_pkey_export($res, $privateKey);
$publicKey = openssl_pkey_get_details($res)['key'];
$signatory->setPublicKey($publicKey);
$signatory->setPrivateKey($privateKey);
}
/**
* @param string $clear
* @param NCSignatory $signatory
*
* @return string
* @throws SignatoryException
*/
public function signString(string $clear, NCSignatory $signatory): string {
$privateKey = $signatory->getPrivateKey();
if ($privateKey === '') {
throw new SignatoryException('empty private key');
}
openssl_sign($clear, $signed, $privateKey, $this->getOpenSSLAlgo($signatory));
return base64_encode($signed);
}
/**
* @param string $clear
* @param string $signed
* @param string $publicKey
* @param string $algo
*
* @throws SignatureException
*/
public function verifyString(
string $clear, string $signed, string $publicKey, string $algo = NCSignatory::SHA256
) {
if (openssl_verify($clear, $signed, $publicKey, $algo) !== 1) {
throw new SignatureException('signature issue');
}
}
}
@@ -0,0 +1,128 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2022
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Tools\Traits;
use OCA\Circles\Tools\Exceptions\RequestNetworkException;
use OCA\Circles\Tools\Exceptions\WellKnownLinkNotFoundException;
use OCA\Circles\Tools\Model\NCRequest;
use OCA\Circles\Tools\Model\NCWebfinger;
use OCA\Circles\Tools\Model\NCWellKnownLink;
use OCA\Circles\Tools\Model\SimpleDataStore;
trait TNCWellKnown {
use TNCRequest;
public static $WEBFINGER = '/.well-known/webfinger';
/**
* @param string $host
* @param string $subject
* @param string $rel
*
* @return SimpleDataStore
* @throws RequestNetworkException
* @throws WellKnownLinkNotFoundException
*/
public function getResourceData(string $host, string $subject, string $rel): SimpleDataStore {
$link = $this->getLink($host, $subject, $rel);
$request = new NCRequest('');
$request->basedOnUrl($link->getHref());
$request->addHeader('Accept', $link->getType());
$request->setFollowLocation(true);
$request->setLocalAddressAllowed(true);
$request->setTimeout(5);
$data = $this->retrieveJson($request);
return new SimpleDataStore($data);
}
/**
* @param string $host
* @param string $subject
* @param string $rel
*
* @return NCWellKnownLink
* @throws RequestNetworkException
* @throws WellKnownLinkNotFoundException
*/
public function getLink(string $host, string $subject, string $rel): NCWellKnownLink {
return $this->extractLink($rel, $this->getWebfinger($host, $subject));
}
/**
* @param string $host
* @param string $subject
* @param string $rel
*
* @return NCWebfinger
* @throws RequestNetworkException
*/
public function getWebfinger(string $host, string $subject, string $rel = ''): NCWebfinger {
$request = new NCRequest(self::$WEBFINGER);
$request->setHost($host);
$request->setProtocols(['https', 'http']);
$request->setFollowLocation(true);
$request->setLocalAddressAllowed(true);
$request->setTimeout(5);
$request->addParam('resource', $subject);
if ($rel !== '') {
$request->addParam('rel', $rel);
}
$result = $this->retrieveJson($request);
return new NCWebfinger($result);
}
/**
* @param string $rel
* @param NCWebfinger $webfinger
*
* @return NCWellKnownLink
* @throws WellKnownLinkNotFoundException
*/
public function extractLink(string $rel, NCWebfinger $webfinger): NCWellKnownLink {
foreach ($webfinger->getLinks() as $link) {
if ($link->getRel() === $rel) {
return $link;
}
}
throw new WellKnownLinkNotFoundException();
}
}
@@ -0,0 +1,271 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2022
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Tools\Traits;
use DateTime;
use Exception;
trait TStringTools {
use TArrayTools;
/**
* @param int $length
*
* @return string
*/
protected function token(int $length = 15): string {
$chars = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890';
$str = '';
$max = strlen($chars);
for ($i = 0; $i < $length; $i++) {
try {
$str .= $chars[random_int(0, $max - 2)];
} catch (Exception $e) {
}
}
return $str;
}
/**
* Generate uuid: 2b5a7a87-8db1-445f-a17b-405790f91c80
*
* @param int $length
*
* @return string
*/
protected function uuid(int $length = 0): string {
$uuid = sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff),
mt_rand(0, 0xffff), mt_rand(0, 0xfff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000,
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
if ($length > 0) {
if ($length <= 16) {
$uuid = str_replace('-', '', $uuid);
}
$uuid = substr($uuid, 0, $length);
}
return $uuid;
}
/**
* @param string $uuid
*
* @return bool
*/
protected function isUuid(string $uuid): bool {
if ($uuid === '') {
return false;
}
return (preg_match('/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i', $uuid) === 1);
}
/**
* @param string $line
* @param int $length
*
* @return string
*/
protected function cut(string $line, int $length): string {
if (strlen($line) < $length) {
return $line;
}
return substr($line, 0, $length - 5) . ' (..)';
}
/**
* @param string $str1
* @param string $str2
* @param bool $cs case sensitive ?
*
* @return string
*/
protected function commonPart(string $str1, string $str2, bool $cs = true): string {
for ($i = 0; $i < strlen($str1) && $i < strlen($str2); $i++) {
$chr1 = $str1[$i];
$chr2 = $str2[$i];
if (!$cs) {
$chr1 = strtolower($chr1);
$chr2 = strtolower($chr2);
}
if ($chr1 !== $chr2) {
break;
}
}
return substr($str1, 0, $i);
}
/**
* @param string $line
* @param array $params
*
* @return string
*/
protected function feedStringWithParams(string $line, array $params): string {
$ak = array_keys($params);
foreach ($ak as $k) {
$line = str_replace('{' . $k . '}', (string)$params[$k], $line);
}
return $line;
}
/**
* @param int $words
*
* @return string
*/
public function generateRandomSentence(int $words = 5): string {
$sentence = [];
for ($i = 0; $i < $words; $i++) {
$sentence[] = $this->generateRandomWord(rand(2, 12));
}
return implode(' ', $sentence);
}
/**
* @param int $length
*
* @return string
*/
public function generateRandomWord(int $length = 8): string {
$c = ['b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'r', 's', 't', 'v'];
$v = ['a', 'e', 'i', 'o', 'u', 'y'];
$word = [];
for ($i = 0; $i <= ($length / 2); $i++) {
$word[] = $c[array_rand($c)];
$word[] = $v[array_rand($v)];
}
return implode('', $word);
}
/**
* @param int $bytes
*
* @return string
*/
public function humanReadable(int $bytes): string {
if ($bytes == 0) {
return '0.00 B';
}
$s = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
$e = floor(log($bytes, 1024));
return round($bytes / pow(1024, $e), 2) . ' ' . $s[$e];
}
/**
* @param int $first
* @param int $second
* @param bool $short
*
* @return string
* @throws Exception
*/
public function getDateDiff(
int $first,
int $second = 0,
bool $short = false,
array $words = []
): string {
if ($second === 0) {
$first = time() - $first;
$second = time();
}
$f = new DateTime('@' . $first);
$s = new DateTime('@' . $second);
$duration = $second - $first;
if ($short) {
$minutes = $this->get('minutes', $words, 'M');
$hours = $this->get('hours', $words, 'H');
$days = $this->get('days', $words, 'D');
if ($duration < 60) {
return $f->diff($s)->format('<1' . $minutes);
}
if ($duration < 3600) {
return $f->diff($s)->format('%i' . $minutes);
}
if ($duration < 86400) {
return $f->diff($s)->format('%h' . $hours . ', %i' . $minutes);
}
return $f->diff($s)->format('%a' . $days . ', %h' . $hours . ', %i' . $minutes);
}
$seconds = $this->get('seconds', $words, 'seconds');
$minutes = $this->get('minutes', $words, 'minutes');
$hours = $this->get('hours', $words, 'hours');
$days = $this->get('days', $words, 'days');
if ($duration < 60) {
return $f->diff($s)->format('%s ' . $seconds);
}
if ($duration < 3600) {
return $f->diff($s)->format('%i ' . $minutes . ' and %s ' . $seconds);
}
if ($duration < 86400) {
return $f->diff($s)->format('%h ' . $hours . ', %i ' . $minutes . ' and %s ' . $seconds);
}
return $f->diff($s)->format(
'%a ' . $days .
', %h ' . $hours .
', %i ' . $minutes .
' and %s ' . $seconds
);
}
}