]> git.mxchange.org Git - friendica.git/blobdiff - include/diaspora.php
events: use best_link_url() and zrl() for the author_link (author link as magic-link)
[friendica.git] / include / diaspora.php
index cd857ba6bdb9868da06f2d0219e83d1e850a5100..60c7909184360baa6ca007061372d7a98726cb3f 100644 (file)
@@ -11,6 +11,8 @@
 use Friendica\App;
 use Friendica\Core\System;
 use Friendica\Core\Config;
+use Friendica\Core\PConfig;
+use Friendica\Network\Probe;
 
 require_once 'include/items.php';
 require_once 'include/bb2diaspora.php';
@@ -118,7 +120,7 @@ class Diaspora {
         */
        private static function verify_magic_envelope($envelope) {
 
-               $basedom = parse_xml_string($envelope, false);
+               $basedom = parse_xml_string($envelope);
 
                if (!is_object($basedom)) {
                        logger("Envelope is no XML file");
@@ -227,7 +229,7 @@ class Diaspora {
                $basedom = parse_xml_string($xml);
 
                if (!is_object($basedom)) {
-                       logger('Received data does not seem to be an XML. Discarding.');
+                       logger('Received data does not seem to be an XML. Discarding. '.$xml);
                        http_status_exit(400);
                }
 
@@ -287,6 +289,11 @@ class Diaspora {
                        $public = true;
                        $author_link = str_replace('acct:','',$children->header->author_id);
                } else {
+                       // This happens with posts from a relais
+                       if (!$importer) {
+                               logger("This is no private post in the old format", LOGGER_DEBUG);
+                               return false;
+                       }
 
                        $encrypted_header = json_decode(base64_decode($children->encrypted_header));
 
@@ -304,7 +311,7 @@ class Diaspora {
                        $decrypted = self::aes_decrypt($outer_key, $outer_iv, $ciphertext);
 
                        logger('decrypted: '.$decrypted, LOGGER_DEBUG);
-                       $idom = parse_xml_string($decrypted,false);
+                       $idom = parse_xml_string($decrypted);
 
                        $inner_iv = base64_decode($idom->iv);
                        $inner_aes_key = base64_decode($idom->aes_key);
@@ -492,6 +499,9 @@ class Diaspora {
                logger("Received message type ".$type." from ".$sender." for user ".$importer["uid"], LOGGER_DEBUG);
 
                switch ($type) {
+                       case "account_migration":
+                               return self::receiveAccountMigration($importer, $fields);
+
                        case "account_deletion":
                                return self::receive_account_deletion($importer, $fields);
 
@@ -551,7 +561,7 @@ class Diaspora {
         */
        private static function valid_posting($msg) {
 
-               $data = parse_xml_string($msg["message"], false);
+               $data = parse_xml_string($msg["message"]);
 
                if (!is_object($data)) {
                        logger("No valid XML ".$msg["message"], LOGGER_DEBUG);
@@ -1124,7 +1134,8 @@ class Diaspora {
                        return false;
 
                // This will work for new Diaspora servers and Friendica servers from 3.5
-               $source_url = $server."/fetch/post/".$guid;
+               $source_url = $server."/fetch/post/".urlencode($guid);
+
                logger("Fetch post from ".$source_url, LOGGER_DEBUG);
 
                $envelope = fetch_url($source_url);
@@ -1140,7 +1151,7 @@ class Diaspora {
 
                // This will work for older Diaspora and Friendica servers
                if (!$x) {
-                       $source_url = $server."/p/".$guid.".xml";
+                       $source_url = $server."/p/".urlencode($guid).".xml";
                        logger("Fetch post from ".$source_url, LOGGER_DEBUG);
 
                        $x = fetch_url($source_url);
@@ -1148,7 +1159,7 @@ class Diaspora {
                                return false;
                }
 
-               $source_xml = parse_xml_string($x, false);
+               $source_xml = parse_xml_string($x);
 
                if (!is_object($source_xml))
                        return false;
@@ -1312,6 +1323,91 @@ class Diaspora {
                }
        }
 
+       /**
+        * @brief Receives account migration
+        *
+        * @param array $importer Array of the importer user
+        * @param object $data The message object
+        *
+        * @return bool Success
+        */
+       private static function receiveAccountMigration($importer, $data) {
+               $old_handle = notags(unxmlify($data->author));
+               $new_handle = notags(unxmlify($data->profile->author));
+               $signature = notags(unxmlify($data->signature));
+
+               $contact = self::contact_by_handle($importer["uid"], $old_handle);
+               if (!$contact) {
+                       logger("cannot find contact for sender: ".$old_handle." and user ".$importer["uid"]);
+                       return false;
+               }
+
+               logger("Got migration for ".$old_handle.", to ".$new_handle." with user ".$importer["uid"]);
+
+               // Check signature
+               $signed_text = 'AccountMigration:'.$old_handle.':'.$new_handle;
+               $key = self::key($old_handle);
+               if (!rsa_verify($signed_text, $signature, $key, "sha256")) {
+                       logger('No valid signature for migration.');
+                       return false;
+               }
+
+               // Update the profile
+               self::receive_profile($importer, $data->profile);
+
+               // change the technical stuff in contact and gcontact
+               $data = Probe::uri($new_handle);
+               if ($data['network'] == NETWORK_PHANTOM) {
+                       logger('Account for '.$new_handle." couldn't be probed.");
+                       return false;
+               }
+
+               $fields = array('url' => $data['url'], 'nurl' => normalise_link($data['url']),
+                               'name' => $data['name'], 'nick' => $data['nick'],
+                               'addr' => $data['addr'], 'batch' => $data['batch'],
+                               'notify' => $data['notify'], 'poll' => $data['poll'],
+                               'network' => $data['network']);
+
+               dba::update('contact', $fields, array('addr' => $old_handle));
+
+               $fields = array('url' => $data['url'], 'nurl' => normalise_link($data['url']),
+                               'name' => $data['name'], 'nick' => $data['nick'],
+                               'addr' => $data['addr'], 'connect' => $data['addr'],
+                               'notify' => $data['notify'], 'photo' => $data['photo'],
+                               'server_url' => $data['baseurl'], 'network' => $data['network']);
+
+               dba::update('gcontact', $fields, array('addr' => $old_handle));
+
+               logger('Contacts are updated.');
+
+               // update items
+               /// @todo This is an extreme performance killer
+               $fields = array(
+                       'owner-link' => array($contact["url"], $data["url"]),
+                       'author-link' => array($contact["url"], $data["url"]),
+               );
+               foreach ($fields as $n=>$f) {
+                       $r = q("SELECT `id` FROM `item` WHERE `%s` = '%s' AND `uid` = %d LIMIT 1",
+                                       $n, dbesc($f[0]),
+                                       intval($importer["uid"]));
+
+                       if (dbm::is_result($r)) {
+                               $x = q("UPDATE `item` SET `%s` = '%s' WHERE `%s` = '%s' AND `uid` = %d",
+                                               $n, dbesc($f[1]),
+                                               $n, dbesc($f[0]),
+                                               intval($importer["uid"]));
+
+                               if ($x === false) {
+                                       return false;
+                               }
+                       }
+               }
+
+               logger('Items are updated.');
+
+               return true;
+       }
+
        /**
         * @brief Processes an account deletion
         *
@@ -2326,7 +2422,7 @@ class Diaspora {
                        // If it is a reshared post from another network then reformat to avoid display problems with two share elements
                        if (self::is_reshare($r[0]["body"], true)) {
                                $r = array();
-                       } elseif (self::is_reshare($r[0]["body"], false)) {
+                       } elseif (self::is_reshare($r[0]["body"], false) || strstr($r[0]["body"], "[share")) {
                                $r[0]["body"] = diaspora2bb(bb2diaspora($r[0]["body"]));
 
                                $r[0]["body"] = self::replace_people_guid($r[0]["body"], $r[0]["author-link"]);
@@ -2953,6 +3049,32 @@ class Diaspora {
                return $return_code;
        }
 
+       /**
+        * @brief sends an account migration
+        *
+        * @param array $owner the array of the item owner
+        * @param array $contact Target of the communication
+        * @param int $uid User ID
+        *
+        * @return int The result of the transmission
+        */
+       public static function sendAccountMigration($owner, $contact, $uid) {
+
+               $old_handle = PConfig::get($uid, 'system', 'previous_addr');
+               $profile = self::createProfileData($uid);
+
+               $signed_text = 'AccountMigration:'.$old_handle.':'.$profile['author'];
+               $signature = base64_encode(rsa_sign($signed_text, $owner["uprvkey"], "sha256"));
+
+               $message = array("author" => $old_handle,
+                               "profile" => $profile,
+                               "signature" => $signature);
+
+               logger("Send account migration ".print_r($message, true), LOGGER_DEBUG);
+
+               return self::build_and_transmit($owner, $contact, "account_migration", $message);
+       }
+
        /**
         * @brief Sends a "share" message
         *
@@ -3447,11 +3569,11 @@ class Diaspora {
                // Split the signed text
                $signed_parts = explode(";", $signature['signed_text']);
 
-               if ($item["deleted"])
+               if ($item["deleted"]) {
                        $message = array("author" => $signature['signer'],
                                        "target_guid" => $signed_parts[0],
                                        "target_type" => $signed_parts[1]);
-               elseif ($item['verb'] === ACTIVITY_LIKE)
+               } elseif (in_array($item["verb"], array(ACTIVITY_LIKE, ACTIVITY_DISLIKE))) {
                        $message = array("author" => $signed_parts[4],
                                        "guid" => $signed_parts[1],
                                        "parent_guid" => $signed_parts[3],
@@ -3459,7 +3581,7 @@ class Diaspora {
                                        "positive" => $signed_parts[0],
                                        "author_signature" => $signature['signature'],
                                        "parent_author_signature" => "");
-               else {
+               else {
                        // Remove the comment guid
                        $guid = array_shift($signed_parts);
 
@@ -3494,12 +3616,13 @@ class Diaspora {
         */
        public static function send_relay($item, $owner, $contact, $public_batch = false) {
 
-               if ($item["deleted"])
+               if ($item["deleted"]) {
                        return self::send_retraction($item, $owner, $contact, $public_batch, true);
-               elseif ($item['verb'] === ACTIVITY_LIKE)
+               } elseif (in_array($item["verb"], array(ACTIVITY_LIKE, ACTIVITY_DISLIKE))) {
                        $type = "like";
-               else
+               } else {
                        $type = "comment";
+               }
 
                logger("Got relayable data ".$type." for item ".$item["guid"]." (".$item["id"].")", LOGGER_DEBUG);
 
@@ -3566,7 +3689,7 @@ class Diaspora {
 
                if ($item['id'] == $item['parent']) {
                        $target_type = "Post";
-               } elseif ($item["verb"] == ACTIVITY_LIKE) {
+               } elseif (in_array($item["verb"], array(ACTIVITY_LIKE, ACTIVITY_DISLIKE))) {
                        $target_type = "Like";
                } else {
                        $target_type = "Comment";
@@ -3643,25 +3766,13 @@ class Diaspora {
        }
 
        /**
-        * @brief Sends profile data
+        * @brief Create profile data
         *
         * @param int $uid The user id
+        *
+        * @return array The profile data
         */
-       public static function send_profile($uid, $recips = false) {
-
-               if (!$uid)
-                       return;
-
-               if (!$recips)
-                       $recips = q("SELECT `id`,`name`,`network`,`pubkey`,`notify` FROM `contact` WHERE `network` = '%s'
-                               AND `uid` = %d AND `rel` != %d",
-                               dbesc(NETWORK_DIASPORA),
-                               intval($uid),
-                               intval(CONTACT_IS_SHARING)
-                       );
-               if (!$recips)
-                       return;
-
+       private static function createProfileData($uid) {
                $r = q("SELECT `profile`.`uid` AS `profile_uid`, `profile`.* , `user`.*, `user`.`prvkey` AS `uprvkey`, `contact`.`addr`
                        FROM `profile`
                        INNER JOIN `user` ON `profile`.`uid` = `user`.`uid`
@@ -3670,8 +3781,9 @@ class Diaspora {
                        intval($uid)
                );
 
-               if (!$r)
-                       return;
+               if (!$r) {
+                       return array();
+               }
 
                $profile = $r[0];
 
@@ -3709,7 +3821,7 @@ class Diaspora {
                        $tags = trim($tags);
                }
 
-               $message = array("author" => $handle,
+               return array("author" => $handle,
                                "first_name" => $first,
                                "last_name" => $last,
                                "image_url" => $large,
@@ -3722,6 +3834,29 @@ class Diaspora {
                                "searchable" => $searchable,
                                "nsfw" => "false",
                                "tag_string" => $tags);
+       }
+
+       /**
+        * @brief Sends profile data
+        *
+        * @param int $uid The user id
+        */
+       public static function send_profile($uid, $recips = false) {
+
+               if (!$uid)
+                       return;
+
+               if (!$recips)
+                       $recips = q("SELECT `id`,`name`,`network`,`pubkey`,`notify` FROM `contact` WHERE `network` = '%s'
+                               AND `uid` = %d AND `rel` != %d",
+                               dbesc(NETWORK_DIASPORA),
+                               intval($uid),
+                               intval(CONTACT_IS_SHARING)
+                       );
+               if (!$recips)
+                       return;
+
+               $message = self::createProfileData($uid);
 
                foreach ($recips as $recip) {
                        logger("Send updated profile data for user ".$uid." to contact ".$recip["id"], LOGGER_DEBUG);