forked from grumpydevelop/federator
		
	fix remaining phan errors
- also cleaned up dummy - added new rewrite for apache for sharedInbox (your.fqdn/inbox) - fixed json-formatting of publicKey when requesting user via api
This commit is contained in:
		
							parent
							
								
									d9b02bd95b
								
							
						
					
					
						commit
						4d36fc3c61
					
				
					 6 changed files with 50 additions and 524 deletions
				
			
		| 
						 | 
				
			
			@ -52,6 +52,7 @@ To configure an apache server, add the following rewrite rules:
 | 
			
		|||
      RewriteCond expr "%{HTTP:content-type} -strcmatch '*application/activity+json*'"
 | 
			
		||||
      RewriteRule ^@(.*)$ /federator.php?_call=fedusers/$1 [L,END]
 | 
			
		||||
      RewriteRule ^users/(.*)$ /federator.php?_call=fedusers/$1 [L,END]
 | 
			
		||||
      RewriteRule ^inbox[/]?$ /federator.php?_call=fedusers/$1 [L,END]
 | 
			
		||||
      RewriteRule ^api/federator/(.+)$ federator.php?_call=$1 [L,END]
 | 
			
		||||
      RewriteRule ^(\.well-known/.*)$ /federator.php?_call=$1 [L,END]
 | 
			
		||||
      RewriteRule ^(nodeinfo/2\.[01])$ /federator.php?_call=$1 [L,END]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -148,20 +148,32 @@ class FedUsers implements APIInterface
 | 
			
		|||
        if ($user === false || $user->id === null) {
 | 
			
		||||
            throw new \Federator\Exceptions\FileNotFound();
 | 
			
		||||
        }
 | 
			
		||||
        $config = $this->main->getConfig();
 | 
			
		||||
        $domain = $config['generic']['externaldomain'];
 | 
			
		||||
        $jsonKey = json_encode($user->publicKey);
 | 
			
		||||
        if (!is_string($jsonKey)) {
 | 
			
		||||
            throw new \Federator\Exceptions\FileNotFound();
 | 
			
		||||
        }
 | 
			
		||||
        $data = [
 | 
			
		||||
            'iconMediaType' => $user->iconMediaType,
 | 
			
		||||
            'iconURL' => $user->iconURL,
 | 
			
		||||
            'imageMediaType' => $user->imageMediaType,
 | 
			
		||||
            'imageURL' => $user->imageURL,
 | 
			
		||||
            'fqdn' => $_SERVER['SERVER_NAME'],
 | 
			
		||||
            'fqdn' => $domain,
 | 
			
		||||
            'name' => $user->name,
 | 
			
		||||
            'username' => $user->id,
 | 
			
		||||
            'publickey' => $user->publicKey,
 | 
			
		||||
            'publickey' => trim($jsonKey, '"'),
 | 
			
		||||
            'registered' => gmdate('Y-m-d\TH:i:s\Z', $user->registered), // 2021-03-25T00:00:00Z
 | 
			
		||||
            'summary' => $user->summary,
 | 
			
		||||
            'type' => $user->type
 | 
			
		||||
        ];
 | 
			
		||||
        $this->response = $this->main->renderTemplate('user.json', $data);
 | 
			
		||||
        // todo remove HTTP again
 | 
			
		||||
        $this->response = str_replace(
 | 
			
		||||
            'https://192.168.178.143/',
 | 
			
		||||
            'http://192.168.178.143/',
 | 
			
		||||
            $this->response
 | 
			
		||||
        );
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -177,7 +177,8 @@ class Inbox implements \Federator\Api\FedUsers\FedUsersInterface
 | 
			
		|||
                $announce->setAActor((string) $activity['actor'])
 | 
			
		||||
                    ->setPublished($published !== false ? $published : time())
 | 
			
		||||
                    ->setID((string) $activity['id'])
 | 
			
		||||
                    ->setURL((string) $activity['id']);
 | 
			
		||||
                    ->setURL((string) $activity['id'])
 | 
			
		||||
                    ->addTo("https://www.w3.org/ns/activitystreams#Public");
 | 
			
		||||
 | 
			
		||||
                if (array_key_exists('cc', $activity)) {
 | 
			
		||||
                    foreach ($activity['cc'] as $cc) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,43 +58,10 @@ class Dummy implements \Federator\Api\APIInterface
 | 
			
		|||
                        switch ($paths[2]) {
 | 
			
		||||
                            case 'moo':
 | 
			
		||||
                                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;
 | 
			
		||||
            case 'POST':
 | 
			
		||||
| 
						 | 
				
			
			@ -103,41 +70,10 @@ class Dummy implements \Federator\Api\APIInterface
 | 
			
		|||
                        switch ($paths[2]) {
 | 
			
		||||
                            case 'moo':
 | 
			
		||||
                                return $this->postDummy();
 | 
			
		||||
                            case 'sharedInbox':
 | 
			
		||||
                                return $this->postSharedInbox();
 | 
			
		||||
                            default:
 | 
			
		||||
                                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);
 | 
			
		||||
| 
						 | 
				
			
			@ -164,187 +100,6 @@ class Dummy implements \Federator\Api\APIInterface
 | 
			
		|||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * get function for "/v1/dummy/moo"
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $_name user-id which we try to retrieve
 | 
			
		||||
     * @return bool
 | 
			
		||||
     */
 | 
			
		||||
    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"
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -355,256 +110,6 @@ PEM;
 | 
			
		|||
        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
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $successMsg = json_encode([
 | 
			
		||||
            'status' => 'received',
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        if ($successMsg !== false) {
 | 
			
		||||
            $this->message = $successMsg;
 | 
			
		||||
        }
 | 
			
		||||
        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
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $successMsg = json_encode([
 | 
			
		||||
            'status' => 'received',
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        if ($successMsg !== false) {
 | 
			
		||||
            $this->message = $successMsg;
 | 
			
		||||
        }
 | 
			
		||||
        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
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $successMsg = json_encode([
 | 
			
		||||
            'status' => 'received',
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        if ($successMsg !== false) {
 | 
			
		||||
            $this->message = $successMsg;
 | 
			
		||||
        }
 | 
			
		||||
        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
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $successMsg = json_encode([
 | 
			
		||||
            'status' => 'received',
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        if ($successMsg !== false) {
 | 
			
		||||
            $this->message = $successMsg;
 | 
			
		||||
        }
 | 
			
		||||
        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
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $successMsg = json_encode([
 | 
			
		||||
            'status' => 'received',
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        if ($successMsg !== false) {
 | 
			
		||||
            $this->message = $successMsg;
 | 
			
		||||
        }
 | 
			
		||||
        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
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $successMsg = json_encode([
 | 
			
		||||
            'status' => 'received',
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        if ($successMsg !== false) {
 | 
			
		||||
            $this->message = $successMsg;
 | 
			
		||||
        }
 | 
			
		||||
        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
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $successMsg = json_encode([
 | 
			
		||||
            'status' => 'received',
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        if ($successMsg !== false) {
 | 
			
		||||
            $this->message = $successMsg;
 | 
			
		||||
        }
 | 
			
		||||
        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
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $successMsg = json_encode([
 | 
			
		||||
            'status' => 'received',
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        if ($successMsg !== false) {
 | 
			
		||||
            $this->message = $successMsg;
 | 
			
		||||
        }
 | 
			
		||||
        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
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $successMsg = json_encode([
 | 
			
		||||
            'status' => 'received',
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        if ($successMsg !== false) {
 | 
			
		||||
            $this->message = $successMsg;
 | 
			
		||||
        }
 | 
			
		||||
        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
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $successMsg = json_encode([
 | 
			
		||||
            'status' => 'received',
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        if ($successMsg !== false) {
 | 
			
		||||
            $this->message = $successMsg;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * get internal represenation as json string
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -67,6 +67,12 @@ class WebFinger
 | 
			
		|||
            'domain' => $domain,
 | 
			
		||||
        ];
 | 
			
		||||
        $response = $this->main->renderTemplate('webfinger_acct.json', $data);
 | 
			
		||||
        // todo remove HTTP again
 | 
			
		||||
        $response = str_replace(
 | 
			
		||||
            'https://192.168.178.143',
 | 
			
		||||
            'http://192.168.178.143',
 | 
			
		||||
            $response
 | 
			
		||||
        );
 | 
			
		||||
        $this->wellKnown->setResponse($response);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -60,7 +60,7 @@ class Mastodon implements Connector
 | 
			
		|||
     */
 | 
			
		||||
    public function getRemotePostsByUser($userId, $min = null, $max = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (preg_match("#^([^@]+)@([^/]+)#", $userId, $matches) === 1) {
 | 
			
		||||
        if (preg_match("#^([^@]+)@([^/]+)#", $userId, $matches) == 1) {
 | 
			
		||||
            $name = $matches[1];
 | 
			
		||||
        } else {
 | 
			
		||||
            $name = $userId;
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +78,7 @@ class Mastodon implements Connector
 | 
			
		|||
 | 
			
		||||
        [$outboxResponse, $outboxInfo] = \Federator\Main::getFromRemote($remoteURL, ['Accept: application/activity+json']);
 | 
			
		||||
 | 
			
		||||
        if ($outboxInfo['http_code'] !== 200) {
 | 
			
		||||
        if ($outboxInfo['http_code'] != 200) {
 | 
			
		||||
            echo "MastodonConnector::getRemotePostsByUser HTTP call failed for remoteURL $remoteURL\n";
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -128,7 +128,7 @@ class Mastodon implements Connector
 | 
			
		|||
        } elseif (isset($outbox['first'])) {
 | 
			
		||||
            $firstURL = is_array($outbox['first']) ? $outbox['first']['id'] : $outbox['first'];
 | 
			
		||||
            [$pageResponse, $pageInfo] = \Federator\Main::getFromRemote($firstURL, ['Accept: application/activity+json']);
 | 
			
		||||
            if ($pageInfo['http_code'] !== 200) {
 | 
			
		||||
            if ($pageInfo['http_code'] != 200) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            $page = json_decode($pageResponse, true);
 | 
			
		||||
| 
						 | 
				
			
			@ -146,11 +146,12 @@ class Mastodon implements Connector
 | 
			
		|||
                    }
 | 
			
		||||
 | 
			
		||||
                    $obj = $activity['object'];
 | 
			
		||||
                    $published = strtotime($activity['published'] ?? $obj['published'] ?? 'now');
 | 
			
		||||
                    $create = new \Federator\Data\ActivityPub\Common\Create();
 | 
			
		||||
                    $create->setID($activity['id'])
 | 
			
		||||
                    $create->setAActor($activity['actor'])
 | 
			
		||||
                        ->setID($activity['id'])
 | 
			
		||||
                        ->setURL($activity['id'])
 | 
			
		||||
                        ->setPublished(strtotime($activity['published'] ?? $obj['published'] ?? 'now'))
 | 
			
		||||
                        ->setAActor($activity['actor'])
 | 
			
		||||
                        ->setPublished($published !== false ? $published : time())
 | 
			
		||||
                        ->addCC($activity['cc']);
 | 
			
		||||
 | 
			
		||||
                    if (array_key_exists('to', $activity)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -161,9 +162,10 @@ class Mastodon implements Connector
 | 
			
		|||
 | 
			
		||||
                    switch ($obj['type']) {
 | 
			
		||||
                        case 'Note':
 | 
			
		||||
                            $published = strtotime($obj['published'] ?? 'now');
 | 
			
		||||
                            $apNote = new \Federator\Data\ActivityPub\Common\Note();
 | 
			
		||||
                            $apNote->setID($obj['id'])
 | 
			
		||||
                                ->setPublished(strtotime($obj['published'] ?? 'now'))
 | 
			
		||||
                                ->setPublished($published !== false ? $published : time())
 | 
			
		||||
                                ->setContent($obj['content'] ?? '')
 | 
			
		||||
                                ->setSummary($obj['summary'])
 | 
			
		||||
                                ->setURL($obj['url'])
 | 
			
		||||
| 
						 | 
				
			
			@ -220,6 +222,11 @@ class Mastodon implements Connector
 | 
			
		|||
 | 
			
		||||
                    $objectURL = is_array($activity['object']) ? $activity['object']['id'] : $activity['object'];
 | 
			
		||||
 | 
			
		||||
                    if (!is_string($objectURL)) {
 | 
			
		||||
                        // Handle error or cast/normalize
 | 
			
		||||
                        throw new \InvalidArgumentException('objectURL must be a string, got ' . gettype($objectURL));
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Fetch the original object (e.g. Note)
 | 
			
		||||
                    [$response, $info] = \Federator\Main::getFromRemote($objectURL, ['Accept: application/activity+json']);
 | 
			
		||||
                    if ($info['http_code'] != 200) {
 | 
			
		||||
| 
						 | 
				
			
			@ -232,11 +239,15 @@ class Mastodon implements Connector
 | 
			
		|||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    assert(is_string($activity['id']));
 | 
			
		||||
                    assert(is_string($activity['actor']));
 | 
			
		||||
 | 
			
		||||
                    $published = strtotime((string) $activity['published']);
 | 
			
		||||
                    $announce = new \Federator\Data\ActivityPub\Common\Announce();
 | 
			
		||||
                    $announce->setID($activity['id'])
 | 
			
		||||
                    $announce->setAActor($activity['actor'])
 | 
			
		||||
                        ->setPublished($published !== false ? $published : time())
 | 
			
		||||
                        ->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)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -248,10 +259,11 @@ class Mastodon implements Connector
 | 
			
		|||
                    // Optionally parse the shared object as a Note or something else
 | 
			
		||||
                    switch ($objData['type']) {
 | 
			
		||||
                        case 'Note':
 | 
			
		||||
                            $published = strtotime($objData['published'] ?? 'now');
 | 
			
		||||
                            $note = new \Federator\Data\ActivityPub\Common\Note();
 | 
			
		||||
                            $note->setID($objData['id'])
 | 
			
		||||
                                ->setContent($objData['content'] ?? '')
 | 
			
		||||
                                ->setPublished(strtotime($objData['published'] ?? 'now'))
 | 
			
		||||
                                ->setPublished($published !== false ? $published : time())
 | 
			
		||||
                                ->setURL($objData['url'] ?? $objData['id'])
 | 
			
		||||
                                ->setAttributedTo($objData['attributedTo'] ?? null)
 | 
			
		||||
                                ->addTo("https://www.w3.org/ns/activitystreams#Public");
 | 
			
		||||
| 
						 | 
				
			
			@ -277,18 +289,6 @@ class Mastodon implements Connector
 | 
			
		|||
        return $posts;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the MIME type of a remote file by its URL.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $_url The URL of the remote file.
 | 
			
		||||
     * @return string|false The MIME type if found, or false on failure.
 | 
			
		||||
     */
 | 
			
		||||
    public function getRemoteMimeType($url)
 | 
			
		||||
    {
 | 
			
		||||
        $headers = get_headers($url, 1);
 | 
			
		||||
        return $headers['Content-Type'] ?? 'unknown';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * get statistics from remote system
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -321,7 +321,7 @@ class Mastodon implements Connector
 | 
			
		|||
    public function getRemoteUserByName(string $_name)
 | 
			
		||||
    {
 | 
			
		||||
        // Validate username (Mastodon usernames can include @ and domain parts)
 | 
			
		||||
        if (preg_match("/^[a-zA-Z0-9_\-@.]+$/", $_name) !== 1) {
 | 
			
		||||
        if (preg_match("/^[a-zA-Z0-9_\-@.]+$/", $_name) != 1) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -334,7 +334,7 @@ class Mastodon implements Connector
 | 
			
		|||
        [$response, $info] = \Federator\Main::getFromRemote($remoteURL, $headers);
 | 
			
		||||
 | 
			
		||||
        // Handle HTTP errors
 | 
			
		||||
        if ($info['http_code'] !== 200) {
 | 
			
		||||
        if ($info['http_code'] != 200) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -344,6 +344,7 @@ class Mastodon implements Connector
 | 
			
		|||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $createdAt = strtotime($r['created_at']);
 | 
			
		||||
        // Map response to User object
 | 
			
		||||
        $user = new \Federator\Data\User();
 | 
			
		||||
        $user->externalid = (string) $r['id']; // Mastodon uses numeric IDs
 | 
			
		||||
| 
						 | 
				
			
			@ -354,7 +355,7 @@ class Mastodon implements Connector
 | 
			
		|||
        $user->name = $r['display_name'] ?: $r['username'];
 | 
			
		||||
        $user->summary = $r['note'];
 | 
			
		||||
        $user->type = 'Person'; // Mastodon profiles are ActivityPub "Person" objects
 | 
			
		||||
        $user->registered = strtotime($r['created_at']);
 | 
			
		||||
        $user->registered = $createdAt !== false ? $createdAt : time();
 | 
			
		||||
 | 
			
		||||
        return $user;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue