diff --git a/appinfo/database.xml b/appinfo/database.xml
index 1a615ac0..fe39af89 100644
--- a/appinfo/database.xml
+++ b/appinfo/database.xml
@@ -64,6 +64,7 @@
true
+ // @deprecated
service_id
integer
@@ -77,30 +78,41 @@
63
+
+ username
+ text
+ 63
+
+
+ // @deprecated
account
text
127
+ // @deprecated
account_id
integer
7
+ // @deprecated
status
integer
1
+ // @deprecated
auth
text
2000
+ // @deprecated
config
text
@@ -115,5 +127,261 @@
+
+ *dbprefix*social_server_actors
+
+
+
+ id
+ integer
+ 7
+ true
+ true
+ true
+ true
+
+
+
+ type
+ text
+ 15
+ true
+
+
+
+ user_id
+ text
+ 63
+ true
+
+
+
+ preferred_username
+ text
+ 127
+ true
+
+
+
+ public_key
+ text
+ 1000
+
+
+
+ private_key
+ text
+ 2000
+
+
+
+ creation
+ timestamp
+
+
+
+
+
+
+ *dbprefix*social_server_activities
+
+
+
+ id
+ text
+ 127
+ true
+
+
+
+ activity
+ text
+ 6000
+ true
+
+
+
+ object
+ text
+ 127
+ true
+
+
+
+
+
+
+ *dbprefix*social_server_notes
+
+
+
+ id
+ text
+ 127
+ true
+
+
+
+
+
+
+
+
+
+
+ to
+ text
+ 127
+ true
+
+
+
+ to_array
+ text
+ 2000
+ true
+
+
+
+ cc
+ text
+ 127
+ true
+
+
+
+ bcc
+ text
+ 127
+ true
+
+
+
+ content
+ text
+ 3000
+ true
+
+
+
+ summary
+ text
+ 255
+ true
+
+
+
+ published
+ text
+ 31
+ true
+
+
+
+ attributed_to
+ text
+ 127
+
+
+
+ in_reply_to
+ text
+ 127
+
+
+
+ creation
+ timestamp
+
+
+
+
+
+
+ *dbprefix*social_cache_actors
+
+
+
+ id
+ integer
+ 8
+ true
+ true
+ true
+ true
+
+
+
+ account
+ text
+ 127
+ true
+
+
+
+ url
+ text
+ 127
+ true
+
+
+
+ actor
+ text
+ 8000
+ true
+
+
+
+ creation
+ timestamp
+
+
+
+
+
+
+ *dbprefix*social_cache_actors
+
+
+
+ id
+ integer
+ 8
+ true
+ true
+ true
+ true
+
+
+
+ account
+ text
+ 127
+ true
+
+
+
+ url
+ text
+ 127
+ true
+
+
+
+ actor
+ text
+ 8000
+ true
+
+
+
+ creation
+ timestamp
+
+
+
+
+
diff --git a/appinfo/info.xml b/appinfo/info.xml
index 8c9e1088..058e8160 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -5,7 +5,7 @@
Social
🎉 Nextcloud becomes part of the federated social networks!
- 0.0.10
+ 0.0.17
agpl
Maxence Lange
Julius Härtl
@@ -26,4 +26,8 @@
+
+ OCA\Social\Command\NoteCreate
+
+
diff --git a/appinfo/routes.php b/appinfo/routes.php
index a5276275..20642577 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -39,6 +39,22 @@ return [
[
'name' => 'OAuth2#setCode', 'url' => '/client/oauth2/redirect/{serviceId}/',
'verb' => 'GET'
- ]
- ]
+ ],
+
+ ['name' => 'Account#create', 'url' => '/local/account/{username}', 'verb' => 'POST'],
+
+ ['name' => 'ActivityPub#sharedInbox', 'url' => '/inbox', 'verb' => 'POST'],
+ ['name' => 'ActivityPub#actor', 'url' => '/users/{username}', 'verb' => 'GET'],
+ ['name' => 'ActivityPub#aliasactor', 'url' => '/@{username}', 'verb' => 'GET'],
+ ['name' => 'ActivityPub#inbox', 'url' => '/@{username}/inbox', 'verb' => 'POST'],
+ ['name' => 'ActivityPub#outbox', 'url' => '/@{username}/outbox', 'verb' => 'POST'],
+ ['name' => 'ActivityPub#followers', 'url' => '/@{username}/followers', 'verb' => 'GET'],
+ ['name' => 'ActivityPub#following', 'url' => '/@{username}/following', 'verb' => 'GET'],
+
+ ['name' => 'SocialPub#displayPost', 'url' => '/@{username}/{postId}', 'verb' => 'GET']
+,
+ ['name' => 'ActivityPub#test', 'url' => '/inbox/{username}', 'verb' => 'POST'],
+
+
+]
];
diff --git a/lib/Command/NoteCreate.php b/lib/Command/NoteCreate.php
new file mode 100644
index 00000000..48c1193f
--- /dev/null
+++ b/lib/Command/NoteCreate.php
@@ -0,0 +1,150 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+
+namespace OCA\Social\Command;
+
+use Exception;
+use OC\Core\Command\Base;
+use OCA\Social\Service\ActivityPub\NoteService;
+use OCA\Social\Service\ActivityPubService;
+use OCA\Social\Service\ActorService;
+use OCA\Social\Service\ConfigService;
+use OCA\Social\Service\CurlService;
+use OCA\Social\Service\MiscService;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+
+class NoteCreate extends Base {
+
+ /** @var ConfigService */
+ private $configService;
+
+ /** @var ActivityPubService */
+ private $activityPubService;
+
+ /** @var ActorService */
+ private $actorService;
+
+ /** @var NoteService */
+ private $noteService;
+
+ /** @var CurlService */
+ private $curlService;
+
+ /** @var MiscService */
+ private $miscService;
+
+
+ /**
+ * Index constructor.
+ *
+ * @param ActivityPubService $activityPubService
+ * @param ActorService $actorService
+ * @param NoteService $noteService
+ * @param CurlService $curlService
+ * @param ConfigService $configService
+ * @param MiscService $miscService
+ */
+ public function __construct(
+ ActivityPubService $activityPubService, ActorService $actorService,
+ NoteService $noteService, CurlService $curlService,
+ ConfigService $configService, MiscService $miscService
+ ) {
+ parent::__construct();
+
+ $this->activityPubService = $activityPubService;
+ $this->actorService = $actorService;
+ $this->noteService = $noteService;
+ $this->curlService = $curlService;
+ $this->configService = $configService;
+ $this->miscService = $miscService;
+ }
+
+
+ /**
+ *
+ */
+ protected function configure() {
+ parent::configure();
+ $this->setName('social:note:create')
+ ->addOption(
+ 'replyTo', 'r', InputOption::VALUE_OPTIONAL, 'in reply to an existing thread'
+ )
+ ->addOption(
+ 'to', 't', InputOption::VALUE_OPTIONAL, 'to (default Public)'
+ )
+ ->addArgument('userid', InputArgument::REQUIRED, 'userId of the author')
+ ->addArgument('content', InputArgument::REQUIRED, 'content of the post')
+ ->setDescription('Create a new note');
+ }
+
+
+ /**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ *
+ * @throws Exception
+ */
+ protected function execute(InputInterface $input, OutputInterface $output) {
+
+ $userId = $input->getArgument('userid');
+ $content = $input->getArgument('content');
+ $to = $input->getOption('to');
+ $replyTo = $input->getOption('replyTo');
+
+ $note = $this->noteService->generateNote($userId, $content, ActivityPubService::TO_PUBLIC);
+
+ if ($to !== null) {
+ $note->setTo($to);
+ $note->addTag(
+ [
+ 'type' => 'Mention',
+ 'href' => $to
+ ]
+ );
+ }
+
+
+ if ($replyTo !== null) {
+ $note->setInReplyTo($replyTo);
+ }
+
+ $result = $this->activityPubService->createActivity($userId, $note, $activity);
+
+ echo 'object: ' . json_encode($activity, JSON_PRETTY_PRINT) . "\n";
+ echo 'result: ' . json_encode($result, JSON_PRETTY_PRINT) . "\n";
+
+ }
+
+}
+
diff --git a/lib/Controller/AccountController.php b/lib/Controller/AccountController.php
new file mode 100644
index 00000000..d68c0cca
--- /dev/null
+++ b/lib/Controller/AccountController.php
@@ -0,0 +1,106 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Controller;
+
+use daita\Traits\TArrayTools;
+use daita\Traits\TNCDataResponse;
+use Exception;
+use OCA\Social\AppInfo\Application;
+use OCA\Social\Service\ActivityPubService;
+use OCA\Social\Service\ActivityStreamsService;
+use OCA\Social\Service\ActorService;
+use OCA\Social\Service\ConfigService;
+use OCA\Social\Service\MiscService;
+use OCA\Social\Service\ServiceAccountsService;
+use OCA\Social\Service\ServicesService;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\DataResponse;
+use OCP\IRequest;
+
+
+class AccountController extends Controller {
+
+ use TNCDataResponse;
+
+ /** @var string */
+ private $userId;
+
+ /** @var ConfigService */
+ private $configService;
+
+ /** @var ActorService */
+ private $actorService;
+
+ /** @var MiscService */
+ private $miscService;
+
+
+ /**
+ * AccountController constructor.
+ *
+ * @param IRequest $request
+ * @param string $userId
+ * @param ConfigService $configService
+ * @param ActorService $actorService
+ * @param MiscService $miscService
+ */
+ public function __construct(
+ IRequest $request, string $userId, ConfigService $configService,
+ ActorService $actorService, MiscService $miscService
+ ) {
+ parent::__construct(Application::APP_NAME, $request);
+
+ $this->userId = $userId;
+ $this->configService = $configService;
+ $this->actorService = $actorService;
+ $this->miscService = $miscService;
+ }
+
+
+ /**
+ * @NoAdminRequired
+ *
+ * @param array $data
+ *
+ * @return DataResponse
+ */
+ public function create(string $username): DataResponse {
+ try {
+ $this->actorService->createActor($this->userId, $username);
+
+ return $this->success([]);
+ } catch (Exception $e) {
+ return $this->fail($e->getMessage());
+ }
+ }
+
+
+}
+
diff --git a/lib/Controller/ActivityPubController.php b/lib/Controller/ActivityPubController.php
new file mode 100644
index 00000000..083ddecb
--- /dev/null
+++ b/lib/Controller/ActivityPubController.php
@@ -0,0 +1,250 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Controller;
+
+
+use daita\Traits\TNCDataResponse;
+use Exception;
+use OCA\Social\AppInfo\Application;
+use OCA\Social\Service\ActivityPubService;
+use OCA\Social\Service\ActorService;
+use OCA\Social\Service\MiscService;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\Response;
+use OCP\IRequest;
+
+
+class ActivityPubController extends Controller {
+
+
+ use TNCDataResponse;
+
+
+ /** @var SocialPubController */
+ private $socialPubController;
+
+ /** @var ActivityPubService */
+ private $activityPubService;
+
+ /** @var ActorService */
+ private $actorService;
+
+ /** @var MiscService */
+ private $miscService;
+
+
+ /**
+ * ActivityPubController constructor.
+ *
+ * @param SocialPubController $socialPubController
+ * @param ActivityPubService $activityPubService
+ * @param ActorService $actorService
+ * @param IRequest $request
+ * @param MiscService $miscService
+ */
+ public function __construct(
+ SocialPubController $socialPubController, ActivityPubService $activityPubService,
+ ActorService $actorService, IRequest $request,
+ MiscService $miscService
+ ) {
+ parent::__construct(Application::APP_NAME, $request);
+
+ $this->socialPubController = $socialPubController;
+ $this->activityPubService = $activityPubService;
+ $this->actorService = $actorService;
+ $this->miscService = $miscService;
+ }
+
+
+ /**
+ * @NoCSRFRequired
+ * @PublicPage
+ *
+ * @param string $username
+ *
+ * @return Response
+ */
+ public function actor(string $username) {
+ if (!$this->checkSourceActivityStreams()) {
+ return $this->socialPubController->actor($username);
+ }
+
+ try {
+// $this->activityPubService->generateActor($userId);
+
+ $actor = $this->actorService->getActor($username);
+
+ return $this->directSuccess($actor);
+ } catch (Exception $e) {
+ return $this->fail($e->getMessage());
+ }
+ }
+
+ /**
+ * @NoCSRFRequired
+ * @PublicPage
+ *
+ * @param string $username
+ *
+ * @return Response
+ */
+ public function aliasactor(string $username) {
+ return $this->actor($username);
+ }
+
+
+ /**
+ * @NoCSRFRequired
+ * @PublicPage
+ *
+ * @return Response
+ */
+ public function sharedInbox() {
+ return $this->success([]);
+ }
+
+
+ /**
+ * @NoCSRFRequired
+ * @PublicPage
+ *
+ * @return Response
+ */
+ public function inbox($username) {
+
+ try {
+ $this->activityPubService->checkRequest($this->request);
+// $this->noteService->receiving(file_get_contents('php://input'));
+ $body = file_get_contents('php://input');
+$this->miscService->log('### ' . $body);
+ return $this->success([]);
+ } catch (Exception $e) {
+ return $this->fail($e->getMessage());
+ }
+ }
+
+
+ /**
+ * @NoCSRFRequired
+ * @PublicPage
+ *
+ * @param string $username
+ *
+ * @return Response
+ */
+ public function test($username, $body) {
+
+ return $this->success([$username]);
+// $this->miscService->log('#### ' . $toto . ' ' . json_encode($_SERVER) . ' ' . json_encode($_POST));
+// try {
+// return $this->success(['author' => $author]);
+// } catch (Exception $e) {
+// return $this->fail('ddsaads');
+// }
+ }
+
+
+ /**
+ * @NoCSRFRequired
+ * @PublicPage
+ *
+ * @param string $username
+ *
+ * @return Response
+ */
+ public function outbox($username) {
+ return $this->success([$username]);
+ }
+
+
+ /**
+ * @NoCSRFRequired
+ * @PublicPage
+ *
+ * @param string $username
+ *
+ * @return Response
+ */
+ public function followers($username) {
+ if (!$this->checkSourceActivityStreams()) {
+ return $this->socialPubController->followers($username);
+ }
+
+ return $this->success([$username]);
+ }
+
+
+ /**
+ * @NoCSRFRequired
+ * @PublicPage
+ *
+ * @param string $username
+ *
+ * @return Response
+ */
+ public function following($username) {
+ if (!$this->checkSourceActivityStreams()) {
+ return $this->socialPubController->following($username);
+ }
+
+ return $this->success([$username]);
+ }
+
+
+ /**
+ * @NoCSRFRequired
+ * @PublicPage
+ *
+ * @param string $username
+ * @param $postId
+ *
+ * @return Response
+ */
+ public function displayPost($username, $postId) {
+ return $this->success([$username, $postId]);
+ }
+
+
+ /**
+ *
+ */
+ private function checkSourceActivityStreams() {
+
+ return true;
+ if ($this->request->getHeader('Accept')
+ === 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"') {
+ return true;
+ }
+
+ return false;
+ }
+}
+
+
diff --git a/lib/Controller/SocialPubController.php b/lib/Controller/SocialPubController.php
new file mode 100644
index 00000000..d51b3754
--- /dev/null
+++ b/lib/Controller/SocialPubController.php
@@ -0,0 +1,132 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Controller;
+
+
+use daita\Traits\TNCDataResponse;
+use OCA\Social\AppInfo\Application;
+use OCA\Social\Service\ActivityPubService;
+use OCA\Social\Service\ActorService;
+use OCA\Social\Service\MiscService;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\Response;
+use OCP\AppFramework\Http\TemplateResponse;
+use OCP\IRequest;
+
+class SocialPubController extends Controller {
+
+
+ use TNCDataResponse;
+
+ /** @var ActivityPubService */
+ private $activityPubService;
+
+ /** @var ActorService */
+ private $actorService;
+
+ /** @var MiscService */
+ private $miscService;
+
+
+ /**
+ * ActivityPubController constructor.
+ *
+ * @param ActivityPubService $activityPubService
+ * @param ActorService $actorService
+ * @param IRequest $request
+ * @param MiscService $miscService
+ */
+ public function __construct(
+ ActivityPubService $activityPubService, ActorService $actorService, IRequest $request,
+ MiscService $miscService
+ ) {
+ parent::__construct(Application::APP_NAME, $request);
+
+ $this->activityPubService = $activityPubService;
+ $this->actorService = $actorService;
+ $this->miscService = $miscService;
+ }
+
+
+ /**
+ * @NoCSRFRequired
+ * @PublicPage
+ e*
+ * @param string $username
+ *
+ * @return Response
+ */
+ public function actor(string $username) {
+ return new TemplateResponse(Application::APP_NAME, 'actor', [], 'blank');
+ }
+
+
+ /**
+ * @NoCSRFRequired
+ * @PublicPage
+ *
+ * @param string $username
+ *
+ * @return Response
+ */
+ public function followers($username) {
+ return new TemplateResponse(Application::APP_NAME, 'followers', [], 'blank');
+ }
+
+
+ /**
+ * @NoCSRFRequired
+ * @PublicPage
+ *
+ * @param string $username
+ *
+ * @return Response
+ */
+ public function following($username) {
+ return new TemplateResponse(Application::APP_NAME, 'following', [], 'blank');
+ }
+
+
+ /**
+ * @NoCSRFRequired
+ * @PublicPage
+ *
+ * @param string $username
+ * @param $postId
+ *
+ * @return Response
+ */
+ public function displayPost($username, $postId) {
+ return $this->success([$username, $postId]);
+ }
+
+}
+
+
diff --git a/lib/Db/ActorsRequest.php b/lib/Db/ActorsRequest.php
new file mode 100644
index 00000000..0b80f610
--- /dev/null
+++ b/lib/Db/ActorsRequest.php
@@ -0,0 +1,173 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Db;
+
+
+use OCA\Social\Exceptions\ActorDoesNotExistException;
+use OCA\Social\Model\ActivityPub\Actor;
+use OCA\Social\Service\ConfigService;
+use OCA\Social\Service\MiscService;
+use OCP\IDBConnection;
+
+class ActorsRequest extends ActorsRequestBuilder {
+
+
+ /**
+ * ServicesRequest constructor.
+ *
+ * @param IDBConnection $connection
+ * @param ConfigService $configService
+ * @param MiscService $miscService
+ */
+ public function __construct(
+ IDBConnection $connection, ConfigService $configService, MiscService $miscService
+ ) {
+ parent::__construct($connection, $configService, $miscService);
+ }
+
+
+ /**
+ * @param Actor $actor
+ *
+ * @return int
+ * @throws \Exception
+ */
+ public function create(Actor $actor): int {
+
+ try {
+ $qb = $this->getActorsInsertSql();
+ $qb->setValue('type', $qb->createNamedParameter($actor->getType()))
+ ->setValue('user_id', $qb->createNamedParameter($actor->getUserId()))
+ ->setValue(
+ 'preferred_username', $qb->createNamedParameter($actor->getPreferredUsername())
+ )
+ ->setValue('public_key', $qb->createNamedParameter($actor->getPublicKey()))
+ ->setValue('private_key', $qb->createNamedParameter($actor->getPrivateKey()));
+
+ $qb->execute();
+
+ return $qb->getLastInsertId();
+ } catch (\Exception $e) {
+ throw $e;
+ }
+ }
+
+
+ /**
+ * return service.
+ *
+ * @param string $username
+ *
+ * @return Actor
+ * @throws ActorDoesNotExistException
+ */
+ public function getFromUsername(string $username): Actor {
+ $qb = $this->getActorsSelectSql();
+ $this->limitToPreferredUsername($qb, $username);
+
+ $cursor = $qb->execute();
+ $data = $cursor->fetch();
+ $cursor->closeCursor();
+
+ if ($data === false) {
+ throw new ActorDoesNotExistException('Actor not found');
+ }
+
+ return $this->parseActorsSelectSql($data);
+ }
+
+
+
+ /**
+ * return service.
+ *
+ * @param string $userId
+ *
+ * @return Actor
+ * @throws ActorDoesNotExistException
+ */
+ public function getFromUserId(string $userId): Actor {
+ $qb = $this->getActorsSelectSql();
+ $this->limitToUserId($qb, $userId);
+
+ $cursor = $qb->execute();
+ $data = $cursor->fetch();
+ $cursor->closeCursor();
+
+ if ($data === false) {
+ throw new ActorDoesNotExistException('Actor not found');
+ }
+
+ return $this->parseActorsSelectSql($data);
+ }
+
+
+
+
+//
+// /**
+// * @param Service $service
+// *
+// * @return bool
+// */
+// public function update(Service $service): bool {
+//
+// try {
+// $this->getService($service->getId());
+// } catch (ServiceDoesNotExistException $e) {
+// return false;
+// }
+//
+// $qb = $this->getServicesUpdateSql();
+// $qb->set('address', $qb->createNamedParameter($service->getAddress()));
+// $qb->set('config', $qb->createNamedParameter(json_encode($service->getConfigAll())));
+// $qb->set('status', $qb->createNamedParameter($service->getStatus()));
+// $qb->set('config', $qb->createNamedParameter(json_encode($service->getConfigAll())));
+//
+// $this->limitToId($qb, $service->getId());
+//
+// $qb->execute();
+//
+// return true;
+// }
+//
+
+// /**
+// * @param int $serviceId
+// */
+// public function delete(int $serviceId) {
+// $qb = $this->getServicesDeleteSql();
+// $this->limitToId($qb, $serviceId);
+//
+// $qb->execute();
+// }
+
+
+}
diff --git a/lib/Db/ActorsRequestBuilder.php b/lib/Db/ActorsRequestBuilder.php
new file mode 100644
index 00000000..7bc5d7d3
--- /dev/null
+++ b/lib/Db/ActorsRequestBuilder.php
@@ -0,0 +1,129 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Db;
+
+
+use daita\Traits\TArrayTools;
+use OCA\Social\Model\ActivityPub\Actor;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+
+class ActorsRequestBuilder extends CoreRequestBuilder {
+
+
+ use TArrayTools;
+
+
+ /**
+ * Base of the Sql Insert request
+ *
+ * @return IQueryBuilder
+ */
+ protected function getActorsInsertSql() {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->insert(self::TABLE_SERVER_ACTORS);
+
+ return $qb;
+ }
+
+
+ /**
+ * Base of the Sql Update request
+ *
+ * @return IQueryBuilder
+ */
+ protected function getActorsUpdateSql() {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->update(self::TABLE_SERVER_ACTORS);
+
+ return $qb;
+ }
+
+
+ /**
+ * Base of the Sql Select request for Shares
+ *
+ * @return IQueryBuilder
+ */
+ protected function getActorsSelectSql() {
+ $qb = $this->dbConnection->getQueryBuilder();
+
+ /** @noinspection PhpMethodParametersCountMismatchInspection */
+ $qb->select(
+ 'sa.id', 'sa.type', 'sa.user_id', 'sa.preferred_username', 'sa.public_key',
+ 'sa.private_key', 'sa.creation'
+ )
+ ->from(self::TABLE_SERVER_ACTORS, 'sa');
+
+ $this->defaultSelectAlias = 'sa';
+
+ return $qb;
+ }
+
+
+ /**
+ * Base of the Sql Delete request
+ *
+ * @return IQueryBuilder
+ */
+ protected function getActorsDeleteSql() {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->delete(self::TABLE_SERVER_ACTORS);
+
+ return $qb;
+ }
+
+
+ /**
+ * @param array $data
+ *
+ * @return Actor
+ */
+ protected function parseActorsSelectSql($data): Actor {
+ $id = $this->configService->getRoot() . '@' . $data['preferred_username'];
+ $actor = new Actor();
+ $actor->setId($id)
+ ->setType($this->get('type', $data, ''))
+ ->setRoot($this->configService->getRoot());
+ $actor->setUserId($data['user_id'])
+ ->setPreferredUsername($data['preferred_username'])
+ ->setPublicKey($data['public_key'])
+ ->setPrivateKey($data['private_key'])
+ ->setInbox($id . '/inbox')
+ ->setOutbox($id . '/outbox')
+ ->setFollowers($id . '/followers')
+ ->setFollowing($id . '/following')
+ ->setSharedInbox($this->configService->getRoot() . 'inbox')
+ ->setCreation($this->getInt('creation', $data, 0));
+
+ return $actor;
+ }
+
+}
+
diff --git a/lib/Db/CacheActorsRequest.php b/lib/Db/CacheActorsRequest.php
new file mode 100644
index 00000000..4806da79
--- /dev/null
+++ b/lib/Db/CacheActorsRequest.php
@@ -0,0 +1,130 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Db;
+
+
+use OCA\Social\Exceptions\ActorDoesNotExistException;
+use OCA\Social\Exceptions\CacheActorDoesNotExistException;
+use OCA\Social\Model\ActivityPub\Actor;
+use OCA\Social\Model\ActivityPub\Cache\CacheActor;
+use OCA\Social\Service\ConfigService;
+use OCA\Social\Service\MiscService;
+use OCP\IDBConnection;
+
+class CacheActorsRequest extends CacheActorsRequestBuilder {
+
+
+ /**
+ * ServicesRequest constructor.
+ *
+ * @param IDBConnection $connection
+ * @param ConfigService $configService
+ * @param MiscService $miscService
+ */
+ public function __construct(
+ IDBConnection $connection, ConfigService $configService, MiscService $miscService
+ ) {
+ parent::__construct($connection, $configService, $miscService);
+ }
+
+
+ /**
+ * @param Actor $actor
+ *
+ * @return int
+ * @throws \Exception
+ */
+ public function create(Actor $actor, array $object): int {
+
+ try {
+ $qb = $this->getCacheActorsInsertSql();
+ $qb->setValue('account', $qb->createNamedParameter($actor->getAccount()))
+ ->setValue('url', $qb->createNamedParameter($actor->getId()))
+ ->setValue('actor', $qb->createNamedParameter(json_encode($object)));
+
+ $qb->execute();
+
+ return $qb->getLastInsertId();
+ } catch (\Exception $e) {
+ throw $e;
+ }
+ }
+
+
+ /**
+ * return service.
+ *
+ * @param string $account
+ *
+ * @return CacheActor
+ * @throws CacheActorDoesNotExistException
+ */
+ public function getFromAccount(string $account): Actor {
+ $qb = $this->getCacheActorsSelectSql();
+ $this->limitToAccount($qb, $account);
+
+ $cursor = $qb->execute();
+ $data = $cursor->fetch();
+ $cursor->closeCursor();
+
+ if ($data === false) {
+ throw new CacheActorDoesNotExistException();
+ }
+
+ return $this->parseCacheActorsSelectSql($data);
+ }
+
+
+ /**
+ * return service.
+ *
+ * @param string $url
+ *
+ * @return CacheActor
+ * @throws CacheActorDoesNotExistException
+ */
+ public function getFromUrl(string $url): CacheActor {
+ $qb = $this->getCacheActorsSelectSql();
+ $this->limitToUrl($qb, $url);
+
+ $cursor = $qb->execute();
+ $data = $cursor->fetch();
+ $cursor->closeCursor();
+
+ if ($data === false) {
+ throw new CacheActorDoesNotExistException();
+ }
+
+ return $this->parseCacheActorsSelectSql($data);
+ }
+
+
+}
+
diff --git a/lib/Db/CacheActorsRequestBuilder.php b/lib/Db/CacheActorsRequestBuilder.php
new file mode 100644
index 00000000..ab4add74
--- /dev/null
+++ b/lib/Db/CacheActorsRequestBuilder.php
@@ -0,0 +1,122 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Db;
+
+
+use daita\Traits\TArrayTools;
+use OCA\Social\Model\ActivityPub\Actor;
+use OCA\Social\Model\ActivityPub\Cache\CacheActor;
+use OCA\Social\Model\Service;
+use OCA\Social\Model\ServiceAccount;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+
+class CacheActorsRequestBuilder extends CoreRequestBuilder {
+
+
+ use TArrayTools;
+
+
+ /**
+ * Base of the Sql Insert request
+ *
+ * @return IQueryBuilder
+ */
+ protected function getCacheActorsInsertSql() {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->insert(self::TABLE_CACHE_ACTORS);
+
+ return $qb;
+ }
+
+
+ /**
+ * Base of the Sql Update request
+ *
+ * @return IQueryBuilder
+ */
+ protected function getCacheActorsUpdateSql() {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->update(self::TABLE_CACHE_ACTORS);
+
+ return $qb;
+ }
+
+
+ /**
+ * Base of the Sql Select request for Shares
+ *
+ * @return IQueryBuilder
+ */
+ protected function getCacheActorsSelectSql() {
+ $qb = $this->dbConnection->getQueryBuilder();
+
+ /** @noinspection PhpMethodParametersCountMismatchInspection */
+ $qb->select(
+ 'ca.id', 'ca.account', 'ca.url', 'ca.actor', 'ca.creation'
+ )
+ ->from(self::TABLE_CACHE_ACTORS, 'ca');
+
+ $this->defaultSelectAlias = 'ca';
+
+ return $qb;
+ }
+
+
+ /**
+ * Base of the Sql Delete request
+ *
+ * @return IQueryBuilder
+ */
+ protected function getCacheActorsDeleteSql() {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->delete(self::TABLE_CACHE_ACTORS);
+
+ return $qb;
+ }
+
+
+ /**
+ * @param array $data
+ *
+ * @return CacheActor
+ */
+ protected function parseCacheActorsSelectSql($data): CacheActor {
+ $actor = new CacheActor();
+ $actor->setId($this->getInt('id', $data))
+ ->setAccount($data['account'])
+ ->setUrl($data['url'])
+ ->setActor(json_decode($data['actor'], true))
+ ->setCreation($this->getInt('creation', $data, 0));
+
+ return $actor;
+ }
+
+}
+
diff --git a/lib/Db/CoreRequestBuilder.php b/lib/Db/CoreRequestBuilder.php
index eea39b91..fa5d13de 100644
--- a/lib/Db/CoreRequestBuilder.php
+++ b/lib/Db/CoreRequestBuilder.php
@@ -40,7 +40,11 @@ class CoreRequestBuilder {
const TABLE_SERVICES = 'social_services';
const TABLE_ACCOUNTS = 'social_accounts';
- const TABLE_OAUTH2_TOKENS = 'social_oauth2_tokens';
+
+ const TABLE_SERVER_ACTORS = 'social_server_actors';
+ const TABLE_SERVER_NOTES = 'social_server_notes';
+
+ const TABLE_CACHE_ACTORS = 'social_cache_actors';
/** @var IDBConnection */
@@ -95,6 +99,17 @@ class CoreRequestBuilder {
}
+ /**
+ * Limit the request to the OwnerId
+ *
+ * @param IQueryBuilder $qb
+ * @param string $userId
+ */
+ protected function limitToPreferredUsername(IQueryBuilder &$qb, $userId) {
+ $this->limitToDBField($qb, 'preferred_username', $userId);
+ }
+
+
/**
* Limit the request to the OwnerId
*
@@ -128,6 +143,17 @@ class CoreRequestBuilder {
}
+ /**
+ * Limit the request to the url
+ *
+ * @param IQueryBuilder $qb
+ * @param string $url
+ */
+ protected function limitToUrl(IQueryBuilder &$qb, string $url) {
+ $this->limitToDBField($qb, 'url', $url);
+ }
+
+
/**
* Limit the request to the status
*
diff --git a/lib/Db/NotesRequest.php b/lib/Db/NotesRequest.php
new file mode 100644
index 00000000..ec1fa58f
--- /dev/null
+++ b/lib/Db/NotesRequest.php
@@ -0,0 +1,86 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Db;
+
+
+use OCA\Social\Exceptions\ActorDoesNotExistException;
+use OCA\Social\Model\ActivityPub\Note;
+use OCA\Social\Model\ActivityPub\Actor;
+use OCA\Social\Service\ConfigService;
+use OCA\Social\Service\MiscService;
+use OCP\IDBConnection;
+
+class NotesRequest extends NotesRequestBuilder {
+
+
+ /**
+ * ServicesRequest constructor.
+ *
+ * @param IDBConnection $connection
+ * @param ConfigService $configService
+ * @param MiscService $miscService
+ */
+ public function __construct(
+ IDBConnection $connection, ConfigService $configService, MiscService $miscService
+ ) {
+ parent::__construct($connection, $configService, $miscService);
+ }
+
+
+ /**
+ * @param Note $note
+ *
+ * @return int
+ * @throws \Exception
+ */
+ public function create(Note $note): int {
+
+ try {
+ $qb = $this->getNotesInsertSql();
+ $qb->setValue('id', $qb->createNamedParameter($note->getId()))
+ ->setValue('to', $qb->createNamedParameter($note->getTo()))
+ ->setValue('to_array', $qb->createNamedParameter(json_encode($note->getToArray())))
+ ->setValue('cc', $qb->createNamedParameter(json_encode($note->getCc())))
+ ->setValue('bcc', $qb->createNamedParameter(json_encode($note->getBcc())))
+ ->setValue('content', $qb->createNamedParameter($note->getContent()))
+ ->setValue('summary', $qb->createNamedParameter($note->getSummary()))
+ ->setValue('published', $qb->createNamedParameter($note->getPublished()))
+ ->setValue('attributed_to', $qb->createNamedParameter($note->getAttributedTo()))
+ ->setValue('in_reply_to', $qb->createNamedParameter($note->getInReplyTo()));
+
+ $qb->execute();
+
+ return $qb->getLastInsertId();
+ } catch (\Exception $e) {
+ throw $e;
+ }
+ }
+
+}
diff --git a/lib/Db/NotesRequestBuilder.php b/lib/Db/NotesRequestBuilder.php
new file mode 100644
index 00000000..45735a7c
--- /dev/null
+++ b/lib/Db/NotesRequestBuilder.php
@@ -0,0 +1,124 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Db;
+
+
+use daita\Traits\TArrayTools;
+use OCA\Social\Model\ActivityPub\Note;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+
+class NotesRequestBuilder extends CoreRequestBuilder {
+
+
+ use TArrayTools;
+
+
+ /**
+ * Base of the Sql Insert request
+ *
+ * @return IQueryBuilder
+ */
+ protected function getNotesInsertSql() {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->insert(self::TABLE_SERVER_NOTES);
+
+ return $qb;
+ }
+
+
+ /**
+ * Base of the Sql Update request
+ *
+ * @return IQueryBuilder
+ */
+ protected function getNotesUpdateSql() {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->update(self::TABLE_SERVER_NOTES);
+
+ return $qb;
+ }
+
+
+ /**
+ * Base of the Sql Select request for Shares
+ *
+ * @return IQueryBuilder
+ */
+ protected function getNotesSelectSql() {
+ $qb = $this->dbConnection->getQueryBuilder();
+
+ /** @noinspection PhpMethodParametersCountMismatchInspection */
+ $qb->select(
+ 'sn.id', 'sn.to', 'sn.to_array', 'sn.cc', 'sn.bcc', 'sn.content', 'sn_summary',
+ 'sn.published', 'sn.attributed_to', 'sn.in_reply_to', 'sn.creation'
+ )
+ ->from(self::TABLE_SERVER_NOTES, 'sn');
+
+ $this->defaultSelectAlias = 'sn';
+
+ return $qb;
+ }
+
+
+ /**
+ * Base of the Sql Delete request
+ *
+ * @return IQueryBuilder
+ */
+ protected function getNotesDeleteSql() {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->delete(self::TABLE_SERVER_NOTES);
+
+ return $qb;
+ }
+
+
+ /**
+ * @param array $data
+ *
+ * @return Note
+ */
+ protected function parseNotesSelectSql($data): Note {
+ $note = new Note();
+ $note->setId($data['id'])
+ ->setTo($data['to'])
+ ->setToArray(json_decode($data['to_array'], true))
+ ->setCc(json_decode($data['cc'], true))
+ ->setBcc(json_decode($data['bcc']));
+ $note->setContent($data['content'])
+ ->setPublished($data['published'])
+ ->setAttributedTo($data['attributed_to'])
+ ->setInReplyTo($data['in_reply_to']);
+
+ return $note;
+ }
+
+}
+
diff --git a/lib/Exceptions/ActorAlreadyExistsException.php b/lib/Exceptions/ActorAlreadyExistsException.php
new file mode 100644
index 00000000..a2556c9e
--- /dev/null
+++ b/lib/Exceptions/ActorAlreadyExistsException.php
@@ -0,0 +1,8 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Model;
+
+
+use daita\Traits\TArrayTools;
+use JsonSerializable;
+
+class APHosts implements JsonSerializable {
+
+ use TArrayTools;
+
+ /** @var string */
+ private $address;
+
+ /** @var array */
+ private $uriIds = [];
+
+ public function __construct(string $address = '') {
+ $this->address = $address;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getAddress(): string {
+ return $this->address;
+ }
+
+
+ /**
+ * @param string $uriId
+ *
+ * @return APHosts
+ */
+ public function addUriId(string $uriId): APHosts {
+ $this->uriIds[] = $uriId;
+
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getUriIds(): array {
+ return $this->uriIds;
+ }
+
+
+ public function jsonSerialize() {
+ return [
+ 'address' => $this->address,
+ 'urlIds' => $this->getUriIds()
+ ];
+ }
+
+
+}
+
diff --git a/lib/Model/ActivityPub/ActivityCreate.php b/lib/Model/ActivityPub/ActivityCreate.php
new file mode 100644
index 00000000..f1946ba5
--- /dev/null
+++ b/lib/Model/ActivityPub/ActivityCreate.php
@@ -0,0 +1,62 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Model\ActivityPub;
+
+
+use JsonSerializable;
+
+class ActivityCreate extends Core implements JsonSerializable {
+
+
+ /**
+ * ActivityCreate constructor.
+ *
+ * @param bool $isTopLevel
+ */
+ public function __construct(bool $isTopLevel = false) {
+ parent::__construct($isTopLevel);
+
+ $this->setType('Create');
+ }
+
+
+ /**
+ * @return array
+ */
+ public function jsonSerialize(): array {
+ return array_merge(
+ parent::jsonSerialize(),
+ [
+ ]
+ );
+ }
+
+}
+
diff --git a/lib/Model/ActivityPub/Actor.php b/lib/Model/ActivityPub/Actor.php
new file mode 100644
index 00000000..25bba176
--- /dev/null
+++ b/lib/Model/ActivityPub/Actor.php
@@ -0,0 +1,330 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Model\ActivityPub;
+
+
+use JsonSerializable;
+use OCA\Social\Service\ActivityPubService;
+
+class Actor extends Core implements JsonSerializable {
+
+ /** @var string */
+ private $userId = '';
+
+ /** @var string */
+ private $preferredUsername = '';
+
+ /** @var string */
+ private $publicKey = '';
+
+ /** @var string */
+ private $privateKey = '';
+
+ /** @var int */
+ private $creation = 0;
+
+ /** @var string */
+ private $account = '';
+
+ /** @var string */
+ private $following = '';
+
+ /** @var string */
+ private $followers = '';
+
+ /** @var string */
+ private $inbox = '';
+
+ /** @var string */
+ private $outbox = '';
+
+ /** @var string */
+ private $sharedInbox = '';
+
+
+ /**
+ * Actor constructor.
+ *
+ * @param bool $isTopLevel
+ */
+ public function __construct(bool $isTopLevel = false) {
+ parent::__construct($isTopLevel);
+
+ $this->setType('Person');
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getUserId(): string {
+ return $this->userId;
+ }
+
+ /**
+ * @param string $userId
+ *
+ * @return Actor
+ */
+ public function setUserId(string $userId): Actor {
+ $this->userId = $userId;
+
+ if ($this->getPreferredUsername() === '') {
+ $this->setPreferredUsername($userId);
+ }
+
+ return $this;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getPreferredUsername(): string {
+ return $this->preferredUsername;
+ }
+
+ /**
+ * @param string $preferredUsername
+ *
+ * @return Actor
+ */
+ public function setPreferredUsername(string $preferredUsername): Actor {
+ if ($preferredUsername !== '') {
+ $this->preferredUsername = $preferredUsername;
+ }
+
+ return $this;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getPublicKey(): string {
+ return $this->publicKey;
+ }
+
+ /**
+ * @param string $publicKey
+ *
+ * @return Actor
+ */
+ public function setPublicKey(string $publicKey): Actor {
+ $this->publicKey = $publicKey;
+
+ return $this;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getPrivateKey(): string {
+ return $this->privateKey;
+ }
+
+ /**
+ * @param string $privateKey
+ *
+ * @return Actor
+ */
+ public function setPrivateKey(string $privateKey): Actor {
+ $this->privateKey = $privateKey;
+
+ return $this;
+ }
+
+
+ /**
+ * @return int
+ */
+ public function getCreation(): int {
+ return $this->creation;
+ }
+
+ /**
+ * @param int $creation
+ *
+ * @return Actor
+ */
+ public function setCreation(int $creation): Actor {
+ $this->creation = $creation;
+
+ return $this;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getFollowing(): string {
+ return $this->following;
+ }
+
+ /**
+ * @param string $following
+ *
+ * @return Actor
+ */
+ public function setFollowing(string $following): Actor {
+ $this->following = $following;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getFollowers(): string {
+ return $this->followers;
+ }
+
+ /**
+ * @param string $followers
+ *
+ * @return Actor
+ */
+ public function setFollowers(string $followers): Actor {
+ $this->followers = $followers;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getAccount(): string {
+ return $this->account;
+ }
+
+ /**
+ * @param string $account
+ *
+ * @return Actor
+ */
+ public function setAccount(string $account): Actor {
+ $this->account = $account;
+
+ return $this;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getInbox(): string {
+ return $this->inbox;
+ }
+
+ /**
+ * @param string $inbox
+ *
+ * @return Actor
+ */
+ public function setInbox(string $inbox): Actor {
+ $this->inbox = $inbox;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getOutbox(): string {
+ return $this->outbox;
+ }
+
+ /**
+ * @param string $outbox
+ *
+ * @return Actor
+ */
+ public function setOutbox(string $outbox): Actor {
+ $this->outbox = $outbox;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSharedInbox(): string {
+ return $this->sharedInbox;
+ }
+
+ /**
+ * @param string $sharedInbox
+ *
+ * @return Actor
+ */
+ public function setSharedInbox(string $sharedInbox): Actor {
+ $this->sharedInbox = $sharedInbox;
+
+ return $this;
+ }
+
+
+ /**
+ * @return array
+ */
+ public function jsonSerialize(): array {
+ return [
+ '@context' => [
+ ActivityPubService::CONTEXT_ACTIVITYSTREAMS,
+ ActivityPubService::CONTEXT_SECURITY
+ ],
+ 'aliases' => [
+ $this->getRoot() . '@' . $this->getPreferredUsername(),
+ $this->getRoot() . 'users/' . $this->getPreferredUsername()
+ ],
+
+ 'id' => $this->getId(),
+ 'type' => $this->getType(),
+ 'preferredUsername' => $this->getPreferredUsername(),
+ 'inbox' => $this->getInbox(),
+ 'outbox' => $this->getOutbox(),
+ 'following' => $this->getFollowing(),
+ 'followers' => $this->getFollowers(),
+ 'url' => $this->getId(),
+ 'endpoints' =>
+ ['sharedInbox' => $this->getSharedInbox()],
+
+ 'publicKey' => [
+ 'id' => $this->getId() . '#main-key',
+ 'owner' => $this->getId(),
+ 'publicKeyPem' => $this->getPublicKey()
+ ]
+ ];
+ }
+
+
+}
+
diff --git a/lib/Model/ActivityPub/Cache/CacheActor.php b/lib/Model/ActivityPub/Cache/CacheActor.php
new file mode 100644
index 00000000..27768ae5
--- /dev/null
+++ b/lib/Model/ActivityPub/Cache/CacheActor.php
@@ -0,0 +1,157 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Model\ActivityPub\Cache;
+
+
+class CacheActor {
+
+ /** @var int */
+ private $id;
+
+ /** @var string */
+ private $account = '';
+
+ /** @var string */
+ private $url;
+
+ /** @var array */
+ private $actor = [];
+
+ /** @var int */
+ private $creation = 0;
+
+
+ /**
+ * CacheActor constructor.
+ *
+ * @param int $id
+ */
+ public function __construct($id = 0) {
+ $this->id = $id;
+ }
+
+
+ /**
+ * @return int
+ */
+ public function getId(): int {
+ return $this->id;
+ }
+
+ /**
+ * @param int $id
+ *
+ * @return CacheActor
+ */
+ public function setId(int $id): CacheActor {
+ $this->account = $id;
+
+ return $this;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getAccount(): string {
+ return $this->account;
+ }
+
+ /**
+ * @param string $account
+ *
+ * @return CacheActor
+ */
+ public function setAccount(string $account): CacheActor {
+ $this->account = $account;
+
+ return $this;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getUrl(): string {
+ return $this->url;
+ }
+
+ /**
+ * @param string $url
+ *
+ * @return CacheActor
+ */
+ public function setUrl(string $url): CacheActor {
+ $this->url = $url;
+
+ return $this;
+ }
+
+
+ /**
+ * @return array
+ */
+ public function getActor(): array {
+ return $this->actor;
+ }
+
+ /**
+ * @param array $actor
+ *
+ * @return CacheActor
+ */
+ public function setActor(array $actor): CacheActor {
+ $this->actor = $actor;
+
+ return $this;
+ }
+
+
+ /**
+ * @return int
+ */
+ public function getCreation(): int {
+ return $this->creation;
+ }
+
+ /**
+ * @param int $creation
+ *
+ * @return CacheActor
+ */
+ public function setCreation(int $creation): CacheActor {
+ $this->creation = $creation;
+
+ return $this;
+ }
+
+
+}
+
diff --git a/lib/Model/ActivityPub/Core.php b/lib/Model/ActivityPub/Core.php
new file mode 100644
index 00000000..24d1dcd4
--- /dev/null
+++ b/lib/Model/ActivityPub/Core.php
@@ -0,0 +1,443 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Model\ActivityPub;
+
+
+use JsonSerializable;
+use OCA\Social\Service\ICoreService;
+
+class Core implements JsonSerializable {
+
+
+ /** @var string */
+ private $root = '';
+
+ /** @var bool */
+ private $isTopLevel = false;
+
+ /** @var string */
+ private $address = '';
+
+ /** @var string */
+ private $id = '';
+
+ /** @var string */
+ private $type;
+
+ /** @var string */
+ private $to = '';
+
+ /** @var array */
+ private $toArray = [];
+
+ /** @var array */
+ private $cc = [];
+
+ /** @var array */
+ private $bcc = [];
+
+ /** @var Actor */
+ private $actor;
+
+ /** @var array */
+ private $tags = [];
+
+ /** @var array */
+ private $entries = [];
+
+ /** @var Core */
+ private $object = null;
+
+ /** @var ICoreService */
+ private $saveAs;
+
+ /**
+ * Core constructor.
+ *
+ * @param bool $isTopLevel
+ */
+ public function __construct(bool $isTopLevel = false) {
+ $this->isTopLevel = $isTopLevel;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getId(): string {
+ return $this->id;
+ }
+
+
+ /**
+ * @param string $id
+ *
+ * @return Core
+ */
+ public function setId(string $id): Core {
+ $this->id = $id;
+
+ return $this;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getType(): string {
+ return $this->type;
+ }
+
+ /**
+ * @param string $type
+ *
+ * @return Core
+ */
+ public function setType(string $type): Core {
+ $this->type = $type;
+
+ return $this;
+ }
+
+ /**
+ * @return Actor
+ */
+ public function getActor(): Actor {
+ return $this->actor;
+ }
+
+ /**
+ * @param Actor $actor
+ *
+ * @return Core
+ */
+ public function setActor(Actor $actor): Core {
+ $this->actor = $actor;
+
+ return $this;
+ }
+
+ /**
+ * @return bool
+ */
+ public function gotActor(): bool {
+ if ($this->actor === null) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getRoot(): string {
+ return $this->root;
+ }
+
+ /**
+ * @param string $path
+ *
+ * @return Core
+ */
+ public function setRoot(string $path): Core {
+ $this->root = $path;
+
+ return $this;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getAddress(): string {
+ return $this->address;
+ }
+
+ /**
+ * @param string $address
+ *
+ * @return Core
+ */
+ public function setAddress(string $address) {
+ $this->address = $address;
+
+ return $this;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getTo(): string {
+ return $this->to;
+ }
+
+ /**
+ * @param string $to
+ *
+ * @return Core
+ */
+ public function setTo(string $to): Core {
+ $this->to = $to;
+
+ return $this;
+ }
+
+
+ /**
+ * @return array
+ */
+ public function getToArray(): array {
+ return $this->toArray;
+ }
+
+ /**
+ * @param array $toArray
+ *
+ * @return Core
+ */
+ public function setToArray(array $toArray): Core {
+ $this->toArray = $toArray;
+
+ return $this;
+ }
+
+
+ /**
+ * @return array
+ */
+ public function getCc(): array {
+ return $this->cc;
+ }
+
+ /**
+ * @param array $cc
+ *
+ * @return Core
+ */
+ public function setCc(array $cc): Core {
+ $this->cc = $cc;
+
+ return $this;
+ }
+
+
+ /**
+ * @return array
+ */
+ public function getBcc(): array {
+ return $this->bcc;
+ }
+
+ /**
+ * @param array $bcc
+ *
+ * @return Core
+ */
+ public function setBcc(array $bcc): Core {
+ $this->bcc = $bcc;
+
+ return $this;
+ }
+
+
+ /**
+ * @param array $tag
+ *
+ * @return Core
+ */
+ public function addTag(array $tag): Core {
+ $this->tags[] = $tag;
+
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getTags(): array {
+ return $this->tags;
+ }
+
+ /**
+ * @param array $tag
+ *
+ * @return Core
+ */
+ public function setTags(array $tag): Core {
+ $this->tags = $tag;
+
+ return $this;
+ }
+
+
+ /**
+ * @return bool
+ */
+ public function gotObject(): bool {
+ if ($this->object === null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @return Core
+ */
+ public function getObject(): Core {
+ return $this->object;
+ }
+
+ /**
+ * @param Core $object
+ *
+ * @return Core
+ */
+ public function setObject(Core $object): Core {
+ $this->object = $object;
+
+ return $this;
+ }
+
+
+ /**
+ * @return bool
+ */
+ public function isTopLevel(): bool {
+ return $this->isTopLevel;
+ }
+
+
+ /**
+ * @param array $arr
+ *
+ * @return Core
+ */
+ public function setEntries(array $arr): Core {
+ $this->entries = $arr;
+
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getEntries(): array {
+ return $this->entries;
+ }
+
+ /**
+ * @param string $k
+ * @param string $v
+ *
+ * @return Core
+ */
+ public function addEntry(string $k, string $v): Core {
+ if ($v === '') {
+ unset($this->entries[$k]);
+
+ return $this;
+ }
+
+ $this->entries[$k] = $v;
+
+ return $this;
+ }
+
+
+ /**
+ * @param string $k
+ * @param array $v
+ *
+ * @return Core
+ */
+ public function addEntryArray(string $k, array $v): Core {
+ $this->entries[$k] = $v;
+
+ return $this;
+ }
+
+
+ /**
+ * @param ICoreService $class
+ */
+ public function saveAs(ICoreService $class) {
+ $this->saveAs = $class;
+ }
+
+ /**
+ * @return ICoreService
+ */
+ public function savingAs() {
+ return $this->saveAs;
+ }
+
+
+ /**
+ * @return array
+ */
+ public function jsonSerialize(): array {
+ $this->addEntry('id', $this->getId());
+ $this->addEntry('type', $this->getType());
+ $this->addEntry('url', $this->getId());
+
+ if ($this->getToArray() === []) {
+ $this->addEntry('to', $this->getTo());
+ } else {
+ $this->addEntryArray('to', $this->getToArray());
+ }
+
+ $this->addEntryArray('cc', $this->getCc());
+
+ if ($this->gotActor()) {
+ $this->addEntry(
+ 'actor', $this->getActor()
+ ->getId()
+ );
+ }
+
+ if ($this->getTags() !== []) {
+ $this->addEntryArray('tag', $this->getTags());
+ }
+
+ $arr = $this->getEntries();
+ if ($this->gotObject()) {
+ $arr['object'] = $this->getObject();
+ }
+
+ return $arr;
+ }
+
+}
+
+
diff --git a/lib/Model/ActivityPub/Note.php b/lib/Model/ActivityPub/Note.php
new file mode 100644
index 00000000..741ae542
--- /dev/null
+++ b/lib/Model/ActivityPub/Note.php
@@ -0,0 +1,190 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Model\ActivityPub;
+
+
+use JsonSerializable;
+
+class Note extends Core implements JsonSerializable {
+
+
+ /** @var string */
+ private $content;
+
+ /** @var string */
+ private $summary = '';
+
+ /** @var string */
+ private $published;
+
+ /** @var string */
+ private $attributedTo;
+
+ /** @var string */
+ private $inReplyTo = '';
+
+
+ /**
+ * Note constructor.
+ *
+ * @param bool $isTopLevel
+ */
+ public function __construct(bool $isTopLevel = false) {
+ parent::__construct($isTopLevel);
+
+ $this->setType('Note');
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getContent(): string {
+ return $this->content;
+ }
+
+ /**
+ * @param string $content
+ *
+ * @return Note
+ */
+ public function setContent(string $content): Note {
+ $this->content = $content;
+
+ return $this;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getSummary(): string {
+ return $this->summary;
+ }
+
+ /**
+ * @param string $summary
+ *
+ * @return Note
+ */
+ public function setSummary(string $summary): Note {
+ $this->summary = $summary;
+
+ return $this;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getPublished(): string {
+ return $this->published;
+ }
+
+ /**
+ * @param string $published
+ *
+ * @return Note
+ */
+ public function setPublished(string $published): Note {
+ $this->published = $published;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getAttributedTo(): string {
+ return $this->attributedTo;
+ }
+
+ /**
+ * @param string $attributedTo
+ *
+ * @return Note
+ */
+ public function setAttributedTo(string $attributedTo): Note {
+ $this->attributedTo = $attributedTo;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getInReplyTo(): string {
+ return $this->inReplyTo;
+ }
+
+ /**
+ * @param string $inReplyTo
+ *
+ * @return Note
+ */
+ public function setInReplyTo(string $inReplyTo): Note {
+ $this->inReplyTo = $inReplyTo;
+
+ return $this;
+ }
+
+
+
+
+
+
+
+
+
+
+
+// public function
+//"published": "2018-06-23T17:17:11Z",
+//"attributedTo": "https://my-example.com/actor",
+//"inReplyTo": "https://mastodon.social/@Gargron/100254678717223630",
+
+
+ /**
+ * @return array
+ */
+ public function jsonSerialize(): array {
+ return array_merge(
+ parent::jsonSerialize(),
+ [
+ 'content' => $this->getContent(),
+ 'published' => $this->getPublished(),
+ 'attributedTo' => $this->getRoot() . $this->getAttributedTo(),
+ 'inReplyTo' => $this->getInReplyTo()
+ ]
+ );
+ }
+
+}
+
diff --git a/lib/Service/ActivityPub/InboxService.php b/lib/Service/ActivityPub/InboxService.php
new file mode 100644
index 00000000..de02401b
--- /dev/null
+++ b/lib/Service/ActivityPub/InboxService.php
@@ -0,0 +1,76 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Service\ActivityPub;
+
+
+use Exception;
+use OC\User\NoUserException;
+use OCA\Social\Db\NotesRequest;
+use OCA\Social\Exceptions\ActorDoesNotExistException;
+use OCA\Social\Model\ActivityPub\Core;
+use OCA\Social\Model\ActivityPub\Note;
+use OCA\Social\Service\ActivityStreamsService;
+use OCA\Social\Service\ActorService;
+use OCA\Social\Service\ConfigService;
+use OCA\Social\Service\CurlService;
+use OCA\Social\Service\ICoreService;
+use OCA\Social\Service\MiscService;
+
+class InboxService implements ICoreService {
+
+ /** @var InboxRequest */
+ private $inboxRequest;
+
+ /** @var ConfigService */
+ private $configService;
+
+ /** @var MiscService */
+ private $miscService;
+
+
+ /**
+ * InboxService constructor.
+ *
+ * @param InboxRequest $inboxRequest
+ * @param ConfigService $configService
+ * @param MiscService $miscService
+ */
+ public function __construct(
+ InboxRequest $inboxRequest,
+ ConfigService $configService,
+ MiscService $miscService
+ ) {
+ $this->inboxRequest = $inboxRequest;
+ $this->configService = $configService;
+ $this->miscService = $miscService;
+ }
+
+}
+
diff --git a/lib/Service/ActivityPub/NoteService.php b/lib/Service/ActivityPub/NoteService.php
new file mode 100644
index 00000000..8e461514
--- /dev/null
+++ b/lib/Service/ActivityPub/NoteService.php
@@ -0,0 +1,130 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Service\ActivityPub;
+
+
+use Exception;
+use OC\User\NoUserException;
+use OCA\Social\Db\NotesRequest;
+use OCA\Social\Exceptions\ActorDoesNotExistException;
+use OCA\Social\Model\ActivityPub\Core;
+use OCA\Social\Model\ActivityPub\Note;
+use OCA\Social\Service\ActivityStreamsService;
+use OCA\Social\Service\ActorService;
+use OCA\Social\Service\ConfigService;
+use OCA\Social\Service\CurlService;
+use OCA\Social\Service\ICoreService;
+use OCA\Social\Service\MiscService;
+
+class NoteService implements ICoreService {
+
+ /** @var NotesRequest */
+ private $notesRequest;
+
+ /** @var ActivityStreamsService */
+ private $activityStreamsService;
+
+ /** @var ActorService */
+ private $actorService;
+
+ /** @var CurlService */
+ private $curlService;
+
+ /** @var ConfigService */
+ private $configService;
+
+ /** @var MiscService */
+ private $miscService;
+
+
+ /**
+ * NoteService constructor.
+ *
+ * @param NotesRequest $notesRequest
+ * @param ActivityStreamsService $activityStreamsService
+ * @param ActorService $actorService
+ * @param CurlService $curlService
+ * @param ConfigService $configService
+ * @param MiscService $miscService
+ */
+ public function __construct(
+ NotesRequest $notesRequest, ActivityStreamsService $activityStreamsService,
+ ActorService $actorService,
+ CurlService $curlService,
+ ConfigService $configService,
+ MiscService $miscService
+ ) {
+ $this->notesRequest = $notesRequest;
+ $this->activityStreamsService = $activityStreamsService;
+ $this->actorService = $actorService;
+ $this->curlService = $curlService;
+ $this->configService = $configService;
+ $this->miscService = $miscService;
+ }
+
+
+ /**
+ * @param string $userId
+ * @param string $content
+ * @param string $to
+ *
+ * @return Note
+ * @throws ActorDoesNotExistException
+ * @throws NoUserException
+ */
+ public function generateNote(string $userId, string $content, string $to) {
+ $note = new Note();
+ $actor = $this->actorService->getActorFromUserId($userId);
+
+ $note->setId($this->configService->generateId('@' . $actor->getPreferredUsername()));
+ $note->setPublished(date("c"));
+ $note->setAttributedTo(
+ $this->configService->getRoot() . '@' . $actor->getPreferredUsername()
+ );
+ $note->setTo($to);
+ $note->setContent($content);
+
+ $note->saveAs($this);
+
+ return $note;
+ }
+
+
+ /**
+ * @param Core $note
+ *
+ * @throws Exception
+ */
+ public function save(Core $note) {
+ /** @var Note $note */
+ $this->notesRequest->create($note);
+ }
+
+}
diff --git a/lib/Service/ActivityPubService.php b/lib/Service/ActivityPubService.php
new file mode 100644
index 00000000..949a83d5
--- /dev/null
+++ b/lib/Service/ActivityPubService.php
@@ -0,0 +1,491 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Service;
+
+
+use daita\Model\Request;
+use DateTime;
+use Exception;
+use OC\User\NoUserException;
+use OCA\Social\Db\ActorsRequest;
+use OCA\Social\Exceptions\ActorDoesNotExistException;
+use OCA\Social\Exceptions\APIRequestException;
+use OCA\Social\Exceptions\InvalidAccessTokenException;
+use OCA\Social\Exceptions\MovedPermanentlyException;
+use OCA\Social\Model\ActivityPub\ActivityCreate;
+use OCA\Social\Model\ActivityPub\Core;
+use OCA\Social\Model\ActivityPub\Note;
+use OCA\Social\Model\APHosts;
+use OCP\IRequest;
+
+class ActivityPubService {
+
+
+ const CONTEXT_ACTIVITYSTREAMS = 'https://www.w3.org/ns/activitystreams';
+ const CONTEXT_SECURITY = 'https://w3id.org/security/v1';
+
+ const TO_PUBLIC = 'https://www.w3.org/ns/activitystreams#Public';
+
+ const DATE_FORMAT = 'D, d M Y H:i:s T';
+ const DATE_DELAY = 30;
+
+ /** @var ActivityStreamsService */
+ private $activityStreamsService;
+
+ /** @var ActorsRequest */
+ private $actorsRequest;
+
+ /** @var ActorService */
+ private $actorService;
+
+ /** @var ConfigService */
+ private $configService;
+
+ /** @var CurlService */
+ private $curlService;
+
+ /** @var MiscService */
+ private $miscService;
+
+
+ /**
+ * ActivityStreamsService constructor.
+ *
+ * @param ActivityStreamsService $activityStreamsService
+ * @param CurlService $curlService
+ * @param ActorsRequest $actorsRequest
+ * @param ActorService $actorService
+ * @param ConfigService $configService
+ * @param MiscService $miscService
+ */
+ public function __construct(
+ ActivityStreamsService $activityStreamsService,
+ CurlService $curlService,
+ ActorsRequest $actorsRequest,
+ ActorService $actorService,
+ ConfigService $configService, MiscService $miscService
+ ) {
+ $this->activityStreamsService = $activityStreamsService;
+ $this->curlService = $curlService;
+ $this->actorsRequest = $actorsRequest;
+ $this->actorService = $actorService;
+ $this->configService = $configService;
+ $this->miscService = $miscService;
+ }
+
+
+ public function test() {
+
+
+ }
+
+
+ /**
+ * @param string $userId
+ *
+ * @param Core $item
+ * @param Core $activity
+ *
+ * @return array
+ * @throws ActorDoesNotExistException
+ * @throws NoUserException
+ * @throws APIRequestException
+ * @throws InvalidAccessTokenException
+ * @throws MovedPermanentlyException
+ */
+ public function createActivity($userId, Core $item, Core &$activity = null): array {
+
+ $activity = new ActivityCreate(true);
+// $this->activityStreamsService->initCore($activity);
+
+ $actor = $this->actorService->getActorFromUserId($userId);
+ $activity->setId($item->getId() . '/activity');
+
+ if ($item->getToArray() !== []) {
+ $activity->setToArray($item->getToArray());
+ } else {
+ $activity->setTo($item->getTo());
+ }
+
+ $activity->setActor($actor);
+ $activity->setObject($item);
+
+ $this->setupCore($activity);
+ $result = $this->request($activity);
+
+ return $result;
+ }
+
+
+ /**
+ * @param Core $activity
+ *
+ * @return array
+ * @throws APIRequestException
+ * @throws InvalidAccessTokenException
+ * @throws MovedPermanentlyException
+ */
+ public function request(Core $activity) {
+ $hosts = $this->getAPHostsFromActivity($activity);
+
+ $result = [];
+ foreach ($hosts as $host) {
+ $request = $this->generateRequest($host, $activity);
+ $result[] = $this->curlService->request($request);
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * @param APHosts $host
+ * @param Core $activity
+ *
+ * @return Request
+ */
+ public function generateRequest(APHosts $host, Core $activity): Request {
+
+ $document = json_encode($activity);
+ $date = date(self::DATE_FORMAT);
+
+ $localActor = $activity->getActor();
+ $uriIds = $host->getUriIds();
+ $remoteActor = $this->getRemoteActor($uriIds[0]);
+
+ $localActorLink =
+ $this->configService->getRoot() . '@' . $localActor->getPreferredUsername();
+ $signature = "(request-target): post /users/testSocial/inbox\nhost: " . $host->getAddress()
+ . "\ndate: " . $date;
+
+ openssl_sign($signature, $signed, $localActor->getPrivateKey(), OPENSSL_ALGO_SHA256);
+
+ $signed = base64_encode($signed);
+ $header =
+ 'keyId="' . $localActorLink . '",headers="(request-target) host date",signature="'
+ . $signed . '"';
+
+ $request = new Request('/users/testSocial/inbox', Request::TYPE_POST);
+ $request->addHeader('Host: ' . $host->getAddress());
+ $request->addHeader('Date: ' . $date);
+ $request->addHeader('Signature: ' . $header);
+
+ $request->setDataJson($document);
+ $request->setAddress($host->getAddress());
+
+ return $request;
+ }
+
+
+ /**
+ * @param IRequest $request
+ *
+ * @throws APIRequestException
+ * @throws InvalidAccessTokenException
+ * @throws MovedPermanentlyException
+ * @throws Exception
+ */
+ public function checkRequest(IRequest $request) {
+ $dTime = new DateTime($request->getHeader('date'));
+ $dTime->format(self::DATE_FORMAT);
+
+ if ($dTime->getTimestamp() < (time() - self::DATE_DELAY)) {
+ throw new Exception('object is too old');
+ }
+
+ $this->checkSignature($request);
+ }
+
+
+// /**
+// * @param Core $activity
+// *
+// * @return array
+// */
+// private function getHostsFromActivity(Core $activity) {
+//
+// $hosts = [];
+// $hosts[] = $this->getHostFromUriId($activity->getTo());
+// foreach ($activity->getToArray() as $to) {
+// $hosts[] = $this->getHostFromUriId($to);
+// }
+//
+// if ($activity instanceof Note) {
+// /** @var Note $activity */
+// $hosts[] = $this->getHostFromUriId($activity->getInReplyTo());
+// }
+//
+// $hosts = $this->cleaningHosts($hosts);
+//
+// return $hosts;
+// }
+
+
+ /**
+ * @param Core $activity
+ *
+ * @return APHosts[]
+ */
+ private function getAPHostsFromActivity(Core $activity) {
+
+ $hosts = [];
+// $uriIds = [];
+ $this->addAPHosts($activity->getTo(), $hosts);
+ foreach ($activity->getToArray() as $to) {
+ $this->addAPHosts($to, $hosts);
+// $uriIds[] = $to;
+ }
+
+ if ($activity instanceof Note) {
+ /** @var Note $activity */
+ $this->addAPHosts($activity->getInReplyTo(), $hosts);
+// $uriIds[] = $activity->getInReplyTo();
+ }
+
+// $uriId = $this->cleaningHosts($uriId);
+
+ return $hosts;
+ }
+
+
+ /**
+ * @param string $uriId
+ * @param APHosts[] $hosts
+ */
+ private function addAPHosts(string $uriId, array &$hosts) {
+ $address = $this->getHostFromUriId($uriId);
+ if ($address === '') {
+ return;
+ }
+
+ foreach ($hosts as $host) {
+ if ($host->getAddress() === $address) {
+ $host->addUriId($uriId);
+
+ return;
+ }
+ }
+
+ $apHost = new APHosts($address);
+ $apHost->addUriId($uriId);
+ $hosts[] = $apHost;
+ }
+
+
+ /**
+ * @param string $uriId
+ *
+ * @return mixed
+ * @throws APIRequestException
+ * @throws InvalidAccessTokenException
+ * @throws MovedPermanentlyException
+ */
+ private function getRemoteActor(string $uriId) {
+ $actor = $this->actorService->getFromUri($uriId);
+
+ return $actor;
+ }
+
+
+ /**
+ * @param string $uriId
+ *
+ * @return string
+ */
+ private function getHostFromUriId(string $uriId) {
+ $ignoreThose = [
+ '',
+ 'https://www.w3.org/ns/activitystreams#Public'
+ ];
+
+ if (in_array($uriId, $ignoreThose)) {
+ return '';
+ }
+
+ $url = parse_url($uriId);
+ if (!is_array($url) || !array_key_exists('host', $url)) {
+ return '';
+ }
+
+ return $url['host'];
+ }
+
+
+// /**
+// * @param array $hosts
+// *
+// * @return array
+// */
+// private function cleaningHosts(array $hosts) {
+// $ret = [];
+// foreach ($hosts as $host) {
+// if ($host === '') {
+// continue;
+// }
+//
+// $ret[] = $host;
+// }
+//
+// return $ret;
+// }
+
+
+ /**
+ * @param IRequest $request
+ *
+ * @throws APIRequestException
+ * @throws InvalidAccessTokenException
+ * @throws MovedPermanentlyException
+ * @throws Exception
+ */
+ private function checkSignature(IRequest $request) {
+ $signatureHeader = $request->getHeader('Signature');
+
+ $sign = $this->parseSignatureHeader($signatureHeader);
+ $this->miscService->mustContains(['keyId', 'headers', 'signature'], $sign);
+
+ $keyId = $sign['keyId'];
+ $headers = $sign['headers'];
+ $signed = base64_decode($sign['signature']);
+ $estimated = $this->generateEstimatedSignature($headers, $request);
+
+ $publicKey = $this->retrieveKey($keyId);
+ if (openssl_verify($estimated, $signed, $publicKey, 'sha256') !== 1) {
+ throw new Exception('signature cannot be checked');
+ }
+
+ }
+
+
+ /**
+ * @param string $headers
+ * @param IRequest $request
+ *
+ * @return string
+ */
+ private function generateEstimatedSignature(string $headers, IRequest $request): string {
+ $keys = explode(' ', $headers);
+
+ $estimated = "(request-target): post /apps/social/@maxence/inbox";
+ foreach ($keys as $key) {
+ if ($key === '(request-target)') {
+ continue;
+ }
+
+ $estimated .= "\n" . $key . ': ' . $request->getHeader($key);
+ }
+
+ return $estimated;
+ }
+
+
+ /**
+ * @param $signatureHeader
+ *
+ * @return array
+ */
+ private function parseSignatureHeader($signatureHeader) {
+ $sign = [];
+
+ $entries = explode(',', $signatureHeader);
+ foreach ($entries as $entry) {
+ list($k, $v) = explode('=', $entry, 2);
+ preg_match('/"([^"]+)"/', $v, $varr);
+ $v = trim($varr[0], '"');
+
+ $sign[$k] = $v;
+ }
+
+ return $sign;
+ }
+
+
+ /**
+ * @param $keyId
+ *
+ * @return array
+ * @throws MovedPermanentlyException
+ * @throws APIRequestException
+ * @throws InvalidAccessTokenException
+ */
+ private function retrieveKey($keyId) {
+ $actor = $this->retrieveObject($keyId);
+
+ return $actor['publicKey']['publicKeyPem'];
+ }
+
+//
+// /**
+// * @param $id
+// *
+// * @return mixed
+// * @throws MovedPermanentlyException
+// * @throws APIRequestException
+// * @throws InvalidAccessTokenException
+// */
+// private function retrieveObject($id) {
+// $url = parse_url($id);
+//
+// $request = new Request($url['path'], Request::TYPE_GET);
+// $request->setAddress($url['host']);
+//
+//// $key = $url['fragment'];
+//
+// return $this->curlService->request($request);
+// }
+
+
+ /**
+ * @param Core $activity
+ */
+ private function setupCore(Core $activity) {
+
+// $this->initCore($activity);
+ if ($activity->isTopLevel()) {
+ $activity->addEntry('@context', self::CONTEXT_ACTIVITYSTREAMS);
+ }
+
+ $coreService = $activity->savingAs();
+ if ($coreService !== null) {
+ $coreService->save($activity);
+ }
+
+ if ($activity->gotObject()) {
+ $this->setupCore($activity->getObject());
+ }
+ }
+
+
+// public function setRoot(IActivityPub $actor) {
+// $actor->setRoot('https://test.artificial-owl.com/apps/social');
+// }
+
+
+}
diff --git a/lib/Service/ActivityStreamsService.php b/lib/Service/ActivityStreamsService.php
index 9a76a31c..a73341d0 100644
--- a/lib/Service/ActivityStreamsService.php
+++ b/lib/Service/ActivityStreamsService.php
@@ -35,6 +35,7 @@ use Exception;
use OCA\Social\Db\ServiceAccountsRequest;
use OCA\Social\Exceptions\ActivityStreamsRequestException;
use OCA\Social\Exceptions\InvalidAccessTokenException;
+use OCA\Social\Model\ActivityPub\Core;
use OCA\Social\Model\ServiceAccount;
use OCA\Social\Traits\TOAuth2;
@@ -57,8 +58,8 @@ class ActivityStreamsService {
/** @var ConfigService */
private $configService;
- /** @var CurlService */
- private $curlService;
+ /** @var ClientCurlService */
+ private $clientCurlService;
/** @var MiscService */
private $miscService;
@@ -69,20 +70,32 @@ class ActivityStreamsService {
*
* @param ServiceAccountsRequest $serviceAccountsRequest
* @param ConfigService $configService
- * @param CurlService $curlService
+ * @param ClientCurlService $clientCurlService
* @param MiscService $miscService
*/
public function __construct(
ServiceAccountsRequest $serviceAccountsRequest, ConfigService $configService,
- CurlService $curlService, MiscService $miscService
+ ClientCurlService $clientCurlService, MiscService $miscService
) {
$this->serviceAccountsRequest = $serviceAccountsRequest;
$this->configService = $configService;
- $this->curlService = $curlService;
+ $this->clientCurlService = $clientCurlService;
$this->miscService = $miscService;
}
+ /**
+ * @param Core $core
+ */
+ public function initCore(Core &$core) {
+ $core->setRoot('https://test.artificial-owl.com/apps/social');
+ }
+
+
+
+
+ // TODO : clean below !
+
/**
* @param ServiceAccount $account
*
@@ -146,7 +159,7 @@ class ActivityStreamsService {
*/
private function request(ServiceAccount $account, Request $request) {
try {
- return $this->curlService->request($account, $request, true);
+ return $this->clientCurlService->request($account, $request, true);
} catch (InvalidAccessTokenException $e) {
// $this->oAuth2TokensRequest->resetToken($auth);
throw new ActivityStreamsRequestException($e->getMessage());
diff --git a/lib/Service/ActorService.php b/lib/Service/ActorService.php
new file mode 100644
index 00000000..3856eef7
--- /dev/null
+++ b/lib/Service/ActorService.php
@@ -0,0 +1,232 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Service;
+
+
+use daita\Traits\TArrayTools;
+use Exception;
+use OC\User\NoUserException;
+use OCA\Social\Db\ActorsRequest;
+use OCA\Social\Db\CacheActorsRequest;
+use OCA\Social\Exceptions\ActorAlreadyExistsException;
+use OCA\Social\Exceptions\ActorDoesNotExistException;
+use OCA\Social\Exceptions\APIRequestException;
+use OCA\Social\Exceptions\CacheActorDoesNotExistException;
+use OCA\Social\Exceptions\InvalidAccessTokenException;
+use OCA\Social\Exceptions\MovedPermanentlyException;
+use OCA\Social\Model\ActivityPub\Actor;
+
+class ActorService {
+
+
+ use TArrayTools;
+
+
+ /** @var UriIdService */
+ private $uriIdService;
+
+ /** @var ConfigService */
+ private $configService;
+
+ /** @var ActorsRequest */
+ private $actorsRequest;
+
+ /** @var CacheActorsRequest */
+ private $cacheActorsRequest;
+
+ /** @var MiscService */
+ private $miscService;
+
+
+ /**
+ * ActivityStreamsService constructor.
+ *
+ * @param ActorsRequest $actorsRequest
+ * @param CacheActorsRequest $cacheActorsRequest
+ * @param UriIdService $uriIdService
+ * @param ConfigService $configService
+ * @param MiscService $miscService
+ */
+ public function __construct(
+ ActorsRequest $actorsRequest,
+ CacheActorsRequest $cacheActorsRequest, UriIdService $uriIdService,
+ ConfigService $configService, MiscService $miscService
+ ) {
+ $this->configService = $configService;
+ $this->uriIdService = $uriIdService;
+ $this->actorsRequest = $actorsRequest;
+ $this->cacheActorsRequest = $cacheActorsRequest;
+ $this->miscService = $miscService;
+ }
+
+
+ /**
+ * @param string $username
+ *
+ * @return Actor
+ * @throws ActorDoesNotExistException
+ * @throws NoUserException
+ */
+ public function getActor(string $username): Actor {
+ $actor = $this->actorsRequest->getFromUsername($username);
+
+ return $actor;
+ }
+
+
+ /**
+ * @param string $userId
+ *
+ * @return Actor
+ * @throws ActorDoesNotExistException
+ * @throws NoUserException
+ */
+ public function getActorFromUserId(string $userId): Actor {
+ $this->miscService->confirmUserId($userId);
+ $actor = $this->actorsRequest->getFromUserId($userId);
+
+ return $actor;
+ }
+
+
+ /**
+ * @param string $uriId
+ *
+ * @return Actor
+ * @throws APIRequestException
+ * @throws InvalidAccessTokenException
+ * @throws MovedPermanentlyException
+ * @throws Exception
+ */
+ public function getFromUri(string $uriId) {
+
+ try {
+ $cache = $this->cacheActorsRequest->getFromUrl($uriId);
+
+ return $this->generateActor($cache->getActor());
+ } catch (CacheActorDoesNotExistException $e) {
+ $object = $this->uriIdService->retrieveObject($uriId);
+ $actor = $this->generateActor($object);
+ $this->cacheActorsRequest->create($actor, $object);
+
+ return $actor;
+ }
+ }
+
+
+ /**
+ * @param array $object
+ *
+ * @return Actor
+ */
+ public function generateActor(array $object) {
+ $actor = new Actor();
+
+ $actor->setId($this->get('id', $object));
+ $actor->setFollowers($this->get('followers', $object));
+ $actor->setFollowing($this->get('following', $object));
+ $actor->setInbox($this->get('inbox', $object));
+ $actor->setOutbox($this->get('outbox', $object));
+ $actor->setPublicKey($object['publicKey']['publicKeyPem']);
+ $actor->setPreferredUsername($this->get('preferredUsername', $object));
+ $actor->setAccount('@' . $actor->getPreferredUsername() . '@' . $object['_address']);
+
+// $actor->setSharedInbox($this->get(''))
+
+ return $actor;
+ }
+
+
+ /**
+ * @param string $userId
+ *
+ * @throws ActorAlreadyExistsException
+ * @throws NoUserException
+ * @throws Exception
+ */
+ public function createActor(string $userId, string $username) {
+
+ $this->miscService->confirmUserId($userId);
+ $this->checkActorUsername($username);
+ $this->configService->setCoreValue('public_webfinger', 'social/lib/webfinger.php');
+
+ try {
+ $this->actorsRequest->getFromUsername($username);
+ throw new ActorAlreadyExistsException('actor with that name already exist');
+ } catch (ActorDoesNotExistException $e) {
+ /* we do nohtin */
+ }
+
+ try {
+ $this->actorsRequest->getFromUserId($userId);
+ throw new ActorAlreadyExistsException('account for this user already exist');
+ } catch (ActorDoesNotExistException $e) {
+ /* we do nohtin */
+ }
+
+ $actor = new Actor();
+ $actor->setUserId($userId);
+ $actor->setPreferredUsername($username);
+
+ $this->generateKeys($actor);
+ $this->actorsRequest->create($actor);
+ }
+
+
+ /**
+ * @param $username
+ *
+ * @return bool
+ */
+ private function checkActorUsername($username) {
+ return;
+ }
+
+ /**
+ * @param Actor $actor
+ */
+ private function generateKeys(Actor &$actor) {
+ $res = openssl_pkey_new(
+ [
+ "digest_alg" => "rsa",
+ "private_key_bits" => 2048,
+ "private_key_type" => OPENSSL_KEYTYPE_RSA,
+ ]
+ );
+
+ openssl_pkey_export($res, $privateKey);
+ $publicKey = openssl_pkey_get_details($res)['key'];
+
+ $actor->setPublicKey($publicKey);
+ $actor->setPrivateKey($privateKey);
+ }
+
+
+}
diff --git a/lib/Service/ClientCurlService.php b/lib/Service/ClientCurlService.php
new file mode 100644
index 00000000..d5fcb72c
--- /dev/null
+++ b/lib/Service/ClientCurlService.php
@@ -0,0 +1,214 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Service;
+
+
+use daita\Model\Request;
+use OCA\Social\Exceptions\APIRequestException;
+use OCA\Social\Exceptions\InvalidAccessTokenException;
+use OCA\Social\Exceptions\MovedPermanentlyException;
+use OCA\Social\Model\Service;
+use OCA\Social\Model\ServiceAccount;
+
+class ClientCurlService {
+
+
+ /** @var MiscService */
+ private $miscService;
+
+
+ /**
+ * ClientCurlService constructor.
+ *
+ * @param MiscService $miscService
+ */
+ public function __construct(MiscService $miscService) {
+ $this->miscService = $miscService;
+ }
+
+
+ /**
+ * @param ServiceAccount $account
+ * @param Request $request
+ * @param bool $authed
+ *
+ * @return array
+ * @throws InvalidAccessTokenException
+ * @throws MovedPermanentlyException
+ * @throws APIRequestException
+ */
+ public function request(ServiceAccount $account, Request $request, bool $authed = true) {
+
+ $curl = $this->initRequest($account, $request, $authed);
+ $this->initRequestPost($curl, $request);
+ $this->initRequestPut($curl, $request);
+ $this->initRequestDelete($curl, $request);
+
+ $result = curl_exec($curl);
+ $code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+
+ echo 'code: ' . $code . "\n";
+ echo 'result: ' . json_encode($result) . "\n";
+
+ $this->parseRequestResultCode301($code);
+ $this->parseRequestResultCode401($code);
+ $this->parseRequestResultCode404($code);
+// $this->parseRequestResultCode503($code);
+// $this->parseRequestResultCode500($code);
+// $this->parseRequestResult($result);
+
+
+ return json_decode($result, true);
+ }
+
+
+ /**
+ * @param ServiceAccount $account
+ * @param Request $request
+ * @param bool $authed
+ *
+ * @return resource
+ */
+ private function initRequest(ServiceAccount $account, Request $request, bool $authed) {
+
+ $curl = $this->generateCurlRequest($account, $request);
+ $headers = $request->getHeaders();
+
+ if ($authed) {
+ $headers[] = 'Authorization: Bearer ' . $account->getAuth('token');
+ }
+
+ curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
+ curl_setopt($curl, CURLOPT_TIMEOUT, 20);
+
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
+
+ return $curl;
+ }
+
+
+ /**
+ * @param ServiceAccount $account
+ * @param Request $request
+ *
+ * @return resource
+ */
+ private function generateCurlRequest(ServiceAccount $account, Request $request) {
+ $service = $account->getService();
+ $url = 'https://' . $service->getAddress() . $request->getParsedUrl();
+
+ if ($request->getType() !== Request::TYPE_GET) {
+ $curl = curl_init($url);
+ } else {
+ $curl = curl_init($url . '?' . $request->getDataBody());
+ }
+
+ return $curl;
+ }
+
+
+ /**
+ * @param resource $curl
+ * @param Request $request
+ */
+ private function initRequestPost($curl, Request $request) {
+ if ($request->getType() !== Request::TYPE_POST) {
+ return;
+ }
+
+ curl_setopt($curl, CURLOPT_POST, true);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $request->getDataBody());
+ }
+
+
+ /**
+ * @param resource $curl
+ * @param Request $request
+ */
+ private function initRequestPut($curl, Request $request) {
+ if ($request->getType() !== Request::TYPE_PUT) {
+ return;
+ }
+
+ curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT");
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $request->getDataBody());
+ }
+
+
+ /**
+ * @param resource $curl
+ * @param Request $request
+ */
+ private function initRequestDelete($curl, Request $request) {
+ if ($request->getType() !== Request::TYPE_DELETE) {
+ return;
+ }
+
+ curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $request->getDataBody());
+ }
+
+
+ /**
+ * @param int $code
+ *
+ * @throws MovedPermanentlyException
+ */
+ private function parseRequestResultCode301($code) {
+ if ($code === 301) {
+ throw new MovedPermanentlyException('301 Moved Permanently');
+ }
+ }
+
+ /**
+ * @param int $code
+ *
+ * @throws InvalidAccessTokenException
+ */
+ private function parseRequestResultCode401($code) {
+ if ($code === 401) {
+ throw new InvalidAccessTokenException('401 Access Token Invalid');
+ }
+ }
+
+ /**
+ * @param int $code
+ *
+ * @throws APIRequestException
+ */
+ private function parseRequestResultCode404($code) {
+ if ($code === 404) {
+ throw new APIRequestException('404 Not Found');
+ }
+ }
+
+
+}
diff --git a/lib/Service/ConfigService.php b/lib/Service/ConfigService.php
index c165b76e..be155bee 100644
--- a/lib/Service/ConfigService.php
+++ b/lib/Service/ConfigService.php
@@ -70,7 +70,8 @@ class ConfigService {
* @param MiscService $miscService
*/
public function __construct(
- string $userId, IConfig $config, IRequest $request, MiscService $miscService
+ $userId, IConfig $config, IRequest $request,
+ MiscService $miscService
) {
$this->userId = $userId;
$this->config = $config;
@@ -176,6 +177,15 @@ class ConfigService {
}
+ /**
+ * @param string $key
+ * @param string $value
+ */
+ public function setCoreValue(string $key, string $value) {
+ $this->config->setAppValue('core', $key, $value);
+ }
+
+
/**
* @param $key
*
@@ -189,6 +199,32 @@ class ConfigService {
return $this->request->getServerHost();
}
+
+ /**
+ * @return string
+ */
+ public function getRoot(): string {
+ return 'https://test.artificial-owl.com/apps/social/';
+ }
+
+
+ /**
+ * @param string $path
+ * @param bool $generateId
+ *
+ * @return string
+ */
+ public function generateId(string $path = '', $generateId = true): string {
+ $this->miscService->formatPath($path);
+
+ $id = $this->getRoot() . $path;
+ if ($generateId === true) {
+ $id .= time() . crc32(uniqid());
+ }
+
+ return $id;
+ }
+
// /**
// * @return array
// */
diff --git a/lib/Service/CurlService.php b/lib/Service/CurlService.php
index 3560aa3a..15f57399 100644
--- a/lib/Service/CurlService.php
+++ b/lib/Service/CurlService.php
@@ -34,6 +34,7 @@ use daita\Model\Request;
use OCA\Social\Exceptions\APIRequestException;
use OCA\Social\Exceptions\InvalidAccessTokenException;
use OCA\Social\Exceptions\MovedPermanentlyException;
+use OCA\Social\Model\Service;
use OCA\Social\Model\ServiceAccount;
class CurlService {
@@ -44,7 +45,7 @@ class CurlService {
/**
- * CurlService constructor.
+ * ClientCurlService constructor.
*
* @param MiscService $miscService
*/
@@ -54,51 +55,53 @@ class CurlService {
/**
- * @param ServiceAccount $account
* @param Request $request
- * @param bool $authed
*
* @return array
+ * @throws APIRequestException
* @throws InvalidAccessTokenException
* @throws MovedPermanentlyException
- * @throws APIRequestException
*/
- public function request(ServiceAccount $account, Request $request, bool $authed = true) {
+ public function request(Request $request): array {
+
+ $curl = $this->initRequest($request);
- $curl = $this->initRequest($account, $request, $authed);
$this->initRequestPost($curl, $request);
$this->initRequestPut($curl, $request);
$this->initRequestDelete($curl, $request);
$result = curl_exec($curl);
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+
$this->parseRequestResultCode301($code);
- $this->parseRequestResultCode401($code);
+// $this->parseRequestResultCode401($code);
$this->parseRequestResultCode404($code);
// $this->parseRequestResultCode503($code);
// $this->parseRequestResultCode500($code);
// $this->parseRequestResult($result);
+ $ret = json_decode($result, true);
+ if (!is_array($ret)) {
+ $ret = ['_result' => $result];
+ }
- return json_decode($result, true);
+ $ret['_address'] = $request->getAddress();
+ $ret['_code'] = $code;
+
+ return $ret;
}
/**
- * @param ServiceAccount $account
* @param Request $request
- * @param bool $authed
*
* @return resource
*/
- private function initRequest(ServiceAccount $account, Request $request, bool $authed) {
+ private function initRequest(Request $request) {
- $curl = $this->generateCurlRequest($account, $request);
- $headers = [];
-
- if ($authed) {
- $headers[] = 'Authorization: Bearer ' . $account->getAuth('token');
- }
+ $curl = $this->generateCurlRequest($request);
+ $headers = $request->getHeaders();
+ $headers[] = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($curl, CURLOPT_TIMEOUT, 20);
@@ -111,14 +114,12 @@ class CurlService {
/**
- * @param ServiceAccount $account
* @param Request $request
*
* @return resource
*/
- private function generateCurlRequest(ServiceAccount $account, Request $request) {
- $service = $account->getService();
- $url = 'https://' . $service->getAddress() . $request->getParsedUrl();
+ private function generateCurlRequest(Request $request) {
+ $url = 'https://' . $request->getAddress() . $request->getParsedUrl();
if ($request->getType() !== Request::TYPE_GET) {
$curl = curl_init($url);
@@ -183,16 +184,6 @@ class CurlService {
}
}
- /**
- * @param int $code
- *
- * @throws InvalidAccessTokenException
- */
- private function parseRequestResultCode401($code) {
- if ($code === 401) {
- throw new InvalidAccessTokenException('401 Access Token Invalid');
- }
- }
/**
* @param int $code
diff --git a/lib/Service/ICoreService.php b/lib/Service/ICoreService.php
new file mode 100644
index 00000000..f0b94dcd
--- /dev/null
+++ b/lib/Service/ICoreService.php
@@ -0,0 +1,39 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Service;
+
+
+use OCA\Social\Model\ActivityPub\Core;
+
+interface ICoreService {
+
+ public function save(Core $core);
+
+}
diff --git a/lib/Service/MiscService.php b/lib/Service/MiscService.php
index 01e8bbfe..19dd7477 100644
--- a/lib/Service/MiscService.php
+++ b/lib/Service/MiscService.php
@@ -29,8 +29,11 @@ declare(strict_types=1);
namespace OCA\Social\Service;
+use Exception;
+use OC\User\NoUserException;
use OCA\Social\AppInfo\Application;
use OCP\ILogger;
+use OCP\IUserManager;
class MiscService {
@@ -38,14 +41,19 @@ class MiscService {
/** @var ILogger */
private $logger;
+ /** @var IUserManager */
+ private $userManager;
+
/**
* MiscService constructor.
*
* @param ILogger $logger
+ * @param IUserManager $userManager
*/
- public function __construct(ILogger $logger) {
+ public function __construct(ILogger $logger, IUserManager $userManager) {
$this->logger = $logger;
+ $this->userManager = $userManager;
}
@@ -63,6 +71,20 @@ class MiscService {
}
+ /**
+ * @param array $keys
+ * @param array $arr
+ *
+ * @throws Exception
+ */
+ public function mustContains(array $keys, array $arr) {
+ foreach ($keys as $key) {
+ if (!array_key_exists($key, $arr)) {
+ throw new Exception('missing elements');
+ }
+ }
+ }
+
public static function noEndSlash($path) {
if (substr($path, -1) === '/') {
$path = substr($path, 0, -1);
@@ -72,5 +94,39 @@ class MiscService {
}
+ /**
+ * @param string $path
+ */
+ public function formatPath(string &$path) {
+ if ($path === '') {
+ return;
+ }
+
+ if (substr($path, 0, 1) === '/') {
+ $path = substr($path, 1);
+ }
+
+ if (substr($path, -1) !== '/') {
+ $path .= '/';
+ }
+ }
+
+
+ /**
+ * @param string $userId
+ *
+ * @throws NoUserException
+ */
+ public function confirmUserId(string &$userId) {
+ $user = $this->userManager->get($userId);
+
+ return;
+ if ($user === null) {
+ throw new NoUserException('user does not exist');
+ }
+
+ $userId = $user->getUID();
+ }
+
}
diff --git a/lib/Service/ServicesService.php b/lib/Service/ServicesService.php
index ba23d3ef..0938e8b4 100644
--- a/lib/Service/ServicesService.php
+++ b/lib/Service/ServicesService.php
@@ -53,8 +53,8 @@ class ServicesService {
/** @var ConfigService */
private $configService;
- /** @var CurlService */
- private $curlService;
+ /** @var ClientCurlService */
+ private $clientCurlService;
/** @var MiscService */
private $miscService;
@@ -65,16 +65,16 @@ class ServicesService {
*
* @param ServicesRequest $servicesRequest
* @param ConfigService $configService
- * @param CurlService $curlService
+ * @param ClientCurlService $clientCurlService
* @param MiscService $miscService
*/
public function __construct(
- ServicesRequest $servicesRequest, ConfigService $configService, CurlService $curlService,
+ ServicesRequest $servicesRequest, ConfigService $configService, ClientCurlService $clientCurlService,
MiscService $miscService
) {
$this->servicesRequest = $servicesRequest;
$this->configService = $configService;
- $this->curlService = $curlService;
+ $this->clientCurlService = $clientCurlService;
$this->miscService = $miscService;
}
@@ -151,7 +151,7 @@ class ServicesService {
$request = new Request(ActivityStreamsService::URL_CREATE_APP, Request::TYPE_POST);
$request->setData($data);
- return $this->curlService->request($account, $request, false);
+ return $this->clientCurlService->request($account, $request, false);
}
diff --git a/lib/Service/UriIdService.php b/lib/Service/UriIdService.php
new file mode 100644
index 00000000..1e3ed300
--- /dev/null
+++ b/lib/Service/UriIdService.php
@@ -0,0 +1,86 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social\Service;
+
+
+use daita\Model\Request;
+use OCA\Social\Exceptions\APIRequestException;
+use OCA\Social\Exceptions\InvalidAccessTokenException;
+use OCA\Social\Exceptions\MovedPermanentlyException;
+
+class UriIdService {
+
+
+ /** @var ConfigService */
+ private $configService;
+
+
+ private $curlService;
+
+ /** @var MiscService */
+ private $miscService;
+
+
+ /**
+ * UriIdService constructor.
+ *
+ * @param ConfigService $configService
+ * @param CurlService $curlService
+ * @param MiscService $miscService
+ */
+ public function __construct(
+ ConfigService $configService, CurlService $curlService, MiscService $miscService
+ ) {
+ $this->configService = $configService;
+ $this->curlService = $curlService;
+ $this->miscService = $miscService;
+ }
+
+
+ /**
+ * @param $id
+ *
+ * @return mixed
+ * @throws MovedPermanentlyException
+ * @throws APIRequestException
+ * @throws InvalidAccessTokenException
+ */
+ public function retrieveObject($id) {
+ $url = parse_url($id);
+
+ $request = new Request($url['path'], Request::TYPE_GET);
+ $request->setAddress($url['host']);
+
+// $key = $url['fragment'];
+
+ return $this->curlService->request($request);
+ }
+
+}
diff --git a/lib/Tools/Model/Request.php b/lib/Tools/Model/Request.php
index b19e3e98..463ef067 100644
--- a/lib/Tools/Model/Request.php
+++ b/lib/Tools/Model/Request.php
@@ -47,6 +47,9 @@ class Request implements \JsonSerializable {
/** @var int */
private $type;
+ /** @var array */
+ private $headers = [];
+
/** @var array */
private $data = [];
@@ -88,6 +91,10 @@ class Request implements \JsonSerializable {
$url = $this->getUrl();
$ak = array_keys($this->getData());
foreach ($ak as $k) {
+ if (!is_string($this->data[$k])) {
+ continue;
+ }
+
$url = str_replace(':' . $k, $this->data[$k], $url);
}
@@ -111,6 +118,32 @@ class Request implements \JsonSerializable {
}
+ public function addHeader($header): Request {
+ $this->headers[] = $header;
+
+ return $this;
+ }
+
+
+ /**
+ * @return array
+ */
+ public function getHeaders(): array {
+ return $this->headers;
+ }
+
+ /**
+ * @param array $headers
+ *
+ * @return Request
+ */
+ public function setHeaders(array $headers): Request {
+ $this->headers = $headers;
+
+ return $this;
+ }
+
+
/**
* @return array
*/
@@ -185,14 +218,14 @@ class Request implements \JsonSerializable {
* @return string
*/
public function getDataBody() {
-
- if ($this->getData() === []) {
- return '';
- }
-
- return preg_replace(
- '/([(%5B)]{1})[0-9]+([(%5D)]{1})/', '$1$2', http_build_query($this->getData())
- );
+ return json_encode($this->getData());
+// if ($this->getData() === []) {
+// return '';
+// }
+//
+// return preg_replace(
+// '/([(%5B)]{1})[0-9]+([(%5D)]{1})/', '$1$2', http_build_query($this->getData())
+// );
}
diff --git a/lib/Tools/Traits/TNCDataResponse.php b/lib/Tools/Traits/TNCDataResponse.php
index 13d9e278..12ef036d 100644
--- a/lib/Tools/Traits/TNCDataResponse.php
+++ b/lib/Tools/Traits/TNCDataResponse.php
@@ -30,6 +30,7 @@ declare(strict_types=1);
namespace daita\Traits;
+use JsonSerializable;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
@@ -60,7 +61,17 @@ trait TNCDataResponse {
'status' => 1
];
- return new DataResponse($data, Http::STATUS_CREATED);
+ return new DataResponse($data, Http::STATUS_OK);
+ }
+
+
+ /**
+ * @param JsonSerializable $result
+ *
+ * @return DataResponse
+ */
+ private function directSuccess(JsonSerializable $result): DataResponse {
+ return new DataResponse($result, Http::STATUS_OK);
}
}
diff --git a/lib/webfinger.php b/lib/webfinger.php
new file mode 100644
index 00000000..8973ee19
--- /dev/null
+++ b/lib/webfinger.php
@@ -0,0 +1,66 @@
+
+ * @copyright 2018, Maxence Lange
+ * @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 .
+ *
+ */
+
+namespace OCA\Social;
+
+require_once __DIR__ . '/../appinfo/autoload.php';
+require_once(__DIR__ . '/../lib/autoload.php');
+
+if (!array_key_exists('resource', $_GET)) {
+ echo 'missing resource';
+ exit();
+}
+
+$subject = $_GET['resource'];
+
+$urlGenerator = \OC::$server->getURLGenerator();
+
+list($type, $account) = explode(':', $subject, 2);
+if ($type !== 'acct') {
+ echo 'no acct';
+ exit();
+}
+
+
+$username = substr($account, 0, strrpos($account, '@'));
+$finger = [
+ 'subject' => $subject,
+ 'links' => [
+ [
+ 'rel' => 'self',
+ 'type' => 'application/activity+json',
+ // 'href' => 'https://test.artificial-owl.com/apps/social/@' . $username
+ 'href' => $urlGenerator->linkToRouteAbsolute(
+ 'social.ActivityPub.aliasactor', ['username' => $username]
+ )
+ ]
+ ]
+];
+
+header('Content-type: application/json');
+echo json_encode($finger);
diff --git a/templates/actor.php b/templates/actor.php
new file mode 100644
index 00000000..cab3b5fc
--- /dev/null
+++ b/templates/actor.php
@@ -0,0 +1 @@
+ACTOR !
diff --git a/templates/followers.php b/templates/followers.php
new file mode 100644
index 00000000..82235440
--- /dev/null
+++ b/templates/followers.php
@@ -0,0 +1 @@
+FOLLOWERS !!!1
diff --git a/templates/following.php b/templates/following.php
new file mode 100644
index 00000000..c040e4d9
--- /dev/null
+++ b/templates/following.php
@@ -0,0 +1 @@
+FOLLOWING