]> git.mxchange.org Git - friendica.git/blobdiff - include/items.php
Merge pull request #1683 from fabrixxm/issue-1655
[friendica.git] / include / items.php
index 3832b80b318f30f51a0f8b716d87c752f65ac855..475980d2dc972584aaef409cbf64134566b53c1a 100644 (file)
@@ -9,12 +9,16 @@ require_once('include/tags.php');
 require_once('include/files.php');
 require_once('include/text.php');
 require_once('include/email.php');
-require_once('include/ostatus_conversation.php');
 require_once('include/threads.php');
 require_once('include/socgraph.php');
+require_once('include/plaintext.php');
+require_once('include/ostatus.php');
 require_once('mod/share.php');
 
-function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) {
+require_once('library/defuse/php-encryption-1.2.1/Crypto.php');
+
+
+function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0, $forpubsub = false) {
 
 
        $sitefeed    = ((strlen($owner_nick)) ? false : true); // not yet implemented, need to rewrite huge chunks of following logic
@@ -37,7 +41,7 @@ function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0)
 
        // default permissions - anonymous user
 
-       $sql_extra = " AND `allow_cid` = '' AND `allow_gid` = '' AND `deny_cid`  = '' AND `deny_gid`  = '' ";
+       $sql_extra = " AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid`  = '' AND `item`.`deny_gid`  = '' ";
 
        $r = q("SELECT `contact`.*, `user`.`uid` AS `user_uid`, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags`
                FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid`
@@ -55,6 +59,7 @@ function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0)
        $birthday = feed_birthday($owner_id,$owner['timezone']);
 
        $sql_post_table = "";
+       $visibility = "";
 
        if(! $public_feed) {
 
@@ -114,6 +119,19 @@ function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0)
        else
                $sort = 'ASC';
 
+       // Include answers to status.net posts in pubsub feeds
+       if($forpubsub) {
+               $sql_post_table = "INNER JOIN `thread` ON `thread`.`iid` = `item`.`parent`
+                               LEFT JOIN `item` AS `thritem` ON `thritem`.`uri`=`item`.`thr-parent` AND `thritem`.`uid`=`item`.`uid`";
+               $visibility = sprintf("AND (`item`.`parent` = `item`.`id`) OR (`item`.`network` = '%s' AND ((`thread`.`network`='%s') OR (`thritem`.`network` = '%s')))",
+                                       dbesc(NETWORK_DFRN), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_OSTATUS));
+               $date_field = "`received`";
+               $sql_order = "`item`.`received` DESC";
+       } else {
+               $date_field = "`changed`";
+               $sql_order = "`item`.`parent` ".$sort.", `item`.`created` ASC";
+       }
+
        if(! strlen($last_update))
                $last_update = 'now -30 days';
 
@@ -133,7 +151,7 @@ function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0)
        //      AND ( `item`.`edited` > '%s' OR `item`.`changed` > '%s' )
        //      dbesc($check_date),
 
-       $r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
+       $r = q("SELECT STRAIGHT_JOIN `item`.*, `item`.`id` AS `item_id`,
                `contact`.`name`, `contact`.`network`, `contact`.`photo`, `contact`.`url`,
                `contact`.`name-date`, `contact`.`uri-date`, `contact`.`avatar-date`,
                `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`,
@@ -144,9 +162,9 @@ function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0)
                AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0
                LEFT JOIN `sign` ON `sign`.`iid` = `item`.`id`
                WHERE `item`.`uid` = %d AND `item`.`visible` = 1 and `item`.`moderated` = 0 AND `item`.`parent` != 0
-               AND `item`.`wall` = 1 AND `item`.`changed` > '%s'
+               AND ((`item`.`wall` = 1) $visibility) AND `item`.$date_field > '%s'
                $sql_extra
-               ORDER BY `parent` %s, `created` ASC LIMIT 0, 300",
+               ORDER BY $sql_order LIMIT 0, 300",
                intval($owner_id),
                dbesc($check_date),
                dbesc($sort)
@@ -464,12 +482,26 @@ function get_atom_elements($feed, $item, $contact = array()) {
 
        // look for a photo. We should check media size and find the best one,
        // but for now let's just find any author photo
+       // Additionally we look for an alternate author link. On OStatus this one is the one we want.
+
+       $authorlinks = $item->feed->data["child"][SIMPLEPIE_NAMESPACE_ATOM_10]["feed"][0]["child"][SIMPLEPIE_NAMESPACE_ATOM_10]["author"][0]["child"]["http://www.w3.org/2005/Atom"]["link"];
+       if (is_array($authorlinks)) {
+               foreach ($authorlinks as $link) {
+                       $linkdata = array_shift($link["attribs"]);
+
+                       if ($linkdata["rel"] == "alternate")
+                               $res["author-link"] = $linkdata["href"];
+               };
+       }
 
        $rawauthor = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author');
 
        if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
                $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
                foreach($base as $link) {
+                       if($link['attribs']['']['rel'] === 'alternate')
+                               $res['author-link'] = unxmlify($link['attribs']['']['href']);
+
                        if(!x($res, 'author-avatar') || !$res['author-avatar']) {
                                if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
                                        $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
@@ -828,7 +860,7 @@ function get_atom_elements($feed, $item, $contact = array()) {
                logger('get_atom_elements: Looking for status.net repeated message');
 
                $message = $child["http://activitystrea.ms/spec/1.0/"]["object"][0]["child"][SIMPLEPIE_NAMESPACE_ATOM_10]["content"][0]["data"];
-               $orig_uri = $child["http://activitystrea.ms/spec/1.0/"]["object"][0]["child"][SIMPLEPIE_NAMESPACE_ATOM_10]["id"][0]["data"];
+               $orig_id = ostatus_convert_href($child["http://activitystrea.ms/spec/1.0/"]["object"][0]["child"][SIMPLEPIE_NAMESPACE_ATOM_10]["id"][0]["data"]);
                $author = $child[SIMPLEPIE_NAMESPACE_ATOM_10]["author"][0]["child"][SIMPLEPIE_NAMESPACE_ATOM_10];
                $uri = $author["uri"][0]["data"];
                $name = $author["name"][0]["data"];
@@ -836,10 +868,10 @@ function get_atom_elements($feed, $item, $contact = array()) {
                $avatar = $avatar["href"];
 
                if (($name != "") and ($uri != "") and ($avatar != "") and ($message != "")) {
-                       logger('get_atom_elements: fixing sender of repeated message.');
+                       logger('get_atom_elements: fixing sender of repeated message. '.$orig_id, LOGGER_DEBUG);
 
                        if (!intval(get_config('system','wall-to-wall_share'))) {
-                               $prefix = share_header($name, $uri, $avatar, "", "", $orig_uri);
+                               $prefix = share_header($name, $uri, $avatar, "", "", $orig_link);
 
                                $res["body"] = $prefix.html2bbcode($message)."[/share]";
                        } else {
@@ -856,20 +888,6 @@ function get_atom_elements($feed, $item, $contact = array()) {
                }
        }
 
-       // Search for ostatus conversation url
-       $links = $item->feed->data["child"][SIMPLEPIE_NAMESPACE_ATOM_10]["feed"][0]["child"][SIMPLEPIE_NAMESPACE_ATOM_10]["entry"][0]["child"]["http://www.w3.org/2005/Atom"]["link"];
-
-       if (is_array($links)) {
-               foreach ($links as $link) {
-                       $conversation = array_shift($link["attribs"]);
-
-                       if ($conversation["rel"] == "ostatus:conversation") {
-                               $res["ostatus_conversation"] = $conversation["href"];
-                               logger('get_atom_elements: found conversation url '.$res["ostatus_conversation"]);
-                       }
-               };
-       }
-
        if (isset($contact["network"]) AND ($contact["network"] == NETWORK_FEED) AND $contact['fetch_further_information']) {
                $preview = "";
 
@@ -1068,7 +1086,15 @@ function encode_rel_links($links) {
        return xmlify($o);
 }
 
+function add_guid($item) {
+       $r = q("SELECT `guid` FROM `guid` WHERE `guid` = '%s' LIMIT 1", dbesc($item["guid"]));
+       if ($r)
+               return;
 
+       q("INSERT INTO `guid` (`guid`,`plink`,`uri`,`network`) VALUES ('%s','%s','%s','%s')",
+               dbesc($item["guid"]), dbesc($item["plink"]),
+               dbesc($item["uri"]), dbesc($item["network"]));
+}
 
 function item_store($arr,$force_parent = false, $notify = false, $dontcache = false) {
 
@@ -1090,13 +1116,12 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
                unset($arr['dsprsig']);
        }
 
-       // if an OStatus conversation url was passed in, it is stored and then
-       // removed from the array.
-       $ostatus_conversation = null;
-
-       if (isset($arr["ostatus_conversation"])) {
-               $ostatus_conversation = $arr["ostatus_conversation"];
-               unset($arr["ostatus_conversation"]);
+       // Converting the plink
+       if ($arr['network'] == NETWORK_OSTATUS) {
+               if (isset($arr['plink']))
+                       $arr['plink'] = ostatus_convert_href($arr['plink']);
+               elseif (isset($arr['uri']))
+                       $arr['plink'] = ostatus_convert_href($arr['uri']);
        }
 
        if(x($arr, 'gravity'))
@@ -1115,7 +1140,7 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
 
        /* check for create  date and expire time */
        $uid = intval($arr['uid']);
-       $r = q("SELECT expire FROM user WHERE uid = %d", $uid);
+       $r = q("SELECT expire FROM user WHERE uid = %d", intval($uid));
        if(count($r)) {
                $expire_interval = $r[0]['expire'];
                if ($expire_interval>0) {
@@ -1129,11 +1154,10 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
        }
 
        // If there is no guid then take the same guid that was taken before for the same uri
-       if ((trim($arr['guid']) == "") AND (trim($arr['uri']) != "")) {
+       if ((trim($arr['guid']) == "") AND (trim($arr['uri']) != "") AND (trim($arr['network']) != "")) {
                logger('item_store: checking for an existing guid for uri '.$arr['uri'], LOGGER_DEBUG);
-               $r = q("SELECT `guid` FROM `item` WHERE `uri` = '%s' AND `guid` != '' LIMIT 1",
-                       dbesc(trim($arr['uri']))
-               );
+               $r = q("SELECT `guid` FROM `guid` WHERE `uri` = '%s' AND `network` = '%s' LIMIT 1",
+                       dbesc(trim($arr['uri'])), dbesc(trim($arr['network'])));
 
                if(count($r)) {
                        $arr['guid'] = $r[0]["guid"];
@@ -1141,6 +1165,21 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
                }
        }
 
+       // If there is no guid then take the same guid that was taken before for the same plink
+       if ((trim($arr['guid']) == "") AND (trim($arr['plink']) != "") AND (trim($arr['network']) != "")) {
+               logger('item_store: checking for an existing guid for plink '.$arr['plink'], LOGGER_DEBUG);
+               $r = q("SELECT `guid`, `uri` FROM `guid` WHERE `plink` = '%s' AND `network` = '%s' LIMIT 1",
+                       dbesc(trim($arr['plink'])), dbesc(trim($arr['network'])));
+
+               if(count($r)) {
+                       $arr['guid'] = $r[0]["guid"];
+                       logger('item_store: found guid '.$arr['guid'].' for plink '.$arr['plink'], LOGGER_DEBUG);
+
+                       if ($r[0]["uri"] != $arr['uri'])
+                       logger('Different uri for same guid: '.$arr['uri'].' and '.$r[0]["uri"].' - this shouldnt happen!', LOGGER_DEBUG);
+               }
+       }
+
        // Shouldn't happen but we want to make absolutely sure it doesn't leak from a plugin.
        // Deactivated, since the bbcode parser can handle with it - and it destroys posts with some smileys that contain "<"
        //if((strpos($arr['body'],'<') !== false) || (strpos($arr['body'],'>') !== false))
@@ -1237,6 +1276,18 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
                logger("item_store: Set network to ".$arr["network"]." for ".$arr["uri"], LOGGER_DEBUG);
        }
 
+       if ($arr['guid'] != "") {
+               // Checking if there is already an item with the same guid
+               logger('checking for an item for user '.$arr['uid'].' on network '.$arr['network'].' with the guid '.$arr['guid'], LOGGER_DEBUG);
+               $r = q("SELECT `guid` FROM `item` WHERE `guid` = '%s' AND `network` = '%s' AND `uid` = '%d' LIMIT 1",
+                       dbesc($arr['guid']), dbesc($arr['network']), intval($arr['uid']));
+
+               if(count($r)) {
+                       logger('found item with guid '.$arr['guid'].' for user '.$arr['uid'].' on network '.$arr['network'], LOGGER_DEBUG);
+                       return 0;
+               }
+       }
+
        // Check for hashtags in the body and repair or add hashtag links
        item_body_set_hashtags($arr);
 
@@ -1327,7 +1378,7 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
                                $arr['gravity'] = 0;
                        }
                        else {
-                               logger('item_store: item parent was not found - ignoring item');
+                               logger('item_store: item parent '.$arr['parent-uri'].' for '.$arr['uid'].' was not found - ignoring item');
                                return 0;
                        }
 
@@ -1335,12 +1386,26 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
                }
        }
 
-       $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
+       $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `network` = '%s' AND `uid` = %d LIMIT 1",
                dbesc($arr['uri']),
+               dbesc($arr['network']),
+               intval($arr['uid'])
+       );
+       if($r && count($r)) {
+               logger('duplicated item with the same uri found. ' . print_r($arr,true));
+               return 0;
+       }
+
+       // Check for an existing post with the same content. There seems to be a problem with OStatus.
+       $r = q("SELECT `id` FROM `item` WHERE `body` = '%s' AND `network` = '%s' AND `created` = '%s' AND `contact-id` = %d AND `uid` = %d LIMIT 1",
+               dbesc($arr['body']),
+               dbesc($arr['network']),
+               dbesc($arr['created']),
+               intval($arr['contact-id']),
                intval($arr['uid'])
        );
        if($r && count($r)) {
-               logger('item-store: duplicate item ignored. ' . print_r($arr,true));
+               logger('duplicated item with the same body found. ' . print_r($arr,true));
                return 0;
        }
 
@@ -1388,15 +1453,31 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
        );
 
        if(count($r)) {
+
+               // Store the guid and other relevant data
+               add_guid($arr);
+
                $current_post = $r[0]['id'];
                logger('item_store: created item ' . $current_post);
 
-               // Set "success_update" to the date of the last time we heard from this contact
-               // This can be used to filter for inactive contacts and poco.
+               // Set "success_update" and "last-item" to the date of the last time we heard from this contact
+               // This can be used to filter for inactive contacts.
                // Only do this for public postings to avoid privacy problems, since poco data is public.
                // Don't set this value if it isn't from the owner (could be an author that we don't know)
-               if (!$arr['private'] AND (($arr["author-link"] === $arr["owner-link"]) OR ($arr["parent-uri"] === $arr["uri"])))
-                       q("UPDATE `contact` SET `success_update` = '%s' WHERE `id` = %d",
+
+               $update = (!$arr['private'] AND (($arr["author-link"] === $arr["owner-link"]) OR ($arr["parent-uri"] === $arr["uri"])));
+
+               // Is it a forum? Then we don't care about the rules from above
+               if (!$update AND ($arr["network"] == NETWORK_DFRN) AND ($arr["parent-uri"] === $arr["uri"])) {
+                       $isforum = q("SELECT `forum` FROM `contact` WHERE `id` = %d AND `forum`",
+                                       intval($arr['contact-id']));
+                       if ($isforum)
+                               $update = true;
+               }
+
+               if ($update)
+                       q("UPDATE `contact` SET `success_update` = '%s', `last-item` = '%s' WHERE `id` = %d",
+                               dbesc($arr['received']),
                                dbesc($arr['received']),
                                intval($arr['contact-id'])
                        );
@@ -1435,10 +1516,6 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
                intval($current_post)
        );
 
-       // Complete ostatus threads
-       if ($ostatus_conversation)
-               complete_conversation($current_post, $ostatus_conversation);
-
        $arr['id'] = $current_post;
        $arr['parent'] = $parent_id;
        $arr['allow_cid'] = $allow_cid;
@@ -1555,7 +1632,8 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
                                'source_link'  => $item[0]['author-link'],
                                'source_photo' => $item[0]['author-avatar'],
                                'verb'         => ACTIVITY_TAG,
-                               'otype'        => 'item'
+                               'otype'        => 'item',
+                               'parent'       => $arr['parent']
                        ));
                        logger('item_store: Notification sent for contact '.$arr['contact-id'].' and post '.$current_post, LOGGER_DEBUG);
                }
@@ -1596,12 +1674,12 @@ function item_body_set_hashtags(&$item) {
        // mask hashtags inside of url, bookmarks and attachments to avoid urls in urls
        $item["body"] = preg_replace_callback("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
                function ($match){
-                       return("[url=".$match[1]."]".str_replace("#", "&num;", $match[2])."[/url]");
+                       return("[url=".str_replace("#", "&num;", $match[1])."]".str_replace("#", "&num;", $match[2])."[/url]");
                },$item["body"]);
 
        $item["body"] = preg_replace_callback("/\[bookmark\=([$URLSearchString]*)\](.*?)\[\/bookmark\]/ism",
                function ($match){
-                       return("[bookmark=".$match[1]."]".str_replace("#", "&num;", $match[2])."[/bookmark]");
+                       return("[bookmark=".str_replace("#", "&num;", $match[1])."]".str_replace("#", "&num;", $match[2])."[/bookmark]");
                },$item["body"]);
 
        $item["body"] = preg_replace_callback("/\[attachment (.*)\](.*?)\[\/attachment\]/ism",
@@ -1613,6 +1691,7 @@ function item_body_set_hashtags(&$item) {
        $item["body"] = preg_replace("/&num;\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
                        "&num;$2", $item["body"]);
 
+
        foreach($tags as $tag) {
                if(strpos($tag,'#') !== 0)
                        continue;
@@ -1907,13 +1986,13 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) {
        if($contact['duplex'] && $contact['issued-id'])
                $idtosend = '1:' . $orig_id;
 
-       $rino = ((function_exists('mcrypt_encrypt')) ? 1 : 0);
+    
+       $rino = get_config('system','rino_encrypt');
+       $rino = intval($rino);
 
-       $rino_enable = get_config('system','rino_encrypt');
-
-       if(! $rino_enable)
-               $rino = 0;
+   
 
+       
        $ssl_val = intval(get_config('system','ssl_policy'));
        $ssl_policy = '';
 
@@ -1930,7 +2009,7 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) {
                        break;
        }
 
-       $url = $contact['notify'] . '&dfrn_id=' . $idtosend . '&dfrn_version=' . DFRN_PROTOCOL_VERSION . (($rino) ? '&rino=1' : '');
+       $url = $contact['notify'] . '&dfrn_id=' . $idtosend . '&dfrn_version=' . DFRN_PROTOCOL_VERSION . (($rino) ? '&rino='.$rino : '');
 
        logger('dfrn_deliver: ' . $url);
 
@@ -1961,7 +2040,7 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) {
        $challenge    = hex2bin((string) $res->challenge);
        $perm         = (($res->perm) ? $res->perm : null);
        $dfrn_version = (float) (($res->dfrn_version) ? $res->dfrn_version : 2.0);
-       $rino_allowed = ((intval($res->rino) === 1) ? 1 : 0);
+       $rino_remote_version = intval($res->rino);
        $page         = (($owner['page-flags'] == PAGE_COMMUNITY) ? 1 : 0);
 
        if($owner['page-flags'] == PAGE_PRVGROUP)
@@ -2022,11 +2101,46 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) {
        if($page)
                $postvars['page'] = $page;
 
-       if($rino && $rino_allowed && (! $dissolve)) {
-               $key = substr(random_string(),0,16);
-               $data = bin2hex(aes_encrypt($postvars['data'],$key));
-               $postvars['data'] = $data;
-               logger('rino: sent key = ' . $key, LOGGER_DEBUG);
+
+       if($rino>0 && $rino_remote_version>0 && (! $dissolve)) {
+               logger('rino version: '. $rino_remote_version);
+
+               switch($rino_remote_version) {
+                       case 1:
+                               // Deprecated rino version!
+                               $key = substr(random_string(),0,16);
+                               $data = aes_encrypt($postvars['data'],$key);
+                               break;
+                       case 2:
+                               // RINO 2 based on php-encryption
+                               try {
+                                       $key = Crypto::createNewRandomKey();
+                               } catch (CryptoTestFailed $ex) {
+                                       logger('Cannot safely create a key');
+                                       return -1;
+                               } catch (CannotPerformOperation $ex) {
+                                       logger('Cannot safely create a key');
+                                       return -1; 
+                               }
+                               try {
+                                       $data = Crypto::encrypt($postvars['data'], $key);
+                               } catch (CryptoTestFailed $ex) {
+                                       logger('Cannot safely perform encryption');
+                                       return -1; 
+                               } catch (CannotPerformOperation $ex) {
+                                       logger('Cannot safely perform encryption');
+                                       return -1; 
+                               }
+                               break;
+                       default:
+                               logger("rino: invalid requested verision '$rino_remote_version'");
+                               return -1;
+               }
+               
+               $postvars['rino'] = $rino_remote_version;
+               $postvars['data'] = bin2hex($data);
+               
+               #logger('rino: sent key = ' . $key, LOGGER_DEBUG);
 
 
                if($dfrn_version >= 2.1) {
@@ -2053,6 +2167,7 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) {
 
                $postvars['key'] = bin2hex($postvars['key']);
        }
+       
 
        logger('dfrn_deliver: ' . "SENDING: " . print_r($postvars,true), LOGGER_DATA);
 
@@ -2133,6 +2248,17 @@ function edited_timestamp_is_newer($existing, $update) {
  */
 
 function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) {
+       if ($contact['network'] === NETWORK_OSTATUS) {
+               if ($pass < 2) {
+                       // Test - remove before flight
+                       //$tempfile = tempnam(get_temppath(), "ostatus");
+                       //file_put_contents($tempfile, $xml);
+
+                       logger("Consume OStatus messages ", LOGGER_DEBUG);
+                       ostatus_import($xml,$importer,$contact, $hub);
+               }
+               return;
+       }
 
        require_once('library/simplepie/simplepie.inc');
        require_once('include/contact_selectors.php');
@@ -3647,6 +3773,9 @@ function local_delivery($importer,$data) {
                                $parent = 0;
 
                                if($posted_id) {
+
+                                       $datarray["id"] = $posted_id;
+
                                        $r = q("SELECT `parent`, `parent-uri` FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
                                                intval($posted_id),
                                                intval($importer['importer_uid'])
@@ -4009,7 +4138,7 @@ function local_delivery($importer,$data) {
                                                        'verb'         => $datarray['verb'],
                                                        'otype'        => 'person',
                                                        'activity'     => $verb,
-
+                                                       'parent'       => $datarray['parent']
                                                ));
                                        }
                                }
@@ -4094,9 +4223,7 @@ function new_follower($importer,$contact,$datarray,$item,$sharing = false) {
                        }
 
                        if(($r[0]['notify-flags'] & NOTIFY_INTRO) &&
-                               (($r[0]['page-flags'] == PAGE_NORMAL) OR ($r[0]['page-flags'] == PAGE_SOAPBOX))) {
-
-
+                               in_array($r[0]['page-flags'], array(PAGE_NORMAL, PAGE_SOAPBOX, PAGE_FREELOVE))) {
 
                                notification(array(
                                        'type'         => NOTIFY_INTRO,
@@ -4113,7 +4240,6 @@ function new_follower($importer,$contact,$datarray,$item,$sharing = false) {
                                        'otype'        => 'intro'
                                ));
 
-
                        }
                }
        }
@@ -4201,10 +4327,48 @@ function atom_author($tag,$name,$uri,$h,$w,$photo) {
 
 
        $o .= "<$tag>\r\n";
-       $o .= "<name>$name</name>\r\n";
-       $o .= "<uri>$uri</uri>\r\n";
-       $o .= '<link rel="photo"  type="image/jpeg" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n";
-       $o .= '<link rel="avatar" type="image/jpeg" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n";
+       $o .= "\t<name>$name</name>\r\n";
+       $o .= "\t<uri>$uri</uri>\r\n";
+       $o .= "\t".'<link rel="photo"  type="image/jpeg" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n";
+       $o .= "\t".'<link rel="avatar" type="image/jpeg" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n";
+
+       if ($tag == "author") {
+               $r = q("SELECT `profile`.`locality`, `profile`.`region`, `profile`.`country-name`,
+                               `profile`.`name`, `profile`.`pub_keywords`, `profile`.`about`,
+                               `profile`.`homepage`,`contact`.`nick` FROM `profile`
+                               INNER JOIN `contact` ON `contact`.`uid` = `profile`.`uid`
+                               INNER JOIN `user` ON `user`.`uid` = `profile`.`uid`
+                               WHERE `profile`.`is-default` AND `contact`.`self` AND
+                                       NOT `user`.`hidewall` AND `contact`.`nurl`='%s'",
+                       dbesc(normalise_link($uri)));
+               if ($r) {
+                       $location = '';
+                       if($r[0]['locality'])
+                               $location .= $r[0]['locality'];
+                       if($r[0]['region']) {
+                               if($location)
+                                       $location .= ', ';
+                               $location .= $r[0]['region'];
+                       }
+                       if($r[0]['country-name']) {
+                               if($location)
+                                       $location .= ', ';
+                               $location .= $r[0]['country-name'];
+                       }
+
+                       $o .= "\t<poco:preferredUsername>".xmlify($r[0]["nick"])."</poco:preferredUsername>\r\n";
+                       $o .= "\t<poco:displayName>".xmlify($r[0]["name"])."</poco:displayName>\r\n";
+                       $o .= "\t<poco:note>".xmlify($r[0]["about"])."</poco:note>\r\n";
+                       $o .= "\t<poco:address>\r\n";
+                       $o .= "\t\t<poco:formatted>".xmlify($location)."</poco:formatted>\r\n";
+                       $o .= "\t</poco:address>\r\n";
+                       $o .= "\t<poco:urls>\r\n";
+                       $o .= "\t<poco:type>homepage</poco:type>\r\n";
+                       $o .= "\t\t<poco:value>".xmlify($r[0]["homepage"])."</poco:value>\r\n";
+                       $o .= "\t\t<poco:primary>true</poco:primary>\r\n";
+                       $o .= "\t</poco:urls>\r\n";
+               }
+       }
 
        call_hooks('atom_author', $o);
 
@@ -4248,7 +4412,8 @@ function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) {
        if ($item['title'] != "")
                $htmlbody = "[b]".$item['title']."[/b]\n\n".$htmlbody;
 
-       $htmlbody = bbcode(bb_remove_share_information($htmlbody), false, false, 7);
+       //$htmlbody = bbcode(bb_remove_share_information($htmlbody), false, false, 7);
+       $htmlbody = bbcode($htmlbody, false, false, 7);
 
        $o .= '<id>' . xmlify($item['uri']) . '</id>' . "\r\n";
        $o .= '<title>' . xmlify($item['title']) . '</title>' . "\r\n";
@@ -4259,6 +4424,8 @@ function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) {
        $o .= '<link rel="alternate" type="text/html" href="' . xmlify($a->get_baseurl() . '/display/' . $owner['nickname'] . '/' . $item['id']) . '" />' . "\r\n";
 
 
+       $o .= '<status_net notice_id="'.$item['id'].'"></status_net>'."\r\n";
+
        if($comment)
                $o .= '<dfrn:comment-allow>' . intval($item['last-child']) . '</dfrn:comment-allow>' . "\r\n";
 
@@ -4300,11 +4467,19 @@ function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) {
 
        $tags = item_getfeedtags($item);
        if(count($tags)) {
-               foreach($tags as $t) {
-                       $o .= '<category scheme="X-DFRN:' . xmlify($t[0]) . ':' . xmlify($t[1]) . '" term="' . xmlify($t[2]) . '" />' . "\r\n";
-               }
+               foreach($tags as $t)
+                       if (($type != 'html') OR ($t[0] != "@"))
+                               $o .= '<category scheme="X-DFRN:' . xmlify($t[0]) . ':' . xmlify($t[1]) . '" term="' . xmlify($t[2]) . '" />' . "\r\n";
        }
 
+       // To-Do:
+       // To support these elements, the API needs to be enhanced
+       //$o .= '<link rel="ostatus:conversation" href="'.xmlify($a->get_baseurl().'/display/'.$owner['nickname'].'/'.$item['parent']).'"/>'."\r\n";
+       //$o .= "\t".'<link rel="self" type="application/atom+xml" href="'.xmlify($a->get_baseurl().'/api/statuses/show/'.$item['id'].'.atom').'"/>'."\r\n";
+       //$o .= "\t".'<link rel="edit" type="application/atom+xml" href="'.xmlify($a->get_baseurl().'/api/statuses/show/'.$item['id'].'.atom').'"/>'."\r\n";
+
+       $o .= item_get_attachment($item);
+
        $o .= item_getfeedattach($item);
 
        $mentioned = get_mentions($item);
@@ -4482,6 +4657,28 @@ function item_getfeedtags($item) {
        return $ret;
 }
 
+function item_get_attachment($item) {
+       $o = "";
+       $siteinfo = get_attached_data($item["body"]);
+
+       switch($siteinfo["type"]) {
+               case 'link':
+                       $o = '<link rel="enclosure" href="'.xmlify($siteinfo["url"]).'" type="text/html; charset=UTF-8" length="" title="'.xmlify($siteinfo["title"]).'"/>'."\r\n";
+                       break;
+               case 'photo':
+                       $imgdata = get_photo_info($siteinfo["image"]);
+                       $o = '<link rel="enclosure" href="'.xmlify($siteinfo["image"]).'" type="'.$imgdata["mime"].'" length="'.$imgdata["size"].'"/>'."\r\n";
+                       break;
+               case 'video':
+                       $o = '<link rel="enclosure" href="'.xmlify($siteinfo["url"]).'" type="text/html; charset=UTF-8" length="" title="'.xmlify($siteinfo["title"]).'"/>'."\r\n";
+                       break;
+               default:
+                       break;
+       }
+
+       return $o;
+}
+
 function item_getfeedattach($item) {
        $ret = '';
        $arr = explode('[/attach],',$item['attach']);
@@ -4720,6 +4917,18 @@ function drop_item($id,$interactive = true) {
                        // ignore the result
                }
 
+               // If item has attachments, drop them
+
+               foreach(explode(",",$item['attach']) as $attach){
+                       preg_match("|attach/(\d+)|", $attach, $matches);
+                       q("DELETE FROM `attach` WHERE `id` = %d AND `uid` = %d",
+                               intval($matches[1]),
+                               local_user()
+                       );
+                       // ignore the result
+               }
+
+
                // clean up item_id and sign meta-data tables
 
                /*
@@ -4796,6 +5005,7 @@ function drop_item($id,$interactive = true) {
                        // Add a relayable_retraction signature for Diaspora.
                        store_diaspora_retract_sig($item, $a->user, $a->get_baseurl());
                }
+
                $drop_id = intval($item['id']);
 
                // send the notification upstream/downstream as the case may be
@@ -4833,6 +5043,37 @@ function first_post_date($uid,$wall = false) {
        return false;
 }
 
+/* modified posted_dates() {below} to arrange the list in years */
+function list_post_dates($uid, $wall) {
+       $dnow = datetime_convert('',date_default_timezone_get(),'now','Y-m-d');
+
+       $dthen = first_post_date($uid, $wall);
+       if(! $dthen)
+               return array();
+
+       // Set the start and end date to the beginning of the month
+       $dnow = substr($dnow,0,8).'01';
+       $dthen = substr($dthen,0,8).'01';
+
+       $ret = array();
+
+       // Starting with the current month, get the first and last days of every
+       // month down to and including the month of the first post
+       while(substr($dnow, 0, 7) >= substr($dthen, 0, 7)) {
+               $dyear = intval(substr($dnow,0,4));
+               $dstart = substr($dnow,0,8) . '01';
+               $dend = substr($dnow,0,8) . get_dim(intval($dnow),intval(substr($dnow,5)));
+               $start_month = datetime_convert('','',$dstart,'Y-m-d');
+               $end_month = datetime_convert('','',$dend,'Y-m-d');
+               $str = day_translate(datetime_convert('','',$dnow,'F'));
+               if(! $ret[$dyear])
+                       $ret[$dyear] = array();
+               $ret[$dyear][] = array($str,$end_month,$start_month);
+               $dnow = datetime_convert('','',$dnow . ' -1 month', 'Y-m-d');
+       }
+       return $ret;
+}
+
 function posted_dates($uid,$wall) {
        $dnow = datetime_convert('',date_default_timezone_get(),'now','Y-m-d');
 
@@ -4871,15 +5112,27 @@ function posted_date_widget($url,$uid,$wall) {
 /*     if($wall && intval(get_pconfig($uid,'system','no_wall_archive_widget')))
                return $o;*/
 
-       $ret = posted_dates($uid,$wall);
+       $visible_years = get_pconfig($uid,'system','archive_visible_years');
+       if(! $visible_years)
+               $visible_years = 5;
+
+       $ret = list_post_dates($uid,$wall);
+
        if(! count($ret))
                return $o;
 
+       $cutoff_year = intval(datetime_convert('',date_default_timezone_get(),'now','Y')) - $visible_years;
+       $cutoff = ((array_key_exists($cutoff_year,$ret))? true : false);
+
        $o = replace_macros(get_markup_template('posted_date_widget.tpl'),array(
                '$title' => t('Archives'),
-               '$size' => ((count($ret) > 6) ? 6 : count($ret)),
+               '$size' => $visible_years,
+               '$cutoff_year' => $cutoff_year,
+               '$cutoff' => $cutoff,
                '$url' => $url,
-               '$dates' => $ret
+               '$dates' => $ret,
+               '$showmore' => t('show more')
+
        ));
        return $o;
 }