diff --git a/boot.php b/boot.php index f40edaf90..ea1b273f2 100644 --- a/boot.php +++ b/boot.php @@ -41,7 +41,7 @@ define('FRIENDICA_PLATFORM', 'Friendica'); define('FRIENDICA_CODENAME', 'The Tazmans Flax-lily'); define('FRIENDICA_VERSION', '2018.12-dev'); define('DFRN_PROTOCOL_VERSION', '2.23'); -define('DB_UPDATE_VERSION', 1286); +define('DB_UPDATE_VERSION', 1287); define('NEW_UPDATE_ROUTINE_VERSION', 1170); /** diff --git a/config/dbstructure.json b/config/dbstructure.json index 8ea5aaa4b..51cb3ce4f 100644 --- a/config/dbstructure.json +++ b/config/dbstructure.json @@ -254,6 +254,16 @@ "received": ["received"] } }, + "diaspora-interaction": { + "comment": "Signed Diaspora Interaction", + "fields": { + "uri-id": {"type": "int unsigned", "not null": "1", "primary": "1", "relation": {"item-uri": "id"}, "comment": "Id of the item-uri table entry that contains the item uri"}, + "interaction": {"type": "mediumtext", "comment": "The Diaspora interaction"} + }, + "indexes": { + "PRIMARY": ["uri-id"] + } + }, "event": { "comment": "Events", "fields": { diff --git a/database.sql b/database.sql index 341ee5a85..2f67a341c 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2018.12-dev (The Tazmans Flax-lily) --- DB_UPDATE_VERSION 1285 +-- DB_UPDATE_VERSION 1287 -- ------------------------------------------ @@ -247,6 +247,15 @@ CREATE TABLE IF NOT EXISTS `conversation` ( INDEX `received` (`received`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Raw data and structure information for messages'; +-- +-- TABLE diaspora-interaction +-- +CREATE TABLE IF NOT EXISTS `diaspora-interaction` ( + `uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri', + `interaction` mediumtext COMMENT 'The Diaspora interaction', + PRIMARY KEY(`uri-id`) +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Signed Diaspora Interaction'; + -- -- TABLE event -- @@ -1242,12 +1251,14 @@ CREATE TABLE IF NOT EXISTS `workerqueue` ( `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Creation date', `pid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Process id of the worker', `executed` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Execution date', + `next_try` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Next retrial date', + `retrial` tinyint NOT NULL DEFAULT 0 COMMENT 'Retrial counter', `done` boolean NOT NULL DEFAULT '0' COMMENT 'Marked 1 when the task was done - will be deleted later', PRIMARY KEY(`id`), INDEX `pid` (`pid`), INDEX `parameter` (`parameter`(64)), - INDEX `priority_created` (`priority`,`created`), - INDEX `done_executed` (`done`,`executed`) + INDEX `priority_created_next_try` (`priority`,`created`,`next_try`), + INDEX `done_executed_next_try` (`done`,`executed`,`next_try`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Background tasks queue entries'; diff --git a/include/enotify.php b/include/enotify.php index b184a6935..d8e5614c1 100644 --- a/include/enotify.php +++ b/include/enotify.php @@ -42,9 +42,9 @@ function notification($params) } $params['notify_flags'] = defaults($params, 'notify_flags', $user['notify-flags']); - $params['language'] = defaults($params, 'language', $user['language']); - $params['to_name'] = defaults($params, 'to_name', $user['username']); - $params['to_email'] = defaults($params, 'to_email', $user['email']); + $params['language'] = defaults($params, 'language' , $user['language']); + $params['to_name'] = defaults($params, 'to_name' , $user['username']); + $params['to_email'] = defaults($params, 'to_email' , $user['email']); // from here on everything is in the recipients language L10n::pushLang($params['language']); diff --git a/mod/editpost.php b/mod/editpost.php index d6493b3c0..780145ed3 100644 --- a/mod/editpost.php +++ b/mod/editpost.php @@ -21,20 +21,14 @@ function editpost_content(App $a) } $post_id = (($a->argc > 1) ? intval($a->argv[1]) : 0); - $return_url = (($a->argc > 2) ? base64_decode($a->argv[2]) : ''); if (!$post_id) { notice(L10n::t('Item not found') . EOL); return; } - // Fallback to SESSION return_path - if (empty($return_url)) { - $return_url = $_SESSION['return_url']; - } - $fields = ['allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', - 'type', 'body', 'title', 'file', 'wall', 'post-type']; + 'type', 'body', 'title', 'file', 'wall', 'post-type', 'guid']; $item = Item::selectFirstForUser(local_user(), $fields, ['id' => $post_id, 'uid' => local_user()]); @@ -92,7 +86,7 @@ function editpost_content(App $a) $o .= replace_macros($tpl, [ '$is_edit' => true, - '$return_path' => $return_url, + '$return_path' => '/display/' . $item['guid'], '$action' => 'item', '$share' => L10n::t('Save'), '$upload' => L10n::t('Upload photo'), diff --git a/mod/group.php b/mod/group.php index 5bc188e50..f8fefc78f 100644 --- a/mod/group.php +++ b/mod/group.php @@ -16,7 +16,7 @@ use Friendica\Module; function group_init(App $a) { if (local_user()) { - $a->page['aside'] = Group::sidebarWidget('contacts', 'group', 'extended', (($a->argc > 1) ? $a->argv[1] : 'everyone')); + $a->page['aside'] = Model\Group::sidebarWidget('contacts', 'group', 'extended', (($a->argc > 1) ? $a->argv[1] : 'everyone')); } } @@ -31,10 +31,10 @@ function group_post(App $a) { check_form_security_token_redirectOnErr('/group/new', 'group_edit'); $name = notags(trim($_POST['groupname'])); - $r = Group::create(local_user(), $name); + $r = Model\Group::create(local_user(), $name); if ($r) { info(L10n::t('Group created.') . EOL); - $r = Group::getIdByName(local_user(), $name); + $r = Model\Group::getIdByName(local_user(), $name); if ($r) { goaway(System::baseUrl() . '/group/' . $r); } @@ -71,7 +71,7 @@ function group_post(App $a) { } } - $a->page['aside'] = Group::sidebarWidget(); + $a->page['aside'] = Model\Group::sidebarWidget(); } return; } @@ -148,7 +148,7 @@ function group_content(App $a) { $result = null; if (DBA::isResult($r)) { - $result = Group::removeByName(local_user(), $r[0]['name']); + $result = Model\Group::removeByName(local_user(), $r[0]['name']); } if ($result) { @@ -198,9 +198,9 @@ function group_content(App $a) { if ($change) { if (in_array($change, $preselected)) { - Group::removeMember($group['id'], $change); + Model\Group::removeMember($group['id'], $change); } else { - Group::addMember($group['id'], $change); + Model\Group::addMember($group['id'], $change); } $members = Model\Contact::getByGroupId($group['id']); @@ -261,7 +261,7 @@ function group_content(App $a) { $groupeditor['members'][] = $entry; } else { - Group::removeMember($group['id'], $member['id']); + Model\Group::removeMember($group['id'], $member['id']); } } diff --git a/src/Database/DBStructure.php b/src/Database/DBStructure.php index 0472d753d..f8fc1651c 100644 --- a/src/Database/DBStructure.php +++ b/src/Database/DBStructure.php @@ -83,10 +83,11 @@ class DBStructure $body = sprintf($body, $error_message); notification([ - 'type' => SYSTEM_EMAIL, + 'uid' => $admin['uid'], + 'type' => SYSTEM_EMAIL, 'to_email' => $admin['email'], 'preamble' => $preamble, - 'body' => $body, + 'body' => $body, 'language' => $lang] ); } diff --git a/src/Model/Item.php b/src/Model/Item.php index ed80f4e9e..824e240cc 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -231,6 +231,10 @@ class Item extends BaseObject } } + if (array_key_exists('signed_text', $row) && array_key_exists('interaction', $row) && !is_null($row['interaction'])) { + $row['signed_text'] = $row['interaction']; + } + if (array_key_exists('ignored', $row) && array_key_exists('internal-user-ignored', $row) && !is_null($row['internal-user-ignored'])) { $row['ignored'] = $row['internal-user-ignored']; } @@ -242,6 +246,7 @@ class Item extends BaseObject unset($row['internal-iaid']); unset($row['internal-icid']); unset($row['internal-user-ignored']); + unset($row['interaction']); return $row; } @@ -567,6 +572,8 @@ class Item extends BaseObject $fields['sign'] = ['signed_text', 'signature', 'signer']; + $fields['diaspora-interaction'] = ['interaction']; + return $fields; } @@ -653,6 +660,10 @@ class Item extends BaseObject $joins .= " LEFT JOIN `sign` ON `sign`.`iid` = `item`.`id`"; } + if (strpos($sql_commands, "`diaspora-interaction`.") !== false) { + $joins .= " LEFT JOIN `diaspora-interaction` ON `diaspora-interaction`.`uri-id` = `item`.`uri-id`"; + } + if (strpos($sql_commands, "`item-activity`.") !== false) { $joins .= " LEFT JOIN `item-activity` ON `item-activity`.`id` = `item`.`iaid`"; } @@ -705,6 +716,10 @@ class Item extends BaseObject $selected[] = 'internal-user-ignored'; } + if (in_array('signed_text', $selected)) { + $selected[] = 'interaction'; + } + $selection = []; foreach ($fields as $table => $table_fields) { foreach ($table_fields as $field => $select) { @@ -1487,7 +1502,6 @@ class Item extends BaseObject $deny_gid = ''; if ($item['parent-uri'] === $item['uri']) { - $diaspora_signed_text = ''; $parent_id = 0; $parent_deleted = 0; $allow_cid = $item['allow_cid']; @@ -1534,10 +1548,6 @@ class Item extends BaseObject $item['wall'] = $parent['wall']; $notify_type = 'comment-new'; - if (!$parent['origin']) { - $diaspora_signed_text = ''; - } - /* * If the parent is private, force privacy for the entire conversation * This differs from the above settings as it subtly allows comments from @@ -1578,7 +1588,6 @@ class Item extends BaseObject $parent_id = 0; $item['parent-uri'] = $item['uri']; $item['gravity'] = GRAVITY_PARENT; - $diaspora_signed_text = ''; } else { logger('item parent '.$item['parent-uri'].' for '.$item['uid'].' was not found - ignoring item'); return 0; @@ -1803,14 +1812,17 @@ class Item extends BaseObject logger("Repaired double encoded signature from handle ".$dsprsig->signer, LOGGER_DEBUG); } - DBA::insert('sign', ['iid' => $current_post, 'signed_text' => $dsprsig->signed_text, - 'signature' => $dsprsig->signature, 'signer' => $dsprsig->signer]); + if (!empty($dsprsig->signed_text) && empty($dsprsig->signature) && empty($dsprsig->signer)) { + DBA::insert('diaspora-interaction', ['uri-id' => $item['uri-id'], 'interaction' => $dsprsig->signed_text], true); + } else { + // The other fields are used by very old Friendica servers, so we currently store them differently + DBA::insert('sign', ['iid' => $current_post, 'signed_text' => $dsprsig->signed_text, + 'signature' => $dsprsig->signature, 'signer' => $dsprsig->signer]); + } } if (!empty($diaspora_signed_text)) { - // Formerly we stored the signed text, the signature and the author in different fields. - // We now store the raw data so that we are more flexible. - DBA::insert('sign', ['iid' => $current_post, 'signed_text' => $diaspora_signed_text]); + DBA::insert('diaspora-interaction', ['uri-id' => $item['uri-id'], 'interaction' => $diaspora_signed_text], true); } $deleted = self::tagDeliver($item['uid'], $current_post); diff --git a/src/Model/Profile.php b/src/Model/Profile.php index 28736750b..1f0773cd1 100644 --- a/src/Model/Profile.php +++ b/src/Model/Profile.php @@ -1010,56 +1010,59 @@ class Profile $my_url = self::getMyURL(); $my_url = Network::isUrlValid($my_url); - if ($my_url) { - if (!local_user()) { - // Is it a DDoS attempt? - // The check fetches the cached value from gprobe to reduce the load for this system - $urlparts = parse_url($my_url); + if (empty($my_url) || local_user()) { + return; + } - $result = Cache::get('gprobe:' . $urlparts['host']); - if ((!is_null($result)) && (in_array($result['network'], [Protocol::FEED, Protocol::PHANTOM]))) { - logger('DDoS attempt detected for ' . $urlparts['host'] . ' by ' . $_SERVER['REMOTE_ADDR'] . '. server data: ' . print_r($_SERVER, true), LOGGER_DEBUG); - return; - } + $arr = ['zrl' => $my_url, 'url' => $a->cmd]; + Addon::callHooks('zrl_init', $arr); - Worker::add(PRIORITY_LOW, 'GProbe', $my_url); - $arr = ['zrl' => $my_url, 'url' => $a->cmd]; - Addon::callHooks('zrl_init', $arr); + // Try to find the public contact entry of the visitor. + $cid = Contact::getIdForURL($my_url); + if (!$cid) { + logger('No contact record found for ' . $my_url, LOGGER_DEBUG); + return; + } - // Try to find the public contact entry of the visitor. - $cid = Contact::getIdForURL($my_url); - if (!$cid) { - logger('No contact record found for ' . $my_url, LOGGER_DEBUG); - return; - } + $contact = DBA::selectFirst('contact',['id', 'url'], ['id' => $cid]); - $contact = DBA::selectFirst('contact',['id', 'url'], ['id' => $cid]); + if (DBA::isResult($contact) && remote_user() && remote_user() == $contact['id']) { + logger('The visitor ' . $my_url . ' is already authenticated', LOGGER_DEBUG); + return; + } - if (DBA::isResult($contact) && remote_user() && remote_user() == $contact['id']) { - // The visitor is already authenticated. - return; - } + // Avoid endless loops + $cachekey = 'zrlInit:' . $my_url; + if (Cache::get($cachekey)) { + logger('URL ' . $my_url . ' already tried to authenticate.', LOGGER_DEBUG); + return; + } else { + Cache::set($cachekey, true, CACHE_MINUTE); + } - logger('Not authenticated. Invoking reverse magic-auth for ' . $my_url, LOGGER_DEBUG); + logger('Not authenticated. Invoking reverse magic-auth for ' . $my_url, LOGGER_DEBUG); - // Try to avoid recursion - but send them home to do a proper magic auth. - $query = str_replace(array('?zrl=', '&zid='), array('?rzrl=', '&rzrl='), $a->query_string); - // The other instance needs to know where to redirect. - $dest = urlencode(System::baseUrl() . '/' . $query); + Worker::add(PRIORITY_LOW, 'GProbe', $my_url); - // We need to extract the basebath from the profile url - // to redirect the visitors '/magic' module. - // Note: We should have the basepath of a contact also in the contact table. - $urlarr = explode('/profile/', $contact['url']); - $basepath = $urlarr[0]; + // Try to avoid recursion - but send them home to do a proper magic auth. + $query = str_replace(array('?zrl=', '&zid='), array('?rzrl=', '&rzrl='), $a->query_string); + // The other instance needs to know where to redirect. + $dest = urlencode(System::baseUrl() . '/' . $query); - if ($basepath != System::baseUrl() && !strstr($dest, '/magic') && !strstr($dest, '/rmagic')) { - $magic_path = $basepath . '/magic' . '?f=&owa=1&dest=' . $dest; - $serverret = Network::curl($magic_path); - if ($serverret->isSuccess()) { - goaway($magic_path); - } - } + // We need to extract the basebath from the profile url + // to redirect the visitors '/magic' module. + // Note: We should have the basepath of a contact also in the contact table. + $urlarr = explode('/profile/', $contact['url']); + $basepath = $urlarr[0]; + + if ($basepath != System::baseUrl() && !strstr($dest, '/magic') && !strstr($dest, '/rmagic')) { + $magic_path = $basepath . '/magic' . '?f=&owa=1&dest=' . $dest; + + // We have to check if the remote server does understand /magic without invoking something + $serverret = Network::curl($basepath . '/magic'); + if ($serverret->isSuccess()) { + logger('Doing magic auth for visitor ' . $my_url . ' to ' . $magic_path, LOGGER_DEBUG); + goaway($magic_path); } } } diff --git a/src/Network/Probe.php b/src/Network/Probe.php index 2c42643b4..23b97a5cd 100644 --- a/src/Network/Probe.php +++ b/src/Network/Probe.php @@ -953,13 +953,13 @@ class Probe $prof_data["addr"] = $data["addr"]; $prof_data["nick"] = $data["nick"]; - $prof_data["dfrn-request"] = $data["request"]; - $prof_data["dfrn-confirm"] = $data["confirm"]; - $prof_data["dfrn-notify"] = $data["notify"]; - $prof_data["dfrn-poll"] = $data["poll"]; - $prof_data["photo"] = $data["photo"]; - $prof_data["fn"] = $data["name"]; - $prof_data["key"] = $data["pubkey"]; + $prof_data["dfrn-request"] = defaults($data, 'request', null); + $prof_data["dfrn-confirm"] = defaults($data, 'confirm', null); + $prof_data["dfrn-notify"] = defaults($data, 'notify' , null); + $prof_data["dfrn-poll"] = defaults($data, 'poll' , null); + $prof_data["photo"] = defaults($data, 'photo' , null); + $prof_data["fn"] = defaults($data, 'name' , null); + $prof_data["key"] = defaults($data, 'pubkey' , null); logger("Result for profile ".$profile_link.": ".print_r($prof_data, true), LOGGER_DEBUG); diff --git a/src/Object/Post.php b/src/Object/Post.php index 48ffe65b7..34a5f8138 100644 --- a/src/Object/Post.php +++ b/src/Object/Post.php @@ -157,7 +157,7 @@ class Post extends BaseObject if ($item["event-id"] != 0) { $edpost = ["events/event/" . $item['event-id'], L10n::t("Edit")]; } else { - $edpost = ["editpost/" . $item['id'] . "/" . base64_encode($a->cmd), L10n::t("Edit")]; + $edpost = ["editpost/" . $item['id'], L10n::t("Edit")]; } $dropping = in_array($item['uid'], [0, local_user()]); } else { diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index eb52c0503..00f4e1702 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -3749,13 +3749,13 @@ class Diaspora * * @return string The message */ - private static function messageFromSignature(array $item, array $signature) + private static function messageFromSignature(array $item) { // Split the signed text - $signed_parts = explode(";", $signature['signed_text']); + $signed_parts = explode(";", $item['signed_text']); if ($item["deleted"]) { - $message = ["author" => $signature['signer'], + $message = ["author" => $item['signer'], "target_guid" => $signed_parts[0], "target_type" => $signed_parts[1]]; } elseif (in_array($item["verb"], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) { @@ -3764,7 +3764,7 @@ class Diaspora "parent_guid" => $signed_parts[3], "parent_type" => $signed_parts[2], "positive" => $signed_parts[0], - "author_signature" => $signature['signature'], + "author_signature" => $item['signature'], "parent_author_signature" => ""]; } else { // Remove the comment guid @@ -3783,7 +3783,7 @@ class Diaspora "guid" => $guid, "parent_guid" => $parent_guid, "text" => implode(";", $signed_parts), - "author_signature" => $signature['signature'], + "author_signature" => $item['signature'], "parent_author_signature" => ""]; } return $message; @@ -3811,20 +3811,12 @@ class Diaspora logger("Got relayable data ".$type." for item ".$item["guid"]." (".$item["id"].")", LOGGER_DEBUG); - // fetch the original signature - $fields = ['signed_text', 'signature', 'signer']; - $signature = DBA::selectFirst('sign', $fields, ['iid' => $item["id"]]); - if (!DBA::isResult($signature)) { - logger("Couldn't fetch signatur for item ".$item["guid"]." (".$item["id"].")", LOGGER_DEBUG); - return false; - } - // Old way - is used by the internal Friendica functions /// @todo Change all signatur storing functions to the new format - if ($signature['signed_text'] && $signature['signature'] && $signature['signer']) { - $message = self::messageFromSignature($item, $signature); + if ($item['signed_text'] && $item['signature'] && $item['signer']) { + $message = self::messageFromSignature($item); } else {// New way - $msg = json_decode($signature['signed_text'], true); + $msg = json_decode($item['signed_text'], true); $message = []; if (is_array($msg)) { @@ -3841,7 +3833,7 @@ class Diaspora $message[$field] = $data; } } else { - logger("Signature text for item ".$item["guid"]." (".$item["id"].") couldn't be extracted: ".$signature['signed_text'], LOGGER_DEBUG); + logger("Signature text for item ".$item["guid"]." (".$item["id"].") couldn't be extracted: ".$item['signed_text'], LOGGER_DEBUG); } } diff --git a/src/Worker/OnePoll.php b/src/Worker/OnePoll.php index 3c16af75f..0066a04ae 100644 --- a/src/Worker/OnePoll.php +++ b/src/Worker/OnePoll.php @@ -333,7 +333,7 @@ class OnePoll $curlResult = Network::curl($contact['poll'], false, $redirects, ['cookiejar' => $cookiejar]); unlink($cookiejar); - if (!$curlResult->isTimeout()) { + if ($curlResult->isTimeout()) { // set the last-update so we don't keep polling DBA::update('contact', ['last-update' => DateTimeFormat::utcNow()], ['id' => $contact['id']]); Contact::markForArchival($contact);