Exemple #1
0
/**
 *
 * consume_feed - process atom feed and update anything/everything we might need to update
 *
 * $xml = the (atom) feed to consume - RSS isn't as fully supported but may work for simple feeds.
 *
 * $importer = the contact_record (joined to user_record) of the local user who owns this relationship.
 *             It is this person's stuff that is going to be updated.
 * $contact =  the person who is sending us stuff. If not set, we MAY be processing a "follow" activity
 *             from an external network and MAY create an appropriate contact record. Otherwise, we MUST 
 *             have a contact record.
 * $hub = should we find a hub declation in the feed, pass it back to our calling process, who might (or 
 *        might not) try and subscribe to it.
 * $datedir sorts in reverse order
 * $pass - by default ($pass = 0) we cannot guarantee that a parent item has been 
 *      imported prior to its children being seen in the stream unless we are certain
 *      of how the feed is arranged/ordered.
 * With $pass = 1, we only pull parent items out of the stream.
 * With $pass = 2, we only pull children (comments/likes).
 *
 * So running this twice, first with pass 1 and then with pass 2 will do the right
 * thing regardless of feed ordering. This won't be adequate in a fully-threaded
 * model where comments can have sub-threads. That would require some massive sorting
 * to get all the feed items into a mostly linear ordering, and might still require
 * recursion.  
 */
function consume_feed($xml, $importer, &$contact, &$hub, $datedir = 0, $pass = 0)
{
    require_once 'library/simplepie/simplepie.inc';
    if (!strlen($xml)) {
        logger('consume_feed: empty input');
        return;
    }
    $feed = new SimplePie();
    $feed->set_raw_data($xml);
    if ($datedir) {
        $feed->enable_order_by_date(true);
    } else {
        $feed->enable_order_by_date(false);
    }
    $feed->init();
    if ($feed->error()) {
        logger('consume_feed: Error parsing XML: ' . $feed->error());
    }
    $permalink = $feed->get_permalink();
    // Check at the feed level for updated contact name and/or photo
    $name_updated = '';
    $new_name = '';
    $photo_timestamp = '';
    $photo_url = '';
    $birthday = '';
    $hubs = $feed->get_links('hub');
    if (count($hubs)) {
        $hub = implode(',', $hubs);
    }
    $rawtags = $feed->get_feed_tags(NAMESPACE_DFRN, 'owner');
    if (!$rawtags) {
        $rawtags = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author');
    }
    if ($rawtags) {
        $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10];
        if ($elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated']) {
            $name_updated = $elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated'];
            $new_name = $elems['name'][0]['data'];
        }
        if (x($elems, 'link') && $elems['link'][0]['attribs']['']['rel'] === 'photo' && $elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated']) {
            $photo_timestamp = datetime_convert('UTC', 'UTC', $elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated']);
            $photo_url = $elems['link'][0]['attribs']['']['href'];
        }
        if (x($rawtags[0]['child'], NAMESPACE_DFRN) && x($rawtags[0]['child'][NAMESPACE_DFRN], 'birthday')) {
            $birthday = datetime_convert('UTC', 'UTC', $rawtags[0]['child'][NAMESPACE_DFRN]['birthday'][0]['data']);
        }
    }
    if (is_array($contact) && $photo_timestamp && strlen($photo_url) && $photo_timestamp > $contact['avatar-date']) {
        logger('consume_feed: Updating photo for ' . $contact['name']);
        require_once "Photo.php";
        $photo_failure = false;
        $have_photo = false;
        $r = q("SELECT `resource-id` FROM `photo` WHERE `contact-id` = %d AND `uid` = %d LIMIT 1", intval($contact['id']), intval($contact['uid']));
        if (count($r)) {
            $resource_id = $r[0]['resource-id'];
            $have_photo = true;
        } else {
            $resource_id = photo_new_resource();
        }
        $img_str = fetch_url($photo_url, true);
        $img = new Photo($img_str);
        if ($img->is_valid()) {
            if ($have_photo) {
                q("DELETE FROM `photo` WHERE `resource-id` = '%s' AND `contact-id` = %d AND `uid` = %d", dbesc($resource_id), intval($contact['id']), intval($contact['uid']));
            }
            $img->scaleImageSquare(175);
            $hash = $resource_id;
            $r = $img->store($contact['uid'], $contact['id'], $hash, basename($photo_url), 'Contact Photos', 4);
            $img->scaleImage(80);
            $r = $img->store($contact['uid'], $contact['id'], $hash, basename($photo_url), 'Contact Photos', 5);
            $img->scaleImage(48);
            $r = $img->store($contact['uid'], $contact['id'], $hash, basename($photo_url), 'Contact Photos', 6);
            $a = get_app();
            q("UPDATE `contact` SET `avatar-date` = '%s', `photo` = '%s', `thumb` = '%s', `micro` = '%s'  \n\t\t\t\tWHERE `uid` = %d AND `id` = %d LIMIT 1", dbesc(datetime_convert()), dbesc($a->get_baseurl() . '/photo/' . $hash . '-4.jpg'), dbesc($a->get_baseurl() . '/photo/' . $hash . '-5.jpg'), dbesc($a->get_baseurl() . '/photo/' . $hash . '-6.jpg'), intval($contact['uid']), intval($contact['id']));
        }
    }
    if (is_array($contact) && $name_updated && strlen($new_name) && $name_updated > $contact['name-date']) {
        $r = q("select * from contact where uid = %d and id = %d limit 1", intval($contact['uid']), intval($contact['id']));
        $x = q("UPDATE `contact` SET `name` = '%s', `name-date` = '%s' WHERE `uid` = %d AND `id` = %d LIMIT 1", dbesc(notags(trim($new_name))), dbesc(datetime_convert()), intval($contact['uid']), intval($contact['id']));
        // do our best to update the name on content items
        if (count($r)) {
            q("update item set `author-name` = '%s' where `author-name` = '%s' and `author-link` = '%s' and uid = %d", dbesc(notags(trim($new_name))), dbesc($r[0]['name']), dbesc($r[0]['url']), intval($contact['uid']));
        }
    }
    if (strlen($birthday)) {
        if (substr($birthday, 0, 4) != $contact['bdyear']) {
            logger('consume_feed: updating birthday: ' . $birthday);
            /**
             *
             * Add new birthday event for this person
             *
             * $bdtext is just a readable placeholder in case the event is shared
             * with others. We will replace it during presentation to our $importer
             * to contain a sparkle link and perhaps a photo. 
             *
             */
            $bdtext = t('Birthday:') . ' [url=' . $contact['url'] . ']' . $contact['name'] . '[/url]';
            $r = q("INSERT INTO `event` (`uid`,`cid`,`created`,`edited`,`start`,`finish`,`desc`,`type`)\n\t\t\t\tVALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s' ) ", intval($contact['uid']), intval($contact['id']), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert('UTC', 'UTC', $birthday)), dbesc(datetime_convert('UTC', 'UTC', $birthday . ' + 1 day ')), dbesc($bdtext), dbesc('birthday'));
            // update bdyear
            q("UPDATE `contact` SET `bdyear` = '%s' WHERE `uid` = %d AND `id` = %d LIMIT 1", dbesc(substr($birthday, 0, 4)), intval($contact['uid']), intval($contact['id']));
            // This function is called twice without reloading the contact
            // Make sure we only create one event. This is why &$contact
            // is a reference var in this function
            $contact['bdyear'] = substr($birthday, 0, 4);
        }
    }
    $community_page = 0;
    $rawtags = $feed->get_feed_tags(NAMESPACE_DFRN, 'community');
    if ($rawtags) {
        $community_page = intval($rawtags[0]['data']);
    }
    if (is_array($contact) && intval($contact['forum']) != $community_page) {
        q("update contact set forum = %d where id = %d limit 1", intval($community_page), intval($contact['id']));
        $contact['forum'] = (string) $community_page;
    }
    // process any deleted entries
    $del_entries = $feed->get_feed_tags(NAMESPACE_TOMB, 'deleted-entry');
    if (is_array($del_entries) && count($del_entries) && $pass != 2) {
        foreach ($del_entries as $dentry) {
            $deleted = false;
            if (isset($dentry['attribs']['']['ref'])) {
                $uri = $dentry['attribs']['']['ref'];
                $deleted = true;
                if (isset($dentry['attribs']['']['when'])) {
                    $when = $dentry['attribs']['']['when'];
                    $when = datetime_convert('UTC', 'UTC', $when, 'Y-m-d H:i:s');
                } else {
                    $when = datetime_convert('UTC', 'UTC', 'now', 'Y-m-d H:i:s');
                }
            }
            if ($deleted && is_array($contact)) {
                $r = q("SELECT `item`.*, `contact`.`self` FROM `item` left join `contact` on `item`.`contact-id` = `contact`.`id` \n\t\t\t\t\tWHERE `uri` = '%s' AND `item`.`uid` = %d AND `contact-id` = %d AND NOT `item`.`file` LIKE '%%[%%' LIMIT 1", dbesc($uri), intval($importer['uid']), intval($contact['id']));
                if (count($r)) {
                    $item = $r[0];
                    if (!$item['deleted']) {
                        logger('consume_feed: deleting item ' . $item['id'] . ' uri=' . $item['uri'], LOGGER_DEBUG);
                    }
                    if ($item['verb'] === ACTIVITY_TAG && $item['object-type'] === ACTIVITY_OBJ_TAGTERM) {
                        $xo = parse_xml_string($item['object'], false);
                        $xt = parse_xml_string($item['target'], false);
                        if ($xt->type === ACTIVITY_OBJ_NOTE) {
                            $i = q("select * from `item` where uri = '%s' and uid = %d limit 1", dbesc($xt->id), intval($importer['importer_uid']));
                            if (count($i)) {
                                // For tags, the owner cannot remove the tag on the author's copy of the post.
                                $owner_remove = $item['contact-id'] == $i[0]['contact-id'] ? true : false;
                                $author_remove = $item['origin'] && $item['self'] ? true : false;
                                $author_copy = $item['origin'] ? true : false;
                                if ($owner_remove && $author_copy) {
                                    continue;
                                }
                                if ($author_remove || $owner_remove) {
                                    $tags = explode(',', $i[0]['tag']);
                                    $newtags = array();
                                    if (count($tags)) {
                                        foreach ($tags as $tag) {
                                            if (trim($tag) !== trim($xo->body)) {
                                                $newtags[] = trim($tag);
                                            }
                                        }
                                    }
                                    q("update item set tag = '%s' where id = %d limit 1", dbesc(implode(',', $newtags)), intval($i[0]['id']));
                                }
                            }
                        }
                    }
                    if ($item['uri'] == $item['parent-uri']) {
                        $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s',\n\t\t\t\t\t\t\t`body` = '', `title` = ''\n\t\t\t\t\t\t\tWHERE `parent-uri` = '%s' AND `uid` = %d", dbesc($when), dbesc(datetime_convert()), dbesc($item['uri']), intval($importer['uid']));
                    } else {
                        $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s',\n\t\t\t\t\t\t\t`body` = '', `title` = '' \n\t\t\t\t\t\t\tWHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($when), dbesc(datetime_convert()), dbesc($uri), intval($importer['uid']));
                        if ($item['last-child']) {
                            // ensure that last-child is set in case the comment that had it just got wiped.
                            q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d ", dbesc(datetime_convert()), dbesc($item['parent-uri']), intval($item['uid']));
                            // who is the last child now?
                            $r = q("SELECT `id` FROM `item` WHERE `parent-uri` = '%s' AND `type` != 'activity' AND `deleted` = 0 AND `moderated` = 0 AND `uid` = %d \n\t\t\t\t\t\t\t\tORDER BY `created` DESC LIMIT 1", dbesc($item['parent-uri']), intval($importer['uid']));
                            if (count($r)) {
                                q("UPDATE `item` SET `last-child` = 1 WHERE `id` = %d LIMIT 1", intval($r[0]['id']));
                            }
                        }
                    }
                }
            }
        }
    }
    // Now process the feed
    if ($feed->get_item_quantity()) {
        logger('consume_feed: feed item count = ' . $feed->get_item_quantity());
        // in inverse date order
        if ($datedir) {
            $items = array_reverse($feed->get_items());
        } else {
            $items = $feed->get_items();
        }
        foreach ($items as $item) {
            $is_reply = false;
            $item_id = $item->get_id();
            $rawthread = $item->get_item_tags(NAMESPACE_THREAD, 'in-reply-to');
            if (isset($rawthread[0]['attribs']['']['ref'])) {
                $is_reply = true;
                $parent_uri = $rawthread[0]['attribs']['']['ref'];
            }
            if ($is_reply && is_array($contact)) {
                if ($pass == 1) {
                    continue;
                }
                // Have we seen it? If not, import it.
                $item_id = $item->get_id();
                $datarray = get_atom_elements($feed, $item);
                if (!x($datarray, 'author-name') && $contact['network'] != NETWORK_DFRN) {
                    $datarray['author-name'] = $contact['name'];
                }
                if (!x($datarray, 'author-link') && $contact['network'] != NETWORK_DFRN) {
                    $datarray['author-link'] = $contact['url'];
                }
                if (!x($datarray, 'author-avatar') && $contact['network'] != NETWORK_DFRN) {
                    $datarray['author-avatar'] = $contact['thumb'];
                }
                if (!x($datarray, 'author-name') || !x($datarray, 'author-link')) {
                    logger('consume_feed: no author information! ' . print_r($datarray, true));
                    continue;
                }
                $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($item_id), intval($importer['uid']));
                // Update content if 'updated' changes
                if (count($r)) {
                    if (x($datarray, 'edited') !== false && datetime_convert('UTC', 'UTC', $datarray['edited']) !== $r[0]['edited']) {
                        $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($datarray['title']), dbesc($datarray['body']), dbesc($datarray['tag']), dbesc(datetime_convert('UTC', 'UTC', $datarray['edited'])), dbesc($item_id), intval($importer['uid']));
                    }
                    // update last-child if it changes
                    $allow = $item->get_item_tags(NAMESPACE_DFRN, 'comment-allow');
                    if ($allow && $allow[0]['data'] != $r[0]['last-child']) {
                        $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d", dbesc(datetime_convert()), dbesc($parent_uri), intval($importer['uid']));
                        $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s'  WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", intval($allow[0]['data']), dbesc(datetime_convert()), dbesc($item_id), intval($importer['uid']));
                    }
                    continue;
                }
                $force_parent = false;
                if ($contact['network'] === NETWORK_OSTATUS || stristr($contact['url'], 'twitter.com')) {
                    if ($contact['network'] === NETWORK_OSTATUS) {
                        $force_parent = true;
                    }
                    if (strlen($datarray['title'])) {
                        unset($datarray['title']);
                    }
                    $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d", dbesc(datetime_convert()), dbesc($parent_uri), intval($importer['uid']));
                    $datarray['last-child'] = 1;
                }
                if ($contact['network'] === NETWORK_FEED || !strlen($contact['notify'])) {
                    // one way feed - no remote comment ability
                    $datarray['last-child'] = 0;
                }
                $datarray['parent-uri'] = $parent_uri;
                $datarray['uid'] = $importer['uid'];
                $datarray['contact-id'] = $contact['id'];
                if (activity_match($datarray['verb'], ACTIVITY_LIKE) || activity_match($datarray['verb'], ACTIVITY_DISLIKE)) {
                    $datarray['type'] = 'activity';
                    $datarray['gravity'] = GRAVITY_LIKE;
                    // only one like or dislike per person
                    $r = q("select id from item where uid = %d and `contact-id` = %d and verb ='%s' and deleted = 0 limit 1", intval($datarray['uid']), intval($datarray['contact-id']), dbesc($datarray['verb']));
                    if ($r && count($r)) {
                        continue;
                    }
                }
                if ($datarray['verb'] === ACTIVITY_TAG && $datarray['object-type'] === ACTIVITY_OBJ_TAGTERM) {
                    $xo = parse_xml_string($datarray['object'], false);
                    $xt = parse_xml_string($datarray['target'], false);
                    if ($xt->type == ACTIVITY_OBJ_NOTE) {
                        $r = q("select * from item where `uri` = '%s' AND `uid` = %d limit 1", dbesc($xt->id), intval($importer['importer_uid']));
                        if (!count($r)) {
                            continue;
                        }
                        // extract tag, if not duplicate, add to parent item
                        if ($xo->id && $xo->content) {
                            $newtag = '#[url=' . $xo->id . ']' . $xo->content . '[/url]';
                            if (!stristr($r[0]['tag'], $newtag)) {
                                q("UPDATE item SET tag = '%s' WHERE id = %d LIMIT 1", dbesc($r[0]['tag'] . (strlen($r[0]['tag']) ? ',' : '') . $newtag), intval($r[0]['id']));
                            }
                        }
                    }
                }
                $r = item_store($datarray, $force_parent);
                continue;
            } else {
                // Head post of a conversation. Have we seen it? If not, import it.
                $item_id = $item->get_id();
                $datarray = get_atom_elements($feed, $item);
                if (is_array($contact)) {
                    if (!x($datarray, 'author-name') && $contact['network'] != NETWORK_DFRN) {
                        $datarray['author-name'] = $contact['name'];
                    }
                    if (!x($datarray, 'author-link') && $contact['network'] != NETWORK_DFRN) {
                        $datarray['author-link'] = $contact['url'];
                    }
                    if (!x($datarray, 'author-avatar') && $contact['network'] != NETWORK_DFRN) {
                        $datarray['author-avatar'] = $contact['thumb'];
                    }
                }
                if (!x($datarray, 'author-name') || !x($datarray, 'author-link')) {
                    logger('consume_feed: no author information! ' . print_r($datarray, true));
                    continue;
                }
                // special handling for events
                if (x($datarray, 'object-type') && $datarray['object-type'] === ACTIVITY_OBJ_EVENT) {
                    $ev = bbtoevent($datarray['body']);
                    if (x($ev, 'desc') && x($ev, 'start')) {
                        $ev['uid'] = $importer['uid'];
                        $ev['uri'] = $item_id;
                        $ev['edited'] = $datarray['edited'];
                        $ev['private'] = $datarray['private'];
                        if (is_array($contact)) {
                            $ev['cid'] = $contact['id'];
                        }
                        $r = q("SELECT * FROM `event` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($item_id), intval($importer['uid']));
                        if (count($r)) {
                            $ev['id'] = $r[0]['id'];
                        }
                        $xyz = event_store($ev);
                        continue;
                    }
                }
                $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($item_id), intval($importer['uid']));
                // Update content if 'updated' changes
                if (count($r)) {
                    if (x($datarray, 'edited') !== false && datetime_convert('UTC', 'UTC', $datarray['edited']) !== $r[0]['edited']) {
                        $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($datarray['title']), dbesc($datarray['body']), dbesc($datarray['tag']), dbesc(datetime_convert('UTC', 'UTC', $datarray['edited'])), dbesc($item_id), intval($importer['uid']));
                    }
                    // update last-child if it changes
                    $allow = $item->get_item_tags(NAMESPACE_DFRN, 'comment-allow');
                    if ($allow && $allow[0]['data'] != $r[0]['last-child']) {
                        $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", intval($allow[0]['data']), dbesc(datetime_convert()), dbesc($item_id), intval($importer['uid']));
                    }
                    continue;
                }
                if (activity_match($datarray['verb'], ACTIVITY_FOLLOW)) {
                    logger('consume-feed: New follower');
                    new_follower($importer, $contact, $datarray, $item);
                    return;
                }
                if (activity_match($datarray['verb'], ACTIVITY_UNFOLLOW)) {
                    lose_follower($importer, $contact, $datarray, $item);
                    return;
                }
                if (activity_match($datarray['verb'], ACTIVITY_REQ_FRIEND)) {
                    logger('consume-feed: New friend request');
                    new_follower($importer, $contact, $datarray, $item, true);
                    return;
                }
                if (activity_match($datarray['verb'], ACTIVITY_UNFRIEND)) {
                    lose_sharer($importer, $contact, $datarray, $item);
                    return;
                }
                if (!is_array($contact)) {
                    return;
                }
                if ($contact['network'] === NETWORK_OSTATUS || stristr($contact['url'], 'twitter.com')) {
                    if (strlen($datarray['title'])) {
                        unset($datarray['title']);
                    }
                    $datarray['last-child'] = 1;
                }
                if ($contact['network'] === NETWORK_FEED || !strlen($contact['notify'])) {
                    // one way feed - no remote comment ability
                    $datarray['last-child'] = 0;
                }
                // This is my contact on another system, but it's really me.
                // Turn this into a wall post.
                if ($contact['remote_self']) {
                    $datarray['wall'] = 1;
                }
                $datarray['parent-uri'] = $item_id;
                $datarray['uid'] = $importer['uid'];
                $datarray['contact-id'] = $contact['id'];
                if (!link_compare($datarray['owner-link'], $contact['url'])) {
                    // The item owner info is not our contact. It's OK and is to be expected if this is a tgroup delivery,
                    // but otherwise there's a possible data mixup on the sender's system.
                    // the tgroup delivery code called from item_store will correct it if it's a forum,
                    // but we're going to unconditionally correct it here so that the post will always be owned by our contact.
                    logger('consume_feed: Correcting item owner.', LOGGER_DEBUG);
                    $datarray['owner-name'] = $contact['name'];
                    $datarray['owner-link'] = $contact['url'];
                    $datarray['owner-avatar'] = $contact['thumb'];
                }
                $r = item_store($datarray);
                continue;
            }
        }
    }
}
Exemple #2
0
/**
 *
 * consume_feed - process atom feed and update anything/everything we might need to update
 *
 * $xml = the (atom) feed to consume - RSS isn't as fully supported but may work for simple feeds.
 *
 * $importer = the contact_record (joined to user_record) of the local user who owns this relationship.
 *             It is this person's stuff that is going to be updated.
 * $contact =  the person who is sending us stuff. If not set, we MAY be processing a "follow" activity
 *             from an external network and MAY create an appropriate contact record. Otherwise, we MUST
 *             have a contact record.
 * $hub = should we find a hub declation in the feed, pass it back to our calling process, who might (or
 *        might not) try and subscribe to it.
 * $datedir sorts in reverse order
 * $pass - by default ($pass = 0) we cannot guarantee that a parent item has been
 *      imported prior to its children being seen in the stream unless we are certain
 *      of how the feed is arranged/ordered.
 * With $pass = 1, we only pull parent items out of the stream.
 * With $pass = 2, we only pull children (comments/likes).
 *
 * So running this twice, first with pass 1 and then with pass 2 will do the right
 * thing regardless of feed ordering. This won't be adequate in a fully-threaded
 * model where comments can have sub-threads. That would require some massive sorting
 * to get all the feed items into a mostly linear ordering, and might still require
 * recursion.
 */
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(), "ostatus2");
            //file_put_contents($tempfile, $xml);
            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';
    if (!strlen($xml)) {
        logger('consume_feed: empty input');
        return;
    }
    $feed = new SimplePie();
    $feed->set_raw_data($xml);
    if ($datedir) {
        $feed->enable_order_by_date(true);
    } else {
        $feed->enable_order_by_date(false);
    }
    $feed->init();
    if ($feed->error()) {
        logger('consume_feed: Error parsing XML: ' . $feed->error());
    }
    $permalink = $feed->get_permalink();
    // Check at the feed level for updated contact name and/or photo
    $name_updated = '';
    $new_name = '';
    $photo_timestamp = '';
    $photo_url = '';
    $birthday = '';
    $contact_updated = '';
    $hubs = $feed->get_links('hub');
    logger('consume_feed: hubs: ' . print_r($hubs, true), LOGGER_DATA);
    if (count($hubs)) {
        $hub = implode(',', $hubs);
    }
    $rawtags = $feed->get_feed_tags(NAMESPACE_DFRN, 'owner');
    if (!$rawtags) {
        $rawtags = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author');
    }
    if ($rawtags) {
        $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10];
        if ($elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated']) {
            $name_updated = $elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated'];
            $new_name = $elems['name'][0]['data'];
            // Manually checking for changed contact names
            if ($new_name != $contact['name'] and $new_name != "" and $name_updated <= $contact['name-date']) {
                $name_updated = date("c");
                $photo_timestamp = date("c");
            }
        }
        if (x($elems, 'link') && $elems['link'][0]['attribs']['']['rel'] === 'photo' && $elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated']) {
            if ($photo_timestamp == "") {
                $photo_timestamp = datetime_convert('UTC', 'UTC', $elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated']);
            }
            $photo_url = $elems['link'][0]['attribs']['']['href'];
        }
        if (x($rawtags[0]['child'], NAMESPACE_DFRN) && x($rawtags[0]['child'][NAMESPACE_DFRN], 'birthday')) {
            $birthday = datetime_convert('UTC', 'UTC', $rawtags[0]['child'][NAMESPACE_DFRN]['birthday'][0]['data']);
        }
    }
    if (is_array($contact) && $photo_timestamp && strlen($photo_url) && $photo_timestamp > $contact['avatar-date']) {
        logger('consume_feed: Updating photo for ' . $contact['name'] . ' from ' . $photo_url . ' uid: ' . $contact['uid']);
        $contact_updated = $photo_timestamp;
        require_once "include/Photo.php";
        $photos = import_profile_photo($photo_url, $contact['uid'], $contact['id']);
        q("UPDATE `contact` SET `avatar-date` = '%s', `photo` = '%s', `thumb` = '%s', `micro` = '%s'\n\t\t\tWHERE `uid` = %d AND `id` = %d AND NOT `self`", dbesc(datetime_convert()), dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), intval($contact['uid']), intval($contact['id']));
    }
    if (is_array($contact) && $name_updated && strlen($new_name) && $name_updated > $contact['name-date']) {
        if ($name_updated > $contact_updated) {
            $contact_updated = $name_updated;
        }
        $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `id` = %d LIMIT 1", intval($contact['uid']), intval($contact['id']));
        $x = q("UPDATE `contact` SET `name` = '%s', `name-date` = '%s' WHERE `uid` = %d AND `id` = %d AND `name` != '%s' AND NOT `self`", dbesc(notags(trim($new_name))), dbesc(datetime_convert()), intval($contact['uid']), intval($contact['id']), dbesc(notags(trim($new_name))));
        // do our best to update the name on content items
        if (count($r) and notags(trim($new_name)) != $r[0]['name']) {
            q("UPDATE `item` SET `author-name` = '%s' WHERE `author-name` = '%s' AND `author-link` = '%s' AND `uid` = %d AND `author-name` != '%s'", dbesc(notags(trim($new_name))), dbesc($r[0]['name']), dbesc($r[0]['url']), intval($contact['uid']), dbesc(notags(trim($new_name))));
        }
    }
    if ($contact_updated and $new_name and $photo_url) {
        poco_check($contact['url'], $new_name, NETWORK_DFRN, $photo_url, "", "", "", "", "", $contact_updated, 2, $contact['id'], $contact['uid']);
    }
    if (strlen($birthday)) {
        if (substr($birthday, 0, 4) != $contact['bdyear']) {
            logger('consume_feed: updating birthday: ' . $birthday);
            /**
             *
             * Add new birthday event for this person
             *
             * $bdtext is just a readable placeholder in case the event is shared
             * with others. We will replace it during presentation to our $importer
             * to contain a sparkle link and perhaps a photo.
             *
             */
            $bdtext = sprintf(t('%s\'s birthday'), $contact['name']);
            $bdtext2 = sprintf(t('Happy Birthday %s'), ' [url=' . $contact['url'] . ']' . $contact['name'] . '[/url]');
            $r = q("INSERT INTO `event` (`uid`,`cid`,`created`,`edited`,`start`,`finish`,`summary`,`desc`,`type`)\n\t\t\t\tVALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", intval($contact['uid']), intval($contact['id']), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert('UTC', 'UTC', $birthday)), dbesc(datetime_convert('UTC', 'UTC', $birthday . ' + 1 day ')), dbesc($bdtext), dbesc($bdtext2), dbesc('birthday'));
            // update bdyear
            q("UPDATE `contact` SET `bdyear` = '%s' WHERE `uid` = %d AND `id` = %d", dbesc(substr($birthday, 0, 4)), intval($contact['uid']), intval($contact['id']));
            // This function is called twice without reloading the contact
            // Make sure we only create one event. This is why &$contact
            // is a reference var in this function
            $contact['bdyear'] = substr($birthday, 0, 4);
        }
    }
    $community_page = 0;
    $rawtags = $feed->get_feed_tags(NAMESPACE_DFRN, 'community');
    if ($rawtags) {
        $community_page = intval($rawtags[0]['data']);
    }
    if (is_array($contact) && intval($contact['forum']) != $community_page) {
        q("update contact set forum = %d where id = %d", intval($community_page), intval($contact['id']));
        $contact['forum'] = (string) $community_page;
    }
    // process any deleted entries
    $del_entries = $feed->get_feed_tags(NAMESPACE_TOMB, 'deleted-entry');
    if (is_array($del_entries) && count($del_entries) && $pass != 2) {
        foreach ($del_entries as $dentry) {
            $deleted = false;
            if (isset($dentry['attribs']['']['ref'])) {
                $uri = $dentry['attribs']['']['ref'];
                $deleted = true;
                if (isset($dentry['attribs']['']['when'])) {
                    $when = $dentry['attribs']['']['when'];
                    $when = datetime_convert('UTC', 'UTC', $when, 'Y-m-d H:i:s');
                } else {
                    $when = datetime_convert('UTC', 'UTC', 'now', 'Y-m-d H:i:s');
                }
            }
            if ($deleted && is_array($contact)) {
                $r = q("SELECT `item`.*, `contact`.`self` FROM `item` INNER JOIN `contact` on `item`.`contact-id` = `contact`.`id`\n\t\t\t\t\tWHERE `uri` = '%s' AND `item`.`uid` = %d AND `contact-id` = %d AND NOT `item`.`file` LIKE '%%[%%' LIMIT 1", dbesc($uri), intval($importer['uid']), intval($contact['id']));
                if (count($r)) {
                    $item = $r[0];
                    if (!$item['deleted']) {
                        logger('consume_feed: deleting item ' . $item['id'] . ' uri=' . $item['uri'], LOGGER_DEBUG);
                    }
                    if ($item['object-type'] === ACTIVITY_OBJ_EVENT) {
                        logger("Deleting event " . $item['event-id'], LOGGER_DEBUG);
                        event_delete($item['event-id']);
                    }
                    if ($item['verb'] === ACTIVITY_TAG && $item['object-type'] === ACTIVITY_OBJ_TAGTERM) {
                        $xo = parse_xml_string($item['object'], false);
                        $xt = parse_xml_string($item['target'], false);
                        if ($xt->type === ACTIVITY_OBJ_NOTE) {
                            $i = q("select * from `item` where uri = '%s' and uid = %d limit 1", dbesc($xt->id), intval($importer['importer_uid']));
                            if (count($i)) {
                                // For tags, the owner cannot remove the tag on the author's copy of the post.
                                $owner_remove = $item['contact-id'] == $i[0]['contact-id'] ? true : false;
                                $author_remove = $item['origin'] && $item['self'] ? true : false;
                                $author_copy = $item['origin'] ? true : false;
                                if ($owner_remove && $author_copy) {
                                    continue;
                                }
                                if ($author_remove || $owner_remove) {
                                    $tags = explode(',', $i[0]['tag']);
                                    $newtags = array();
                                    if (count($tags)) {
                                        foreach ($tags as $tag) {
                                            if (trim($tag) !== trim($xo->body)) {
                                                $newtags[] = trim($tag);
                                            }
                                        }
                                    }
                                    q("update item set tag = '%s' where id = %d", dbesc(implode(',', $newtags)), intval($i[0]['id']));
                                    create_tags_from_item($i[0]['id']);
                                }
                            }
                        }
                    }
                    if ($item['uri'] == $item['parent-uri']) {
                        $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s',\n\t\t\t\t\t\t\t`body` = '', `title` = ''\n\t\t\t\t\t\t\tWHERE `parent-uri` = '%s' AND `uid` = %d", dbesc($when), dbesc(datetime_convert()), dbesc($item['uri']), intval($importer['uid']));
                        create_tags_from_itemuri($item['uri'], $importer['uid']);
                        create_files_from_itemuri($item['uri'], $importer['uid']);
                        update_thread_uri($item['uri'], $importer['uid']);
                    } else {
                        $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s',\n\t\t\t\t\t\t\t`body` = '', `title` = ''\n\t\t\t\t\t\t\tWHERE `uri` = '%s' AND `uid` = %d", dbesc($when), dbesc(datetime_convert()), dbesc($uri), intval($importer['uid']));
                        create_tags_from_itemuri($uri, $importer['uid']);
                        create_files_from_itemuri($uri, $importer['uid']);
                        if ($item['last-child']) {
                            // ensure that last-child is set in case the comment that had it just got wiped.
                            q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d ", dbesc(datetime_convert()), dbesc($item['parent-uri']), intval($item['uid']));
                            // who is the last child now?
                            $r = q("SELECT `id` FROM `item` WHERE `parent-uri` = '%s' AND `type` != 'activity' AND `deleted` = 0 AND `moderated` = 0 AND `uid` = %d\n\t\t\t\t\t\t\t\tORDER BY `created` DESC LIMIT 1", dbesc($item['parent-uri']), intval($importer['uid']));
                            if (count($r)) {
                                q("UPDATE `item` SET `last-child` = 1 WHERE `id` = %d", intval($r[0]['id']));
                            }
                        }
                    }
                }
            }
        }
    }
    // Now process the feed
    if ($feed->get_item_quantity()) {
        logger('consume_feed: feed item count = ' . $feed->get_item_quantity());
        // in inverse date order
        if ($datedir) {
            $items = array_reverse($feed->get_items());
        } else {
            $items = $feed->get_items();
        }
        foreach ($items as $item) {
            $is_reply = false;
            $item_id = $item->get_id();
            $rawthread = $item->get_item_tags(NAMESPACE_THREAD, 'in-reply-to');
            if (isset($rawthread[0]['attribs']['']['ref'])) {
                $is_reply = true;
                $parent_uri = $rawthread[0]['attribs']['']['ref'];
            }
            if ($is_reply && is_array($contact)) {
                if ($pass == 1) {
                    continue;
                }
                // not allowed to post
                if ($contact['rel'] == CONTACT_IS_FOLLOWER) {
                    continue;
                }
                // Have we seen it? If not, import it.
                $item_id = $item->get_id();
                $datarray = get_atom_elements($feed, $item, $contact);
                if (!x($datarray, 'author-name') && $contact['network'] != NETWORK_DFRN) {
                    $datarray['author-name'] = $contact['name'];
                }
                if (!x($datarray, 'author-link') && $contact['network'] != NETWORK_DFRN) {
                    $datarray['author-link'] = $contact['url'];
                }
                if (!x($datarray, 'author-avatar') && $contact['network'] != NETWORK_DFRN) {
                    $datarray['author-avatar'] = $contact['thumb'];
                }
                if (!x($datarray, 'author-name') || !x($datarray, 'author-link')) {
                    logger('consume_feed: no author information! ' . print_r($datarray, true));
                    continue;
                }
                $force_parent = false;
                if ($contact['network'] === NETWORK_OSTATUS || stristr($contact['url'], 'twitter.com')) {
                    if ($contact['network'] === NETWORK_OSTATUS) {
                        $force_parent = true;
                    }
                    if (strlen($datarray['title'])) {
                        unset($datarray['title']);
                    }
                    $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d", dbesc(datetime_convert()), dbesc($parent_uri), intval($importer['uid']));
                    $datarray['last-child'] = 1;
                    update_thread_uri($parent_uri, $importer['uid']);
                }
                $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($item_id), intval($importer['uid']));
                // Update content if 'updated' changes
                if (count($r)) {
                    if (edited_timestamp_is_newer($r[0], $datarray)) {
                        // do not accept (ignore) an earlier edit than one we currently have.
                        if (datetime_convert('UTC', 'UTC', $datarray['edited']) < $r[0]['edited']) {
                            continue;
                        }
                        $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s', `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d", dbesc($datarray['title']), dbesc($datarray['body']), dbesc($datarray['tag']), dbesc(datetime_convert('UTC', 'UTC', $datarray['edited'])), dbesc(datetime_convert()), dbesc($item_id), intval($importer['uid']));
                        create_tags_from_itemuri($item_id, $importer['uid']);
                        update_thread_uri($item_id, $importer['uid']);
                    }
                    // update last-child if it changes
                    $allow = $item->get_item_tags(NAMESPACE_DFRN, 'comment-allow');
                    if ($allow && $allow[0]['data'] != $r[0]['last-child']) {
                        $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d", dbesc(datetime_convert()), dbesc($parent_uri), intval($importer['uid']));
                        $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s'  WHERE `uri` = '%s' AND `uid` = %d", intval($allow[0]['data']), dbesc(datetime_convert()), dbesc($item_id), intval($importer['uid']));
                        update_thread_uri($item_id, $importer['uid']);
                    }
                    continue;
                }
                if ($contact['network'] === NETWORK_FEED || !strlen($contact['notify'])) {
                    // one way feed - no remote comment ability
                    $datarray['last-child'] = 0;
                }
                $datarray['parent-uri'] = $parent_uri;
                $datarray['uid'] = $importer['uid'];
                $datarray['contact-id'] = $contact['id'];
                if ($datarray['verb'] === ACTIVITY_LIKE || $datarray['verb'] === ACTIVITY_DISLIKE || $datarray['verb'] === ACTIVITY_ATTEND || $datarray['verb'] === ACTIVITY_ATTENDNO || $datarray['verb'] === ACTIVITY_ATTENDMAYBE) {
                    $datarray['type'] = 'activity';
                    $datarray['gravity'] = GRAVITY_LIKE;
                    // only one like or dislike per person
                    // splitted into two queries for performance issues
                    $r = q("select id from item where uid = %d and `contact-id` = %d and verb ='%s' and deleted = 0 and (`parent-uri` = '%s') limit 1", intval($datarray['uid']), intval($datarray['contact-id']), dbesc($datarray['verb']), dbesc($parent_uri));
                    if ($r && count($r)) {
                        continue;
                    }
                    $r = q("select id from item where uid = %d and `contact-id` = %d and verb ='%s' and deleted = 0 and (`thr-parent` = '%s') limit 1", intval($datarray['uid']), intval($datarray['contact-id']), dbesc($datarray['verb']), dbesc($parent_uri));
                    if ($r && count($r)) {
                        continue;
                    }
                }
                if ($datarray['verb'] === ACTIVITY_TAG && $datarray['object-type'] === ACTIVITY_OBJ_TAGTERM) {
                    $xo = parse_xml_string($datarray['object'], false);
                    $xt = parse_xml_string($datarray['target'], false);
                    if ($xt->type == ACTIVITY_OBJ_NOTE) {
                        $r = q("select * from item where `uri` = '%s' AND `uid` = %d limit 1", dbesc($xt->id), intval($importer['importer_uid']));
                        if (!count($r)) {
                            continue;
                        }
                        // extract tag, if not duplicate, add to parent item
                        if ($xo->id && $xo->content) {
                            $newtag = '#[url=' . $xo->id . ']' . $xo->content . '[/url]';
                            if (!stristr($r[0]['tag'], $newtag)) {
                                q("UPDATE item SET tag = '%s' WHERE id = %d", dbesc($r[0]['tag'] . (strlen($r[0]['tag']) ? ',' : '') . $newtag), intval($r[0]['id']));
                                create_tags_from_item($r[0]['id']);
                            }
                        }
                    }
                }
                $r = item_store($datarray, $force_parent);
                continue;
            } else {
                // Head post of a conversation. Have we seen it? If not, import it.
                $item_id = $item->get_id();
                $datarray = get_atom_elements($feed, $item, $contact);
                if (is_array($contact)) {
                    if (!x($datarray, 'author-name') && $contact['network'] != NETWORK_DFRN) {
                        $datarray['author-name'] = $contact['name'];
                    }
                    if (!x($datarray, 'author-link') && $contact['network'] != NETWORK_DFRN) {
                        $datarray['author-link'] = $contact['url'];
                    }
                    if (!x($datarray, 'author-avatar') && $contact['network'] != NETWORK_DFRN) {
                        $datarray['author-avatar'] = $contact['thumb'];
                    }
                }
                if (!x($datarray, 'author-name') || !x($datarray, 'author-link')) {
                    logger('consume_feed: no author information! ' . print_r($datarray, true));
                    continue;
                }
                // special handling for events
                if (x($datarray, 'object-type') && $datarray['object-type'] === ACTIVITY_OBJ_EVENT) {
                    $ev = bbtoevent($datarray['body']);
                    if ((x($ev, 'desc') || x($ev, 'summary')) && x($ev, 'start')) {
                        $ev['uid'] = $importer['uid'];
                        $ev['uri'] = $item_id;
                        $ev['edited'] = $datarray['edited'];
                        $ev['private'] = $datarray['private'];
                        $ev['guid'] = $datarray['guid'];
                        if (is_array($contact)) {
                            $ev['cid'] = $contact['id'];
                        }
                        $r = q("SELECT * FROM `event` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($item_id), intval($importer['uid']));
                        if (count($r)) {
                            $ev['id'] = $r[0]['id'];
                        }
                        $xyz = event_store($ev);
                        continue;
                    }
                }
                if ($contact['network'] === NETWORK_OSTATUS || stristr($contact['url'], 'twitter.com')) {
                    if (strlen($datarray['title'])) {
                        unset($datarray['title']);
                    }
                    $datarray['last-child'] = 1;
                }
                $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($item_id), intval($importer['uid']));
                // Update content if 'updated' changes
                if (count($r)) {
                    if (edited_timestamp_is_newer($r[0], $datarray)) {
                        // do not accept (ignore) an earlier edit than one we currently have.
                        if (datetime_convert('UTC', 'UTC', $datarray['edited']) < $r[0]['edited']) {
                            continue;
                        }
                        $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s', `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d", dbesc($datarray['title']), dbesc($datarray['body']), dbesc($datarray['tag']), dbesc(datetime_convert('UTC', 'UTC', $datarray['edited'])), dbesc(datetime_convert()), dbesc($item_id), intval($importer['uid']));
                        create_tags_from_itemuri($item_id, $importer['uid']);
                        update_thread_uri($item_id, $importer['uid']);
                    }
                    // update last-child if it changes
                    $allow = $item->get_item_tags(NAMESPACE_DFRN, 'comment-allow');
                    if ($allow && $allow[0]['data'] != $r[0]['last-child']) {
                        $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d", intval($allow[0]['data']), dbesc(datetime_convert()), dbesc($item_id), intval($importer['uid']));
                        update_thread_uri($item_id, $importer['uid']);
                    }
                    continue;
                }
                if (activity_match($datarray['verb'], ACTIVITY_FOLLOW)) {
                    logger('consume-feed: New follower');
                    new_follower($importer, $contact, $datarray, $item);
                    return;
                }
                if (activity_match($datarray['verb'], ACTIVITY_UNFOLLOW)) {
                    lose_follower($importer, $contact, $datarray, $item);
                    return;
                }
                if (activity_match($datarray['verb'], ACTIVITY_REQ_FRIEND)) {
                    logger('consume-feed: New friend request');
                    new_follower($importer, $contact, $datarray, $item, true);
                    return;
                }
                if (activity_match($datarray['verb'], ACTIVITY_UNFRIEND)) {
                    lose_sharer($importer, $contact, $datarray, $item);
                    return;
                }
                if (!is_array($contact)) {
                    return;
                }
                if ($contact['network'] === NETWORK_FEED || !strlen($contact['notify'])) {
                    // one way feed - no remote comment ability
                    $datarray['last-child'] = 0;
                }
                if ($contact['network'] === NETWORK_FEED) {
                    $datarray['private'] = 2;
                }
                $datarray['parent-uri'] = $item_id;
                $datarray['uid'] = $importer['uid'];
                $datarray['contact-id'] = $contact['id'];
                if (!link_compare($datarray['owner-link'], $contact['url'])) {
                    // The item owner info is not our contact. It's OK and is to be expected if this is a tgroup delivery,
                    // but otherwise there's a possible data mixup on the sender's system.
                    // the tgroup delivery code called from item_store will correct it if it's a forum,
                    // but we're going to unconditionally correct it here so that the post will always be owned by our contact.
                    logger('consume_feed: Correcting item owner.', LOGGER_DEBUG);
                    $datarray['owner-name'] = $contact['name'];
                    $datarray['owner-link'] = $contact['url'];
                    $datarray['owner-avatar'] = $contact['thumb'];
                }
                // We've allowed "followers" to reach this point so we can decide if they are
                // posting an @-tag delivery, which followers are allowed to do for certain
                // page types. Now that we've parsed the post, let's check if it is legit. Otherwise ignore it.
                if ($contact['rel'] == CONTACT_IS_FOLLOWER && !tgroup_check($importer['uid'], $datarray)) {
                    continue;
                }
                // This is my contact on another system, but it's really me.
                // Turn this into a wall post.
                $notify = item_is_remote_self($contact, $datarray);
                $r = item_store($datarray, false, $notify);
                logger('Stored - Contact ' . $contact['url'] . ' Notify ' . $notify . ' return ' . $r . ' Item ' . print_r($datarray, true), LOGGER_DEBUG);
                continue;
            }
        }
    }
}
Exemple #3
0
function ostatus_import($xml, $importer, &$contact, &$hub)
{
    $a = get_app();
    logger("Import OStatus message", LOGGER_DEBUG);
    if ($xml == "") {
        return;
    }
    $doc = new DOMDocument();
    @$doc->loadXML($xml);
    $xpath = new DomXPath($doc);
    $xpath->registerNamespace('atom', "http://www.w3.org/2005/Atom");
    $xpath->registerNamespace('thr', "http://purl.org/syndication/thread/1.0");
    $xpath->registerNamespace('georss', "http://www.georss.org/georss");
    $xpath->registerNamespace('activity', "http://activitystrea.ms/spec/1.0/");
    $xpath->registerNamespace('media', "http://purl.org/syndication/atommedia");
    $xpath->registerNamespace('poco', "http://portablecontacts.net/spec/1.0");
    $xpath->registerNamespace('ostatus', "http://ostatus.org/schema/1.0");
    $xpath->registerNamespace('statusnet', "http://status.net/schema/api/1/");
    $gub = "";
    $hub_attributes = $xpath->query("/atom:feed/atom:link[@rel='hub']")->item(0)->attributes;
    if (is_object($hub_attributes)) {
        foreach ($hub_attributes as $hub_attribute) {
            if ($hub_attribute->name == "href") {
                $hub = $hub_attribute->textContent;
                logger("Found hub " . $hub, LOGGER_DEBUG);
            }
        }
    }
    $header = array();
    $header["uid"] = $importer["uid"];
    $header["network"] = NETWORK_OSTATUS;
    $header["type"] = "remote";
    $header["wall"] = 0;
    $header["origin"] = 0;
    $header["gravity"] = GRAVITY_PARENT;
    // it could either be a received post or a post we fetched by ourselves
    // depending on that, the first node is different
    $first_child = $doc->firstChild->tagName;
    if ($first_child == "feed") {
        $entries = $xpath->query('/atom:feed/atom:entry');
    } else {
        $entries = $xpath->query('/atom:entry');
    }
    $conversation = "";
    $conversationlist = array();
    $item_id = 0;
    // Reverse the order of the entries
    $entrylist = array();
    foreach ($entries as $entry) {
        $entrylist[] = $entry;
    }
    foreach (array_reverse($entrylist) as $entry) {
        $mention = false;
        // fetch the author
        if ($first_child == "feed") {
            $author = ostatus_fetchauthor($xpath, $doc->firstChild, $importer, $contact, false);
        } else {
            $author = ostatus_fetchauthor($xpath, $entry, $importer, $contact, false);
        }
        $value = $xpath->evaluate('atom:author/poco:preferredUsername/text()', $context)->item(0)->nodeValue;
        if ($value != "") {
            $nickname = $value;
        } else {
            $nickname = $author["author-name"];
        }
        $item = array_merge($header, $author);
        // Now get the item
        $item["uri"] = $xpath->query('atom:id/text()', $entry)->item(0)->nodeValue;
        $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s'", intval($importer["uid"]), dbesc($item["uri"]));
        if ($r) {
            logger("Item with uri " . $item["uri"] . " for user " . $importer["uid"] . " already existed under id " . $r[0]["id"], LOGGER_DEBUG);
            continue;
        }
        $item["body"] = add_page_info_to_body(html2bbcode($xpath->query('atom:content/text()', $entry)->item(0)->nodeValue));
        $item["object-type"] = $xpath->query('activity:object-type/text()', $entry)->item(0)->nodeValue;
        if ($item["object-type"] == ACTIVITY_OBJ_BOOKMARK or $item["object-type"] == ACTIVITY_OBJ_EVENT) {
            $item["title"] = $xpath->query('atom:title/text()', $entry)->item(0)->nodeValue;
            $item["body"] = $xpath->query('atom:summary/text()', $entry)->item(0)->nodeValue;
        } elseif ($item["object-type"] == ACTIVITY_OBJ_QUESTION) {
            $item["title"] = $xpath->query('atom:title/text()', $entry)->item(0)->nodeValue;
        }
        $item["object"] = $xml;
        $item["verb"] = $xpath->query('activity:verb/text()', $entry)->item(0)->nodeValue;
        // To-Do:
        // Delete a message
        if ($item["verb"] == "qvitter-delete-notice") {
            // ignore "Delete" messages (by now)
            logger("Ignore delete message " . print_r($item, true));
            continue;
        }
        if ($item["verb"] == ACTIVITY_JOIN) {
            // ignore "Join" messages
            logger("Ignore join message " . print_r($item, true));
            continue;
        }
        if ($item["verb"] == ACTIVITY_FOLLOW) {
            new_follower($importer, $contact, $item, $nickname);
            continue;
        }
        if ($item["verb"] == NAMESPACE_OSTATUS . "/unfollow") {
            lose_follower($importer, $contact, $item, $dummy);
            continue;
        }
        if ($item["verb"] == ACTIVITY_FAVORITE) {
            $orig_uri = $xpath->query("activity:object/atom:id", $entry)->item(0)->nodeValue;
            logger("Favorite " . $orig_uri . " " . print_r($item, true));
            $item["verb"] = ACTIVITY_LIKE;
            $item["parent-uri"] = $orig_uri;
            $item["gravity"] = GRAVITY_LIKE;
        }
        if ($item["verb"] == NAMESPACE_OSTATUS . "/unfavorite") {
            // Ignore "Unfavorite" message
            logger("Ignore unfavorite message " . print_r($item, true));
            continue;
        }
        // http://activitystrea.ms/schema/1.0/rsvp-yes
        if (!in_array($item["verb"], array(ACTIVITY_POST, ACTIVITY_LIKE, ACTIVITY_SHARE))) {
            logger("Unhandled verb " . $item["verb"] . " " . print_r($item, true));
        }
        $item["created"] = $xpath->query('atom:published/text()', $entry)->item(0)->nodeValue;
        $item["edited"] = $xpath->query('atom:updated/text()', $entry)->item(0)->nodeValue;
        $conversation = $xpath->query('ostatus:conversation/text()', $entry)->item(0)->nodeValue;
        $related = "";
        $inreplyto = $xpath->query('thr:in-reply-to', $entry);
        if (is_object($inreplyto->item(0))) {
            foreach ($inreplyto->item(0)->attributes as $attributes) {
                if ($attributes->name == "ref") {
                    $item["parent-uri"] = $attributes->textContent;
                }
                if ($attributes->name == "href") {
                    $related = $attributes->textContent;
                }
            }
        }
        $georsspoint = $xpath->query('georss:point', $entry);
        if ($georsspoint) {
            $item["coord"] = $georsspoint->item(0)->nodeValue;
        }
        // To-Do
        // $item["location"] =
        $categories = $xpath->query('atom:category', $entry);
        if ($categories) {
            foreach ($categories as $category) {
                foreach ($category->attributes as $attributes) {
                    if ($attributes->name == "term") {
                        $term = $attributes->textContent;
                        if (strlen($item["tag"])) {
                            $item["tag"] .= ',';
                        }
                        $item["tag"] .= "#[url=" . $a->get_baseurl() . "/search?tag=" . $term . "]" . $term . "[/url]";
                    }
                }
            }
        }
        $self = "";
        $enclosure = "";
        $links = $xpath->query('atom:link', $entry);
        if ($links) {
            $rel = "";
            $href = "";
            $type = "";
            $length = "0";
            $title = "";
            foreach ($links as $link) {
                foreach ($link->attributes as $attributes) {
                    if ($attributes->name == "href") {
                        $href = $attributes->textContent;
                    }
                    if ($attributes->name == "rel") {
                        $rel = $attributes->textContent;
                    }
                    if ($attributes->name == "type") {
                        $type = $attributes->textContent;
                    }
                    if ($attributes->name == "length") {
                        $length = $attributes->textContent;
                    }
                    if ($attributes->name == "title") {
                        $title = $attributes->textContent;
                    }
                }
                if ($rel != "" and $href != "") {
                    switch ($rel) {
                        case "alternate":
                            $item["plink"] = $href;
                            if ($item["object-type"] == ACTIVITY_OBJ_QUESTION or $item["object-type"] == ACTIVITY_OBJ_EVENT) {
                                $item["body"] .= add_page_info($href);
                            }
                            break;
                        case "ostatus:conversation":
                            $conversation = $href;
                            break;
                        case "enclosure":
                            $enclosure = $href;
                            if (strlen($item["attach"])) {
                                $item["attach"] .= ',';
                            }
                            $item["attach"] .= '[attach]href="' . $href . '" length="' . $length . '" type="' . $type . '" title="' . $title . '"[/attach]';
                            break;
                        case "related":
                            if ($item["object-type"] != ACTIVITY_OBJ_BOOKMARK) {
                                if (!isset($item["parent-uri"])) {
                                    $item["parent-uri"] = $href;
                                }
                                if ($related == "") {
                                    $related = $href;
                                }
                            } else {
                                $item["body"] .= add_page_info($href);
                            }
                            break;
                        case "self":
                            $self = $href;
                            break;
                        case "mentioned":
                            // Notification check
                            if ($importer["nurl"] == normalise_link($href)) {
                                $mention = true;
                            }
                            break;
                    }
                }
            }
        }
        $local_id = "";
        $repeat_of = "";
        $notice_info = $xpath->query('statusnet:notice_info', $entry);
        if ($notice_info and $notice_info->length > 0) {
            foreach ($notice_info->item(0)->attributes as $attributes) {
                if ($attributes->name == "source") {
                    $item["app"] = strip_tags($attributes->textContent);
                }
                if ($attributes->name == "local_id") {
                    $local_id = $attributes->textContent;
                }
                if ($attributes->name == "repeat_of") {
                    $repeat_of = $attributes->textContent;
                }
            }
        }
        // Is it a repeated post?
        if ($repeat_of != "") {
            $activityobjects = $xpath->query('activity:object', $entry)->item(0);
            if (is_object($activityobjects)) {
                $orig_uri = $xpath->query("activity:object/atom:id", $activityobjects)->item(0)->nodeValue;
                if (!isset($orig_uri)) {
                    $orig_uri = $xpath->query('atom:id/text()', $activityobjects)->item(0)->nodeValue;
                }
                $orig_links = $xpath->query("activity:object/atom:link[@rel='alternate']", $activityobjects);
                if ($orig_links and $orig_links->length > 0) {
                    foreach ($orig_links->item(0)->attributes as $attributes) {
                        if ($attributes->name == "href") {
                            $orig_link = $attributes->textContent;
                        }
                    }
                }
                if (!isset($orig_link)) {
                    $orig_link = $xpath->query("atom:link[@rel='alternate']", $activityobjects)->item(0)->nodeValue;
                }
                if (!isset($orig_link)) {
                    $orig_link = ostatus_convert_href($orig_uri);
                }
                $orig_body = $xpath->query('activity:object/atom:content/text()', $activityobjects)->item(0)->nodeValue;
                if (!isset($orig_body)) {
                    $orig_body = $xpath->query('atom:content/text()', $activityobjects)->item(0)->nodeValue;
                }
                $orig_created = $xpath->query('atom:published/text()', $activityobjects)->item(0)->nodeValue;
                $orig_contact = $contact;
                $orig_author = ostatus_fetchauthor($xpath, $activityobjects, $importer, $orig_contact, false);
                //if (!intval(get_config('system','wall-to-wall_share'))) {
                //	$prefix = share_header($orig_author['author-name'], $orig_author['author-link'], $orig_author['author-avatar'], "", $orig_created, $orig_link);
                //	$item["body"] = $prefix.add_page_info_to_body(html2bbcode($orig_body))."[/share]";
                //} else {
                $item["author-name"] = $orig_author["author-name"];
                $item["author-link"] = $orig_author["author-link"];
                $item["author-avatar"] = $orig_author["author-avatar"];
                $item["body"] = add_page_info_to_body(html2bbcode($orig_body));
                $item["created"] = $orig_created;
                $item["uri"] = $orig_uri;
                $item["plink"] = $orig_link;
                //}
                $item["verb"] = $xpath->query('activity:verb/text()', $activityobjects)->item(0)->nodeValue;
                $item["object-type"] = $xpath->query('activity:object/activity:object-type/text()', $activityobjects)->item(0)->nodeValue;
                if (!isset($item["object-type"])) {
                    $item["object-type"] = $xpath->query('activity:object-type/text()', $activityobjects)->item(0)->nodeValue;
                }
            }
        }
        //if ($enclosure != "")
        //	$item["body"] .= add_page_info($enclosure);
        if (isset($item["parent-uri"])) {
            $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s'", intval($importer["uid"]), dbesc($item["parent-uri"]));
            if (!$r and $related != "") {
                $reply_path = str_replace("/notice/", "/api/statuses/show/", $related) . ".atom";
                if ($reply_path != $related) {
                    logger("Fetching related items for user " . $importer["uid"] . " from " . $reply_path, LOGGER_DEBUG);
                    $reply_xml = fetch_url($reply_path);
                    $reply_contact = $contact;
                    ostatus_import($reply_xml, $importer, $reply_contact, $reply_hub);
                    // After the import try to fetch the parent item again
                    $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s'", intval($importer["uid"]), dbesc($item["parent-uri"]));
                }
            }
            if ($r) {
                $item["type"] = 'remote-comment';
                $item["gravity"] = GRAVITY_COMMENT;
            }
        } else {
            $item["parent-uri"] = $item["uri"];
        }
        $item_id = ostatus_completion($conversation, $importer["uid"], $item);
        if (!$item_id) {
            logger("Error storing item", LOGGER_DEBUG);
            continue;
        }
        logger("Item was stored with id " . $item_id, LOGGER_DEBUG);
        $item["id"] = $item_id;
        if ($mention) {
            $u = q("SELECT `notify-flags`, `language`, `username`, `email` FROM user WHERE uid = %d LIMIT 1", intval($item['uid']));
            $r = q("SELECT `parent` FROM `item` WHERE `id` = %d", intval($item_id));
            notification(array('type' => NOTIFY_TAGSELF, 'notify_flags' => $u[0]["notify-flags"], 'language' => $u[0]["language"], 'to_name' => $u[0]["username"], 'to_email' => $u[0]["email"], 'uid' => $item["uid"], 'item' => $item, 'link' => $a->get_baseurl() . '/display/' . urlencode(get_item_guid($item_id)), 'source_name' => $item["author-name"], 'source_link' => $item["author-link"], 'source_photo' => $item["author-avatar"], 'verb' => ACTIVITY_TAG, 'otype' => 'item', 'parent' => $r[0]["parent"]));
        }
    }
}