forked from grumpydevelop/federator
		
	WIP inbox-support
- includes hacky following-mechanic in order to simulate a follow on mastodon (not properly working, need to also inject the user this creates into the followers db for the target mastodon-user) - created endpoint for inbox. SharedInbox is used when no user is provided (/api/federator/fedusers/inbox'), the regular inbox link now works (/users/username/inbox). - Retrieve all followers of sender and, if they're part of our system, send the activity into their personal inbox - Support Announce and Undo Activity-Types - Inbox currently converts to proper ActPub-objects and saves data to log-files
This commit is contained in:
		
							parent
							
								
									823283183e
								
							
						
					
					
						commit
						305ded4986
					
				
					 12 changed files with 1044 additions and 25 deletions
				
			
		| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
[contentnation]
 | 
					[contentnation]
 | 
				
			||||||
service-uri = https://contentnation.net
 | 
					service-uri = http://local.contentnation.net
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[userdata]
 | 
					[userdata]
 | 
				
			||||||
path = '/home/net/contentnation/userdata/htdocs/' // need to download local copy of image and put img-path here
 | 
					path = '/home/net/contentnation/userdata/htdocs/' // need to download local copy of image and put img-path here
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
[mastodon]
 | 
					[mastodon]
 | 
				
			||||||
service-uri = https://mastodon.social
 | 
					service-uri = http://mastodon.local
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[userdata]
 | 
					[userdata]
 | 
				
			||||||
path = '/home/net/contentnation/userdata/htdocs/' // need to download local copy of image and put img-path here
 | 
					path = '/home/net/contentnation/userdata/htdocs/' // need to download local copy of image and put img-path here
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,7 +46,7 @@ class Api extends Main
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function __construct()
 | 
					    public function __construct()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->contentType = "application/json";
 | 
					        $this->contentType = "application/activity+json";
 | 
				
			||||||
        Main::__construct();
 | 
					        Main::__construct();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,12 +52,20 @@ class FedUsers implements APIInterface
 | 
				
			||||||
        switch (sizeof($paths)) {
 | 
					        switch (sizeof($paths)) {
 | 
				
			||||||
            case 2:
 | 
					            case 2:
 | 
				
			||||||
                if ($method === 'GET') {
 | 
					                if ($method === 'GET') {
 | 
				
			||||||
                    // /users/username or /@username
 | 
					                    // /fedusers/username or /@username
 | 
				
			||||||
                    return $this->returnUserProfile($paths[1]);
 | 
					                    return $this->returnUserProfile($paths[1]);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    switch ($paths[1]) {
 | 
				
			||||||
 | 
					                        case 'inbox':
 | 
				
			||||||
 | 
					                            $handler = new FedUsers\Inbox($this->main);
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                        default:
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case 3:
 | 
					            case 3:
 | 
				
			||||||
                // /users/username/(inbox|outbox|following|followers)
 | 
					                // /fedusers/username/(inbox|outbox|following|followers)
 | 
				
			||||||
                switch ($paths[2]) {
 | 
					                switch ($paths[2]) {
 | 
				
			||||||
                    case 'following':
 | 
					                    case 'following':
 | 
				
			||||||
                        // $handler = new FedUsers\Following();
 | 
					                        // $handler = new FedUsers\Following();
 | 
				
			||||||
| 
						 | 
					@ -66,7 +74,19 @@ class FedUsers implements APIInterface
 | 
				
			||||||
                        // $handler = new FedUsers\Followers();
 | 
					                        // $handler = new FedUsers\Followers();
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
                    case 'inbox':
 | 
					                    case 'inbox':
 | 
				
			||||||
                        // $handler = new FedUsers\Inbox();
 | 
					                        $handler = new FedUsers\Inbox($this->main);
 | 
				
			||||||
 | 
					                        $user = $paths[1];
 | 
				
			||||||
 | 
					                        if (!preg_match("#^([^@]+)@([^/]+)#", $user, $matches) === 1) {
 | 
				
			||||||
 | 
					                            $hostUrl = $this->main->getHost();
 | 
				
			||||||
 | 
					                            if ($hostUrl !== false) {
 | 
				
			||||||
 | 
					                                $host = parse_url($hostUrl, PHP_URL_HOST);
 | 
				
			||||||
 | 
					                                $port = parse_url($hostUrl, PHP_URL_PORT);
 | 
				
			||||||
 | 
					                                if ($port !== null) {
 | 
				
			||||||
 | 
					                                    $host .= `:$port`;
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                $user = `$user@$host`;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
                    case 'outbox':
 | 
					                    case 'outbox':
 | 
				
			||||||
                        $handler = new FedUsers\Outbox($this->main);
 | 
					                        $handler = new FedUsers\Outbox($this->main);
 | 
				
			||||||
| 
						 | 
					@ -86,7 +106,7 @@ class FedUsers implements APIInterface
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case 4:
 | 
					            case 4:
 | 
				
			||||||
                // /users/username/collections/(features|tags)
 | 
					                // /fedusers/username/collections/(features|tags)
 | 
				
			||||||
                // not yet implemented
 | 
					                // not yet implemented
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										401
									
								
								php/federator/api/fedusers/inbox.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										401
									
								
								php/federator/api/fedusers/inbox.php
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,401 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * SPDX-FileCopyrightText: 2024 Sascha Nitsch (grumpydeveloper) https://contentnation.net/@grumpydevelop
 | 
				
			||||||
 | 
					 * SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Sascha Nitsch (grumpydeveloper)
 | 
				
			||||||
 | 
					 **/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Federator\Api\FedUsers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * handle activitypub outbox requests
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class Inbox implements \Federator\Api\FedUsers\FedUsersInterface
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * main instance
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @var \Federator\Main $main
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private $main;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * constructor
 | 
				
			||||||
 | 
					     * @param \Federator\Main $main main instance
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function __construct($main)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->main = $main;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * handle get call
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param string $_user user to fetch inbox for @unused-param
 | 
				
			||||||
 | 
					     * @return string|false response
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function get($_user)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * handle post call
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param string $_user user to add data to inbox
 | 
				
			||||||
 | 
					     * @return string|false response
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function post($_user)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $inboxActivity = null;
 | 
				
			||||||
 | 
					        $_rawInput = file_get_contents('php://input');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $activity = json_decode($_rawInput, true);
 | 
				
			||||||
 | 
					        $host = $_SERVER['SERVER_NAME'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $sendTo = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch ($activity['type']) {
 | 
				
			||||||
 | 
					            case 'Create':
 | 
				
			||||||
 | 
					                if (!isset($activity['object'])) {
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $obj = $activity['object'];
 | 
				
			||||||
 | 
					                $create = new \Federator\Data\ActivityPub\Common\Create();
 | 
				
			||||||
 | 
					                $create->setID($activity['id'])
 | 
				
			||||||
 | 
					                    ->setURL($activity['id'])
 | 
				
			||||||
 | 
					                    ->setPublished(strtotime($activity['published'] ?? $obj['published'] ?? 'now'))
 | 
				
			||||||
 | 
					                    ->setAActor($activity['actor']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (array_key_exists('cc', $activity)) {
 | 
				
			||||||
 | 
					                    foreach ($activity['cc'] as $cc) {
 | 
				
			||||||
 | 
					                        $create->addCC($cc);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (array_key_exists('to', $activity)) {
 | 
				
			||||||
 | 
					                    foreach ($activity['to'] as $to) {
 | 
				
			||||||
 | 
					                        $create->addTo($to);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                switch ($obj['type']) {
 | 
				
			||||||
 | 
					                    case 'Note':
 | 
				
			||||||
 | 
					                        $apNote = new \Federator\Data\ActivityPub\Common\Note();
 | 
				
			||||||
 | 
					                        $apNote->setID($obj['id'])
 | 
				
			||||||
 | 
					                            ->setPublished(strtotime($obj['published'] ?? 'now'))
 | 
				
			||||||
 | 
					                            ->setContent($obj['content'] ?? '')
 | 
				
			||||||
 | 
					                            ->setSummary($obj['summary'])
 | 
				
			||||||
 | 
					                            ->setURL($obj['url'])
 | 
				
			||||||
 | 
					                            ->setAttributedTo($obj['attributedTo'] ?? $activity['actor'])
 | 
				
			||||||
 | 
					                            ->addTo("https://www.w3.org/ns/activitystreams#Public");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (!empty($obj['sensitive'])) {
 | 
				
			||||||
 | 
					                            $apNote->setSensitive($obj['sensitive']);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        if (!empty($obj['conversation'])) {
 | 
				
			||||||
 | 
					                            $apNote->setConversation($obj['conversation']);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        if (!empty($obj['inReplyTo'])) {
 | 
				
			||||||
 | 
					                            $apNote->setInReplyTo($obj['inReplyTo']);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Handle attachments
 | 
				
			||||||
 | 
					                        if (!empty($obj['attachment']) && is_array($obj['attachment'])) {
 | 
				
			||||||
 | 
					                            foreach ($obj['attachment'] as $media) {
 | 
				
			||||||
 | 
					                                if (!isset($media['type'], $media['url']))
 | 
				
			||||||
 | 
					                                    continue;
 | 
				
			||||||
 | 
					                                $mediaObj = new \Federator\Data\ActivityPub\Common\APObject($media['type']);
 | 
				
			||||||
 | 
					                                $mediaObj->setURL($media['url']);
 | 
				
			||||||
 | 
					                                $apNote->addAttachment($mediaObj);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (array_key_exists('tag', $obj)) {
 | 
				
			||||||
 | 
					                            foreach ($obj['tag'] as $tag) {
 | 
				
			||||||
 | 
					                                $tagName = is_array($tag) && isset($tag['name']) ? $tag['name'] : (string) $tag;
 | 
				
			||||||
 | 
					                                $cleanName = preg_replace('/\s+/', '', ltrim($tagName, '#')); // Remove space and leading #
 | 
				
			||||||
 | 
					                                $tagObj = new \Federator\Data\ActivityPub\Common\Tag();
 | 
				
			||||||
 | 
					                                $tagObj->setName('#' . $cleanName)
 | 
				
			||||||
 | 
					                                    ->setHref("https://$host/tags/" . urlencode($cleanName))
 | 
				
			||||||
 | 
					                                    ->setType('Hashtag');
 | 
				
			||||||
 | 
					                                $apNote->addTag($tagObj);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        $create->setObject($apNote);
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    default:
 | 
				
			||||||
 | 
					                        error_log("Inbox::post we currently don't support the obj type " . $obj['type'] . "\n");
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $inboxActivity = $create;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case 'Announce':
 | 
				
			||||||
 | 
					                if (!isset($activity['object'])) {
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $objectURL = is_array($activity['object']) ? $activity['object']['id'] : $activity['object'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Fetch the original object (e.g. Note)
 | 
				
			||||||
 | 
					                [$response, $info] = \Federator\Main::getFromRemote($objectURL, ['Accept: application/activity+json']);
 | 
				
			||||||
 | 
					                if ($info['http_code'] != 200) {
 | 
				
			||||||
 | 
					                    print_r($info);
 | 
				
			||||||
 | 
					                    error_log("Inbox::post Failed to fetch original object for Announce: $objectURL\n");
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                $objData = json_decode($response, true);
 | 
				
			||||||
 | 
					                if ($objData === false || $objData === null || !is_array($objData)) {
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $announce = new \Federator\Data\ActivityPub\Common\Announce();
 | 
				
			||||||
 | 
					                $announce->setID($activity['id'])
 | 
				
			||||||
 | 
					                    ->setURL($activity['id'])
 | 
				
			||||||
 | 
					                    ->setPublished(strtotime($activity['published'] ?? 'now'))
 | 
				
			||||||
 | 
					                    ->setAActor($activity['actor']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (array_key_exists('cc', $activity)) {
 | 
				
			||||||
 | 
					                    foreach ($activity['cc'] as $cc) {
 | 
				
			||||||
 | 
					                        $announce->addCC($cc);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (array_key_exists('to', $activity)) {
 | 
				
			||||||
 | 
					                    foreach ($activity['to'] as $to) {
 | 
				
			||||||
 | 
					                        $announce->addTo($to);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Parse the shared object as a Note or something else
 | 
				
			||||||
 | 
					                switch ($objData['type']) {
 | 
				
			||||||
 | 
					                    case 'Note':
 | 
				
			||||||
 | 
					                        $note = new \Federator\Data\ActivityPub\Common\Note();
 | 
				
			||||||
 | 
					                        $note->setID($objData['id'])
 | 
				
			||||||
 | 
					                            ->setContent($objData['content'] ?? '')
 | 
				
			||||||
 | 
					                            ->setPublished(strtotime($objData['published'] ?? 'now'))
 | 
				
			||||||
 | 
					                            ->setURL($objData['url'] ?? $objData['id'])
 | 
				
			||||||
 | 
					                            ->setAttributedTo($objData['attributedTo'] ?? null)
 | 
				
			||||||
 | 
					                            ->addTo("https://www.w3.org/ns/activitystreams#Public");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (array_key_exists('cc', $objData)) {
 | 
				
			||||||
 | 
					                            foreach ($objData['cc'] as $cc) {
 | 
				
			||||||
 | 
					                                $note->addCC($cc);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        $announce->setObject($note);
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    default:
 | 
				
			||||||
 | 
					                        // fallback object
 | 
				
			||||||
 | 
					                        $fallback = new \Federator\Data\ActivityPub\Common\APObject($objData['type']);
 | 
				
			||||||
 | 
					                        $fallback->setID($objData['id'] ?? $objectURL);
 | 
				
			||||||
 | 
					                        $announce->setObject($fallback);
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $inboxActivity = $announce;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case 'Undo':
 | 
				
			||||||
 | 
					                if (!isset($activity['object'])) {
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $undo = new \Federator\Data\ActivityPub\Common\Undo();
 | 
				
			||||||
 | 
					                $undo->setID($activity['id'] ?? "test")
 | 
				
			||||||
 | 
					                    ->setURL($activity['url'] ?? $activity['id'])
 | 
				
			||||||
 | 
					                    ->setActor($activity['actor'] ?? null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (array_key_exists('cc', $activity)) {
 | 
				
			||||||
 | 
					                    foreach ($activity['cc'] as $cc) {
 | 
				
			||||||
 | 
					                        $undo->addCC($cc);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (array_key_exists('to', $activity)) {
 | 
				
			||||||
 | 
					                    foreach ($activity['to'] as $to) {
 | 
				
			||||||
 | 
					                        $undo->addTo($to);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // what was undone
 | 
				
			||||||
 | 
					                $undone = $activity['object'];
 | 
				
			||||||
 | 
					                if (is_array($undone) && isset($undone['type'])) {
 | 
				
			||||||
 | 
					                    switch ($undone['type']) {
 | 
				
			||||||
 | 
					                        case 'Announce':
 | 
				
			||||||
 | 
					                            $announce = new \Federator\Data\ActivityPub\Common\Announce();
 | 
				
			||||||
 | 
					                            $announce->setID($undone['id'] ?? null)
 | 
				
			||||||
 | 
					                                ->setAActor($undone['actor'] ?? null)
 | 
				
			||||||
 | 
					                                ->setURL($undone['url'] ?? $undone['id'])
 | 
				
			||||||
 | 
					                                ->setPublished(strtotime($undone['published'] ?? 'now'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            if (array_key_exists('cc', $undone)) {
 | 
				
			||||||
 | 
					                                foreach ($undone['cc'] as $cc) {
 | 
				
			||||||
 | 
					                                    $announce->addCC($cc);
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            $undo->setObject($announce);
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                        case 'Follow':
 | 
				
			||||||
 | 
					                            // Implement if needed
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                        default:
 | 
				
			||||||
 | 
					                            // Fallback for unknown types
 | 
				
			||||||
 | 
					                            $apObject = new \Federator\Data\ActivityPub\Common\APObject($undone['type']);
 | 
				
			||||||
 | 
					                            $apObject->setID($undone['id'] ?? null);
 | 
				
			||||||
 | 
					                            $undo->setObject($apObject);
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $inboxActivity = $undo;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                error_log("Inbox::post we currently don't support the activity type " . $activity['type'] . "\n");
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Shared inbox
 | 
				
			||||||
 | 
					        if (!$_user) {
 | 
				
			||||||
 | 
					            $rootDir = $_SERVER['DOCUMENT_ROOT'] . '../';
 | 
				
			||||||
 | 
					            file_put_contents(
 | 
				
			||||||
 | 
					                $rootDir . 'logs/inbox.log',
 | 
				
			||||||
 | 
					                date('Y-m-d H:i:s') . ": ==== WILL TRY WORK WITH ACTIVITY ====\n" . json_encode($activity, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) . "\n\n",
 | 
				
			||||||
 | 
					                FILE_APPEND
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Save the raw input and parsed JSON to a file for inspection
 | 
				
			||||||
 | 
					            file_put_contents(
 | 
				
			||||||
 | 
					                $rootDir . 'logs/inbox.log',
 | 
				
			||||||
 | 
					                date('Y-m-d H:i:s') . ": ==== POST Inbox Activity ====\n" . json_encode($inboxActivity, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) . "\n\n",
 | 
				
			||||||
 | 
					                FILE_APPEND
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $sendTo = $inboxActivity->getCC();
 | 
				
			||||||
 | 
					        if ($inboxActivity->getType() === 'Undo') {
 | 
				
			||||||
 | 
					            $sendTo = $inboxActivity->getObject()->getCC();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $users = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        foreach ($sendTo as $receiver) {
 | 
				
			||||||
 | 
					            if (!$receiver || !is_string($receiver)) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (str_ends_with($receiver, '/followers')) {
 | 
				
			||||||
 | 
					                $users = array_merge($users, $this->fetchAllFollowers($receiver, $host));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($_user !== false && in_array($_user, $users)) {
 | 
				
			||||||
 | 
					            $users[] = $_user;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        foreach ($users as $user) {
 | 
				
			||||||
 | 
					            if (!$user)
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $this->postForUser($user, $inboxActivity);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return json_encode($inboxActivity, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * handle post call for specific user
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param string $_user user to add data to inbox
 | 
				
			||||||
 | 
					     * @param \Federator\Data\ActivityPub\Common\Activity $inboxActivity the activity that we received
 | 
				
			||||||
 | 
					     * @return string|false response
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private function postForUser($_user, $inboxActivity)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($_user) {
 | 
				
			||||||
 | 
					            $dbh = $this->main->getDatabase();
 | 
				
			||||||
 | 
					            $cache = $this->main->getCache();
 | 
				
			||||||
 | 
					            $connector = $this->main->getConnector();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // get user
 | 
				
			||||||
 | 
					            $user = \Federator\DIO\User::getUserByName(
 | 
				
			||||||
 | 
					                $dbh,
 | 
				
			||||||
 | 
					                $_user,
 | 
				
			||||||
 | 
					                $connector,
 | 
				
			||||||
 | 
					                $cache
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            if ($user->id === null) {
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $rootDir = $_SERVER['DOCUMENT_ROOT'] . '../';
 | 
				
			||||||
 | 
					        // Save the raw input and parsed JSON to a file for inspection
 | 
				
			||||||
 | 
					        file_put_contents(
 | 
				
			||||||
 | 
					            $rootDir . 'logs/inbox_' . $_user . '.log',
 | 
				
			||||||
 | 
					            date('Y-m-d H:i:s') . ": ==== POST " . $_user . " Inbox Activity ====\n" . json_encode($inboxActivity, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) . "\n\n",
 | 
				
			||||||
 | 
					            FILE_APPEND
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * fetch all followers from url and return the ones that belong to our server
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param string $collectionUrl The url of f.e. the posters followers
 | 
				
			||||||
 | 
					     * @param string $host our current host-url
 | 
				
			||||||
 | 
					     * @return array|false the names of the followers that are hosted on our server
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private function fetchAllFollowers(string $collectionUrl, string $host): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $users = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [$collectionResponse, $collectionInfo] = \Federator\Main::getFromRemote($collectionUrl, ['Accept: application/activity+json']);
 | 
				
			||||||
 | 
					        if ($collectionInfo['http_code'] !== 200) {
 | 
				
			||||||
 | 
					            error_log("Inbox::fetchAllFollowers Failed to fetch follower collection metadata from $collectionUrl");
 | 
				
			||||||
 | 
					            return [];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $collectionData = json_decode($collectionResponse, true);
 | 
				
			||||||
 | 
					        $nextPage = $collectionData['first'] ?? $collectionData['current'] ?? null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!$nextPage) {
 | 
				
			||||||
 | 
					            error_log("Inbox::fetchAllFollowers No 'first' or 'current' page in collection at $collectionUrl");
 | 
				
			||||||
 | 
					            return [];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Loop through all pages
 | 
				
			||||||
 | 
					        while ($nextPage) {
 | 
				
			||||||
 | 
					            [$pageResponse, $pageInfo] = \Federator\Main::getFromRemote($nextPage, ['Accept: application/activity+json']);
 | 
				
			||||||
 | 
					            if ($pageInfo['http_code'] !== 200) {
 | 
				
			||||||
 | 
					                error_log("Inbox::fetchAllFollowers Failed to fetch follower page at $nextPage");
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $pageData = json_decode($pageResponse, true);
 | 
				
			||||||
 | 
					            $items = $pageData['orderedItems'] ?? $pageData['items'] ?? [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach ($items as $followerUrl) {
 | 
				
			||||||
 | 
					                $parts = parse_url($followerUrl);
 | 
				
			||||||
 | 
					                if (!isset($parts['host']) || !str_ends_with($parts['host'], $host)) {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                [$actorResponse, $actorInfo] = \Federator\Main::getFromRemote($followerUrl, ['Accept: application/activity+json']);
 | 
				
			||||||
 | 
					                if ($actorInfo['http_code'] !== 200) {
 | 
				
			||||||
 | 
					                    error_log("Inbox::fetchAllFollowers Failed to fetch actor data for follower: $followerUrl");
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $actorData = json_decode($actorResponse, true);
 | 
				
			||||||
 | 
					                if (isset($actorData['preferredUsername'])) {
 | 
				
			||||||
 | 
					                    $users[] = $actorData['preferredUsername'];
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $nextPage = $pageData['next'] ?? null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $users;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -23,7 +23,7 @@ class Dummy implements \Federator\Api\APIInterface
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * internal message to output
 | 
					     * internal message to output
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @var array<string, mixed> $message
 | 
					     * @var string $response
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private $message = [];
 | 
					    private $message = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,26 +47,97 @@ class Dummy implements \Federator\Api\APIInterface
 | 
				
			||||||
    public function exec($paths, $user): bool
 | 
					    public function exec($paths, $user): bool
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // only for user with the 'publish' permission
 | 
					        // only for user with the 'publish' permission
 | 
				
			||||||
        if ($user === false || $user->hasPermission('publish') === false) {
 | 
					        // if ($user === false || $user->hasPermission('publish') === false) {
 | 
				
			||||||
            throw new \Federator\Exceptions\PermissionDenied();
 | 
					        //     throw new \Federator\Exceptions\PermissionDenied();
 | 
				
			||||||
        }
 | 
					        // }
 | 
				
			||||||
        $method = $_SERVER["REQUEST_METHOD"];
 | 
					        $method = $_SERVER["REQUEST_METHOD"];
 | 
				
			||||||
        switch ($method) {
 | 
					        switch ($method) {
 | 
				
			||||||
            case 'GET':
 | 
					            case 'GET':
 | 
				
			||||||
                switch (sizeof($paths)) {
 | 
					                switch (sizeof($paths)) {
 | 
				
			||||||
                    case 3:
 | 
					                    case 3:
 | 
				
			||||||
                        if ($paths[2] === 'moo') {
 | 
					                        switch ($paths[2]) {
 | 
				
			||||||
 | 
					                            case 'moo':
 | 
				
			||||||
                                return $this->getDummy();
 | 
					                                return $this->getDummy();
 | 
				
			||||||
 | 
					                            case 'sharedInbox':
 | 
				
			||||||
 | 
					                                return $this->getSharedInbox();
 | 
				
			||||||
 | 
					                            default:
 | 
				
			||||||
 | 
					                                break;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    case 4:
 | 
				
			||||||
 | 
					                    case 5:
 | 
				
			||||||
 | 
					                        switch ($paths[2]) {
 | 
				
			||||||
 | 
					                            case 'inbox':
 | 
				
			||||||
 | 
					                                return $this->getInbox($paths[3]);
 | 
				
			||||||
 | 
					                            case 'follow':
 | 
				
			||||||
 | 
					                                return $this->followAdmin($paths[3]);
 | 
				
			||||||
 | 
					                            case 'users':
 | 
				
			||||||
 | 
					                                switch (sizeof($paths)) {
 | 
				
			||||||
 | 
					                                    case 4:
 | 
				
			||||||
 | 
					                                        return $this->getUser($paths[3]);
 | 
				
			||||||
 | 
					                                    case 5:
 | 
				
			||||||
 | 
					                                        switch ($paths[4]) {
 | 
				
			||||||
 | 
					                                            case 'inbox':
 | 
				
			||||||
 | 
					                                                return $this->getInbox($paths[3]);
 | 
				
			||||||
 | 
					                                            case 'outbox':
 | 
				
			||||||
 | 
					                                                return $this->getOutbox($paths[3]);
 | 
				
			||||||
 | 
					                                            case 'following':
 | 
				
			||||||
 | 
					                                                return $this->getFollowing($paths[3]);
 | 
				
			||||||
 | 
					                                            case 'followers':
 | 
				
			||||||
 | 
					                                                return $this->getFollowing($paths[3]);
 | 
				
			||||||
 | 
					                                            default:
 | 
				
			||||||
 | 
					                                                break;
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                        break;
 | 
				
			||||||
 | 
					                                    default:
 | 
				
			||||||
 | 
					                                        break;
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            default:
 | 
				
			||||||
 | 
					                                break;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case 'POST':
 | 
					            case 'POST':
 | 
				
			||||||
                switch (sizeof($paths)) {
 | 
					                switch (sizeof($paths)) {
 | 
				
			||||||
                    case 3:
 | 
					                    case 3:
 | 
				
			||||||
                        if ($paths[2] === 'moo') {
 | 
					                        switch ($paths[2]) {
 | 
				
			||||||
 | 
					                            case 'moo':
 | 
				
			||||||
                                return $this->postDummy();
 | 
					                                return $this->postDummy();
 | 
				
			||||||
 | 
					                            case 'sharedInbox':
 | 
				
			||||||
 | 
					                                return $this->postSharedInbox();
 | 
				
			||||||
 | 
					                            default:
 | 
				
			||||||
 | 
					                                break;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
 | 
					                    case 4:
 | 
				
			||||||
 | 
					                    case 5:
 | 
				
			||||||
 | 
					                        switch ($paths[2]) {
 | 
				
			||||||
 | 
					                            case 'inbox':
 | 
				
			||||||
 | 
					                                return $this->postInbox($paths[3]);
 | 
				
			||||||
 | 
					                            case 'follow':
 | 
				
			||||||
 | 
					                                return $this->followAdmin($paths[3]);
 | 
				
			||||||
 | 
					                            case 'users':
 | 
				
			||||||
 | 
					                                switch (sizeof($paths)) {
 | 
				
			||||||
 | 
					                                    case 5:
 | 
				
			||||||
 | 
					                                        switch ($paths[4]) {
 | 
				
			||||||
 | 
					                                            case 'inbox':
 | 
				
			||||||
 | 
					                                                return $this->postInbox($paths[3]);
 | 
				
			||||||
 | 
					                                            case 'outbox':
 | 
				
			||||||
 | 
					                                                return $this->postOutbox($paths[3]);
 | 
				
			||||||
 | 
					                                            case 'following':
 | 
				
			||||||
 | 
					                                                return $this->postFollowing($paths[3]);
 | 
				
			||||||
 | 
					                                            case 'followers':
 | 
				
			||||||
 | 
					                                                return $this->postFollowing($paths[3]);
 | 
				
			||||||
 | 
					                                            default:
 | 
				
			||||||
 | 
					                                                break;
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                        break;
 | 
				
			||||||
 | 
					                                    default:
 | 
				
			||||||
 | 
					                                        break;
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            default:
 | 
				
			||||||
 | 
					                                break;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $this->main->setResponseCode(404);
 | 
					        $this->main->setResponseCode(404);
 | 
				
			||||||
| 
						 | 
					@ -80,16 +151,190 @@ class Dummy implements \Federator\Api\APIInterface
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function getDummy()
 | 
					    public function getDummy()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->message = [
 | 
					        $this->message = json_encode([
 | 
				
			||||||
            'r1' => '             (__)       ',
 | 
					            'r1' => '             (__)       ',
 | 
				
			||||||
            'r2' => '      `------(oo)       ',
 | 
					            'r2' => '      `------(oo)       ',
 | 
				
			||||||
            'r3' => '       || __ (__)       ',
 | 
					            'r3' => '       || __ (__)       ',
 | 
				
			||||||
            'r4' => '       ||w  ||          ',
 | 
					            'r4' => '       ||w  ||          ',
 | 
				
			||||||
            'r5' => '                        '
 | 
					            'r5' => '                        '
 | 
				
			||||||
        ];
 | 
					        ], JSON_PRETTY_PRINT);
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getUser($_name)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        error_log("Someone tried to get user: " . $_name);
 | 
				
			||||||
 | 
					        $user = \Federator\DIO\User::getUserByName(
 | 
				
			||||||
 | 
					            $this->main->getDatabase(),
 | 
				
			||||||
 | 
					            $_name,
 | 
				
			||||||
 | 
					            $this->main->getConnector(),
 | 
				
			||||||
 | 
					            $this->main->getCache()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        if ($user === false || $user->id === null) {
 | 
				
			||||||
 | 
					            throw new \Federator\Exceptions\FileNotFound();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $publicKeyPem = <<<PEM
 | 
				
			||||||
 | 
					-----BEGIN PUBLIC KEY-----
 | 
				
			||||||
 | 
					MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1MDmIPDcTey9lNYicfho
 | 
				
			||||||
 | 
					u3EeVKeQkm1FkFl4Yoj1FW0SFyGkgtPr8hgAL1JIqyrFokgbPRtihmhTUHaQNoV8
 | 
				
			||||||
 | 
					Uj5UIKG6zM1y1dHWizqwQw13pSMWri3IcSf08GSiolYBb19A98EMzIyGZHzjlfw8
 | 
				
			||||||
 | 
					VAhW6qL6ML5YAR2YvckRRpS4pPVseQLHfDzkWlyXePJQInMai0kdrH39XiXw8B0C
 | 
				
			||||||
 | 
					ver+7I1Z3rzJu+iOLmlblekFJtWDiipjuMedzluL3mNwV9Lk1ka1m7vHrtyqjtv1
 | 
				
			||||||
 | 
					X5FLRXVzpFziJsIpWZ6ojU9KRX8l4yvv9FL4dZIn7edbcosvnNDpnvEl+NsGnf4R
 | 
				
			||||||
 | 
					1wIDAQAB
 | 
				
			||||||
 | 
					-----END PUBLIC KEY-----
 | 
				
			||||||
 | 
					PEM;
 | 
				
			||||||
 | 
					$publicKeyPemJsonSafe = json_encode($publicKeyPem); // gives string with \n inside
 | 
				
			||||||
 | 
					        $data = [
 | 
				
			||||||
 | 
					            'iconMediaType' => $user->iconMediaType,
 | 
				
			||||||
 | 
					            'iconURL' => $user->iconURL,
 | 
				
			||||||
 | 
					            'imageMediaType' => $user->imageMediaType,
 | 
				
			||||||
 | 
					            'imageURL' => $user->imageURL,
 | 
				
			||||||
 | 
					            'fqdn' => '192.168.178.143',
 | 
				
			||||||
 | 
					            'name' => $user->name,
 | 
				
			||||||
 | 
					            'username' => $user->id,
 | 
				
			||||||
 | 
					            'publickey' => "<placeholderPublicKey>",
 | 
				
			||||||
 | 
					            'registered' => gmdate('Y-m-d\TH:i:s\Z', $user->registered), // 2021-03-25T00:00:00Z
 | 
				
			||||||
 | 
					            'summary' => $user->summary,
 | 
				
			||||||
 | 
					            'type' => "Person"
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					        $this->message = $this->main->renderTemplate('user.json', $data);
 | 
				
			||||||
 | 
					        $fixedJson = str_replace(
 | 
				
			||||||
 | 
					            'https://192.168.178.143/users/yannis_test',
 | 
				
			||||||
 | 
					            'https://192.168.178.143/api/federator/v1/dummy/users/yannis_test',
 | 
				
			||||||
 | 
					            $this->message
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        $fixedJson = preg_replace(
 | 
				
			||||||
 | 
					            '/"id"\s*:\s*"[^"]+"/',
 | 
				
			||||||
 | 
					            '"id": "http://192.168.178.143/api/federator/v1/dummy/users/yannis_test"',
 | 
				
			||||||
 | 
					            $fixedJson
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        $fixedJson = preg_replace(
 | 
				
			||||||
 | 
					            '/"inbox"\s*:\s*"[^"]+"/',
 | 
				
			||||||
 | 
					            '"inbox": "http://192.168.178.143/users/yannis_test/inbox"',
 | 
				
			||||||
 | 
					            $fixedJson
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        $fixedJson = str_replace(
 | 
				
			||||||
 | 
					            'https://192.168.178.143',
 | 
				
			||||||
 | 
					            'http://192.168.178.143',
 | 
				
			||||||
 | 
					            $fixedJson
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        $fixedJson = str_replace(
 | 
				
			||||||
 | 
					            '"<placeholderPublicKey>"',
 | 
				
			||||||
 | 
					            $publicKeyPemJsonSafe,
 | 
				
			||||||
 | 
					            $fixedJson
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        $fixedJson = str_replace(
 | 
				
			||||||
 | 
					            'http://192.168.178.143/inbox',
 | 
				
			||||||
 | 
					            'http://192.168.178.143/api/federator/fedusers/inbox',
 | 
				
			||||||
 | 
					            $fixedJson
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        // $fixedJson = str_replace(
 | 
				
			||||||
 | 
					        //     'http://192.168.178.143/api/federator/v1/dummy/users/yannis_test@192.168.178.143#main-key',
 | 
				
			||||||
 | 
					        //     'http://192.168.178.143/api/federator/v1/dummy/users/yannis_test@192.168.178.143/key#main-key',
 | 
				
			||||||
 | 
					        //     $fixedJson
 | 
				
			||||||
 | 
					        // );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->message = $fixedJson;
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function followAdmin($_name)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $user = \Federator\DIO\User::getUserByName(
 | 
				
			||||||
 | 
					            $this->main->getDatabase(),
 | 
				
			||||||
 | 
					            $_name,
 | 
				
			||||||
 | 
					            $this->main->getConnector(),
 | 
				
			||||||
 | 
					            $this->main->getCache()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        if ($user === false || $user->id === null) {
 | 
				
			||||||
 | 
					            throw new \Federator\Exceptions\FileNotFound();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Step 2: Prepare the Follow activity
 | 
				
			||||||
 | 
					        $activityData = [
 | 
				
			||||||
 | 
					            '@context' => 'https://www.w3.org/ns/activitystreams',
 | 
				
			||||||
 | 
					            'type' => 'Follow',
 | 
				
			||||||
 | 
					            'actor' => 'http://192.168.178.143/api/federator/v1/dummy/users/' . $_name,  // Your user URL
 | 
				
			||||||
 | 
					            'object' => 'http://mastodon.local/users/admin'  // Mastodon user to follow (e.g., http://mastodon.local/users/admin)
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Step 3: Send the Follow activity to Mastodon
 | 
				
			||||||
 | 
					        $inboxUrl = 'http://mastodon.local/users/admin/inbox';  // The inbox URL for the Mastodon user
 | 
				
			||||||
 | 
					        $this->sendFollowActivityToMastodon($inboxUrl, $activityData);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->message = "\n";
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function sendFollowActivityToMastodon($url, $data)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $json = json_encode($data, JSON_UNESCAPED_SLASHES);
 | 
				
			||||||
 | 
					        $digest = 'SHA-256=' . base64_encode(hash('sha256', $json, true));
 | 
				
			||||||
 | 
					        $date = gmdate('D, d M Y H:i:s') . ' GMT';
 | 
				
			||||||
 | 
					        $parsed = parse_url($url);
 | 
				
			||||||
 | 
					        $host = $parsed['host'];
 | 
				
			||||||
 | 
					        $path = $parsed['path'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Build the signature string
 | 
				
			||||||
 | 
					        $signatureString = "(request-target): post {$path}\n" .
 | 
				
			||||||
 | 
					            "host: {$host}\n" .
 | 
				
			||||||
 | 
					            "date: {$date}\n" .
 | 
				
			||||||
 | 
					            "digest: {$digest}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Load your private key here (replace with how you store keys)
 | 
				
			||||||
 | 
					        $privateKey = "REDACTED"; // OR from DB
 | 
				
			||||||
 | 
					        $pkeyId = openssl_pkey_get_private($privateKey);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!$pkeyId) {
 | 
				
			||||||
 | 
					            throw new \Exception('Invalid private key');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        openssl_sign($signatureString, $signature, $pkeyId, OPENSSL_ALGO_SHA256);
 | 
				
			||||||
 | 
					        $signature_b64 = base64_encode($signature);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Build keyId (public key ID from your actor object)
 | 
				
			||||||
 | 
					        $keyId = 'http://192.168.178.143/api/federator/v1/dummy/users/yannis_test#main-key';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $signatureHeader = 'keyId="' . $keyId . '",algorithm="rsa-sha256",headers="(request-target) host date digest",signature="' . $signature_b64 . '"';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $headers = [
 | 
				
			||||||
 | 
					            'Host: ' . $host,
 | 
				
			||||||
 | 
					            'Date: ' . $date,
 | 
				
			||||||
 | 
					            'Digest: ' . $digest,
 | 
				
			||||||
 | 
					            'Content-Type: application/activity+json',
 | 
				
			||||||
 | 
					            'Signature: ' . $signatureHeader,
 | 
				
			||||||
 | 
					            'Accept: application/activity+json',
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $ch = curl_init($url);
 | 
				
			||||||
 | 
					        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
 | 
				
			||||||
 | 
					        curl_setopt($ch, CURLOPT_POST, true);
 | 
				
			||||||
 | 
					        curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
 | 
				
			||||||
 | 
					        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $response = curl_exec($ch);
 | 
				
			||||||
 | 
					        $err = curl_error($ch);
 | 
				
			||||||
 | 
					        curl_close($ch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $response = curl_exec($ch);
 | 
				
			||||||
 | 
					        curl_close($ch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Log the response for debugging if needed
 | 
				
			||||||
 | 
					        if ($response === false) {
 | 
				
			||||||
 | 
					            error_log("Failed to send Follow activity to Mastodon: " . curl_error($ch));
 | 
				
			||||||
 | 
					            echo "Failed to send Follow activity to Mastodon: " . curl_error($ch);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
 | 
				
			||||||
 | 
					            if ($httpcode !== 200 && $httpcode !== 202) {
 | 
				
			||||||
 | 
					                throw new \Exception("Unexpected HTTP code $httpcode: $response");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            error_log("Follow activity response from Mastodon: " . $response);
 | 
				
			||||||
 | 
					            echo "Follow activity response from Mastodon: " . $response;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * post function for /v1/dummy/moo"
 | 
					     * post function for /v1/dummy/moo"
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
| 
						 | 
					@ -100,6 +345,216 @@ class Dummy implements \Federator\Api\APIInterface
 | 
				
			||||||
        return $this->getDummy();
 | 
					        return $this->getDummy();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getInbox($_name)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $_rawInput = file_get_contents('php://input');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Decode if it's JSON (as Mastodon usually sends JSON)
 | 
				
			||||||
 | 
					        $jsonData = json_decode($_rawInput, true);
 | 
				
			||||||
 | 
					        error_log("=== Masto GET Inbox Raw ===\n" . $_rawInput);
 | 
				
			||||||
 | 
					        error_log("=== Masto GET Inbox JSON ===\n" . print_r($jsonData, true));
 | 
				
			||||||
 | 
					        // Save the raw input and parsed JSON to a file for inspection
 | 
				
			||||||
 | 
					        file_put_contents(
 | 
				
			||||||
 | 
					            __DIR__ . '/inbox_log.txt',
 | 
				
			||||||
 | 
					            time() . ": ==== Masto GET Inbox RAW ====\n" . $_rawInput . "\n\n==== Masto GET Inbox JSON ====\n" . print_r($jsonData, true) . "\n\n",
 | 
				
			||||||
 | 
					            FILE_APPEND
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->message = json_encode([
 | 
				
			||||||
 | 
					            'status' => 'received',
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getOutbox($_name)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $_rawInput = file_get_contents('php://input');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Decode if it's JSON (as Mastodon usually sends JSON)
 | 
				
			||||||
 | 
					        $jsonData = json_decode($_rawInput, true);
 | 
				
			||||||
 | 
					        error_log("=== Masto GET Outbox Raw ===\n" . $_rawInput);
 | 
				
			||||||
 | 
					        error_log("=== Masto GET Outbox JSON ===\n" . print_r($jsonData, true));
 | 
				
			||||||
 | 
					        // Save the raw input and parsed JSON to a file for inspection
 | 
				
			||||||
 | 
					        file_put_contents(
 | 
				
			||||||
 | 
					            __DIR__ . '/outbox_log.txt',
 | 
				
			||||||
 | 
					            time() . ": ==== Masto GET Outbox RAW ====\n" . $_rawInput . "\n\n==== Masto GET Outbox JSON ====\n" . print_r($jsonData, true) . "\n\n",
 | 
				
			||||||
 | 
					            FILE_APPEND
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->message = json_encode([
 | 
				
			||||||
 | 
					            'status' => 'received',
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getFollowing($_name)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $_rawInput = file_get_contents('php://input');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Decode if it's JSON (as Mastodon usually sends JSON)
 | 
				
			||||||
 | 
					        $jsonData = json_decode($_rawInput, true);
 | 
				
			||||||
 | 
					        error_log("=== Masto GET Following Raw ===\n" . $_rawInput);
 | 
				
			||||||
 | 
					        error_log("=== Masto GET Following JSON ===\n" . print_r($jsonData, true));
 | 
				
			||||||
 | 
					        // Save the raw input and parsed JSON to a file for inspection
 | 
				
			||||||
 | 
					        file_put_contents(
 | 
				
			||||||
 | 
					            __DIR__ . '/following_log.txt',
 | 
				
			||||||
 | 
					            time() . ": ==== Masto GET Following RAW ====\n" . $_rawInput . "\n\n==== Masto GET Following JSON ====\n" . print_r($jsonData, true) . "\n\n",
 | 
				
			||||||
 | 
					            FILE_APPEND
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->message = json_encode([
 | 
				
			||||||
 | 
					            'status' => 'received',
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getFollowers($_name)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $_rawInput = file_get_contents('php://input');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Decode if it's JSON (as Mastodon usually sends JSON)
 | 
				
			||||||
 | 
					        $jsonData = json_decode($_rawInput, true);
 | 
				
			||||||
 | 
					        error_log("=== Masto GET Followers Raw ===\n" . $_rawInput);
 | 
				
			||||||
 | 
					        error_log("=== Masto GET Followers JSON ===\n" . print_r($jsonData, true));
 | 
				
			||||||
 | 
					        // Save the raw input and parsed JSON to a file for inspection
 | 
				
			||||||
 | 
					        file_put_contents(
 | 
				
			||||||
 | 
					            __DIR__ . '/followers_log.txt',
 | 
				
			||||||
 | 
					            time() . ": ==== Masto GET Followers RAW ====\n" . $_rawInput . "\n\n==== Masto GET Followers JSON ====\n" . print_r($jsonData, true) . "\n\n",
 | 
				
			||||||
 | 
					            FILE_APPEND
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->message = json_encode([
 | 
				
			||||||
 | 
					            'status' => 'received',
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getSharedInbox()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $_rawInput = file_get_contents('php://input');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Decode if it's JSON (as Mastodon usually sends JSON)
 | 
				
			||||||
 | 
					        $jsonData = json_decode($_rawInput, true);
 | 
				
			||||||
 | 
					        error_log("=== Masto GET SharedInbox Raw ===\n" . $_rawInput);
 | 
				
			||||||
 | 
					        error_log("=== Masto GET SharedInbox JSON ===\n" . print_r($jsonData, true));
 | 
				
			||||||
 | 
					        // Save the raw input and parsed JSON to a file for inspection
 | 
				
			||||||
 | 
					        file_put_contents(
 | 
				
			||||||
 | 
					            __DIR__ . '/sharedInbox_log.txt',
 | 
				
			||||||
 | 
					            time() . ": ==== Masto GET SharedInbox RAW ====\n" . $_rawInput . "\n\n==== Masto GET SharedInbox JSON ====\n" . print_r($jsonData, true) . "\n\n",
 | 
				
			||||||
 | 
					            FILE_APPEND
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->message = json_encode([
 | 
				
			||||||
 | 
					            'status' => 'received',
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function postInbox($_name)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $_rawInput = file_get_contents('php://input');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Decode if it's JSON (as Mastodon usually sends JSON)
 | 
				
			||||||
 | 
					        $jsonData = json_decode($_rawInput, true);
 | 
				
			||||||
 | 
					        error_log("=== Masto POST Inbox Raw ===\n" . $_rawInput);
 | 
				
			||||||
 | 
					        error_log("=== Masto POST Inbox JSON ===\n" . print_r($jsonData, true));
 | 
				
			||||||
 | 
					        // Save the raw input and parsed JSON to a file for inspection
 | 
				
			||||||
 | 
					        file_put_contents(
 | 
				
			||||||
 | 
					            __DIR__ . '/inbox_log.txt',
 | 
				
			||||||
 | 
					            time() . ": ==== Masto POST Inbox RAW ====\n" . $_rawInput . "\n\n==== Masto POST Inbox JSON ====\n" . print_r($jsonData, true) . "\n\n",
 | 
				
			||||||
 | 
					            FILE_APPEND
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->message = json_encode([
 | 
				
			||||||
 | 
					            'status' => 'received',
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function postOutbox($_name)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $_rawInput = file_get_contents('php://input');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Decode if it's JSON (as Mastodon usually sends JSON)
 | 
				
			||||||
 | 
					        $jsonData = json_decode($_rawInput, true);
 | 
				
			||||||
 | 
					        error_log("=== Masto POST Outbox Raw ===\n" . $_rawInput);
 | 
				
			||||||
 | 
					        error_log("=== Masto POST Outbox JSON ===\n" . print_r($jsonData, true));
 | 
				
			||||||
 | 
					        // Save the raw input and parsed JSON to a file for inspection
 | 
				
			||||||
 | 
					        file_put_contents(
 | 
				
			||||||
 | 
					            __DIR__ . '/outbox_log.txt',
 | 
				
			||||||
 | 
					            time() . ": ==== Masto POST Outbox RAW ====\n" . $_rawInput . "\n\n==== Masto POST Outbox JSON ====\n" . print_r($jsonData, true) . "\n\n",
 | 
				
			||||||
 | 
					            FILE_APPEND
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->message = json_encode([
 | 
				
			||||||
 | 
					            'status' => 'received',
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function postFollowing($_name)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $_rawInput = file_get_contents('php://input');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Decode if it's JSON (as Mastodon usually sends JSON)
 | 
				
			||||||
 | 
					        $jsonData = json_decode($_rawInput, true);
 | 
				
			||||||
 | 
					        error_log("=== Masto POST Following Raw ===\n" . $_rawInput);
 | 
				
			||||||
 | 
					        error_log("=== Masto POST Following JSON ===\n" . print_r($jsonData, true));
 | 
				
			||||||
 | 
					        // Save the raw input and parsed JSON to a file for inspection
 | 
				
			||||||
 | 
					        file_put_contents(
 | 
				
			||||||
 | 
					            __DIR__ . '/following_log.txt',
 | 
				
			||||||
 | 
					            time() . ": ==== Masto POST Following RAW ====\n" . $_rawInput . "\n\n==== Masto POST Following JSON ====\n" . print_r($jsonData, true) . "\n\n",
 | 
				
			||||||
 | 
					            FILE_APPEND
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->message = json_encode([
 | 
				
			||||||
 | 
					            'status' => 'received',
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function postFollowers($_name)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $_rawInput = file_get_contents('php://input');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Decode if it's JSON (as Mastodon usually sends JSON)
 | 
				
			||||||
 | 
					        $jsonData = json_decode($_rawInput, true);
 | 
				
			||||||
 | 
					        error_log("=== Masto POST Followers Raw ===\n" . $_rawInput);
 | 
				
			||||||
 | 
					        error_log("=== Masto POST Followers JSON ===\n" . print_r($jsonData, true));
 | 
				
			||||||
 | 
					        // Save the raw input and parsed JSON to a file for inspection
 | 
				
			||||||
 | 
					        file_put_contents(
 | 
				
			||||||
 | 
					            __DIR__ . '/followers_log.txt',
 | 
				
			||||||
 | 
					            time() . ": ==== Masto POST Followers RAW ====\n" . $_rawInput . "\n\n==== Masto POST Followers JSON ====\n" . print_r($jsonData, true) . "\n\n",
 | 
				
			||||||
 | 
					            FILE_APPEND
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->message = json_encode([
 | 
				
			||||||
 | 
					            'status' => 'received',
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function postSharedInbox()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $_rawInput = file_get_contents('php://input');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Decode if it's JSON (as Mastodon usually sends JSON)
 | 
				
			||||||
 | 
					        $jsonData = json_decode($_rawInput, true);
 | 
				
			||||||
 | 
					        error_log("=== Masto POST SharedInbox Raw ===\n" . $_rawInput);
 | 
				
			||||||
 | 
					        error_log("=== Masto POST SharedInbox JSON ===\n" . print_r($jsonData, true));
 | 
				
			||||||
 | 
					        // Save the raw input and parsed JSON to a file for inspection
 | 
				
			||||||
 | 
					        file_put_contents(
 | 
				
			||||||
 | 
					            __DIR__ . '/sharedInbox_log.txt',
 | 
				
			||||||
 | 
					            time() . ": ==== Masto POST SharedInbox RAW ====\n" . $_rawInput . "\n\n==== Masto POST SharedInbox JSON ====\n" . print_r($jsonData, true) . "\n\n",
 | 
				
			||||||
 | 
					            FILE_APPEND
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->message = json_encode([
 | 
				
			||||||
 | 
					            'status' => 'received',
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * get internal represenation as json string
 | 
					     * get internal represenation as json string
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
| 
						 | 
					@ -107,6 +562,6 @@ class Dummy implements \Federator\Api\APIInterface
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function toJson()
 | 
					    public function toJson()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return json_encode($this->message, JSON_PRETTY_PRINT) . "\n";
 | 
					        return $this->message;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										43
									
								
								php/federator/data/activitypub/common/Announce.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								php/federator/data/activitypub/common/Announce.php
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,43 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * SPDX-FileCopyrightText: 2024 Sascha Nitsch (grumpydeveloper) https://contentnation.net/@grumpydevelop
 | 
				
			||||||
 | 
					 * SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Sascha Nitsch (grumpydeveloper)
 | 
				
			||||||
 | 
					 **/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Federator\Data\ActivityPub\Common;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Announce extends Activity
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public function __construct()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        parent::__construct('Announce');
 | 
				
			||||||
 | 
					        parent::addContext('https://www.w3.org/ns/activitystreams');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * convert internal state to php array
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return array<string,mixed>
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function toObject()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $return = parent::toObject();
 | 
				
			||||||
 | 
					        $return['type'] = 'Announce';
 | 
				
			||||||
 | 
					        // overwrite id from url
 | 
				
			||||||
 | 
					        $return['id'] = $this->getURL();
 | 
				
			||||||
 | 
					        return $return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   /**
 | 
				
			||||||
 | 
					     * create object from json
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param array<string,mixed> $json input json
 | 
				
			||||||
 | 
					     * @return bool true on success
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function fromJson($json)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return parent::fromJson($json);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										43
									
								
								php/federator/data/activitypub/common/Undo.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								php/federator/data/activitypub/common/Undo.php
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,43 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * SPDX-FileCopyrightText: 2024 Sascha Nitsch (grumpydeveloper) https://contentnation.net/@grumpydevelop
 | 
				
			||||||
 | 
					 * SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Sascha Nitsch (grumpydeveloper)
 | 
				
			||||||
 | 
					 **/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Federator\Data\ActivityPub\Common;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Undo extends Activity
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public function __construct()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        parent::__construct('Undo');
 | 
				
			||||||
 | 
					        parent::addContext('https://www.w3.org/ns/activitystreams');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * convert internal state to php array
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return array<string,mixed>
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function toObject()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $return = parent::toObject();
 | 
				
			||||||
 | 
					        $return['type'] = 'Undo';
 | 
				
			||||||
 | 
					        // overwrite id from url
 | 
				
			||||||
 | 
					        $return['id'] = $this->getURL();
 | 
				
			||||||
 | 
					        return $return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   /**
 | 
				
			||||||
 | 
					     * create object from json
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param array<string,mixed> $json input json
 | 
				
			||||||
 | 
					     * @return bool true on success
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function fromJson($json)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return parent::fromJson($json);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -262,7 +262,7 @@ class Main
 | 
				
			||||||
    public function setConnector(Connector\Connector $connector) : void
 | 
					    public function setConnector(Connector\Connector $connector) : void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ($this->connector) {
 | 
					        if ($this->connector) {
 | 
				
			||||||
            echo "main::setConnector Setting new connector will override old one.\n"; // TODO CHANGE TO LOG WARNING
 | 
					            # echo "main::setConnector Setting new connector will override old one.\n"; // TODO CHANGE TO LOG WARNING
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $this->connector = $connector;
 | 
					        $this->connector = $connector;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -275,7 +275,7 @@ class Main
 | 
				
			||||||
    public function setHost(string $host) : void
 | 
					    public function setHost(string $host) : void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ($this->host) {
 | 
					        if ($this->host) {
 | 
				
			||||||
            echo "main::setHost Setting new host will override old one.\n"; // TODO CHANGE TO LOG WARNING
 | 
					            # echo "main::setHost Setting new host will override old one.\n"; // TODO CHANGE TO LOG WARNING
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $this->host = $host;
 | 
					        $this->host = $host;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -310,6 +310,6 @@ namespace Federator;
 | 
				
			||||||
function contentnation_load($main)
 | 
					function contentnation_load($main)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    $cn = new Connector\ContentNation($main);
 | 
					    $cn = new Connector\ContentNation($main);
 | 
				
			||||||
    echo "contentnation::contentnation_load Loaded new connector, adding to main\n"; // TODO change to proper log
 | 
					    # echo "contentnation::contentnation_load Loaded new connector, adding to main\n"; // TODO change to proper log
 | 
				
			||||||
    $main->setConnector($cn);
 | 
					    $main->setConnector($cn);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -87,6 +87,6 @@ namespace Federator;
 | 
				
			||||||
function dummy_load($main)
 | 
					function dummy_load($main)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    $dummy = new Connector\DummyConnector();
 | 
					    $dummy = new Connector\DummyConnector();
 | 
				
			||||||
    echo "dummyconnector::dummy_load Loaded new connector, adding to main\n"; // TODO change to proper log
 | 
					    # echo "dummyconnector::dummy_load Loaded new connector, adding to main\n"; // TODO change to proper log
 | 
				
			||||||
    $main->setConnector($dummy);
 | 
					    $main->setConnector($dummy);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -62,7 +62,10 @@ class Mastodon implements Connector
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (preg_match("#^([^@]+)@([^/]+)#", $userId, $matches) === 1) {
 | 
					        if (preg_match("#^([^@]+)@([^/]+)#", $userId, $matches) === 1) {
 | 
				
			||||||
            $name = $matches[1];
 | 
					            $name = $matches[1];
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $name = $userId;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $remoteURL = $this->service . '/users/' . $name . '/outbox';
 | 
					        $remoteURL = $this->service . '/users/' . $name . '/outbox';
 | 
				
			||||||
        if ($min !== '') {
 | 
					        if ($min !== '') {
 | 
				
			||||||
            $remoteURL .= '&minTS=' . urlencode($min);
 | 
					            $remoteURL .= '&minTS=' . urlencode($min);
 | 
				
			||||||
| 
						 | 
					@ -209,6 +212,61 @@ class Mastodon implements Connector
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    $posts[] = $create;
 | 
					                    $posts[] = $create;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case 'Announce':
 | 
				
			||||||
 | 
					                    if (!isset($activity['object'])) {
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    $objectURL = is_array($activity['object']) ? $activity['object']['id'] : $activity['object'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Fetch the original object (e.g. Note)
 | 
				
			||||||
 | 
					                    [$response, $info] = \Federator\Main::getFromRemote($objectURL, ['Accept: application/activity+json']);
 | 
				
			||||||
 | 
					                    if ($info['http_code'] != 200) {
 | 
				
			||||||
 | 
					                        print_r($info);
 | 
				
			||||||
 | 
					                        echo "MastodonConnector::getRemotePostsByUser Failed to fetch original object for Announce: $objectURL\n";
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    $objData = json_decode($response, true);
 | 
				
			||||||
 | 
					                    if ($objData === false || $objData === null || !is_array($objData)) {
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    $announce = new \Federator\Data\ActivityPub\Common\Announce();
 | 
				
			||||||
 | 
					                    $announce->setID($activity['id'])
 | 
				
			||||||
 | 
					                        ->setURL($activity['id'])
 | 
				
			||||||
 | 
					                        ->setPublished(strtotime($activity['published'] ?? 'now'))
 | 
				
			||||||
 | 
					                        ->setAActor($activity['actor'])
 | 
				
			||||||
 | 
					                        ->addTo("https://www.w3.org/ns/activitystreams#Public");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (array_key_exists('to', $activity)) {
 | 
				
			||||||
 | 
					                        foreach ($activity['to'] as $to) {
 | 
				
			||||||
 | 
					                            $announce->addTo($to);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Optionally parse the shared object as a Note or something else
 | 
				
			||||||
 | 
					                    switch ($objData['type']) {
 | 
				
			||||||
 | 
					                        case 'Note':
 | 
				
			||||||
 | 
					                            $note = new \Federator\Data\ActivityPub\Common\Note();
 | 
				
			||||||
 | 
					                            $note->setID($objData['id'])
 | 
				
			||||||
 | 
					                                ->setContent($objData['content'] ?? '')
 | 
				
			||||||
 | 
					                                ->setPublished(strtotime($objData['published'] ?? 'now'))
 | 
				
			||||||
 | 
					                                ->setURL($objData['url'] ?? $objData['id'])
 | 
				
			||||||
 | 
					                                ->setAttributedTo($objData['attributedTo'] ?? null)
 | 
				
			||||||
 | 
					                                ->addTo("https://www.w3.org/ns/activitystreams#Public");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            $announce->setObject($note);
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                        default:
 | 
				
			||||||
 | 
					                            // fallback object
 | 
				
			||||||
 | 
					                            $fallback = new \Federator\Data\ActivityPub\Common\APObject($objData['type']);
 | 
				
			||||||
 | 
					                            $fallback->setID($objData['id'] ?? $objectURL);
 | 
				
			||||||
 | 
					                            $announce->setObject($fallback);
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    $posts[] = $announce;
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                default:
 | 
					                default:
 | 
				
			||||||
                    echo "MastodonConnector::getRemotePostsByUser we currently don't support the activity type " . $activity['type'] . "\n";
 | 
					                    echo "MastodonConnector::getRemotePostsByUser we currently don't support the activity type " . $activity['type'] . "\n";
 | 
				
			||||||
| 
						 | 
					@ -269,7 +327,6 @@ class Mastodon implements Connector
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Mastodon lookup API endpoint
 | 
					        // Mastodon lookup API endpoint
 | 
				
			||||||
        $remoteURL = $this->service . '/api/v1/accounts/lookup?acct=' . urlencode($_name);
 | 
					        $remoteURL = $this->service . '/api/v1/accounts/lookup?acct=' . urlencode($_name);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Set headers
 | 
					        // Set headers
 | 
				
			||||||
        $headers = ['Accept: application/json'];
 | 
					        $headers = ['Accept: application/json'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -354,6 +411,6 @@ namespace Federator;
 | 
				
			||||||
function mastodon_load($main)
 | 
					function mastodon_load($main)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    $mast = new Connector\Mastodon($main);
 | 
					    $mast = new Connector\Mastodon($main);
 | 
				
			||||||
    echo "mastodon::mastodon_load Loaded new connector, adding to main\n"; // TODO change to proper log
 | 
					    # echo "mastodon::mastodon_load Loaded new connector, adding to main\n"; // TODO change to proper log
 | 
				
			||||||
    $main->setConnector($mast);
 | 
					    $main->setConnector($mast);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue