]> git.mxchange.org Git - friendica.git/blobdiff - include/items.php
Bugfix: Feeds weren't imported if a post with the same URI existed.
[friendica.git] / include / items.php
index 3fe624b00124d7c876aeab840b8b9ecdce5f0cbb..1506e737bae4346a28ca9a103d8a4335bc1fa363 100644 (file)
@@ -9,11 +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('include/feed.php');
 require_once('mod/share.php');
 
+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) {
 
 
@@ -37,7 +42,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`
@@ -117,9 +122,10 @@ function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0,
 
        // Include answers to status.net posts in pubsub feeds
        if($forpubsub) {
-               $sql_post_table = "INNER JOIN `thread` ON `thread`.`iid` = `item`.`parent` ";
-               $visibility = sprintf("OR (`item`.`network` = '%s' AND `thread`.`network`='%s')",
-                                       dbesc(NETWORK_DFRN), dbesc(NETWORK_OSTATUS));
+               $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 {
@@ -479,7 +485,6 @@ function get_atom_elements($feed, $item, $contact = array()) {
        // 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.
 
-       // Search for ostatus conversation url
        $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) {
@@ -884,23 +889,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"] = ostatus_convert_href($conversation["href"]);
-                               logger('get_atom_elements: found conversation url '.$res["ostatus_conversation"]);
-                       //} elseif ($conversation["rel"] == "alternate") {
-                       //      $res["plink"] = $conversation["href"];
-                       //      logger('get_atom_elements: found plink '.$res["plink"]);
-                       }
-               };
-       }
-
        if (isset($contact["network"]) AND ($contact["network"] == NETWORK_FEED) AND $contact['fetch_further_information']) {
                $preview = "";
 
@@ -982,7 +970,7 @@ function query_page_info($url, $no_photos = false, $photo = "", $keywords = fals
        $data = Cache::get("parse_url:".$url);
        if (is_null($data)){
                $data = parseurl_getsiteinfo($url, true);
-               Cache::set("parse_url:".$url,serialize($data));
+               Cache::set("parse_url:".$url,serialize($data), CACHE_DAY);
        } else
                $data = unserialize($data);
 
@@ -1109,6 +1097,48 @@ function add_guid($item) {
                dbesc($item["uri"]), dbesc($item["network"]));
 }
 
+// Adds a "lang" specification in a "postopts" element of given $arr,
+// if possible and not already present.
+// Expects "body" element to exist in $arr.
+// TODO: add a parameter to request forcing override
+function item_add_language_opt(&$arr) {
+
+       if (version_compare(PHP_VERSION, '5.3.0', '<')) return; // LanguageDetect.php not available ?
+
+       if ( x($arr, 'postopts') )
+       {
+               if ( strstr($arr['postopts'], 'lang=') )
+               {
+                       // do not override
+                       // TODO: add parameter to request overriding
+                       return;
+               }
+               $postopts = $arr['postopts'];
+       }
+       else
+       {
+               $postopts = "";
+       }
+
+       require_once('library/langdet/Text/LanguageDetect.php');
+       $naked_body = preg_replace('/\[(.+?)\]/','',$arr['body']);
+       $l = new Text_LanguageDetect;
+       //$lng = $l->detectConfidence($naked_body);
+       //$arr['postopts'] = (($lng['language']) ? 'lang=' . $lng['language'] . ';' . $lng['confidence'] : '');
+       $lng = $l->detect($naked_body, 3);
+
+       if (sizeof($lng) > 0) {
+               if ($postopts != "") $postopts .= '&'; // arbitrary separator, to be reviewed
+               $postopts .= 'lang=';
+               $sep = "";
+               foreach ($lng as $language => $score) {
+                       $postopts .= $sep . $language.";".$score;
+                       $sep = ':';
+               }
+               $arr['postopts'] = $postopts;
+       }
+}
+
 function item_store($arr,$force_parent = false, $notify = false, $dontcache = false) {
 
        // If it is a posting where users should get notifications, then define it as wall posting
@@ -1137,15 +1167,6 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
                        $arr['plink'] = ostatus_convert_href($arr['uri']);
        }
 
-       // 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"]);
-       }
-
        if(x($arr, 'gravity'))
                $arr['gravity'] = intval($arr['gravity']);
        elseif($arr['parent-uri'] === $arr['uri'])
@@ -1187,37 +1208,36 @@ 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))
        //      $arr['body'] = strip_tags($arr['body']);
 
+       item_add_language_opt($arr);
 
-       if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
-               require_once('library/langdet/Text/LanguageDetect.php');
-               $naked_body = preg_replace('/\[(.+?)\]/','',$arr['body']);
-               $l = new Text_LanguageDetect;
-               //$lng = $l->detectConfidence($naked_body);
-               //$arr['postopts'] = (($lng['language']) ? 'lang=' . $lng['language'] . ';' . $lng['confidence'] : '');
-               $lng = $l->detect($naked_body, 3);
-
-               if (sizeof($lng) > 0) {
-                       $postopts = "";
-
-                       foreach ($lng as $language => $score) {
-                               if ($postopts == "")
-                                       $postopts = "lang=";
-                               else
-                                       $postopts .= ":";
-
-                               $postopts .= $language.";".$score;
-                       }
-                       $arr['postopts'] = $postopts;
-               }
-       }
+       if ($notify)
+               $guid_prefix = "";
+       else
+               $guid_prefix = $arr['network'];
 
        $arr['wall']          = ((x($arr,'wall'))          ? intval($arr['wall'])                : 0);
-       $arr['uri']           = ((x($arr,'uri'))           ? notags(trim($arr['uri']))           : random_string());
+       $arr['guid']          = ((x($arr,'guid'))          ? notags(trim($arr['guid']))          : get_guid(32, $guid_prefix));
+       $arr['uri']           = ((x($arr,'uri'))           ? notags(trim($arr['uri']))           : $arr['guid']);
        $arr['extid']         = ((x($arr,'extid'))         ? notags(trim($arr['extid']))         : '');
        $arr['author-name']   = ((x($arr,'author-name'))   ? notags(trim($arr['author-name']))   : '');
        $arr['author-link']   = ((x($arr,'author-link'))   ? notags(trim($arr['author-link']))   : '');
@@ -1255,7 +1275,6 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
        $arr['app']           = ((x($arr,'app'))           ? notags(trim($arr['app']))           : '');
        $arr['origin']        = ((x($arr,'origin'))        ? intval($arr['origin'])              : 0 );
        $arr['network']       = ((x($arr,'network'))       ? trim($arr['network'])               : '');
-       $arr['guid']          = ((x($arr,'guid'))          ? notags(trim($arr['guid']))          : get_guid(32, $arr['network']));
        $arr['postopts']      = ((x($arr,'postopts'))      ? trim($arr['postopts'])              : '');
        $arr['resource-id']   = ((x($arr,'resource-id'))   ? trim($arr['resource-id'])           : '');
        $arr['event-id']      = ((x($arr,'event-id'))      ? intval($arr['event-id'])            : 0 );
@@ -1268,11 +1287,24 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
        }
 
        if ($arr['network'] == "") {
-               $r = q("SELECT `network` FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
-                       intval($arr['contact-id']),
+               $r = q("SELECT `network` FROM `contact` WHERE `network` IN ('%s', '%s', '%s') AND `nurl` = '%s' AND `uid` = %d LIMIT 1",
+                       dbesc(NETWORK_DFRN), dbesc(NETWORK_DIASPORA), dbesc(NETWORK_OSTATUS),
+                       dbesc(normalise_link($arr['author-link'])),
                        intval($arr['uid'])
                );
 
+               if(!count($r))
+                       $r = q("SELECT `network` FROM `gcontact` WHERE `network` IN ('%s', '%s', '%s') AND `nurl` = '%s' LIMIT 1",
+                               dbesc(NETWORK_DFRN), dbesc(NETWORK_DIASPORA), dbesc(NETWORK_OSTATUS),
+                               dbesc(normalise_link($arr['author-link']))
+                       );
+
+               if(!count($r))
+                       $r = q("SELECT `network` FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
+                               intval($arr['contact-id']),
+                               intval($arr['uid'])
+                       );
+
                if(count($r))
                        $arr['network'] = $r[0]["network"];
 
@@ -1385,7 +1417,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;
                        }
 
@@ -1430,7 +1462,10 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
        // Fill the cache field
        put_item_in_cache($arr);
 
-       call_hooks('post_remote',$arr);
+       if ($notify)
+               call_hooks('post_local',$arr);
+       else
+               call_hooks('post_remote',$arr);
 
        if(x($arr,'cancel')) {
                logger('item_store: post cancelled by plugin.');
@@ -1454,9 +1489,10 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
        $arr = $unescaped;
 
        // find the item we just created
-       $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d ORDER BY `id` ASC ",
+       $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d AND `network` = '%s' ORDER BY `id` ASC ",
                dbesc($arr['uri']),
-               intval($arr['uid'])
+               intval($arr['uid']),
+               dbesc($arr['network'])
        );
 
        if(count($r)) {
@@ -1523,10 +1559,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;
@@ -1580,7 +1612,10 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
 
                $r = q('SELECT * FROM `item` WHERE id = %d', intval($current_post));
                if (count($r) == 1) {
-                       call_hooks('post_remote_end', $r[0]);
+                       if ($notify)
+                               call_hooks('post_local_end', $r[0]);
+                       else
+                               call_hooks('post_remote_end', $r[0]);
                } else
                        logger('item_store: new item not found in DB, id ' . $current_post);
        }
@@ -1997,12 +2032,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_enable = get_config('system','rino_encrypt');
+       $rino = get_config('system','rino_encrypt');
+       $rino = intval($rino);
+       // use RINO1 if mcrypt isn't installed and RINO2 was selected
+       if ($rino==2 and !function_exists('mcrypt_create_iv')) $rino=1;
 
-       if(! $rino_enable)
-               $rino = 0;
+       logger("Local rino version: ". $rino, LOGGER_DEBUG);
 
        $ssl_val = intval(get_config('system','ssl_policy'));
        $ssl_policy = '';
@@ -2020,7 +2056,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);
 
@@ -2051,9 +2087,11 @@ 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);
 
+       logger("Remote rino version: ".$rino_remote_version." for ".$contact["url"], LOGGER_DEBUG);
+
        if($owner['page-flags'] == PAGE_PRVGROUP)
                $page = 2;
 
@@ -2112,11 +2150,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) {
@@ -2144,6 +2217,7 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) {
                $postvars['key'] = bin2hex($postvars['key']);
        }
 
+
        logger('dfrn_deliver: ' . "SENDING: " . print_r($postvars,true), LOGGER_DATA);
 
        $xml = post_url($contact['notify'],$postvars);
@@ -2223,6 +2297,21 @@ 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) {
+                       logger("Consume OStatus messages ", LOGGER_DEBUG);
+                       ostatus_import($xml,$importer,$contact, $hub);
+               }
+               return;
+       }
+
+       if ($contact['network'] === NETWORK_FEED) {
+               if ($pass < 2) {
+                       logger("Consume feeds", LOGGER_DEBUG);
+                       feed_import($xml,$importer,$contact, $hub);
+               }
+               return;
+       }
 
        require_once('library/simplepie/simplepie.inc');
        require_once('include/contact_selectors.php');
@@ -2232,12 +2321,6 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0)
                return;
        }
 
-       // Test - remove before flight
-//     if ($contact['network'] === NETWORK_OSTATUS) {
-//             $tempfile = tempnam(get_temppath(), "ostatus");
-//             file_put_contents($tempfile, $xml);
-//     }
-
        $feed = new SimplePie();
        $feed->set_raw_data($xml);
        if($datedir)
@@ -2938,7 +3021,7 @@ function item_is_remote_self($contact, &$datarray) {
                if ($contact['network'] != NETWORK_FEED) {
                        $datarray["guid"] = get_guid(32);
                        unset($datarray["plink"]);
-                       $datarray["uri"] = item_new_uri($a->get_hostname(),$contact['uid']);
+                       $datarray["uri"] = item_new_uri($a->get_hostname(),$contact['uid'], $datarray["guid"]);
                        $datarray["parent-uri"] = $datarray["uri"];
                        $datarray["extid"] = $contact['network'];
                        $urlpart = parse_url($datarray2['author-link']);
@@ -2947,9 +3030,6 @@ function item_is_remote_self($contact, &$datarray) {
                        $datarray['private'] = 0;
        }
 
-       //if (!isset($datarray["app"]) OR ($datarray["app"] == ""))
-       //      $datarray["app"] = network_to_name($contact['network']);
-
        if ($contact['network'] != NETWORK_FEED) {
                // Store the original post
                $r = item_store($datarray2, false, false);
@@ -4129,9 +4209,12 @@ function new_follower($importer,$contact,$datarray,$item,$sharing = false) {
        $name = notags(trim($datarray['author-name']));
        $photo = notags(trim($datarray['author-avatar']));
 
-       $rawtag = $item->get_item_tags(NAMESPACE_ACTIVITY,'actor');
-       if($rawtag && $rawtag[0]['child'][NAMESPACE_POCO]['preferredUsername'][0]['data'])
-               $nick = $rawtag[0]['child'][NAMESPACE_POCO]['preferredUsername'][0]['data'];
+       if (is_object($item)) {
+               $rawtag = $item->get_item_tags(NAMESPACE_ACTIVITY,'actor');
+               if($rawtag && $rawtag[0]['child'][NAMESPACE_POCO]['preferredUsername'][0]['data'])
+                       $nick = $rawtag[0]['child'][NAMESPACE_POCO]['preferredUsername'][0]['data'];
+       } else
+               $nick = $item;
 
        if(is_array($contact)) {
                if(($contact['network'] == NETWORK_OSTATUS && $contact['rel'] == CONTACT_IS_SHARING)
@@ -4269,7 +4352,7 @@ function subscribe_to_hub($url,$importer,$contact,$hubmode = 'subscribe') {
 
        logger('subscribe_to_hub: ' . $hubmode . ' ' . $contact['name'] . ' to hub ' . $url . ' endpoint: '  . $push_url . ' with verifier ' . $verify_token);
 
-       if(! strlen($contact['hub-verify'])) {
+       if(!strlen($contact['hub-verify']) OR ($contact['hub-verify'] != $verify_token)) {
                $r = q("UPDATE `contact` SET `hub-verify` = '%s' WHERE `id` = %d",
                        dbesc($verify_token),
                        intval($contact['id'])
@@ -4297,10 +4380,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);
 
@@ -4335,8 +4456,9 @@ function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) {
                $o .= atom_author('dfrn:owner',$item['owner-name'],$item['owner-link'],80,80,$item['owner-avatar']);
 
        if(($item['parent'] != $item['id']) || ($item['parent-uri'] !== $item['uri']) || (($item['thr-parent'] !== '') && ($item['thr-parent'] !== $item['uri']))) {
+               $parent = q("SELECT `guid` FROM `item` WHERE `id` = %d", intval($item["parent"]));
                $parent_item = (($item['thr-parent']) ? $item['thr-parent'] : $item['parent-uri']);
-               $o .= '<thr:in-reply-to ref="' . xmlify($parent_item) . '" type="text/html" href="' .  xmlify($a->get_baseurl() . '/display/' . $owner['nickname'] . '/' . $item['parent']) . '" />' . "\r\n";
+               $o .= '<thr:in-reply-to ref="'.xmlify($parent_item).'" type="text/html" href="'.xmlify($a->get_baseurl().'/display/'.$parent[0]['guid']).'" />'."\r\n";
        }
 
        $htmlbody = $body;
@@ -4344,7 +4466,7 @@ 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($htmlbody, false, false, 7);
 
        $o .= '<id>' . xmlify($item['uri']) . '</id>' . "\r\n";
        $o .= '<title>' . xmlify($item['title']) . '</title>' . "\r\n";
@@ -4352,8 +4474,9 @@ function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) {
        $o .= '<updated>' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '</updated>' . "\r\n";
        $o .= '<dfrn:env>' . base64url_encode($body, true) . '</dfrn:env>' . "\r\n";
        $o .= '<content type="' . $type . '" >' . xmlify((($type === 'html') ? $htmlbody : $body)) . '</content>' . "\r\n";
-       $o .= '<link rel="alternate" type="text/html" href="' . xmlify($a->get_baseurl() . '/display/' . $owner['nickname'] . '/' . $item['id']) . '" />' . "\r\n";
+       $o .= '<link rel="alternate" type="text/html" href="'.xmlify($a->get_baseurl().'/display/'.$item['guid']).'" />'."\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";
@@ -4396,11 +4519,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);
@@ -4578,6 +4709,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']);
@@ -4951,7 +5104,7 @@ function list_post_dates($uid, $wall) {
                return array();
 
        // Set the start and end date to the beginning of the month
-        $dnow = substr($dnow,0,8).'01';
+       $dnow = substr($dnow,0,8).'01';
        $dthen = substr($dthen,0,8).'01';
 
        $ret = array();
@@ -4967,7 +5120,7 @@ function list_post_dates($uid, $wall) {
                $str = day_translate(datetime_convert('','',$dnow,'F'));
                if(! $ret[$dyear])
                        $ret[$dyear] = array();
-               $ret[$dyear][] = array($str,$end_month,$start_month);
+               $ret[$dyear][] = array($str,$end_month,$start_month);
                $dnow = datetime_convert('','',$dnow . ' -1 month', 'Y-m-d');
        }
        return $ret;
@@ -5015,12 +5168,12 @@ function posted_date_widget($url,$uid,$wall) {
        if(! $visible_years)
                $visible_years = 5;
 
-        $ret = list_post_dates($uid,$wall);
+       $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_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(
@@ -5030,7 +5183,7 @@ function posted_date_widget($url,$uid,$wall) {
                '$cutoff' => $cutoff,
                '$url' => $url,
                '$dates' => $ret,
-                '$showmore' => t('show more')
+               '$showmore' => t('show more')
 
        ));
        return $o;