- public static function profile($uid)
- {
- $accounttype = ['Person', 'Organization', 'Service', 'Group', 'Application'];
- $condition = ['uid' => $uid, 'blocked' => false, 'account_expired' => false,
- 'account_removed' => false, 'verified' => true];
- $fields = ['guid', 'nickname', 'pubkey', 'account-type', 'page-flags'];
- $user = DBA::selectFirst('user', $fields, $condition);
- if (!DBA::isResult($user)) {
- return [];
- }
-
- $fields = ['locality', 'region', 'country-name'];
- $profile = DBA::selectFirst('profile', $fields, ['uid' => $uid, 'is-default' => true]);
- if (!DBA::isResult($profile)) {
- return [];
- }
-
- $fields = ['name', 'url', 'location', 'about', 'avatar'];
- $contact = DBA::selectFirst('contact', $fields, ['uid' => $uid, 'self' => true]);
- if (!DBA::isResult($contact)) {
- return [];
- }
-
- $data = ['@context' => ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1',
- ['vcard' => 'http://www.w3.org/2006/vcard/ns#', 'uuid' => 'http://schema.org/identifier',
- 'sensitive' => 'as:sensitive', 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers']]];
-
- $data['id'] = $contact['url'];
- $data['uuid'] = $user['guid'];
- $data['type'] = $accounttype[$user['account-type']];
- $data['following'] = System::baseUrl() . '/following/' . $user['nickname'];
- $data['followers'] = System::baseUrl() . '/followers/' . $user['nickname'];
- $data['inbox'] = System::baseUrl() . '/inbox/' . $user['nickname'];
- $data['outbox'] = System::baseUrl() . '/outbox/' . $user['nickname'];
- $data['preferredUsername'] = $user['nickname'];
- $data['name'] = $contact['name'];
- $data['vcard:hasAddress'] = ['@type' => 'vcard:Home', 'vcard:country-name' => $profile['country-name'],
- 'vcard:region' => $profile['region'], 'vcard:locality' => $profile['locality']];
- $data['summary'] = $contact['about'];
- $data['url'] = $contact['url'];
- $data['manuallyApprovesFollowers'] = in_array($user['page-flags'], [Contact::PAGE_NORMAL, Contact::PAGE_PRVGROUP]);
- $data['publicKey'] = ['id' => $contact['url'] . '#main-key',
- 'owner' => $contact['url'],
- 'publicKeyPem' => $user['pubkey']];
- $data['endpoints'] = ['sharedInbox' => System::baseUrl() . '/inbox'];
- $data['icon'] = ['type' => 'Image',
- 'url' => $contact['avatar']];
-
- // tags: https://kitty.town/@inmysocks/100656097926961126.json
- return $data;
- }
-
- private static function fetchPermissionBlockFromConversation($item)
- {
- if (empty($item['thr-parent'])) {
- return [];
- }
-
- $condition = ['item-uri' => $item['thr-parent'], 'protocol' => Conversation::PARCEL_ACTIVITYPUB];
- $conversation = DBA::selectFirst('conversation', ['source'], $condition);
- if (!DBA::isResult($conversation)) {
- return [];
- }
-
- $activity = json_decode($conversation['source'], true);
-
- $actor = JsonLD::fetchElement($activity, 'actor', 'id');
- $profile = ActivityPub::fetchprofile($actor);
-
- $item_profile = ActivityPub::fetchprofile($item['owner-link']);
-
- $permissions = [];
-
- $elements = ['to', 'cc', 'bto', 'bcc'];
- foreach ($elements as $element) {
- if (empty($activity[$element])) {
- continue;
- }
- if (is_string($activity[$element])) {
- $activity[$element] = [$activity[$element]];
- }
- foreach ($activity[$element] as $receiver) {
- if ($receiver == $profile['followers'] && !empty($item_profile['followers'])) {
- $receiver = $item_profile['followers'];
- }
- $permissions[$element][] = $receiver;
- }
- }
- return $permissions;
- }
-
- public static function createPermissionBlockForItem($item)
- {
- $data = ['to' => [], 'cc' => []];
-
- $data = array_merge($data, self::fetchPermissionBlockFromConversation($item));
-
- $actor_profile = ActivityPub::fetchprofile($item['author-link']);
-
- $terms = Term::tagArrayFromItemId($item['id']);
-
- $contacts = [];
-
- if (!$item['private']) {
- $data['to'][] = self::PUBLIC;
- if (!empty($actor_profile['followers'])) {
- $data['cc'][] = $actor_profile['followers'];
- }
-
- foreach ($terms as $term) {
- if ($term['type'] != TERM_MENTION) {
- continue;
- }
- $profile = self::fetchprofile($term['url']);
- if (!empty($profile) && empty($contacts[$profile['url']])) {
- $data['cc'][] = $profile['url'];
- $contacts[$profile['url']] = $profile['url'];
- }
- }
- } else {
- $receiver_list = Item::enumeratePermissions($item);
-
- $mentioned = [];
-
- foreach ($terms as $term) {
- if ($term['type'] != TERM_MENTION) {
- continue;
- }
- $cid = Contact::getIdForURL($term['url'], $item['uid']);
- if (!empty($cid) && in_array($cid, $receiver_list)) {
- $contact = DBA::selectFirst('contact', ['url'], ['id' => $cid, 'network' => Protocol::ACTIVITYPUB]);
- $data['to'][] = $contact['url'];
- $contacts[$contact['url']] = $contact['url'];
- }
- }
-
- foreach ($receiver_list as $receiver) {
- $contact = DBA::selectFirst('contact', ['url'], ['id' => $receiver, 'network' => Protocol::ACTIVITYPUB]);
- if (empty($contacts[$contact['url']])) {
- $data['cc'][] = $contact['url'];
- $contacts[$contact['url']] = $contact['url'];
- }
- }
-
- if (empty($data['to'])) {
- $data['to'] = $data['cc'];
- $data['cc'] = [];
- }
- }
-
- $parents = Item::select(['author-link', 'owner-link'], ['parent' => $item['parent']]);
- while ($parent = Item::fetch($parents)) {
- $profile = self::fetchprofile($parent['author-link']);
- if (!empty($profile) && empty($contacts[$profile['url']])) {
- $data['cc'][] = $profile['url'];
- $contacts[$profile['url']] = $profile['url'];
- }
-
- $profile = self::fetchprofile($parent['owner-link']);
- if (!empty($profile) && empty($contacts[$profile['url']])) {
- $data['cc'][] = $profile['url'];
- $contacts[$profile['url']] = $profile['url'];
- }
- }
- DBA::close($parents);
-
- return $data;
- }
-
- public static function fetchTargetInboxes($item, $uid)
- {
- $permissions = self::createPermissionBlockForItem($item);
- if (empty($permissions)) {
- return [];
- }
-
- $inboxes = [];
-
- $item_profile = ActivityPub::fetchprofile($item['owner-link']);
-
- $elements = ['to', 'cc', 'bto', 'bcc'];
- foreach ($elements as $element) {
- if (empty($permissions[$element])) {
- continue;
- }
- foreach ($permissions[$element] as $receiver) {
- if ($receiver == $item_profile['followers']) {
- $contacts = DBA::select('contact', ['notify', 'batch'], ['uid' => $uid,
- 'rel' => [Contact::FOLLOWER, Contact::FRIEND], 'network' => Protocol::ACTIVITYPUB]);
- while ($contact = DBA::fetch($contacts)) {
- $contact = defaults($contact, 'batch', $contact['notify']);
- $inboxes[$contact] = $contact;
- }
- DBA::close($contacts);
- } else {
- $profile = self::fetchprofile($receiver);
- if (!empty($profile)) {
- $target = defaults($profile, 'sharedinbox', $profile['inbox']);
- $inboxes[$target] = $target;
- }
- }
- }
- }
-
- if (!empty($item_profile['sharedinbox'])) {
- unset($inboxes[$item_profile['sharedinbox']]);
- }
-
- if (!empty($item_profile['inbox'])) {
- unset($inboxes[$item_profile['inbox']]);
- }
-
- return $inboxes;
- }
-
- public static function createActivityFromItem($item_id)
- {
- $item = Item::selectFirst([], ['id' => $item_id]);
-
- if (!DBA::isResult($item)) {
- return false;
- }
-
- $condition = ['item-uri' => $item['uri'], 'protocol' => Conversation::PARCEL_ACTIVITYPUB];
- $conversation = DBA::selectFirst('conversation', ['source'], $condition);
- if (DBA::isResult($conversation)) {
- $data = json_decode($conversation['source']);
- if (!empty($data)) {
- return $data;
- }
- }
-
- $data = ['@context' => ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1',
- ['ostatus' => 'http://ostatus.org#', 'sensitive' => 'as:sensitive',
- 'Hashtag' => 'as:Hashtag', 'atomUri' => 'ostatus:atomUri',
- 'conversation' => 'ostatus:conversation',
- 'inReplyToAtomUri' => 'ostatus:inReplyToAtomUri']]];
-
- $data['id'] = $item['uri'] . '#activity';
- $data['type'] = 'Create';
- $data['actor'] = $item['author-link'];
-
- $data['published'] = DateTimeFormat::utc($item["created"]."+00:00", DateTimeFormat::ATOM);
-
- if ($item["created"] != $item["edited"]) {
- $data['updated'] = DateTimeFormat::utc($item["edited"]."+00:00", DateTimeFormat::ATOM);
- }
-
- $data = array_merge($data, ActivityPub::createPermissionBlockForItem($item));
-
- $data['object'] = self::createNote($item);
-
- $owner = User::getOwnerDataById($item['uid']);
-
- return LDSignature::sign($data, $owner);
- }
-
- public static function createObjectFromItemID($item_id)
- {
- $item = Item::selectFirst([], ['id' => $item_id]);
-
- if (!DBA::isResult($item)) {
- return false;
- }
-
- $data = ['@context' => ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1',
- ['ostatus' => 'http://ostatus.org#', 'sensitive' => 'as:sensitive',
- 'Hashtag' => 'as:Hashtag', 'atomUri' => 'ostatus:atomUri',
- 'conversation' => 'ostatus:conversation',
- 'inReplyToAtomUri' => 'ostatus:inReplyToAtomUri']]];
-
- $data = array_merge($data, self::createNote($item));
-
-
- return $data;
- }
-
- private static function createTagList($item)
- {
- $tags = [];
-
- $terms = Term::tagArrayFromItemId($item['id']);
- foreach ($terms as $term) {
- if ($term['type'] == TERM_MENTION) {
- $contact = Contact::getDetailsByURL($term['url']);
- if (!empty($contact['addr'])) {
- $mention = '@' . $contact['addr'];
- } else {
- $mention = '@' . $term['url'];
- }
-
- $tags[] = ['type' => 'Mention', 'href' => $term['url'], 'name' => $mention];
- }
- }
-
- return $tags;
- }
-
- public static function createNote($item)
- {
- $data = [];
- $data['id'] = $item['uri'];
- $data['type'] = 'Note';
- $data['summary'] = null; // Ignore by now
-
- if ($item['uri'] != $item['thr-parent']) {
- $data['inReplyTo'] = $item['thr-parent'];
- } else {
- $data['inReplyTo'] = null;
- }
-
- $data['published'] = DateTimeFormat::utc($item["created"]."+00:00", DateTimeFormat::ATOM);
-
- if ($item["created"] != $item["edited"]) {
- $data['updated'] = DateTimeFormat::utc($item["edited"]."+00:00", DateTimeFormat::ATOM);
- }
-
- $data['url'] = $item['uri'];
- $data['attributedTo'] = $item['author-link'];
- $data['actor'] = $item['author-link'];
- $data['sensitive'] = false; // - Query NSFW
-
- $conversation = DBA::selectFirst('conversation', ['conversation-uri'], ['item-uri' => $item['parent-uri']]);
- if (DBA::isResult($conversation) && !empty($conversation['conversation-uri'])) {
- $conversation_uri = $conversation['conversation-uri'];
- } else {
- $conversation_uri = $item['parent-uri'];
- }
-
- $data['conversation'] = $conversation_uri;
-
- if (!empty($item['title'])) {
- $data['name'] = BBCode::convert($item['title'], false, 7);
- }
-
- $data['content'] = BBCode::convert($item['body'], false, 7);
- $data['source'] = ['content' => $item['body'], 'mediaType' => "text/bbcode"];
- $data['attachment'] = []; // @ToDo
- $data['tag'] = self::createTagList($item);
- $data = array_merge($data, ActivityPub::createPermissionBlockForItem($item));
-
- //$data['emoji'] = []; // Ignore by now
- return $data;
- }
-
- public static function transmitActivity($activity, $target, $uid)
- {
- $profile = self::fetchprofile($target);
-
- $owner = User::getOwnerDataById($uid);
-
- $data = ['@context' => 'https://www.w3.org/ns/activitystreams',
- 'id' => System::baseUrl() . '/activity/' . System::createGUID(),
- 'type' => $activity,
- 'actor' => $owner['url'],
- 'object' => $profile['url'],
- 'to' => $profile['url']];
-
- logger('Sending activity ' . $activity . ' to ' . $target . ' for user ' . $uid, LOGGER_DEBUG);
-
- $signed = LDSignature::sign($data, $owner);
- return HTTPSignature::transmit($signed, $profile['inbox'], $uid);
- }
-
- public static function transmitContactAccept($target, $id, $uid)
- {
- $profile = self::fetchprofile($target);
-
- $owner = User::getOwnerDataById($uid);
- $data = ['@context' => 'https://www.w3.org/ns/activitystreams',
- 'id' => System::baseUrl() . '/activity/' . System::createGUID(),
- 'type' => 'Accept',
- 'actor' => $owner['url'],
- 'object' => ['id' => $id, 'type' => 'Follow',
- 'actor' => $profile['url'],
- 'object' => $owner['url']],
- 'to' => $profile['url']];
-
- logger('Sending accept to ' . $target . ' for user ' . $uid . ' with id ' . $id, LOGGER_DEBUG);
-
- $signed = LDSignature::sign($data, $owner);
- return HTTPSignature::transmit($signed, $profile['inbox'], $uid);
- }
-
- public static function transmitContactReject($target, $id, $uid)
- {
- $profile = self::fetchprofile($target);
-
- $owner = User::getOwnerDataById($uid);
- $data = ['@context' => 'https://www.w3.org/ns/activitystreams',
- 'id' => System::baseUrl() . '/activity/' . System::createGUID(),
- 'type' => 'Reject',
- 'actor' => $owner['url'],
- 'object' => ['id' => $id, 'type' => 'Follow',
- 'actor' => $profile['url'],
- 'object' => $owner['url']],
- 'to' => $profile['url']];
-
- logger('Sending reject to ' . $target . ' for user ' . $uid . ' with id ' . $id, LOGGER_DEBUG);
-
- $signed = LDSignature::sign($data, $owner);
- return HTTPSignature::transmit($signed, $profile['inbox'], $uid);
- }
-
- public static function transmitContactUndo($target, $uid)