]> git.mxchange.org Git - friendica.git/blobdiff - src/Protocol/DFRN.php
Merge pull request #6009 from vinzv/develop
[friendica.git] / src / Protocol / DFRN.php
index 55d635d2e0b0460f5b4b843f1419a6ce72ed1c55..1dcc1197a5d3ae70cc614700ac2fbb1fe89a0212 100644 (file)
@@ -52,6 +52,46 @@ class DFRN
        const REPLY = 1;                // Regular reply that is stored locally
        const REPLY_RC = 2;     // Reply that will be relayed
 
+       /**
+        * @brief Generates an array of contact and user for DFRN imports
+        *
+        * This array contains not only the receiver but also the sender of the message.
+        *
+        * @param integer $cid Contact id
+        * @param integer $uid User id
+        *
+        * @return array importer
+        */
+       public static function getImporter($cid, $uid = 0)
+       {
+               $condition = ['id' => $cid, 'blocked' => false, 'pending' => false];
+               $contact = DBA::selectFirst('contact', [], $condition);
+               if (!DBA::isResult($contact)) {
+                       return [];
+               }
+
+               $contact['cpubkey'] = $contact['pubkey'];
+               $contact['cprvkey'] = $contact['prvkey'];
+               $contact['senderName'] = $contact['name'];
+
+               if ($uid != 0) {
+                       $condition = ['uid' => $uid, 'account_expired' => false, 'account_removed' => false];
+                       $user = DBA::selectFirst('user', [], $condition);
+                       if (!DBA::isResult($user)) {
+                               return [];
+                       }
+
+                       $user['importer_uid'] = $user['uid'];
+                       $user['uprvkey'] = $user['prvkey'];
+               } else {
+                       $user = ['importer_uid' => 0, 'uprvkey' => '', 'timezone' => 'UTC',
+                               'nickname' => '', 'sprvkey' => '', 'spubkey' => '',
+                               'page-flags' => 0, 'account-type' => 0, 'prvnets' => 0];
+               }
+
+               return array_merge($contact, $user);
+       }
+
        /**
         * @brief Generates the atom entries for delivery.php
         *
@@ -176,7 +216,6 @@ class DFRN
                        }
 
                        $contact = $r[0];
-                       include_once 'include/security.php';
 
                        $set = PermissionSet::get($owner_id, $contact['id']);
 
@@ -607,7 +646,7 @@ class DFRN
                XML::addElement($doc, $author, "dfrn:handle", $owner["addr"], $attributes);
 
                $attributes = ["rel" => "photo", "type" => "image/jpeg",
-                                       "media:width" => 175, "media:height" => 175, "href" => $owner['photo']];
+                                       "media:width" => 300, "media:height" => 300, "href" => $owner['photo']];
 
                if (!$public || !$hidewall) {
                        $attributes["dfrn:updated"] = $picdate;
@@ -1056,13 +1095,10 @@ class DFRN
                }
 
                foreach ($mentioned as $mention) {
-                       $r = q(
-                               "SELECT `forum`, `prv` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s'",
-                               intval($owner["uid"]),
-                               DBA::escape(normalise_link($mention))
-                       );
+                       $condition = ['uid' => $owner["uid"], 'nurl' => normalise_link($mention)];
+                       $contact = DBA::selectFirst('contact', ['forum', 'prv'], $condition);
 
-                       if (DBA::isResult($r) && ($r[0]["forum"] || $r[0]["prv"])) {
+                       if (DBA::isResult($contact) && ($contact["forum"] || $contact["prv"])) {
                                XML::addElement(
                                        $doc,
                                        $entry,
@@ -1127,15 +1163,17 @@ class DFRN
         * @return int Deliver status. Negative values mean an error.
         * @todo Add array type-hint for $owner, $contact
         */
-       public static function deliver($owner, $contact, $atom, $dissolve = false)
+       public static function deliver($owner, $contact, $atom, $dissolve = false, $legacy_transport = false)
        {
                $a = get_app();
 
                // At first try the Diaspora transport layer
-               $ret = self::transmit($owner, $contact, $atom);
-               if ($ret >= 200) {
-                       logger('Delivery via Diaspora transport layer was successful with status ' . $ret);
-                       return $ret;
+               if (!$dissolve && !$legacy_transport) {
+                       $curlResult = self::transmit($owner, $contact, $atom);
+                       if ($curlResult >= 200) {
+                               logger('Delivery via Diaspora transport layer was successful with status ' . $curlResult);
+                               return $curlResult;
+                       }
                }
 
                $idtosend = $orig_id = (($contact['dfrn-id']) ? $contact['dfrn-id'] : $contact['issued-id']);
@@ -1172,16 +1210,16 @@ class DFRN
 
                logger('dfrn_deliver: ' . $url);
 
-               $ret = Network::curl($url);
+               $curlResult = Network::curl($url);
 
-               if (!empty($ret["errno"]) && ($ret['errno'] == CURLE_OPERATION_TIMEDOUT)) {
+               if ($curlResult->isTimeout()) {
                        Contact::markForArchival($contact);
                        return -2; // timed out
                }
 
-               $xml = $ret['body'];
+               $xml = $curlResult->getBody();
 
-               $curl_stat = $a->get_curl_code();
+               $curl_stat = $curlResult->getReturnCode();
                if (empty($curl_stat)) {
                        Contact::markForArchival($contact);
                        return -3; // timed out
@@ -1232,14 +1270,11 @@ class DFRN
                $final_dfrn_id = '';
 
                if ($perm) {
-                       if ((($perm == 'rw') && (! intval($contact['writable'])))
-                               || (($perm == 'r') && (intval($contact['writable'])))
+                       if ((($perm == 'rw') && !intval($contact['writable']))
+                               || (($perm == 'r') && intval($contact['writable']))
                        ) {
-                               q(
-                                       "update contact set writable = %d where id = %d",
-                                       intval(($perm == 'rw') ? 1 : 0),
-                                       intval($contact['id'])
-                               );
+                               DBA::update('contact', ['writable' => ($perm == 'rw')], ['id' => $contact['id']]);
+
                                $contact['writable'] = (string) 1 - intval($contact['writable']);
                        }
                }
@@ -1297,6 +1332,7 @@ class DFRN
                                        $key = openssl_random_pseudo_bytes(16);
                                        $data = self::aesEncrypt($postvars['data'], $key);
                                        break;
+
                                default:
                                        logger("rino: invalid requested version '$rino_remote_version'");
                                        Contact::markForArchival($contact);
@@ -1331,17 +1367,19 @@ class DFRN
 
                logger('dfrn_deliver: ' . "SENDING: " . print_r($postvars, true), LOGGER_DATA);
 
-               $xml = Network::post($contact['notify'], $postvars);
+               $postResult = Network::post($contact['notify'], $postvars);
+
+               $xml = $postResult->getBody();
 
                logger('dfrn_deliver: ' . "RECEIVED: " . $xml, LOGGER_DATA);
 
-               $curl_stat = $a->get_curl_code();
+               $curl_stat = $postResult->getReturnCode();
                if (empty($curl_stat) || empty($xml)) {
                        Contact::markForArchival($contact);
                        return -9; // timed out
                }
 
-               if (($curl_stat == 503) && stristr($a->get_curl_headers(), 'retry-after')) {
+               if (($curl_stat == 503) && stristr($postResult->getHeader(), 'retry-after')) {
                        Contact::markForArchival($contact);
                        return -10;
                }
@@ -1430,16 +1468,17 @@ class DFRN
 
                $content_type = ($public_batch ? "application/magic-envelope+xml" : "application/json");
 
-               $xml = Network::post($dest_url, $envelope, ["Content-Type: ".$content_type]);
+               $postResult = Network::post($dest_url, $envelope, ["Content-Type: ".$content_type]);
+               $xml = $postResult->getBody();
 
-               $curl_stat = $a->get_curl_code();
+               $curl_stat = $postResult->getReturnCode();
                if (empty($curl_stat) || empty($xml)) {
                        logger('Empty answer from ' . $contact['id'] . ' - ' . $dest_url);
                        Contact::markForArchival($contact);
                        return -9; // timed out
                }
 
-               if (($curl_stat == 503) && (stristr($a->get_curl_headers(), 'retry-after'))) {
+               if (($curl_stat == 503) && (stristr($postResult->getHeader(), 'retry-after'))) {
                        Contact::markForArchival($contact);
                        return -10;
                }
@@ -1480,15 +1519,9 @@ class DFRN
        private static function birthdayEvent($contact, $birthday)
        {
                // Check for duplicates
-               $r = q(
-                       "SELECT `id` FROM `event` WHERE `uid` = %d AND `cid` = %d AND `start` = '%s' AND `type` = '%s' LIMIT 1",
-                       intval($contact['uid']),
-                       intval($contact['id']),
-                       DBA::escape(DateTimeFormat::utc($birthday)),
-                       DBA::escape('birthday')
-               );
-
-               if (DBA::isResult($r)) {
+               $condition = ['uid' => $contact['uid'], 'cid' => $contact['id'],
+                       'start' => DateTimeFormat::utc($birthday), 'type' => 'birthday'];
+               if (DBA::exists('event', $condition)) {
                        return;
                }
 
@@ -1580,6 +1613,20 @@ class DFRN
                        $author["avatar"] = current($avatarlist);
                }
 
+               if (empty($author['avatar']) && !empty($author['link'])) {
+                       $cid = Contact::getIdForURL($author['link'], 0);
+                       if (!empty($cid)) {
+                               $contact = DBA::selectFirst('contact', ['avatar'], ['id' => $cid]);
+                               if (DBA::isResult($contact)) {
+                                       $author['avatar'] = $contact['avatar'];
+                               }
+                       }
+               }
+
+               if (empty($author['avatar'])) {
+                       logger('Empty author: ' . $xml);
+               }
+
                if (DBA::isResult($contact_old) && !$onlyfetch) {
                        logger("Check if contact details for contact " . $contact_old["id"] . " (" . $contact_old["nick"] . ") have to be updated.", LOGGER_DEBUG);
 
@@ -1709,6 +1756,10 @@ class DFRN
                        // Update check for this field has to be done differently
                        $datefields = ["name-date", "uri-date"];
                        foreach ($datefields as $field) {
+                               // The date fields arrives as '2018-07-17T10:44:45Z' - the database return '2018-07-17 10:44:45'
+                               // The fields have to be in the same format to be comparable, since strtotime does add timezones.
+                               $contact[$field] = DateTimeFormat::utc($contact[$field]);
+
                                if (strtotime($contact[$field]) > strtotime($contact_old[$field])) {
                                        logger("Difference for contact " . $contact["id"] . " in field '" . $field . "'. New value: '" . $contact[$field] . "', old value '" . $contact_old[$field] . "'", LOGGER_DEBUG);
                                        $update = true;
@@ -1900,13 +1951,6 @@ class DFRN
 
                // Does our member already have a friend matching this description?
 
-               $r = q(
-                       "SELECT `id` FROM `contact` WHERE `name` = '%s' AND `nurl` = '%s' AND `uid` = %d LIMIT 1",
-                       DBA::escape($suggest["name"]),
-                       DBA::escape(normalise_link($suggest["url"])),
-                       intval($suggest["uid"])
-               );
-
                /*
                 * The valid result means the friend we're about to send a friend
                 * suggestion already has them in their contact, which means no further
@@ -1914,37 +1958,29 @@ class DFRN
                 *
                 * @see https://github.com/friendica/friendica/pull/3254#discussion_r107315246
                 */
-               if (DBA::isResult($r)) {
+               $condition = ['name' => $suggest["name"], 'nurl' => normalise_link($suggest["url"]),
+                       'uid' => $suggest["uid"]];
+               if (DBA::exists('contact', $condition)) {
                        return false;
                }
 
                // Do we already have an fcontact record for this person?
 
                $fid = 0;
-               $r = q(
-                       "SELECT `id` FROM `fcontact` WHERE `url` = '%s' AND `name` = '%s' AND `request` = '%s' LIMIT 1",
-                       DBA::escape($suggest["url"]),
-                       DBA::escape($suggest["name"]),
-                       DBA::escape($suggest["request"])
-               );
-               if (DBA::isResult($r)) {
-                       $fid = $r[0]["id"];
+               $condition = ['url' => $suggest["url"], 'name' => $suggest["name"], 'request' => $suggest["request"]];
+               $fcontact = DBA::selectFirst('fcontact', ['id'], $condition);
+               if (DBA::isResult($fcontact)) {
+                       $fid = $fcontact["id"];
 
-                       // OK, we do. Do we already have an introduction for this person ?
-                       $r = q(
-                               "SELECT `id` FROM `intro` WHERE `uid` = %d AND `fid` = %d LIMIT 1",
-                               intval($suggest["uid"]),
-                               intval($fid)
-                       );
-
-                       /*
-                        * The valid result means the friend we're about to send a friend
-                        * suggestion already has them in their contact, which means no further
-                        * action is required.
-                        *
-                        * @see https://github.com/friendica/friendica/pull/3254#discussion_r107315246
-                        */
-                       if (DBA::isResult($r)) {
+                       // OK, we do. Do we already have an introduction for this person?
+                       if (DBA::exists('intro', ['uid' => $suggest["uid"], 'fid' => $fid])) {
+                               /*
+                                * The valid result means the friend we're about to send a friend
+                                * suggestion already has them in their contact, which means no further
+                                * action is required.
+                                *
+                                * @see https://github.com/friendica/friendica/pull/3254#discussion_r107315246
+                                */
                                return false;
                        }
                }
@@ -1957,18 +1993,15 @@ class DFRN
                                DBA::escape($suggest["request"])
                        );
                }
-               $r = q(
-                       "SELECT `id` FROM `fcontact` WHERE `url` = '%s' AND `name` = '%s' AND `request` = '%s' LIMIT 1",
-                       DBA::escape($suggest["url"]),
-                       DBA::escape($suggest["name"]),
-                       DBA::escape($suggest["request"])
-               );
+
+               $condition = ['url' => $suggest["url"], 'name' => $suggest["name"], 'request' => $suggest["request"]];
+               $fcontact = DBA::selectFirst('fcontact', ['id'], $condition);
 
                /*
                 * If no record in fcontact is found, below INSERT statement will not
                 * link an introduction to it.
                 */
-               if (!DBA::isResult($r)) {
+               if (!DBA::isResult($fcontact)) {
                        // Database record did not get created. Quietly give up.
                        killme();
                }
@@ -2224,6 +2257,11 @@ class DFRN
                        if ($Blink && link_compare($Blink, System::baseUrl() . "/profile/" . $importer["nickname"])) {
                                $author = DBA::selectFirst('contact', ['name', 'thumb', 'url'], ['id' => $item['author-id']]);
 
+                               $item['id'] = $posted_id;
+
+                               $parent = Item::selectFirst(['id'], ['uri' => $item['parent-uri'], 'uid' => $importer["importer_uid"]]);
+                               $item["parent"] = $parent['id'];
+
                                // send a notification
                                notification(
                                        [
@@ -2313,6 +2351,12 @@ class DFRN
                                if (Item::exists($condition)) {
                                        return false;
                                }
+
+                               // The owner of an activity must be the author
+                               $item["owner-name"] = $item["author-name"];
+                               $item["owner-link"] = $item["author-link"];
+                               $item["owner-avatar"] = $item["author-avatar"];
+                               $item["owner-id"] = $item["author-id"];
                        } else {
                                $is_like = false;
                        }
@@ -2374,8 +2418,11 @@ class DFRN
                                                break;
                                        case "enclosure":
                                                $enclosure = $href;
-                                               if (strlen($item["attach"])) {
+
+                                               if (!empty($item["attach"])) {
                                                        $item["attach"] .= ",";
+                                               } else {
+                                                       $item["attach"] = "";
                                                }
 
                                                $item["attach"] .= '[attach]href="' . $href . '" length="' . $length . '" type="' . $type . '" title="' . $title . '"[/attach]';
@@ -2421,7 +2468,7 @@ class DFRN
                }
 
                // Fetch the owner
-               $owner = self::fetchauthor($xpath, $entry, $importer, "dfrn:owner", true);
+               $owner = self::fetchauthor($xpath, $entry, $importer, "dfrn:owner", true, $xml);
 
                $owner_unknown = (isset($owner["contact-unknown"]) && $owner["contact-unknown"]);
 
@@ -2431,7 +2478,7 @@ class DFRN
                $item["owner-id"] = Contact::getIdForURL($owner["link"], 0);
 
                // fetch the author
-               $author = self::fetchauthor($xpath, $entry, $importer, "atom:author", true);
+               $author = self::fetchauthor($xpath, $entry, $importer, "atom:author", true, $xml);
 
                $item["author-name"] = $author["name"];
                $item["author-link"] = $author["link"];
@@ -2451,7 +2498,7 @@ class DFRN
 
                /// @todo Do we really need this check for HTML elements? (It was copied from the old function)
                if ((strpos($item['body'], '<') !== false) && (strpos($item['body'], '>') !== false)) {
-                       $base_url = get_app()->get_baseurl();
+                       $base_url = get_app()->getBaseURL();
                        $item['body'] = reltoabs($item['body'], $base_url);
 
                        $item['body'] = html2bb_video($item['body']);
@@ -2636,13 +2683,10 @@ class DFRN
                                        $ev["guid"]    = $item["guid"];
                                        $ev["plink"]   = $item["plink"];
 
-                                       $r = q(
-                                               "SELECT `id` FROM `event` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
-                                               DBA::escape($item["uri"]),
-                                               intval($importer["importer_uid"])
-                                       );
-                                       if (DBA::isResult($r)) {
-                                               $ev["id"] = $r[0]["id"];
+                                       $condition = ['uri' => $item["uri"], 'uid' => $importer["importer_uid"]];
+                                       $event = DBA::selectFirst('event', ['id'], $condition);
+                                       if (DBA::isResult($event)) {
+                                               $ev["id"] = $event["id"];
                                        }
 
                                        $event_id = Event::store($ev);
@@ -2993,7 +3037,7 @@ class DFRN
 
                        logger('auto_redir: ' . $r[0]['name'] . ' ' . $sec, LOGGER_DEBUG);
                        $dest = (($url) ? '&destination_url=' . $url : '');
-                       goaway($r[0]['poll'] . '?dfrn_id=' . $dfrn_id
+                       System::externalRedirect($r[0]['poll'] . '?dfrn_id=' . $dfrn_id
                                . '&dfrn_version=' . DFRN_PROTOCOL_VERSION . '&type=profile&sec=' . $sec . $dest);
                }
 
@@ -3025,23 +3069,21 @@ class DFRN
                        return false;
                }
 
-               $u = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1",
-                       intval($uid)
-               );
-               if (!DBA::isResult($u)) {
+               $user = DBA::selectFirst('user', ['page-flags', 'nickname'], ['uid' => $uid]);
+               if (!DBA::isResult($user)) {
                        return false;
                }
 
-               $community_page = ($u[0]['page-flags'] == Contact::PAGE_COMMUNITY);
-               $prvgroup = ($u[0]['page-flags'] == Contact::PAGE_PRVGROUP);
+               $community_page = ($user['page-flags'] == Contact::PAGE_COMMUNITY);
+               $prvgroup = ($user['page-flags'] == Contact::PAGE_PRVGROUP);
 
-               $link = normalise_link(System::baseUrl() . '/profile/' . $u[0]['nickname']);
+               $link = normalise_link(System::baseUrl() . '/profile/' . $user['nickname']);
 
                /*
                 * Diaspora uses their own hardwired link URL in @-tags
                 * instead of the one we supply with webfinger
                 */
-               $dlink = normalise_link(System::baseUrl() . '/u/' . $u[0]['nickname']);
+               $dlink = normalise_link(System::baseUrl() . '/u/' . $user['nickname']);
 
                $cnt = preg_match_all('/[\@\!]\[url\=(.*?)\](.*?)\[\/url\]/ism', $item['body'], $matches, PREG_SET_ORDER);
                if ($cnt) {