Exemple #1
0
function items_fetch($arr, $channel = null, $observer_hash = null, $client_mode = CLIENT_MODE_NORMAL, $module = 'network')
{
    $result = array('success' => false);
    $a = get_app();
    $sql_extra = '';
    $sql_nets = '';
    $sql_options = '';
    $sql_extra2 = '';
    $sql_extra3 = '';
    $def_acl = '';
    $item_uids = ' true ';
    if ($arr['uid']) {
        $uid = $arr['uid'];
    }
    if ($channel) {
        $uid = $channel['channel_id'];
        $uidhash = $channel['channel_hash'];
        $item_uids = " item.uid = " . intval($uid) . " ";
    }
    if ($arr['star']) {
        $sql_options .= " and (item_flags & " . intval(ITEM_STARRED) . ")>0 ";
    }
    if ($arr['wall']) {
        $sql_options .= " and (item_flags & " . intval(ITEM_WALL) . ")>0 ";
    }
    $sql_extra = " AND item.parent IN ( SELECT parent FROM item WHERE (item_flags & " . intval(ITEM_THREAD_TOP) . ")>0 {$sql_options} ) ";
    if ($arr['since_id']) {
        $sql_extra .= " and item.id > " . $since_id . " ";
    }
    if ($arr['gid'] && $uid) {
        $r = q("SELECT * FROM `groups` WHERE id = %d AND uid = %d LIMIT 1", intval($arr['group']), intval($uid));
        if (!$r) {
            $result['message'] = t('Collection not found.');
            return $result;
        }
        $contact_str = '';
        /** @FIXME $group is undefined */
        $contacts = group_get_members($group);
        if ($contacts) {
            foreach ($contacts as $c) {
                if ($contact_str) {
                    $contact_str .= ',';
                }
                $contact_str .= "'" . $c['xchan'] . "'";
            }
        } else {
            $contact_str = ' 0 ';
            $result['message'] = t('Collection is empty.');
            return $result;
        }
        $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true {$sql_options} AND (( author_xchan IN ( {$contact_str} ) OR owner_xchan in ( {$contact_str})) or allow_gid like '" . protect_sprintf('%<' . dbesc($r[0]['hash']) . '>%') . "' ) and id = parent and item_restrict = 0 ) ";
        $x = group_rec_byhash($uid, $r[0]['hash']);
        $result['headline'] = sprintf(t('Collection: %s'), $x['name']);
    } elseif ($arr['cid'] && $uid) {
        $r = q("SELECT abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_channel = %d and not ( abook_flags & " . intval(ABOOK_FLAG_BLOCKED) . ")>0 limit 1", intval($arr['cid']), intval(local_channel()));
        if ($r) {
            $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true {$sql_options} AND uid = " . intval($arr['uid']) . " AND ( author_xchan = '" . dbesc($r[0]['abook_xchan']) . "' or owner_xchan = '" . dbesc($r[0]['abook_xchan']) . "' ) and item_restrict = 0 ) ";
            $result['headline'] = sprintf(t('Connection: %s'), $r[0]['xchan_name']);
        } else {
            $result['message'] = t('Connection not found.');
            return $result;
        }
    }
    if ($arr['datequery']) {
        $sql_extra3 .= protect_sprintf(sprintf(" AND item.created <= '%s' ", dbesc(datetime_convert(date_default_timezone_get(), '', $arr['datequery']))));
    }
    if ($arr['datequery2']) {
        $sql_extra3 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert(date_default_timezone_get(), '', $arr['datequery2']))));
    }
    if (!array_key_exists('nouveau', $arr)) {
        $sql_extra2 = " AND item.parent = item.id ";
        $sql_extra3 = '';
    }
    if ($arr['search']) {
        if (strpos($arr['search'], '#') === 0) {
            $sql_extra .= term_query('item', substr($arr['search'], 1), TERM_HASHTAG);
        } else {
            $sql_extra .= sprintf(" AND item.body like '%s' ", dbesc(protect_sprintf('%' . $arr['search'] . '%')));
        }
    }
    if (strlen($arr['file'])) {
        $sql_extra .= term_query('item', $arr['files'], TERM_FILE);
    }
    if ($arr['conv'] && $channel) {
        $sql_extra .= sprintf(" AND parent IN (SELECT distinct parent from item where ( author_xchan like '%s' or ( item_flags & %d )>0)) ", dbesc(protect_sprintf($uidhash)), intval(ITEM_MENTIONSME));
    }
    if ($client_mode & CLIENT_MODE_UPDATE && !($client_mode & CLIENT_MODE_LOAD)) {
        // only setup pagination on initial page view
        $pager_sql = '';
    } else {
        $itemspage = $channel ? get_pconfig($uid, 'system', 'itemspage') : 20;
        $a->set_pager_itemspage(intval($itemspage) ? $itemspage : 20);
        $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(get_app()->pager['itemspage']), intval(get_app()->pager['start']));
    }
    if (isset($arr['start']) && isset($arr['records'])) {
        $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval($arr['records']), intval($arr['start']));
    }
    if (array_key_exists('cmin', $arr) || array_key_exists('cmax', $arr)) {
        if ($arr['cmin'] != 0 || $arr['cmax'] != 99) {
            // Not everybody who shows up in the network stream will be in your address book.
            // By default those that aren't are assumed to have closeness = 99; but this isn't
            // recorded anywhere. So if cmax is 99, we'll open the search up to anybody in
            // the stream with a NULL address book entry.
            $sql_nets .= " AND ";
            if ($arr['cmax'] == 99) {
                $sql_nets .= " ( ";
            }
            $sql_nets .= "( abook.abook_closeness >= " . intval($arr['cmin']) . " ";
            $sql_nets .= " AND abook.abook_closeness <= " . intval($arr['cmax']) . " ) ";
            /** @fixme dead code, $cmax is undefined */
            if ($cmax == 99) {
                $sql_nets .= " OR abook.abook_closeness IS NULL ) ";
            }
        }
    }
    $simple_update = $client_mode & CLIENT_MODE_UPDATE ? " and ( item.item_unseen = 1 ) " : '';
    if ($client_mode & CLIENT_MODE_LOAD) {
        $simple_update = '';
    }
    //$start = dba_timer();
    require_once 'include/security.php';
    $sql_extra .= item_permissions_sql($channel['channel_id'], $observer_hash);
    if ($arr['pages']) {
        $item_restrict = " AND (item_restrict & " . ITEM_WEBPAGE . ") ";
    } else {
        $item_restrict = " AND item_restrict = 0 ";
    }
    if ($arr['nouveau'] && $client_mode & CLIENT_MODE_LOAD && $channel) {
        // "New Item View" - show all items unthreaded in reverse created date order
        $items = q("SELECT item.*, item.id AS item_id FROM item\n\t\t\t\tWHERE {$item_uids} {$item_restrict}\n\t\t\t\t{$simple_update}\n\t\t\t\t{$sql_extra} {$sql_nets}\n\t\t\t\tORDER BY item.received DESC {$pager_sql}");
        require_once 'include/items.php';
        xchan_query($items);
        $items = fetch_post_tags($items, true);
    } else {
        // Normal conversation view
        if ($arr['order'] === 'post') {
            $ordering = "created";
        } else {
            $ordering = "commented";
        }
        if ($client_mode & CLIENT_MODE_LOAD || $client_mode == CLIENT_MODE_NORMAL) {
            // Fetch a page full of parent items for this page
            $r = q("SELECT distinct item.id AS item_id, item.{$ordering} FROM item\n                left join abook on item.author_xchan = abook.abook_xchan\n                WHERE {$item_uids} {$item_restrict}\n                AND item.parent = item.id\n                and ((abook.abook_flags & %d) = 0 or abook.abook_flags is null)\n                {$sql_extra3} {$sql_extra} {$sql_nets}\n                ORDER BY item.{$ordering} DESC {$pager_sql} ", intval(ABOOK_FLAG_BLOCKED));
        } else {
            // update
            $r = q("SELECT item.parent AS item_id FROM item\n                left join abook on item.author_xchan = abook.abook_xchan\n                WHERE {$item_uids} {$item_restrict} {$simple_update}\n                and ((abook.abook_flags & %d) = 0 or abook.abook_flags is null)\n                {$sql_extra3} {$sql_extra} {$sql_nets} ", intval(ABOOK_FLAG_BLOCKED));
        }
        //$first = dba_timer();
        // Then fetch all the children of the parents that are on this page
        if ($r) {
            $parents_str = ids_to_querystr($r, 'item_id');
            if ($arr['top']) {
                $sql_extra = ' and id = parent ' . $sql_extra;
            }
            $items = q("SELECT item.*, item.id AS item_id FROM item\n\t\t\t\tWHERE {$item_uids} {$item_restrict}\n\t\t\t\tAND item.parent IN ( %s )\n\t\t\t\t{$sql_extra} ", dbesc($parents_str));
            //$second = dba_timer();
            xchan_query($items);
            //$third = dba_timer();
            $items = fetch_post_tags($items, true);
            //$fourth = dba_timer();
            require_once 'include/conversation.php';
            $items = conv_sort($items, $ordering);
            //logger('items: ' . print_r($items,true));
        } else {
            $items = array();
        }
        if ($parents_str && $arr['mark_seen']) {
            $update_unseen = ' AND parent IN ( ' . dbesc($parents_str) . ' )';
        }
        /** @FIXME finish mark unseen sql */
    }
    return $items;
}
Exemple #2
0
function connections_post(&$a)
{
    if (!local_channel()) {
        return;
    }
    $contact_id = intval(argv(1));
    if (!$contact_id) {
        return;
    }
    $orig_record = q("SELECT * FROM abook WHERE abook_id = %d AND abook_channel = %d LIMIT 1", intval($contact_id), intval(local_channel()));
    if (!$orig_record) {
        notice(t('Could not access contact record.') . EOL);
        goaway(z_root() . '/connections');
        return;
        // NOTREACHED
    }
    call_hooks('contact_edit_post', $_POST);
    $profile_id = $_POST['profile_assign'];
    if ($profile_id) {
        $r = q("SELECT profile_guid FROM profile WHERE profile_guid = '%s' AND `uid` = %d LIMIT 1", dbesc($profile_id), intval(local_channel()));
        if (!count($r)) {
            notice(t('Could not locate selected profile.') . EOL);
            return;
        }
    }
    $hidden = intval($_POST['hidden']);
    $priority = intval($_POST['poll']);
    if ($priority > 5 || $priority < 0) {
        $priority = 0;
    }
    $closeness = intval($_POST['closeness']);
    if ($closeness < 0) {
        $closeness = 99;
    }
    $abook_my_perms = 0;
    foreach ($_POST as $k => $v) {
        if (strpos($k, 'perms_') === 0) {
            $abook_my_perms += $v;
        }
    }
    $abook_flags = $orig_record[0]['abook_flags'];
    $new_friend = false;
    if ($_REQUEST['pending'] && $abook_flags & ABOOK_FLAG_PENDING) {
        $abook_flags = $abook_flags ^ ABOOK_FLAG_PENDING;
        $new_friend = true;
    }
    $r = q("UPDATE abook SET abook_profile = '%s', abook_my_perms = %d , abook_closeness = %d, abook_flags = %d\n\t\twhere abook_id = %d AND abook_channel = %d", dbesc($profile_id), intval($abook_my_perms), intval($closeness), intval($abook_flags), intval($contact_id), intval(local_channel()));
    if ($r) {
        info(t('Connection updated.') . EOL);
    } else {
        notice(t('Failed to update connection record.') . EOL);
    }
    if (x($a->data, 'abook') && $a->data['abook']['abook_my_perms'] != $abook_my_perms && !($a->data['abook']['abook_flags'] & ABOOK_FLAG_SELF)) {
        proc_run('php', 'include/notifier.php', 'permission_update', $contact_id);
    }
    if ($new_friend) {
        $channel = $a->get_channel();
        $default_group = $channel['channel_default_group'];
        if ($default_group) {
            require_once 'include/group.php';
            $g = group_rec_byhash(local_channel(), $default_group);
            if ($g) {
                group_add_member(local_channel(), '', $a->data['abook_xchan'], $g['id']);
            }
        }
        // Check if settings permit ("post new friend activity" is allowed, and
        // friends in general or this friend in particular aren't hidden)
        // and send out a new friend activity
        // TODO
        // pull in a bit of content if there is any to pull in
        proc_run('php', 'include/onepoll.php', $contact_id);
    }
    // Refresh the structure in memory with the new data
    $r = q("SELECT abook.*, xchan.* \n\t\tFROM abook left join xchan on abook_xchan = xchan_hash\n\t\tWHERE abook_channel = %d and abook_id = %d LIMIT 1", intval(local_channel()), intval($contact_id));
    if ($r) {
        $a->data['abook'] = $r[0];
    }
    if ($new_friend) {
        $arr = array('channel_id' => local_channel(), 'abook' => $a->data['abook']);
        call_hooks('accept_follow', $arr);
    }
    connections_clone($a);
    return;
}
Exemple #3
0
function new_contact($uid, $url, $channel, $interactive = false, $confirm = false)
{
    $result = array('success' => false, 'message' => '');
    $is_red = false;
    $is_http = strpos($url, '://') !== false ? true : false;
    if ($is_http && substr($url, -1, 1) === '/') {
        $url = substr($url, 0, -1);
    }
    if (!allowed_url($url)) {
        $result['message'] = t('Channel is blocked on this site.');
        return $result;
    }
    if (!$url) {
        $result['message'] = t('Channel location missing.');
        return $result;
    }
    // check service class limits
    $r = q("select count(*) as total from abook where abook_channel = %d and abook_self = 0 ", intval($uid));
    if ($r) {
        $total_channels = $r[0]['total'];
    }
    if (!service_class_allows($uid, 'total_channels', $total_channels)) {
        $result['message'] = upgrade_message();
        return $result;
    }
    $arr = array('url' => $url, 'channel' => array());
    call_hooks('follow', $arr);
    if ($arr['channel']['success']) {
        $ret = $arr['channel'];
    } elseif (!$is_http) {
        $ret = Zotlabs\Zot\Finger::run($url, $channel);
    }
    if ($ret && is_array($ret) && $ret['success']) {
        $is_red = true;
        $j = $ret;
    }
    $my_perms = get_channel_default_perms($uid);
    $role = get_pconfig($uid, 'system', 'permissions_role');
    if ($role) {
        $x = \Zotlabs\Access\PermissionRoles::role_perms($role);
        if ($x['perms_connect']) {
            $my_perms = $x['perms_connect'];
        }
    }
    if ($is_red && $j) {
        logger('follow: ' . $url . ' ' . print_r($j, true), LOGGER_DEBUG);
        if (!($j['success'] && $j['guid'])) {
            $result['message'] = t('Response from remote channel was incomplete.');
            logger('mod_follow: ' . $result['message']);
            return $result;
        }
        // Premium channel, set confirm before callback to avoid recursion
        if (array_key_exists('connect_url', $j) && $interactive && !$confirm) {
            goaway(zid($j['connect_url']));
        }
        // do we have an xchan and hubloc?
        // If not, create them.
        $x = import_xchan($j);
        if (array_key_exists('deleted', $j) && intval($j['deleted'])) {
            $result['message'] = t('Channel was deleted and no longer exists.');
            return $result;
        }
        if (!$x['success']) {
            return $x;
        }
        $xchan_hash = $x['hash'];
        if (array_key_exists('permissions', $j) && array_key_exists('data', $j['permissions'])) {
            $permissions = crypto_unencapsulate(array('data' => $j['permissions']['data'], 'key' => $j['permissions']['key'], 'iv' => $j['permissions']['iv']), $channel['channel_prvkey']);
            if ($permissions) {
                $permissions = json_decode($permissions, true);
            }
            logger('decrypted permissions: ' . print_r($permissions, true), LOGGER_DATA);
        } else {
            $permissions = $j['permissions'];
        }
        if (is_array($permissions) && $permissions) {
            foreach ($permissions as $k => $v) {
                set_abconfig($channel['channel_uid'], $xchan_hash, 'their_perms', $k, intval($v));
            }
        }
    } else {
        $xchan_hash = '';
        $r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' limit 1", dbesc($url), dbesc($url));
        if (!$r) {
            // attempt network auto-discovery
            $d = discover_by_webbie($url);
            if (!$d && $is_http) {
                // try RSS discovery
                if (get_config('system', 'feed_contacts')) {
                    $d = discover_by_url($url);
                } else {
                    $result['message'] = t('Protocol disabled.');
                    return $result;
                }
            }
            if ($d) {
                $r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' limit 1", dbesc($url), dbesc($url));
            }
        }
        // if discovery was a success we should have an xchan record in $r
        if ($r) {
            $xchan = $r[0];
            $xchan_hash = $r[0]['xchan_hash'];
            $their_perms = 0;
        }
    }
    if (!$xchan_hash) {
        $result['message'] = t('Channel discovery failed.');
        logger('follow: ' . $result['message']);
        return $result;
    }
    $allowed = $is_red || $r[0]['xchan_network'] === 'rss' ? 1 : 0;
    $x = array('channel_id' => $uid, 'follow_address' => $url, 'xchan' => $r[0], 'allowed' => $allowed, 'singleton' => 0);
    call_hooks('follow_allow', $x);
    if (!$x['allowed']) {
        $result['message'] = t('Protocol disabled.');
        return $result;
    }
    $singleton = intval($x['singleton']);
    $aid = $channel['channel_account_id'];
    $hash = get_observer_hash();
    $default_group = $channel['channel_default_group'];
    if ($xchan['xchan_network'] === 'rss') {
        // check service class feed limits
        $r = q("select count(*) as total from abook where abook_account = %d and abook_feed = 1 ", intval($aid));
        if ($r) {
            $total_feeds = $r[0]['total'];
        }
        if (!service_class_allows($uid, 'total_feeds', $total_feeds)) {
            $result['message'] = upgrade_message();
            return $result;
        }
    }
    if ($hash == $xchan_hash) {
        $result['message'] = t('Cannot connect to yourself.');
        return $result;
    }
    $r = q("select abook_xchan, abook_instance from abook where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($xchan_hash), intval($uid));
    if ($is_http) {
        // Always set these "remote" permissions for feeds since we cannot interact with them
        // to negotiate a suitable permission response
        set_abconfig($uid, $xchan_hash, 'their_perms', 'view_stream', 1);
        set_abconfig($uid, $xchan_hash, 'their_perms', 'republish', 1);
    }
    if ($r) {
        $abook_instance = $r[0]['abook_instance'];
        if ($singleton && strpos($abook_instance, z_root()) === false) {
            if ($abook_instance) {
                $abook_instance .= ',';
            }
            $abook_instance .= z_root();
        }
        $x = q("update abook set abook_instance = '%s' where abook_id = %d", dbesc($abook_instance), intval($r[0]['abook_id']));
    } else {
        $closeness = get_pconfig($uid, 'system', 'new_abook_closeness');
        if ($closeness === false) {
            $closeness = 80;
        }
        $r = q("insert into abook ( abook_account, abook_channel, abook_closeness, abook_xchan, abook_feed, abook_created, abook_updated, abook_instance )\n\t\t\tvalues( %d, %d, %d, '%s', %d, '%s', '%s', '%s' ) ", intval($aid), intval($uid), intval($closeness), dbesc($xchan_hash), intval($is_http ? 1 : 0), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($singleton ? z_root() : ''));
    }
    if (!$r) {
        logger('mod_follow: abook creation failed');
    }
    $all_perms = \Zotlabs\Access\Permissions::Perms();
    if ($all_perms) {
        foreach ($all_perms as $k => $v) {
            if (in_array($k, $my_perms)) {
                set_abconfig($uid, $xchan_hash, 'my_perms', $k, 1);
            } else {
                set_abconfig($uid, $xchan_hash, 'my_perms', $k, 0);
            }
        }
    }
    $r = q("select abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash \n\t\twhere abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($xchan_hash), intval($uid));
    if ($r) {
        $result['abook'] = $r[0];
        Zotlabs\Daemon\Master::Summon(array('Notifier', 'permission_create', $result['abook']['abook_id']));
    }
    $arr = array('channel_id' => $uid, 'channel' => $channel, 'abook' => $result['abook']);
    call_hooks('follow', $arr);
    /** If there is a default group for this channel, add this connection to it */
    if ($default_group) {
        require_once 'include/group.php';
        $g = group_rec_byhash($uid, $default_group);
        if ($g) {
            group_add_member($uid, '', $xchan_hash, $g['id']);
        }
    }
    $result['success'] = true;
    return $result;
}
Exemple #4
0
function diaspora_request($importer, $xml)
{
    $a = get_app();
    $sender_handle = unxmlify($xml->sender_handle);
    $recipient_handle = unxmlify($xml->recipient_handle);
    if (!$sender_handle || !$recipient_handle) {
        return;
    }
    // Do we already have an abook record?
    $contact = diaspora_get_contact_by_handle($importer['channel_id'], $sender_handle);
    if ($contact && $contact['abook_id']) {
        // perhaps we were already sharing with this person. Now they're sharing with us.
        // That makes us friends. Maybe.
        // Please note some of these permissions such as PERMS_R_PAGES are impossible for Disapora.
        // They cannot authenticate to our system.
        $newperms = PERMS_R_STREAM | PERMS_R_PROFILE | PERMS_R_PHOTOS | PERMS_R_ABOOK | PERMS_W_STREAM | PERMS_W_COMMENT | PERMS_W_MAIL | PERMS_W_CHAT | PERMS_R_STORAGE | PERMS_R_PAGES;
        $r = q("update abook set abook_their_perms = %d where abook_id = %d and abook_channel = %d limit 1", intval($newperms), intval($contact['abook_id']), intval($importer['channel_id']));
        return;
    }
    $ret = find_diaspora_person_by_handle($sender_handle);
    if (!$ret || !strstr($ret['xchan_network'], 'diaspora')) {
        logger('diaspora_request: Cannot resolve diaspora handle ' . $sender_handle . ' for ' . $recipient_handle);
        return;
    }
    $default_perms = 0;
    // look for default permissions to apply in return - e.g. auto-friend
    $z = q("select * from abook where abook_channel = %d and (abook_flags & %d) limit 1", intval($importer['channel_id']), intval(ABOOK_FLAG_SELF));
    if ($z) {
        $default_perms = intval($z[0]['abook_my_perms']);
    }
    $their_perms = PERMS_R_STREAM | PERMS_R_PROFILE | PERMS_R_PHOTOS | PERMS_R_ABOOK | PERMS_W_STREAM | PERMS_W_COMMENT | PERMS_W_MAIL | PERMS_W_CHAT | PERMS_R_STORAGE | PERMS_R_PAGES;
    $r = q("insert into abook ( abook_account, abook_channel, abook_xchan, abook_my_perms, abook_their_perms, abook_closeness, abook_rating, abook_created, abook_updated, abook_connected, abook_dob, abook_flags) values ( %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', %d )", intval($importer['channel_account_id']), intval($importer['channel_id']), dbesc($ret['xchan_hash']), intval($default_perms), intval($their_perms), intval(99), intval(0), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(NULL_DATE), intval($default_perms ? 0 : ABOOK_FLAG_PENDING));
    if ($r) {
        logger("New Diaspora introduction received for {$importer['channel_name']}");
        $new_connection = q("select * from abook left join xchan on abook_xchan = xchan_hash left join hubloc on hubloc_hash = xchan_hash where abook_channel = %d and abook_xchan = '%s' order by abook_created desc limit 1", intval($importer['channel_id']), dbesc($ret['xchan_hash']));
        if ($new_connection) {
            require_once 'include/enotify.php';
            notification(array('type' => NOTIFY_INTRO, 'from_xchan' => $ret['xchan_hash'], 'to_xchan' => $importer['channel_hash'], 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id']));
            if ($default_perms) {
                // Send back a sharing notification to them
                diaspora_share($importer, $new_connection[0]);
            }
        }
    }
    // find the abook record we just created
    $contact_record = diaspora_get_contact_by_handle($importer['channel_id'], $sender_handle);
    if (!$contact_record) {
        logger('diaspora_request: unable to locate newly created contact record.');
        return;
    }
    /** If there is a default group for this channel, add this member to it */
    if ($importer['channel_default_group']) {
        require_once 'include/group.php';
        $g = group_rec_byhash($importer['channel_id'], $importer['channel_default_group']);
        if ($g) {
            group_add_member($importer['channel_id'], '', $contact_record['xchan_hash'], $g['id']);
        }
    }
    return;
}
 function get($update = 0, $load = false)
 {
     if (!local_channel()) {
         $_SESSION['return_url'] = \App::$query_string;
         return login(false);
     }
     if ($load) {
         $_SESSION['loadtime'] = datetime_convert();
     }
     $arr = array('query' => \App::$query_string);
     call_hooks('network_content_init', $arr);
     $channel = \App::get_channel();
     $item_normal = item_normal();
     $datequery = $datequery2 = '';
     $group = 0;
     $nouveau = false;
     $datequery = x($_GET, 'dend') && is_a_date_arg($_GET['dend']) ? notags($_GET['dend']) : '';
     $datequery2 = x($_GET, 'dbegin') && is_a_date_arg($_GET['dbegin']) ? notags($_GET['dbegin']) : '';
     $nouveau = x($_GET, 'new') ? intval($_GET['new']) : 0;
     $gid = x($_GET, 'gid') ? intval($_GET['gid']) : 0;
     $category = x($_REQUEST, 'cat') ? $_REQUEST['cat'] : '';
     $hashtags = x($_REQUEST, 'tag') ? $_REQUEST['tag'] : '';
     $verb = x($_REQUEST, 'verb') ? $_REQUEST['verb'] : '';
     $search = $_GET['search'] ? $_GET['search'] : '';
     if ($search) {
         if (strpos($search, '@') === 0) {
             $r = q("select abook_id from abook left join xchan on abook_xchan = xchan_hash where xchan_name = '%s' and abook_channel = %d limit 1", dbesc(substr($search, 1)), intval(local_channel()));
             if ($r) {
                 $_GET['cid'] = $r[0]['abook_id'];
                 $search = $_GET['search'] = '';
             }
         } elseif (strpos($search, '#') === 0) {
             $hashtags = substr($search, 1);
             $search = $_GET['search'] = '';
         }
     }
     if ($datequery) {
         $_GET['order'] = 'post';
     }
     // filter by collection (e.g. group)
     if ($gid) {
         $r = q("SELECT * FROM groups WHERE id = %d AND uid = %d LIMIT 1", intval($gid), intval(local_channel()));
         if (!$r) {
             if ($update) {
                 killme();
             }
             notice(t('No such group') . EOL);
             goaway(z_root() . '/network');
             // NOTREACHED
         }
         $group = $gid;
         $group_hash = $r[0]['hash'];
         $def_acl = array('allow_gid' => '<' . $r[0]['hash'] . '>');
     }
     $o = '';
     // if no tabs are selected, defaults to comments
     $cid = x($_GET, 'cid') ? intval($_GET['cid']) : 0;
     $star = x($_GET, 'star') ? intval($_GET['star']) : 0;
     $order = x($_GET, 'order') ? notags($_GET['order']) : 'comment';
     $liked = x($_GET, 'liked') ? intval($_GET['liked']) : 0;
     $conv = x($_GET, 'conv') ? intval($_GET['conv']) : 0;
     $spam = x($_GET, 'spam') ? intval($_GET['spam']) : 0;
     $cmin = x($_GET, 'cmin') ? intval($_GET['cmin']) : 0;
     $cmax = x($_GET, 'cmax') ? intval($_GET['cmax']) : 99;
     $firehose = x($_GET, 'fh') ? intval($_GET['fh']) : 0;
     $file = x($_GET, 'file') ? $_GET['file'] : '';
     $deftag = '';
     if (x($_GET, 'search') || x($_GET, 'file')) {
         $nouveau = true;
     }
     if ($cid) {
         $r = q("SELECT abook_xchan FROM abook WHERE abook_id = %d AND abook_channel = %d LIMIT 1", intval($cid), intval(local_channel()));
         if (!$r) {
             if ($update) {
                 killme();
             }
             notice(t('No such channel') . EOL);
             goaway(z_root() . '/network');
             // NOTREACHED
         }
         if ($_GET['pf'] === '1') {
             $deftag = '@' . t('forum') . '+' . intval($cid) . '+';
         } else {
             $def_acl = array('allow_cid' => '<' . $r[0]['abook_xchan'] . '>');
         }
     }
     if (!$update) {
         $tabs = network_tabs();
         $o .= $tabs;
         // search terms header
         if ($search) {
             $o .= replace_macros(get_markup_template("section_title.tpl"), array('$title' => t('Search Results For:') . ' ' . htmlspecialchars($search, ENT_COMPAT, 'UTF-8')));
         }
         nav_set_selected('network');
         $channel_acl = array('allow_cid' => $channel['channel_allow_cid'], 'allow_gid' => $channel['channel_allow_gid'], 'deny_cid' => $channel['channel_deny_cid'], 'deny_gid' => $channel['channel_deny_gid']);
         $private_editing = ($group || $cid) && !intval($_GET['pf']) ? true : false;
         $x = array('is_owner' => true, 'allow_location' => intval(get_pconfig($channel['channel_id'], 'system', 'use_browser_location')) ? '1' : '', 'default_location' => $channel['channel_location'], 'nickname' => $channel['channel_address'], 'lockstate' => $private_editing || $channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid'] ? 'lock' : 'unlock', 'acl' => populate_acl($private_editing ? $def_acl : $channel_acl, true, \PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'), 'bang' => $private_editing ? '!' : '', 'visitor' => true, 'profile_uid' => local_channel(), 'editor_autocomplete' => true, 'bbco_autocomplete' => 'bbcode', 'bbcode' => true);
         if ($deftag) {
             $x['pretext'] = $deftag;
         }
         $status_editor = status_editor($a, $x);
         $o .= $status_editor;
     }
     // We don't have to deal with ACL's on this page. You're looking at everything
     // that belongs to you, hence you can see all of it. We will filter by group if
     // desired.
     $sql_options = $star ? " and item_starred = 1 " : '';
     $sql_nets = '';
     $sql_extra = " AND `item`.`parent` IN ( SELECT `parent` FROM `item` WHERE item_thread_top = 1 {$sql_options} ) ";
     if ($group) {
         $contact_str = '';
         $contacts = group_get_members($group);
         if ($contacts) {
             foreach ($contacts as $c) {
                 if ($contact_str) {
                     $contact_str .= ',';
                 }
                 $contact_str .= "'" . $c['xchan'] . "'";
             }
         } else {
             $contact_str = ' 0 ';
             info(t('Privacy group is empty'));
         }
         $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true {$sql_options} AND (( author_xchan IN ( {$contact_str} ) OR owner_xchan in ( {$contact_str} )) or allow_gid like '" . protect_sprintf('%<' . dbesc($group_hash) . '>%') . "' ) and id = parent {$item_normal} ) ";
         $x = group_rec_byhash(local_channel(), $group_hash);
         if ($x) {
             $title = replace_macros(get_markup_template("section_title.tpl"), array('$title' => t('Privacy group: ') . $x['name']));
         }
         $o = $tabs;
         $o .= $title;
         $o .= $status_editor;
     } elseif ($cid) {
         $r = q("SELECT abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_channel = %d and abook_blocked = 0 limit 1", intval($cid), intval(local_channel()));
         if ($r) {
             $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true {$sql_options} AND uid = " . intval(local_channel()) . " AND ( author_xchan = '" . dbesc($r[0]['abook_xchan']) . "' or owner_xchan = '" . dbesc($r[0]['abook_xchan']) . "' ) {$item_normal} ) ";
             $title = replace_macros(get_markup_template("section_title.tpl"), array('$title' => '<a href="' . zid($r[0]['xchan_url']) . '" ><img src="' . zid($r[0]['xchan_photo_s']) . '" alt="' . urlencode($r[0]['xchan_name']) . '" /></a> <a href="' . zid($r[0]['xchan_url']) . '" >' . $r[0]['xchan_name'] . '</a>'));
             $o = $tabs;
             $o .= $title;
             $o .= $status_editor;
         } else {
             notice(t('Invalid connection.') . EOL);
             goaway(z_root() . '/network');
         }
     }
     if (x($category)) {
         $sql_extra .= protect_sprintf(term_query('item', $category, TERM_CATEGORY));
     }
     if (x($hashtags)) {
         $sql_extra .= protect_sprintf(term_query('item', $hashtags, TERM_HASHTAG, TERM_COMMUNITYTAG));
     }
     if (!$update) {
         // The special div is needed for liveUpdate to kick in for this page.
         // We only launch liveUpdate if you aren't filtering in some incompatible
         // way and also you aren't writing a comment (discovered in javascript).
         if ($gid || $cid || $cmin || $cmax != 99 || $star || $liked || $conv || $spam || $nouveau || $list) {
             $firehose = 0;
         }
         $maxheight = get_pconfig(local_channel(), 'system', 'network_divmore_height');
         if (!$maxheight) {
             $maxheight = 400;
         }
         $o .= '<div id="live-network"></div>' . "\r\n";
         $o .= "<script> var profile_uid = " . local_channel() . "; var profile_page = " . \App::$pager['page'] . "; divmore_height = " . intval($maxheight) . "; </script>\r\n";
         \App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"), array('$baseurl' => z_root(), '$pgtype' => 'network', '$uid' => local_channel() ? local_channel() : '0', '$gid' => $gid ? $gid : '0', '$cid' => $cid ? $cid : '0', '$cmin' => $cmin ? $cmin : '0', '$cmax' => $cmax ? $cmax : '0', '$star' => $star ? $star : '0', '$liked' => $liked ? $liked : '0', '$conv' => $conv ? $conv : '0', '$spam' => $spam ? $spam : '0', '$fh' => $firehose ? $firehose : '0', '$nouveau' => $nouveau ? $nouveau : '0', '$wall' => '0', '$list' => x($_REQUEST, 'list') ? intval($_REQUEST['list']) : 0, '$page' => \App::$pager['page'] != 1 ? \App::$pager['page'] : 1, '$search' => $search ? $search : '', '$order' => $order, '$file' => $file, '$cats' => $category, '$tags' => $hashtags, '$dend' => $datequery, '$mid' => '', '$verb' => $verb, '$dbegin' => $datequery2));
     }
     $sql_extra3 = '';
     if ($datequery) {
         $sql_extra3 .= protect_sprintf(sprintf(" AND item.created <= '%s' ", dbesc(datetime_convert(date_default_timezone_get(), '', $datequery))));
     }
     if ($datequery2) {
         $sql_extra3 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert(date_default_timezone_get(), '', $datequery2))));
     }
     $sql_extra2 = $nouveau ? '' : " AND item.parent = item.id ";
     $sql_extra3 = $nouveau ? '' : $sql_extra3;
     if (x($_GET, 'search')) {
         $search = escape_tags($_GET['search']);
         if (strpos($search, '#') === 0) {
             $sql_extra .= term_query('item', substr($search, 1), TERM_HASHTAG, TERM_COMMUNITYTAG);
         } else {
             $sql_extra .= sprintf(" AND item.body like '%s' ", dbesc(protect_sprintf('%' . $search . '%')));
         }
     }
     if ($verb) {
         $sql_extra .= sprintf(" AND item.verb like '%s' ", dbesc(protect_sprintf('%' . $verb . '%')));
     }
     if (strlen($file)) {
         $sql_extra .= term_query('item', $file, TERM_FILE);
     }
     if ($conv) {
         $sql_extra .= sprintf(" AND parent IN (SELECT distinct(parent) from item where ( author_xchan like '%s' or item_mentionsme = 1 )) ", dbesc(protect_sprintf($channel['channel_hash'])));
     }
     if ($update && !$load) {
         // only setup pagination on initial page view
         $pager_sql = '';
     } else {
         $itemspage = get_pconfig(local_channel(), 'system', 'itemspage');
         \App::set_pager_itemspage(intval($itemspage) ? $itemspage : 20);
         $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(\App::$pager['itemspage']), intval(\App::$pager['start']));
     }
     if ($cmin != 0 || $cmax != 99) {
         // Not everybody who shows up in the network stream will be in your address book.
         // By default those that aren't are assumed to have closeness = 99; but this isn't
         // recorded anywhere. So if cmax is 99, we'll open the search up to anybody in
         // the stream with a NULL address book entry.
         $sql_nets .= " AND ";
         if ($cmax == 99) {
             $sql_nets .= " ( ";
         }
         $sql_nets .= "( abook.abook_closeness >= " . intval($cmin) . " ";
         $sql_nets .= " AND abook.abook_closeness <= " . intval($cmax) . " ) ";
         if ($cmax == 99) {
             $sql_nets .= " OR abook.abook_closeness IS NULL ) ";
         }
     }
     $abook_uids = " and abook.abook_channel = " . local_channel() . " ";
     if ($firehose && !get_config('system', 'disable_discover_tab')) {
         require_once 'include/identity.php';
         $sys = get_sys_channel();
         $uids = " and item.uid  = " . intval($sys['channel_id']) . " ";
         \App::$data['firehose'] = intval($sys['channel_id']);
     } else {
         $uids = " and item.uid = " . local_channel() . " ";
     }
     if (get_pconfig(local_channel(), 'system', 'network_list_mode')) {
         $page_mode = 'list';
     } else {
         $page_mode = 'client';
     }
     $simple_update = $update ? " and item_unseen = 1 " : '';
     // This fixes a very subtle bug so I'd better explain it. You wake up in the morning or return after a day
     // or three and look at your matrix page - after opening up your browser. The first page loads just as it
     // should. All of a sudden a few seconds later, page 2 will get inserted at the beginning of the page
     // (before the page 1 content). The update code is actually doing just what it's supposed
     // to, it's fetching posts that have the ITEM_UNSEEN bit set. But the reason that page 2 content is being
     // returned in an UPDATE is because you hadn't gotten that far yet - you're still on page 1 and everything
     // that we loaded for page 1 is now marked as seen. But the stuff on page 2 hasn't been. So... it's being
     // treated as "new fresh" content because it is unseen. We need to distinguish it somehow from content
     // which "arrived as you were reading page 1". We're going to do this
     // by storing in your session the current UTC time whenever you LOAD a network page, and only UPDATE items
     // which are both ITEM_UNSEEN and have "changed" since that time. Cross fingers...
     if ($update && $_SESSION['loadtime']) {
         $simple_update = " AND (( item_unseen = 1 AND item.changed > '" . datetime_convert('UTC', 'UTC', $_SESSION['loadtime']) . "' )  OR item.changed > '" . datetime_convert('UTC', 'UTC', $_SESSION['loadtime']) . "' ) ";
     }
     if ($load) {
         $simple_update = '';
     }
     if ($nouveau && $load) {
         // "New Item View" - show all items unthreaded in reverse created date order
         $items = q("SELECT item.*, item.id AS item_id, received FROM item\n\t\t\t\tleft join abook on ( item.owner_xchan = abook.abook_xchan {$abook_uids} )\n\t\t\t\tWHERE true {$uids} {$item_normal}\n\t\t\t\tand (abook.abook_blocked = 0 or abook.abook_flags is null)\n\t\t\t\t{$simple_update}\n\t\t\t\t{$sql_extra} {$sql_nets}\n\t\t\t\tORDER BY item.received DESC {$pager_sql} ");
         require_once 'include/items.php';
         xchan_query($items);
         $items = fetch_post_tags($items, true);
     } elseif ($update) {
         // Normal conversation view
         if ($order === 'post') {
             $ordering = "created";
         } else {
             $ordering = "commented";
         }
         if ($load) {
             // Fetch a page full of parent items for this page
             $r = q("SELECT distinct item.id AS item_id, {$ordering} FROM item\n\t\t\t\t\tleft join abook on ( item.owner_xchan = abook.abook_xchan {$abook_uids} )\n\t\t\t\t\tWHERE true {$uids} {$item_normal}\n\t\t\t\t\tAND item.parent = item.id\n\t\t\t\t\tand (abook.abook_blocked = 0 or abook.abook_flags is null)\n\t\t\t\t\t{$sql_extra3} {$sql_extra} {$sql_nets}\n\t\t\t\t\tORDER BY {$ordering} DESC {$pager_sql} ");
         } else {
             // this is an update
             $r = q("SELECT item.parent AS item_id FROM item\n\t\t\t\t\tleft join abook on ( item.owner_xchan = abook.abook_xchan {$abook_uids} )\n\t\t\t\t\tWHERE true {$uids} {$item_normal} {$simple_update}\n\t\t\t\t\tand (abook.abook_blocked = 0 or abook.abook_flags is null)\n\t\t\t\t\t{$sql_extra3} {$sql_extra} {$sql_nets} ");
             $_SESSION['loadtime'] = datetime_convert();
         }
         // Then fetch all the children of the parents that are on this page
         $parents_str = '';
         $update_unseen = '';
         if ($r) {
             $parents_str = ids_to_querystr($r, 'item_id');
             $items = q("SELECT item.*, item.id AS item_id FROM item\n\t\t\t\t\tWHERE true {$uids} {$item_normal}\n\t\t\t\t\tAND item.parent IN ( %s )\n\t\t\t\t\t{$sql_extra} ", dbesc($parents_str));
             xchan_query($items, true, $firehose ? local_channel() : 0);
             $items = fetch_post_tags($items, true);
             $items = conv_sort($items, $ordering);
         } else {
             $items = array();
         }
         if ($page_mode === 'list') {
             /**
              * in "list mode", only mark the parent item and any like activities as "seen". 
              * We won't distinguish between comment likes and post likes. The important thing
              * is that the number of unseen comments will be accurate. The SQL to separate the
              * comment likes could also get somewhat hairy. 
              */
             if ($parents_str) {
                 $update_unseen = " AND ( id IN ( " . dbesc($parents_str) . " )";
                 $update_unseen .= " OR ( parent IN ( " . dbesc($parents_str) . " ) AND verb in ( '" . dbesc(ACTIVITY_LIKE) . "','" . dbesc(ACTIVITY_DISLIKE) . "' ))) ";
             }
         } else {
             if ($parents_str) {
                 $update_unseen = " AND parent IN ( " . dbesc($parents_str) . " )";
             }
         }
     }
     if ($update_unseen && !$firehose) {
         $r = q("UPDATE item SET item_unseen = 0 WHERE item_unseen = 1 AND uid = %d {$update_unseen} ", intval(local_channel()));
     }
     $mode = $nouveau ? 'network-new' : 'network';
     $o .= conversation($a, $items, $mode, $update, $page_mode);
     if ($items && !$update) {
         $o .= alt_pager($a, count($items));
     }
     return $o;
 }
Exemple #6
0
function diaspora_request($importer, $xml)
{
    $a = get_app();
    $sender_handle = unxmlify($xml->sender_handle);
    $recipient_handle = unxmlify($xml->recipient_handle);
    if (!$sender_handle || !$recipient_handle) {
        return;
    }
    // Do we already have an abook record?
    $contact = diaspora_get_contact_by_handle($importer['channel_id'], $sender_handle);
    if ($contact && $contact['abook_id']) {
        // perhaps we were already sharing with this person. Now they're sharing with us.
        // That makes us friends. Maybe.
        // Please note some of these permissions such as PERMS_R_PAGES are impossible for Disapora.
        // They cannot authenticate to our system.
        $newperms = PERMS_R_STREAM | PERMS_R_PROFILE | PERMS_R_PHOTOS | PERMS_R_ABOOK | PERMS_W_STREAM | PERMS_W_COMMENT | PERMS_W_MAIL | PERMS_W_CHAT | PERMS_R_STORAGE | PERMS_R_PAGES;
        $r = q("update abook set abook_their_perms = %d where abook_id = %d and abook_channel = %d", intval($newperms), intval($contact['abook_id']), intval($importer['channel_id']));
        return;
    }
    $ret = find_diaspora_person_by_handle($sender_handle);
    if (!$ret || !strstr($ret['xchan_network'], 'diaspora')) {
        logger('diaspora_request: Cannot resolve diaspora handle ' . $sender_handle . ' for ' . $recipient_handle);
        return;
    }
    //FIXME
    /*
    	if(feature_enabled($channel['channel_id'],'premium_channel')) {
    		$myaddr = $importer['channel_address'] . '@' .  get_app()->get_hostname();
    		$cnv = random_string();
    		$mid = random_string();
    		$msg = t('You have started sharing with a Redmatrix premium channel.');
    		$msg .= t('Redmatrix premium channels are not available for sharing with Diaspora members. This sharing request has been blocked.') . "\r";
    		$msg .= t('Please do not reply to this message, as this channel is not sharing with you and any reply will not be seen by the recipient.') . "\r";
    		$created = datetime_convert('UTC','UTC',$item['created'],'Y-m-d H:i:s \U\T\C');
    		$signed_text =  $mid . ';' . $cnv . ';' . $msg .  ';'
            . $created . ';' . $myaddr . ';' . $cnv;
    		$sig = base64_encode(rsa_sign($signed_text,$importer['channel_prvkey'],'sha256'));
    		$conv = array(
    			'guid' => xmlify($cnv),
    			'subject' => xmlify(t('Sharing request failed.')),
    			'created_at' => xmlify($created),
    			'diaspora_handle' => xmlify($myaddr),
    			'participant_handles' => xmlify($myaddr . ';' . $sender_handle)
    		);
    		$msg = array(
    			'guid' => xmlify($mid),
    			'parent_guid' => xmlify($cnv),
    			'parent_author_signature' => xmlify($sig),
    			'author_signature' => xmlify($sig),
    			'text' => xmlify($msg),
    			'created_at' => xmlify($created),
    			'diaspora_handle' => xmlify($myaddr),
    			'conversation_guid' => xmlify($cnv)
    		);
    		$conv['messages'] = array($msg);
    		$tpl = get_markup_template('diaspora_conversation.tpl');
    		$xmsg = replace_macros($tpl, array('$conv' => $conv));
    		$slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($xmsg,$importer,$ret,$importer['channel_prvkey'],$ret['xchan_pubkey'],false)));
    		diaspora_transmit($importer,$ret,$slap,false);
    		return;
    	}
    */
    // End FIXME
    $role = get_pconfig($channel['channel_id'], 'system', 'permissions_role');
    if ($role) {
        $x = get_role_perms($role);
        if ($x['perms_auto']) {
            $default_perms = $x['perms_accept'];
        }
    }
    if (!$default_perms) {
        $default_perms = intval(get_pconfig($importer['channel_id'], 'system', 'autoperms'));
    }
    $their_perms = PERMS_R_STREAM | PERMS_R_PROFILE | PERMS_R_PHOTOS | PERMS_R_ABOOK | PERMS_W_STREAM | PERMS_W_COMMENT | PERMS_W_MAIL | PERMS_W_CHAT | PERMS_R_STORAGE | PERMS_R_PAGES;
    $closeness = get_pconfig($importer['channel_id'], 'system', 'new_abook_closeness');
    if ($closeness === false) {
        $closeness = 80;
    }
    $r = q("insert into abook ( abook_account, abook_channel, abook_xchan, abook_my_perms, abook_their_perms, abook_closeness, abook_rating, abook_created, abook_updated, abook_connected, abook_dob, abook_flags) values ( %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', %d )", intval($importer['channel_account_id']), intval($importer['channel_id']), dbesc($ret['xchan_hash']), intval($default_perms), intval($their_perms), intval($closeness), intval(0), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(NULL_DATE), intval($default_perms ? 0 : ABOOK_FLAG_PENDING));
    if ($r) {
        logger("New Diaspora introduction received for {$importer['channel_name']}");
        $new_connection = q("select * from abook left join xchan on abook_xchan = xchan_hash left join hubloc on hubloc_hash = xchan_hash where abook_channel = %d and abook_xchan = '%s' order by abook_created desc limit 1", intval($importer['channel_id']), dbesc($ret['xchan_hash']));
        if ($new_connection) {
            require_once 'include/enotify.php';
            notification(array('type' => NOTIFY_INTRO, 'from_xchan' => $ret['xchan_hash'], 'to_xchan' => $importer['channel_hash'], 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id']));
            if ($default_perms) {
                // Send back a sharing notification to them
                diaspora_share($importer, $new_connection[0]);
            }
        }
    }
    // find the abook record we just created
    $contact_record = diaspora_get_contact_by_handle($importer['channel_id'], $sender_handle);
    if (!$contact_record) {
        logger('diaspora_request: unable to locate newly created contact record.');
        return;
    }
    /** If there is a default group for this channel, add this member to it */
    if ($importer['channel_default_group']) {
        require_once 'include/group.php';
        $g = group_rec_byhash($importer['channel_id'], $importer['channel_default_group']);
        if ($g) {
            group_add_member($importer['channel_id'], '', $contact_record['xchan_hash'], $g['id']);
        }
    }
    return;
}
Exemple #7
0
function new_contact($uid, $url, $channel, $interactive = false, $confirm = false)
{
    $result = array('success' => false, 'message' => '');
    $a = get_app();
    $is_red = false;
    $is_http = strpos($url, '://') !== false ? true : false;
    if ($is_http && substr($url, -1, 1) === '/') {
        $url = substr($url, 0, -1);
    }
    if (!allowed_url($url)) {
        $result['message'] = t('Channel is blocked on this site.');
        return $result;
    }
    if (!$url) {
        $result['message'] = t('Channel location missing.');
        return $result;
    }
    // check service class limits
    $r = q("select count(*) as total from abook where abook_channel = %d and abook_self = 0 ", intval($uid));
    if ($r) {
        $total_channels = $r[0]['total'];
    }
    if (!service_class_allows($uid, 'total_channels', $total_channels)) {
        $result['message'] = upgrade_message();
        return $result;
    }
    $arr = array('url' => $url, 'channel' => array());
    call_hooks('follow', $arr);
    if ($arr['channel']['success']) {
        $ret = $arr['channel'];
    } elseif (!$is_http) {
        $ret = zot_finger($url, $channel);
    }
    if ($ret && $ret['success']) {
        $is_red = true;
        $j = json_decode($ret['body'], true);
    }
    $my_perms = get_channel_default_perms($uid);
    $role = get_pconfig($uid, 'system', 'permissions_role');
    if ($role) {
        $x = get_role_perms($role);
        if ($x['perms_follow']) {
            $my_perms = $x['perms_follow'];
        }
    }
    if ($is_red && $j) {
        logger('follow: ' . $url . ' ' . print_r($j, true), LOGGER_DEBUG);
        if (!($j['success'] && $j['guid'])) {
            $result['message'] = t('Response from remote channel was incomplete.');
            logger('mod_follow: ' . $result['message']);
            return $result;
        }
        // Premium channel, set confirm before callback to avoid recursion
        if (array_key_exists('connect_url', $j) && $interactive && !$confirm) {
            goaway(zid($j['connect_url']));
        }
        // do we have an xchan and hubloc?
        // If not, create them.
        $x = import_xchan($j);
        if (array_key_exists('deleted', $j) && intval($j['deleted'])) {
            $result['message'] = t('Channel was deleted and no longer exists.');
            return $result;
        }
        if (!$x['success']) {
            return $x;
        }
        $xchan_hash = $x['hash'];
        $their_perms = 0;
        $global_perms = get_perms();
        if (array_key_exists('permissions', $j) && array_key_exists('data', $j['permissions'])) {
            $permissions = crypto_unencapsulate(array('data' => $j['permissions']['data'], 'key' => $j['permissions']['key'], 'iv' => $j['permissions']['iv']), $channel['channel_prvkey']);
            if ($permissions) {
                $permissions = json_decode($permissions, true);
            }
            logger('decrypted permissions: ' . print_r($permissions, true), LOGGER_DATA);
        } else {
            $permissions = $j['permissions'];
        }
        foreach ($permissions as $k => $v) {
            if ($v) {
                $their_perms = $their_perms | intval($global_perms[$k][1]);
            }
        }
    } else {
        $their_perms = 0;
        $xchan_hash = '';
        $r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' limit 1", dbesc($url), dbesc($url));
        if (!$r) {
            // attempt network auto-discovery
            if (strpos($url, '@') && !$is_http) {
                $r = discover_by_webbie($url);
            } elseif ($is_http) {
                $r = discover_by_url($url);
                $r['allowed'] = intval(get_config('system', 'feed_contacts'));
            }
            if ($r) {
                $r['channel_id'] = $uid;
                call_hooks('follow_allow', $r);
                if (!$r['allowed']) {
                    $result['message'] = t('Protocol disabled.');
                    return $result;
                }
                $r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' limit 1", dbesc($url), dbesc($url));
            }
        }
        if ($r) {
            $xchan_hash = $r[0]['xchan_hash'];
            $their_perms = 0;
        }
    }
    if (!$xchan_hash) {
        $result['message'] = t('Channel discovery failed.');
        logger('follow: ' . $result['message']);
        return $result;
    }
    if (local_channel() && $uid == local_channel()) {
        $aid = get_account_id();
        $hash = get_observer_hash();
        $ch = $a->get_channel();
        $default_group = $ch['channel_default_group'];
    } else {
        $r = q("select * from channel where channel_id = %d limit 1", intval($uid));
        if (!$r) {
            $result['message'] = t('local account not found.');
            return $result;
        }
        $aid = $r[0]['channel_account_id'];
        $hash = $r[0]['channel_hash'];
        $default_group = $r[0]['channel_default_group'];
    }
    if ($is_http) {
        $r = q("select count(*) as total from abook where abook_account = %d and abook_feed = 1 ", intval($aid));
        if ($r) {
            $total_feeds = $r[0]['total'];
        }
        if (!service_class_allows($uid, 'total_feeds', $total_feeds)) {
            $result['message'] = upgrade_message();
            return $result;
        }
    }
    if ($hash == $xchan_hash) {
        $result['message'] = t('Cannot connect to yourself.');
        return $result;
    }
    $r = q("select abook_xchan from abook where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($xchan_hash), intval($uid));
    if ($r) {
        $x = q("update abook set abook_their_perms = %d where abook_id = %d", intval($their_perms), intval($r[0]['abook_id']));
    } else {
        $closeness = get_pconfig($uid, 'system', 'new_abook_closeness');
        if ($closeness === false) {
            $closeness = 80;
        }
        $r = q("insert into abook ( abook_account, abook_channel, abook_closeness, abook_xchan, abook_feed, abook_their_perms, abook_my_perms, abook_created, abook_updated )\n\t\t\tvalues( %d, %d, %d, '%s', %d, %d, %d, '%s', '%s' ) ", intval($aid), intval($uid), intval($closeness), dbesc($xchan_hash), intval($is_http ? 1 : 0), intval($is_http ? $their_perms | PERMS_R_STREAM | PERMS_A_REPUBLISH : $their_perms), intval($my_perms), dbesc(datetime_convert()), dbesc(datetime_convert()));
    }
    if (!$r) {
        logger('mod_follow: abook creation failed');
    }
    $r = q("select abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash \n\t\twhere abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($xchan_hash), intval($uid));
    if ($r) {
        $result['abook'] = $r[0];
        proc_run('php', 'include/notifier.php', 'permission_update', $result['abook']['abook_id']);
    }
    $arr = array('channel_id' => $uid, 'abook' => $result['abook']);
    call_hooks('follow', $arr);
    /** If there is a default group for this channel, add this member to it */
    if ($default_group) {
        require_once 'include/group.php';
        $g = group_rec_byhash($uid, $default_group);
        if ($g) {
            group_add_member($uid, '', $xchan_hash, $g['id']);
        }
    }
    $result['success'] = true;
    return $result;
}
Exemple #8
0
/**
 * @brief Refreshes after permission changed or friending, etc.
 *
 * zot_refresh is typically invoked when somebody has changed permissions of a channel and they are notified
 * to fetch new permissions via a finger/discovery operation. This may result in a new connection
 * (abook entry) being added to a local channel and it may result in auto-permissions being granted.
 *
 * Friending in zot is accomplished by sending a refresh packet to a specific channel which indicates a
 * permission change has been made by the sender which affects the target channel. The hub controlling
 * the target channel does targetted discovery (a zot-finger request requesting permissions for the local
 * channel). These are decoded here, and if necessary and abook structure (addressbook) is created to store
 * the permissions assigned to this channel.
 *
 * Initially these abook structures are created with a 'pending' flag, so that no reverse permissions are
 * implied until this is approved by the owner channel. A channel can also auto-populate permissions in
 * return and send back a refresh packet of its own. This is used by forum and group communication channels
 * so that friending and membership in the channel's "club" is automatic.
 *
 * @param array $them => xchan structure of sender
 * @param array $channel => local channel structure of target recipient, required for "friending" operations
 * @param array $force default false
 *
 * @returns boolean true if successful, else false
 */
function zot_refresh($them, $channel = null, $force = false)
{
    if (array_key_exists('xchan_network', $them) && $them['xchan_network'] !== 'zot') {
        logger('zot_refresh: not got zot. ' . $them['xchan_name']);
        return true;
    }
    logger('zot_refresh: them: ' . print_r($them, true), LOGGER_DATA, LOG_DEBUG);
    if ($channel) {
        logger('zot_refresh: channel: ' . print_r($channel, true), LOGGER_DATA, LOG_DEBUG);
    }
    $url = null;
    if ($them['hubloc_url']) {
        $url = $them['hubloc_url'];
    } else {
        $r = null;
        // if they re-installed the server we could end up with the wrong record - pointing to the old install.
        // We'll order by reverse id to try and pick off the newest one first and hopefully end up with the
        // correct hubloc. If this doesn't work we may have to re-write this section to try them all.
        if (array_key_exists('xchan_addr', $them) && $them['xchan_addr']) {
            $r = q("select hubloc_url, hubloc_primary from hubloc where hubloc_addr = '%s' order by hubloc_id desc", dbesc($them['xchan_addr']));
        }
        if (!$r) {
            $r = q("select hubloc_url, hubloc_primary from hubloc where hubloc_hash = '%s' order by hubloc_id desc", dbesc($them['xchan_hash']));
        }
        if ($r) {
            foreach ($r as $rr) {
                if (intval($rr['hubloc_primary'])) {
                    $url = $rr['hubloc_url'];
                    break;
                }
            }
            if (!$url) {
                $url = $r[0]['hubloc_url'];
            }
        }
    }
    if (!$url) {
        logger('zot_refresh: no url');
        return false;
    }
    $token = random_string();
    $postvars = array();
    $postvars['token'] = $token;
    if ($channel) {
        $postvars['target'] = $channel['channel_guid'];
        $postvars['target_sig'] = $channel['channel_guid_sig'];
        $postvars['key'] = $channel['channel_pubkey'];
    }
    if (array_key_exists('xchan_addr', $them) && $them['xchan_addr']) {
        $postvars['address'] = $them['xchan_addr'];
    }
    if (array_key_exists('xchan_hash', $them) && $them['xchan_hash']) {
        $postvars['guid_hash'] = $them['xchan_hash'];
    }
    if (array_key_exists('xchan_guid', $them) && $them['xchan_guid'] && array_key_exists('xchan_guid_sig', $them) && $them['xchan_guid_sig']) {
        $postvars['guid'] = $them['xchan_guid'];
        $postvars['guid_sig'] = $them['xchan_guid_sig'];
    }
    $rhs = '/.well-known/zot-info';
    $result = z_post_url($url . $rhs, $postvars);
    logger('zot_refresh: zot-info: ' . print_r($result, true), LOGGER_DATA, LOG_DEBUG);
    if ($result['success']) {
        $j = json_decode($result['body'], true);
        if (!($j && $j['success'])) {
            logger('zot_refresh: result not decodable');
            return false;
        }
        $signed_token = is_array($j) && array_key_exists('signed_token', $j) ? $j['signed_token'] : null;
        if ($signed_token) {
            $valid = rsa_verify('token.' . $token, base64url_decode($signed_token), $j['key']);
            if (!$valid) {
                logger('invalid signed token: ' . $url . $rhs, LOGGER_NORMAL, LOG_ERR);
                return false;
            }
        } else {
            logger('No signed token from ' . $url . $rhs, LOGGER_NORMAL, LOG_WARNING);
            // after 2017-01-01 this will be a hard error unless you over-ride it.
            if (time() > 1483228800 && !get_config('system', 'allow_unsigned_zotfinger')) {
                return false;
            }
        }
        $x = import_xchan($j, $force ? UPDATE_FLAGS_FORCED : UPDATE_FLAGS_UPDATED);
        if (!$x['success']) {
            return false;
        }
        $their_perms = 0;
        if ($channel) {
            $global_perms = get_perms();
            if ($j['permissions']['data']) {
                $permissions = crypto_unencapsulate(array('data' => $j['permissions']['data'], 'key' => $j['permissions']['key'], 'iv' => $j['permissions']['iv']), $channel['channel_prvkey']);
                if ($permissions) {
                    $permissions = json_decode($permissions, true);
                }
                logger('decrypted permissions: ' . print_r($permissions, true), LOGGER_DATA, LOG_DEBUG);
            } else {
                $permissions = $j['permissions'];
            }
            $connected_set = false;
            if ($permissions && is_array($permissions)) {
                foreach ($permissions as $k => $v) {
                    // The connected permission means you are in their address book
                    if ($k === 'connected') {
                        $connected_set = intval($v);
                        continue;
                    }
                    if ($v && array_key_exists($k, $global_perms)) {
                        $their_perms = $their_perms | intval($global_perms[$k][1]);
                    }
                }
            }
            if (array_key_exists('profile', $j) && array_key_exists('next_birthday', $j['profile'])) {
                $next_birthday = datetime_convert('UTC', 'UTC', $j['profile']['next_birthday']);
            } else {
                $next_birthday = NULL_DATE;
            }
            $r = q("select * from abook where abook_xchan = '%s' and abook_channel = %d and abook_self = 0 limit 1", dbesc($x['hash']), intval($channel['channel_id']));
            if ($r) {
                // connection exists
                // if the dob is the same as what we have stored (disregarding the year), keep the one
                // we have as we may have updated the year after sending a notification; and resetting
                // to the one we just received would cause us to create duplicated events.
                if (substr($r[0]['abook_dob'], 5) == substr($next_birthday, 5)) {
                    $next_birthday = $r[0]['abook_dob'];
                }
                $current_abook_connected = intval($r[0]['abook_unconnected']) ? 0 : 1;
                $y = q("update abook set abook_their_perms = %d, abook_dob = '%s'\n\t\t\t\t\twhere abook_xchan = '%s' and abook_channel = %d\n\t\t\t\t\tand abook_self = 0 ", intval($their_perms), dbescdate($next_birthday), dbesc($x['hash']), intval($channel['channel_id']));
                //				if(($connected_set === 0 || $connected_set === 1) && ($connected_set !== $current_abook_unconnected)) {
                // if they are in your address book but you aren't in theirs, and/or this does not
                // match your current connected state setting, toggle it.
                /** @FIXME uncoverted to postgres */
                /** @FIXME when this was enabled, all contacts became unconnected. Currently disabled intentionally */
                //					$y1 = q("update abook set abook_unconnected = 1
                //						where abook_xchan = '%s' and abook_channel = %d
                //						and abook_self = 0 limit 1",
                //						dbesc($x['hash']),
                //						intval($channel['channel_id'])
                //					);
                //				}
                if (!$y) {
                    logger('abook update failed');
                } else {
                    // if we were just granted read stream permission and didn't have it before, try to pull in some posts
                    if (!($r[0]['abook_their_perms'] & PERMS_R_STREAM) && $their_perms & PERMS_R_STREAM) {
                        Zotlabs\Daemon\Master::Summon(array('Onepoll', $r[0]['abook_id']));
                    }
                }
            } else {
                // new connection
                $role = get_pconfig($channel['channel_id'], 'system', 'permissions_role');
                if ($role) {
                    $xx = get_role_perms($role);
                    if ($xx['perms_auto']) {
                        $default_perms = $xx['perms_accept'];
                    }
                }
                if (!$default_perms) {
                    $default_perms = intval(get_pconfig($channel['channel_id'], 'system', 'autoperms'));
                }
                // Keep original perms to check if we need to notify them
                $previous_perms = get_all_perms($channel['channel_id'], $x['hash']);
                $closeness = get_pconfig($channel['channel_id'], 'system', 'new_abook_closeness');
                if ($closeness === false) {
                    $closeness = 80;
                }
                $y = q("insert into abook ( abook_account, abook_channel, abook_closeness, abook_xchan, abook_their_perms, abook_my_perms, abook_created, abook_updated, abook_dob, abook_pending ) values ( %d, %d, %d, '%s', %d, %d, '%s', '%s', '%s', %d )", intval($channel['channel_account_id']), intval($channel['channel_id']), intval($closeness), dbesc($x['hash']), intval($their_perms), intval($default_perms), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($next_birthday), intval($default_perms ? 0 : 1));
                if ($y) {
                    logger("New introduction received for {$channel['channel_name']}");
                    $new_perms = get_all_perms($channel['channel_id'], $x['hash']);
                    // Send a clone sync packet and a permissions update if permissions have changed
                    $new_connection = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_xchan = '%s' and abook_channel = %d and abook_self = 0 order by abook_created desc limit 1", dbesc($x['hash']), intval($channel['channel_id']));
                    if ($new_connection) {
                        if ($new_perms != $previous_perms) {
                            Zotlabs\Daemon\Master::Summon(array('Notifier', 'permission_create', $new_connection[0]['abook_id']));
                        }
                        Zotlabs\Lib\Enotify::submit(array('type' => NOTIFY_INTRO, 'from_xchan' => $x['hash'], 'to_xchan' => $channel['channel_hash'], 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id']));
                        if ($their_perms & PERMS_R_STREAM) {
                            if ($channel['channel_w_stream'] & PERMS_PENDING || !intval($new_connection[0]['abook_pending'])) {
                                Zotlabs\Daemon\Master::Summon(array('Onepoll', $new_connection[0]['abook_id']));
                            }
                        }
                        /** If there is a default group for this channel, add this connection to it */
                        $default_group = $channel['channel_default_group'];
                        if ($default_group) {
                            require_once 'include/group.php';
                            $g = group_rec_byhash($channel['channel_id'], $default_group);
                            if ($g) {
                                group_add_member($channel['channel_id'], '', $x['hash'], $g['id']);
                            }
                        }
                        unset($new_connection[0]['abook_id']);
                        unset($new_connection[0]['abook_account']);
                        unset($new_connection[0]['abook_channel']);
                        $abconfig = load_abconfig($channel['channel_id'], $new_connection['abook_xchan']);
                        if ($abconfig) {
                            $new_connection['abconfig'] = $abconfig;
                        }
                        build_sync_packet($channel['channel_id'], array('abook' => $new_connection));
                    }
                }
            }
        }
        return true;
    }
    return false;
}
 function post()
 {
     if (!local_channel()) {
         return;
     }
     $contact_id = intval(argv(1));
     if (!$contact_id) {
         return;
     }
     $channel = \App::get_channel();
     // TODO if configured for hassle-free permissions, we'll post the form with ajax as soon as the
     // connection enable is toggled to a special autopost url and set permissions immediately, leaving
     // the other form elements alone pending a manual submit of the form. The downside is that there
     // will be a window of opportunity when the permissions have been set but before you've had a chance
     // to review and possibly restrict them. The upside is we won't have to warn you that your connection
     // can't do anything until you save the bloody form.
     $autopost = argc() > 2 && argv(2) === 'auto' ? true : false;
     $orig_record = q("SELECT * FROM abook WHERE abook_id = %d AND abook_channel = %d LIMIT 1", intval($contact_id), intval(local_channel()));
     if (!$orig_record) {
         notice(t('Could not access contact record.') . EOL);
         goaway(z_root() . '/connections');
         return;
         // NOTREACHED
     }
     call_hooks('contact_edit_post', $_POST);
     if (intval($orig_record[0]['abook_self'])) {
         $autoperms = intval($_POST['autoperms']);
         $is_self = true;
     } else {
         $autoperms = null;
         $is_self = false;
     }
     $profile_id = $_POST['profile_assign'];
     if ($profile_id) {
         $r = q("SELECT profile_guid FROM profile WHERE profile_guid = '%s' AND `uid` = %d LIMIT 1", dbesc($profile_id), intval(local_channel()));
         if (!count($r)) {
             notice(t('Could not locate selected profile.') . EOL);
             return;
         }
     }
     $abook_incl = escape_tags($_POST['abook_incl']);
     $abook_excl = escape_tags($_POST['abook_excl']);
     $hidden = intval($_POST['hidden']);
     $priority = intval($_POST['poll']);
     if ($priority > 5 || $priority < 0) {
         $priority = 0;
     }
     $closeness = intval($_POST['closeness']);
     if ($closeness < 0) {
         $closeness = 99;
     }
     $rating = intval($_POST['rating']);
     if ($rating < -10) {
         $rating = -10;
     }
     if ($rating > 10) {
         $rating = 10;
     }
     $rating_text = trim(escape_tags($_REQUEST['rating_text']));
     $abook_my_perms = 0;
     foreach ($_POST as $k => $v) {
         if (strpos($k, 'perms_') === 0) {
             $abook_my_perms += $v;
         }
     }
     $new_friend = false;
     if (!$is_self) {
         $signed = $orig_record[0]['abook_xchan'] . '.' . $rating . '.' . $rating_text;
         $sig = base64url_encode(rsa_sign($signed, $channel['channel_prvkey']));
         $z = q("select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' and xlink_static = 1 limit 1", dbesc($channel['channel_hash']), dbesc($orig_record[0]['abook_xchan']));
         if ($z) {
             $record = $z[0]['xlink_id'];
             $w = q("update xlink set xlink_rating = '%d', xlink_rating_text = '%s', xlink_sig = '%s', xlink_updated = '%s'\n\t\t\t\t\twhere xlink_id = %d", intval($rating), dbesc($rating_text), dbesc($sig), dbesc(datetime_convert()), intval($record));
         } else {
             $w = q("insert into xlink ( xlink_xchan, xlink_link, xlink_rating, xlink_rating_text, xlink_sig, xlink_updated, xlink_static ) values ( '%s', '%s', %d, '%s', '%s', '%s', 1 ) ", dbesc($channel['channel_hash']), dbesc($orig_record[0]['abook_xchan']), intval($rating), dbesc($rating_text), dbesc($sig), dbesc(datetime_convert()));
             $z = q("select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' and xlink_static = 1 limit 1", dbesc($channel['channel_hash']), dbesc($orig_record[0]['abook_xchan']));
             if ($z) {
                 $record = $z[0]['xlink_id'];
             }
         }
         if ($record) {
             proc_run('php', 'include/ratenotif.php', 'rating', $record);
         }
     }
     if ($_REQUEST['pending'] && intval($orig_record[0]['abook_pending'])) {
         $new_friend = true;
         // @fixme it won't be common, but when you accept a new connection request
         // the permissions will now be that of your permissions role and ignore
         // any you may have set manually on the form. We'll probably see a bug if somebody
         // tries to set the permissions *and* approve the connection in the same
         // request. The workaround is to approve the connection, then go back and
         // adjust permissions as desired.
         $abook_my_perms = get_channel_default_perms(local_channel());
         $role = get_pconfig(local_channel(), 'system', 'permissions_role');
         if ($role) {
             $x = get_role_perms($role);
             if ($x['perms_accept']) {
                 $abook_my_perms = $x['perms_accept'];
             }
         }
     }
     $abook_pending = $new_friend ? 0 : $orig_record[0]['abook_pending'];
     $r = q("UPDATE abook SET abook_profile = '%s', abook_my_perms = %d , abook_closeness = %d, abook_pending = %d,\n\t\t\tabook_incl = '%s', abook_excl = '%s'\n\t\t\twhere abook_id = %d AND abook_channel = %d", dbesc($profile_id), intval($abook_my_perms), intval($closeness), intval($abook_pending), dbesc($abook_incl), dbesc($abook_excl), intval($contact_id), intval(local_channel()));
     if ($orig_record[0]['abook_profile'] != $profile_id) {
         //Update profile photo permissions
         logger('A new profile was assigned - updating profile photos');
         profile_photo_set_profile_perms($profile_id);
     }
     if ($r) {
         info(t('Connection updated.') . EOL);
     } else {
         notice(t('Failed to update connection record.') . EOL);
     }
     if (\App::$poi && \App::$poi['abook_my_perms'] != $abook_my_perms && !intval(\App::$poi['abook_self'])) {
         proc_run('php', 'include/notifier.php', $new_friend ? 'permission_create' : 'permission_update', $contact_id);
     }
     if ($new_friend) {
         $default_group = $channel['channel_default_group'];
         if ($default_group) {
             require_once 'include/group.php';
             $g = group_rec_byhash(local_channel(), $default_group);
             if ($g) {
                 group_add_member(local_channel(), '', \App::$poi['abook_xchan'], $g['id']);
             }
         }
         // Check if settings permit ("post new friend activity" is allowed, and
         // friends in general or this friend in particular aren't hidden)
         // and send out a new friend activity
         $pr = q("select * from profile where uid = %d and is_default = 1 and hide_friends = 0", intval($channel['channel_id']));
         if ($pr && !intval($orig_record[0]['abook_hidden']) && intval(get_pconfig($channel['channel_id'], 'system', 'post_newfriend'))) {
             $xarr = array();
             $xarr['verb'] = ACTIVITY_FRIEND;
             $xarr['item_wall'] = 1;
             $xarr['item_origin'] = 1;
             $xarr['item_thread_top'] = 1;
             $xarr['owner_xchan'] = $xarr['author_xchan'] = $channel['channel_hash'];
             $xarr['allow_cid'] = $channel['channel_allow_cid'];
             $xarr['allow_gid'] = $channel['channel_allow_gid'];
             $xarr['deny_cid'] = $channel['channel_deny_cid'];
             $xarr['deny_gid'] = $channel['channel_deny_gid'];
             $xarr['item_private'] = $xarr['allow_cid'] || $xarr['allow_gid'] || $xarr['deny_cid'] || $xarr['deny_gid'] ? 1 : 0;
             $obj = array('type' => ACTIVITY_OBJ_PERSON, 'title' => \App::$poi['xchan_name'], 'id' => \App::$poi['xchan_hash'], 'link' => array(array('rel' => 'alternate', 'type' => 'text/html', 'href' => \App::$poi['xchan_url']), array('rel' => 'photo', 'type' => \App::$poi['xchan_photo_mimetype'], 'href' => \App::$poi['xchan_photo_l'])));
             $xarr['object'] = json_encode($obj);
             $xarr['obj_type'] = ACTIVITY_OBJ_PERSON;
             $xarr['body'] = '[zrl=' . $channel['xchan_url'] . ']' . $channel['xchan_name'] . '[/zrl]' . ' ' . t('is now connected to') . ' ' . '[zrl=' . \App::$poi['xchan_url'] . ']' . \App::$poi['xchan_name'] . '[/zrl]';
             $xarr['body'] .= "\n\n\n" . '[zrl=' . \App::$poi['xchan_url'] . '][zmg=80x80]' . \App::$poi['xchan_photo_m'] . '[/zmg][/zrl]';
             post_activity_item($xarr);
         }
         // pull in a bit of content if there is any to pull in
         proc_run('php', 'include/onepoll.php', $contact_id);
     }
     // Refresh the structure in memory with the new data
     $r = q("SELECT abook.*, xchan.*\n\t\t\tFROM abook left join xchan on abook_xchan = xchan_hash\n\t\t\tWHERE abook_channel = %d and abook_id = %d LIMIT 1", intval(local_channel()), intval($contact_id));
     if ($r) {
         \App::$poi = $r[0];
     }
     if ($new_friend) {
         $arr = array('channel_id' => local_channel(), 'abook' => \App::$poi);
         call_hooks('accept_follow', $arr);
     }
     if (!is_null($autoperms)) {
         set_pconfig(local_channel(), 'system', 'autoperms', $autoperms ? $abook_my_perms : 0);
     }
     $this->connedit_clone($a);
     if ($_REQUEST['pending'] && !$_REQUEST['done']) {
         goaway(z_root() . '/connections/ifpending');
     }
     return;
 }
Exemple #10
0
function diaspora_request($importer, $xml)
{
    $a = get_app();
    $sender_handle = unxmlify(diaspora_get_author($xml));
    $recipient_handle = unxmlify(diaspora_get_recipient($xml));
    // @TODO - map these perms to $newperms below
    if (array_key_exists('following', $xml) && array_key_exists('sharing', $xml)) {
        $following = unxmlify($xml['following']) === 'true' ? true : false;
        $sharing = unxmlify($xml['sharing']) === 'true' ? true : false;
    } else {
        $following = true;
        $sharing = true;
    }
    if (!$sender_handle || !$recipient_handle) {
        return;
    }
    // Do we already have an abook record?
    $contact = diaspora_get_contact_by_handle($importer['channel_id'], $sender_handle);
    // Please note some permissions such as PERMS_R_PAGES are impossible for Disapora.
    // They cannot currently authenticate to our system.
    $x = \Zotlabs\Access\PermissionRoles::role_perms('social');
    $their_perms = \Zotlabs\Access\Permissions::FilledPerms($x['perms_connect']);
    if ($contact && $contact['abook_id']) {
        // perhaps we were already sharing with this person. Now they're sharing with us.
        // That makes us friends. Maybe.
        foreach ($their_perms as $k => $v) {
            set_abconfig($importer['channel_id'], $contact['abook_xchan'], 'their_perms', $k, $v);
        }
        $abook_instance = $contact['abook_instance'];
        if ($abook_instance) {
            $abook_instance .= ',';
        }
        $abook_instance .= z_root();
        $r = q("update abook set abook_instance = '%s' where abook_id = %d and abook_channel = %d", dbesc($abook_instance), intval($contact['abook_id']), intval($importer['channel_id']));
        return;
    }
    $ret = find_diaspora_person_by_handle($sender_handle);
    if (!$ret || !strstr($ret['xchan_network'], 'diaspora')) {
        logger('diaspora_request: Cannot resolve diaspora handle ' . $sender_handle . ' for ' . $recipient_handle);
        return;
    }
    $my_perms = false;
    $role = get_pconfig($importer['channel_id'], 'system', 'permissions_role');
    if ($role) {
        $x = \Zotlabs\Access\PermissionRoles::role_perms($role);
        if ($x['perms_auto']) {
            $my_perms = \Zotlabs\Access\Permissions::FilledPerms($x['perms_connect']);
        }
    }
    if (!$my_perms) {
        $my_perms = \Zotlabs\Access\Permissions::FilledAutoperms($importer['channel_id']);
    }
    $closeness = get_pconfig($importer['channel_id'], 'system', 'new_abook_closeness');
    if ($closeness === false) {
        $closeness = 80;
    }
    $r = q("insert into abook ( abook_account, abook_channel, abook_xchan, abook_my_perms, abook_their_perms, abook_closeness, abook_created, abook_updated, abook_connected, abook_dob, abook_pending, abook_instance ) values ( %d, %d, '%s', %d, %d, %d, '%s', '%s', '%s', '%s', %d, '%s' )", intval($importer['channel_account_id']), intval($importer['channel_id']), dbesc($ret['xchan_hash']), intval($default_perms), intval($their_perms), intval($closeness), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(NULL_DATE), intval($my_perms ? 0 : 1), dbesc(z_root()));
    if ($my_perms) {
        foreach ($my_perms as $k => $v) {
            set_abconfig($importer['channel_id'], $ret['xchan_hash'], 'my_perms', $k, $v);
        }
    }
    if ($their_perms) {
        foreach ($their_perms as $k => $v) {
            set_abconfig($importer['channel_id'], $ret['xchan_hash'], 'their_perms', $k, $v);
        }
    }
    if ($r) {
        logger("New Diaspora introduction received for {$importer['channel_name']}");
        $new_connection = q("select * from abook left join xchan on abook_xchan = xchan_hash left join hubloc on hubloc_hash = xchan_hash where abook_channel = %d and abook_xchan = '%s' order by abook_created desc limit 1", intval($importer['channel_id']), dbesc($ret['xchan_hash']));
        if ($new_connection) {
            \Zotlabs\Lib\Enotify::submit(['type' => NOTIFY_INTRO, 'from_xchan' => $ret['xchan_hash'], 'to_xchan' => $importer['channel_hash'], 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id']]);
            if ($my_perms) {
                // Send back a sharing notification to them
                $x = diaspora_share($importer, $new_connection[0]);
                if ($x) {
                    Zotlabs\Daemon\Master::Summon(array('Deliver', $x));
                }
            }
            $clone = array();
            foreach ($new_connection[0] as $k => $v) {
                if (strpos($k, 'abook_') === 0) {
                    $clone[$k] = $v;
                }
            }
            unset($clone['abook_id']);
            unset($clone['abook_account']);
            unset($clone['abook_channel']);
            $abconfig = load_abconfig($importer['channel_id'], $clone['abook_xchan']);
            if ($abconfig) {
                $clone['abconfig'] = $abconfig;
            }
            build_sync_packet($importer['channel_id'], ['abook' => array($clone)]);
        }
    }
    // find the abook record we just created
    $contact_record = diaspora_get_contact_by_handle($importer['channel_id'], $sender_handle);
    if (!$contact_record) {
        logger('diaspora_request: unable to locate newly created contact record.');
        return;
    }
    /** If there is a default group for this channel, add this member to it */
    if ($importer['channel_default_group']) {
        require_once 'include/group.php';
        $g = group_rec_byhash($importer['channel_id'], $importer['channel_default_group']);
        if ($g) {
            group_add_member($importer['channel_id'], '', $contact_record['xchan_hash'], $g['id']);
        }
    }
    return;
}
Exemple #11
0
function connedit_post(&$a)
{
    if (!local_user()) {
        return;
    }
    $contact_id = intval(argv(1));
    if (!$contact_id) {
        return;
    }
    $orig_record = q("SELECT * FROM abook WHERE abook_id = %d AND abook_channel = %d LIMIT 1", intval($contact_id), intval(local_user()));
    if (!$orig_record) {
        notice(t('Could not access contact record.') . EOL);
        goaway($a->get_baseurl(true) . '/connections');
        return;
        // NOTREACHED
    }
    call_hooks('contact_edit_post', $_POST);
    $profile_id = $_POST['profile_assign'];
    if ($profile_id) {
        $r = q("SELECT profile_guid FROM profile WHERE profile_guid = '%s' AND `uid` = %d LIMIT 1", dbesc($profile_id), intval(local_user()));
        if (!count($r)) {
            notice(t('Could not locate selected profile.') . EOL);
            return;
        }
    }
    $hidden = intval($_POST['hidden']);
    $priority = intval($_POST['poll']);
    if ($priority > 5 || $priority < 0) {
        $priority = 0;
    }
    $closeness = intval($_POST['closeness']);
    if ($closeness < 0) {
        $closeness = 99;
    }
    $abook_my_perms = 0;
    foreach ($_POST as $k => $v) {
        if (strpos($k, 'perms_') === 0) {
            $abook_my_perms += $v;
        }
    }
    $abook_flags = $orig_record[0]['abook_flags'];
    $new_friend = false;
    if ($_REQUEST['pending'] && $abook_flags & ABOOK_FLAG_PENDING) {
        $abook_flags = $abook_flags ^ ABOOK_FLAG_PENDING;
        $new_friend = true;
    }
    $r = q("UPDATE abook SET abook_profile = '%s', abook_my_perms = %d , abook_closeness = %d, abook_flags = %d\n\t\twhere abook_id = %d AND abook_channel = %d LIMIT 1", dbesc($profile_id), intval($abook_my_perms), intval($closeness), intval($abook_flags), intval($contact_id), intval(local_user()));
    if ($orig_record[0]['abook_profile'] != $profile_id) {
        //Update profile photo permissions
        logger('As a new profile was assigned updating profile photos');
        require_once 'mod/profile_photo.php';
        profile_photo_set_profile_perms($profile_id);
    }
    if ($r) {
        info(t('Connection updated.') . EOL);
    } else {
        notice(t('Failed to update connection record.') . EOL);
    }
    if ($a->poi && $a->poi['abook_my_perms'] != $abook_my_perms && !($a->poi['abook_flags'] & ABOOK_FLAG_SELF)) {
        proc_run('php', 'include/notifier.php', 'permission_update', $contact_id);
    }
    if ($new_friend) {
        $channel = $a->get_channel();
        $default_group = $channel['channel_default_group'];
        if ($default_group) {
            require_once 'include/group.php';
            $g = group_rec_byhash(local_user(), $default_group);
            if ($g) {
                group_add_member(local_user(), '', $a->poi['abook_xchan'], $g['id']);
            }
        }
        // Check if settings permit ("post new friend activity" is allowed, and
        // friends in general or this friend in particular aren't hidden)
        // and send out a new friend activity
        $pr = q("select * from profile where uid = %d and is_default = 1 and hide_friends = 0", intval($channel['channel_id']));
        if ($pr && !($abook_flags & ABOOK_FLAG_HIDDEN) && intval(get_pconfig($channel['channel_id'], 'system', 'post_newfriend'))) {
            $xarr = array();
            $xarr['verb'] = ACTIVITY_FRIEND;
            $xarr['item_flags'] = ITEM_WALL | ITEM_ORIGIN | ITEM_THREAD_TOP;
            $xarr['owner_xchan'] = $xarr['author_xchan'] = $channel['channel_hash'];
            $xarr['allow_cid'] = $channel['channel_allow_cid'];
            $xarr['allow_gid'] = $channel['channel_allow_gid'];
            $xarr['deny_cid'] = $channel['channel_deny_cid'];
            $xarr['deny_gid'] = $channel['channel_deny_gid'];
            $xarr['item_private'] = $xarr['allow_cid'] || $xarr['allow_gid'] || $xarr['deny_cid'] || $xarr['deny_gid'] ? 1 : 0;
            $obj = array('type' => ACTIVITY_OBJ_PERSON, 'title' => $a->poi['xchan_name'], 'id' => $a->poi['xchan_hash'], 'link' => array(array('rel' => 'alternate', 'type' => 'text/html', 'href' => $a->poi['xchan_url']), array('rel' => 'photo', 'type' => $a->poi['xchan_photo_mimetype'], 'href' => $a->poi['xchan_photo_l'])));
            $xarr['object'] = json_encode($obj);
            $xarr['obj_type'] = ACTIVITY_OBJ_PERSON;
            $xarr['body'] = '[zrl=' . $channel['xchan_url'] . ']' . $channel['xchan_name'] . '[/zrl]' . ' ' . t('is now connected to') . ' ' . '[zrl=' . $a->poi['xchan_url'] . ']' . $a->poi['xchan_name'] . '[/zrl]';
            $xarr['body'] .= "\n\n\n" . '[zrl=' . $a->poi['xchan_url'] . '][zmg=80x80]' . $a->poi['xchan_photo_m'] . '[/zmg][/zrl]';
            post_activity_item($xarr);
        }
        // pull in a bit of content if there is any to pull in
        proc_run('php', 'include/onepoll.php', $contact_id);
    }
    // Refresh the structure in memory with the new data
    $r = q("SELECT abook.*, xchan.* \n\t\tFROM abook left join xchan on abook_xchan = xchan_hash\n\t\tWHERE abook_channel = %d and abook_id = %d LIMIT 1", intval(local_user()), intval($contact_id));
    if ($r) {
        $a->poi = $r[0];
    }
    if ($new_friend) {
        $arr = array('channel_id' => local_user(), 'abook' => $a->poi);
        call_hooks('accept_follow', $arr);
    }
    connedit_clone($a);
    return;
}
Exemple #12
0
function network_content(&$a, $update = 0, $load = false)
{
    if (!local_user()) {
        $_SESSION['return_url'] = $a->query_string;
        return login(false);
    }
    $arr = array('query' => $a->query_string);
    call_hooks('network_content_init', $arr);
    $channel = $a->get_channel();
    $search = $_GET['search'] ? $_GET['search'] : '';
    if ($search) {
        if (strpos($search, '@') === 0) {
            $r = q("select abook_id from abook left join xchan on abook_xchan = xchan_hash where xchan_name = '%s' and abook_channel = %d limit 1", dbesc(substr($search, 1)), intval(local_user()));
            if ($r) {
                $_GET['cid'] = $r[0]['abook_id'];
                $search = $_GET['search'] = '';
            }
        } elseif (strpos($search, '#') === 0) {
            $search = $_GET['search'] = substr($search, 1);
        }
    }
    $datequery = $datequery2 = '';
    $group = 0;
    $nouveau = false;
    $datequery = x($_GET, 'dend') && is_a_date_arg($_GET['dend']) ? notags($_GET['dend']) : '';
    $datequery2 = x($_GET, 'dbegin') && is_a_date_arg($_GET['dbegin']) ? notags($_GET['dbegin']) : '';
    $nouveau = x($_GET, 'new') ? intval($_GET['new']) : 0;
    $gid = x($_GET, 'gid') ? intval($_GET['gid']) : 0;
    if ($datequery) {
        $_GET['order'] = 'post';
    }
    if ($gid) {
        $r = q("SELECT * FROM `groups` WHERE id = %d AND uid = %d LIMIT 1", intval($gid), intval(local_user()));
        if (!$r) {
            if ($update) {
                killme();
            }
            notice(t('No such group') . EOL);
            goaway($a->get_baseurl(true) . '/network');
            // NOTREACHED
        }
        $group = $gid;
        $group_hash = $r[0]['hash'];
        $def_acl = array('allow_gid' => '<' . $r[0]['hash'] . '>');
    }
    $o = '';
    // if no tabs are selected, defaults to comments
    $cid = x($_GET, 'cid') ? intval($_GET['cid']) : 0;
    $star = x($_GET, 'star') ? intval($_GET['star']) : 0;
    $order = x($_GET, 'order') ? notags($_GET['order']) : 'comment';
    $liked = x($_GET, 'liked') ? intval($_GET['liked']) : 0;
    $conv = x($_GET, 'conv') ? intval($_GET['conv']) : 0;
    $spam = x($_GET, 'spam') ? intval($_GET['spam']) : 0;
    $cmin = x($_GET, 'cmin') ? intval($_GET['cmin']) : 0;
    $cmax = x($_GET, 'cmax') ? intval($_GET['cmax']) : 99;
    $firehose = x($_GET, 'fh') ? intval($_GET['fh']) : 0;
    $file = x($_GET, 'file') ? $_GET['file'] : '';
    if (x($_GET, 'search') || x($_GET, 'file')) {
        $nouveau = true;
    }
    if ($cid) {
        $def_acl = array('allow_cid' => '<' . intval($cid) . '>');
    }
    if (!$update) {
        $o .= network_tabs();
        // search terms header
        if ($search) {
            $o .= '<h2>' . t('Search Results For:') . ' ' . htmlspecialchars($search, ENT_COMPAT, 'UTF-8') . '</h2>';
        }
        nav_set_selected('network');
        $channel_acl = array('allow_cid' => $channel['channel_allow_cid'], 'allow_gid' => $channel['channel_allow_gid'], 'deny_cid' => $channel['channel_deny_cid'], 'deny_gid' => $channel['channel_deny_gid']);
        $x = array('is_owner' => true, 'allow_location' => intval(get_pconfig($channel['channel_id'], 'system', 'use_browser_location')) ? '1' : '', 'default_location' => $channel['channel_location'], 'nickname' => $channel['channel_address'], 'lockstate' => $group || $cid || $channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid'] ? 'lock' : 'unlock', 'acl' => populate_acl($group || $cid ? $def_acl : $channel_acl), 'bang' => $group || $cid ? '!' : '', 'visitor' => true, 'profile_uid' => local_user());
        $o .= status_editor($a, $x);
    }
    // We don't have to deal with ACL's on this page. You're looking at everything
    // that belongs to you, hence you can see all of it. We will filter by group if
    // desired.
    $sql_options = $star ? " and (item_flags & " . intval(ITEM_STARRED) . ")" : '';
    $sql_nets = '';
    $sql_extra = " AND `item`.`parent` IN ( SELECT `parent` FROM `item` WHERE (item_flags & " . intval(ITEM_THREAD_TOP) . ") {$sql_options} ) ";
    if ($group) {
        $contact_str = '';
        $contacts = group_get_members($group);
        if ($contacts) {
            foreach ($contacts as $c) {
                if ($contact_str) {
                    $contact_str .= ',';
                }
                $contact_str .= "'" . $c['xchan'] . "'";
            }
        } else {
            $contact_str = ' 0 ';
            info(t('Collection is empty'));
        }
        $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true {$sql_options} AND (( author_xchan IN ( {$contact_str} ) OR owner_xchan in ( {$contact_str} )) or allow_gid like '" . protect_sprintf('%<' . dbesc($group_hash) . '>%') . "' ) and id = parent and item_restrict = 0 ) ";
        $x = group_rec_byhash(local_user(), $group_hash);
        if ($x) {
            $o = '<h2>' . t('Collection: ') . $x['name'] . '</h2>' . $o;
        }
    } elseif ($cid) {
        $r = q("SELECT abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_channel = %d and not ( abook_flags & " . intval(ABOOK_FLAG_BLOCKED) . ") limit 1", intval($cid), intval(local_user()));
        if ($r) {
            $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true {$sql_options} AND uid = " . intval(local_user()) . " AND ( author_xchan = '" . dbesc($r[0]['abook_xchan']) . "' or owner_xchan = '" . dbesc($r[0]['abook_xchan']) . "' ) and item_restrict = 0 ) ";
            $o = '<h2>' . t('Connection: ') . $r[0]['xchan_name'] . '</h2>' . $o;
        } else {
            notice(t('Invalid connection.') . EOL);
            goaway($a->get_baseurl(true) . '/network');
        }
    }
    if (!$update) {
        // The special div is needed for liveUpdate to kick in for this page.
        // We only launch liveUpdate if you aren't filtering in some incompatible
        // way and also you aren't writing a comment (discovered in javascript).
        if ($gid || $cid || $cmin || $cmax != 99 || $star || $liked || $conv || $spam || $nouveau || $list) {
            $firehose = 0;
        }
        $o .= '<div id="live-network"></div>' . "\r\n";
        $o .= "<script> var profile_uid = " . $_SESSION['uid'] . "; var profile_page = " . $a->pager['page'] . ";</script>";
        $a->page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"), array('$baseurl' => z_root(), '$pgtype' => 'network', '$uid' => local_user() ? local_user() : '0', '$gid' => $gid ? $gid : '0', '$cid' => $cid ? $cid : '0', '$cmin' => $cmin ? $cmin : '0', '$cmax' => $cmax ? $cmax : '0', '$star' => $star ? $star : '0', '$liked' => $liked ? $liked : '0', '$conv' => $conv ? $conv : '0', '$spam' => $spam ? $spam : '0', '$fh' => $firehose ? $firehose : '0', '$nouveau' => $nouveau ? $nouveau : '0', '$wall' => '0', '$list' => x($_REQUEST, 'list') ? intval($_REQUEST['list']) : 0, '$page' => $a->pager['page'] != 1 ? $a->pager['page'] : 1, '$search' => $search ? $search : '', '$order' => $order, '$file' => $file, '$cats' => '', '$dend' => $datequery, '$mid' => '', '$dbegin' => $datequery2));
    }
    $sql_extra3 = '';
    if ($datequery) {
        $sql_extra3 .= protect_sprintf(sprintf(" AND item.created <= '%s' ", dbesc(datetime_convert(date_default_timezone_get(), '', $datequery))));
    }
    if ($datequery2) {
        $sql_extra3 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert(date_default_timezone_get(), '', $datequery2))));
    }
    $sql_extra2 = $nouveau ? '' : " AND `item`.`parent` = `item`.`id` ";
    $sql_extra3 = $nouveau ? '' : $sql_extra3;
    if (x($_GET, 'search')) {
        $search = escape_tags($_GET['search']);
        if (strpos($search, '#') === 0) {
            $sql_extra .= term_query('item', substr($search, 1), TERM_HASHTAG);
        } else {
            $sql_extra .= sprintf(" AND `item`.`body` like '%s' ", dbesc(protect_sprintf('%' . $search . '%')));
        }
    }
    if (strlen($file)) {
        $sql_extra .= term_query('item', $file, TERM_FILE);
    }
    if ($conv) {
        $sql_extra .= sprintf(" AND parent IN (SELECT distinct(parent) from item where ( author_xchan like '%s' or ( item_flags & %d ))) ", dbesc(protect_sprintf($channel['channel_hash'])), intval(ITEM_MENTIONSME));
    }
    if ($update && !$load) {
        // only setup pagination on initial page view
        $pager_sql = '';
    } else {
        $itemspage = get_pconfig(local_user(), 'system', 'itemspage');
        $a->set_pager_itemspage(intval($itemspage) ? $itemspage : 20);
        $pager_sql = sprintf(" LIMIT %d, %d ", intval($a->pager['start']), intval($a->pager['itemspage']));
    }
    if ($cmin != 0 || $cmax != 99) {
        // Not everybody who shows up in the network stream will be in your address book.
        // By default those that aren't are assumed to have closeness = 99; but this isn't
        // recorded anywhere. So if cmax is 99, we'll open the search up to anybody in
        // the stream with a NULL address book entry.
        $sql_nets .= " AND ";
        if ($cmax == 99) {
            $sql_nets .= " ( ";
        }
        $sql_nets .= "( abook.abook_closeness >= " . intval($cmin) . " ";
        $sql_nets .= " AND abook.abook_closeness <= " . intval($cmax) . " ) ";
        if ($cmax == 99) {
            $sql_nets .= " OR abook.abook_closeness IS NULL ) ";
        }
    }
    if ($firehose && !get_config('system', 'disable_discover_tab')) {
        require_once 'include/identity.php';
        $sys = get_sys_channel();
        $uids = " and item.uid  = " . intval($sys['channel_id']) . " ";
        $a->data['firehose'] = intval($sys['channel_id']);
    } else {
        $uids = " and item.uid = " . local_user() . " ";
    }
    $simple_update = $update ? " and ( item.item_flags & " . intval(ITEM_UNSEEN) . " ) " : '';
    // This fixes a very subtle bug so I'd better explain it. You wake up in the morning or return after a day
    // or three and look at your matrix page - after opening up your browser. The first page loads just as it
    // should. All of a sudden a few seconds later, page 2 will get inserted at the beginning of the page
    // (before the page 1 content). The update code is actually doing just what it's supposed
    // to, it's fetching posts that have the ITEM_UNSEEN bit set. But the reason that page 2 content is being
    // returned in an UPDATE is because you hadn't gotten that far yet - you're still on page 1 and everything
    // that we loaded for page 1 is now marked as seen. But the stuff on page 2 hasn't been. So... it's being
    // treated as "new fresh" content because it is unseen. We need to distinguish it somehow from content
    // which "arrived as you were reading page 1". We're going to do this
    // by storing in your session the current UTC time whenever you LOAD a network page, and only UPDATE items
    // which are both ITEM_UNSEEN and have "changed" since that time. Cross fingers...
    if ($update && $_SESSION['loadtime']) {
        $simple_update .= " and item.changed > '" . datetime_convert('UTC', 'UTC', $_SESSION['loadtime']) . "' ";
    }
    if ($load) {
        $simple_update = '';
    }
    if ($nouveau && $load) {
        // "New Item View" - show all items unthreaded in reverse created date order
        $items = q("SELECT `item`.*, `item`.`id` AS `item_id` FROM `item` \n\t\t\tWHERE true {$uids} AND item_restrict = 0 \n\t\t\t{$simple_update}\n\t\t\t{$sql_extra} {$sql_nets}\n\t\t\tORDER BY `item`.`received` DESC {$pager_sql} ");
        require_once 'include/items.php';
        xchan_query($items);
        $items = fetch_post_tags($items, true);
    } elseif ($update) {
        // Normal conversation view
        if ($order === 'post') {
            $ordering = "`created`";
        } else {
            $ordering = "`commented`";
        }
        if ($load) {
            $_SESSION['loadtime'] = datetime_convert();
            // Fetch a page full of parent items for this page
            $r = q("SELECT distinct item.id AS item_id FROM item \n\t\t\t\tleft join abook on item.author_xchan = abook.abook_xchan\n\t\t\t\tWHERE true {$uids} AND item.item_restrict = 0\n\t\t\t\tAND item.parent = item.id\n\t\t\t\tand ((abook.abook_flags & %d) = 0 or abook.abook_flags is null)\n\t\t\t\t{$sql_extra3} {$sql_extra} {$sql_nets}\n\t\t\t\tORDER BY item.{$ordering} DESC {$pager_sql} ", intval(ABOOK_FLAG_BLOCKED));
        } else {
            if (!$firehose) {
                // update
                $r = q("SELECT item.parent AS item_id FROM item\n\t\t\t\t\tleft join abook on item.author_xchan = abook.abook_xchan\n\t\t\t\t\tWHERE true {$uids} AND item.item_restrict = 0 {$simple_update}\n\t\t\t\t\tand ((abook.abook_flags & %d) = 0 or abook.abook_flags is null)\n\t\t\t\t\t{$sql_extra3} {$sql_extra} {$sql_nets} ", intval(ABOOK_FLAG_BLOCKED));
            }
        }
        // Then fetch all the children of the parents that are on this page
        $parents_str = '';
        $update_unseen = '';
        if ($r) {
            $parents_str = ids_to_querystr($r, 'item_id');
            $items = q("SELECT `item`.*, `item`.`id` AS `item_id` FROM `item` \n\t\t\t\tWHERE true {$uids} AND `item`.`item_restrict` = 0\n\t\t\t\tAND `item`.`parent` IN ( %s )\n\t\t\t\t{$sql_extra} ", dbesc($parents_str));
            xchan_query($items);
            $items = fetch_post_tags($items, true);
            $items = conv_sort($items, $ordering);
        } else {
            $items = array();
        }
        if ($parents_str) {
            $update_unseen = ' AND parent IN ( ' . dbesc($parents_str) . ' )';
        }
    }
    if ($update_unseen && !$firehose) {
        $r = q("UPDATE `item` SET item_flags = ( item_flags ^ %d)\n\t\t\tWHERE (item_flags & %d) AND `uid` = %d {$update_unseen} ", intval(ITEM_UNSEEN), intval(ITEM_UNSEEN), intval(local_user()));
    }
    $mode = $nouveau ? 'network-new' : 'network';
    $o .= conversation($a, $items, $mode, $update, 'client');
    if ($items && !$update) {
        $o .= alt_pager($a, count($items));
    }
    return $o;
}