diff --git a/database.sql b/database.sql index 2e7a189b3..95e96a135 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2021.09-dev (Siberian Iris) --- DB_UPDATE_VERSION 1429 +-- DB_UPDATE_VERSION 1430 -- ------------------------------------------ @@ -117,7 +117,7 @@ CREATE TABLE IF NOT EXISTS `contact` ( `self` boolean NOT NULL DEFAULT '0' COMMENT '1 if the contact is the user him/her self', `remote_self` boolean NOT NULL DEFAULT '0' COMMENT '', `rel` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'The kind of the relation between the user and the contact', - `duplex` boolean NOT NULL DEFAULT '0' COMMENT '', + `duplex` boolean NOT NULL DEFAULT '0' COMMENT 'Deprecated', `network` char(4) NOT NULL DEFAULT '' COMMENT 'Network of the contact', `protocol` char(4) NOT NULL DEFAULT '' COMMENT 'Protocol of the contact', `name` varchar(255) NOT NULL DEFAULT '' COMMENT 'Name that this contact is known by', @@ -133,9 +133,9 @@ CREATE TABLE IF NOT EXISTS `contact` ( `thumb` varchar(255) DEFAULT '' COMMENT 'Link to the profile photo (thumb size)', `micro` varchar(255) DEFAULT '' COMMENT 'Link to the profile photo (micro size)', `header` varchar(255) COMMENT 'Header picture', - `site-pubkey` text COMMENT '', - `issued-id` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `dfrn-id` varchar(255) NOT NULL DEFAULT '' COMMENT '', + `site-pubkey` text COMMENT 'Deprecated', + `issued-id` varchar(255) NOT NULL DEFAULT '' COMMENT 'Deprecated', + `dfrn-id` varchar(255) NOT NULL DEFAULT '' COMMENT 'Deprecated', `url` varchar(255) NOT NULL DEFAULT '' COMMENT '', `nurl` varchar(255) NOT NULL DEFAULT '' COMMENT '', `uri-id` int unsigned COMMENT 'Id of the item-uri table entry that contains the contact url', @@ -150,8 +150,8 @@ CREATE TABLE IF NOT EXISTS `contact` ( `confirm` varchar(255) COMMENT '', `subscribe` varchar(255) COMMENT '', `poco` varchar(255) COMMENT '', - `aes_allow` boolean NOT NULL DEFAULT '0' COMMENT '', - `ret-aes` boolean NOT NULL DEFAULT '0' COMMENT '', + `aes_allow` boolean NOT NULL DEFAULT '0' COMMENT 'Deprecated', + `ret-aes` boolean NOT NULL DEFAULT '0' COMMENT 'Deprecated', `usehub` boolean NOT NULL DEFAULT '0' COMMENT '', `subhub` boolean NOT NULL DEFAULT '0' COMMENT '', `hub-verify` varchar(255) NOT NULL DEFAULT '' COMMENT '', @@ -205,8 +205,6 @@ CREATE TABLE IF NOT EXISTS `contact` ( INDEX `nurl_uid` (`nurl`(128),`uid`), INDEX `nick_uid` (`nick`(128),`uid`), INDEX `attag_uid` (`attag`(96),`uid`), - INDEX `dfrn-id` (`dfrn-id`(64)), - INDEX `issued-id` (`issued-id`(64)), INDEX `network_uid_lastupdate` (`network`,`uid`,`last-update`), INDEX `uid_network_self_lastupdate` (`uid`,`network`,`self`,`last-update`), INDEX `uid_lastitem` (`uid`,`last-item`), @@ -462,20 +460,6 @@ CREATE TABLE IF NOT EXISTS `cache` ( INDEX `k_expires` (`k`,`expires`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Stores temporary data'; --- --- TABLE challenge --- -CREATE TABLE IF NOT EXISTS `challenge` ( - `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', - `challenge` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `dfrn-id` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `expire` int unsigned NOT NULL DEFAULT 0 COMMENT '', - `type` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `last_update` varchar(255) NOT NULL DEFAULT '' COMMENT '', - PRIMARY KEY(`id`), - INDEX `expire` (`expire`) -) DEFAULT COLLATE utf8mb4_general_ci COMMENT=''; - -- -- TABLE config -- @@ -1684,7 +1668,6 @@ CREATE VIEW `post-user-view` AS SELECT `contact`.`uri-date` AS `uri-date`, `contact`.`avatar-date` AS `avatar-date`, `contact`.`thumb` AS `thumb`, - `contact`.`dfrn-id` AS `dfrn-id`, `post-user`.`author-id` AS `author-id`, `author`.`url` AS `author-link`, `author`.`addr` AS `author-addr`, @@ -1846,7 +1829,6 @@ CREATE VIEW `post-thread-user-view` AS SELECT `contact`.`uri-date` AS `uri-date`, `contact`.`avatar-date` AS `avatar-date`, `contact`.`thumb` AS `thumb`, - `contact`.`dfrn-id` AS `dfrn-id`, `post-thread-user`.`author-id` AS `author-id`, `author`.`url` AS `author-link`, `author`.`addr` AS `author-addr`, @@ -1993,7 +1975,6 @@ CREATE VIEW `post-view` AS SELECT `author`.`uri-date` AS `uri-date`, `author`.`avatar-date` AS `avatar-date`, `author`.`thumb` AS `thumb`, - `author`.`dfrn-id` AS `dfrn-id`, `post`.`author-id` AS `author-id`, `author`.`url` AS `author-link`, `author`.`addr` AS `author-addr`, @@ -2115,7 +2096,6 @@ CREATE VIEW `post-thread-view` AS SELECT `author`.`uri-date` AS `uri-date`, `author`.`avatar-date` AS `avatar-date`, `author`.`thumb` AS `thumb`, - `author`.`dfrn-id` AS `dfrn-id`, `post-thread`.`author-id` AS `author-id`, `author`.`url` AS `author-link`, `author`.`addr` AS `author-addr`, @@ -2270,7 +2250,6 @@ CREATE VIEW `owner-view` AS SELECT `contact`.`self` AS `self`, `contact`.`remote_self` AS `remote_self`, `contact`.`rel` AS `rel`, - `contact`.`duplex` AS `duplex`, `contact`.`network` AS `network`, `contact`.`protocol` AS `protocol`, `contact`.`name` AS `name`, @@ -2286,9 +2265,6 @@ CREATE VIEW `owner-view` AS SELECT `contact`.`thumb` AS `thumb`, `contact`.`micro` AS `micro`, `contact`.`header` AS `header`, - `contact`.`site-pubkey` AS `site-pubkey`, - `contact`.`issued-id` AS `issued-id`, - `contact`.`dfrn-id` AS `dfrn-id`, `contact`.`url` AS `url`, `contact`.`nurl` AS `nurl`, `contact`.`uri-id` AS `uri-id`, @@ -2302,8 +2278,6 @@ CREATE VIEW `owner-view` AS SELECT `contact`.`poll` AS `poll`, `contact`.`confirm` AS `confirm`, `contact`.`poco` AS `poco`, - `contact`.`aes_allow` AS `aes_allow`, - `contact`.`ret-aes` AS `ret-aes`, `contact`.`usehub` AS `usehub`, `contact`.`subhub` AS `subhub`, `contact`.`hub-verify` AS `hub-verify`, @@ -2547,12 +2521,6 @@ CREATE VIEW `account-user-view` AS SELECT `ucontact`.`subhub` AS `subhub`, `ucontact`.`hub-verify` AS `hub-verify`, `ucontact`.`reason` AS `reason`, - `ucontact`.`duplex` AS `dfrn-duplex`, - `ucontact`.`ret-aes` AS `dfrn-ret-aes`, - `ucontact`.`site-pubkey` AS `dfrn-site-pubkey`, - `ucontact`.`issued-id` AS `dfrn-issued-id`, - `ucontact`.`dfrn-id` AS `dfrn-id`, - `ucontact`.`aes_allow` AS `dfrn-aes_allow`, `contact`.`request` AS `dfrn-request`, `contact`.`notify` AS `dfrn-notify`, `contact`.`poll` AS `dfrn-poll`, diff --git a/doc/API-Friendica.md b/doc/API-Friendica.md index 62a71dc6b..af47638d2 100644 --- a/doc/API-Friendica.md +++ b/doc/API-Friendica.md @@ -689,15 +689,6 @@ General description of profile data in API returns: --- -### GET api/friendica/remoteauth - -Similar as /redir, redirects to `url` after DFRN authentication. - -#### Parameters - -- `c_url`: url of remote contact to auth to -- `url`: string, url to redirect after auth - ## Deprecated endpoints - POST api/statuses/mediap diff --git a/doc/database.md b/doc/database.md index 51f4ae5c6..b2f4e4322 100644 --- a/doc/database.md +++ b/doc/database.md @@ -15,7 +15,6 @@ Database Tables | [attach](help/database/db_attach) | file attachments | | [auth_codes](help/database/db_auth_codes) | OAuth usage | | [cache](help/database/db_cache) | Stores temporary data | -| [challenge](help/database/db_challenge) | | | [clients](help/database/db_clients) | OAuth usage | | [config](help/database/db_config) | main configuration storage | | [contact](help/database/db_contact) | contact table | diff --git a/doc/database/db_challenge.md b/doc/database/db_challenge.md deleted file mode 100644 index 161838909..000000000 --- a/doc/database/db_challenge.md +++ /dev/null @@ -1,27 +0,0 @@ -Table challenge -=========== - - - -Fields ------- - -| Field | Description | Type | Null | Key | Default | Extra | -| ----------- | ------------- | ------------ | ---- | --- | ------- | -------------- | -| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment | -| challenge | | varchar(255) | NO | | | | -| dfrn-id | | varchar(255) | NO | | | | -| expire | | int unsigned | NO | | 0 | | -| type | | varchar(255) | NO | | | | -| last_update | | varchar(255) | NO | | | | - -Indexes ------------- - -| Name | Fields | -| ------- | ------ | -| PRIMARY | id | -| expire | expire | - - -Return to [database documentation](help/database) diff --git a/doc/database/db_contact.md b/doc/database/db_contact.md index 5a97c6e4d..2d8d292ac 100644 --- a/doc/database/db_contact.md +++ b/doc/database/db_contact.md @@ -15,7 +15,7 @@ Fields | self | 1 if the contact is the user him/her self | boolean | NO | | 0 | | | remote_self | | boolean | NO | | 0 | | | rel | The kind of the relation between the user and the contact | tinyint unsigned | NO | | 0 | | -| duplex | | boolean | NO | | 0 | | +| duplex | Deprecated | boolean | NO | | 0 | | | network | Network of the contact | char(4) | NO | | | | | protocol | Protocol of the contact | char(4) | NO | | | | | name | Name that this contact is known by | varchar(255) | NO | | | | @@ -31,9 +31,9 @@ Fields | thumb | Link to the profile photo (thumb size) | varchar(255) | YES | | | | | micro | Link to the profile photo (micro size) | varchar(255) | YES | | | | | header | Header picture | varchar(255) | YES | | NULL | | -| site-pubkey | | text | YES | | NULL | | -| issued-id | | varchar(255) | NO | | | | -| dfrn-id | | varchar(255) | NO | | | | +| site-pubkey | Deprecated | text | YES | | NULL | | +| issued-id | Deprecated | varchar(255) | NO | | | | +| dfrn-id | Deprecated | varchar(255) | NO | | | | | url | | varchar(255) | NO | | | | | nurl | | varchar(255) | NO | | | | | uri-id | Id of the item-uri table entry that contains the contact url | int unsigned | YES | | NULL | | @@ -48,8 +48,8 @@ Fields | confirm | | varchar(255) | YES | | NULL | | | subscribe | | varchar(255) | YES | | NULL | | | poco | | varchar(255) | YES | | NULL | | -| aes_allow | | boolean | NO | | 0 | | -| ret-aes | | boolean | NO | | 0 | | +| aes_allow | Deprecated | boolean | NO | | 0 | | +| ret-aes | Deprecated | boolean | NO | | 0 | | | usehub | | boolean | NO | | 0 | | | subhub | | boolean | NO | | 0 | | | hub-verify | | varchar(255) | NO | | | | @@ -109,8 +109,6 @@ Indexes | nurl_uid | nurl(128), uid | | nick_uid | nick(128), uid | | attag_uid | attag(96), uid | -| dfrn-id | dfrn-id(64) | -| issued-id | issued-id(64) | | network_uid_lastupdate | network, uid, last-update | | uid_network_self_lastupdate | uid, network, self, last-update | | uid_lastitem | uid, last-item | diff --git a/include/api.php b/include/api.php index 951c2b3e5..1d1cdc8f6 100644 --- a/include/api.php +++ b/include/api.php @@ -4950,70 +4950,6 @@ function prepare_photo_data($type, $scale, $photo_id) return $data; } - -/** - * Similar as /mod/redir.php - * redirect to 'url' after dfrn auth - * - * Why this when there is mod/redir.php already? - * This use api_user() and api_login() - * - * params - * c_url: url of remote contact to auth to - * url: string, url to redirect after auth - */ -function api_friendica_remoteauth() -{ - $url = $_GET['url'] ?? ''; - $c_url = $_GET['c_url'] ?? ''; - - if ($url === '' || $c_url === '') { - throw new BadRequestException("Wrong parameters."); - } - - $c_url = Strings::normaliseLink($c_url); - - // traditional DFRN - - $contact = DBA::selectFirst('contact', [], ['uid' => api_user(), 'nurl' => $c_url]); - if (!DBA::isResult($contact)) { - throw new BadRequestException("Unknown contact"); - } - - $cid = $contact['id']; - - $dfrn_id = $contact['issued-id'] ?: $contact['dfrn-id']; - - if (($contact['network'] !== Protocol::DFRN) || empty($dfrn_id)) { - System::externalRedirect($url ?: $c_url); - } - - if ($contact['duplex'] && $contact['issued-id']) { - $orig_id = $contact['issued-id']; - $dfrn_id = '1:' . $orig_id; - } - if ($contact['duplex'] && $contact['dfrn-id']) { - $orig_id = $contact['dfrn-id']; - $dfrn_id = '0:' . $orig_id; - } - - $sec = Strings::getRandomHex(); - - $fields = ['uid' => api_user(), 'cid' => $cid, 'dfrn_id' => $dfrn_id, - 'sec' => $sec, 'expire' => time() + 45]; - DBA::insert('profile_check', $fields); - - Logger::info(API_LOG_PREFIX . 'for contact {contact}', ['module' => 'api', 'action' => 'friendica_remoteauth', 'contact' => $contact['name'], 'hey' => $sec]); - $dest = ($url ? '&destination_url=' . $url : ''); - - System::externalRedirect( - $contact['poll'] . '?dfrn_id=' . $dfrn_id - . '&dfrn_version=' . DFRN_PROTOCOL_VERSION - . '&type=profile&sec=' . $sec . $dest - ); -} -api_register_func('api/friendica/remoteauth', 'api_friendica_remoteauth', true); - /** * Return an item with announcer data if it had been announced * diff --git a/mod/dfrn_confirm.php b/mod/dfrn_confirm.php deleted file mode 100644 index 47cc09326..000000000 --- a/mod/dfrn_confirm.php +++ /dev/null @@ -1,563 +0,0 @@ -. - * - * Friendship acceptance for DFRN contacts - * - * There are two possible entry points and three scenarios. - * - * 1. A form was submitted by our user approving a friendship that originated elsewhere. - * This may also be called from dfrn_request to automatically approve a friendship. - * - * 2. We may be the target or other side of the conversation to scenario 1, and will - * interact with that process on our own user's behalf. - * - * @see PDF with dfrn specs: https://github.com/friendica/friendica/blob/stable/spec/dfrn2.pdf - * You also find a graphic which describes the confirmation process at - * https://github.com/friendica/friendica/blob/stable/spec/dfrn2_contact_confirmation.png - */ - -use Friendica\App; -use Friendica\Core\Logger; -use Friendica\Core\Protocol; -use Friendica\Core\System; -use Friendica\Database\DBA; -use Friendica\DI; -use Friendica\Model\Contact; -use Friendica\Model\Group; -use Friendica\Model\Notification; -use Friendica\Model\User; -use Friendica\Protocol\Activity; -use Friendica\Util\Crypto; -use Friendica\Util\DateTimeFormat; -use Friendica\Util\Strings; -use Friendica\Util\XML; - -function dfrn_confirm_post(App $a, $handsfree = null) -{ - $node = null; - if (is_array($handsfree)) { - /* - * We were called directly from dfrn_request due to automatic friend acceptance. - * Any $_POST parameters we may require are supplied in the $handsfree array. - * - */ - $node = $handsfree['node']; - $a->interactive = false; // notice() becomes a no-op since nobody is there to see it - } elseif ($a->argc > 1) { - $node = $a->argv[1]; - } - - /* - * Main entry point. Scenario 1. Our user received a friend request notification (perhaps - * from another site) and clicked 'Approve'. - * $POST['source_url'] is not set. If it is, it indicates Scenario 2. - * - * We may also have been called directly from dfrn_request ($handsfree != null) due to - * this being a page type which supports automatic friend acceptance. That is also Scenario 1 - * since we are operating on behalf of our registered user to approve a friendship. - */ - if (empty($_POST['source_url'])) { - $uid = ($handsfree['uid'] ?? 0) ?: local_user(); - if (!$uid) { - notice(DI::l10n()->t('Permission denied.')); - return; - } - - $user = DBA::selectFirst('user', [], ['uid' => $uid]); - if (!DBA::isResult($user)) { - notice(DI::l10n()->t('Profile not found.')); - return; - } - - // These data elements may come from either the friend request notification form or $handsfree array. - if (is_array($handsfree)) { - Logger::log('Confirm in handsfree mode'); - $dfrn_id = $handsfree['dfrn_id']; - $intro_id = $handsfree['intro_id']; - $duplex = $handsfree['duplex']; - $cid = 0; - $hidden = intval($handsfree['hidden'] ?? 0); - } else { - $dfrn_id = Strings::escapeTags(trim($_POST['dfrn_id'] ?? '')); - $intro_id = intval($_POST['intro_id'] ?? 0); - $duplex = intval($_POST['duplex'] ?? 0); - $cid = intval($_POST['contact_id'] ?? 0); - $hidden = intval($_POST['hidden'] ?? 0); - } - - /* - * Ensure that dfrn_id has precedence when we go to find the contact record. - * We only want to search based on contact id if there is no dfrn_id, - * e.g. for OStatus network followers. - */ - if (strlen($dfrn_id)) { - $cid = 0; - } - - Logger::log('Confirming request for dfrn_id (issued) ' . $dfrn_id); - if ($cid) { - Logger::log('Confirming follower with contact_id: ' . $cid); - } - - /* - * The other person will have been issued an ID when they first requested friendship. - * Locate their record. At this time, their record will have both pending and blocked set to 1. - * There won't be any dfrn_id if this is a network follower, so use the contact_id instead. - */ - $r = q("SELECT * - FROM `contact` - WHERE ( - (`issued-id` != '' AND `issued-id` = '%s') - OR - (`id` = %d AND `id` != 0) - ) - AND `uid` = %d - AND `duplex` = 0 - LIMIT 1", - DBA::escape($dfrn_id), - intval($cid), - intval($uid) - ); - if (!DBA::isResult($r)) { - Logger::log('Contact not found in DB.'); - notice(DI::l10n()->t('Contact not found.')); - notice(DI::l10n()->t('This may occasionally happen if contact was requested by both persons and it has already been approved.')); - return; - } - - $contact = $r[0]; - - $contact_id = $contact['id']; - $relation = $contact['rel']; - $site_pubkey = $contact['site-pubkey']; - $dfrn_confirm = $contact['confirm']; - $aes_allow = $contact['aes_allow']; - $protocol = $contact['network']; - - /* - * Generate a key pair for all further communications with this person. - * We have a keypair for every contact, and a site key for unknown people. - * This provides a means to carry on relationships with other people if - * any single key is compromised. It is a robust key. We're much more - * worried about key leakage than anybody cracking it. - */ - $res = Crypto::newKeypair(4096); - - $private_key = $res['prvkey']; - $public_key = $res['pubkey']; - - // Save the private key. Send them the public key. - $fields = ['prvkey' => $private_key, 'protocol' => Protocol::DFRN]; - DBA::update('contact', $fields, ['id' => $contact_id]); - - $params = []; - - /* - * Per the DFRN protocol, we will verify both ends by encrypting the dfrn_id with our - * site private key (person on the other end can decrypt it with our site public key). - * Then encrypt our profile URL with the other person's site public key. They can decrypt - * it with their site private key. If the decryption on the other end fails for either - * item, it indicates tampering or key failure on at least one site and we will not be - * able to provide a secure communication pathway. - * - * If other site is willing to accept full encryption, (aes_allow is 1 AND we have php5.3 - * or later) then we encrypt the personal public key we send them using AES-256-CBC and a - * random key which is encrypted with their site public key. - */ - - $src_aes_key = random_bytes(64); - - $result = ''; - openssl_private_encrypt($dfrn_id, $result, $user['prvkey']); - - $params['dfrn_id'] = bin2hex($result); - $params['public_key'] = $public_key; - - $my_url = DI::baseUrl() . '/profile/' . $user['nickname']; - - openssl_public_encrypt($my_url, $params['source_url'], $site_pubkey); - $params['source_url'] = bin2hex($params['source_url']); - - if ($aes_allow && function_exists('openssl_encrypt')) { - openssl_public_encrypt($src_aes_key, $params['aes_key'], $site_pubkey); - $params['aes_key'] = bin2hex($params['aes_key']); - $params['public_key'] = bin2hex(openssl_encrypt($public_key, 'AES-256-CBC', $src_aes_key)); - } - - $params['dfrn_version'] = DFRN_PROTOCOL_VERSION; - if ($duplex == 1) { - $params['duplex'] = 1; - } - - if ($user['page-flags'] == User::PAGE_FLAGS_COMMUNITY) { - $params['page'] = 1; - } - - if ($user['page-flags'] == User::PAGE_FLAGS_PRVGROUP) { - $params['page'] = 2; - } - - Logger::debug('Confirm: posting data', ['confirm' => $dfrn_confirm, 'parameter' => $params]); - - /* - * - * POST all this stuff to the other site. - * Temporarily raise the network timeout to 120 seconds because the default 60 - * doesn't always give the other side quite enough time to decrypt everything. - * - */ - - $res = DI::httpRequest()->post($dfrn_confirm, $params, [], 120)->getBody(); - - Logger::log(' Confirm: received data: ' . $res, Logger::DATA); - - // Now figure out what they responded. Try to be robust if the remote site is - // having difficulty and throwing up errors of some kind. - - $leading_junk = substr($res, 0, strpos($res, 't('Response from remote site was not understood.')); - return; - } - - if (strlen($leading_junk) && DI::config()->get('system', 'debugging')) { - // This might be more common. Mixed error text and some XML. - // If we're configured for debugging, show the text. Proceed in either case. - notice(DI::l10n()->t('Unexpected response from remote site: ') . $leading_junk); - } - - if (stristr($res, "t('Unexpected response from remote site: ') . EOL . htmlspecialchars($res)); - return; - } - - $xml = XML::parseString($res); - $status = (int) $xml->status; - $message = XML::unescape($xml->message); // human readable text of what may have gone wrong. - switch ($status) { - case 0: - info(DI::l10n()->t("Confirmation completed successfully.")); - break; - case 1: - // birthday paradox - generate new dfrn-id and fall through. - $new_dfrn_id = Strings::getRandomHex(); - q("UPDATE contact SET `issued-id` = '%s' WHERE `id` = %d AND `uid` = %d", - DBA::escape($new_dfrn_id), - intval($contact_id), - intval($uid) - ); - - case 2: - notice(DI::l10n()->t("Temporary failure. Please wait and try again.")); - break; - case 3: - notice(DI::l10n()->t("Introduction failed or was revoked.")); - break; - } - - if (strlen($message)) { - notice(DI::l10n()->t('Remote site reported: ') . $message); - } - - if (($status == 0) && $intro_id) { - $intro = DBA::selectFirst('intro', ['note'], ['id' => $intro_id]); - if (DBA::isResult($intro)) { - DBA::update('contact', ['reason' => $intro['note']], ['id' => $contact_id]); - } - - // Success. Delete the notification. - DBA::delete('intro', ['id' => $intro_id]); - } - - if ($status != 0) { - return; - } - - /* - * We have now established a relationship with the other site. - * Let's make our own personal copy of their profile photo so we don't have - * to always load it from their site. - * - * We will also update the contact record with the nature and scope of the relationship. - */ - Contact::updateAvatar($contact_id, $contact['photo']); - - Logger::log('dfrn_confirm: confirm - imported photos'); - - $new_relation = Contact::FOLLOWER; - - if (($relation == Contact::SHARING) || ($duplex)) { - $new_relation = Contact::FRIEND; - } - - if (($relation == Contact::SHARING) && ($duplex)) { - $duplex = 0; - } - - $r = q("UPDATE `contact` SET `rel` = %d, - `name-date` = '%s', - `uri-date` = '%s', - `blocked` = 0, - `pending` = 0, - `duplex` = %d, - `hidden` = %d, - `network` = '%s' WHERE `id` = %d - ", - intval($new_relation), - DBA::escape(DateTimeFormat::utcNow()), - DBA::escape(DateTimeFormat::utcNow()), - intval($duplex), - intval($hidden), - DBA::escape(Protocol::DFRN), - intval($contact_id) - ); - - // reload contact info - $contact = DBA::selectFirst('contact', [], ['id' => $contact_id]); - - Group::addMember(User::getDefaultGroup($uid, $contact["network"]), $contact['id']); - - // Let's send our user to the contact editor in case they want to - // do anything special with this new friend. - if ($handsfree === null) { - DI::baseUrl()->redirect('contact/' . intval($contact_id)); - } else { - return; - } - //NOTREACHED - } - - /* - * End of Scenario 1. [Local confirmation of remote friend request]. - * - * Begin Scenario 2. This is the remote response to the above scenario. - * This will take place on the site that originally initiated the friend request. - * In the section above where the confirming party makes a POST and - * retrieves xml status information, they are communicating with the following code. - */ - if (!empty($_POST['source_url'])) { - // We are processing an external confirmation to an introduction created by our user. - $public_key = $_POST['public_key'] ?? ''; - $dfrn_id = hex2bin($_POST['dfrn_id'] ?? ''); - $source_url = hex2bin($_POST['source_url'] ?? ''); - $aes_key = $_POST['aes_key'] ?? ''; - $duplex = intval($_POST['duplex'] ?? 0); - $page = intval($_POST['page'] ?? 0); - - $forum = (($page == 1) ? 1 : 0); - $prv = (($page == 2) ? 1 : 0); - - Logger::notice('requestee contacted', ['node' => $node]); - - Logger::debug('request', ['POST' => $_POST]); - - // If $aes_key is set, both of these items require unpacking from the hex transport encoding. - - if (!empty($aes_key)) { - $aes_key = hex2bin($aes_key); - $public_key = hex2bin($public_key); - } - - // Find our user's account - $user = DBA::selectFirst('user', [], ['nickname' => $node]); - if (!DBA::isResult($user)) { - $message = DI::l10n()->t('No user record found for \'%s\' ', $node); - System::xmlExit(3, $message); // failure - // NOTREACHED - } - - $my_prvkey = $user['prvkey']; - $local_uid = $user['uid']; - - - if (!strstr($my_prvkey, 'PRIVATE KEY')) { - $message = DI::l10n()->t('Our site encryption key is apparently messed up.'); - System::xmlExit(3, $message); - } - - // verify everything - - $decrypted_source_url = ""; - openssl_private_decrypt($source_url, $decrypted_source_url, $my_prvkey); - - - if (!strlen($decrypted_source_url)) { - $message = DI::l10n()->t('Empty site URL was provided or URL could not be decrypted by us.'); - System::xmlExit(3, $message); - // NOTREACHED - } - - $contact = DBA::selectFirst('contact', [], ['url' => $decrypted_source_url, 'uid' => $local_uid]); - if (!DBA::isResult($contact)) { - if (strstr($decrypted_source_url, 'http:')) { - $newurl = str_replace('http:', 'https:', $decrypted_source_url); - } else { - $newurl = str_replace('https:', 'http:', $decrypted_source_url); - } - - $contact = DBA::selectFirst('contact', [], ['url' => $newurl, 'uid' => $local_uid]); - if (!DBA::isResult($contact)) { - // this is either a bogus confirmation (?) or we deleted the original introduction. - $message = DI::l10n()->t('Contact record was not found for you on our site.'); - System::xmlExit(3, $message); - return; // NOTREACHED - } - } - - $relation = $contact['rel']; - - // Decrypt all this stuff we just received - - $foreign_pubkey = $contact['site-pubkey']; - $dfrn_record = $contact['id']; - - if (!$foreign_pubkey) { - $message = DI::l10n()->t('Site public key not available in contact record for URL %s.', $decrypted_source_url); - System::xmlExit(3, $message); - } - - $decrypted_dfrn_id = ""; - openssl_public_decrypt($dfrn_id, $decrypted_dfrn_id, $foreign_pubkey); - - if (strlen($aes_key)) { - $decrypted_aes_key = ""; - openssl_private_decrypt($aes_key, $decrypted_aes_key, $my_prvkey); - $dfrn_pubkey = openssl_decrypt($public_key, 'AES-256-CBC', $decrypted_aes_key); - } else { - $dfrn_pubkey = $public_key; - } - - if (DBA::exists('contact', ['dfrn-id' => $decrypted_dfrn_id])) { - $message = DI::l10n()->t('The ID provided by your system is a duplicate on our system. It should work if you try again.'); - System::xmlExit(1, $message); // Birthday paradox - duplicate dfrn-id - // NOTREACHED - } - - $r = q("UPDATE `contact` SET `dfrn-id` = '%s', `pubkey` = '%s' WHERE `id` = %d", - DBA::escape($decrypted_dfrn_id), - DBA::escape($dfrn_pubkey), - intval($dfrn_record) - ); - if (!DBA::isResult($r)) { - $message = DI::l10n()->t('Unable to set your contact credentials on our system.'); - System::xmlExit(3, $message); - } - - // It's possible that the other person also requested friendship. - // If it is a duplex relationship, ditch the issued-id if one exists. - - if ($duplex) { - q("UPDATE `contact` SET `issued-id` = '' WHERE `id` = %d", - intval($dfrn_record) - ); - } - - // We're good but now we have to scrape the profile photo and send notifications. - $contact = DBA::selectFirst('contact', ['photo'], ['id' => $dfrn_record]); - if (DBA::isResult($contact)) { - $photo = $contact['photo']; - } else { - $photo = DI::baseUrl() . Contact::DEFAULT_AVATAR_PHOTO; - } - - Contact::updateAvatar($dfrn_record, $photo); - - Logger::log('dfrn_confirm: request - photos imported'); - - $new_relation = Contact::SHARING; - - if (($relation == Contact::FOLLOWER) || ($duplex)) { - $new_relation = Contact::FRIEND; - } - - if (($relation == Contact::FOLLOWER) && ($duplex)) { - $duplex = 0; - } - - $r = q("UPDATE `contact` SET - `rel` = %d, - `name-date` = '%s', - `uri-date` = '%s', - `blocked` = 0, - `pending` = 0, - `duplex` = %d, - `forum` = %d, - `prv` = %d, - `network` = '%s' WHERE `id` = %d - ", - intval($new_relation), - DBA::escape(DateTimeFormat::utcNow()), - DBA::escape(DateTimeFormat::utcNow()), - intval($duplex), - intval($forum), - intval($prv), - DBA::escape(Protocol::DFRN), - intval($dfrn_record) - ); - if (!DBA::isResult($r)) { // indicates schema is messed up or total db failure - $message = DI::l10n()->t('Unable to update your contact profile details on our system'); - System::xmlExit(3, $message); - } - - // Otherwise everything seems to have worked and we are almost done. Yay! - // Send an email notification - - Logger::log('dfrn_confirm: request: info updated'); - - $combined = null; - $r = q("SELECT `contact`.*, `user`.* - FROM `contact` - LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid` - WHERE `contact`.`id` = %d - LIMIT 1", - intval($dfrn_record) - ); - if (DBA::isResult($r)) { - $combined = $r[0]; - - if ($combined['notify-flags'] & Notification\Type::CONFIRM) { - $mutual = ($new_relation == Contact::FRIEND); - notification([ - 'type' => Notification\Type::CONFIRM, - 'otype' => Notification\ObjectType::INTRO, - 'verb' => ($mutual ? Activity::FRIEND : Activity::FOLLOW), - 'uid' => $combined['uid'], - 'cid' => $combined['id'], - 'link' => DI::baseUrl() . '/contact/' . $dfrn_record, - ]); - } - } - - System::xmlExit(0); // Success - return; // NOTREACHED - ////////////////////// End of this scenario /////////////////////////////////////////////// - } - - // somebody arrived here by mistake or they are fishing. Send them to the homepage. - DI::baseUrl()->redirect(); - // NOTREACHED -} diff --git a/mod/dfrn_notify.php b/mod/dfrn_notify.php index f43357fd4..f5a1d97bf 100644 --- a/mod/dfrn_notify.php +++ b/mod/dfrn_notify.php @@ -26,14 +26,12 @@ use Friendica\App; use Friendica\Core\Logger; use Friendica\Core\System; use Friendica\Database\DBA; -use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Conversation; -use Friendica\Model\User; +use Friendica\Network\HTTPException; use Friendica\Protocol\DFRN; use Friendica\Protocol\Diaspora; use Friendica\Util\Network; -use Friendica\Util\Strings; function dfrn_notify_post(App $a) { $postdata = Network::postdata(); @@ -53,148 +51,7 @@ function dfrn_notify_post(App $a) { salmon_post($a, $postdata); } } - - $dfrn_id = (!empty($_POST['dfrn_id']) ? Strings::escapeTags(trim($_POST['dfrn_id'])) : ''); - $dfrn_version = (!empty($_POST['dfrn_version']) ? (float) $_POST['dfrn_version'] : 2.0); - $challenge = (!empty($_POST['challenge']) ? Strings::escapeTags(trim($_POST['challenge'])) : ''); - $data = $_POST['data'] ?? ''; - $key = $_POST['key'] ?? ''; - $rino_remote = (!empty($_POST['rino']) ? intval($_POST['rino']) : 0); - $dissolve = (!empty($_POST['dissolve']) ? intval($_POST['dissolve']) : 0); - $perm = (!empty($_POST['perm']) ? Strings::escapeTags(trim($_POST['perm'])) : 'r'); - $ssl_policy = (!empty($_POST['ssl_policy']) ? Strings::escapeTags(trim($_POST['ssl_policy'])): 'none'); - $page = (!empty($_POST['page']) ? intval($_POST['page']) : 0); - - $forum = (($page == 1) ? 1 : 0); - $prv = (($page == 2) ? 1 : 0); - - $writable = (-1); - if ($dfrn_version >= 2.21) { - $writable = (($perm === 'rw') ? 1 : 0); - } - - $direction = (-1); - if (strpos($dfrn_id, ':') == 1) { - $direction = intval(substr($dfrn_id, 0, 1)); - $dfrn_id = substr($dfrn_id, 2); - } - - if (!DBA::exists('challenge', ['dfrn-id' => $dfrn_id, 'challenge' => $challenge])) { - Logger::log('could not match challenge to dfrn_id ' . $dfrn_id . ' challenge=' . $challenge); - System::xmlExit(3, 'Could not match challenge'); - } - - DBA::delete('challenge', ['dfrn-id' => $dfrn_id, 'challenge' => $challenge]); - - $user = DBA::selectFirst('user', ['uid'], ['nickname' => $a->argv[1]]); - if (!DBA::isResult($user)) { - Logger::log('User not found for nickname ' . $a->argv[1]); - System::xmlExit(3, 'User not found'); - } - - // find the local user who owns this relationship. - $condition = []; - switch ($direction) { - case (-1): - $condition = ["(`issued-id` = ? OR `dfrn-id` = ?) AND `uid` = ?", $dfrn_id, $dfrn_id, $user['uid']]; - break; - case 0: - $condition = ['issued-id' => $dfrn_id, 'duplex' => true, 'uid' => $user['uid']]; - break; - case 1: - $condition = ['dfrn-id' => $dfrn_id, 'duplex' => true, 'uid' => $user['uid']]; - break; - default: - System::xmlExit(3, 'Invalid direction'); - break; // NOTREACHED - } - - $contact = DBA::selectFirst('contact', ['id'], $condition); - if (!DBA::isResult($contact)) { - Logger::log('contact not found for dfrn_id ' . $dfrn_id); - System::xmlExit(3, 'Contact not found'); - } - - // $importer in this case contains the contact record for the remote contact joined with the user record of our user. - $importer = DFRN::getImporter($contact['id'], $user['uid']); - - if ((($writable != (-1)) && ($writable != $importer['writable'])) || ($importer['forum'] != $forum) || ($importer['prv'] != $prv)) { - $fields = ['writable' => ($writable == (-1)) ? $importer['writable'] : $writable, - 'forum' => $forum, 'prv' => $prv]; - DBA::update('contact', $fields, ['id' => $importer['id']]); - - if ($writable != (-1)) { - $importer['writable'] = $writable; - } - $importer['forum'] = $page; - } - - - // if contact's ssl policy changed, update our links - - $importer = Contact::updateSslPolicy($importer, $ssl_policy); - - Logger::log('data: ' . $data, Logger::DATA); - - if ($dissolve == 1) { - // Relationship is dissolved permanently - Contact::remove($importer['id']); - Logger::log('relationship dissolved : ' . $importer['name'] . ' dissolved ' . $importer['username']); - System::xmlExit(0, 'relationship dissolved'); - } - - $rino = DI::config()->get('system', 'rino_encrypt'); - $rino = intval($rino); - - if (strlen($key)) { - - // if local rino is lower than remote rino, abort: should not happen! - // but only for $remote_rino > 1, because old code did't send rino version - if ($rino_remote > 1 && $rino < $rino_remote) { - Logger::log("rino version '$rino_remote' is lower than supported '$rino'"); - System::xmlExit(0, "rino version '$rino_remote' is lower than supported '$rino'"); - } - - $rawkey = hex2bin(trim($key)); - Logger::log('rino: md5 raw key: ' . md5($rawkey), Logger::DATA); - - $final_key = ''; - - if ($dfrn_version >= 2.1) { - if (($importer['duplex'] && strlen($importer['cprvkey'])) || !strlen($importer['cpubkey'])) { - openssl_private_decrypt($rawkey, $final_key, $importer['cprvkey']); - } else { - openssl_public_decrypt($rawkey, $final_key, $importer['cpubkey']); - } - } else { - if (($importer['duplex'] && strlen($importer['cpubkey'])) || !strlen($importer['cprvkey'])) { - openssl_public_decrypt($rawkey, $final_key, $importer['cpubkey']); - } else { - openssl_private_decrypt($rawkey, $final_key, $importer['cprvkey']); - } - } - - switch ($rino_remote) { - case 0: - case 1: - // we got a key. old code send only the key, without RINO version. - // we assume RINO 1 if key and no RINO version - $data = DFRN::aesDecrypt(hex2bin($data), $final_key); - break; - default: - Logger::log("rino: invalid sent version '$rino_remote'"); - System::xmlExit(0, "Invalid sent version '$rino_remote'"); - } - - Logger::log('rino: decrypted data: ' . $data, Logger::DATA); - } - - Logger::log('Importing post from ' . $importer['addr'] . ' to ' . $importer['nickname'] . ' with the RINO ' . $rino_remote . ' encryption.', Logger::DEBUG); - - $ret = DFRN::import($data, $importer, Conversation::PARCEL_LEGACY_DFRN, Conversation::PUSH); - System::xmlExit($ret, 'Processed'); - - // NOTREACHED + throw new HTTPException\BadRequestException(); } function dfrn_dispatch_public($postdata) @@ -261,132 +118,5 @@ function dfrn_dispatch_private($user, $postdata) } function dfrn_notify_content(App $a) { - - if (!empty($_GET['dfrn_id'])) { - - /* - * initial communication from external contact, $direction is their direction. - * If this is a duplex communication, ours will be the opposite. - */ - - $dfrn_id = Strings::escapeTags(trim($_GET['dfrn_id'])); - $rino_remote = (!empty($_GET['rino']) ? intval($_GET['rino']) : 0); - $type = ""; - $last_update = ""; - - Logger::log('new notification dfrn_id=' . $dfrn_id); - - $direction = (-1); - if (strpos($dfrn_id,':') == 1) { - $direction = intval(substr($dfrn_id,0,1)); - $dfrn_id = substr($dfrn_id,2); - } - - $hash = Strings::getRandomHex(); - - $status = 0; - - DBA::delete('challenge', ["`expire` < ?", time()]); - - $fields = ['challenge' => $hash, 'dfrn-id' => $dfrn_id, 'expire' => time() + 90, - 'type' => $type, 'last_update' => $last_update]; - DBA::insert('challenge', $fields); - - Logger::log('challenge=' . $hash, Logger::DATA); - - $user = DBA::selectFirst('user', ['uid'], ['nickname' => $a->argv[1]]); - if (!DBA::isResult($user)) { - Logger::log('User not found for nickname ' . $a->argv[1]); - exit(); - } - - $condition = []; - switch ($direction) { - case (-1): - $condition = ["(`issued-id` = ? OR `dfrn-id` = ?) AND `uid` = ?", $dfrn_id, $dfrn_id, $user['uid']]; - $my_id = $dfrn_id; - break; - case 0: - $condition = ['issued-id' => $dfrn_id, 'duplex' => true, 'uid' => $user['uid']]; - $my_id = '1:' . $dfrn_id; - break; - case 1: - $condition = ['dfrn-id' => $dfrn_id, 'duplex' => true, 'uid' => $user['uid']]; - $my_id = '0:' . $dfrn_id; - break; - default: - $status = 1; - $my_id = ''; - break; - } - - $contact = DBA::selectFirst('contact', ['id'], $condition); - if (!DBA::isResult($contact)) { - Logger::log('contact not found for dfrn_id ' . $dfrn_id); - System::xmlExit(3, 'Contact not found'); - } - - // $importer in this case contains the contact record for the remote contact joined with the user record of our user. - $importer = DFRN::getImporter($contact['id'], $user['uid']); - if (empty($importer)) { - Logger::log('No importer data found for user ' . $a->argv[1] . ' and contact ' . $dfrn_id); - exit(); - } - - Logger::log("Remote rino version: ".$rino_remote." for ".$importer["url"], Logger::DATA); - - $challenge = ''; - $encrypted_id = ''; - $id_str = $my_id . '.' . mt_rand(1000,9999); - - $prv_key = trim($importer['cprvkey']); - $pub_key = trim($importer['cpubkey']); - $dplx = intval($importer['duplex']); - - if (($dplx && strlen($prv_key)) || (strlen($prv_key) && !strlen($pub_key))) { - openssl_private_encrypt($hash, $challenge, $prv_key); - openssl_private_encrypt($id_str, $encrypted_id, $prv_key); - } elseif (strlen($pub_key)) { - openssl_public_encrypt($hash, $challenge, $pub_key); - openssl_public_encrypt($id_str, $encrypted_id, $pub_key); - } else { - /// @TODO these kind of else-blocks are making the code harder to understand - $status = 1; - } - - $challenge = bin2hex($challenge); - $encrypted_id = bin2hex($encrypted_id); - - - $rino = DI::config()->get('system', 'rino_encrypt'); - $rino = intval($rino); - - Logger::log("Local rino version: ". $rino, Logger::DATA); - - // if requested rino is lower than enabled local rino, lower local rino version - // if requested rino is higher than enabled local rino, reply with local rino - if ($rino_remote < $rino) { - $rino = $rino_remote; - } - - if (($importer['rel'] && ($importer['rel'] != Contact::SHARING)) || ($importer['page-flags'] == User::PAGE_FLAGS_COMMUNITY)) { - $perm = 'rw'; - } else { - $perm = 'r'; - } - - header("Content-type: text/xml"); - - echo '' . "\r\n" - . '' . "\r\n" - . "\t" . '' . $status . '' . "\r\n" - . "\t" . '' . DFRN_PROTOCOL_VERSION . '' . "\r\n" - . "\t" . '' . $rino . '' . "\r\n" - . "\t" . '' . $perm . '' . "\r\n" - . "\t" . '' . $encrypted_id . '' . "\r\n" - . "\t" . '' . $challenge . '' . "\r\n" - . '' . "\r\n"; - - exit(); - } + throw new HTTPException\NotFoundException(); } diff --git a/mod/dfrn_poll.php b/mod/dfrn_poll.php index f0a8e855e..7fd3abbd9 100644 --- a/mod/dfrn_poll.php +++ b/mod/dfrn_poll.php @@ -20,536 +20,17 @@ */ use Friendica\App; -use Friendica\Core\Logger; -use Friendica\Core\Session; -use Friendica\Core\System; -use Friendica\Database\DBA; -use Friendica\DI; -use Friendica\Protocol\DFRN; use Friendica\Protocol\OStatus; -use Friendica\Util\Strings; -use Friendica\Util\XML; +use Friendica\Network\HTTPException; function dfrn_poll_init(App $a) { - DI::auth()->withSession($a); - - $dfrn_id = $_GET['dfrn_id'] ?? ''; - $type = ($_GET['type'] ?? '') ?: 'data'; - $last_update = $_GET['last_update'] ?? ''; - $destination_url = $_GET['destination_url'] ?? ''; - $challenge = $_GET['challenge'] ?? ''; - $sec = $_GET['sec'] ?? ''; - $dfrn_version = floatval(($_GET['dfrn_version'] ?? 0.0) ?: 2.0); - $quiet = !empty($_GET['quiet']); - - // Possibly it is an OStatus compatible server that requests a user feed - $user_agent = $_SERVER['HTTP_USER_AGENT'] ?? ''; - if (($a->argc > 1) && ($dfrn_id == '') && !strstr($user_agent, 'Friendica')) { + if ($a->argc > 1) { $nickname = $a->argv[1]; header("Content-type: application/atom+xml"); + $last_update = $_GET['last_update'] ?? ''; echo OStatus::feed($nickname, $last_update, 10); exit(); } - - $direction = -1; - - if (strpos($dfrn_id, ':') == 1) { - $direction = intval(substr($dfrn_id, 0, 1)); - $dfrn_id = substr($dfrn_id, 2); - } - - $hidewall = false; - - if (($dfrn_id === '') && empty($_POST['dfrn_id'])) { - if (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) { - throw new \Friendica\Network\HTTPException\ForbiddenException(); - } - - $user = ''; - if ($a->argc > 1) { - $r = q("SELECT `hidewall`,`nickname` FROM `user` WHERE `user`.`nickname` = '%s' LIMIT 1", - DBA::escape($a->argv[1]) - ); - if (!$r) { - throw new \Friendica\Network\HTTPException\NotFoundException(); - } - - $hidewall = ($r[0]['hidewall'] && !local_user()); - - $user = $r[0]['nickname']; - } - - Logger::log('dfrn_poll: public feed request from ' . $_SERVER['REMOTE_ADDR'] . ' for ' . $user); - header("Content-type: application/atom+xml"); - echo DFRN::feed('', $user, $last_update, 0, $hidewall); - exit(); - } - - if (($type === 'profile') && (!strlen($sec))) { - $sql_extra = ''; - switch ($direction) { - case -1: - $sql_extra = sprintf(" AND ( `dfrn-id` = '%s' OR `issued-id` = '%s' ) ", DBA::escape($dfrn_id), DBA::escape($dfrn_id)); - $my_id = $dfrn_id; - break; - case 0: - $sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", DBA::escape($dfrn_id)); - $my_id = '1:' . $dfrn_id; - break; - case 1: - $sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", DBA::escape($dfrn_id)); - $my_id = '0:' . $dfrn_id; - break; - default: - DI::baseUrl()->redirect(); - break; // NOTREACHED - } - - $r = q("SELECT `contact`.*, `user`.`username`, `user`.`nickname` - FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid` - WHERE `contact`.`blocked` = 0 AND `contact`.`pending` = 0 - AND `user`.`nickname` = '%s' $sql_extra LIMIT 1", - DBA::escape($a->argv[1]) - ); - - if (DBA::isResult($r)) { - $s = DI::httpRequest()->fetch($r[0]['poll'] . '?dfrn_id=' . $my_id . '&type=profile-check'); - - Logger::log("dfrn_poll: old profile returns " . $s, Logger::DATA); - - if (strlen($s)) { - $xml = XML::parseString($s); - - if ((int)$xml->status === 1) { - $_SESSION['authenticated'] = 1; - $_SESSION['visitor_id'] = $r[0]['id']; - $_SESSION['visitor_home'] = $r[0]['url']; - $_SESSION['visitor_handle'] = $r[0]['addr']; - $_SESSION['visitor_visiting'] = $r[0]['uid']; - $_SESSION['my_url'] = $r[0]['url']; - $_SESSION['remote_comment'] = $r[0]['subscribe']; - - Session::setVisitorsContacts(); - - if (!$quiet) { - info(DI::l10n()->t('%1$s welcomes %2$s', $r[0]['username'], $r[0]['name'])); - } - - // Visitors get 1 day session. - $session_id = session_id(); - $expire = time() + 86400; - q("UPDATE `session` SET `expire` = '%s' WHERE `sid` = '%s'", - DBA::escape($expire), - DBA::escape($session_id) - ); - } - } - - $profile = (count($r) > 0 && isset($r[0]['nickname']) ? $r[0]['nickname'] : ''); - if (!empty($destination_url)) { - System::externalRedirect($destination_url); - } else { - DI::baseUrl()->redirect('profile/' . $profile); - } - } - DI::baseUrl()->redirect(); - } - - if ($type === 'profile-check' && $dfrn_version < 2.2) { - if ((strlen($challenge)) && (strlen($sec))) { - DBA::delete('profile_check', ["`expire` < ?", time()]); - $r = q("SELECT * FROM `profile_check` WHERE `sec` = '%s' ORDER BY `expire` DESC LIMIT 1", - DBA::escape($sec) - ); - if (!DBA::isResult($r)) { - System::xmlExit(3, 'No ticket'); - // NOTREACHED - } - - $orig_id = $r[0]['dfrn_id']; - if (strpos($orig_id, ':')) { - $orig_id = substr($orig_id, 2); - } - - $c = q("SELECT * FROM `contact` WHERE `id` = %d LIMIT 1", - intval($r[0]['cid']) - ); - if (!DBA::isResult($c)) { - System::xmlExit(3, 'No profile'); - } - - $contact = $c[0]; - - $sent_dfrn_id = hex2bin($dfrn_id); - $challenge = hex2bin($challenge); - - $final_dfrn_id = ''; - - if (($contact['duplex']) && strlen($contact['prvkey'])) { - openssl_private_decrypt($sent_dfrn_id, $final_dfrn_id, $contact['prvkey']); - openssl_private_decrypt($challenge, $decoded_challenge, $contact['prvkey']); - } else { - openssl_public_decrypt($sent_dfrn_id, $final_dfrn_id, $contact['pubkey']); - openssl_public_decrypt($challenge, $decoded_challenge, $contact['pubkey']); - } - - $final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.')); - - if (strpos($final_dfrn_id, ':') == 1) { - $final_dfrn_id = substr($final_dfrn_id, 2); - } - - if ($final_dfrn_id != $orig_id) { - Logger::log('profile_check: ' . $final_dfrn_id . ' != ' . $orig_id, Logger::DEBUG); - // did not decode properly - cannot trust this site - System::xmlExit(3, 'Bad decryption'); - } - - header("Content-type: text/xml"); - echo "0$decoded_challenge$sec"; - exit(); - // NOTREACHED - } else { - // old protocol - switch ($direction) { - case 1: - $dfrn_id = '0:' . $dfrn_id; - break; - case 0: - $dfrn_id = '1:' . $dfrn_id; - break; - default: - break; - } - - DBA::delete('profile_check', ["`expire` < ?", time()]); - $r = q("SELECT * FROM `profile_check` WHERE `dfrn_id` = '%s' ORDER BY `expire` DESC", - DBA::escape($dfrn_id)); - if (DBA::isResult($r)) { - System::xmlExit(1); - return; // NOTREACHED - } - System::xmlExit(0); - return; // NOTREACHED - } - } -} - -function dfrn_poll_post(App $a) -{ - $dfrn_id = $_POST['dfrn_id'] ?? ''; - $challenge = $_POST['challenge'] ?? ''; - $sec = $_POST['sec'] ?? ''; - $ptype = $_POST['type'] ?? ''; - $perm = ($_POST['perm'] ?? '') ?: 'r'; - $dfrn_version = floatval(($_GET['dfrn_version'] ?? 0.0) ?: 2.0); - - if ($ptype === 'profile-check') { - if (strlen($challenge) && strlen($sec)) { - Logger::log('dfrn_poll: POST: profile-check'); - - DBA::delete('profile_check', ["`expire` < ?", time()]); - $r = q("SELECT * FROM `profile_check` WHERE `sec` = '%s' ORDER BY `expire` DESC LIMIT 1", - DBA::escape($sec) - ); - if (!DBA::isResult($r)) { - System::xmlExit(3, 'No ticket'); - // NOTREACHED - } - - $orig_id = $r[0]['dfrn_id']; - if (strpos($orig_id, ':')) { - $orig_id = substr($orig_id, 2); - } - - $c = q("SELECT * FROM `contact` WHERE `id` = %d LIMIT 1", - intval($r[0]['cid']) - ); - if (!DBA::isResult($c)) { - System::xmlExit(3, 'No profile'); - } - - $contact = $c[0]; - - $sent_dfrn_id = hex2bin($dfrn_id); - $challenge = hex2bin($challenge); - - $final_dfrn_id = ''; - - if ($contact['duplex'] && strlen($contact['prvkey'])) { - openssl_private_decrypt($sent_dfrn_id, $final_dfrn_id, $contact['prvkey']); - openssl_private_decrypt($challenge, $decoded_challenge, $contact['prvkey']); - } else { - openssl_public_decrypt($sent_dfrn_id, $final_dfrn_id, $contact['pubkey']); - openssl_public_decrypt($challenge, $decoded_challenge, $contact['pubkey']); - } - - $final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.')); - - if (strpos($final_dfrn_id, ':') == 1) { - $final_dfrn_id = substr($final_dfrn_id, 2); - } - - if ($final_dfrn_id != $orig_id) { - Logger::log('profile_check: ' . $final_dfrn_id . ' != ' . $orig_id, Logger::DEBUG); - // did not decode properly - cannot trust this site - System::xmlExit(3, 'Bad decryption'); - } - - header("Content-type: text/xml"); - echo "0$decoded_challenge$sec"; - exit(); - // NOTREACHED - } - } - - $direction = -1; - if (strpos($dfrn_id, ':') == 1) { - $direction = intval(substr($dfrn_id, 0, 1)); - $dfrn_id = substr($dfrn_id, 2); - } - - $r = q("SELECT * FROM `challenge` WHERE `dfrn-id` = '%s' AND `challenge` = '%s' LIMIT 1", - DBA::escape($dfrn_id), - DBA::escape($challenge) - ); - - if (!DBA::isResult($r)) { - exit(); - } - - $last_update = $r[0]['last_update']; - - DBA::delete('challenge', ['dfrn-id' => $dfrn_id, 'challenge' => $challenge]); - - $sql_extra = ''; - switch ($direction) { - case -1: - $sql_extra = sprintf(" AND `issued-id` = '%s' ", DBA::escape($dfrn_id)); - break; - case 0: - $sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", DBA::escape($dfrn_id)); - break; - case 1: - $sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", DBA::escape($dfrn_id)); - break; - default: - DI::baseUrl()->redirect(); - break; // NOTREACHED - } - - $r = q("SELECT * FROM `contact` WHERE `blocked` = 0 AND `pending` = 0 $sql_extra LIMIT 1"); - if (!DBA::isResult($r)) { - exit(); - } - - $contact = $r[0]; - $contact_id = $r[0]['id']; - - // Update the writable flag if it changed - Logger::debug('post request feed', ['post' => $_POST]); - if ($dfrn_version >= 2.21) { - if ($perm === 'rw') { - $writable = 1; - } else { - $writable = 0; - } - - if ($writable != $contact['writable']) { - q("UPDATE `contact` SET `writable` = %d WHERE `id` = %d", - intval($writable), - intval($contact_id) - ); - } - } - - header("Content-type: application/atom+xml"); - $o = DFRN::feed($dfrn_id, $a->argv[1], $last_update, $direction); - echo $o; - exit(); -} - -function dfrn_poll_content(App $a) -{ - $dfrn_id = $_GET['dfrn_id'] ?? ''; - $type = ($_GET['type'] ?? '') ?: 'data'; - $last_update = $_GET['last_update'] ?? ''; - $destination_url = $_GET['destination_url'] ?? ''; - $sec = $_GET['sec'] ?? ''; - $dfrn_version = floatval(($_GET['dfrn_version'] ?? 0.0) ?: 2.0); - $quiet = !empty($_GET['quiet']); - - $direction = -1; - if (strpos($dfrn_id, ':') == 1) { - $direction = intval(substr($dfrn_id, 0, 1)); - $dfrn_id = substr($dfrn_id, 2); - } - - if ($dfrn_id != '') { - // initial communication from external contact - $hash = Strings::getRandomHex(); - - $status = 0; - - DBA::delete('challenge', ["`expire` < ?", time()]); - - if ($type !== 'profile') { - q("INSERT INTO `challenge` ( `challenge`, `dfrn-id`, `expire` , `type`, `last_update` ) - VALUES( '%s', '%s', '%s', '%s', '%s' ) ", - DBA::escape($hash), - DBA::escape($dfrn_id), - intval(time() + 60 ), - DBA::escape($type), - DBA::escape($last_update) - ); - } - - $sql_extra = ''; - switch ($direction) { - case -1: - if ($type === 'profile') { - $sql_extra = sprintf(" AND (`dfrn-id` = '%s' OR `issued-id` = '%s') ", DBA::escape($dfrn_id), DBA::escape($dfrn_id)); - } else { - $sql_extra = sprintf(" AND `issued-id` = '%s' ", DBA::escape($dfrn_id)); - } - - $my_id = $dfrn_id; - break; - case 0: - $sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", DBA::escape($dfrn_id)); - $my_id = '1:' . $dfrn_id; - break; - case 1: - $sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", DBA::escape($dfrn_id)); - $my_id = '0:' . $dfrn_id; - break; - default: - DI::baseUrl()->redirect(); - break; // NOTREACHED - } - - $nickname = $a->argv[1]; - - $r = q("SELECT `contact`.*, `user`.`username`, `user`.`nickname` - FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid` - WHERE `contact`.`blocked` = 0 AND `contact`.`pending` = 0 - AND `user`.`nickname` = '%s' $sql_extra LIMIT 1", - DBA::escape($nickname) - ); - if (DBA::isResult($r)) { - $challenge = ''; - $encrypted_id = ''; - $id_str = $my_id . '.' . mt_rand(1000, 9999); - - if (($r[0]['duplex'] && strlen($r[0]['pubkey'])) || !strlen($r[0]['prvkey'])) { - openssl_public_encrypt($hash, $challenge, $r[0]['pubkey']); - openssl_public_encrypt($id_str, $encrypted_id, $r[0]['pubkey']); - } else { - openssl_private_encrypt($hash, $challenge, $r[0]['prvkey']); - openssl_private_encrypt($id_str, $encrypted_id, $r[0]['prvkey']); - } - - $challenge = bin2hex($challenge); - $encrypted_id = bin2hex($encrypted_id); - } else { - $status = 1; - $challenge = ''; - $encrypted_id = ''; - } - - if (($type === 'profile') && (strlen($sec))) { - // heluecht: I don't know why we don't fail immediately when the user or contact hadn't been found. - // Since it doesn't make sense to continue from this point on, we now fail here. This should be safe. - if (!DBA::isResult($r)) { - throw new \Friendica\Network\HTTPException\NotFoundException(); - } - - // URL reply - if ($dfrn_version < 2.2) { - $s = DI::httpRequest()->fetch($r[0]['poll'] - . '?dfrn_id=' . $encrypted_id - . '&type=profile-check' - . '&dfrn_version=' . DFRN_PROTOCOL_VERSION - . '&challenge=' . $challenge - . '&sec=' . $sec - ); - } else { - $s = DI::httpRequest()->post($r[0]['poll'], [ - 'dfrn_id' => $encrypted_id, - 'type' => 'profile-check', - 'dfrn_version' => DFRN_PROTOCOL_VERSION, - 'challenge' => $challenge, - 'sec' => $sec - ])->getBody(); - } - - Logger::log("dfrn_poll: sec profile: " . $s, Logger::DATA); - - if (strlen($s) && strstr($s, ' $xml]); - - Logger::log('dfrn_poll: secure profile: challenge: ' . $xml->challenge . ' expecting ' . $hash); - Logger::log('dfrn_poll: secure profile: sec: ' . $xml->sec . ' expecting ' . $sec); - - if (((int) $xml->status == 0) && ($xml->challenge == $hash) && ($xml->sec == $sec)) { - $_SESSION['authenticated'] = 1; - $_SESSION['visitor_id'] = $r[0]['id']; - $_SESSION['visitor_home'] = $r[0]['url']; - $_SESSION['visitor_handle'] = $r[0]['addr']; - $_SESSION['visitor_visiting'] = $r[0]['uid']; - $_SESSION['my_url'] = $r[0]['url']; - $_SESSION['remote_comment'] = $r[0]['subscribe']; - - Session::setVisitorsContacts(); - - if (!$quiet) { - info(DI::l10n()->t('%1$s welcomes %2$s', $r[0]['username'], $r[0]['name'])); - } - - // Visitors get 1 day session. - $session_id = session_id(); - $expire = time() + 86400; - q("UPDATE `session` SET `expire` = '%s' WHERE `sid` = '%s'", - DBA::escape($expire), - DBA::escape($session_id) - ); - } - } - - $profile = ((DBA::isResult($r) && $r[0]['nickname']) ? $r[0]['nickname'] : $nickname); - - switch ($destination_url) { - case 'profile': - DI::baseUrl()->redirect('profile/' . $profile . '/profile'); - break; - case 'photos': - DI::baseUrl()->redirect('photos/' . $profile); - break; - case 'status': - case '': - DI::baseUrl()->redirect('profile/' . $profile); - break; - default: - $appendix = (strstr($destination_url, '?') ? '&redir=1' : '?redir=1'); - DI::baseUrl()->redirect($destination_url . $appendix); - break; - } - // NOTREACHED - } else { - // XML reply - header("Content-type: text/xml"); - echo '' . "\r\n" - . '' . "\r\n" - . "\t" . '' . $status . '' . "\r\n" - . "\t" . '' . DFRN_PROTOCOL_VERSION . '' . "\r\n" - . "\t" . '' . $encrypted_id . '' . "\r\n" - . "\t" . '' . $challenge . '' . "\r\n" - . '' . "\r\n"; - exit(); - // NOTREACHED - } - } + throw new HTTPException\BadRequestException(); } diff --git a/mod/dfrn_request.php b/mod/dfrn_request.php deleted file mode 100644 index bdb5a8d2f..000000000 --- a/mod/dfrn_request.php +++ /dev/null @@ -1,653 +0,0 @@ -. - * - *Handles communication associated with the issuance of friend requests. - * - * @see PDF with dfrn specs: https://github.com/friendica/friendica/blob/stable/spec/dfrn2.pdf - * You also find a graphic which describes the confirmation process at - * https://github.com/friendica/friendica/blob/stable/spec/dfrn2_contact_request.png - */ - -use Friendica\App; -use Friendica\Core\Logger; -use Friendica\Core\Protocol; -use Friendica\Core\Renderer; -use Friendica\Core\Search; -use Friendica\Core\Session; -use Friendica\Core\System; -use Friendica\Database\DBA; -use Friendica\DI; -use Friendica\Model\Contact; -use Friendica\Model\Group; -use Friendica\Model\Notification; -use Friendica\Model\Profile; -use Friendica\Model\User; -use Friendica\Module\Security\Login; -use Friendica\Network\Probe; -use Friendica\Protocol\Activity; -use Friendica\Util\DateTimeFormat; -use Friendica\Util\Network; -use Friendica\Util\Strings; - -function dfrn_request_init(App $a) -{ - if ($a->argc > 1) { - $which = $a->argv[1]; - Profile::load($a, $which); - } - - return; -} - -/** - * Function: dfrn_request_post - * - * Purpose: - * Handles multiple scenarios. - * - * Scenario 1: - * Clicking 'submit' on a friend request page. - * - * Scenario 2: - * Following Scenario 1, we are brought back to our home site - * in order to link our friend request with our own server cell. - * After logging in, we click 'submit' to approve the linkage. - * - * @param App $a - * @throws ImagickException - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ -function dfrn_request_post(App $a) -{ - if ($a->argc != 2 || empty($a->profile)) { - Logger::log('Wrong count of argc or profiles: argc=' . $a->argc . ', profile()=' . count($a->profile ?? [])); - return; - } - - if (!empty($_POST['cancel'])) { - DI::baseUrl()->redirect(); - } - - /* - * Scenario 2: We've introduced ourself to another cell, then have been returned to our own cell - * to confirm the request, and then we've clicked submit (perhaps after logging in). - * That brings us here: - */ - if (!empty($_POST['localconfirm']) && ($_POST['localconfirm'] == 1)) { - // Ensure this is a valid request - if (local_user() && ($a->user['nickname'] == $a->argv[1]) && !empty($_POST['dfrn_url'])) { - $dfrn_url = Strings::escapeTags(trim($_POST['dfrn_url'])); - $aes_allow = !empty($_POST['aes_allow']); - $confirm_key = $_POST['confirm_key'] ?? ''; - $hidden = (!empty($_POST['hidden-contact']) ? intval($_POST['hidden-contact']) : 0); - $contact_record = null; - $blocked = 1; - $pending = 1; - - if (!empty($dfrn_url)) { - // Lookup the contact based on their URL (which is the only unique thing we have at the moment) - $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND NOT `self` LIMIT 1", - intval(local_user()), - DBA::escape(Strings::normaliseLink($dfrn_url)) - ); - - if (DBA::isResult($r)) { - if (strlen($r[0]['dfrn-id'])) { - // We don't need to be here. It has already happened. - notice(DI::l10n()->t("This introduction has already been accepted.")); - return; - } else { - $contact_record = $r[0]; - } - } - - if (is_array($contact_record)) { - $r = q("UPDATE `contact` SET `ret-aes` = %d, hidden = %d WHERE `id` = %d", - intval($aes_allow), - intval($hidden), - intval($contact_record['id']) - ); - } else { - // Scrape the other site's profile page to pick up the dfrn links, key, fn, and photo - $parms = Probe::profile($dfrn_url); - - if (!count($parms)) { - notice(DI::l10n()->t('Profile location is not valid or does not contain profile information.')); - return; - } else { - if (empty($parms['fn'])) { - notice(DI::l10n()->t('Warning: profile location has no identifiable owner name.')); - } - if (empty($parms['photo'])) { - notice(DI::l10n()->t('Warning: profile location has no profile photo.')); - } - $invalid = Probe::validDfrn($parms); - if ($invalid) { - notice(DI::l10n()->tt("%d required parameter was not found at the given location", "%d required parameters were not found at the given location", $invalid)); - return; - } - } - - $dfrn_request = $parms['dfrn-request']; - - $photo = $parms["photo"]; - - // Escape the entire array - DBA::escapeArray($parms); - - // Create a contact record on our site for the other person - $r = q("INSERT INTO `contact` ( `uid`, `created`,`url`, `nurl`, `addr`, `name`, `nick`, `photo`, `site-pubkey`, - `request`, `confirm`, `notify`, `poll`, `network`, `aes_allow`, `hidden`, `blocked`, `pending`) - VALUES ( %d, '%s', '%s', '%s', '%s', '%s' , '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d)", - intval(local_user()), - DateTimeFormat::utcNow(), - DBA::escape($dfrn_url), - DBA::escape(Strings::normaliseLink($dfrn_url)), - $parms['addr'], - $parms['fn'], - $parms['nick'], - $parms['photo'], - $parms['key'], - $parms['dfrn-request'], - $parms['dfrn-confirm'], - $parms['dfrn-notify'], - $parms['dfrn-poll'], - DBA::escape(Protocol::DFRN), - intval($aes_allow), - intval($hidden), - intval($blocked), - intval($pending) - ); - } - - if ($r) { - info(DI::l10n()->t("Introduction complete.")); - } - - $r = q("SELECT `id`, `network` FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `site-pubkey` = '%s' LIMIT 1", - intval(local_user()), - DBA::escape($dfrn_url), - $parms['key'] ?? '' // Potentially missing - ); - if (DBA::isResult($r)) { - Group::addMember(User::getDefaultGroup(local_user(), $r[0]["network"]), $r[0]['id']); - - if (isset($photo)) { - Contact::updateAvatar($r[0]["id"], $photo, true); - } - - $forward_path = "contact/" . $r[0]['id']; - } else { - $forward_path = "contact"; - } - - // Allow the blocked remote notification to complete - if (is_array($contact_record)) { - $dfrn_request = $contact_record['request']; - } - - if (!empty($dfrn_request) && strlen($confirm_key)) { - DI::httpRequest()->fetch($dfrn_request . '?confirm_key=' . $confirm_key); - } - - // (ignore reply, nothing we can do it failed) - DI::baseUrl()->redirect($forward_path); - return; // NOTREACHED - } - } - - // invalid/bogus request - notice(DI::l10n()->t('Unrecoverable protocol error.')); - DI::baseUrl()->redirect(); - return; // NOTREACHED - } - - /* - * Otherwise: - * - * Scenario 1: - * We are the requestee. A person from a remote cell has made an introduction - * on our profile web page and clicked submit. We will use their DFRN-URL to - * figure out how to contact their cell. - * - * Scrape the originating DFRN-URL for everything we need. Create a contact record - * and an introduction to show our user next time he/she logs in. - * Finally redirect back to the requestor so that their site can record the request. - * If our user (the requestee) later confirms this request, a record of it will need - * to exist on the requestor's cell in order for the confirmation process to complete.. - * - * It's possible that neither the requestor or the requestee are logged in at the moment, - * and the requestor does not yet have any credentials to the requestee profile. - * - * Who is the requestee? We've already loaded their profile which means their nickname should be - * in $a->argv[1] and we should have their complete info in $a->profile. - * - */ - if (empty($a->profile['uid'])) { - notice(DI::l10n()->t('Profile unavailable.')); - return; - } - - $nickname = $a->profile['nickname']; - $uid = $a->profile['uid']; - $maxreq = intval($a->profile['maxreq']); - $contact_record = null; - $failed = false; - $parms = null; - $blocked = 1; - $pending = 1; - - if (!empty($_POST['dfrn_url'])) { - // Block friend request spam - if ($maxreq) { - $r = q("SELECT * FROM `intro` WHERE `datetime` > '%s' AND `uid` = %d", - DBA::escape(DateTimeFormat::utc('now - 24 hours')), - intval($uid) - ); - if (DBA::isResult($r) && count($r) > $maxreq) { - notice(DI::l10n()->t('%s has received too many connection requests today.', $a->profile['name'])); - notice(DI::l10n()->t('Spam protection measures have been invoked.')); - notice(DI::l10n()->t('Friends are advised to please try again in 24 hours.')); - return; - } - } - - /* Cleanup old introductions that remain blocked. - * Also remove the contact record, but only if there is no existing relationship - */ - $r = q("SELECT `intro`.*, `intro`.`id` AS `iid`, `contact`.`id` AS `cid`, `contact`.`rel` - FROM `intro` LEFT JOIN `contact` on `intro`.`contact-id` = `contact`.`id` - WHERE `intro`.`blocked` = 1 AND `contact`.`self` = 0 - AND `intro`.`datetime` < UTC_TIMESTAMP() - INTERVAL 30 MINUTE " - ); - if (DBA::isResult($r)) { - foreach ($r as $rr) { - if (!$rr['rel']) { - DBA::delete('contact', ['id' => $rr['cid'], 'self' => false]); - } - DBA::delete('intro', ['id' => $rr['iid']]); - } - } - - $url = trim($_POST['dfrn_url']); - if (!strlen($url)) { - notice(DI::l10n()->t("Invalid locator")); - return; - } - - $hcard = ''; - - // Detect the network - $data = Contact::getByURL($url); - $network = $data["network"]; - - // Canonicalize email-style profile locator - $url = Probe::webfingerDfrn($data['url'] ?? $url, $hcard); - - if (substr($url, 0, 5) === 'stat:') { - // Every time we detect the remote subscription we define this as OStatus. - // We do this even if it is not OStatus. - // we only need to pass this through another section of the code. - if ($network != Protocol::DIASPORA) { - $network = Protocol::OSTATUS; - } - - $url = substr($url, 5); - } else { - $network = Protocol::DFRN; - } - - Logger::log('dfrn_request: url: ' . $url . ',network=' . $network, Logger::DEBUG); - - if ($network === Protocol::DFRN) { - $ret = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `self` = 0 LIMIT 1", - intval($uid), - DBA::escape($url) - ); - - if (DBA::isResult($ret)) { - if (strlen($ret[0]['issued-id'])) { - notice(DI::l10n()->t('You have already introduced yourself here.')); - return; - } elseif ($ret[0]['rel'] == Contact::FRIEND) { - notice(DI::l10n()->t('Apparently you are already friends with %s.', $a->profile['name'])); - return; - } else { - $contact_record = $ret[0]; - $parms = ['dfrn-request' => $ret[0]['request']]; - } - } - - $issued_id = Strings::getRandomHex(); - - if (is_array($contact_record)) { - // There is a contact record but no issued-id, so this - // is a reciprocal introduction from a known contact - $r = q("UPDATE `contact` SET `issued-id` = '%s' WHERE `id` = %d", - DBA::escape($issued_id), - intval($contact_record['id']) - ); - } else { - $url = Network::isUrlValid($url); - if (!$url) { - notice(DI::l10n()->t('Invalid profile URL.')); - DI::baseUrl()->redirect(DI::args()->getCommand()); - return; // NOTREACHED - } - - if (!Network::isUrlAllowed($url)) { - notice(DI::l10n()->t('Disallowed profile URL.')); - DI::baseUrl()->redirect(DI::args()->getCommand()); - return; // NOTREACHED - } - - if (Network::isUrlBlocked($url)) { - notice(DI::l10n()->t('Blocked domain')); - DI::baseUrl()->redirect(DI::args()->getCommand()); - return; // NOTREACHED - } - - $parms = Probe::profile(($hcard) ? $hcard : $url); - - if (!count($parms)) { - notice(DI::l10n()->t('Profile location is not valid or does not contain profile information.')); - DI::baseUrl()->redirect(DI::args()->getCommand()); - } else { - if (empty($parms['fn'])) { - notice(DI::l10n()->t('Warning: profile location has no identifiable owner name.')); - } - if (empty($parms['photo'])) { - notice(DI::l10n()->t('Warning: profile location has no profile photo.')); - } - $invalid = Probe::validDfrn($parms); - if ($invalid) { - notice(DI::l10n()->tt("%d required parameter was not found at the given location", "%d required parameters were not found at the given location", $invalid)); - - return; - } - } - - $parms['url'] = $url; - $parms['issued-id'] = $issued_id; - $photo = $parms["photo"]; - - DBA::escapeArray($parms); - $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `name`, `nick`, `issued-id`, `photo`, `site-pubkey`, - `request`, `confirm`, `notify`, `poll`, `network`, `blocked`, `pending` ) - VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d )", - intval($uid), - DBA::escape(DateTimeFormat::utcNow()), - $parms['url'], - DBA::escape(Strings::normaliseLink($url)), - $parms['addr'], - $parms['fn'], - $parms['nick'], - $parms['issued-id'], - $parms['photo'], - $parms['key'], - $parms['dfrn-request'], - $parms['dfrn-confirm'], - $parms['dfrn-notify'], - $parms['dfrn-poll'], - DBA::escape(Protocol::DFRN), - intval($blocked), - intval($pending) - ); - - // find the contact record we just created - if ($r) { - $r = q("SELECT `id` FROM `contact` - WHERE `uid` = %d AND `url` = '%s' AND `issued-id` = '%s' LIMIT 1", - intval($uid), - $parms['url'], - $parms['issued-id'] - ); - if (DBA::isResult($r)) { - $contact_record = $r[0]; - Contact::updateAvatar($contact_record["id"], $photo, true); - } - } - } - if ($r === false) { - notice(DI::l10n()->t('Failed to update contact record.')); - return; - } - - $hash = Strings::getRandomHex() . (string) time(); // Generate a confirm_key - - if (is_array($contact_record)) { - q("INSERT INTO `intro` ( `uid`, `contact-id`, `blocked`, `knowyou`, `note`, `hash`, `datetime`) - VALUES ( %d, %d, 1, %d, '%s', '%s', '%s' )", - intval($uid), - intval($contact_record['id']), - intval(!empty($_POST['knowyou'])), - DBA::escape(Strings::escapeTags(trim($_POST['dfrn-request-message'] ?? ''))), - DBA::escape($hash), - DBA::escape(DateTimeFormat::utcNow()) - ); - } - - // This notice will only be seen by the requestor if the requestor and requestee are on the same server. - if (!$failed) { - info(DI::l10n()->t('Your introduction has been sent.')); - } - - // "Homecoming" - send the requestor back to their site to record the introduction. - $dfrn_url = bin2hex(DI::baseUrl()->get() . '/profile/' . $nickname); - $aes_allow = ((function_exists('openssl_encrypt')) ? 1 : 0); - - System::externalRedirect($parms['dfrn-request'] . "?dfrn_url=$dfrn_url" - . '&dfrn_version=' . DFRN_PROTOCOL_VERSION - . '&confirm_key=' . $hash - . (($aes_allow) ? "&aes_allow=1" : "") - ); - // NOTREACHED - // END $network === Protocol::DFRN - } elseif (($network != Protocol::PHANTOM) && ($url != "")) { - - /* Substitute our user's feed URL into $url template - * Send the subscriber home to subscribe - */ - // Diaspora needs the uri in the format user@domain.tld - // Diaspora will support the remote subscription in a future version - if ($network == Protocol::DIASPORA) { - $uri = urlencode($a->profile['addr']); - } else { - $uri = urlencode($a->profile['url']); - } - - $url = str_replace('{uri}', $uri, $url); - System::externalRedirect($url); - // NOTREACHED - // END $network != Protocol::PHANTOM - } else { - notice(DI::l10n()->t("Remote subscription can't be done for your network. Please subscribe directly on your system.")); - return; - } - } return; -} - -function dfrn_request_content(App $a) -{ - if ($a->argc != 2 || empty($a->profile)) { - return ""; - } - - // "Homecoming". Make sure we're logged in to this site as the correct user. Then offer a confirm button - // to send us to the post section to record the introduction. - if (!empty($_GET['dfrn_url'])) { - if (!local_user()) { - info(DI::l10n()->t("Please login to confirm introduction.")); - /* setup the return URL to come back to this page if they use openid */ - return Login::form(); - } - - // Edge case, but can easily happen in the wild. This person is authenticated, - // but not as the person who needs to deal with this request. - if ($a->user['nickname'] != $a->argv[1]) { - notice(DI::l10n()->t("Incorrect identity currently logged in. Please login to this profile.")); - return Login::form(); - } - - $dfrn_url = Strings::escapeTags(trim(hex2bin($_GET['dfrn_url']))); - $aes_allow = !empty($_GET['aes_allow']); - $confirm_key = $_GET['confirm_key'] ?? ''; - - // Checking fastlane for validity - if (!empty($_SESSION['fastlane']) && (Strings::normaliseLink($_SESSION["fastlane"]) == Strings::normaliseLink($dfrn_url))) { - $_POST["dfrn_url"] = $dfrn_url; - $_POST["confirm_key"] = $confirm_key; - $_POST["localconfirm"] = 1; - $_POST["hidden-contact"] = 0; - $_POST["submit"] = DI::l10n()->t('Confirm'); - - dfrn_request_post($a); - - exit(); - } - - $tpl = Renderer::getMarkupTemplate("dfrn_req_confirm.tpl"); - $o = Renderer::replaceMacros($tpl, [ - '$dfrn_url' => $dfrn_url, - '$aes_allow' => (($aes_allow) ? '' : "" ), - '$hidethem' => DI::l10n()->t('Hide this contact'), - '$confirm_key' => $confirm_key, - '$welcome' => DI::l10n()->t('Welcome home %s.', $a->user['username']), - '$please' => DI::l10n()->t('Please confirm your introduction/connection request to %s.', $dfrn_url), - '$submit' => DI::l10n()->t('Confirm'), - '$uid' => $_SESSION['uid'], - '$nickname' => $a->user['nickname'], - 'dfrn_rawurl' => $_GET['dfrn_url'] - ]); - return $o; - } elseif (!empty($_GET['confirm_key'])) { - // we are the requestee and it is now safe to send our user their introduction, - // We could just unblock it, but first we have to jump through a few hoops to - // send an email, or even to find out if we need to send an email. - $intro = q("SELECT * FROM `intro` WHERE `hash` = '%s' LIMIT 1", - DBA::escape($_GET['confirm_key']) - ); - - if (DBA::isResult($intro)) { - $r = q("SELECT `contact`.*, `user`.* FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid` - WHERE `contact`.`id` = %d LIMIT 1", - intval($intro[0]['contact-id']) - ); - - $auto_confirm = false; - - if (DBA::isResult($r)) { - if ($r[0]['page-flags'] != User::PAGE_FLAGS_NORMAL && $r[0]['page-flags'] != User::PAGE_FLAGS_PRVGROUP) { - $auto_confirm = true; - } - - if (!$auto_confirm) { - notification([ - 'type' => Notification\Type::INTRO, - 'otype' => Notification\ObjectType::INTRO, - 'verb' => Activity::REQ_FRIEND, - 'uid' => $r[0]['uid'], - 'cid' => $r[0]['id'], - 'link' => DI::baseUrl() . '/notifications/intros', - ]); - } - - if ($auto_confirm) { - require_once 'mod/dfrn_confirm.php'; - $handsfree = [ - 'uid' => $r[0]['uid'], - 'node' => $r[0]['nickname'], - 'dfrn_id' => $r[0]['issued-id'], - 'intro_id' => $intro[0]['id'], - 'duplex' => (($r[0]['page-flags'] == User::PAGE_FLAGS_FREELOVE) ? 1 : 0), - ]; - dfrn_confirm_post($a, $handsfree); - } - } - - if (!$auto_confirm) { - - // If we are auto_confirming, this record will have already been nuked - // in dfrn_confirm_post() - - q("UPDATE `intro` SET `blocked` = 0 WHERE `hash` = '%s'", - DBA::escape($_GET['confirm_key']) - ); - } - } - - exit(); - } else { - // Normal web request. Display our user's introduction form. - if (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) { - if (!DI::config()->get('system', 'local_block')) { - notice(DI::l10n()->t('Public access denied.')); - return; - } - } - - // Try to auto-fill the profile address - // At first look if an address was provided - // Otherwise take the local address - if (!empty($_GET['addr'])) { - $myaddr = hex2bin($_GET['addr']); - } elseif (!empty($_GET['address'])) { - $myaddr = $_GET['address']; - } elseif (local_user()) { - if (strlen(DI::baseUrl()->getUrlPath())) { - $myaddr = DI::baseUrl() . '/profile/' . $a->user['nickname']; - } else { - $myaddr = $a->user['nickname'] . '@' . substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3); - } - } else { - // last, try a zrl - $myaddr = Profile::getMyURL(); - } - - $target_addr = $a->profile['nickname'] . '@' . substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3); - - /* The auto_request form only has the profile address - * because nobody is going to read the comments and - * it doesn't matter if they know you or not. - */ - if ($a->profile['page-flags'] == User::PAGE_FLAGS_NORMAL) { - $tpl = Renderer::getMarkupTemplate('dfrn_request.tpl'); - } else { - $tpl = Renderer::getMarkupTemplate('auto_request.tpl'); - } - - $o = Renderer::replaceMacros($tpl, [ - '$header' => DI::l10n()->t('Friend/Connection Request'), - '$page_desc' => DI::l10n()->t('Enter your Webfinger address (user@domain.tld) or profile URL here. If this isn\'t supported by your system (for example it doesn\'t work with Diaspora), you have to subscribe to %s directly on your system', $target_addr), - '$invite_desc' => DI::l10n()->t('If you are not yet a member of the free social web, follow this link to find a public Friendica node and join us today.', Search::getGlobalDirectory() . '/servers'), - '$your_address' => DI::l10n()->t('Your Webfinger address or profile URL:'), - '$pls_answer' => DI::l10n()->t('Please answer the following:'), - '$submit' => DI::l10n()->t('Submit Request'), - '$cancel' => DI::l10n()->t('Cancel'), - - '$request' => 'dfrn_request/' . $a->argv[1], - '$name' => $a->profile['name'], - '$myaddr' => $myaddr, - - '$does_know_you' => ['knowyou', DI::l10n()->t('%s knows you', $a->profile['name'])], - '$addnote_field' => ['dfrn-request-message', DI::l10n()->t('Add a personal note:')], - ]); - return $o; - } -} diff --git a/mod/follow.php b/mod/follow.php index ed8e3aa5f..ac74d217b 100644 --- a/mod/follow.php +++ b/mod/follow.php @@ -118,18 +118,13 @@ function follow_content(App $a) $contact['url'] = $contact['addr']; } - if (($protocol === Protocol::DFRN) && !DBA::isResult($contact)) { - $request = $contact['request']; - $tpl = Renderer::getMarkupTemplate('dfrn_request.tpl'); - } else { - if (!empty($_REQUEST['auto'])) { - follow_process($a, $contact['url']); - } - - $request = DI::baseUrl() . '/follow'; - $tpl = Renderer::getMarkupTemplate('auto_request.tpl'); + if (!empty($_REQUEST['auto'])) { + follow_process($a, $contact['url']); } + $request = DI::baseUrl() . '/follow'; + $tpl = Renderer::getMarkupTemplate('auto_request.tpl'); + $owner = User::getOwnerDataById($uid); if (empty($owner)) { notice(DI::l10n()->t('Permission denied.')); @@ -139,9 +134,6 @@ function follow_content(App $a) $myaddr = $owner['url']; - // Makes the connection request for friendica contacts easier - $_SESSION['fastlane'] = $contact['url']; - $o = Renderer::replaceMacros($tpl, [ '$header' => DI::l10n()->t('Connect/Follow'), '$pls_answer' => DI::l10n()->t('Please answer the following:'), @@ -182,10 +174,6 @@ function follow_process(App $a, string $url) { $return_path = 'follow?url=' . urlencode($url); - // Makes the connection request for friendica contacts easier - // This is just a precaution if maybe this page is called somewhere directly via POST - $_SESSION['fastlane'] = $url; - $result = Contact::createFromProbe($a->user, $url, true); if ($result['success'] == false) { diff --git a/mod/redir.php b/mod/redir.php index 71db74e9a..2f97911dc 100644 --- a/mod/redir.php +++ b/mod/redir.php @@ -50,7 +50,7 @@ function redir_init(App $a) { throw new \Friendica\Network\HTTPException\BadRequestException(DI::l10n()->t('Bad Request.')); } - $fields = ['id', 'uid', 'nurl', 'url', 'addr', 'name', 'network', 'poll', 'issued-id', 'dfrn-id', 'duplex', 'pending']; + $fields = ['id', 'uid', 'nurl', 'url', 'addr', 'name']; $contact = DBA::selectFirst('contact', $fields, ['id' => $cid, 'uid' => [0, local_user()]]); if (!DBA::isResult($contact)) { throw new \Friendica\Network\HTTPException\NotFoundException(DI::l10n()->t('Contact not found.')); @@ -99,33 +99,6 @@ function redir_init(App $a) { } } - // Doing remote auth with dfrn. - if (local_user() && (!empty($contact['dfrn-id']) || !empty($contact['issued-id'])) && empty($contact['pending'])) { - $dfrn_id = $orig_id = (($contact['issued-id']) ? $contact['issued-id'] : $contact['dfrn-id']); - - if ($contact['duplex'] && $contact['issued-id']) { - $orig_id = $contact['issued-id']; - $dfrn_id = '1:' . $orig_id; - } - if ($contact['duplex'] && $contact['dfrn-id']) { - $orig_id = $contact['dfrn-id']; - $dfrn_id = '0:' . $orig_id; - } - - $sec = Strings::getRandomHex(); - - $fields = ['uid' => local_user(), 'cid' => $cid, 'dfrn_id' => $dfrn_id, - 'sec' => $sec, 'expire' => time() + 45]; - DBA::insert('profile_check', $fields); - - Logger::log('mod_redir: ' . $contact['name'] . ' ' . $sec, Logger::DEBUG); - - $dest = (!empty($url) ? '&destination_url=' . $url : ''); - - System::externalRedirect($contact['poll'] . '?dfrn_id=' . $dfrn_id - . '&dfrn_version=' . DFRN_PROTOCOL_VERSION . '&type=profile&sec=' . $sec . $dest . $quiet); - } - if (empty($url)) { throw new \Friendica\Network\HTTPException\BadRequestException(DI::l10n()->t('Bad Request.')); } diff --git a/mod/unfollow.php b/mod/unfollow.php index aca17727b..97cb0e1a3 100644 --- a/mod/unfollow.php +++ b/mod/unfollow.php @@ -84,9 +84,6 @@ function unfollow_content(App $a) // NOTREACHED } - // Makes the connection request for friendica contacts easier - $_SESSION['fastlane'] = $contact['url']; - if (!empty($_REQUEST['auto'])) { unfollow_process($contact['url']); } diff --git a/src/Database/DBStructure.php b/src/Database/DBStructure.php index 477948728..a6d4061a7 100644 --- a/src/Database/DBStructure.php +++ b/src/Database/DBStructure.php @@ -81,7 +81,7 @@ class DBStructure $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']; + 'deliverq', 'dsprphotoq', 'ffinder', 'sign', 'spam', 'term', 'user-item', 'thread', 'item', 'challenge']; $tables = DBA::selectToArray(['INFORMATION_SCHEMA' => 'TABLES'], ['TABLE_NAME'], ['TABLE_SCHEMA' => DBA::databaseName(), 'TABLE_TYPE' => 'BASE TABLE']); diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 0e3de02d6..ab8a7db8c 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -811,13 +811,11 @@ class Contact } $protocol = $contact['network']; - if (($protocol == Protocol::DFRN) && !self::isLegacyDFRNContact($contact)) { - $protocol = Protocol::ACTIVITYPUB; + if (($protocol == Protocol::DFRN) && !empty($contact['protocol'])) { + $protocol = $contact['protocol']; } - if (($protocol == Protocol::DFRN) && $dissolve) { - DFRN::deliver($user, $contact, 'placeholder', true); - } elseif (in_array($protocol, [Protocol::OSTATUS, Protocol::DFRN])) { + if (in_array($protocol, [Protocol::OSTATUS, Protocol::DFRN])) { // create an unfollow slap $item = []; $item['verb'] = Activity::O_UNFOLLOW; @@ -2273,18 +2271,6 @@ class Contact return $id; } - /** - * Detects if a given contact array belongs to a legacy DFRN connection - * - * @param array $contact - * @return boolean - */ - public static function isLegacyDFRNContact($contact) - { - // Newer Friendica contacts are connected via AP, then these fields aren't set - return !empty($contact['dfrn-id']) || !empty($contact['issued-id']); - } - /** * Detects the communication protocol for a given contact url. * This is used to detect Friendica contacts that we can communicate via AP. @@ -2387,24 +2373,6 @@ class Contact $protocol = self::getProtocol($ret['url'], $ret['network']); - if (($protocol === Protocol::DFRN) && !DBA::isResult($contact)) { - if ($interactive) { - if (strlen(DI::baseUrl()->getUrlPath())) { - $myaddr = bin2hex(DI::baseUrl() . '/profile/' . $user['nickname']); - } else { - $myaddr = bin2hex($user['nickname'] . '@' . DI::baseUrl()->getHostname()); - } - - DI::baseUrl()->redirect($ret['request'] . "&addr=$myaddr"); - - // NOTREACHED - } - } elseif (DI::config()->get('system', 'dfrn_only') && ($ret['network'] != Protocol::DFRN)) { - $result['message'] = DI::l10n()->t('This site is not configured to allow communications with other networks.') . EOL; - $result['message'] .= DI::l10n()->t('No compatible communication protocols or feeds were discovered.') . EOL; - return $result; - } - // This extra param just confuses things, remove it if ($protocol === Protocol::DIASPORA) { $ret['url'] = str_replace('?absolute=true', '', $ret['url']); diff --git a/src/Model/Conversation.php b/src/Model/Conversation.php index 742d5230a..aa2faca50 100644 --- a/src/Model/Conversation.php +++ b/src/Model/Conversation.php @@ -38,7 +38,7 @@ class Conversation const PARCEL_SALMON = 3; const PARCEL_FEED = 4; // Deprecated const PARCEL_SPLIT_CONVERSATION = 6; - const PARCEL_LEGACY_DFRN = 7; + const PARCEL_LEGACY_DFRN = 7; // Deprecated const PARCEL_DIASPORA_DFRN = 8; const PARCEL_LOCAL_DFRN = 9; const PARCEL_DIRECT = 10; diff --git a/src/Module/Notifications/Introductions.php b/src/Module/Notifications/Introductions.php index 5ce9eb86f..878195931 100644 --- a/src/Module/Notifications/Introductions.php +++ b/src/Module/Notifications/Introductions.php @@ -135,13 +135,7 @@ class Introductions extends BaseNotifications $friend = ['duplex', DI::l10n()->t('Friend'), '1', $helptext2, true]; $follower = ['duplex', DI::l10n()->t('Subscriber'), '0', $helptext3, false]; - $contact = DBA::selectFirst('contact', ['network', 'protocol'], ['id' => $notification->getContactId()]); - - if (($contact['network'] != Protocol::DFRN) || ($contact['protocol'] == Protocol::ACTIVITYPUB)) { - $action = 'follow_confirm'; - } else { - $action = 'dfrn_confirm'; - } + $action = 'follow_confirm'; $header = $notification->getName(); diff --git a/src/Object/Notification/Introduction.php b/src/Object/Notification/Introduction.php index bb13ce3f2..2a2d90cda 100644 --- a/src/Object/Notification/Introduction.php +++ b/src/Object/Notification/Introduction.php @@ -286,7 +286,7 @@ class Introduction implements \JsonSerializable $this->knowYou = $data['knowYou'] ?? false; $this->note = $data['note'] ?? ''; $this->request = $data['request'] ?? ''; - $this->dfrnId = $data['dfrn_id'] ?? -1; + $this->dfrnId = -1; $this->addr = $data['addr'] ?? ''; $this->network = $data['network'] ?? ''; $this->uid = $data['uid'] ?? -1; diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index faae900b3..61f5bab62 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -23,9 +23,7 @@ namespace Friendica\Protocol; use DOMDocument; use DOMXPath; -use Friendica\App\BaseURL; use Friendica\Content\Text\BBCode; -use Friendica\Core\Hook; use Friendica\Core\Logger; use Friendica\Core\Protocol; use Friendica\Database\DBA; @@ -39,13 +37,10 @@ use Friendica\Model\Item; use Friendica\Model\ItemURI; use Friendica\Model\Mail; use Friendica\Model\Notification; -use Friendica\Model\PermissionSet; use Friendica\Model\Post; -use Friendica\Model\Post\Category; use Friendica\Model\Profile; use Friendica\Model\Tag; use Friendica\Model\User; -use Friendica\Model\Verb; use Friendica\Network\Probe; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; @@ -145,172 +140,6 @@ class DFRN return trim($doc->saveXML()); } - /** - * Generate an atom feed for the given user - * - * This function is called when another server is pulling data from the user feed. - * - * @param string $dfrn_id DFRN ID from the requesting party - * @param string $owner_nick Owner nick name - * @param string $last_update Date of the last update - * @param int $direction Can be -1, 0 or 1. - * @param boolean $onlyheader Output only the header without content? (Default is "no") - * - * @return string DFRN feed entries - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function feed($dfrn_id, $owner_nick, $last_update, $direction = 0, $onlyheader = false) - { - $a = DI::app(); - - $sitefeed = ((strlen($owner_nick)) ? false : true); // not yet implemented, need to rewrite huge chunks of following logic - $public_feed = (($dfrn_id) ? false : true); - $starred = false; // not yet implemented, possible security issues - $converse = false; - - if ($public_feed && $a->argc > 2) { - for ($x = 2; $x < $a->argc; $x++) { - if ($a->argv[$x] == 'converse') { - $converse = true; - } - if ($a->argv[$x] == 'starred') { - $starred = true; - } - if ($a->argv[$x] == 'category' && $a->argc > ($x + 1) && strlen($a->argv[$x+1])) { - $category = $a->argv[$x+1]; - } - } - } - - // default permissions - anonymous user - - $sql_extra = sprintf(" AND `private` != %s ", Item::PRIVATE); - - $owner = DBA::selectFirst('owner-view', [], ['nickname' => $owner_nick]); - if (!DBA::isResult($owner)) { - Logger::log(sprintf('No contact found for nickname=%d', $owner_nick), Logger::WARNING); - exit(); - } - - $owner_id = $owner['uid']; - - if (!$public_feed) { - switch ($direction) { - case (-1): - $sql_extra = sprintf(" AND `issued-id` = '%s' ", DBA::escape($dfrn_id)); - break; - case 0: - $sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", DBA::escape($dfrn_id)); - break; - case 1: - $sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", DBA::escape($dfrn_id)); - break; - default: - return false; - break; // NOTREACHED - } - - $contact = DBA::selectFirst('contact', [], ["NOT `blocked` AND `contact`.`uid` = ?" . $sql_extra, $owner_id]); - if (!DBA::isResult($contact)) { - Logger::notice('No contact found', ['uid' => $owner_id]); - exit(); - } - - $set = PermissionSet::get($owner_id, $contact['id']); - - if (!empty($set)) { - $sql_extra = " AND `psid` IN (" . implode(',', $set) .")"; - } else { - $sql_extra = sprintf(" AND `private` != %s", Item::PRIVATE); - } - } - - if (!strlen($last_update)) { - $last_update = 'now -30 days'; - } - - if (isset($category)) { - $sql_extra .= sprintf(" AND `uri-id` IN (SELECT `uri-id` FROM `category-view` WHERE `name` = '%s' AND `type` = %d AND `uid` = %d)", - DBA::escape(Strings::protectSprintf($category)), intval(Category::CATEGORY), intval($owner_id)); - } - - if ($public_feed && ! $converse) { - $sql_extra .= " AND `self` "; - } - - $check_date = DateTimeFormat::utc($last_update); - - $condition = ["`uid` = ? AND `wall` AND `changed` > ? AND `vid` != ? AND `visible`" . $sql_extra, - $owner_id, $check_date, Verb::getID(Activity::ANNOUNCE)]; - - $params = ['sort' => ['parent' => $public_feed, 'received']]; - $items = Post::selectToArray(Item::DELIVER_FIELDLIST, $condition, $params, ['limit' => 300]); - - /* - * Will check further below if this actually returned results. - * We will provide an empty feed if that is the case. - */ - - $doc = new DOMDocument('1.0', 'utf-8'); - $doc->formatOutput = true; - - $alternatelink = $owner['url']; - - if (isset($category)) { - $alternatelink .= "/category/".$category; - } - - if ($public_feed) { - $author = "dfrn:owner"; - } else { - $author = "author"; - } - - $root = self::addHeader($doc, $owner, $author, $alternatelink, true); - - /// @TODO This hook can't work anymore - // \Friendica\Core\Hook::callAll('atom_feed', $atom); - - if (!DBA::isResult($items) || $onlyheader) { - $atom = trim($doc->saveXML()); - - Hook::callAll('atom_feed_end', $atom); - - return $atom; - } - - foreach ($items as $item) { - // prevent private email from leaking. - if ($item['network'] == Protocol::MAIL) { - continue; - } - - // public feeds get html, our own nodes use bbcode - - if ($public_feed) { - $type = 'html'; - // catch any email that's in a public conversation and make sure it doesn't leak - if ($item['private'] == Item::PRIVATE) { - continue; - } - } else { - $type = 'text'; - } - - $entry = self::entry($doc, $type, $item, $owner, true); - if (isset($entry)) { - $root->appendChild($entry); - } - } - - $atom = trim($doc->saveXML()); - - Hook::callAll('atom_feed_end', $atom); - - return $atom; - } - /** * Generate an atom entry for a given uri id and user * @@ -1089,268 +918,6 @@ class DFRN return $entry; } - /** - * encrypts data via AES - * - * @param string $data The data that is to be encrypted - * @param string $key The AES key - * - * @return string encrypted data - */ - private static function aesEncrypt($data, $key) - { - return openssl_encrypt($data, 'aes-128-ecb', $key, OPENSSL_RAW_DATA); - } - - /** - * decrypts data via AES - * - * @param string $encrypted The encrypted data - * @param string $key The AES key - * - * @return string decrypted data - */ - public static function aesDecrypt($encrypted, $key) - { - return openssl_decrypt($encrypted, 'aes-128-ecb', $key, OPENSSL_RAW_DATA); - } - - /** - * Delivers the atom content to the contacts - * - * @param array $owner Owner record - * @param array $contact Contact record of the receiver - * @param string $atom Content that will be transmitted - * @param bool $dissolve (to be documented) - * - * @return int Deliver status. Negative values mean an error. - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - * @todo Add array type-hint for $owner, $contact - */ - public static function deliver($owner, $contact, $atom, $dissolve = false) - { - $idtosend = $orig_id = (($contact['dfrn-id']) ? $contact['dfrn-id'] : $contact['issued-id']); - - if ($contact['duplex'] && $contact['dfrn-id']) { - $idtosend = '0:' . $orig_id; - } - if ($contact['duplex'] && $contact['issued-id']) { - $idtosend = '1:' . $orig_id; - } - - $rino = DI::config()->get('system', 'rino_encrypt'); - $rino = intval($rino); - - Logger::log("Local rino version: ". $rino, Logger::DEBUG); - - $ssl_val = intval(DI::config()->get('system', 'ssl_policy')); - - switch ($ssl_val) { - case BaseURL::SSL_POLICY_FULL: - $ssl_policy = 'full'; - break; - case BaseURL::SSL_POLICY_SELFSIGN: - $ssl_policy = 'self'; - break; - case BaseURL::SSL_POLICY_NONE: - default: - $ssl_policy = 'none'; - break; - } - - $url = $contact['notify'] . '&dfrn_id=' . $idtosend . '&dfrn_version=' . DFRN_PROTOCOL_VERSION . (($rino) ? '&rino='.$rino : ''); - - Logger::log('dfrn_deliver: ' . $url); - - $curlResult = DI::httpRequest()->get($url); - - if ($curlResult->isTimeout()) { - return -2; // timed out - } - - $xml = $curlResult->getBody(); - - $curl_stat = $curlResult->getReturnCode(); - if (empty($curl_stat)) { - return -3; // timed out - } - - Logger::log('dfrn_deliver: ' . $xml, Logger::DATA); - - if (empty($xml)) { - return 3; - } - - if (strpos($xml, 'status) != 0) || !strlen($res->challenge) || !strlen($res->dfrn_id)) { - if (empty($res->status)) { - $status = 3; - } else { - $status = $res->status; - } - - return $status; - } - - $postvars = []; - $sent_dfrn_id = hex2bin((string) $res->dfrn_id); - $challenge = hex2bin((string) $res->challenge); - $perm = (($res->perm) ? $res->perm : null); - $dfrn_version = floatval($res->dfrn_version ?: 2.0); - $rino_remote_version = intval($res->rino); - $page = (($owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY) ? 1 : 0); - - Logger::log("Remote rino version: ".$rino_remote_version." for ".$contact["url"], Logger::DEBUG); - - if ($owner['page-flags'] == User::PAGE_FLAGS_PRVGROUP) { - $page = 2; - } - - $final_dfrn_id = ''; - - if ($perm) { - if ((($perm == 'rw') && !intval($contact['writable'])) - || (($perm == 'r') && intval($contact['writable'])) - ) { - DBA::update('contact', ['writable' => ($perm == 'rw')], ['id' => $contact['id']]); - - $contact['writable'] = (string) 1 - intval($contact['writable']); - } - } - - if (($contact['duplex'] && strlen($contact['pubkey'])) - || ($owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY && strlen($contact['pubkey'])) - || ($contact['rel'] == Contact::SHARING && strlen($contact['pubkey'])) - ) { - openssl_public_decrypt($sent_dfrn_id, $final_dfrn_id, $contact['pubkey']); - openssl_public_decrypt($challenge, $postvars['challenge'], $contact['pubkey']); - } else { - openssl_private_decrypt($sent_dfrn_id, $final_dfrn_id, $contact['prvkey']); - openssl_private_decrypt($challenge, $postvars['challenge'], $contact['prvkey']); - } - - $final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.')); - - if (strpos($final_dfrn_id, ':') == 1) { - $final_dfrn_id = substr($final_dfrn_id, 2); - } - - if ($final_dfrn_id != $orig_id) { - Logger::log('dfrn_deliver: wrong dfrn_id.'); - // did not decode properly - cannot trust this site - return 3; - } - - $postvars['dfrn_id'] = $idtosend; - $postvars['dfrn_version'] = DFRN_PROTOCOL_VERSION; - if ($dissolve) { - $postvars['dissolve'] = '1'; - } - - if ((($contact['rel']) && ($contact['rel'] != Contact::SHARING) && (! $contact['blocked'])) || ($owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY)) { - $postvars['data'] = $atom; - $postvars['perm'] = 'rw'; - } else { - $postvars['data'] = str_replace('1', '0', $atom); - $postvars['perm'] = 'r'; - } - - $postvars['ssl_policy'] = $ssl_policy; - - if ($page) { - $postvars['page'] = $page; - } - - - if ($rino > 0 && $rino_remote_version > 0 && (! $dissolve)) { - Logger::log('rino version: '. $rino_remote_version); - - switch ($rino_remote_version) { - case 1: - $key = random_bytes(16); - $data = self::aesEncrypt($postvars['data'], $key); - break; - - default: - Logger::log("rino: invalid requested version '$rino_remote_version'"); - return -8; - } - - $postvars['rino'] = $rino_remote_version; - $postvars['data'] = bin2hex($data); - - if ($dfrn_version >= 2.1) { - if (($contact['duplex'] && strlen($contact['pubkey'])) - || ($owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY && strlen($contact['pubkey'])) - || ($contact['rel'] == Contact::SHARING && strlen($contact['pubkey'])) - ) { - openssl_public_encrypt($key, $postvars['key'], $contact['pubkey']); - } else { - openssl_private_encrypt($key, $postvars['key'], $contact['prvkey']); - } - } else { - if (($contact['duplex'] && strlen($contact['prvkey'])) || ($owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY)) { - openssl_private_encrypt($key, $postvars['key'], $contact['prvkey']); - } else { - openssl_public_encrypt($key, $postvars['key'], $contact['pubkey']); - } - } - - Logger::log('md5 rawkey ' . md5($postvars['key'])); - - $postvars['key'] = bin2hex($postvars['key']); - } - - - Logger::debug('dfrn_deliver', ['post' => $postvars]); - - $postResult = DI::httpRequest()->post($contact['notify'], $postvars); - - $xml = $postResult->getBody(); - - Logger::log('dfrn_deliver: ' . "RECEIVED: " . $xml, Logger::DATA); - - $curl_stat = $postResult->getReturnCode(); - if (empty($curl_stat) || empty($xml)) { - return -9; // timed out - } - - if (($curl_stat == 503) && stristr($postResult->getHeader(), 'retry-after')) { - return -10; - } - - if (strpos($xml, 'status)) { - return -11; - } - - // Possibly old servers had returned an empty value when everything was okay - if (empty($res->status)) { - $res->status = 200; - } - - if (!empty($res->message)) { - Logger::log('Delivery returned status '.$res->status.' - '.$res->message, Logger::DEBUG); - } - - return intval($res->status); - } - /** * Transmits atom content to the contacts via the Diaspora transport layer * @@ -2618,12 +2185,8 @@ class DFRN Logger::log("Import DFRN message for user " . $importer["importer_uid"] . " from contact " . $importer["id"], Logger::DEBUG); - if (!empty($importer['gsid'])) { - if ($protocol == Conversation::PARCEL_DIASPORA_DFRN) { - GServer::setProtocol($importer['gsid'], Post\DeliveryData::DFRN); - } elseif ($protocol == Conversation::PARCEL_LEGACY_DFRN) { - GServer::setProtocol($importer['gsid'], Post\DeliveryData::LEGACY_DFRN); - } + if (!empty($importer['gsid']) && ($protocol == Conversation::PARCEL_DIASPORA_DFRN)) { + GServer::setProtocol($importer['gsid'], Post\DeliveryData::DFRN); } // is it a public forum? Private forums aren't exposed with this method diff --git a/src/Worker/Delivery.php b/src/Worker/Delivery.php index dad2ff7b3..e42b5a101 100644 --- a/src/Worker/Delivery.php +++ b/src/Worker/Delivery.php @@ -340,17 +340,9 @@ class Delivery self::deliverDiaspora($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup); return; } - } elseif ($cmd != self::RELOCATION) { + } else { // DFRN payload over Diaspora transport layer $deliver_status = DFRN::transmit($owner, $contact, $atom); - if (($deliver_status < 200) && (empty($server_protocol) || ($server_protocol == Model\Post\DeliveryData::LEGACY_DFRN))) { - // Legacy DFRN - $deliver_status = DFRN::deliver($owner, $contact, $atom); - $protocol = Model\Post\DeliveryData::LEGACY_DFRN; - } - } else { - $deliver_status = DFRN::deliver($owner, $contact, $atom); - $protocol = Model\Post\DeliveryData::LEGACY_DFRN; } Logger::info('DFRN Delivery', ['cmd' => $cmd, 'url' => $contact['url'], 'guid' => ($target_item['guid'] ?? '') ?: $target_item['id'], 'return' => $deliver_status]); diff --git a/src/Worker/OptimizeTables.php b/src/Worker/OptimizeTables.php index f98898660..c24d991b5 100644 --- a/src/Worker/OptimizeTables.php +++ b/src/Worker/OptimizeTables.php @@ -42,7 +42,6 @@ class OptimizeTables DBA::e("OPTIMIZE TABLE `auth_codes`"); DBA::e("OPTIMIZE TABLE `cache`"); - DBA::e("OPTIMIZE TABLE `challenge`"); DBA::e("OPTIMIZE TABLE `locks`"); DBA::e("OPTIMIZE TABLE `oembed`"); DBA::e("OPTIMIZE TABLE `parsed_url`"); diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 19688e698..8af6c16eb 100644 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -55,7 +55,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1429); + define('DB_UPDATE_VERSION', 1430); } return [ @@ -175,7 +175,7 @@ return [ "self" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "1 if the contact is the user him/her self"], "remote_self" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], "rel" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => "The kind of the relation between the user and the contact"], - "duplex" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], + "duplex" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Deprecated"], "network" => ["type" => "char(4)", "not null" => "1", "default" => "", "comment" => "Network of the contact"], "protocol" => ["type" => "char(4)", "not null" => "1", "default" => "", "comment" => "Protocol of the contact"], "name" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Name that this contact is known by"], @@ -191,9 +191,9 @@ return [ "thumb" => ["type" => "varchar(255)", "default" => "", "comment" => "Link to the profile photo (thumb size)"], "micro" => ["type" => "varchar(255)", "default" => "", "comment" => "Link to the profile photo (micro size)"], "header" => ["type" => "varchar(255)", "comment" => "Header picture"], - "site-pubkey" => ["type" => "text", "comment" => ""], - "issued-id" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], - "dfrn-id" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], + "site-pubkey" => ["type" => "text", "comment" => "Deprecated"], + "issued-id" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Deprecated"], + "dfrn-id" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Deprecated"], "url" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "nurl" => ["type" => "varchar(255)", "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 contact url"], @@ -208,8 +208,8 @@ return [ "confirm" => ["type" => "varchar(255)", "comment" => ""], "subscribe" => ["type" => "varchar(255)", "comment" => ""], "poco" => ["type" => "varchar(255)", "comment" => ""], - "aes_allow" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], - "ret-aes" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], + "aes_allow" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Deprecated"], + "ret-aes" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Deprecated"], "usehub" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], "subhub" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], "hub-verify" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], @@ -265,8 +265,6 @@ return [ "nurl_uid" => ["nurl(128)", "uid"], "nick_uid" => ["nick(128)", "uid"], "attag_uid" => ["attag(96)", "uid"], - "dfrn-id" => ["dfrn-id(64)"], - "issued-id" => ["issued-id(64)"], "network_uid_lastupdate" => ["network", "uid", "last-update"], "uid_network_self_lastupdate" => ["uid", "network", "self", "last-update"], "uid_lastitem" => ["uid", "last-item"], @@ -524,21 +522,6 @@ return [ "k_expires" => ["k", "expires"], ] ], - "challenge" => [ - "comment" => "", - "fields" => [ - "id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "sequential ID"], - "challenge" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], - "dfrn-id" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], - "expire" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "comment" => ""], - "type" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], - "last_update" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], - ], - "indexes" => [ - "PRIMARY" => ["id"], - "expire" => ["expire"], - ] - ], "config" => [ "comment" => "main configuration storage", "fields" => [ diff --git a/static/dbview.config.php b/static/dbview.config.php index bb2cd0f17..b2b9875c1 100644 --- a/static/dbview.config.php +++ b/static/dbview.config.php @@ -138,7 +138,6 @@ "uri-date" => ["contact", "uri-date"], "avatar-date" => ["contact", "avatar-date"], "thumb" => ["contact", "thumb"], - "dfrn-id" => ["contact", "dfrn-id"], "author-id" => ["post-user", "author-id"], "author-link" => ["author", "url"], "author-addr" => ["author", "addr"], @@ -298,7 +297,6 @@ "uri-date" => ["contact", "uri-date"], "avatar-date" => ["contact", "avatar-date"], "thumb" => ["contact", "thumb"], - "dfrn-id" => ["contact", "dfrn-id"], "author-id" => ["post-thread-user", "author-id"], "author-link" => ["author", "url"], "author-addr" => ["author", "addr"], @@ -443,7 +441,6 @@ "uri-date" => ["author", "uri-date"], "avatar-date" => ["author", "avatar-date"], "thumb" => ["author", "thumb"], - "dfrn-id" => ["author", "dfrn-id"], "author-id" => ["post", "author-id"], "author-link" => ["author", "url"], "author-addr" => ["author", "addr"], @@ -563,7 +560,6 @@ "uri-date" => ["author", "uri-date"], "avatar-date" => ["author", "avatar-date"], "thumb" => ["author", "thumb"], - "dfrn-id" => ["author", "dfrn-id"], "author-id" => ["post-thread", "author-id"], "author-link" => ["author", "url"], "author-addr" => ["author", "addr"], @@ -708,7 +704,6 @@ "self" => ["contact", "self"], "remote_self" => ["contact", "remote_self"], "rel" => ["contact", "rel"], - "duplex" => ["contact", "duplex"], "network" => ["contact", "network"], "protocol" => ["contact", "protocol"], "name" => ["contact", "name"], @@ -724,9 +719,6 @@ "thumb" => ["contact", "thumb"], "micro" => ["contact", "micro"], "header" => ["contact", "header"], - "site-pubkey" => ["contact", "site-pubkey"], - "issued-id" => ["contact", "issued-id"], - "dfrn-id" => ["contact", "dfrn-id"], "url" => ["contact", "url"], "nurl" => ["contact", "nurl"], "uri-id" => ["contact", "uri-id"], @@ -740,8 +732,6 @@ "poll" => ["contact", "poll"], "confirm" => ["contact", "confirm"], "poco" => ["contact", "poco"], - "aes_allow" => ["contact", "aes_allow"], - "ret-aes" => ["contact", "ret-aes"], "usehub" => ["contact", "usehub"], "subhub" => ["contact", "subhub"], "hub-verify" => ["contact", "hub-verify"], @@ -981,12 +971,6 @@ "subhub" => ["ucontact", "subhub"], "hub-verify" => ["ucontact", "hub-verify"], "reason" => ["ucontact", "reason"], - "dfrn-duplex" => ["ucontact", "duplex"], - "dfrn-ret-aes" => ["ucontact", "ret-aes"], - "dfrn-site-pubkey" => ["ucontact", "site-pubkey"], - "dfrn-issued-id" => ["ucontact", "issued-id"], - "dfrn-id" => ["ucontact", "dfrn-id"], - "dfrn-aes_allow" => ["ucontact", "aes_allow"], "dfrn-request" => ["contact", "request"], "dfrn-notify" => ["contact", "notify"], "dfrn-poll" => ["contact", "poll"], diff --git a/view/lang/C/messages.po b/view/lang/C/messages.po index d4a43080e..127423df4 100644 --- a/view/lang/C/messages.po +++ b/view/lang/C/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 2021.09-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-07-14 02:55+0000\n" +"POT-Creation-Date: 2021-07-15 08:57+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -54,7 +54,7 @@ msgstr "" msgid "%1$s poked %2$s" msgstr "" -#: include/conversation.php:138 src/Model/Item.php:2647 +#: include/conversation.php:138 src/Model/Item.php:2606 msgid "event" msgstr "" @@ -62,7 +62,7 @@ msgstr "" msgid "status" msgstr "" -#: include/conversation.php:146 mod/tagger.php:90 src/Model/Item.php:2649 +#: include/conversation.php:146 mod/tagger.php:90 src/Model/Item.php:2608 msgid "photo" msgstr "" @@ -184,32 +184,32 @@ msgstr "" msgid "Follow Thread" msgstr "" -#: include/conversation.php:852 src/Model/Contact.php:1045 +#: include/conversation.php:852 src/Model/Contact.php:1043 msgid "View Status" msgstr "" #: include/conversation.php:853 include/conversation.php:875 -#: src/Model/Contact.php:971 src/Model/Contact.php:1037 -#: src/Model/Contact.php:1046 src/Module/Directory.php:166 +#: src/Model/Contact.php:969 src/Model/Contact.php:1035 +#: src/Model/Contact.php:1044 src/Module/Directory.php:166 #: src/Module/Settings/Profile/Index.php:224 msgid "View Profile" msgstr "" -#: include/conversation.php:854 src/Model/Contact.php:1047 +#: include/conversation.php:854 src/Model/Contact.php:1045 msgid "View Photos" msgstr "" -#: include/conversation.php:855 src/Model/Contact.php:1038 -#: src/Model/Contact.php:1048 +#: include/conversation.php:855 src/Model/Contact.php:1036 +#: src/Model/Contact.php:1046 msgid "Network Posts" msgstr "" -#: include/conversation.php:856 src/Model/Contact.php:1039 -#: src/Model/Contact.php:1049 +#: include/conversation.php:856 src/Model/Contact.php:1037 +#: src/Model/Contact.php:1047 msgid "View Contact" msgstr "" -#: include/conversation.php:857 src/Model/Contact.php:1051 +#: include/conversation.php:857 src/Model/Contact.php:1049 msgid "Send PM" msgstr "" @@ -223,7 +223,7 @@ msgstr "" #: include/conversation.php:859 src/Module/Contact.php:634 #: src/Module/Contact.php:892 src/Module/Contact.php:1181 #: src/Module/Notifications/Introductions.php:113 -#: src/Module/Notifications/Introductions.php:191 +#: src/Module/Notifications/Introductions.php:185 #: src/Module/Notifications/Notification.php:59 msgid "Ignore" msgstr "" @@ -232,12 +232,12 @@ msgstr "" msgid "Languages" msgstr "" -#: include/conversation.php:867 src/Model/Contact.php:1052 +#: include/conversation.php:867 src/Model/Contact.php:1050 msgid "Poke" msgstr "" -#: include/conversation.php:872 mod/follow.php:146 src/Content/Widget.php:76 -#: src/Model/Contact.php:1040 src/Model/Contact.php:1053 +#: include/conversation.php:872 mod/follow.php:138 src/Content/Widget.php:76 +#: src/Model/Contact.php:1038 src/Model/Contact.php:1051 #: view/theme/vier/theme.php:172 msgid "Connect/Follow" msgstr "" @@ -476,11 +476,10 @@ msgstr "" msgid "Preview" msgstr "" -#: include/conversation.php:1145 mod/dfrn_request.php:642 mod/editpost.php:128 -#: mod/fbrowser.php:105 mod/fbrowser.php:134 mod/follow.php:152 -#: mod/photos.php:1036 mod/photos.php:1142 mod/tagrm.php:37 mod/tagrm.php:129 -#: mod/unfollow.php:100 src/Module/Contact.php:467 -#: src/Module/RemoteFollow.php:110 +#: include/conversation.php:1145 mod/editpost.php:128 mod/fbrowser.php:105 +#: mod/fbrowser.php:134 mod/follow.php:144 mod/photos.php:1036 +#: mod/photos.php:1142 mod/tagrm.php:37 mod/tagrm.php:129 mod/unfollow.php:97 +#: src/Module/Contact.php:467 src/Module/RemoteFollow.php:110 msgid "Cancel" msgstr "" @@ -823,17 +822,17 @@ msgstr "" msgid "Please visit %s to approve or reject the request." msgstr "" -#: mod/api.php:52 mod/api.php:57 mod/dfrn_confirm.php:78 mod/editpost.php:37 -#: mod/events.php:231 mod/follow.php:55 mod/follow.php:135 mod/item.php:185 -#: mod/item.php:190 mod/item.php:917 mod/message.php:69 mod/message.php:112 -#: mod/notes.php:44 mod/ostatus_subscribe.php:32 mod/photos.php:175 -#: mod/photos.php:921 mod/repair_ostatus.php:31 mod/settings.php:47 -#: mod/settings.php:65 mod/settings.php:474 mod/suggest.php:34 -#: mod/uimport.php:32 mod/unfollow.php:35 mod/unfollow.php:50 -#: mod/unfollow.php:82 mod/wall_attach.php:78 mod/wall_attach.php:81 -#: mod/wall_upload.php:99 mod/wall_upload.php:102 mod/wallmessage.php:35 -#: mod/wallmessage.php:59 mod/wallmessage.php:96 mod/wallmessage.php:120 -#: src/Module/Attach.php:56 src/Module/BaseApi.php:81 src/Module/BaseApi.php:92 +#: mod/api.php:52 mod/api.php:57 mod/editpost.php:37 mod/events.php:231 +#: mod/follow.php:55 mod/follow.php:130 mod/item.php:185 mod/item.php:190 +#: mod/item.php:917 mod/message.php:69 mod/message.php:112 mod/notes.php:44 +#: mod/ostatus_subscribe.php:32 mod/photos.php:175 mod/photos.php:921 +#: mod/repair_ostatus.php:31 mod/settings.php:47 mod/settings.php:65 +#: mod/settings.php:474 mod/suggest.php:34 mod/uimport.php:32 +#: mod/unfollow.php:35 mod/unfollow.php:50 mod/unfollow.php:82 +#: mod/wall_attach.php:78 mod/wall_attach.php:81 mod/wall_upload.php:99 +#: mod/wall_upload.php:102 mod/wallmessage.php:35 mod/wallmessage.php:59 +#: mod/wallmessage.php:96 mod/wallmessage.php:120 src/Module/Attach.php:56 +#: src/Module/BaseApi.php:81 src/Module/BaseApi.php:92 #: src/Module/BaseApi.php:103 src/Module/BaseApi.php:114 #: src/Module/BaseNotifications.php:88 src/Module/Contact.php:385 #: src/Module/Contact/Advanced.php:43 src/Module/Delegation.php:118 @@ -886,7 +885,7 @@ msgid "No" msgstr "" #: mod/cal.php:46 mod/cal.php:50 mod/follow.php:38 mod/redir.php:34 -#: mod/redir.php:203 src/Module/Conversation/Community.php:194 +#: mod/redir.php:176 src/Module/Conversation/Community.php:194 #: src/Module/Debug/ItemBody.php:38 src/Module/Diaspora/Receive.php:51 #: src/Module/Item/Follow.php:42 src/Module/Item/Ignore.php:41 #: src/Module/Item/Pin.php:42 src/Module/Item/Pin.php:57 @@ -968,253 +967,13 @@ msgstr "" msgid "calendar" msgstr "" -#: mod/dfrn_confirm.php:84 src/Module/Profile/Profile.php:82 -msgid "Profile not found." -msgstr "" - -#: mod/dfrn_confirm.php:139 mod/redir.php:56 mod/redir.php:157 -#: src/Module/Contact/Advanced.php:53 src/Module/Contact/Advanced.php:104 -#: src/Module/Contact/Contacts.php:36 src/Module/FriendSuggest.php:54 -#: src/Module/FriendSuggest.php:93 src/Module/Group.php:105 -msgid "Contact not found." -msgstr "" - -#: mod/dfrn_confirm.php:140 -msgid "" -"This may occasionally happen if contact was requested by both persons and it " -"has already been approved." -msgstr "" - -#: mod/dfrn_confirm.php:241 -msgid "Response from remote site was not understood." -msgstr "" - -#: mod/dfrn_confirm.php:248 mod/dfrn_confirm.php:254 -msgid "Unexpected response from remote site: " -msgstr "" - -#: mod/dfrn_confirm.php:263 -msgid "Confirmation completed successfully." -msgstr "" - -#: mod/dfrn_confirm.php:275 -msgid "Temporary failure. Please wait and try again." -msgstr "" - -#: mod/dfrn_confirm.php:278 -msgid "Introduction failed or was revoked." -msgstr "" - -#: mod/dfrn_confirm.php:283 -msgid "Remote site reported: " -msgstr "" - -#: mod/dfrn_confirm.php:388 -#, php-format -msgid "No user record found for '%s' " -msgstr "" - -#: mod/dfrn_confirm.php:398 -msgid "Our site encryption key is apparently messed up." -msgstr "" - -#: mod/dfrn_confirm.php:409 -msgid "Empty site URL was provided or URL could not be decrypted by us." -msgstr "" - -#: mod/dfrn_confirm.php:425 -msgid "Contact record was not found for you on our site." -msgstr "" - -#: mod/dfrn_confirm.php:439 -#, php-format -msgid "Site public key not available in contact record for URL %s." -msgstr "" - -#: mod/dfrn_confirm.php:455 -msgid "" -"The ID provided by your system is a duplicate on our system. It should work " -"if you try again." -msgstr "" - -#: mod/dfrn_confirm.php:466 -msgid "Unable to set your contact credentials on our system." -msgstr "" - -#: mod/dfrn_confirm.php:522 -msgid "Unable to update your contact profile details on our system" -msgstr "" - -#: mod/dfrn_poll.php:136 mod/dfrn_poll.php:509 -#, php-format -msgid "%1$s welcomes %2$s" -msgstr "" - -#: mod/dfrn_request.php:113 -msgid "This introduction has already been accepted." -msgstr "" - -#: mod/dfrn_request.php:131 mod/dfrn_request.php:369 -msgid "Profile location is not valid or does not contain profile information." -msgstr "" - -#: mod/dfrn_request.php:135 mod/dfrn_request.php:373 -msgid "Warning: profile location has no identifiable owner name." -msgstr "" - -#: mod/dfrn_request.php:138 mod/dfrn_request.php:376 -msgid "Warning: profile location has no profile photo." -msgstr "" - -#: mod/dfrn_request.php:142 mod/dfrn_request.php:380 -#, php-format -msgid "%d required parameter was not found at the given location" -msgid_plural "%d required parameters were not found at the given location" -msgstr[0] "" -msgstr[1] "" - -#: mod/dfrn_request.php:180 -msgid "Introduction complete." -msgstr "" - -#: mod/dfrn_request.php:216 -msgid "Unrecoverable protocol error." -msgstr "" - -#: mod/dfrn_request.php:243 src/Module/RemoteFollow.php:54 -msgid "Profile unavailable." -msgstr "" - -#: mod/dfrn_request.php:264 -#, php-format -msgid "%s has received too many connection requests today." -msgstr "" - -#: mod/dfrn_request.php:265 -msgid "Spam protection measures have been invoked." -msgstr "" - -#: mod/dfrn_request.php:266 -msgid "Friends are advised to please try again in 24 hours." -msgstr "" - -#: mod/dfrn_request.php:290 src/Module/RemoteFollow.php:60 -msgid "Invalid locator" -msgstr "" - -#: mod/dfrn_request.php:326 -msgid "You have already introduced yourself here." -msgstr "" - -#: mod/dfrn_request.php:329 -#, php-format -msgid "Apparently you are already friends with %s." -msgstr "" - -#: mod/dfrn_request.php:349 -msgid "Invalid profile URL." -msgstr "" - -#: mod/dfrn_request.php:355 src/Model/Contact.php:2341 -msgid "Disallowed profile URL." -msgstr "" - -#: mod/dfrn_request.php:361 src/Model/Contact.php:2346 -#: src/Module/Friendica.php:80 -msgid "Blocked domain" -msgstr "" - -#: mod/dfrn_request.php:428 src/Module/Contact.php:157 -msgid "Failed to update contact record." -msgstr "" - -#: mod/dfrn_request.php:448 -msgid "Your introduction has been sent." -msgstr "" - -#: mod/dfrn_request.php:480 src/Module/RemoteFollow.php:72 -msgid "" -"Remote subscription can't be done for your network. Please subscribe " -"directly on your system." -msgstr "" - -#: mod/dfrn_request.php:496 -msgid "Please login to confirm introduction." -msgstr "" - -#: mod/dfrn_request.php:504 -msgid "" -"Incorrect identity currently logged in. Please login to this profile." -msgstr "" - -#: mod/dfrn_request.php:518 mod/dfrn_request.php:533 -msgid "Confirm" -msgstr "" - -#: mod/dfrn_request.php:529 -msgid "Hide this contact" -msgstr "" - -#: mod/dfrn_request.php:531 -#, php-format -msgid "Welcome home %s." -msgstr "" - -#: mod/dfrn_request.php:532 -#, php-format -msgid "Please confirm your introduction/connection request to %s." -msgstr "" - -#: mod/dfrn_request.php:600 mod/display.php:179 mod/photos.php:835 -#: mod/videos.php:128 src/Module/Conversation/Community.php:188 -#: src/Module/Debug/Probe.php:39 src/Module/Debug/WebFinger.php:38 -#: src/Module/Directory.php:49 src/Module/Search/Index.php:50 -#: src/Module/Search/Index.php:55 +#: mod/display.php:179 mod/photos.php:835 mod/videos.php:128 +#: src/Module/Conversation/Community.php:188 src/Module/Debug/Probe.php:39 +#: src/Module/Debug/WebFinger.php:38 src/Module/Directory.php:49 +#: src/Module/Search/Index.php:50 src/Module/Search/Index.php:55 msgid "Public access denied." msgstr "" -#: mod/dfrn_request.php:636 src/Module/RemoteFollow.php:104 -msgid "Friend/Connection Request" -msgstr "" - -#: mod/dfrn_request.php:637 -#, php-format -msgid "" -"Enter your Webfinger address (user@domain.tld) or profile URL here. If this " -"isn't supported by your system (for example it doesn't work with Diaspora), " -"you have to subscribe to %s directly on your system" -msgstr "" - -#: mod/dfrn_request.php:638 src/Module/RemoteFollow.php:106 -#, php-format -msgid "" -"If you are not yet a member of the free social web, follow " -"this link to find a public Friendica node and join us today." -msgstr "" - -#: mod/dfrn_request.php:639 src/Module/RemoteFollow.php:107 -msgid "Your Webfinger address or profile URL:" -msgstr "" - -#: mod/dfrn_request.php:640 mod/follow.php:147 src/Module/RemoteFollow.php:108 -msgid "Please answer the following:" -msgstr "" - -#: mod/dfrn_request.php:641 mod/follow.php:74 mod/unfollow.php:99 -#: src/Module/RemoteFollow.php:109 -msgid "Submit Request" -msgstr "" - -#: mod/dfrn_request.php:648 mod/follow.php:161 -#, php-format -msgid "%s knows you" -msgstr "" - -#: mod/dfrn_request.php:649 mod/follow.php:162 -msgid "Add a personal note:" -msgstr "" - #: mod/display.php:235 mod/display.php:320 msgid "The requested item doesn't exist or has been deleted." msgstr "" @@ -1331,7 +1090,7 @@ msgstr "" #: mod/events.php:563 src/Model/Event.php:86 src/Model/Event.php:113 #: src/Model/Event.php:475 src/Model/Event.php:962 src/Model/Profile.php:420 #: src/Module/Contact.php:654 src/Module/Directory.php:156 -#: src/Module/Notifications/Introductions.php:172 +#: src/Module/Notifications/Introductions.php:166 #: src/Module/Profile/Profile.php:190 msgid "Location:" msgstr "" @@ -1389,6 +1148,10 @@ msgstr "" msgid "Files" msgstr "" +#: mod/follow.php:74 mod/unfollow.php:96 src/Module/RemoteFollow.php:109 +msgid "Submit Request" +msgstr "" + #: mod/follow.php:84 msgid "You already added this contact." msgstr "" @@ -1405,29 +1168,42 @@ msgstr "" msgid "OStatus support is disabled. Contact can't be added." msgstr "" -#: mod/follow.php:148 mod/unfollow.php:97 +#: mod/follow.php:139 src/Module/RemoteFollow.php:108 +msgid "Please answer the following:" +msgstr "" + +#: mod/follow.php:140 mod/unfollow.php:94 msgid "Your Identity Address:" msgstr "" -#: mod/follow.php:149 mod/unfollow.php:103 +#: mod/follow.php:141 mod/unfollow.php:100 #: src/Module/Admin/Blocklist/Contact.php:100 src/Module/Contact.php:650 #: src/Module/Notifications/Introductions.php:108 -#: src/Module/Notifications/Introductions.php:183 +#: src/Module/Notifications/Introductions.php:177 msgid "Profile URL" msgstr "" -#: mod/follow.php:150 src/Module/Contact.php:660 -#: src/Module/Notifications/Introductions.php:176 +#: mod/follow.php:142 src/Module/Contact.php:660 +#: src/Module/Notifications/Introductions.php:170 #: src/Module/Profile/Profile.php:203 msgid "Tags:" msgstr "" -#: mod/follow.php:171 mod/unfollow.php:113 src/Module/BaseProfile.php:63 +#: mod/follow.php:153 +#, php-format +msgid "%s knows you" +msgstr "" + +#: mod/follow.php:154 +msgid "Add a personal note:" +msgstr "" + +#: mod/follow.php:163 mod/unfollow.php:110 src/Module/BaseProfile.php:63 #: src/Module/Contact.php:939 msgid "Status Messages and Posts" msgstr "" -#: mod/follow.php:203 +#: mod/follow.php:191 msgid "The contact could not be added." msgstr "" @@ -1628,7 +1404,7 @@ msgid "Message collection failure." msgstr "" #: mod/message.php:121 src/Module/Notifications/Introductions.php:114 -#: src/Module/Notifications/Introductions.php:155 +#: src/Module/Notifications/Introductions.php:149 #: src/Module/Notifications/Notification.php:56 msgid "Discard" msgstr "" @@ -2033,10 +1809,17 @@ msgstr "" msgid "{0} and %d others requested registration" msgstr "" -#: mod/redir.php:50 mod/redir.php:130 +#: mod/redir.php:50 mod/redir.php:103 msgid "Bad Request." msgstr "" +#: mod/redir.php:56 mod/redir.php:130 src/Module/Contact/Advanced.php:53 +#: src/Module/Contact/Advanced.php:104 src/Module/Contact/Contacts.php:36 +#: src/Module/FriendSuggest.php:54 src/Module/FriendSuggest.php:93 +#: src/Module/Group.php:105 +msgid "Contact not found." +msgstr "" + #: mod/removeme.php:63 msgid "User deleted their account" msgstr "" @@ -2912,15 +2695,15 @@ msgid "" "select \"Export account\"" msgstr "" -#: mod/unfollow.php:65 mod/unfollow.php:133 +#: mod/unfollow.php:65 mod/unfollow.php:130 msgid "You aren't following this contact." msgstr "" -#: mod/unfollow.php:71 mod/unfollow.php:139 +#: mod/unfollow.php:71 mod/unfollow.php:136 msgid "Unfollowing is currently not supported by your network." msgstr "" -#: mod/unfollow.php:95 +#: mod/unfollow.php:92 msgid "Disconnect/Unfollow" msgstr "" @@ -3652,8 +3435,8 @@ msgid "" "%2$s %3$s" msgstr "" -#: src/Content/Text/BBCode.php:1108 src/Model/Item.php:3175 -#: src/Model/Item.php:3181 src/Model/Item.php:3182 +#: src/Content/Text/BBCode.php:1108 src/Model/Item.php:3134 +#: src/Model/Item.php:3140 src/Model/Item.php:3141 msgid "Link to source" msgstr "" @@ -3808,7 +3591,7 @@ msgstr "" msgid "Organisations" msgstr "" -#: src/Content/Widget.php:532 src/Model/Contact.php:1469 +#: src/Content/Widget.php:532 src/Model/Contact.php:1467 msgid "News" msgstr "" @@ -4629,82 +4412,85 @@ msgstr "" msgid "Legacy module file not found: %s" msgstr "" -#: src/Model/Contact.php:1041 src/Model/Contact.php:1054 +#: src/Model/Contact.php:1039 src/Model/Contact.php:1052 msgid "UnFollow" msgstr "" -#: src/Model/Contact.php:1050 +#: src/Model/Contact.php:1048 msgid "Drop Contact" msgstr "" -#: src/Model/Contact.php:1060 src/Module/Admin/Users/Pending.php:107 +#: src/Model/Contact.php:1058 src/Module/Admin/Users/Pending.php:107 #: src/Module/Notifications/Introductions.php:111 -#: src/Module/Notifications/Introductions.php:189 +#: src/Module/Notifications/Introductions.php:183 msgid "Approve" msgstr "" -#: src/Model/Contact.php:1465 +#: src/Model/Contact.php:1463 msgid "Organisation" msgstr "" -#: src/Model/Contact.php:1473 +#: src/Model/Contact.php:1471 msgid "Forum" msgstr "" -#: src/Model/Contact.php:2351 +#: src/Model/Contact.php:2327 +msgid "Disallowed profile URL." +msgstr "" + +#: src/Model/Contact.php:2332 src/Module/Friendica.php:80 +msgid "Blocked domain" +msgstr "" + +#: src/Model/Contact.php:2337 msgid "Connect URL missing." msgstr "" -#: src/Model/Contact.php:2360 +#: src/Model/Contact.php:2346 msgid "" "The contact could not be added. Please check the relevant network " "credentials in your Settings -> Social Networks page." msgstr "" -#: src/Model/Contact.php:2403 -msgid "" -"This site is not configured to allow communications with other networks." -msgstr "" - -#: src/Model/Contact.php:2404 src/Model/Contact.php:2417 -msgid "No compatible communication protocols or feeds were discovered." -msgstr "" - -#: src/Model/Contact.php:2415 +#: src/Model/Contact.php:2383 msgid "The profile address specified does not provide adequate information." msgstr "" -#: src/Model/Contact.php:2420 +#: src/Model/Contact.php:2385 +msgid "No compatible communication protocols or feeds were discovered." +msgstr "" + +#: src/Model/Contact.php:2388 msgid "An author or name was not found." msgstr "" -#: src/Model/Contact.php:2423 +#: src/Model/Contact.php:2391 msgid "No browser URL could be matched to this address." msgstr "" -#: src/Model/Contact.php:2426 +#: src/Model/Contact.php:2394 msgid "" "Unable to match @-style Identity Address with a known protocol or email " "contact." msgstr "" -#: src/Model/Contact.php:2427 +#: src/Model/Contact.php:2395 msgid "Use mailto: in front of address to force email check." msgstr "" -#: src/Model/Contact.php:2433 +#: src/Model/Contact.php:2401 msgid "" "The profile address specified belongs to a network which has been disabled " "on this site." msgstr "" -#: src/Model/Contact.php:2438 +#: src/Model/Contact.php:2406 msgid "" "Limited profile. This person will be unable to receive direct/personal " "notifications from you." msgstr "" -#: src/Model/Contact.php:2497 +#: src/Model/Contact.php:2465 msgid "Unable to retrieve contact information." msgstr "" @@ -4821,33 +4607,33 @@ msgstr "" msgid "Edit groups" msgstr "" -#: src/Model/Item.php:1701 +#: src/Model/Item.php:1660 #, php-format msgid "Detected languages in this post:\\n%s" msgstr "" -#: src/Model/Item.php:2651 +#: src/Model/Item.php:2610 msgid "activity" msgstr "" -#: src/Model/Item.php:2653 +#: src/Model/Item.php:2612 msgid "comment" msgstr "" -#: src/Model/Item.php:2656 +#: src/Model/Item.php:2615 msgid "post" msgstr "" -#: src/Model/Item.php:2770 +#: src/Model/Item.php:2729 #, php-format msgid "Content warning: %s" msgstr "" -#: src/Model/Item.php:3140 +#: src/Model/Item.php:3099 msgid "bytes" msgstr "" -#: src/Model/Item.php:3169 src/Model/Item.php:3170 +#: src/Model/Item.php:3128 src/Model/Item.php:3129 msgid "View on separate page" msgstr "" @@ -4870,7 +4656,7 @@ msgid "Homepage:" msgstr "" #: src/Model/Profile.php:424 src/Module/Contact.php:658 -#: src/Module/Notifications/Introductions.php:174 +#: src/Module/Notifications/Introductions.php:168 msgid "About:" msgstr "" @@ -4888,7 +4674,7 @@ msgid "Atom feed" msgstr "" #: src/Model/Profile.php:516 src/Module/Contact.php:338 -#: src/Module/Notifications/Introductions.php:186 +#: src/Module/Notifications/Introductions.php:180 msgid "Network:" msgstr "" @@ -7393,6 +7179,10 @@ msgstr[1] "" msgid "Could not access contact record." msgstr "" +#: src/Module/Contact.php:157 +msgid "Failed to update contact record." +msgstr "" + #: src/Module/Contact.php:417 msgid "You can't block yourself" msgstr "" @@ -7592,7 +7382,7 @@ msgstr "" msgid "Awaiting connection acknowledge" msgstr "" -#: src/Module/Contact.php:642 src/Module/Notifications/Introductions.php:177 +#: src/Module/Contact.php:642 src/Module/Notifications/Introductions.php:171 msgid "Hide this contact from others" msgstr "" @@ -8781,7 +8571,7 @@ msgid "Hide Ignored Requests" msgstr "" #: src/Module/Notifications/Introductions.php:94 -#: src/Module/Notifications/Introductions.php:163 +#: src/Module/Notifications/Introductions.php:157 msgid "Notification type:" msgstr "" @@ -8819,11 +8609,11 @@ msgstr "" msgid "Subscriber" msgstr "" -#: src/Module/Notifications/Introductions.php:201 +#: src/Module/Notifications/Introductions.php:195 msgid "No introductions." msgstr "" -#: src/Module/Notifications/Introductions.php:202 +#: src/Module/Notifications/Introductions.php:196 #: src/Module/Notifications/Notifications.php:133 #, php-format msgid "No more %s notifications." @@ -8907,6 +8697,10 @@ msgstr "" msgid "No contacts." msgstr "" +#: src/Module/Profile/Profile.php:82 +msgid "Profile not found." +msgstr "" + #: src/Module/Profile/Profile.php:135 #, php-format msgid "" @@ -9108,10 +8902,28 @@ msgstr "" msgid "Your registration is pending approval by the site owner." msgstr "" +#: src/Module/RemoteFollow.php:54 +msgid "Profile unavailable." +msgstr "" + +#: src/Module/RemoteFollow.php:60 +msgid "Invalid locator" +msgstr "" + #: src/Module/RemoteFollow.php:67 msgid "The provided profile link doesn't seem to be valid" msgstr "" +#: src/Module/RemoteFollow.php:72 +msgid "" +"Remote subscription can't be done for your network. Please subscribe " +"directly on your system." +msgstr "" + +#: src/Module/RemoteFollow.php:104 +msgid "Friend/Connection Request" +msgstr "" + #: src/Module/RemoteFollow.php:105 #, php-format msgid "" @@ -9120,6 +8932,17 @@ msgid "" "or %s directly on your system." msgstr "" +#: src/Module/RemoteFollow.php:106 +#, php-format +msgid "" +"If you are not yet a member of the free social web, follow " +"this link to find a public Friendica node and join us today." +msgstr "" + +#: src/Module/RemoteFollow.php:107 +msgid "Your Webfinger address or profile URL:" +msgstr "" + #: src/Module/Search/Index.php:54 msgid "Only logged in users are permitted to perform a search." msgstr "" @@ -10465,7 +10288,7 @@ msgstr "" msgid "Show fewer" msgstr "" -#: src/Protocol/Diaspora.php:3435 +#: src/Protocol/Diaspora.php:3434 msgid "Attachments:" msgstr "" @@ -10673,7 +10496,7 @@ msgstr "" msgid "%1$d %2$s ago" msgstr "" -#: src/Worker/Delivery.php:529 +#: src/Worker/Delivery.php:521 msgid "(no subject)" msgstr "" diff --git a/view/templates/dfrn_req_confirm.tpl b/view/templates/dfrn_req_confirm.tpl deleted file mode 100644 index 2e586ec33..000000000 --- a/view/templates/dfrn_req_confirm.tpl +++ /dev/null @@ -1,22 +0,0 @@ - - -

-{{$welcome}} -
-{{$please}} - -

-
- - - -{{$aes_allow nofilter}} - - - - - -
- -
-
\ No newline at end of file diff --git a/view/templates/dfrn_request.tpl b/view/templates/dfrn_request.tpl deleted file mode 100644 index f1ef7c469..000000000 --- a/view/templates/dfrn_request.tpl +++ /dev/null @@ -1,38 +0,0 @@ -

{{$header}}

- -{{if !$myaddr}} -

- {{$page_desc nofilter}} -

-

- {{$invite_desc nofilter}} -

-{{/if}} - -
-
- -{{if $myaddr}} - {{$myaddr}} - -{{else}} - -{{/if}} -
-
- -

- {{$pls_answer}} -

- -
- {{include file="field_checkbox.tpl" field=$does_know_you}} - - {{include file="field_textarea.tpl" field=$addnote_field}} -
- -
- - -
-
diff --git a/view/theme/frio/templates/dfrn_request.tpl b/view/theme/frio/templates/dfrn_request.tpl deleted file mode 100644 index 65f84fd7c..000000000 --- a/view/theme/frio/templates/dfrn_request.tpl +++ /dev/null @@ -1,40 +0,0 @@ -
-

{{$header}}

- -{{if !$myaddr}} -

- {{$page_desc nofilter}} -

-

- {{$invite_desc nofilter}} -

-{{/if}} - -
-
- -{{if $myaddr}} - {{$myaddr}} - -{{else}} - -{{/if}} -
-
- -

- {{$pls_answer}} -

- -
- {{include file="field_checkbox.tpl" field=$does_know_you}} - - {{include file="field_textarea.tpl" field=$addnote_field}} -
- -
- - -
-
-