-- ------------------------------------------
-- Friendica 2022.12-dev (Giant Rhubarb)
--- DB_UPDATE_VERSION 1497
+-- DB_UPDATE_VERSION 1500
-- ------------------------------------------
FOREIGN KEY (`wid`) REFERENCES `workerqueue` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Posts that are about to be distributed at a later time';
+--
+-- TABLE diaspora-contact
+--
+CREATE TABLE IF NOT EXISTS `diaspora-contact` (
+ `uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the contact URL',
+ `addr` varchar(255) COMMENT '',
+ `alias` varchar(255) COMMENT '',
+ `nick` varchar(255) COMMENT '',
+ `name` varchar(255) COMMENT '',
+ `given-name` varchar(255) COMMENT '',
+ `family-name` varchar(255) COMMENT '',
+ `photo` varchar(255) COMMENT '',
+ `photo-medium` varchar(255) COMMENT '',
+ `photo-small` varchar(255) COMMENT '',
+ `batch` varchar(255) COMMENT '',
+ `notify` varchar(255) COMMENT '',
+ `poll` varchar(255) COMMENT '',
+ `subscribe` varchar(255) COMMENT '',
+ `searchable` boolean COMMENT '',
+ `pubkey` text COMMENT '',
+ `gsid` int unsigned COMMENT 'Global Server ID',
+ `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
+ `updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
+ `interacting_count` int unsigned DEFAULT 0 COMMENT 'Number of contacts this contact interactes with',
+ `interacted_count` int unsigned DEFAULT 0 COMMENT 'Number of contacts that interacted with this contact',
+ `post_count` int unsigned DEFAULT 0 COMMENT 'Number of posts and comments',
+ PRIMARY KEY(`uri-id`),
+ UNIQUE INDEX `addr` (`addr`),
+ INDEX `alias` (`alias`),
+ INDEX `gsid` (`gsid`),
+ FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
+ FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
+) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Diaspora compatible contacts - used in the Diaspora implementation';
+
--
-- TABLE diaspora-interaction
--
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Events';
---
--- TABLE fcontact
---
-CREATE TABLE IF NOT EXISTS `fcontact` (
- `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
- `guid` varbinary(255) NOT NULL DEFAULT '' COMMENT 'unique id',
- `url` varbinary(383) NOT NULL DEFAULT '' COMMENT '',
- `uri-id` int unsigned COMMENT 'Id of the item-uri table entry that contains the fcontact url',
- `name` varchar(255) NOT NULL DEFAULT '' COMMENT '',
- `photo` varbinary(383) NOT NULL DEFAULT '' COMMENT '',
- `request` varbinary(383) NOT NULL DEFAULT '' COMMENT '',
- `nick` varchar(255) NOT NULL DEFAULT '' COMMENT '',
- `addr` varchar(255) NOT NULL DEFAULT '' COMMENT '',
- `batch` varbinary(383) NOT NULL DEFAULT '' COMMENT '',
- `notify` varbinary(383) NOT NULL DEFAULT '' COMMENT '',
- `poll` varbinary(383) NOT NULL DEFAULT '' COMMENT '',
- `confirm` varbinary(383) NOT NULL DEFAULT '' COMMENT '',
- `priority` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
- `network` char(4) NOT NULL DEFAULT '' COMMENT '',
- `alias` varbinary(383) NOT NULL DEFAULT '' COMMENT '',
- `pubkey` text COMMENT '',
- `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
- `updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
- `interacting_count` int unsigned DEFAULT 0 COMMENT 'Number of contacts this contact interactes with',
- `interacted_count` int unsigned DEFAULT 0 COMMENT 'Number of contacts that interacted with this contact',
- `post_count` int unsigned DEFAULT 0 COMMENT 'Number of posts and comments',
- PRIMARY KEY(`id`),
- INDEX `addr` (`addr`(32)),
- UNIQUE INDEX `url` (`url`(190)),
- UNIQUE INDEX `uri-id` (`uri-id`),
- FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
-) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Diaspora compatible contacts - used in the Diaspora implementation';
-
--
-- TABLE fetch-entry
--
`contact`.`blocked` AS `blocked`,
`contact`.`notify` AS `dfrn-notify`,
`contact`.`poll` AS `dfrn-poll`,
- `fcontact`.`guid` AS `diaspora-guid`,
- `fcontact`.`batch` AS `diaspora-batch`,
- `fcontact`.`notify` AS `diaspora-notify`,
- `fcontact`.`poll` AS `diaspora-poll`,
- `fcontact`.`alias` AS `diaspora-alias`,
+ `item-uri`.`guid` AS `diaspora-guid`,
+ `diaspora-contact`.`batch` AS `diaspora-batch`,
+ `diaspora-contact`.`notify` AS `diaspora-notify`,
+ `diaspora-contact`.`poll` AS `diaspora-poll`,
+ `diaspora-contact`.`alias` AS `diaspora-alias`,
`apcontact`.`uuid` AS `ap-uuid`,
`apcontact`.`type` AS `ap-type`,
`apcontact`.`following` AS `ap-following`,
FROM `contact`
LEFT JOIN `item-uri` ON `item-uri`.`id` = `contact`.`uri-id`
LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `contact`.`uri-id`
- LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = contact.`uri-id`
+ LEFT JOIN `diaspora-contact` ON `diaspora-contact`.`uri-id` = contact.`uri-id`
LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid`
WHERE `contact`.`uid` = 0;
`ucontact`.`reason` AS `reason`,
`contact`.`notify` AS `dfrn-notify`,
`contact`.`poll` AS `dfrn-poll`,
- `fcontact`.`guid` AS `diaspora-guid`,
- `fcontact`.`batch` AS `diaspora-batch`,
- `fcontact`.`notify` AS `diaspora-notify`,
- `fcontact`.`poll` AS `diaspora-poll`,
- `fcontact`.`alias` AS `diaspora-alias`,
- `fcontact`.`interacting_count` AS `diaspora-interacting_count`,
- `fcontact`.`interacted_count` AS `diaspora-interacted_count`,
- `fcontact`.`post_count` AS `diaspora-post_count`,
+ `item-uri`.`guid` AS `diaspora-guid`,
+ `diaspora-contact`.`batch` AS `diaspora-batch`,
+ `diaspora-contact`.`notify` AS `diaspora-notify`,
+ `diaspora-contact`.`poll` AS `diaspora-poll`,
+ `diaspora-contact`.`alias` AS `diaspora-alias`,
+ `diaspora-contact`.`interacting_count` AS `diaspora-interacting_count`,
+ `diaspora-contact`.`interacted_count` AS `diaspora-interacted_count`,
+ `diaspora-contact`.`post_count` AS `diaspora-post_count`,
`apcontact`.`uuid` AS `ap-uuid`,
`apcontact`.`type` AS `ap-type`,
`apcontact`.`following` AS `ap-following`,
INNER JOIN `contact` ON `contact`.`uri-id` = `ucontact`.`uri-id` AND `contact`.`uid` = 0
LEFT JOIN `item-uri` ON `item-uri`.`id` = `ucontact`.`uri-id`
LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `ucontact`.`uri-id`
- LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = `ucontact`.`uri-id` AND `fcontact`.`network` = 'dspr'
+ LEFT JOIN `diaspora-contact` ON `diaspora-contact`.`uri-id` = `ucontact`.`uri-id`
LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid`;
--
`profile_field`.`edited` AS `edited`
FROM `profile_field`
INNER JOIN `permissionset` ON `permissionset`.`id` = `profile_field`.`psid`;
+
+--
+-- VIEW diaspora-contact-view
+--
+DROP VIEW IF EXISTS `diaspora-contact-view`;
+CREATE VIEW `diaspora-contact-view` AS SELECT
+ `diaspora-contact`.`uri-id` AS `uri-id`,
+ `item-uri`.`uri` AS `url`,
+ `item-uri`.`guid` AS `guid`,
+ `diaspora-contact`.`addr` AS `addr`,
+ `diaspora-contact`.`alias` AS `alias`,
+ `diaspora-contact`.`nick` AS `nick`,
+ `diaspora-contact`.`name` AS `name`,
+ `diaspora-contact`.`given-name` AS `given-name`,
+ `diaspora-contact`.`family-name` AS `family-name`,
+ `diaspora-contact`.`photo` AS `photo`,
+ `diaspora-contact`.`photo-medium` AS `photo-medium`,
+ `diaspora-contact`.`photo-small` AS `photo-small`,
+ `diaspora-contact`.`batch` AS `batch`,
+ `diaspora-contact`.`notify` AS `notify`,
+ `diaspora-contact`.`poll` AS `poll`,
+ `diaspora-contact`.`subscribe` AS `subscribe`,
+ `diaspora-contact`.`searchable` AS `searchable`,
+ `diaspora-contact`.`pubkey` AS `pubkey`,
+ `gserver`.`url` AS `baseurl`,
+ `diaspora-contact`.`gsid` AS `gsid`,
+ `diaspora-contact`.`created` AS `created`,
+ `diaspora-contact`.`updated` AS `updated`,
+ `diaspora-contact`.`interacting_count` AS `interacting_count`,
+ `diaspora-contact`.`interacted_count` AS `interacted_count`,
+ `diaspora-contact`.`post_count` AS `post_count`
+ FROM `diaspora-contact`
+ INNER JOIN `item-uri` ON `item-uri`.`id` = `diaspora-contact`.`uri-id`
+ LEFT JOIN `gserver` ON `gserver`.`id` = `diaspora-contact`.`gsid`;
* [AndStatus](http://andstatus.org) ([F-Droid](https://f-droid.org/repository/browse/?fdid=org.andstatus.app), [Google Play](https://play.google.com/store/apps/details?id=org.andstatus.app))
* [B4X for Pleroma & Mastodon](https://github.com/AnywhereSoftware/B4X-Pleroma)
-* [Fedi](https://play.google.com/store/apps/details?id=com.fediverse.app)
+* [Fedi](https://github.com/Big-Fig/Fediverse.app) ([Google Play](https://play.google.com/store/apps/details?id=com.fediverse.app)
* [Fedilab](https://fedilab.app) ([F-Droid](https://f-droid.org/app/fr.gouv.etalab.mastodon), [Google Play](https://play.google.com/store/apps/details?id=app.fedilab.android))
* [Friendiqa](https://git.friendi.ca/lubuwest/Friendiqa) ([F-Droid](https://git.friendi.ca/lubuwest/Friendiqa#install), [Google Play](https://play.google.com/store/apps/details?id=org.qtproject.friendiqa))
* [Husky](https://git.sr.ht/~captainepoch/husky) ([F-Droid](https://f-droid.org/repository/browse/?fdid=su.xash.husky), [Google Play](https://play.google.com/store/apps/details?id=su.xash.husky))
-* [Mastodon for Android](https://github.com/mastodon/mastodon-android) (F-Droid: Pending, [Google-Play](https://play.google.com/store/apps/details?id=org.joinmastodon.android))
-* [Subway Tooter](https://github.com/tateisu/SubwayTooter)
-* [Tooot](https://tooot.app/)
+* [Mastodon](https://github.com/mastodon/mastodon-android) ([F-Droid](https://f-droid.org/en/packages/org.joinmastodon.android/), [Google Play](https://play.google.com/store/apps/details?id=org.joinmastodon.android))
+* [Subway Tooter](https://github.com/tateisu/SubwayTooter) ([F-Droid](https://android.izzysoft.de/repo/apk/jp.juggler.subwaytooter))
+* [Tooot](https://tooot.app/) ([Google Play](https://play.google.com/store/apps/details?id=com.xmflsct.app.tooot))
* [Tusky](https://tusky.app) ([F-Droid](https://f-droid.org/repository/browse/?fdid=com.keylesspalace.tusky), [Google Play](https://play.google.com/store/apps/details?id=com.keylesspalace.tusky))
* [Twidere](https://github.com/TwidereProject/Twidere-Android) ([F-Droid](https://f-droid.org/repository/browse/?fdid=org.mariotaku.twidere), [Google Play](https://play.google.com/store/apps/details?id=com.twidere.twiderex))
* [TwidereX](https://github.com/TwidereProject/TwidereX-Android) ([F-Droid](https://f-droid.org/en/packages/com.twidere.twiderex/), [Google Play](https://play.google.com/store/apps/details?id=com.twidere.twiderex))
* [Yuito](https://github.com/accelforce/Yuito) ([Google Play](https://play.google.com/store/apps/details?id=net.accelf.yuito))
-#### SailfishOS
-
-* [Friendly](https://openrepos.net/content/fabrixxm/friendly), last update: 2018
-
#### iOS
* [B4X for Pleroma & Mastodon](https://github.com/AnywhereSoftware/B4X-Pleroma) ([AppStore](https://apps.apple.com/app/b4x-pleroma/id1538396871))
-* [Fedi](https://fediapp.com) ([AppStore](https://apps.apple.com/de/app/fedi-for-pleroma-and-mastodon/id1478806281))
+* [Fedi](https://github.com/Big-Fig/Fediverse.app) ([AppStore](https://apps.apple.com/de/app/fedi-for-pleroma-and-mastodon/id1478806281))
* [Mastodon for iPhone and iPad](https://joinmastodon.org/apps) ([AppStore](https://apps.apple.com/us/app/mastodon-for-iphone/id1571998974))
* [Stella*](https://www.stella-app.net/) ([AppStore](https://apps.apple.com/us/app/stella-for-mastodon-twitter/id921372048))
* [Tooot](https://github.com/tooot-app) ([AppStore](https://apps.apple.com/app/id1549772269), Data collection (not linked to identity)
#### Linux
* [Choqok](https://choqok.kde.org)
-* [Whalebird](https://whalebird.social)
-* [TheDesk](https://ja.mstdn.wiki/TheDesk)
+* [Whalebird](https://whalebird.social/en/desktop/contents) ([GitHub](https://github.com/h3poteto/whalebird-desktop))
+* [TheDesk](https://thedesk.top/en/) ([GitHub](https://github.com/cutls/TheDesk))
* [Toot](https://toot.readthedocs.io/en/latest/)
* [Tootle](https://github.com/bleakgrey/tootle)
#### macOS
-* [Mastonaut](https://mastonaut.app/) ([AppStore](https://apps.apple.com/us/app/mastonaut/id1450757574)), closed source
+* [TheDesk](https://thedesk.top/en/) ([GitHub](https://github.com/cutls/TheDesk))
* [Whalebird](https://whalebird.social/en/desktop/contents) ([AppStore](https://apps.apple.com/de/app/whalebird/id1378283354), [GitHub](https://github.com/h3poteto/whalebird-desktop))
#### Windows
+* [TheDesk](https://thedesk.top/en/) ([GitHub](https://github.com/cutls/TheDesk))
* [Whalebird](https://whalebird.social/en/desktop/contents) ([Website Download](https://whalebird.social/en/desktop/contents/downloads#windows), [GitHub](https://github.com/h3poteto/whalebird-desktop))
#### Web Frontend
| [contact-relation](help/database/db_contact-relation) | Contact relations |
| [conv](help/database/db_conv) | private messages |
| [delayed-post](help/database/db_delayed-post) | Posts that are about to be distributed at a later time |
+| [diaspora-contact](help/database/db_diaspora-contact) | Diaspora compatible contacts - used in the Diaspora implementation |
| [diaspora-interaction](help/database/db_diaspora-interaction) | Signed Diaspora Interaction |
| [endpoint](help/database/db_endpoint) | ActivityPub endpoints - used in the ActivityPub implementation |
| [event](help/database/db_event) | Events |
-| [fcontact](help/database/db_fcontact) | Diaspora compatible contacts - used in the Diaspora implementation |
| [fetch-entry](help/database/db_fetch-entry) | |
| [fetched-activity](help/database/db_fetched-activity) | Id of fetched activities |
| [fsuggest](help/database/db_fsuggest) | friend suggestion stuff |
--- /dev/null
+Table diaspora-contact
+===========
+
+Diaspora compatible contacts - used in the Diaspora implementation
+
+Fields
+------
+
+| Field | Description | Type | Null | Key | Default | Extra |
+| ----------------- | ------------------------------------------------------------ | ------------ | ---- | --- | ------------------- | ----- |
+| uri-id | Id of the item-uri table entry that contains the contact URL | int unsigned | NO | PRI | NULL | |
+| addr | | varchar(255) | YES | | NULL | |
+| alias | | varchar(255) | YES | | NULL | |
+| nick | | varchar(255) | YES | | NULL | |
+| name | | varchar(255) | YES | | NULL | |
+| given-name | | varchar(255) | YES | | NULL | |
+| family-name | | varchar(255) | YES | | NULL | |
+| photo | | varchar(255) | YES | | NULL | |
+| photo-medium | | varchar(255) | YES | | NULL | |
+| photo-small | | varchar(255) | YES | | NULL | |
+| batch | | varchar(255) | YES | | NULL | |
+| notify | | varchar(255) | YES | | NULL | |
+| poll | | varchar(255) | YES | | NULL | |
+| subscribe | | varchar(255) | YES | | NULL | |
+| searchable | | boolean | YES | | NULL | |
+| pubkey | | text | YES | | NULL | |
+| gsid | Global Server ID | int unsigned | YES | | NULL | |
+| created | | datetime | NO | | 0001-01-01 00:00:00 | |
+| updated | | datetime | NO | | 0001-01-01 00:00:00 | |
+| interacting_count | Number of contacts this contact interactes with | int unsigned | YES | | 0 | |
+| interacted_count | Number of contacts that interacted with this contact | int unsigned | YES | | 0 | |
+| post_count | Number of posts and comments | int unsigned | YES | | 0 | |
+
+Indexes
+------------
+
+| Name | Fields |
+| ------- | ------------ |
+| PRIMARY | uri-id |
+| addr | UNIQUE, addr |
+| alias | alias |
+| gsid | gsid |
+
+Foreign Keys
+------------
+
+| Field | Target Table | Target Field |
+|-------|--------------|--------------|
+| uri-id | [item-uri](help/database/db_item-uri) | id |
+| gsid | [gserver](help/database/db_gserver) | id |
+
+Return to [database documentation](help/database)
+++ /dev/null
-Table fcontact
-===========
-
-Diaspora compatible contacts - used in the Diaspora implementation
-
-Fields
-------
-
-| Field | Description | Type | Null | Key | Default | Extra |
-| ----------------- | ------------------------------------------------------------- | ---------------- | ---- | --- | ------------------- | -------------- |
-| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment |
-| guid | unique id | varbinary(255) | NO | | | |
-| url | | varbinary(383) | NO | | | |
-| uri-id | Id of the item-uri table entry that contains the fcontact url | int unsigned | YES | | NULL | |
-| name | | varchar(255) | NO | | | |
-| photo | | varbinary(383) | NO | | | |
-| request | | varbinary(383) | NO | | | |
-| nick | | varchar(255) | NO | | | |
-| addr | | varchar(255) | NO | | | |
-| batch | | varbinary(383) | NO | | | |
-| notify | | varbinary(383) | NO | | | |
-| poll | | varbinary(383) | NO | | | |
-| confirm | | varbinary(383) | NO | | | |
-| priority | | tinyint unsigned | NO | | 0 | |
-| network | | char(4) | NO | | | |
-| alias | | varbinary(383) | NO | | | |
-| pubkey | | text | YES | | NULL | |
-| created | | datetime | NO | | 0001-01-01 00:00:00 | |
-| updated | | datetime | NO | | 0001-01-01 00:00:00 | |
-| interacting_count | Number of contacts this contact interactes with | int unsigned | YES | | 0 | |
-| interacted_count | Number of contacts that interacted with this contact | int unsigned | YES | | 0 | |
-| post_count | Number of posts and comments | int unsigned | YES | | 0 | |
-
-Indexes
-------------
-
-| Name | Fields |
-| ------- | ---------------- |
-| PRIMARY | id |
-| addr | addr(32) |
-| url | UNIQUE, url(190) |
-| uri-id | UNIQUE, uri-id |
-
-Foreign Keys
-------------
-
-| Field | Target Table | Target Field |
-|-------|--------------|--------------|
-| uri-id | [item-uri](help/database/db_item-uri) | id |
-
-Return to [database documentation](help/database)
$this->out('Updating event table fields');
$this->database->replaceInTableFields('event', ['uri'], $old_url, $new_url);
- $this->out('Updating fcontact table fields');
- $this->database->replaceInTableFields('fcontact', ['url', 'photo', 'request', 'batch', 'poll', 'confirm', 'alias'], $old_url, $new_url);
- $this->database->replaceInTableFields('fcontact', ['addr'], $old_host, $new_host);
+ $this->out('Updating diaspora-contact table fields');
+ $this->database->replaceInTableFields('diaspora-contact', ['alias', 'photo', 'photo-medium', 'photo-small', 'batch', 'notify', 'poll', 'subscribe'], $old_url, $new_url);
+ $this->database->replaceInTableFields('diaspora-contact', ['addr'], $old_host, $new_host);
$this->out('Updating fsuggest table fields');
$this->database->replaceInTableFields('fsuggest', ['url', 'request', 'photo'], $old_url, $new_url);
return self::$dice->create(Protocol\Activity::class);
}
+ public static function dsprContact(): Protocol\Diaspora\Repository\DiasporaContact
+ {
+ return self::$dice->create(Protocol\Diaspora\Repository\DiasporaContact::class);
+ }
+
//
// "Security" namespace instances
//
$old_tables = ['fserver', 'gcign', 'gcontact', 'gcontact-relation', 'gfollower' ,'glink', 'item-delivery-data',
'item-activity', 'item-content', 'item_id', 'participation', 'poll', 'poll_result', 'queue', 'retriever_rule',
'deliverq', 'dsprphotoq', 'ffinder', 'sign', 'spam', 'term', 'user-item', 'thread', 'item', 'challenge',
- 'auth_codes', 'tokens', 'clients', 'profile_check', 'host', 'conversation'];
+ 'auth_codes', 'tokens', 'clients', 'profile_check', 'host', 'conversation', 'fcontact'];
$tables = DBA::selectToArray('INFORMATION_SCHEMA.TABLES', ['TABLE_NAME'],
['TABLE_SCHEMA' => DBA::databaseName(), 'TABLE_TYPE' => 'BASE TABLE']);
/**
* Check if data exists
*
- * @param string $table Table name in format schema.table (while scheme is optiona)
+ * @param string $table Table name in format [schema.]table
* @param array $condition Array of fields for condition
*
* @return boolean Are there rows for that condition?
/**
* Insert a row into a table. Field value objects will be cast as string.
*
- * @param string $table Table name in format schema.table (while scheme is optiona)
+ * @param string $table Table name in format [schema.]table
* @param array $param parameter array
* @param int $duplicate_mode What to do on a duplicated entry
*
* Inserts a row with the provided data in the provided table.
* If the data corresponds to an existing row through a UNIQUE or PRIMARY index constraints, it updates the row instead.
*
- * @param string $table Table name in format schema.table (while scheme is optiona)
+ * @param string $table Table name in format [schema.]table
* @param array $param parameter array
* @return boolean was the insert successful?
* @throws \Exception
*
* This function can be extended in the future to accept a table array as well.
*
- * @param string $table Table name in format schema.table (while scheme is optiona)
+ * @param string $table Table name in format [schema.]table
* @return boolean was the lock successful?
* @throws \Exception
*/
* Only set $old_fields to a boolean value when you are sure that you will update a single row.
* When you set $old_fields to "true" then $fields must contain all relevant fields!
*
- * @param string $table Table name in format schema.table (while scheme is optiona)
+ * @param string $table Table name in format [schema.]table
* @param array $fields contains the fields that are updated
* @param array $condition condition array with the key values
* @param array|boolean $old_fields array with the old field values that are about to be replaced (true = update on duplicate, false = don't update identical fields)
/**
* Retrieve a single record from a table and returns it in an associative array
*
- * @param string $table Table name in format schema.table (while scheme is optiona)
+ * @param string $table Table name in format [schema.]table
* @param array $fields Array of selected fields, empty for all
* @param array $condition Array of fields for condition
* @param array $params Array of several parameters
/**
* Select rows from a table and fills an array with the data
*
- * @param string $table Table name in format schema.table (while scheme is optiona)
+ * @param string $table Table name in format [schema.]table
* @param array $fields Array of selected fields, empty for all
* @param array $condition Array of fields for condition
* @param array $params Array of several parameters
*
* $data = DBA::select($table, $fields, $condition, $params);
*
- * @param string $table Table name in format schema.table (while scheme is optiona)
+ * @param string $table Table name in format [schema.]table
* @param array $fields Array of selected fields, empty for all
* @param array $condition Array of fields for condition
* @param array $params Array of several parameters
/**
* Counts the rows from a table satisfying the provided condition
*
- * @param string $table Table name in format schema.table (while scheme is optiona)
+ * @param string $table Table name in format [schema.]table
* @param array $condition Array of fields for condition
* @param array $params Array of several parameters
*
return true;
}
+ if (!DBStructure::existsTable('fcontact')) {
+ DI::config()->set('system', 'post_update_version', 1425);
+ return true;
+ }
+
$condition = ["`uri-id` IS NULL"];
Logger::info('Start', ['rest' => DBA::count('fcontact', $condition)]);
*/
public function createFromUriId(int $uriId, int $uid = 0, bool $reblog = true): \Friendica\Object\Api\Mastodon\Status
{
- $fields = ['uri-id', 'uid', 'author-id', 'author-uri-id', 'author-link', 'causer-uri-id', 'post-reason', 'starred', 'app', 'title', 'body', 'raw-body', 'content-warning', 'question-id',
+ $fields = ['uri-id', 'uid', 'author-id', 'causer-id', 'author-uri-id', 'author-link', 'causer-uri-id', 'post-reason', 'starred', 'app', 'title', 'body', 'raw-body', 'content-warning', 'question-id',
'created', 'network', 'thr-parent-id', 'parent-author-id', 'language', 'uri', 'plink', 'private', 'vid', 'gravity', 'featured', 'has-media', 'quote-uri-id'];
$item = Post::selectFirst($fields, ['uri-id' => $uriId, 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
if (!$item) {
throw new HTTPException\NotFoundException('Item with URI ID ' . $uriId . ' not found' . ($uid ? ' for user ' . $uid : '.'));
}
- $is_reshare = $reblog && !is_null($item['causer-uri-id']) && ($item['post-reason'] == Item::PR_ANNOUNCEMENT);
-
- $account = $this->mstdnAccountFactory->createFromUriId($is_reshare ? $item['causer-uri-id']:$item['author-uri-id'], $uid);
+ if (($item['gravity'] == Item::GRAVITY_ACTIVITY) && ($item['vid'] == Verb::getID(Activity::ANNOUNCE))) {
+ $is_reshare = true;
+ $account = $this->mstdnAccountFactory->createFromUriId($item['author-uri-id'], $uid);
+ $uriId = $item['thr-parent-id'];
+ $item = Post::selectFirst($fields, ['uri-id' => $uriId, 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
+ if (!$item) {
+ throw new HTTPException\NotFoundException('Item with URI ID ' . $uriId . ' not found' . ($uid ? ' for user ' . $uid : '.'));
+ }
+ } else {
+ $is_reshare = $reblog && !is_null($item['causer-uri-id']) && ($item['causer-id'] != $item['author-id']) && ($item['post-reason'] == Item::PR_ANNOUNCEMENT);
+ $account = $this->mstdnAccountFactory->createFromUriId($is_reshare ? $item['causer-uri-id'] : $item['author-uri-id'], $uid);
+ }
$count_announce = Post::countPosts([
'thr-parent-id' => $uriId,
}
}
- if ($item['vid'] == Verb::getID(Activity::ANNOUNCE)) {
- $reshare = $this->createFromUriId($item['thr-parent-id'], $uid)->toArray();
- $reshared_item = Post::selectFirst(['title', 'body'], ['uri-id' => $item['thr-parent-id'],'uid' => [0, $uid]]);
- $item['title'] = $reshared_item['title'] ?? $item['title'];
- $item['body'] = $reshared_item['body'] ?? $item['body'];
- } else {
- $item['body'] = $this->contentItem->addSharedPost($item);
- $item['raw-body'] = $this->contentItem->addSharedPost($item, $item['raw-body']);
- $reshare = [];
- }
+ $item['body'] = $this->contentItem->addSharedPost($item);
+ $item['raw-body'] = $this->contentItem->addSharedPost($item, $item['raw-body']);
if ($is_reshare) {
$reshare = $this->createFromUriId($uriId, $uid, false)->toArray();
+ } else {
+ $reshare = [];
}
return new \Friendica\Object\Api\Mastodon\Status($item, $account, $counts, $userAttributes, $sensitive, $application, $mentions, $tags, $card, $attachments, $reshare, $poll);
}
if ($data['network'] == Protocol::DIASPORA) {
- FContact::updateFromProbeArray($data);
+ DI::dsprContact()->updateFromProbeArray($data);
}
self::updateFromProbeArray($contact_id, $data);
* @param integer $cid contact id
* @param string $size One of the Proxy::SIZE_* constants
* @param string $updated Contact update date
+ * @param bool $static If "true" a parameter is added to convert the avatar to a static one
* @return string avatar link
*/
- public static function getAvatarUrlForId(int $cid, string $size = '', string $updated = '', string $guid = ''): string
+ public static function getAvatarUrlForId(int $cid, string $size = '', string $updated = '', string $guid = '', bool $static = false): string
{
// We have to fetch the "updated" variable when it wasn't provided
// The parameter can be provided to improve performance
$url .= Proxy::PIXEL_LARGE . '/';
break;
}
- return $url . ($guid ?: $cid) . ($updated ? '?ts=' . strtotime($updated) : '');
+ $query_params = [];
+ if ($updated) {
+ $query_params['ts'] = strtotime($updated);
+ }
+ if ($static) {
+ $query_params['static'] = true;
+ }
+
+ return $url . ($guid ?: $cid) . (!empty($query_params) ? '?' . http_build_query($query_params) : '');
}
/**
* @param integer $cid contact id
* @param string $size One of the Proxy::SIZE_* constants
* @param string $updated Contact update date
+ * @param bool $static If "true" a parameter is added to convert the header to a static one
* @return string header link
*/
- public static function getHeaderUrlForId(int $cid, string $size = '', string $updated = '', string $guid = ''): string
+ public static function getHeaderUrlForId(int $cid, string $size = '', string $updated = '', string $guid = '', bool $static = false): string
{
// We have to fetch the "updated" variable when it wasn't provided
// The parameter can be provided to improve performance
break;
}
- return $url . ($guid ?: $cid) . ($updated ? '?ts=' . strtotime($updated) : '');
+ $query_params = [];
+ if ($updated) {
+ $query_params['ts'] = strtotime($updated);
+ }
+ if ($static) {
+ $query_params['static'] = true;
+ }
+
+ return $url . ($guid ?: $cid) . (!empty($query_params) ? '?' . http_build_query($query_params) : '');
}
/**
$ret = Probe::uri($contact['url'], $network, $contact['uid']);
if ($ret['network'] == Protocol::DIASPORA) {
- FContact::updateFromProbeArray($ret);
+ DI::dsprContact()->updateFromProbeArray($ret);
}
return self::updateFromProbeArray($id, $ret);
+++ /dev/null
-<?php
-/**
- * @copyright Copyright (C) 2010-2022, the Friendica project
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Model;
-
-use Friendica\Core\Logger;
-use Friendica\Core\Protocol;
-use Friendica\Core\Worker;
-use Friendica\Database\DBA;
-use Friendica\DI;
-use Friendica\Model\Item;
-use Friendica\Network\Probe;
-use Friendica\Util\DateTimeFormat;
-use Friendica\Util\Strings;
-
-class FContact
-{
- /**
- * Fetches data for a given handle
- *
- * @param string $handle The handle
- * @param boolean $update true = always update, false = never update, null = update when not found or outdated
- *
- * @return array the queried data
- * @throws \Friendica\Network\HTTPException\InternalServerErrorException
- * @throws \ImagickException
- */
- public static function getByURL(string $handle, $update = null): array
- {
- Logger::debug('Fetch fcontact', ['handle' => $handle, 'update' => $update]);
- $person = DBA::selectFirst('fcontact', [], ['network' => Protocol::DIASPORA, 'addr' => $handle]);
- if (!DBA::isResult($person)) {
- $urls = [$handle, str_replace('http://', 'https://', $handle), Strings::normaliseLink($handle)];
- $person = DBA::selectFirst('fcontact', [], ['network' => Protocol::DIASPORA, 'url' => $urls]);
- }
-
- if (DBA::isResult($person)) {
- Logger::debug('In cache', ['handle' => $handle]);
-
- if (is_null($update)) {
- $update = empty($person['guid']) || empty($person['uri-id']) || ($person['created'] <= DBA::NULL_DATETIME);
- if (GServer::getNextUpdateDate(true, $person['created'], $person['updated'], false) < DateTimeFormat::utcNow()) {
- Logger::debug('Start background update', ['handle' => $handle]);
- Worker::add(['priority' => Worker::PRIORITY_LOW, 'dont_fork' => true], 'UpdateFContact', $handle);
- }
- }
- } elseif (is_null($update)) {
- $update = true;
- } else {
- $person = [];
- }
-
- if ($update) {
- Logger::info('create or refresh', ['handle' => $handle]);
- $data = Probe::uri($handle, Protocol::DIASPORA);
-
- // Note that Friendica contacts will return a "Diaspora person"
- // if Diaspora connectivity is enabled on their server
- if ($data['network'] ?? '' === Protocol::DIASPORA) {
- self::updateFromProbeArray($data);
-
- $person = self::getByURL($handle, false);
- }
- }
-
- return $person;
- }
-
- /**
- * Updates the fcontact table
- *
- * @param array $arr The fcontact data
- * @throws \Exception
- */
- public static function updateFromProbeArray(array $arr)
- {
- $uriid = ItemURI::insert(['uri' => $arr['url'], 'guid' => $arr['guid']]);
-
- $fcontact = DBA::selectFirst('fcontact', ['created'], ['url' => $arr['url'], 'network' => $arr['network']]);
- $contact = Contact::getByUriId($uriid, ['id', 'created']);
- $apcontact = APContact::getByURL($arr['url'], false);
- if (!empty($apcontact)) {
- $interacted = $apcontact['following_count'];
- $interacting = $apcontact['followers_count'];
- $posts = $apcontact['statuses_count'];
- } elseif (!empty($contact['id'])) {
- $last_interaction = DateTimeFormat::utc('now - 180 days');
-
- $interacted = DBA::count('contact-relation', ["`cid` = ? AND NOT `follows` AND `last-interaction` > ?", $contact['id'], $last_interaction]);
- $interacting = DBA::count('contact-relation', ["`relation-cid` = ? AND NOT `follows` AND `last-interaction` > ?", $contact['id'], $last_interaction]);
- $posts = DBA::count('post', ['author-id' => $contact['id'], 'gravity' => [Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT]]);
- }
-
- $fields = [
- 'name' => $arr['name'],
- 'photo' => $arr['photo'],
- 'request' => $arr['request'],
- 'nick' => $arr['nick'],
- 'addr' => strtolower($arr['addr']),
- 'guid' => $arr['guid'],
- 'batch' => $arr['batch'],
- 'notify' => $arr['notify'],
- 'poll' => $arr['poll'],
- 'confirm' => $arr['confirm'],
- 'alias' => $arr['alias'],
- 'pubkey' => $arr['pubkey'],
- 'uri-id' => $uriid,
- 'interacting_count' => $interacting ?? 0,
- 'interacted_count' => $interacted ?? 0,
- 'post_count' => $posts ?? 0,
- 'updated' => DateTimeFormat::utcNow(),
- ];
-
- if (empty($fcontact['created'])) {
- $fields['created'] = $fields['updated'];
- } elseif (!empty($contact['created']) && ($fcontact['created'] <= DBA::NULL_DATETIME)) {
- $fields['created'] = $contact['created'];
- }
-
- $fields = DI::dbaDefinition()->truncateFieldsForTable('fcontact', $fields);
- DBA::update('fcontact', $fields, ['url' => $arr['url'], 'network' => $arr['network']], true);
- }
-
- /**
- * get a url (scheme://domain.tld/u/user) from a given Diaspora*
- * fcontact guid
- *
- * @param string $fcontact_guid Hexadecimal string guid
- * @return string|null the contact url or null
- * @throws \Exception
- */
- public static function getUrlByGuid(string $fcontact_guid)
- {
- Logger::info('fcontact', ['guid' => $fcontact_guid]);
-
- $fcontact = DBA::selectFirst('fcontact', ['url'], ["`url` != ? AND `network` = ? AND `guid` = ?", '', Protocol::DIASPORA, $fcontact_guid]);
- if (DBA::isResult($fcontact)) {
- return $fcontact['url'];
- }
-
- return null;
- }
-}
}
if (!$request['pinned'] && !$request['only_media']) {
- $condition = DBA::mergeConditions($condition, ["(`gravity` IN (?, ?) OR (`gravity` = ? AND `vid` = ?))",
- Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT, Item::GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE)]);
+ if ($request['exclude_replies']) {
+ $condition = DBA::mergeConditions($condition, ["(`gravity` = ? OR (`gravity` = ? AND `vid` = ?))",
+ Item::GRAVITY_PARENT, Item::GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE)]);
+ } else {
+ $condition = DBA::mergeConditions($condition, ["(`gravity` IN (?, ?) OR (`gravity` = ? AND `vid` = ?))",
+ Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT, Item::GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE)]);
+ }
+ } elseif ($request['exclude_replies']) {
+ $condition = DBA::mergeConditions($condition, ['gravity' => Item::GRAVITY_PARENT]);
}
if (!empty($request['max_id'])) {
$params['order'] = ['uri-id'];
}
- if (($request['pinned'] || $request['only_media']) && $request['exclude_replies']) {
- $condition = DBA::mergeConditions($condition, ['gravity' => Item::GRAVITY_PARENT]);
- }
-
if ($request['pinned']) {
$items = DBA::select('collection-view', ['uri-id'], $condition, $params);
} elseif ($request['only_media']) {
$items = DBA::select('media-view', ['uri-id'], $condition, $params);
- } elseif ($request['exclude_replies']) {
- $items = Post::selectThreadForUser($uid, ['uri-id'], $condition, $params);
} else {
$items = Post::selectForUser($uid, ['uri-id'], $condition, $params);
}
use Friendica\BaseModule;
use Friendica\Core\System;
-use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\APContact;
use Friendica\Model\User;
throw new HTTPException\InternalServerErrorException($error);
}
+ if (!empty($request['static'])) {
+ $img = new Image($imgdata, $photo['type']);
+ $img->toStatic();
+ $imgdata = $img->asString();
+ }
+
// if customsize is set and image is not a gif, resize it
if ($photo['type'] !== 'image/gif' && $customsize > 0 && $customsize <= Proxy::PIXEL_THUMB && $square_resize) {
$img = new Image($imgdata, $photo['type']);
$condition = DBA::mergeConditions($condition, ["((`gravity` = ? AND `wall`) OR
(`gravity` = ? AND `vid` = ? AND `origin`
- AND `thr-parent-id` IN (SELECT `uri-id` FROM `post` WHERE `gravity` = ? AND `network` IN (?, ?))))",
+ AND EXISTS(SELECT `uri-id` FROM `post` WHERE `uri-id` = `post-user-view`.`thr-parent-id` AND `gravity` = ? AND `network` IN (?, ?))))",
Item::GRAVITY_PARENT, Item::GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE), Item::GRAVITY_PARENT, Protocol::ACTIVITYPUB, Protocol::DFRN]);
$condition = DBA::mergeConditions($condition, ['uid' => $profile['uid'], 'network' => Protocol::FEDERATED,
*/
private static function rearrangeData(array $data): array
{
- $fields = ['name', 'nick', 'guid', 'url', 'addr', 'alias', 'photo', 'header',
+ $fields = ['name', 'given_name', 'family_name', 'nick', 'guid', 'url', 'addr', 'alias',
+ 'photo', 'photo_medium', 'photo_small', 'header',
'account-type', 'community', 'keywords', 'location', 'about', 'xmpp', 'matrix',
'hide', 'batch', 'notify', 'poll', 'request', 'confirm', 'subscribe', 'poco',
'following', 'followers', 'inbox', 'outbox', 'sharedinbox',
if (in_array($field, $numeric_fields)) {
$newdata[$field] = (int)$data[$field];
} else {
- $newdata[$field] = $data[$field];
+ $newdata[$field] = trim($data[$field]);
}
} elseif (!in_array($field, $numeric_fields)) {
$newdata[$field] = '';
$data['name'] = $search->item(0)->nodeValue;
}
+ $search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' given_name ')]", $vcard); // */
+ if ($search->length > 0) {
+ $data["given_name"] = $search->item(0)->nodeValue;
+ }
+
+ $search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' family_name ')]", $vcard); // */
+ if ($search->length > 0) {
+ $data["family_name"] = $search->item(0)->nodeValue;
+ }
+
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' searchable ')]", $vcard); // */
if ($search->length > 0) {
- $data['searchable'] = $search->item(0)->nodeValue;
+ $data['hide'] = (strtolower($search->item(0)->nodeValue) != 'true');
}
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' key ')]", $vcard); // */
}
}
- $avatar = [];
+ $avatars = [];
if (!empty($vcard)) {
$photos = $xpath->query("//*[contains(concat(' ', @class, ' '), ' photo ') or contains(concat(' ', @class, ' '), ' avatar ')]", $vcard); // */
foreach ($photos as $photo) {
}
if (isset($attr['src']) && isset($attr['width'])) {
- $avatar[$attr['width']] = $attr['src'];
+ $avatars[$attr['width']] = self::fixAvatar($attr['src'], $data['baseurl']);
}
// We don't have a width. So we just take everything that we got.
// This is a Hubzilla workaround which doesn't send a width.
- if ((sizeof($avatar) == 0) && !empty($attr['src'])) {
- $avatar[] = $attr['src'];
+ if (!$avatars && !empty($attr['src'])) {
+ $avatars[] = self::fixAvatar($attr['src'], $data['baseurl']);
}
}
}
- if (sizeof($avatar)) {
- ksort($avatar);
- $data['photo'] = self::fixAvatar(array_pop($avatar), $data['baseurl']);
+ if ($avatars) {
+ ksort($avatars);
+ $data['photo'] = array_pop($avatars);
+ if ($avatars) {
+ $data['photo_medium'] = array_pop($avatars);
+ }
+
+ if ($avatars) {
+ $data['photo_small'] = array_pop($avatars);
+ }
}
if ($dfrn) {
}
}
-
return $data;
}
$this->note = BBCode::convertForUriId($account['uri-id'], $account['about'], BBCode::EXTERNAL);
$this->url = $account['url'];
$this->avatar = Contact::getAvatarUrlForId($account['id'] ?? 0 ?: $account['pid'], Proxy::SIZE_SMALL, $account['updated'], $account['guid'] ?? '');
- $this->avatar_static = $this->avatar;
+ $this->avatar_static = Contact::getAvatarUrlForId($account['id'] ?? 0 ?: $account['pid'], Proxy::SIZE_SMALL, $account['updated'], $account['guid'] ?? '', true);
$this->header = Contact::getHeaderUrlForId($account['id'] ?? 0 ?: $account['pid'], '', $account['updated'], $account['guid'] ?? '');
- $this->header_static = $this->header;
+ $this->header_static = Contact::getHeaderUrlForId($account['id'] ?? 0 ?: $account['pid'], '', $account['updated'], $account['guid'] ?? '', true);
$this->followers_count = $account['ap-followers_count'] ?? $account['diaspora-interacted_count'] ?? 0;
$this->following_count = $account['ap-following_count'] ?? $account['diaspora-interacting_count'] ?? 0;
$this->statuses_count = $account['ap-statuses_count'] ?? $account['diaspora-post_count'] ?? 0;
use Friendica\DI;
use Friendica\Util\Images;
use Imagick;
+use ImagickDraw;
use ImagickPixel;
use GDImage;
use kornrunner\Blurhash\Blurhash;
}
$this->type = $type;
- if ($this->isImagick() && $this->loadData($data)) {
+ if ($this->isImagick() && (empty($data) || $this->loadData($data))) {
return;
} else {
// Failed to load with Imagick, fallback
*/
public function getBlurHash(): string
{
- if ($this->isImagick()) {
- // Imagick is not supported
- return '';
- }
-
$width = $this->getWidth();
$height = $this->getHeight();
for ($y = 0; $y < $height; ++$y) {
$row = [];
for ($x = 0; $x < $width; ++$x) {
- $index = imagecolorat($this->image, $x, $y);
- $colors = imagecolorsforindex($this->image, $index);
-
- $row[] = [$colors['red'], $colors['green'], $colors['blue']];
+ if ($this->isImagick()) {
+ $colors = $this->image->getImagePixelColor($x, $y)->getColor();
+ $row[] = [$colors['r'], $colors['g'], $colors['b']];
+ } else {
+ $index = imagecolorat($this->image, $x, $y);
+ $colors = @imagecolorsforindex($this->image, $index);
+ $row[] = [$colors['red'], $colors['green'], $colors['blue']];
+ }
}
$pixels[] = $row;
}
*/
public function getFromBlurHash(string $blurhash, int $width, int $height)
{
- if ($this->isImagick()) {
- // Imagick is not supported
- return;
- }
-
$scaled = Images::getScalingDimensions($width, $height, 90);
$pixels = Blurhash::decode($blurhash, $scaled['width'], $scaled['height']);
- $this->image = imagecreatetruecolor($scaled['width'], $scaled['height']);
+ if ($this->isImagick()) {
+ $this->image = new Imagick();
+ $draw = new ImagickDraw();
+ $this->image->newImage($scaled['width'], $scaled['height'], '', 'png');
+ } else {
+ $this->image = imagecreatetruecolor($scaled['width'], $scaled['height']);
+ }
+
for ($y = 0; $y < $scaled['height']; ++$y) {
for ($x = 0; $x < $scaled['width']; ++$x) {
[$r, $g, $b] = $pixels[$y][$x];
- imagesetpixel($this->image, $x, $y, imagecolorallocate($this->image, $r, $g, $b));
+ if ($this->isImagick()) {
+ $draw->setFillColor("rgb($r, $g, $b)");
+ $draw->point($x, $y);
+ } else {
+ imagesetpixel($this->image, $x, $y, imagecolorallocate($this->image, $r, $g, $b));
+ }
}
}
- $this->width = imagesx($this->image);
- $this->height = imagesy($this->image);
- $this->valid = true;
+ if ($this->isImagick()) {
+ $this->image->drawImage($draw);
+ } else {
+ $this->width = imagesx($this->image);
+ $this->height = imagesy($this->image);
+ }
+
+ $this->valid = true;
$this->scaleUp(min($width, $height));
}
use Friendica\Model\Contact;
use Friendica\Model\Conversation;
use Friendica\Model\Event;
-use Friendica\Model\FContact;
use Friendica\Model\GServer;
use Friendica\Model\Item;
use Friendica\Model\ItemURI;
use Friendica\Model\Profile;
use Friendica\Model\Tag;
use Friendica\Model\User;
+use Friendica\Network\HTTPException;
use Friendica\Network\Probe;
use Friendica\Util\Crypto;
use Friendica\Util\DateTimeFormat;
}
}
- $fcontact = FContact::getByURL($contact['addr']);
- if (empty($fcontact)) {
+ try {
+ $pubkey = DI::dsprContact()->getByAddr(WebFingerUri::fromString($contact['addr']))->pubKey;
+ } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
Logger::notice('Unable to find contact details for ' . $contact['id'] . ' - ' . $contact['addr']);
return -22;
}
- $pubkey = $fcontact['pubkey'] ?? '';
} else {
$pubkey = '';
}
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\Conversation;
-use Friendica\Model\FContact;
use Friendica\Model\GServer;
use Friendica\Model\Item;
use Friendica\Model\ItemURI;
use Friendica\Model\Tag;
use Friendica\Model\User;
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
+use Friendica\Network\HTTPException;
use Friendica\Network\Probe;
use Friendica\Util\Crypto;
use Friendica\Util\DateTimeFormat;
return false;
}
- $key = self::key($handle);
- if ($key == '') {
+ try {
+ $key = self::key(WebFingerUri::fromString($handle));
+ if ($key == '') {
+ throw new \InvalidArgumentException();
+ }
+ } catch (\InvalidArgumentException $e) {
Logger::notice("Couldn't get a key for handle " . $handle . ". Discarding.");
return false;
}
}
}
- $key = self::key($author_addr);
- if ($key == '') {
+ try {
+ $author = WebFingerUri::fromString($author_addr);
+ $key = self::key($author);
+ if ($key == '') {
+ throw new \InvalidArgumentException();
+ }
+ } catch (\InvalidArgumentException $e) {
Logger::notice("Couldn't get a key for handle " . $author_addr . ". Discarding.");
if ($no_exit) {
return false;
return [
'message' => (string)Strings::base64UrlDecode($base->data),
- 'author' => XML::unescape($author_addr),
- 'key' => (string)$key
+ 'author' => $author->getAddr(),
+ 'key' => (string)$key
];
}
if ($children->header) {
$public = true;
- $author_link = str_replace('acct:', '', $children->header->author_id);
+ $idom = $children->header;
} else {
// This happens with posts from a relais
if (empty($privKey)) {
$inner_iv = base64_decode($idom->iv);
$inner_aes_key = base64_decode($idom->aes_key);
+ }
- $author_link = str_replace('acct:', '', $idom->author_id);
+ try {
+ $author = WebFingerUri::fromString($idom->author_id);
+ } catch (\Throwable $e) {
+ Logger::notice('Could not retrieve author URI.', ['idom' => $idom]);
+ throw new \Friendica\Network\HTTPException\BadRequestException();
}
$dom = $basedom->children(ActivityNamespace::SALMON_ME);
$inner_decrypted = self::aesDecrypt($inner_aes_key, $inner_iv, $inner_encrypted);
}
- if (!$author_link) {
- Logger::notice('Could not retrieve author URI.');
- throw new \Friendica\Network\HTTPException\BadRequestException();
- }
// Once we have the author URI, go to the web and try to find their public key
- // (first this will look it up locally if it is in the fcontact cache)
+ // (first this will look it up locally if it is in the diaspora-contact cache)
// This will also convert diaspora public key from pkcs#1 to pkcs#8
-
- Logger::notice('Fetching key for '.$author_link);
- $key = self::key($author_link);
-
+ Logger::notice('Fetching key for ' . $author);
+ $key = self::key($author);
if (!$key) {
Logger::notice('Could not retrieve author key.');
throw new \Friendica\Network\HTTPException\BadRequestException();
Logger::notice('Message verified.');
return [
- 'message' => (string)$inner_decrypted,
- 'author' => XML::unescape($author_link),
- 'key' => (string)$key
+ 'message' => $inner_decrypted,
+ 'author' => $author->getAddr(),
+ 'key' => $key
];
}
{
// The sender is the handle of the contact that sent the message.
// This will often be different with relayed messages (for example "like" and "comment")
- $sender = $msg['author'];
+ $sender = WebFingerUri::fromString($msg['author']);
// This is only needed for private postings since this is already done for public ones before
if (is_null($fields)) {
$type = $fields->getName();
- Logger::info('Received message', ['type' => $type, 'sender' => $sender, 'user' => $importer['uid']]);
+ Logger::info('Received message', ['type' => $type, 'sender' => $sender->getAddr(), 'user' => $importer['uid']]);
switch ($type) {
case 'account_migration':
}
if (isset($parent_author_signature)) {
- $key = self::key($msg['author']);
+ $key = self::key(WebFingerUri::fromString($msg['author']));
if (empty($key)) {
Logger::info('No key found for parent', ['author' => $msg['author']]);
return false;
}
}
- $key = self::key($fields->author);
- if (empty($key)) {
+ try {
+ $key = self::key(WebFingerUri::fromString($fields->author));
+ if (empty($key)) {
+ throw new \InvalidArgumentException();
+ }
+ } catch (\Throwable $e) {
Logger::info('No key found', ['author' => $fields->author]);
return false;
}
/**
* Fetches the public key for a given handle
*
- * @param string $handle The handle
+ * @param WebFingerUri $uri The handle
*
* @return string The public key
- * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+ * @throws InternalServerErrorException
* @throws \ImagickException
*/
- private static function key(string $handle = null): string
+ private static function key(WebFingerUri $uri): string
{
- $handle = strval($handle);
-
- Logger::notice('Fetching diaspora key', ['handle' => $handle, 'callstack' => System::callstack(20)]);
-
- $fcontact = FContact::getByURL($handle);
- if (!empty($fcontact['pubkey'])) {
- return $fcontact['pubkey'];
+ Logger::notice('Fetching diaspora key', ['handle' => $uri->getAddr(), 'callstack' => System::callstack(20)]);
+ try {
+ return DI::dsprContact()->getByAddr($uri)->pubKey;
+ } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
+ return '';
}
-
- return '';
}
/**
* Get a contact id for a given handle
*
- * @todo Move to Friendica\Model\Contact
- *
- * @param int $uid The user id
- * @param string $handle The handle in the format user@domain.tld
+ * @param int $uid The user id
+ * @param WebFingerUri $uri
*
* @return array Contact data
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
- private static function contactByHandle(int $uid, string $handle): array
+ private static function contactByHandle(int $uid, WebFingerUri $uri): array
{
- return Contact::getByURL($handle, null, [], $uid);
+ return Contact::getByURL($uri->getAddr(), null, [], $uid);
}
/**
* Checks if the given contact url does support ActivityPub
*
- * @param string $url profile url
- * @param boolean $update true = always update, false = never update, null = update when not found or outdated
+ * @param string $url profile url or WebFinger address
+ * @param boolean|null $update true = always update, false = never update, null = update when not found or outdated
* @return boolean
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
- public static function isSupportedByContactUrl(string $url, $update = null)
+ public static function isSupportedByContactUrl(string $url, ?bool $update = null): bool
{
- return !empty(FContact::getByURL($url, $update));
+ $contact = Contact::getByURL($url, $update);
+
+ return DI::dsprContact()->existsByUriId($contact['uri-id'] ?? 0);
}
/**
/**
* Fetches the contact id for a handle and checks if posting is allowed
*
- * @param array $importer Array of the importer user
- * @param string $handle The checked handle in the format user@domain.tld
- * @param bool $is_comment Is the check for a comment?
+ * @param array $importer Array of the importer user
+ * @param WebFingerUri $contact_uri The checked contact
+ * @param bool $is_comment Is the check for a comment?
*
* @return array|bool The contact data or false on error
- * @throws \Exception
+ * @throws InternalServerErrorException
+ * @throws \ImagickException
*/
- private static function allowedContactByHandle(array $importer, string $handle, bool $is_comment = false)
+ private static function allowedContactByHandle(array $importer, WebFingerUri $contact_uri, bool $is_comment = false)
{
- $contact = self::contactByHandle($importer['uid'], $handle);
+ $contact = self::contactByHandle($importer['uid'], $contact_uri);
if (!$contact) {
- Logger::notice('A Contact for handle ' . $handle . ' and user ' . $importer['uid'] . ' was not found');
+ Logger::notice('A Contact for handle ' . $contact_uri . ' and user ' . $importer['uid'] . ' was not found');
// If a contact isn't found, we accept it anyway if it is a comment
if ($is_comment && ($importer['uid'] != 0)) {
- return self::contactByHandle(0, $handle);
+ return self::contactByHandle(0, $contact_uri);
} elseif ($is_comment) {
return $importer;
} else {
}
if (!self::postAllow($importer, $contact, $is_comment)) {
- Logger::notice('The handle: ' . $handle . ' is not allowed to post to user ' . $importer['uid']);
+ Logger::notice('The handle: ' . $contact_uri . ' is not allowed to post to user ' . $importer['uid']);
return false;
}
return $contact;
// 0 => '[url=/people/0123456789abcdef]Foo Bar[/url]'
// 1 => '0123456789abcdef'
// 2 => 'Foo Bar'
- $handle = FContact::getUrlByGuid($match[1]);
+ $handle = DI::dsprContact()->getUrlByGuid($match[1]);
if ($handle) {
$return = '@[url=' . $handle . ']' . $match[2] . '[/url]';
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
- public static function storeByGuid(string $guid, string $server, bool $force)
+ private static function storeByGuid(string $guid, string $server, bool $force)
{
$serverparts = parse_url($server);
return self::message($source_xml->root_guid, $server, ++$level);
}
- $author = '';
+ $author_handle = '';
// Fetch the author - for the old and the new Diaspora version
if ($source_xml->post->status_message && $source_xml->post->status_message->diaspora_handle) {
- $author = (string)$source_xml->post->status_message->diaspora_handle;
+ $author_handle = (string)$source_xml->post->status_message->diaspora_handle;
} elseif ($source_xml->author && ($source_xml->getName() == 'status_message')) {
- $author = (string)$source_xml->author;
+ $author_handle = (string)$source_xml->author;
}
- // If this isn't a "status_message" then quit
- if (!$author) {
+ try {
+ $author = WebFingerUri::fromString($author_handle);
+ } catch (\InvalidArgumentException $e) {
+ // If this isn't a "status_message" then quit
Logger::info("Message doesn't seem to be a status message");
return false;
}
return [
'message' => $x,
- 'author' => $author,
- 'key' => self::key($author)
+ 'author' => $author->getAddr(),
+ 'key' => self::key($author)
];
}
/**
* Fetches the item record of a given guid
*
- * @param int $uid The user id
- * @param string $guid message guid
- * @param string $author The handle of the item
- * @param array $contact The contact of the item owner
+ * @param int $uid The user id
+ * @param string $guid message guid
+ * @param WebFingerUri $author
+ * @param array $contact The contact of the item owner
*
* @return array|bool the item record or false on failure
* @throws \Exception
*/
- private static function parentItem(int $uid, string $guid, string $author, array $contact)
+ private static function parentItem(int $uid, string $guid, WebFingerUri $author, array $contact)
{
$fields = ['id', 'parent', 'body', 'wall', 'uri', 'guid', 'private', 'origin',
'author-name', 'author-link', 'author-avatar', 'gravity',
$item = Post::selectFirst($fields, $condition);
if (!DBA::isResult($item)) {
- $person = FContact::getByURL($author);
- $result = self::storeByGuid($guid, $person['url'], false);
+ try {
+ $result = self::storeByGuid($guid, DI::dsprContact()->getByAddr($author)->url, false);
- // We don't have an url for items that arrived at the public dispatcher
- if (!$result && !empty($contact['url'])) {
- $result = self::storeByGuid($guid, $contact['url'], false);
- }
+ // We don't have an url for items that arrived at the public dispatcher
+ if (!$result && !empty($contact['url'])) {
+ $result = self::storeByGuid($guid, $contact['url'], false);
+ }
- if ($result) {
- Logger::info('Fetched missing item ' . $guid . ' - result: ' . $result);
+ if ($result) {
+ Logger::info('Fetched missing item ' . $guid . ' - result: ' . $result);
- $item = Post::selectFirst($fields, $condition);
+ $item = Post::selectFirst($fields, $condition);
+ }
+ } catch (HTTPException\NotFoundException $e) {
+ Logger::notice('Unable to retrieve author details', ['author' => $author->getAddr()]);
}
}
}
/**
- * returns contact details
+ * returns contact details for the given user
*
- * @param array $def_contact The default contact if the person isn't found
- * @param array $person The record of the person
- * @param int $uid The user id
+ * @param array $def_contact The default details if the contact isn't found
+ * @param string $contact_url The url of the contact
+ * @param int $uid The user id
*
* @return array
* 'cid' => contact id
* 'network' => network type
* @throws \Exception
*/
- private static function authorContactByUrl(array $def_contact, array $person, int $uid): array
+ private static function authorContactByUrl(array $def_contact, string $contact_url, int $uid): array
{
- $condition = ['nurl' => Strings::normaliseLink($person['url']), 'uid' => $uid];
+ $condition = ['nurl' => Strings::normaliseLink($contact_url), 'uid' => $uid];
$contact = DBA::selectFirst('contact', ['id', 'network'], $condition);
if (DBA::isResult($contact)) {
$cid = $contact['id'];
*/
private static function receiveAccountMigration(array $importer, SimpleXMLElement $data): bool
{
- $old_handle = XML::unescape($data->author);
- $new_handle = XML::unescape($data->profile->author);
+ try {
+ $old_author = WebFingerUri::fromString(XML::unescape($data->author));
+ $new_author = WebFingerUri::fromString(XML::unescape($data->profile->author));
+ } catch (\Throwable $e) {
+ Logger::notice('Cannot find handles for sender and user', ['data' => $data]);
+ return false;
+ }
+
$signature = XML::unescape($data->signature);
- $contact = self::contactByHandle($importer['uid'], $old_handle);
+ $contact = self::contactByHandle($importer['uid'], $old_author);
if (!$contact) {
- Logger::notice('Cannot find contact for sender: ' . $old_handle . ' and user ' . $importer['uid']);
+ Logger::notice('Cannot find contact for sender: ' . $old_author . ' and user ' . $importer['uid']);
return false;
}
- Logger::notice('Got migration for ' . $old_handle . ', to ' . $new_handle . ' with user ' . $importer['uid']);
+ Logger::notice('Got migration for ' . $old_author . ', to ' . $new_author . ' with user ' . $importer['uid']);
// Check signature
- $signed_text = 'AccountMigration:' . $old_handle . ':' . $new_handle;
- $key = self::key($old_handle);
+ $signed_text = 'AccountMigration:' . $old_author . ':' . $new_author;
+ $key = self::key($old_author);
if (!Crypto::rsaVerify($signed_text, $signature, $key, 'sha256')) {
Logger::notice('No valid signature for migration.');
return false;
self::receiveProfile($importer, $data->profile);
// change the technical stuff in contact
- $data = Probe::uri($new_handle);
+ $data = Probe::uri($new_author);
if ($data['network'] == Protocol::PHANTOM) {
- Logger::notice("Account for " . $new_handle . " couldn't be probed.");
+ Logger::notice("Account for " . $new_author . " couldn't be probed.");
return false;
}
'network' => $data['network'],
];
- Contact::update($fields, ['addr' => $old_handle]);
+ Contact::update($fields, ['addr' => $old_author->getAddr()]);
Logger::notice('Contacts are updated.');
*/
private static function receiveAccountDeletion(SimpleXMLElement $data): bool
{
- $author = XML::unescape($data->author);
+ $author_handle = XML::unescape($data->author);
- $contacts = DBA::select('contact', ['id'], ['addr' => $author]);
+ $contacts = DBA::select('contact', ['id'], ['addr' => $author_handle]);
while ($contact = DBA::fetch($contacts)) {
Contact::remove($contact['id']);
}
DBA::close($contacts);
- Logger::notice('Removed contacts for ' . $author);
+ Logger::notice('Removed contacts for ' . $author_handle);
return true;
}
/**
* Fetch the uri from our database if we already have this item (maybe from ourselves)
*
- * @param string $author Author handle
- * @param string $guid Message guid
- * @param boolean $onlyfound Only return uri when found in the database
+ * @param string $guid Message guid
+ * @param WebFingerUri|null $person_uri Optional person to derive the base URL from
*
- * @return string The constructed uri or the one from our database or empty string on if $onlyfound is true
+ * @return string The constructed uri or the one from our database or empty string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
- private static function getUriFromGuid(string $author, string $guid, bool $onlyfound = false): string
+ private static function getUriFromGuid(string $guid, WebFingerUri $person_uri = null): string
{
$item = Post::selectFirst(['uri'], ['guid' => $guid]);
- if (DBA::isResult($item)) {
+ if ($item) {
return $item['uri'];
- } elseif (!$onlyfound) {
- $person = FContact::getByURL($author);
-
- $parts = parse_url($person['url']);
- unset($parts['path']);
- $host_url = (string)Uri::fromParts($parts);
-
- return $host_url . '/objects/' . $guid;
+ } elseif ($person_uri) {
+ try {
+ return DI::dsprContact()->selectOneByAddr($person_uri)->baseurl . '/objects/' . $guid;
+ } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
+ return '';
+ }
}
return '';
continue;
}
- $person = FContact::getByURL($match[3]);
- if (empty($person)) {
- continue;
- }
+ try {
+ $contact = DI::dsprContact()->getByUrl(new Uri($match[3]));
+ Tag::storeByHash($uriid, $match[1], $contact->name ?: $contact->nick, $contact->url);
+ } catch (\Throwable $e) {
- Tag::storeByHash($uriid, $match[1], $person['name'] ?: $person['nick'], $person['url']);
+ }
}
}
/**
* Processes an incoming comment
*
- * @param array $importer Array of the importer user
- * @param string $sender The sender of the message
+ * @param array $importer Array of the importer user
+ * @param WebFingerUri $sender The sender of the message
* @param SimpleXMLElement $data The message object
- * @param string $xml The original XML of the message
- * @param int $direction Indicates if the message had been fetched or pushed (self::PUSHED, self::FETCHED, self::FORCED_FETCH)
+ * @param string $xml The original XML of the message
+ * @param int $direction Indicates if the message had been fetched or pushed (self::PUSHED, self::FETCHED, self::FORCED_FETCH)
*
- * @return int The message id of the generated comment or "false" if there was an error
+ * @return bool The message id of the generated comment or "false" if there was an error
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
- private static function receiveComment(array $importer, string $sender, SimpleXMLElement $data, string $xml, int $direction): bool
+ private static function receiveComment(array $importer, WebFingerUri $sender, SimpleXMLElement $data, string $xml, int $direction): bool
{
- $author = XML::unescape($data->author);
+ $author = WebFingerUri::fromString(XML::unescape($data->author));
$guid = XML::unescape($data->guid);
$parent_guid = XML::unescape($data->parent_guid);
$text = XML::unescape($data->text);
if (isset($data->thread_parent_guid)) {
$thread_parent_guid = XML::unescape($data->thread_parent_guid);
- $thr_parent = self::getUriFromGuid('', $thread_parent_guid, true);
+ $thr_parent = self::getUriFromGuid($thread_parent_guid);
} else {
$thr_parent = '';
}
return false;
}
- $person = FContact::getByURL($author);
- if (!is_array($person)) {
- Logger::notice('Unable to find author details');
+ try {
+ $author_url = (string)DI::dsprContact()->getByAddr($author)->url;
+ } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
+ Logger::notice('Unable to find author details', ['author' => $author->getAddr()]);
return false;
}
// Fetch the contact id - if we know this contact
- $author_contact = self::authorContactByUrl($contact, $person, $importer['uid']);
+ $author_contact = self::authorContactByUrl($contact, $author_url, $importer['uid']);
$datarray = [];
$datarray['contact-id'] = $author_contact['cid'];
$datarray['network'] = $author_contact['network'];
- $datarray['author-link'] = $person['url'];
- $datarray['author-id'] = Contact::getIdForURL($person['url'], 0);
+ $datarray['author-link'] = $author_url;
+ $datarray['author-id'] = Contact::getIdForURL($author_url);
$datarray['owner-link'] = $contact['url'];
- $datarray['owner-id'] = Contact::getIdForURL($contact['url'], 0);
+ $datarray['owner-id'] = Contact::getIdForURL($contact['url']);
// Will be overwritten for sharing accounts in Item::insert
$datarray = self::setDirection($datarray, $direction);
$datarray['guid'] = $guid;
- $datarray['uri'] = self::getUriFromGuid($author, $guid);
+ $datarray['uri'] = self::getUriFromGuid($guid, $author);
$datarray['uri-id'] = ItemURI::insert(['uri' => $datarray['uri'], 'guid' => $datarray['guid']]);
$datarray['verb'] = Activity::POST;
$datarray['plink'] = self::plink($author, $guid, $toplevel_parent_item['guid']);
$body = Markdown::toBBCode($text);
- $datarray['body'] = self::replacePeopleGuid($body, $person['url']);
+ $datarray['body'] = self::replacePeopleGuid($body, $author_url);
self::storeMentions($datarray['uri-id'], $text);
Tag::storeRawTagsFromBody($datarray['uri-id'], $datarray['body']);
*/
private static function receiveConversationMessage(array $importer, array $contact, SimpleXMLElement $data, array $msg, $mesg, array $conversation): bool
{
- $author = XML::unescape($data->author);
+ $author_handle = XML::unescape($data->author);
$guid = XML::unescape($data->guid);
$subject = XML::unescape($data->subject);
// "diaspora_handle" is the element name from the old version
// "author" is the element name from the new version
if ($mesg->author) {
- $msg_author = XML::unescape($mesg->author);
+ $msg_author_handle = XML::unescape($mesg->author);
} elseif ($mesg->diaspora_handle) {
- $msg_author = XML::unescape($mesg->diaspora_handle);
+ $msg_author_handle = XML::unescape($mesg->diaspora_handle);
} else {
return false;
}
+ try {
+ $msg_author_uri = WebFingerUri::fromString($msg_author_handle);
+ } catch (\InvalidArgumentException $e) {
+ return false;
+ }
+
$msg_guid = XML::unescape($mesg->guid);
$msg_conversation_guid = XML::unescape($mesg->conversation_guid);
$msg_text = XML::unescape($mesg->text);
return false;
}
- $body = Markdown::toBBCode($msg_text);
- $message_uri = $msg_author . ':' . $msg_guid;
-
- $person = FContact::getByURL($msg_author);
+ $msg_author = DI::dsprContact()->getByAddr($msg_author_uri);
return Mail::insert([
'uid' => $importer['uid'],
'guid' => $msg_guid,
'convid' => $conversation['id'],
- 'from-name' => $person['name'],
- 'from-photo' => $person['photo'],
- 'from-url' => $person['url'],
+ 'from-name' => $msg_author->name,
+ 'from-photo' => (string)$msg_author->photo,
+ 'from-url' => (string)$msg_author->url,
'contact-id' => $contact['id'],
'title' => $subject,
- 'body' => $body,
- 'uri' => $message_uri,
- 'parent-uri' => $author . ':' . $guid,
+ 'body' => Markdown::toBBCode($msg_text),
+ 'uri' => $msg_author_handle . ':' . $msg_guid,
+ 'parent-uri' => $author_handle . ':' . $guid,
'created' => $msg_created_at
]);
}
*/
private static function receiveConversation(array $importer, array $msg, SimpleXMLElement $data)
{
- $author = XML::unescape($data->author);
+ $author_handle = XML::unescape($data->author);
$guid = XML::unescape($data->guid);
$subject = XML::unescape($data->subject);
$created_at = DateTimeFormat::utc(XML::unescape($data->created_at));
return false;
}
- $contact = self::allowedContactByHandle($importer, $msg['author'], true);
+ $contact = self::allowedContactByHandle($importer, WebFingerUri::fromString($msg['author']), true);
if (!$contact) {
return false;
}
$r = DBA::insert('conv', [
'uid' => $importer['uid'],
'guid' => $guid,
- 'creator' => $author,
+ 'creator' => $author_handle,
'created' => $created_at,
'updated' => DateTimeFormat::utcNow(),
'subject' => $subject,
/**
* Processes "like" messages
*
- * @param array $importer Array of the importer user
- * @param string $sender The sender of the message
+ * @param array $importer Array of the importer user
+ * @param WebFingerUri $sender The sender of the message
* @param SimpleXMLElement $data The message object
- * @param int $direction Indicates if the message had been fetched or pushed (self::PUSHED, self::FETCHED, self::FORCED_FETCH)
+ * @param int $direction Indicates if the message had been fetched or pushed (self::PUSHED, self::FETCHED, self::FORCED_FETCH)
*
* @return bool Success or failure
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
- private static function receiveLike(array $importer, string $sender, SimpleXMLElement $data, int $direction): bool
+ private static function receiveLike(array $importer, WebFingerUri $sender, SimpleXMLElement $data, int $direction): bool
{
- $author = XML::unescape($data->author);
+ $author = WebFingerUri::fromString(XML::unescape($data->author));
$guid = XML::unescape($data->guid);
$parent_guid = XML::unescape($data->parent_guid);
$parent_type = XML::unescape($data->parent_type);
return false;
}
- $person = FContact::getByURL($author);
- if (!is_array($person)) {
- Logger::notice('Unable to find author details');
+ try {
+ $author_url = (string)DI::dsprContact()->getByAddr($author)->url;
+ } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
+ Logger::notice('Unable to find author details', ['author' => $author->getAddr()]);
return false;
}
// Fetch the contact id - if we know this contact
- $author_contact = self::authorContactByUrl($contact, $person, $importer['uid']);
+ $author_contact = self::authorContactByUrl($contact, $author_url, $importer['uid']);
// "positive" = "false" would be a Dislike - wich isn't currently supported by Diaspora
// We would accept this anyhow.
$datarray = self::setDirection($datarray, $direction);
- $datarray['owner-link'] = $datarray['author-link'] = $person['url'];
- $datarray['owner-id'] = $datarray['author-id'] = Contact::getIdForURL($person['url'], 0);
+ $datarray['owner-link'] = $datarray['author-link'] = $author_url;
+ $datarray['owner-id'] = $datarray['author-id'] = Contact::getIdForURL($author_url);
$datarray['guid'] = $guid;
- $datarray['uri'] = self::getUriFromGuid($author, $guid);
+ $datarray['uri'] = self::getUriFromGuid($guid, $author);
$datarray['verb'] = $verb;
$datarray['gravity'] = Item::GRAVITY_ACTIVITY;
*/
private static function receiveMessage(array $importer, SimpleXMLElement $data): bool
{
- $author = XML::unescape($data->author);
+ $author_uri = WebFingerUri::fromString(XML::unescape($data->author));
$guid = XML::unescape($data->guid);
$conversation_guid = XML::unescape($data->conversation_guid);
$text = XML::unescape($data->text);
$created_at = DateTimeFormat::utc(XML::unescape($data->created_at));
- $contact = self::allowedContactByHandle($importer, $author, true);
+ $contact = self::allowedContactByHandle($importer, $author_uri, true);
if (!$contact) {
return false;
}
GServer::setProtocol($contact['gsid'], Post\DeliveryData::DIASPORA);
}
- $conversation = null;
-
$condition = ['uid' => $importer['uid'], 'guid' => $conversation_guid];
$conversation = DBA::selectFirst('conv', [], $condition);
-
if (!DBA::isResult($conversation)) {
Logger::notice('Conversation not available.');
return false;
}
- $message_uri = $author . ':' . $guid;
-
- $person = FContact::getByURL($author);
- if (!$person) {
- Logger::notice('Unable to find author details');
+ try {
+ $author = DI::dsprContact()->getByAddr($author_uri);
+ } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
+ Logger::notice('Unable to find author details', ['author' => $author_uri->getAddr()]);
return false;
}
$body = Markdown::toBBCode($text);
- $body = self::replacePeopleGuid($body, $person['url']);
+ $body = self::replacePeopleGuid($body, $author->url);
return Mail::insert([
'uid' => $importer['uid'],
'guid' => $guid,
'convid' => $conversation['id'],
- 'from-name' => $person['name'],
- 'from-photo' => $person['photo'],
- 'from-url' => $person['url'],
+ 'from-name' => $author->name,
+ 'from-photo' => (string)$author->photo,
+ 'from-url' => (string)$author->url,
'contact-id' => $contact['id'],
'title' => $conversation['subject'],
'body' => $body,
'reply' => 1,
- 'uri' => $message_uri,
- 'parent-uri' => $author . ':' . $conversation['guid'],
+ 'uri' => $author_uri . ':' . $guid,
+ 'parent-uri' => $author_uri . ':' . $conversation['guid'],
'created' => $created_at
]);
}
*/
private static function receiveParticipation(array $importer, SimpleXMLElement $data, int $direction): bool
{
- $author = strtolower(XML::unescape($data->author));
+ $author = WebFingerUri::fromString(strtolower(XML::unescape($data->author)));
$guid = XML::unescape($data->guid);
$parent_guid = XML::unescape($data->parent_guid);
return false;
}
- $person = FContact::getByURL($author);
- if (!is_array($person)) {
- Logger::notice('Person not found: ' . $author);
+ try {
+ $author_url = (string)DI::dsprContact()->getByAddr($author)->url;
+ } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
+ Logger::notice('unable to find author details', ['author' => $author->getAddr()]);
return false;
}
- $author_contact = self::authorContactByUrl($contact, $person, $importer['uid']);
+ $author_contact = self::authorContactByUrl($contact, $author_url, $importer['uid']);
// Store participation
$datarray = [];
$datarray = self::setDirection($datarray, $direction);
- $datarray['owner-link'] = $datarray['author-link'] = $person['url'];
- $datarray['owner-id'] = $datarray['author-id'] = Contact::getIdForURL($person['url'], 0);
+ $datarray['owner-link'] = $datarray['author-link'] = $author_url;
+ $datarray['owner-id'] = $datarray['author-id'] = Contact::getIdForURL($author_url);
$datarray['guid'] = $guid;
- $datarray['uri'] = self::getUriFromGuid($author, $guid);
+ $datarray['uri'] = self::getUriFromGuid($guid, $author);
$datarray['verb'] = Activity::FOLLOW;
$datarray['gravity'] = Item::GRAVITY_ACTIVITY;
*/
private static function receiveProfile(array $importer, SimpleXMLElement $data): bool
{
- $author = strtolower(XML::unescape($data->author));
+ $author = WebFingerUri::fromString(strtolower(XML::unescape($data->author)));
$contact = self::contactByHandle($importer['uid'], $author);
if (!$contact) {
$keywords = implode(', ', $keywords);
- $handle_parts = explode('@', $author);
- $nick = $handle_parts[0];
-
if ($name === '') {
- $name = $handle_parts[0];
+ $name = $author->getUser();
}
if (preg_match('|^https?://|', $image_url) === 0) {
// @TODO No HTTPS here?
- $image_url = 'http://' . $handle_parts[1] . $image_url;
+ $image_url = 'http://' . $author->getFullHost() . $image_url;
}
Contact::updateAvatar($contact['id'], $image_url);
$fields = ['name' => $name, 'location' => $location,
'name-date' => DateTimeFormat::utcNow(), 'about' => $about,
- 'addr' => $author, 'nick' => $nick, 'keywords' => $keywords,
+ 'addr' => $author->getAddr(), 'nick' => $author->getUser(), 'keywords' => $keywords,
'unsearchable' => !$searchable, 'sensitive' => $nsfw];
if (!empty($birthday)) {
*/
private static function receiveContactRequest(array $importer, SimpleXMLElement $data): bool
{
- $author = XML::unescape($data->author);
+ $author_handle = XML::unescape($data->author);
$recipient = XML::unescape($data->recipient);
- if (!$author || !$recipient) {
+ if (!$author_handle || !$recipient) {
return false;
}
+ $author = WebFingerUri::fromString($author_handle);
+
// the current protocol version doesn't know these fields
// That means that we will assume their existance
if (isset($data->following)) {
Logger::info("Author " . $author . " wants to listen to us.");
}
- $ret = FContact::getByURL($author);
-
- if (!$ret || ($ret['network'] != Protocol::DIASPORA)) {
- Logger::notice("Cannot resolve diaspora handle " . $author . " for ".$recipient);
+ try {
+ $author_url = (string)DI::dsprContact()->getByAddr($author)->url;
+ } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
+ Logger::notice('Cannot resolve diaspora handle for recipient', ['author' => $author->getAddr(), 'recipient' => $recipient]);
return false;
}
- $cid = Contact::getIdForURL($ret['url'], $importer['uid']);
+ $cid = Contact::getIdForURL($author_url, $importer['uid']);
if (!empty($cid)) {
$contact = DBA::selectFirst('contact', [], ['id' => $cid, 'network' => Protocol::NATIVE_SUPPORT]);
} else {
$contact = [];
}
- $item = ['author-id' => Contact::getIdForURL($ret['url']),
- 'author-link' => $ret['url']];
+ $item = [
+ 'author-id' => Contact::getIdForURL($author_url),
+ 'author-link' => $author_url
+ ];
$result = Contact::addRelationship($importer, $contact, $item, false);
if ($result === true) {
/**
* Stores a reshare activity
*
- * @param array $item Array of reshare post
- * @param integer $parent_message_id Id of the parent post
- * @param string $guid GUID string of reshare action
- * @param string $author Author handle
+ * @param array $item Array of reshare post
+ * @param integer $parent_message_id Id of the parent post
+ * @param string $guid GUID string of reshare action
+ * @param WebFingerUri $author Author handle
+ * @return false|void
+ * @throws InternalServerErrorException
+ * @throws \ImagickException
*/
- private static function addReshareActivity(array $item, int $parent_message_id, string $guid, string $author)
+ private static function addReshareActivity(array $item, int $parent_message_id, string $guid, WebFingerUri $author)
{
$parent = Post::selectFirst(['uri', 'guid'], ['id' => $parent_message_id]);
$datarray['owner-id'] = $datarray['author-id'];
$datarray['guid'] = $parent['guid'] . '-' . $guid;
- $datarray['uri'] = self::getUriFromGuid($author, $datarray['guid']);
+ $datarray['uri'] = self::getUriFromGuid($datarray['guid'], $author);
$datarray['thr-parent'] = $parent['uri'];
$datarray['verb'] = $datarray['body'] = Activity::ANNOUNCE;
*/
private static function receiveReshare(array $importer, SimpleXMLElement $data, string $xml, int $direction): bool
{
- $author = XML::unescape($data->author);
+ $author = WebFingerUri::fromString(XML::unescape($data->author));
$guid = XML::unescape($data->guid);
$created_at = DateTimeFormat::utc(XML::unescape($data->created_at));
- $root_author = XML::unescape($data->root_author);
+ try {
+ $root_author = WebFingerUri::fromString(XML::unescape($data->root_author));
+ } catch (\InvalidArgumentException $e) {
+ return false;
+ }
+
$root_guid = XML::unescape($data->root_guid);
/// @todo handle unprocessed property "provider_display_name"
$public = XML::unescape($data->public);
- $contact = self::allowedContactByHandle($importer, $author, false);
+ $contact = self::allowedContactByHandle($importer, $author);
if (!$contact) {
return false;
}
return true;
}
- $original_person = FContact::getByURL($root_author);
- if (!$original_person) {
+ try {
+ $original_person = DI::dsprContact()->getByAddr($root_author);
+ } catch (HTTPException\NotFoundException $e) {
return false;
}
$datarray['owner-id'] = $datarray['author-id'];
$datarray['guid'] = $guid;
- $datarray['uri'] = $datarray['thr-parent'] = self::getUriFromGuid($author, $guid);
+ $datarray['uri'] = $datarray['thr-parent'] = self::getUriFromGuid($guid, $author);
$datarray['uri-id'] = ItemURI::insert(['uri' => $datarray['uri'], 'guid' => $datarray['guid']]);
$datarray['verb'] = Activity::POST;
$datarray = self::setDirection($datarray, $direction);
- $datarray['quote-uri-id'] = self::getQuoteUriId($root_guid, $importer['uid'], $original_person['url']);
+ $datarray['quote-uri-id'] = self::getQuoteUriId($root_guid, $importer['uid'], $original_person->url);
if (empty($datarray['quote-uri-id'])) {
return false;
}
*/
private static function itemRetraction(array $importer, array $contact, SimpleXMLElement $data): bool
{
- $author = XML::unescape($data->author);
+ $author_uri = WebFingerUri::fromString(XML::unescape($data->author));
$target_guid = XML::unescape($data->target_guid);
$target_type = XML::unescape($data->target_type);
- $person = FContact::getByURL($author);
- if (!is_array($person)) {
- Logger::notice('Unable to find author detail for ' . $author);
+ try {
+ $author = DI::dsprContact()->getByAddr($author_uri);
+ } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
+ Logger::notice('Unable to find details for author', ['author' => $author_uri->getAddr()]);
return false;
}
- if (empty($contact['url'])) {
- $contact['url'] = $person['url'];
- }
+ $contact_url = $contact['url'] ?? '' ?: (string)$author->url;
// Fetch items that are about to be deleted
$fields = ['uid', 'id', 'parent', 'author-link', 'uri-id'];
$parent = Post::selectFirst(['author-link'], ['id' => $item['parent']]);
// Only delete it if the parent author really fits
- if (!Strings::compareLink($parent['author-link'], $contact['url']) && !Strings::compareLink($item['author-link'], $contact['url'])) {
- Logger::info("Thread author " . $parent['author-link'] . " and item author " . $item['author-link'] . " don't fit to expected contact " . $contact['url']);
+ if (!Strings::compareLink($parent['author-link'], $contact_url) && !Strings::compareLink($item['author-link'], $contact_url)) {
+ Logger::info("Thread author " . $parent['author-link'] . " and item author " . $item['author-link'] . " don't fit to expected contact " . $contact_url);
continue;
}
/**
* Receives retraction messages
*
- * @param array $importer Array of the importer user
- * @param string $sender The sender of the message
+ * @param array $importer Array of the importer user
+ * @param WebFingerUri $sender The sender of the message
* @param SimpleXMLElement $data The message object
*
* @return bool Success
* @throws \Exception
*/
- private static function receiveRetraction(array $importer, string $sender, SimpleXMLElement $data)
+ private static function receiveRetraction(array $importer, WebFingerUri $sender, SimpleXMLElement $data)
{
$target_type = XML::unescape($data->target_type);
*/
private static function receiveStatusMessage(array $importer, SimpleXMLElement $data, string $xml, int $direction)
{
- $author = XML::unescape($data->author);
+ $author = WebFingerUri::fromString(XML::unescape($data->author));
$guid = XML::unescape($data->guid);
$created_at = DateTimeFormat::utc(XML::unescape($data->created_at));
$public = XML::unescape($data->public);
$text = XML::unescape($data->text);
$provider_display_name = XML::unescape($data->provider_display_name);
- $contact = self::allowedContactByHandle($importer, $author, false);
+ $contact = self::allowedContactByHandle($importer, $author);
if (!$contact) {
return false;
}
$datarray = [];
$datarray['guid'] = $guid;
- $datarray['uri'] = $datarray['thr-parent'] = self::getUriFromGuid($author, $guid);
+ $datarray['uri'] = $datarray['thr-parent'] = self::getUriFromGuid($guid, $author);
$datarray['uri-id'] = ItemURI::insert(['uri' => $datarray['uri'], 'guid' => $datarray['guid']]);
// Attach embedded pictures to the body
$logid = Strings::getRandomHex(4);
- // We always try to use the data from the fcontact table.
+ // We always try to use the data from the diaspora-contact table.
// This is important for transmitting data to Friendica servers.
- if (!empty($contact['addr'])) {
- $fcontact = FContact::getByURL($contact['addr']);
- if (!empty($fcontact)) {
- $dest_url = ($public_batch ? $fcontact['batch'] : $fcontact['notify']);
- }
+ try {
+ $target = DI::dsprContact()->getByAddr(WebFingerUri::fromString($contact['addr']));
+ $dest_url = $public_batch ? $target->batch : $target->notify;
+ } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
+
}
if (empty($dest_url)) {
}
// When sending content to Friendica contacts using the Diaspora protocol
- // we have to fetch the public key from the fcontact.
+ // we have to fetch the public key from the diaspora-contact.
// This is due to the fact that legacy DFRN had unique keys for every contact.
$pubkey = $contact['pubkey'];
if (!empty($contact['addr'])) {
- $fcontact = FContact::getByURL($contact['addr']);
- if (!empty($fcontact)) {
- $pubkey = $fcontact['pubkey'];
+ try {
+ $pubkey = DI::dsprContact()->getByAddr(WebFingerUri::fromString($contact['addr']))->pubKey;
+ } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
+
}
} else {
// The "addr" field should always be filled.
// If this isn't the case, it will raise a notice some lines later.
- // And in the log we will see where it came from and we can handle it there.
+ // And in the log we will see where it came from, and we can handle it there.
Logger::notice('Empty addr', ['contact' => $contact ?? [], 'callstack' => System::callstack(20)]);
}
$owner = User::getOwnerDataById($item['uid']);
}
- $author = self::myHandle($owner);
+ $author_handle = self::myHandle($owner);
$message = [
- 'author' => $author,
+ 'author' => $author_handle,
'guid' => System::createUUID(),
'parent_type' => 'Post',
'parent_guid' => $item['guid']
];
- Logger::info('Send participation for ' . $item['guid'] . ' by ' . $author);
+ Logger::info('Send participation for ' . $item['guid'] . ' by ' . $author_handle);
// It doesn't matter what we store, we only want to avoid sending repeated notifications for the same item
DI::cache()->set($cachekey, $item['guid'], Duration::QUARTER_HOUR);
*
* @param integer $parent_id
* @return boolean
+ * @throws InternalServerErrorException
+ * @throws \ImagickException
*/
private static function parentSupportDiaspora(int $parent_id): bool
{
return false;
}
- if (empty(FContact::getByURL($parent_post['author-link'], false))) {
+ if (!self::isSupportedByContactUrl($parent_post['author-link'], false)) {
Logger::info('Parent author is no Diaspora contact.', ['parent-id' => $parent_id]);
return false;
}
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2022, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Protocol\Diaspora\Entity;
+
+use Psr\Http\Message\UriInterface;
+
+/**
+ * @property-read $uriId
+ * @property-read $url
+ * @property-read $guid
+ * @property-read $addr
+ * @property-read $alias
+ * @property-read $nick
+ * @property-read $name
+ * @property-read $givenName
+ * @property-read $familyName
+ * @property-read $photo
+ * @property-read $photoMedium
+ * @property-read $photoSmall
+ * @property-read $batch
+ * @property-read $notify
+ * @property-read $poll
+ * @property-read $subscribe
+ * @property-read $searchable
+ * @property-read $pubKey
+ * @property-read $baseurl
+ * @property-read $gsid
+ * @property-read $created
+ * @property-read $updated
+ * @property-read $interacting_count
+ * @property-read $interacted_count
+ * @property-read $post_count
+ */
+class DiasporaContact extends \Friendica\BaseEntity
+{
+ /** @var int */
+ protected $uriId;
+ /** @var UriInterface */
+ protected $url;
+ /** @var string */
+ protected $guid;
+ /** @var string */
+ protected $addr;
+ /** @var UriInterface */
+ protected $alias;
+ /** @var string */
+ protected $nick;
+ /** @var string */
+ protected $name;
+ /** @var string */
+ protected $givenName;
+ /** @var string */
+ protected $familyName;
+ /** @var UriInterface */
+ protected $photo;
+ /** @var UriInterface */
+ protected $photoMedium;
+ /** @var UriInterface */
+ protected $photoSmall;
+ /** @var UriInterface */
+ protected $batch;
+ /** @var UriInterface */
+ protected $notify;
+ /** @var UriInterface */
+ protected $poll;
+ /** @var UriInterface */
+ protected $subscribe;
+ /** @var bool */
+ protected $searchable;
+ /** @var string */
+ protected $pubKey;
+ /** @var UriInterface */
+ protected $baseurl;
+ /** @var int */
+ protected $gsid;
+ /** @var \DateTime */
+ protected $created;
+ /** @var \DateTime */
+ protected $updated;
+ /** @var int */
+ protected $interacting_count;
+ /** @var int */
+ protected $interacted_count;
+ /** @var int */
+ protected $post_count;
+
+ public function __construct(
+ UriInterface $url, \DateTime $created, string $guid = null, string $addr = null, UriInterface $alias = null,
+ string $nick = null, string $name = null, string $givenName = null, string $familyName = null,
+ UriInterface $photo = null, UriInterface $photoMedium = null, UriInterface $photoSmall = null,
+ UriInterface $batch = null, UriInterface $notify = null, UriInterface $poll = null, UriInterface $subscribe = null,
+ bool $searchable = null, string $pubKey = null, UriInterface $baseurl = null, int $gsid = null,
+ \DateTime $updated = null, int $interacting_count = 0, int $interacted_count = 0, int $post_count = 0, int $uriId = null
+ ) {
+ $this->uriId = $uriId;
+ $this->url = $url;
+ $this->guid = $guid;
+ $this->addr = $addr;
+ $this->alias = $alias;
+ $this->nick = $nick;
+ $this->name = $name;
+ $this->givenName = $givenName;
+ $this->familyName = $familyName;
+ $this->photo = $photo;
+ $this->photoMedium = $photoMedium;
+ $this->photoSmall = $photoSmall;
+ $this->batch = $batch;
+ $this->notify = $notify;
+ $this->poll = $poll;
+ $this->subscribe = $subscribe;
+ $this->searchable = $searchable;
+ $this->pubKey = $pubKey;
+ $this->baseurl = $baseurl;
+ $this->gsid = $gsid;
+ $this->created = $created;
+ $this->updated = $updated;
+ $this->interacting_count = $interacting_count;
+ $this->interacted_count = $interacted_count;
+ $this->post_count = $post_count;
+ }
+}
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2022, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Protocol\Diaspora\Factory;
+
+use Friendica\Capabilities\ICanCreateFromTableRow;
+use Friendica\Database\DBA;
+use GuzzleHttp\Psr7\Uri;
+
+class DiasporaContact extends \Friendica\BaseFactory implements ICanCreateFromTableRow
+{
+ public function createFromTableRow(array $row): \Friendica\Protocol\Diaspora\Entity\DiasporaContact
+ {
+ return new \Friendica\Protocol\Diaspora\Entity\DiasporaContact(
+ new Uri($row['url']),
+ new \DateTime($row['created'], new \DateTimeZone('UTC')),
+ $row['guid'],
+ $row['addr'],
+ $row['alias'] ? new Uri($row['alias']) : null,
+ $row['nick'],
+ $row['name'],
+ $row['given-name'],
+ $row['family-name'],
+ $row['photo'] ? new Uri($row['photo']) : null,
+ $row['photo-medium'] ? new Uri($row['photo-medium']) : null,
+ $row['photo-small'] ? new Uri($row['photo-small']) : null,
+ $row['batch'] ? new Uri($row['batch']) : null,
+ $row['notify'] ? new Uri($row['notify']) : null,
+ $row['poll'] ? new Uri($row['poll']) : null,
+ $row['subscribe'] ? new Uri($row['subscribe']) : null,
+ $row['searchable'],
+ $row['pubkey'],
+ $row['baseurl'] ? new Uri($row['baseurl']) : null,
+ $row['gsid'],
+ $row['updated'] !== DBA::NULL_DATETIME ? new \DateTime($row['updated'], new \DateTimeZone('UTC')) : null,
+ $row['interacting_count'],
+ $row['interacted_count'],
+ $row['post_count'],
+ $row['uri-id'],
+ );
+ }
+
+ /**
+ * @param array $data Data returned by \Friendica\Network\Probe::uri()
+ * @param int $uriId The URI ID of the Diaspora contact URL + GUID
+ * @param \DateTime $created
+ * @param int $interacting_count
+ * @param int $interacted_count
+ * @param int $post_count
+ * @return \Friendica\Protocol\Diaspora\Entity\DiasporaContact
+ */
+ public function createfromProbeData(array $data, int $uriId, \DateTime $created, int $interacting_count = 0, int $interacted_count = 0, int $post_count = 0): \Friendica\Protocol\Diaspora\Entity\DiasporaContact
+ {
+ $alias = $data['alias'] != $data['url'] ? $data['alias'] : null;
+
+ return new \Friendica\Protocol\Diaspora\Entity\DiasporaContact(
+ new Uri($data['url']),
+ $created,
+ $data['guid'],
+ $data['addr'],
+ $alias ? new Uri($alias) : null,
+ $data['nick'],
+ $data['name'],
+ $data['given-name'] ?? '',
+ $data['family-name'] ?? '',
+ $data['photo'] ? new Uri($data['photo']) : null,
+ !empty($data['photo_medium']) ? new Uri($data['photo_medium']) : null,
+ !empty($data['photo_small']) ? new Uri($data['photo_small']) : null,
+ $data['batch'] ? new Uri($data['batch']) : null,
+ $data['notify'] ? new Uri($data['notify']) : null,
+ $data['poll'] ? new Uri($data['poll']) : null,
+ $data['subscribe'] ? new Uri($data['subscribe']) : null,
+ !$data['hide'],
+ $data['pubkey'],
+ $data['baseurl'] ? new Uri($data['baseurl']) : null,
+ $data['gsid'],
+ null,
+ $interacting_count,
+ $interacted_count,
+ $post_count,
+ $uriId,
+ );
+ }
+}
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2022, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Protocol\Diaspora\Repository;
+
+use Friendica\BaseRepository;
+use Friendica\Core\System;
+use Friendica\Database\Database;
+use Friendica\Database\Definition\DbaDefinition;
+use Friendica\Model\APContact;
+use Friendica\Model\Contact;
+use Friendica\Model\Item;
+use Friendica\Model\ItemURI;
+use Friendica\Network\HTTPException;
+use Friendica\Protocol\Diaspora\Entity;
+use Friendica\Protocol\Diaspora\Factory;
+use Friendica\Protocol\WebFingerUri;
+use Friendica\Util\DateTimeFormat;
+use Psr\Http\Message\UriInterface;
+use Psr\Log\LoggerInterface;
+
+class DiasporaContact extends BaseRepository
+{
+ const ALWAYS_UPDATE = true;
+ const NEVER_UPDATE = false;
+ const UPDATE_IF_MISSING_OR_OUTDATED = null;
+
+ protected static $table_name = 'diaspora-contact-view';
+
+ /** @var Factory\DiasporaContact */
+ protected $factory;
+ /** @var DbaDefinition */
+ private $definition;
+
+ public function __construct(DbaDefinition $definition, Database $database, LoggerInterface $logger, Factory\DiasporaContact $factory)
+ {
+ parent::__construct($database, $logger, $factory);
+
+ $this->definition = $definition;
+ }
+
+ /**
+ * @param array $condition
+ * @param array $params
+ * @return Entity\DiasporaContact
+ * @throws HTTPException\NotFoundException
+ */
+ public function selectOne(array $condition, array $params = []): Entity\DiasporaContact
+ {
+ return parent::_selectOne($condition, $params);
+ }
+
+ /**
+ * @param int $uriId
+ * @return Entity\DiasporaContact
+ * @throws HTTPException\NotFoundException
+ */
+ public function selectOneByUriId(int $uriId): Entity\DiasporaContact
+ {
+ return $this->selectOne(['uri-id' => $uriId]);
+ }
+
+ /**
+ * @param UriInterface $uri
+ * @return Entity\DiasporaContact
+ * @throws HTTPException\NotFoundException
+ */
+ public function selectOneByUri(UriInterface $uri): Entity\DiasporaContact
+ {
+ try {
+ return $this->selectOne(['url' => (string) $uri]);
+ } catch (HTTPException\NotFoundException $e) {
+ }
+
+ try {
+ return $this->selectOne(['addr' => (string) $uri]);
+ } catch (HTTPException\NotFoundException $e) {
+ }
+
+ return $this->selectOne(['alias' => (string) $uri]);
+ }
+
+ /**
+ * @param WebFingerUri $uri
+ * @return Entity\DiasporaContact
+ * @throws HTTPException\NotFoundException
+ */
+ public function selectOneByAddr(WebFingerUri $uri): Entity\DiasporaContact
+ {
+ return $this->selectOne(['addr' => $uri->getAddr()]);
+ }
+
+ /**
+ * @param int $uriId
+ * @return bool
+ * @throws \Exception
+ */
+ public function existsByUriId(int $uriId): bool
+ {
+ return $this->db->exists(self::$table_name, ['uri-id' => $uriId]);
+ }
+
+ public function save(Entity\DiasporaContact $DiasporaContact): Entity\DiasporaContact
+ {
+ $uriId = $DiasporaContact->uriId ?? ItemURI::insert(['uri' => $DiasporaContact->url, 'guid' => $DiasporaContact->guid]);
+
+ $fields = [
+ 'uri-id' => $uriId,
+ 'addr' => $DiasporaContact->addr,
+ 'alias' => (string)$DiasporaContact->alias,
+ 'nick' => $DiasporaContact->nick,
+ 'name' => $DiasporaContact->name,
+ 'given-name' => $DiasporaContact->givenName,
+ 'family-name' => $DiasporaContact->familyName,
+ 'photo' => (string)$DiasporaContact->photo,
+ 'photo-medium' => (string)$DiasporaContact->photoMedium,
+ 'photo-small' => (string)$DiasporaContact->photoSmall,
+ 'batch' => (string)$DiasporaContact->batch,
+ 'notify' => (string)$DiasporaContact->notify,
+ 'poll' => (string)$DiasporaContact->poll,
+ 'subscribe' => (string)$DiasporaContact->subscribe,
+ 'searchable' => $DiasporaContact->searchable,
+ 'pubkey' => $DiasporaContact->pubKey,
+ 'gsid' => $DiasporaContact->gsid,
+ 'created' => $DiasporaContact->created->format(DateTimeFormat::MYSQL),
+ 'updated' => DateTimeFormat::utcNow(),
+ 'interacting_count' => $DiasporaContact->interacting_count,
+ 'interacted_count' => $DiasporaContact->interacted_count,
+ 'post_count' => $DiasporaContact->post_count,
+ ];
+
+ // Limit the length on incoming fields
+ $fields = $this->definition->truncateFieldsForTable('diaspora-contact', $fields);
+
+ $this->db->insert('diaspora-contact', $fields, Database::INSERT_UPDATE);
+
+ return $this->selectOneByUriId($uriId);
+ }
+
+ /**
+ * Fetch a Diaspora profile from a given WebFinger address and updates it depending on the mode
+ *
+ * @param WebFingerUri $uri Profile address
+ * @param boolean $update true = always update, false = never update, null = update when not found or outdated
+ * @return Entity\DiasporaContact
+ * @throws HTTPException\NotFoundException
+ */
+ public function getByAddr(WebFingerUri $uri, ?bool $update = self::UPDATE_IF_MISSING_OR_OUTDATED): Entity\DiasporaContact
+ {
+ if ($update !== self::ALWAYS_UPDATE) {
+ try {
+ $dcontact = $this->selectOneByAddr($uri);
+ if ($update === self::NEVER_UPDATE) {
+ return $dcontact;
+ }
+ } catch (HTTPException\NotFoundException $e) {
+ if ($update === self::NEVER_UPDATE) {
+ throw $e;
+ }
+
+ // This is necessary for Contact::getByURL in case the base contact record doesn't need probing,
+ // but we still need the result of a probe to create the missing diaspora-contact record.
+ $update = self::ALWAYS_UPDATE;
+ }
+ }
+
+ $contact = Contact::getByURL($uri, $update, ['uri-id']);
+ if (empty($contact['uri-id'])) {
+ throw new HTTPException\NotFoundException('Diaspora profile with URI ' . $uri . ' not found');
+ }
+
+ return self::selectOneByUriId($contact['uri-id']);
+ }
+
+ /**
+ * Fetch a Diaspora profile from a given profile URL and updates it depending on the mode
+ *
+ * @param UriInterface $uri Profile URL
+ * @param boolean $update true = always update, false = never update, null = update when not found or outdated
+ * @return Entity\DiasporaContact
+ * @throws HTTPException\NotFoundException
+ */
+ public function getByUrl(UriInterface $uri, ?bool $update = self::UPDATE_IF_MISSING_OR_OUTDATED): Entity\DiasporaContact
+ {
+ if ($update !== self::ALWAYS_UPDATE) {
+ try {
+ $dcontact = $this->selectOneByUriId(ItemURI::getIdByURI($uri));
+ if ($update === self::NEVER_UPDATE) {
+ return $dcontact;
+ }
+ } catch (HTTPException\NotFoundException $e) {
+ if ($update === self::NEVER_UPDATE) {
+ throw $e;
+ }
+
+ // This is necessary for Contact::getByURL in case the base contact record doesn't need probing,
+ // but we still need the result of a probe to create the missing diaspora-contact record.
+ $update = self::ALWAYS_UPDATE;
+ }
+ }
+
+ $contact = Contact::getByURL($uri, $update, ['uri-id']);
+ if (empty($contact['uri-id'])) {
+ throw new HTTPException\NotFoundException('Diaspora profile with URI ' . $uri . ' not found');
+ }
+
+ return self::selectOneByUriId($contact['uri-id']);
+ }
+
+ /**
+ * Update or create a diaspora-contact entry via a probe array
+ *
+ * @param array $data Probe array
+ * @return Entity\DiasporaContact
+ * @throws \Exception
+ */
+ public function updateFromProbeArray(array $data): Entity\DiasporaContact
+ {
+ $uriId = ItemURI::insert(['uri' => $data['url'], 'guid' => $data['guid']]);
+
+ $contact = Contact::getByUriId($uriId, ['id', 'created']);
+ $apcontact = APContact::getByURL($data['url'], false);
+ if (!empty($apcontact)) {
+ $interacting_count = $apcontact['followers_count'];
+ $interacted_count = $apcontact['following_count'];
+ $post_count = $apcontact['statuses_count'];
+ } elseif (!empty($contact['id'])) {
+ $last_interaction = DateTimeFormat::utc('now - 180 days');
+
+ $interacting_count = $this->db->count('contact-relation', ["`relation-cid` = ? AND NOT `follows` AND `last-interaction` > ?", $contact['id'], $last_interaction]);
+ $interacted_count = $this->db->count('contact-relation', ["`cid` = ? AND NOT `follows` AND `last-interaction` > ?", $contact['id'], $last_interaction]);
+ $post_count = $this->db->count('post', ['author-id' => $contact['id'], 'gravity' => [Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT]]);
+ }
+
+ $DiasporaContact = $this->factory->createfromProbeData(
+ $data,
+ $uriId,
+ new \DateTime($contact['created'] ?? 'now', new \DateTimeZone('UTC')),
+ $interacting_count ?? 0,
+ $interacted_count ?? 0,
+ $post_count ?? 0
+ );
+
+ $DiasporaContact = $this->save($DiasporaContact);
+
+ $this->logger->info('Updated diaspora-contact', ['url' => (string) $DiasporaContact->url, 'callstack' => System::callstack(20)]);
+
+ return $DiasporaContact;
+ }
+
+ /**
+ * get a url (scheme://domain.tld/u/user) from a given contact guid
+ *
+ * @param mixed $guid Hexadecimal string guid
+ *
+ * @return string the contact url or null
+ * @throws \Exception
+ */
+ public function getUrlByGuid(string $guid): ?string
+ {
+ $diasporaContact = $this->db->selectFirst(self::$table_name, ['url'], ['guid' => $guid]);
+
+ return $diasporaContact['url'] ?? null;
+ }
+}
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2022, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Protocol;
+
+use GuzzleHttp\Psr7\Uri;
+
+class WebFingerUri
+{
+ /**
+ * @var string
+ */
+ private $user;
+ /**
+ * @var string
+ */
+ private $host;
+ /**
+ * @var int|null
+ */
+ private $port;
+ /**
+ * @var string|null
+ */
+ private $path;
+
+ private function __construct(string $user, string $host, int $port = null, string $path = null)
+ {
+ $this->user = $user;
+ $this->host = $host;
+ $this->port = $port;
+ $this->path = $path;
+
+ $this->validate();
+ }
+
+ /**
+ * @param string $addr
+ * @return WebFingerUri
+ */
+ public static function fromString(string $addr): WebFingerUri
+ {
+ $uri = new Uri('acct://' . preg_replace('/^acct:/', '', $addr));
+
+ return new self($uri->getUserInfo(), $uri->getHost(), $uri->getPort(), $uri->getPath());
+ }
+
+ private function validate()
+ {
+ if (!$this->user) {
+ throw new \InvalidArgumentException('WebFinger URI User part is required');
+ }
+
+ if (!$this->host) {
+ throw new \InvalidArgumentException('WebFinger URI Host part is required');
+ }
+ }
+
+ public function getUser(): string
+ {
+ return $this->user;
+ }
+
+ public function getHost(): string
+ {
+ return $this->host;
+ }
+
+ public function getFullHost(): string
+ {
+ return $this->host
+ . ($this->port ? ':' . $this->port : '') .
+ ($this->path ?: '');
+ }
+
+ public function getLongForm(): string
+ {
+ return 'acct:' . $this->getShortForm();
+ }
+
+ public function getShortForm(): string
+ {
+ return $this->user . '@' . $this->getFullHost();
+ }
+
+ public function getAddr(): string
+ {
+ return $this->getShortForm();
+ }
+
+ public function __toString(): string
+ {
+ return $this->getShortForm();
+ }
+}
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\Contact;
-use Friendica\Model\FContact;
use Friendica\Model\GServer;
use Friendica\Model\Item;
use Friendica\Model\Post;
if ($item['verb'] == Activity::ANNOUNCE) {
continue;
}
-
+
if ($item['id'] == $parent_id) {
$parent = $item;
}
private static function deliverDFRN(string $cmd, array $contact, array $owner, array $items, array $target_item, bool $public_message, bool $top_level, bool $followup, int $server_protocol = null)
{
// Transmit Diaspora reshares via Diaspora if the Friendica contact support Diaspora
- if (Diaspora::getReshareDetails($target_item ?? []) && !empty(FContact::getByURL($contact['addr'], false))) {
+ if (Diaspora::getReshareDetails($target_item ?? []) && Diaspora::isSupportedByContactUrl($contact['addr'], false)) {
Logger::info('Reshare will be transmitted via Diaspora', ['url' => $contact['url'], 'guid' => ($target_item['guid'] ?? '') ?: $target_item['id']]);
self::deliverDiaspora($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup);
return;
AND NOT EXISTS(SELECT `uri-id` FROM `user-contact` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `contact` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `apcontact` WHERE `uri-id` = `item-uri`.`id`)
- AND NOT EXISTS(SELECT `uri-id` FROM `fcontact` WHERE `uri-id` = `item-uri`.`id`)
+ AND NOT EXISTS(SELECT `uri-id` FROM `diaspora-contact` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `inbox-status` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `post-delivery` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `post-delivery` WHERE `inbox-id` = `item-uri`.`id`)
+++ /dev/null
-<?php
-/**
- * @copyright Copyright (C) 2010-2022, the Friendica project
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Worker;
-
-use Friendica\Core\Logger;
-use Friendica\Model\FContact;
-
-class UpdateFContact
-{
- /**
- * Update fcontact data via probe
- *
- * @param string $handle Contact handle
- * @return void
- */
- public static function execute(string $handle)
- {
- $success = FContact::getByURL($handle, true);
-
- Logger::info('Updated from probe', ['handle' => $handle, 'success' => $success]);
- }
-}
use Friendica\Database\DBA;
if (!defined('DB_UPDATE_VERSION')) {
- define('DB_UPDATE_VERSION', 1497);
+ define('DB_UPDATE_VERSION', 1500);
}
return [
"wid" => ["wid"],
]
],
+ "diaspora-contact" => [
+ "comment" => "Diaspora compatible contacts - used in the Diaspora implementation",
+ "fields" => [
+ "uri-id" => ["type" => "int unsigned", "not null" => "1", "primary" => "1", "foreign" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the contact URL"],
+ "addr" => ["type" => "varchar(255)", "comment" => ""],
+ "alias" => ["type" => "varchar(255)", "comment" => ""],
+ "nick" => ["type" => "varchar(255)", "comment" => ""],
+ "name" => ["type" => "varchar(255)", "comment" => ""],
+ "given-name" => ["type" => "varchar(255)", "comment" => ""],
+ "family-name" => ["type" => "varchar(255)", "comment" => ""],
+ "photo" => ["type" => "varchar(255)", "comment" => ""],
+ "photo-medium" => ["type" => "varchar(255)", "comment" => ""],
+ "photo-small" => ["type" => "varchar(255)", "comment" => ""],
+ "batch" => ["type" => "varchar(255)", "comment" => ""],
+ "notify" => ["type" => "varchar(255)", "comment" => ""],
+ "poll" => ["type" => "varchar(255)", "comment" => ""],
+ "subscribe" => ["type" => "varchar(255)", "comment" => ""],
+ "searchable" => ["type" => "boolean", "comment" => ""],
+ "pubkey" => ["type" => "text", "comment" => ""],
+ "gsid" => ["type" => "int unsigned", "foreign" => ["gserver" => "id", "on delete" => "restrict"], "comment" => "Global Server ID"],
+ "created" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
+ "updated" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
+ "interacting_count" => ["type" => "int unsigned", "default" => 0, "comment" => "Number of contacts this contact interactes with"],
+ "interacted_count" => ["type" => "int unsigned", "default" => 0, "comment" => "Number of contacts that interacted with this contact"],
+ "post_count" => ["type" => "int unsigned", "default" => 0, "comment" => "Number of posts and comments"],
+ ],
+ "indexes" => [
+ "PRIMARY" => ["uri-id"],
+ "addr" => ["UNIQUE", "addr"],
+ "alias" => ["alias"],
+ "gsid" => ["gsid"],
+ ]
+ ],
"diaspora-interaction" => [
"comment" => "Signed Diaspora Interaction",
"fields" => [
"uri-id" => ["uri-id"],
]
],
- "fcontact" => [
- "comment" => "Diaspora compatible contacts - used in the Diaspora implementation",
- "fields" => [
- "id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "sequential ID"],
- "guid" => ["type" => "varbinary(255)", "not null" => "1", "default" => "", "comment" => "unique id"],
- "url" => ["type" => "varbinary(383)", "not null" => "1", "default" => "", "comment" => ""],
- "uri-id" => ["type" => "int unsigned", "foreign" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the fcontact url"],
- "name" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
- "photo" => ["type" => "varbinary(383)", "not null" => "1", "default" => "", "comment" => ""],
- "request" => ["type" => "varbinary(383)", "not null" => "1", "default" => "", "comment" => ""],
- "nick" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
- "addr" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
- "batch" => ["type" => "varbinary(383)", "not null" => "1", "default" => "", "comment" => ""],
- "notify" => ["type" => "varbinary(383)", "not null" => "1", "default" => "", "comment" => ""],
- "poll" => ["type" => "varbinary(383)", "not null" => "1", "default" => "", "comment" => ""],
- "confirm" => ["type" => "varbinary(383)", "not null" => "1", "default" => "", "comment" => ""],
- "priority" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => ""],
- "network" => ["type" => "char(4)", "not null" => "1", "default" => "", "comment" => ""],
- "alias" => ["type" => "varbinary(383)", "not null" => "1", "default" => "", "comment" => ""],
- "pubkey" => ["type" => "text", "comment" => ""],
- "created" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
- "updated" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
- "interacting_count" => ["type" => "int unsigned", "default" => 0, "comment" => "Number of contacts this contact interactes with"],
- "interacted_count" => ["type" => "int unsigned", "default" => 0, "comment" => "Number of contacts that interacted with this contact"],
- "post_count" => ["type" => "int unsigned", "default" => 0, "comment" => "Number of posts and comments"],
- ],
- "indexes" => [
- "PRIMARY" => ["id"],
- "addr" => ["addr(32)"],
- "url" => ["UNIQUE", "url(190)"],
- "uri-id" => ["UNIQUE", "uri-id"],
- ]
- ],
"fetch-entry" => [
"comment" => "",
"fields" => [
"fields" => [
"id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "sequential ID"],
"uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "foreign" => ["user" => "uid"], "comment" => "User id"],
- "fid" => ["type" => "int unsigned", "relation" => ["fcontact" => "id"], "comment" => "deprecated"],
+ "fid" => ["type" => "int unsigned", "comment" => "deprecated"],
"contact-id" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "foreign" => ["contact" => "id"], "comment" => ""],
"suggest-cid" => ["type" => "int unsigned", "foreign" => ["contact" => "id"], "comment" => "Suggested contact"],
"knowyou" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
"author-hidden" => ["author", "hidden"],
"author-updated" => ["author", "updated"],
"author-gsid" => ["author", "gsid"],
- "author-uri-id" => ["author", "uri-id"],
"owner-id" => ["post-user", "owner-id"],
"owner-uri-id" => ["owner", "uri-id"],
"owner-link" => ["owner", "url"],
"author-hidden" => ["author", "hidden"],
"author-updated" => ["author", "updated"],
"author-gsid" => ["author", "gsid"],
- "author-uri-id" => ["author", "uri-id"],
"owner-id" => ["post-thread-user", "owner-id"],
"owner-uri-id" => ["owner", "uri-id"],
"owner-link" => ["owner", "url"],
"author-hidden" => ["author", "hidden"],
"author-updated" => ["author", "updated"],
"author-gsid" => ["author", "gsid"],
- "author-uri-id" => ["author", "uri-id"],
"owner-id" => ["post", "owner-id"],
"owner-uri-id" => ["owner", "uri-id"],
"owner-link" => ["owner", "url"],
"author-hidden" => ["author", "hidden"],
"author-updated" => ["author", "updated"],
"author-gsid" => ["author", "gsid"],
- "author-uri-id" => ["author", "uri-id"],
"owner-id" => ["post-thread", "owner-id"],
"owner-uri-id" => ["owner", "uri-id"],
"owner-link" => ["owner", "url"],
"blocked" => ["contact", "blocked"],
"dfrn-notify" => ["contact", "notify"],
"dfrn-poll" => ["contact", "poll"],
- "diaspora-guid" => ["fcontact", "guid"],
- "diaspora-batch" => ["fcontact", "batch"],
- "diaspora-notify" => ["fcontact", "notify"],
- "diaspora-poll" => ["fcontact", "poll"],
- "diaspora-alias" => ["fcontact", "alias"],
+ "diaspora-guid" => ["item-uri", "guid"],
+ "diaspora-batch" => ["diaspora-contact", "batch"],
+ "diaspora-notify" => ["diaspora-contact", "notify"],
+ "diaspora-poll" => ["diaspora-contact", "poll"],
+ "diaspora-alias" => ["diaspora-contact", "alias"],
"ap-uuid" => ["apcontact", "uuid"],
"ap-type" => ["apcontact", "type"],
"ap-following" => ["apcontact", "following"],
"query" => "FROM `contact`
LEFT JOIN `item-uri` ON `item-uri`.`id` = `contact`.`uri-id`
LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `contact`.`uri-id`
- LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = contact.`uri-id`
+ LEFT JOIN `diaspora-contact` ON `diaspora-contact`.`uri-id` = contact.`uri-id`
LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid`
- WHERE `contact`.`uid` = 0"
+ WHERE `contact`.`uid` = 0"
],
"account-user-view" => [
"fields" => [
"reason" => ["ucontact", "reason"],
"dfrn-notify" => ["contact", "notify"],
"dfrn-poll" => ["contact", "poll"],
- "diaspora-guid" => ["fcontact", "guid"],
- "diaspora-batch" => ["fcontact", "batch"],
- "diaspora-notify" => ["fcontact", "notify"],
- "diaspora-poll" => ["fcontact", "poll"],
- "diaspora-alias" => ["fcontact", "alias"],
- "diaspora-interacting_count" => ["fcontact", "interacting_count"],
- "diaspora-interacted_count" => ["fcontact", "interacted_count"],
- "diaspora-post_count" => ["fcontact", "post_count"],
+ "diaspora-guid" => ["item-uri", "guid"],
+ "diaspora-batch" => ["diaspora-contact", "batch"],
+ "diaspora-notify" => ["diaspora-contact", "notify"],
+ "diaspora-poll" => ["diaspora-contact", "poll"],
+ "diaspora-alias" => ["diaspora-contact", "alias"],
+ "diaspora-interacting_count" => ["diaspora-contact", "interacting_count"],
+ "diaspora-interacted_count" => ["diaspora-contact", "interacted_count"],
+ "diaspora-post_count" => ["diaspora-contact", "post_count"],
"ap-uuid" => ["apcontact", "uuid"],
"ap-type" => ["apcontact", "type"],
"ap-following" => ["apcontact", "following"],
INNER JOIN `contact` ON `contact`.`uri-id` = `ucontact`.`uri-id` AND `contact`.`uid` = 0
LEFT JOIN `item-uri` ON `item-uri`.`id` = `ucontact`.`uri-id`
LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `ucontact`.`uri-id`
- LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = `ucontact`.`uri-id` AND `fcontact`.`network` = 'dspr'
+ LEFT JOIN `diaspora-contact` ON `diaspora-contact`.`uri-id` = `ucontact`.`uri-id`
LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid`"
],
"pending-view" => [
"query" => "FROM `profile_field`
INNER JOIN `permissionset` ON `permissionset`.`id` = `profile_field`.`psid`"
],
+ "diaspora-contact-view" => [
+ "fields" => [
+ "uri-id" => ["diaspora-contact", "uri-id"],
+ "url" => ["item-uri", "uri"],
+ "guid" => ["item-uri", "guid"],
+ "addr" => ["diaspora-contact", "addr"],
+ "alias" => ["diaspora-contact", "alias"],
+ "nick" => ["diaspora-contact", "nick"],
+ "name" => ["diaspora-contact", "name"],
+ "given-name" => ["diaspora-contact", "given-name"],
+ "family-name" => ["diaspora-contact", "family-name"],
+ "photo" => ["diaspora-contact", "photo"],
+ "photo-medium" => ["diaspora-contact", "photo-medium"],
+ "photo-small" => ["diaspora-contact", "photo-small"],
+ "batch" => ["diaspora-contact", "batch"],
+ "notify" => ["diaspora-contact", "notify"],
+ "poll" => ["diaspora-contact", "poll"],
+ "subscribe" => ["diaspora-contact", "subscribe"],
+ "searchable" => ["diaspora-contact", "searchable"],
+ "pubkey" => ["diaspora-contact", "pubkey"],
+ "baseurl" => ["gserver", "url"],
+ "gsid" => ["diaspora-contact", "gsid"],
+ "created" => ["diaspora-contact", "created"],
+ "updated" => ["diaspora-contact", "updated"],
+ "interacting_count" => ["diaspora-contact", "interacting_count"],
+ "interacted_count" => ["diaspora-contact", "interacted_count"],
+ "post_count" => ["diaspora-contact", "post_count"],
+ ],
+ "query" => "FROM `diaspora-contact`
+ INNER JOIN `item-uri` ON `item-uri`.`id` = `diaspora-contact`.`uri-id`
+ LEFT JOIN `gserver` ON `gserver`.`id` = `diaspora-contact`.`gsid`"
+ ],
];
-
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2022, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Main database structure configuration file.
+ *
+ * Here are described all the tables, fields and indexes Friendica needs to work.
+ * The entry order is mostly alphabetic - with the exception of tables that are used in foreign keys.
+ *
+ * Syntax (braces indicate optionale values):
+ * "<table name>" => [
+ * "comment" => "Description of the table",
+ * "fields" => [
+ * "<field name>" => [
+ * "type" => "<field type>{(<field size>)} <unsigned>",
+ * "not null" => 0|1,
+ * {"extra" => "auto_increment",}
+ * {"default" => "<default value>",}
+ * {"default" => NULL_DATE,} (for datetime fields)
+ * {"primary" => "1",}
+ * {"foreign|relation" => ["<foreign key table name>" => "<foreign key field name>"],}
+ * "comment" => "Description of the fields"
+ * ],
+ * ...
+ * ],
+ * "indexes" => [
+ * "PRIMARY" => ["<primary key field name>", ...],
+ * "<index name>" => [{"UNIQUE",} "<field name>{(<key size>)}", ...]
+ * ...
+ * ],
+ * ],
+ *
+ * Whenever possible prefer "foreign" before "relation" with the foreign keys.
+ * "foreign" adds true foreign keys on the database level, while "relation" is just an indicator of a table relation without any consequences
+ *
+ * If you need to make any change, make sure to increment the DB_UPDATE_VERSION constant value below.
+ *
+ */
+
+namespace Friendica\Test\src\Protocol;
+
+use Friendica\Protocol\WebFingerUri;
+use PHPUnit\Framework\TestCase;
+
+class WebFingerUriTest extends TestCase
+{
+ public function dataFromString(): array
+ {
+ return [
+ 'long' => [
+ 'expectedLong' => 'acct:selma@www.example.com:8080/friend',
+ 'expectedShort' => 'selma@www.example.com:8080/friend',
+ 'input' => 'acct:selma@www.example.com:8080/friend',
+ ],
+ 'short' => [
+ 'expectedLong' => 'acct:selma@www.example.com:8080/friend',
+ 'expectedShort' => 'selma@www.example.com:8080/friend',
+ 'input' => 'selma@www.example.com:8080/friend',
+ ],
+ 'minimal' => [
+ 'expectedLong' => 'acct:bob@example.com',
+ 'expectedShort' => 'bob@example.com',
+ 'input' => 'bob@example.com',
+ ],
+ 'acct:' => [
+ 'expectedLong' => 'acct:alice@example.acct:90',
+ 'expectedShort' => 'alice@example.acct:90',
+ 'input' => 'alice@example.acct:90',
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider dataFromString
+ * @param string $expectedLong
+ * @param string $expectedShort
+ * @param string $input
+ * @return void
+ */
+ public function testFromString(string $expectedLong, string $expectedShort, string $input)
+ {
+ $uri = WebFingerUri::fromString($input);
+
+ $this->assertEquals($expectedLong, $uri->getLongForm());
+ $this->assertEquals($expectedShort, $uri->getShortForm());
+ }
+
+ public function dataFromStringFailure()
+ {
+ return [
+ 'missing user' => [
+ 'input' => 'example.com',
+ ],
+ 'missing user @' => [
+ 'input' => '@example.com',
+ ],
+ 'missing host' => [
+ 'input' => 'alice',
+ ],
+ 'missing host @' => [
+ 'input' => 'alice@',
+ ],
+ 'missing everything' => [
+ 'input' => '',
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider dataFromStringFailure
+ * @param string $input
+ * @return void
+ */
+ public function testFromStringFailure(string $input)
+ {
+ $this->expectException(\InvalidArgumentException::class);
+
+ WebFingerUri::fromString($input);
+ }
+}
return Update::FAILED;
}
- if (!DBA::e("UPDATE `fcontact` SET `uri-id` = null WHERE NOT `uri-id` IS NULL")) {
+ if (DBStructure::existsTable('fcontact') && !DBA::e("UPDATE `fcontact` SET `uri-id` = null WHERE NOT `uri-id` IS NULL")) {
return Update::FAILED;
}
function update_1439()
{
+ if (!DBStructure::existsTable('fcontact')) {
+ return Update::SUCCESS;
+ }
+
$intros = DBA::select('intro', ['id', 'fid'], ["NOT `fid` IS NULL AND `fid` != ?", 0]);
while ($intro = DBA::fetch($intros)) {
$fcontact = DBA::selectFirst('fcontact', ['url'], ['id' => $intro['fid']]);
}
}
DBA::close($intros);
+
+ return Update::SUCCESS;
}
function update_1440()