forked from grumpydevelop/federator
		
	- fixed rewrites to properly support @username/outbox, username/outbox, users/username/outbox, ... - initial support for sending follow to e.g. mastodon (code commented out in api.php) - database migration for follows table and fedusers table - save retrieved fedusers in cache and db - depend more on configs externaldomain, less on server_name - properly implemented following-logic in dio - made postForuUser static in newContent and inbox - provide rsaprivate for signing reuests -> actPub-Servers - change contenttype depending on use-case - changed user json-template to have loop-back between user-template and webfinger (mastodon f.e. needs this)
		
			
				
	
	
		
			176 lines
		
	
	
	
		
			5.6 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			176 lines
		
	
	
	
		
			5.6 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
/**
 | 
						|
 * SPDX-FileCopyrightText: 2024 Sascha Nitsch (grumpydeveloper) https://contentnation.net/@grumpydevelop
 | 
						|
 * SPDX-License-Identifier: GPL-3.0-or-later
 | 
						|
 *
 | 
						|
 * @author Yannis Vogel (vogeldevelopment)
 | 
						|
 **/
 | 
						|
 | 
						|
namespace Federator\Api\FedUsers;
 | 
						|
 | 
						|
/**
 | 
						|
 * handle activitypub inbox requests
 | 
						|
 */
 | 
						|
class Inbox implements \Federator\Api\FedUsers\FedUsersInterface
 | 
						|
{
 | 
						|
    /**
 | 
						|
     * main instance
 | 
						|
     *
 | 
						|
     * @var \Federator\Api $main
 | 
						|
     */
 | 
						|
    private $main;
 | 
						|
 | 
						|
    /**
 | 
						|
     * constructor
 | 
						|
     * @param \Federator\Api $main api main instance
 | 
						|
     */
 | 
						|
    public function __construct($main)
 | 
						|
    {
 | 
						|
        $this->main = $main;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * handle get call
 | 
						|
     *
 | 
						|
     * @param string|null $_user user to fetch inbox for @unused-param
 | 
						|
     * @return string|false response
 | 
						|
     */
 | 
						|
    public function get($_user)
 | 
						|
    {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * handle post call
 | 
						|
     *
 | 
						|
     * @param string|null $_user user to add data to inbox
 | 
						|
     * @return string|false response
 | 
						|
     */
 | 
						|
    public function post($_user)
 | 
						|
    {
 | 
						|
        $_rawInput = file_get_contents('php://input');
 | 
						|
 | 
						|
        $allHeaders = getallheaders();
 | 
						|
        try {
 | 
						|
            $this->main->checkSignature($allHeaders);
 | 
						|
        } catch (\Federator\Exceptions\PermissionDenied $e) {
 | 
						|
            error_log("Inbox::post Signature check failed: " . $e->getMessage());
 | 
						|
            http_response_code(401);
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        $activity = is_string($_rawInput) ? json_decode($_rawInput, true) : null;
 | 
						|
 | 
						|
        if (!is_array($activity)) {
 | 
						|
            error_log("Inbox::post Input wasn't of type array");
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        $inboxActivity = \Federator\Data\ActivityPub\Factory::newActivityFromJson($activity);
 | 
						|
 | 
						|
        $rootDir = $_SERVER['DOCUMENT_ROOT'] . '../';
 | 
						|
 | 
						|
        // Shared inbox
 | 
						|
        if (!isset($_user)) {
 | 
						|
            // 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
 | 
						|
            );
 | 
						|
        }
 | 
						|
 | 
						|
        if ($inboxActivity === false) {
 | 
						|
            error_log("Inbox::post couldn't create inboxActivity, aborting");
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        $sendTo = $inboxActivity->getCC();
 | 
						|
        if ($inboxActivity->getType() === 'Undo') { // for undo the object holds the proper cc
 | 
						|
            $object = $inboxActivity->getObject();
 | 
						|
            if ($object !== null && is_object($object)) {
 | 
						|
                $sendTo = $object->getCC();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        $users = [];
 | 
						|
        $dbh = $this->main->getDatabase();
 | 
						|
        $cache = $this->main->getCache();
 | 
						|
        $connector = $this->main->getConnector();
 | 
						|
 | 
						|
        foreach ($sendTo as $receiver) {
 | 
						|
            if ($receiver === '' || !is_string($receiver)) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            if (str_ends_with($receiver, '/followers')) {
 | 
						|
                $actor = $inboxActivity->getAActor();
 | 
						|
                if ($actor === null || !is_string($actor)) {
 | 
						|
                    error_log("Inbox::post no actor found");
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
 | 
						|
                // Extract username from the actor URL
 | 
						|
                $username = basename((string)(parse_url($actor, PHP_URL_PATH) ?? ''));
 | 
						|
                $domain = parse_url($actor, PHP_URL_HOST);
 | 
						|
                if ($username === null || $domain === null) {
 | 
						|
                    error_log("Inbox::post no username or domain found");
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
                $followers = \Federator\DIO\Followers::getFollowersByFedUser($dbh, $connector, $cache, $username . '@' . $domain);
 | 
						|
 | 
						|
                if (is_array($followers)) {
 | 
						|
                    $users = array_merge($users, array_column($followers, 'id'));
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if ($_user !== false && !in_array($_user, $users, true)) {
 | 
						|
            $users[] = $_user;
 | 
						|
        }
 | 
						|
        foreach ($users as $user) {
 | 
						|
            if (!isset($user)) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            $this->postForUser($dbh, $connector, $cache, $user, $inboxActivity);
 | 
						|
        }
 | 
						|
        return json_encode($inboxActivity, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * handle post call for specific user
 | 
						|
     *
 | 
						|
     * @param \mysqli $dbh database handle
 | 
						|
     * @param \Federator\Connector\Connector $connector connector to use
 | 
						|
     * @param \Federator\Cache\Cache|null $cache optional caching service
 | 
						|
     * @param string $_user user to add data to inbox
 | 
						|
     * @param \Federator\Data\ActivityPub\Common\Activity $inboxActivity the activity that we received
 | 
						|
     * @return boolean response
 | 
						|
     */
 | 
						|
    private static function postForUser($dbh, $connector, $cache, $_user, $inboxActivity)
 | 
						|
    {
 | 
						|
        if (isset($_user)) {
 | 
						|
            // get user
 | 
						|
            $user = \Federator\DIO\User::getUserByName(
 | 
						|
                $dbh,
 | 
						|
                $_user,
 | 
						|
                $connector,
 | 
						|
                $cache
 | 
						|
            );
 | 
						|
            if ($user === null || $user->id === null) {
 | 
						|
                error_log("Inbox::postForUser couldn't find user: $_user");
 | 
						|
                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;
 | 
						|
    }
 | 
						|
}
 |