Example #1
0
function dirstats_content(&$a)
{
    $hubcount = get_config('dirstats', 'hubcount');
    $zotcount = get_config('dirstats', 'zotcount');
    $friendicacount = get_config('dirstats', 'friendicacount');
    $diasporacount = get_config('dirstats', 'diasporacount');
    $channelcount = get_config('dirstats', 'channelcount');
    $friendicachannelcount = get_config('dirstats', 'friendicachannelcount');
    $diasporachannelcount = get_config('dirstats', 'diasporachannelcount');
    $over35s = get_config('dirstats', 'over35s');
    $under35s = get_config('dirstats', 'under35s');
    $average = get_config('dirstats', 'averageage');
    $chatrooms = get_config('dirstats', 'chatrooms');
    $tags = get_config('dirstats', 'tags');
    $ob = $a->get_observer();
    $observer = $ob['xchan_hash'];
    // Requested by Martin
    $fountainofyouth = get_xconfig($observer, 'dirstats', 'averageage');
    if (intval($fountainofyouth)) {
        $average = $fountainofyouth;
    }
    if (argv(1) == 'json') {
        $dirstats = array('hubcount' => $hubcount, 'zotcount' => $zotcount, 'friendicacount' => $friendicacount, 'diasporacount' => $diasporacount, 'channelcount' => $channelcount, 'friendicachannelcount' => $friendicachannelcount, 'diasporachannelcount' => $diasporachannelcount, 'over35s' => $over35s, 'under35s' => $under35s, 'average' => $average, 'chatrooms' => $chatrooms, 'tags' => $tags);
        echo json_return_and_die($dirstats);
    } elseif (argv(1) == 'genpost' && get_config('dirstats', 'allowfiledump')) {
        $result = '[b]Hub count[/b] : ' . $hubcount . "\n" . '[b]Hubzilla Hubs[/b] : ' . $zotcount . "\n" . '[b]Friendica Hubs[/b] : ' . $friendicacount . "\n" . '[b]Diaspora Pods[/b] : ' . $diasporacount . "\n" . '[b]Hubzilla Channels[/b] : ' . $channelcount . "\n" . '[b]Friendica Profiles[/b] : ' . $friendicachannelcount . "\n" . '[b]Diaspora Profiles[/b] : ' . $diasporachannelcount . "\n" . '[b]People aged 35 and above[/b] : ' . $over35s . "\n" . '[b]People aged 34 and below[/b] : ' . $under35s . "\n" . '[b]Average Age[/b] : ' . $average . "\n" . '[b]Known Chatrooms[/b] : ' . $chatrooms . "\n" . '[b]Unique Profile Tags[/b] : ' . $tags . "\n";
        file_put_contents('genpost', $result);
    } else {
        $tpl = get_markup_template("dirstats.tpl", "addon/dirstats/");
        return replace_macros($tpl, array('$title' => t('Hubzilla Directory Stats'), '$hubtitle' => t('Total Hubs'), '$hubcount' => $hubcount, '$zotlabel' => t('Hubzilla Hubs'), '$zotcount' => $zotcount, '$friendicalabel' => t('Friendica Hubs'), '$friendicacount' => $friendicacount, '$diasporalabel' => t('Diaspora Pods'), '$diasporacount' => $diasporacount, '$zotchanlabel' => t('Hubzilla Channels'), '$channelcount' => $channelcount, '$friendicachanlabel' => t('Friendica Channels'), '$friendicachannelcount' => $friendicachannelcount, '$diasporachanlabel' => t('Diaspora Channels'), '$diasporachannelcount' => $diasporachannelcount, '$over35label' => t('Aged 35 and above'), '$over35s' => $over35s, '$under35label' => t('Aged 34 and under'), '$under35s' => $under35s, '$averageagelabel' => t('Average Age'), '$average' => $average, '$chatlabel' => t('Known Chatrooms'), '$chatrooms' => $chatrooms, '$tagslabel' => t('Known Tags'), '$tags' => $tags, '$disclaimer' => t('Please note Diaspora and Friendica statistics are merely those **this directory** is aware of, and not all those known in the network.  This also applies to chatrooms,')));
    }
}
Example #2
0
function gnusoc_follow_local(&$a, &$b)
{
    require_once 'addon/pubsubhubbub/pubsubhubbub.php';
    if ($b['abook']['abook_xchan'] && $b['abook']['xchan_network'] === 'gnusoc') {
        $hubs = get_xconfig($b['abook']['abook_xchan'], 'system', 'push_hubs');
        if ($hubs) {
            foreach ($hubs as $hub) {
                pubsubhubbub_subscribe($hub, $b['channel'], $b['abook'], '', $hubmode = 'subscribe');
            }
        }
    }
}
Example #3
0
function get_directory_setting($observer, $setting)
{
    if ($observer) {
        $ret = get_xconfig($observer, 'directory', $setting);
    } else {
        $ret = array_key_exists($setting, $_SESSION) ? intval($_SESSION[$setting]) : false;
    }
    if ($ret === false) {
        $ret = get_config('directory', $setting);
    }
    if ($setting == 'safemode' && $ret === false) {
        $ret = 1;
    }
    return $ret;
}
Example #4
0
function get_safemode_setting($observer)
{
    if ($observer) {
        $safe_mode = get_xconfig($observer, 'directory', 'safe_mode');
    } else {
        $safe_mode = array_key_exists('safemode', $_SESSION) ? intval($_SESSION['safemode']) : false;
    }
    if ($safe_mode === false) {
        $safe_mode = get_config('directory', 'safe_mode');
    }
    if ($safe_mode === false) {
        $safe_mode = 1;
    }
    return $safe_mode;
}
Example #5
0
function get_directory_setting($observer, $setting)
{
    if ($observer) {
        $ret = get_xconfig($observer, 'directory', $setting);
    } else {
        $ret = array_key_exists($setting, $_SESSION) ? intval($_SESSION[$setting]) : false;
    }
    if ($ret === false) {
        $ret = get_config('directory', $setting);
    }
    // 'safemode' is the default if there is no observer or no established preference.
    if ($setting == 'safemode' && $ret === false) {
        $ret = 1;
    }
    return $ret;
}
Example #6
0
function dir_safe_mode()
{
    $observer = get_observer_hash();
    if (!$observer) {
        return;
    }
    if ($observer) {
        $safe_mode = get_xconfig($observer, 'directory', 'safe_mode');
    }
    if ($safe_mode === '0') {
        $toggle = t('Enable Safe Search');
    } else {
        $toggle = t('Disable Safe Search');
    }
    $o = replace_macros(get_markup_template('safesearch.tpl'), array('$safemode' => t('Safe Mode'), '$toggle' => $toggle));
    return $o;
}
Example #7
0
 function init()
 {
     $observer = get_observer_hash();
     if (!$observer) {
         return;
     }
     if ($observer) {
         $safe_mode = get_xconfig($observer, 'directory', 'safe_mode');
     }
     if ($safe_mode == '') {
         set_xconfig($observer, 'directory', 'safe_mode', '0');
     } elseif ($safe_mode == '0') {
         set_xconfig($observer, 'directory', 'safe_mode', '1');
     } elseif ($safe_mode == '1') {
         set_xconfig($observer, 'directory', 'safe_mode', '0');
     }
     if (isset($_GET['address'])) {
         $address = $_GET['address'];
     } else {
         $address = z_root() . '/directory';
     }
     goaway($address);
 }
Example #8
0
function notifier_run($argv, $argc)
{
    cli_startup();
    $a = get_app();
    if ($argc < 3) {
        return;
    }
    logger('notifier: invoked: ' . print_r($argv, true), LOGGER_DEBUG);
    $cmd = $argv[1];
    $item_id = $argv[2];
    $extra = $argc > 3 ? $argv[3] : null;
    if (!$item_id) {
        return;
    }
    $sys = get_sys_channel();
    $deliveries = array();
    $dead_hubs = array();
    $dh = q("select site_url from site where site_dead = 1");
    if ($dh) {
        foreach ($dh as $dead) {
            $dead_hubs[] = $dead['site_url'];
        }
    }
    $request = false;
    $mail = false;
    $top_level = false;
    $location = false;
    $recipients = array();
    $url_recipients = array();
    $normal_mode = true;
    $packet_type = 'undefined';
    if ($cmd === 'mail') {
        $normal_mode = false;
        $mail = true;
        $private = true;
        $message = q("SELECT * FROM `mail` WHERE `id` = %d LIMIT 1", intval($item_id));
        if (!$message) {
            return;
        }
        xchan_mail_query($message[0]);
        $uid = $message[0]['channel_id'];
        $recipients[] = $message[0]['from_xchan'];
        // include clones
        $recipients[] = $message[0]['to_xchan'];
        $item = $message[0];
        $encoded_item = encode_mail($item);
        $s = q("select * from channel where channel_id = %d limit 1", intval($item['channel_id']));
        if ($s) {
            $channel = $s[0];
        }
    } elseif ($cmd === 'request') {
        $channel_id = $item_id;
        $xchan = $argv[3];
        $request_message_id = $argv[4];
        $s = q("select * from channel where channel_id = %d limit 1", intval($channel_id));
        if ($s) {
            $channel = $s[0];
        }
        $private = true;
        $recipients[] = $xchan;
        $packet_type = 'request';
        $normal_mode = false;
    } elseif ($cmd == 'permission_update' || $cmd == 'permission_create') {
        // Get the (single) recipient
        $r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_self = 0", intval($item_id));
        if ($r) {
            $uid = $r[0]['abook_channel'];
            // Get the sender
            $channel = channelx_by_n($uid);
            if ($channel) {
                $perm_update = array('sender' => $channel, 'recipient' => $r[0], 'success' => false, 'deliveries' => '');
                if ($cmd == 'permission_create') {
                    call_hooks('permissions_create', $perm_update);
                } else {
                    call_hooks('permissions_update', $perm_update);
                }
                if ($perm_update['success']) {
                    if ($perm_update['deliveries']) {
                        $deliveries[] = $perm_update['deliveries'];
                        do_delivery($deliveries);
                    }
                    return;
                } else {
                    $recipients[] = $r[0]['abook_xchan'];
                    $private = false;
                    $packet_type = 'refresh';
                    $packet_recips = array(array('guid' => $r[0]['xchan_guid'], 'guid_sig' => $r[0]['xchan_guid_sig'], 'hash' => $r[0]['xchan_hash']));
                }
            }
        }
    } elseif ($cmd === 'refresh_all') {
        logger('notifier: refresh_all: ' . $item_id);
        $uid = $item_id;
        $channel = channelx_by_n($item_id);
        $r = q("select abook_xchan from abook where abook_channel = %d", intval($item_id));
        if ($r) {
            foreach ($r as $rr) {
                $recipients[] = $rr['abook_xchan'];
            }
        }
        $private = false;
        $packet_type = 'refresh';
    } elseif ($cmd === 'location') {
        logger('notifier: location: ' . $item_id);
        $s = q("select * from channel where channel_id = %d limit 1", intval($item_id));
        if ($s) {
            $channel = $s[0];
        }
        $uid = $item_id;
        $recipients = array();
        $r = q("select abook_xchan from abook where abook_channel = %d", intval($item_id));
        if ($r) {
            foreach ($r as $rr) {
                $recipients[] = $rr['abook_xchan'];
            }
        }
        $encoded_item = array('locations' => zot_encode_locations($channel), 'type' => 'location', 'encoding' => 'zot');
        $target_item = array('aid' => $channel['channel_account_id'], 'uid' => $channel['channel_id']);
        $private = false;
        $packet_type = 'location';
        $location = true;
    } elseif ($cmd === 'purge_all') {
        logger('notifier: purge_all: ' . $item_id);
        $s = q("select * from channel where channel_id = %d limit 1", intval($item_id));
        if ($s) {
            $channel = $s[0];
        }
        $uid = $item_id;
        $recipients = array();
        $r = q("select abook_xchan from abook where abook_channel = %d", intval($item_id));
        if ($r) {
            foreach ($r as $rr) {
                $recipients[] = $rr['abook_xchan'];
            }
        }
        $private = false;
        $packet_type = 'purge';
    } else {
        // Normal items
        // Fetch the target item
        $r = q("SELECT * FROM item WHERE id = %d and parent != 0 LIMIT 1", intval($item_id));
        if (!$r) {
            return;
        }
        xchan_query($r);
        $r = fetch_post_tags($r);
        $target_item = $r[0];
        $deleted_item = false;
        if (intval($target_item['item_deleted'])) {
            logger('notifier: target item ITEM_DELETED', LOGGER_DEBUG);
            $deleted_item = true;
        }
        if (intval($target_item['item_type']) != ITEM_TYPE_POST) {
            logger('notifier: target item not forwardable: type ' . $target_item['item_type'], LOGGER_DEBUG);
            return;
        }
        if (intval($target_item['item_unpublished']) || intval($target_item['item_delayed'])) {
            logger('notifier: target item not published, so not forwardable', LOGGER_DEBUG);
            return;
        }
        if (strpos($target_item['postopts'], 'nodeliver') !== false) {
            logger('notifier: target item is undeliverable', LOGGER_DEBUG);
            return;
        }
        $s = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1", intval($target_item['uid']));
        if ($s) {
            $channel = $s[0];
        }
        if ($channel['channel_hash'] !== $target_item['author_xchan'] && $channel['channel_hash'] !== $target_item['owner_xchan']) {
            logger("notifier: Sending channel {$channel['channel_hash']} is not owner {$target_item['owner_xchan']} or author {$target_item['author_xchan']}", LOGGER_NORMAL, LOG_WARNING);
            return;
        }
        if ($target_item['id'] == $target_item['parent']) {
            $parent_item = $target_item;
            $top_level_post = true;
        } else {
            // fetch the parent item
            $r = q("SELECT * from item where id = %d order by id asc", intval($target_item['parent']));
            if (!$r) {
                return;
            }
            if (strpos($r[0]['postopts'], 'nodeliver') !== false) {
                logger('notifier: target item is undeliverable', LOGGER_DEBUG, LOG_NOTICE);
                return;
            }
            xchan_query($r);
            $r = fetch_post_tags($r);
            $parent_item = $r[0];
            $top_level_post = false;
        }
        // avoid looping of discover items 12/4/2014
        if ($sys && $parent_item['uid'] == $sys['channel_id']) {
            return;
        }
        $encoded_item = encode_item($target_item);
        // Send comments to the owner to re-deliver to everybody in the conversation
        // We only do this if the item in question originated on this site. This prevents looping.
        // To clarify, a site accepting a new comment is responsible for sending it to the owner for relay.
        // Relaying should never be initiated on a post that arrived from elsewhere.
        // We should normally be able to rely on ITEM_ORIGIN, but start_delivery_chain() incorrectly set this
        // flag on comments for an extended period. So we'll also call comment_local_origin() which looks at
        // the hostname in the message_id and provides a second (fallback) opinion.
        $relay_to_owner = !$top_level_post && intval($target_item['item_origin']) && comment_local_origin($target_item) ? true : false;
        $uplink = false;
        // $cmd === 'relay' indicates the owner is sending it to the original recipients
        // don't allow the item in the relay command to relay to owner under any circumstances, it will loop
        logger('notifier: relay_to_owner: ' . ($relay_to_owner ? 'true' : 'false'), LOGGER_DATA, LOG_DEBUG);
        logger('notifier: top_level_post: ' . ($top_level_post ? 'true' : 'false'), LOGGER_DATA, LOG_DEBUG);
        // tag_deliver'd post which needs to be sent back to the original author
        if ($cmd === 'uplink' && intval($parent_item['item_uplink']) && !$top_level_post) {
            logger('notifier: uplink');
            $uplink = true;
        }
        if (($relay_to_owner || $uplink) && $cmd !== 'relay') {
            logger('notifier: followup relay', LOGGER_DEBUG);
            $recipients = array($uplink ? $parent_item['source_xchan'] : $parent_item['owner_xchan']);
            $private = true;
            if (!$encoded_item['flags']) {
                $encoded_item['flags'] = array();
            }
            $encoded_item['flags'][] = 'relay';
        } else {
            logger('notifier: normal distribution', LOGGER_DEBUG);
            if ($cmd === 'relay') {
                logger('notifier: owner relay');
            }
            // if our parent is a tag_delivery recipient, uplink to the original author causing
            // a delivery fork.
            if ($parent_item && intval($parent_item['item_uplink']) && !$top_level_post && $cmd !== 'uplink') {
                // don't uplink a relayed post to the relay owner
                if ($parent_item['source_xchan'] !== $parent_item['owner_xchan']) {
                    logger('notifier: uplinking this item');
                    proc_run('php', 'include/notifier.php', 'uplink', $item_id);
                }
            }
            $private = false;
            $recipients = collect_recipients($parent_item, $private);
            // FIXME add any additional recipients such as mentions, etc.
            // don't send deletions onward for other people's stuff
            // TODO verify this is needed - copied logic from same place in old code
            if (intval($target_item['item_deleted']) && !intval($target_item['item_wall'])) {
                logger('notifier: ignoring delete notification for non-wall item', LOGGER_NORMAL, LOG_NOTICE);
                return;
            }
        }
    }
    $walltowall = $top_level_post && $channel['xchan_hash'] === $target_item['author_xchan'] ? true : false;
    // Generic delivery section, we have an encoded item and recipients
    // Now start the delivery process
    $x = $encoded_item;
    $x['title'] = 'private';
    $x['body'] = 'private';
    logger('notifier: encoded item: ' . print_r($x, true), LOGGER_DATA, LOG_DEBUG);
    stringify_array_elms($recipients);
    if (!$recipients) {
        return;
    }
    //	logger('notifier: recipients: ' . print_r($recipients,true), LOGGER_NORMAL, LOG_DEBUG);
    $env_recips = $private ? array() : null;
    $details = q("select xchan_hash, xchan_instance_url, xchan_network, xchan_addr, xchan_guid, xchan_guid_sig from xchan where xchan_hash in (" . implode(',', $recipients) . ")");
    $recip_list = array();
    if ($details) {
        foreach ($details as $d) {
            $recip_list[] = $d['xchan_addr'] . ' (' . $d['xchan_hash'] . ')';
            if ($private) {
                $env_recips[] = array('guid' => $d['xchan_guid'], 'guid_sig' => $d['xchan_guid_sig'], 'hash' => $d['xchan_hash']);
            }
            if ($d['xchan_network'] === 'mail' && $normal_mode) {
                $delivery_options = get_xconfig($d['xchan_hash'], 'system', 'delivery_mode');
                if (!$delivery_options) {
                    format_and_send_email($channel, $d, $target_item);
                }
            }
        }
    }
    $narr = array('channel' => $channel, 'env_recips' => $env_recips, 'packet_recips' => $packet_recips, 'recipients' => $recipients, 'item' => $item, 'target_item' => $target_item, 'top_level_post' => $top_level_post, 'private' => $private, 'followup' => $followup, 'relay_to_owner' => $relay_to_owner, 'uplink' => $uplink, 'cmd' => $cmd, 'mail' => $mail, 'location' => $location, 'request' => $request, 'normal_mode' => $normal_mode, 'packet_type' => $packet_type, 'walltowall' => $walltowall, 'queued' => array());
    call_hooks('notifier_process', $narr);
    if ($narr['queued']) {
        foreach ($narr['queued'] as $pq) {
            $deliveries[] = $pq;
        }
    }
    if ($private && !$env_recips) {
        // shouldn't happen
        logger('notifier: private message with no envelope recipients.' . print_r($argv, true), LOGGER_NORMAL, LOG_NOTICE);
    }
    logger('notifier: recipients (may be delivered to more if public): ' . print_r($recip_list, true), LOGGER_DEBUG);
    // Now we have collected recipients (except for external mentions, FIXME)
    // Let's reduce this to a set of hubs.
    $r = q("select * from hubloc where hubloc_hash in (" . implode(',', $recipients) . ") \n\t\tand hubloc_error = 0 and hubloc_deleted = 0");
    if (!$r) {
        logger('notifier: no hubs', LOGGER_NORMAL, LOG_NOTICE);
        return;
    }
    $hubs = $r;
    /**
     * Reduce the hubs to those that are unique. For zot hubs, we need to verify uniqueness by the sitekey, since it may have been 
     * a re-install which has not yet been detected and pruned.
     * For other networks which don't have or require sitekeys, we'll have to use the URL
     */
    $hublist = array();
    // this provides an easily printable list for the logs
    $dhubs = array();
    // delivery hubs where we store our resulting unique array
    $keys = array();
    // array of keys to check uniquness for zot hubs
    $urls = array();
    // array of urls to check uniqueness of hubs from other networks
    foreach ($hubs as $hub) {
        if (in_array($hub['hubloc_url'], $dead_hubs)) {
            logger('skipping dead hub: ' . $hub['hubloc_url'], LOGGER_DEBUG, LOG_INFO);
            continue;
        }
        if ($hub['hubloc_network'] == 'zot') {
            if (!in_array($hub['hubloc_sitekey'], $keys)) {
                $hublist[] = $hub['hubloc_host'];
                $dhubs[] = $hub;
                $keys[] = $hub['hubloc_sitekey'];
            }
        } else {
            if (!in_array($hub['hubloc_url'], $urls)) {
                $hublist[] = $hub['hubloc_host'];
                $dhubs[] = $hub;
                $urls[] = $hub['hubloc_url'];
            }
        }
    }
    logger('notifier: will notify/deliver to these hubs: ' . print_r($hublist, true), LOGGER_DEBUG, LOG_DEBUG);
    foreach ($dhubs as $hub) {
        if ($hub['hubloc_network'] !== 'zot') {
            $narr = array('channel' => $channel, 'env_recips' => $env_recips, 'packet_recips' => $packet_recips, 'recipients' => $recipients, 'item' => $item, 'target_item' => $target_item, 'hub' => $hub, 'top_level_post' => $top_level_post, 'private' => $private, 'followup' => $followup, 'relay_to_owner' => $relay_to_owner, 'uplink' => $uplink, 'cmd' => $cmd, 'mail' => $mail, 'location' => $location, 'request' => $request, 'normal_mode' => $normal_mode, 'packet_type' => $packet_type, 'walltowall' => $walltowall, 'queued' => array());
            call_hooks('notifier_hub', $narr);
            if ($narr['queued']) {
                foreach ($narr['queued'] as $pq) {
                    $deliveries[] = $pq;
                }
            }
            continue;
        }
        // default: zot protocol
        $hash = random_string();
        $packet = null;
        if ($packet_type === 'refresh' || $packet_type === 'purge') {
            $packet = zot_build_packet($channel, $packet_type, $packet_recips ? $packet_recips : null);
        } elseif ($packet_type === 'request') {
            $packet = zot_build_packet($channel, $packet_type, $env_recips, $hub['hubloc_sitekey'], $hash, array('message_id' => $request_message_id));
        }
        if ($packet) {
            queue_insert(array('hash' => $hash, 'account_id' => $channel['channel_account_id'], 'channel_id' => $channel['channel_id'], 'posturl' => $hub['hubloc_callback'], 'notify' => $packet));
        } else {
            $packet = zot_build_packet($channel, 'notify', $env_recips, $private ? $hub['hubloc_sitekey'] : null, $hash);
            queue_insert(array('hash' => $hash, 'account_id' => $target_item['aid'], 'channel_id' => $target_item['uid'], 'posturl' => $hub['hubloc_callback'], 'notify' => $packet, 'msg' => json_encode($encoded_item)));
            // only create delivery reports for normal undeleted items
            if (is_array($target_item) && array_key_exists('postopts', $target_item) && !$target_item['item_deleted'] && !get_config('system', 'disable_dreport')) {
                q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_result, dreport_time, dreport_xchan, dreport_queue ) values ( '%s','%s','%s','%s','%s','%s','%s' ) ", dbesc($target_item['mid']), dbesc($hub['hubloc_host']), dbesc($hub['hubloc_host']), dbesc('queued'), dbesc(datetime_convert()), dbesc($channel['channel_hash']), dbesc($hash));
            }
        }
        $deliveries[] = $hash;
    }
    if ($normal_mode) {
        $x = q("select * from hook where hook = 'notifier_normal'");
        if ($x) {
            proc_run('php', 'include/deliver_hooks.php', $target_item['id']);
        }
    }
    if ($deliveries) {
        do_delivery($deliveries);
    }
    logger('notifier: basic loop complete.', LOGGER_DEBUG);
    call_hooks('notifier_end', $target_item);
    logger('notifer: complete.');
    return;
}
function pubsubhubbub_subscribe($url, $channel, $xchan, $feed, $hubmode = 'subscribe')
{
    $push_url = z_root() . '/pubsub/' . $channel['channel_address'] . '/' . $xchan['abook_id'];
    $verify = get_abconfig($channel['channel_id'], $xchan['xchan_hash'], 'pubsubhubbub', 'verify_token');
    if (!$verify) {
        $verify = set_abconfig($channel['channel_id'], $xchan['xchan_hash'], 'pubsubhubbub', 'verify_token', random_string(16));
    }
    if ($feed) {
        set_xconfig($xchan['xchan_hash'], 'system', 'feed_url', $feed);
    } else {
        $feed = get_xconfig($xchan['xchan_hash'], 'system', 'feed_url');
    }
    $params = 'hub.mode=' . $hubmode . '&hub.callback=' . urlencode($push_url) . '&hub.topic=' . urlencode($feed) . '&hub.verify=async&hub.verify_token=' . $verify;
    logger('subscribe_to_hub: ' . $hubmode . ' ' . $xchan['xchan_name'] . ' to hub ' . $url . ' endpoint: ' . $push_url . ' with verifier ' . $verify);
    $x = z_post_url($url, $params);
    logger('subscribe_to_hub: returns: ' . $x['return_code'], LOGGER_DEBUG);
    return;
}
Example #10
0
function get_aconfig($account_id, $family, $key)
{
    return get_xconfig('a_' . $account_id, $family, $key);
}
Example #11
0
function chess_game_settings()
{
    $observer = App::get_observer();
    $notifications = get_xconfig($observer['xchan_hash'], 'chess', 'notifications');
    $sc .= replace_macros(get_markup_template('field_checkbox.tpl'), array('$field' => array('chess_notify_enable', t('Enable notifications'), $notifications, '', $yes_no)));
    return $sc;
}
Example #12
0
File: config.php Project: Mauru/red
function set_xconfig($xchan, $family, $key, $value)
{
    global $a;
    // manage array value
    $dbvalue = is_array($value) ? serialize($value) : $value;
    $dbvalue = is_bool($dbvalue) ? intval($dbvalue) : $dbvalue;
    if (get_xconfig($xchan, $family, $key) === false) {
        if (!array_key_exists($xchan, $a->config)) {
            $a->config[$xchan] = array();
        }
        if (!array_key_exists($family, $a->config[$xchan])) {
            $a->config[$xchan][$family] = array();
        }
        $a->config[$xchan][$family][$key] = $value;
        $ret = q("INSERT INTO xconfig ( xchan, cat, k, v ) VALUES ( '%s', '%s', '%s', '%s' ) ", dbesc($xchan), dbesc($family), dbesc($key), dbesc($dbvalue));
        if ($ret) {
            return $value;
        }
        return $ret;
    }
    $ret = q("UPDATE xconfig SET v = '%s' WHERE xchan = '%s' and cat = '%s' AND k = '%s' LIMIT 1", dbesc($dbvalue), dbesc($xchan), dbesc($family), dbesc($key));
    $a->config[$xchan][$family][$key] = $value;
    if ($ret) {
        return $value;
    }
    return $ret;
}
Example #13
0
function notifier_run($argv, $argc)
{
    cli_startup();
    $a = get_app();
    require_once "session.php";
    require_once "datetime.php";
    require_once 'include/items.php';
    require_once 'include/bbcode.php';
    if ($argc < 3) {
        return;
    }
    logger('notifier: invoked: ' . print_r($argv, true), LOGGER_DEBUG);
    $cmd = $argv[1];
    $item_id = $argv[2];
    $extra = $argc > 3 ? $argv[3] : null;
    if (!$item_id) {
        return;
    }
    require_once 'include/identity.php';
    $sys = get_sys_channel();
    if ($cmd == 'permission_update') {
        // Get the recipient
        $r = q("select abook.*, hubloc.* from abook \n\t\t\tleft join hubloc on hubloc_hash = abook_xchan\n\t\t\twhere abook_id = %d and not ( abook_flags & %d ) > 0 \n\t\t\tand not (hubloc_flags & %d) > 0  and not (hubloc_status & %d) > 0 limit 1", intval($item_id), intval(ABOOK_FLAG_SELF), intval(HUBLOC_FLAGS_DELETED), intval(HUBLOC_OFFLINE));
        if ($r) {
            // Get the sender
            $s = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1", intval($r[0]['abook_channel']));
            if ($s) {
                if ($r[0]['hubloc_network'] === 'diaspora' || $r[0]['hubloc_network'] === 'friendica-over-diaspora') {
                    require_once 'include/diaspora.php';
                    diaspora_share($s[0], $r[0]);
                } else {
                    // send a refresh message to each hub they have registered here
                    $h = q("select * from hubloc where hubloc_hash = '%s' \n\t\t\t\t\t\tand not (hubloc_flags & %d) > 0  and not (hubloc_status & %d) > 0", dbesc($r[0]['hubloc_hash']), intval(HUBLOC_FLAGS_DELETED), intval(HUBLOC_OFFLINE));
                    if ($h) {
                        foreach ($h as $hh) {
                            $data = zot_build_packet($s[0], 'refresh', array(array('guid' => $hh['hubloc_guid'], 'guid_sig' => $hh['hubloc_guid_sig'], 'url' => $hh['hubloc_url'])));
                            if ($data) {
                                $result = zot_zot($hh['hubloc_callback'], $data);
                                // if immediate delivery failed, stick it in the queue to try again later.
                                if (!$result['success']) {
                                    $hash = random_string();
                                    q("insert into outq ( outq_hash, outq_account, outq_channel, outq_driver, outq_posturl, outq_async, outq_created, outq_updated, outq_notify, outq_msg ) \n\t\t\t\t\t\t\t\t\t\tvalues ( '%s', %d, %d, '%s', '%s', %d, '%s', '%s', '%s', '%s' )", dbesc($hash), intval($s[0]['channel_account_id']), intval($s[0]['channel_id']), dbesc('zot'), dbesc($hh['hubloc_callback']), intval(1), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($data), dbesc(''));
                                }
                            }
                        }
                    }
                }
            }
        }
        return;
    }
    $expire = false;
    $request = false;
    $mail = false;
    $fsuggest = false;
    $top_level = false;
    $location = false;
    $recipients = array();
    $url_recipients = array();
    $normal_mode = true;
    $packet_type = 'undefined';
    if ($cmd === 'mail') {
        $normal_mode = false;
        $mail = true;
        $private = true;
        $message = q("SELECT * FROM `mail` WHERE `id` = %d LIMIT 1", intval($item_id));
        if (!$message) {
            return;
        }
        xchan_mail_query($message[0]);
        $uid = $message[0]['channel_id'];
        $recipients[] = $message[0]['from_xchan'];
        // include clones
        $recipients[] = $message[0]['to_xchan'];
        $item = $message[0];
        $encoded_item = encode_mail($item);
        $s = q("select * from channel where channel_id = %d limit 1", intval($item['channel_id']));
        if ($s) {
            $channel = $s[0];
        }
    } elseif ($cmd === 'request') {
        $channel_id = $item_id;
        $xchan = $argv[3];
        $request_message_id = $argv[4];
        $s = q("select * from channel where channel_id = %d limit 1", intval($channel_id));
        if ($s) {
            $channel = $s[0];
        }
        $private = true;
        $recipients[] = $xchan;
        $packet_type = 'request';
        $normal_mode = false;
    } elseif ($cmd === 'expire') {
        // FIXME
        // This will require a special zot packet containing a list of item message_id's to be expired.
        // This packet will be public, since we cannot selectively deliver here.
        // We need the handling on this end to create the array, and the handling on the remote end
        // to verify permissions (for each item) and process it. Until this is complete, the expire feature will be disabled.
        return;
        $normal_mode = false;
        $expire = true;
        $items = q("SELECT * FROM item WHERE uid = %d AND ( item_flags & %d )>0\n\t\t\tAND ( item_restrict & %d )>0 AND `changed` > %s - INTERVAL %s", intval($item_id), intval(ITEM_WALL), intval(ITEM_DELETED), db_utcnow(), db_quoteinterval('10 MINUTE'));
        $uid = $item_id;
        $item_id = 0;
        if (!$items) {
            return;
        }
    } elseif ($cmd === 'suggest') {
        $normal_mode = false;
        $fsuggest = true;
        $suggest = q("SELECT * FROM `fsuggest` WHERE `id` = %d LIMIT 1", intval($item_id));
        if (!count($suggest)) {
            return;
        }
        $uid = $suggest[0]['uid'];
        $recipients[] = $suggest[0]['cid'];
        $item = $suggest[0];
    } elseif ($cmd === 'refresh_all') {
        logger('notifier: refresh_all: ' . $item_id);
        $s = q("select * from channel where channel_id = %d limit 1", intval($item_id));
        if ($s) {
            $channel = $s[0];
        }
        $uid = $item_id;
        $recipients = array();
        $r = q("select abook_xchan from abook where abook_channel = %d", intval($item_id));
        if ($r) {
            foreach ($r as $rr) {
                $recipients[] = $rr['abook_xchan'];
            }
        }
        $private = false;
        $packet_type = 'refresh';
    } elseif ($cmd === 'location') {
        logger('notifier: location: ' . $item_id);
        $s = q("select * from channel where channel_id = %d limit 1", intval($item_id));
        if ($s) {
            $channel = $s[0];
        }
        $uid = $item_id;
        $recipients = array();
        $r = q("select abook_xchan from abook where abook_channel = %d", intval($item_id));
        if ($r) {
            foreach ($r as $rr) {
                $recipients[] = $rr['abook_xchan'];
            }
        }
        $encoded_item = array('locations' => zot_encode_locations($channel), 'type' => 'location', 'encoding' => 'zot');
        $target_item = array('aid' => $channel['channel_account_id'], 'uid' => $channel['channel_id']);
        $private = false;
        $packet_type = 'location';
        $location = true;
    } elseif ($cmd === 'purge_all') {
        logger('notifier: purge_all: ' . $item_id);
        $s = q("select * from channel where channel_id = %d limit 1", intval($item_id));
        if ($s) {
            $channel = $s[0];
        }
        $uid = $item_id;
        $recipients = array();
        $r = q("select abook_xchan from abook where abook_channel = %d", intval($item_id));
        if ($r) {
            foreach ($r as $rr) {
                $recipients[] = $rr['abook_xchan'];
            }
        }
        $private = false;
        $packet_type = 'purge';
    } else {
        // Normal items
        // Fetch the target item
        $r = q("SELECT * FROM item WHERE id = %d and parent != 0 LIMIT 1", intval($item_id));
        if (!$r) {
            return;
        }
        xchan_query($r);
        $r = fetch_post_tags($r);
        $target_item = $r[0];
        $deleted_item = false;
        if ($target_item['item_restrict'] & ITEM_DELETED) {
            logger('notifier: target item ITEM_DELETED', LOGGER_DEBUG);
            $deleted_item = true;
        }
        if (strpos($target_item['postopts'], 'nodeliver') !== false) {
            logger('notifier: target item is undeliverable', LOGGER_DEBUG);
            return;
        }
        $unforwardable = ITEM_UNPUBLISHED | ITEM_DELAYED_PUBLISH | ITEM_WEBPAGE | ITEM_BUILDBLOCK | ITEM_PDL;
        if ($target_item['item_restrict'] & $unforwardable) {
            logger('notifier: target item not forwardable: flags ' . $target_item['item_restrict'], LOGGER_DEBUG);
            return;
        }
        $s = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1", intval($target_item['uid']));
        if ($s) {
            $channel = $s[0];
        }
        if ($channel['channel_hash'] !== $target_item['author_xchan'] && $channel['channel_hash'] !== $target_item['owner_xchan']) {
            logger("notifier: Sending channel {$channel['channel_hash']} is not owner {$target_item['owner_xchan']} or author {$target_item['author_xchan']}");
            return;
        }
        if ($target_item['id'] == $target_item['parent']) {
            $parent_item = $target_item;
            $top_level_post = true;
        } else {
            // fetch the parent item
            $r = q("SELECT * from item where id = %d order by id asc", intval($target_item['parent']));
            if (!$r) {
                return;
            }
            if (strpos($r[0]['postopts'], 'nodeliver') !== false) {
                logger('notifier: target item is undeliverable', LOGGER_DEBUG);
                return;
            }
            xchan_query($r);
            $r = fetch_post_tags($r);
            $parent_item = $r[0];
            $top_level_post = false;
        }
        // avoid looping of discover items 12/4/2014
        if ($sys && $parent_item['uid'] == $sys['channel_id']) {
            return;
        }
        $encoded_item = encode_item($target_item);
        // Send comments to the owner to re-deliver to everybody in the conversation
        // We only do this if the item in question originated on this site. This prevents looping.
        // To clarify, a site accepting a new comment is responsible for sending it to the owner for relay.
        // Relaying should never be initiated on a post that arrived from elsewhere.
        // We should normally be able to rely on ITEM_ORIGIN, but start_delivery_chain() incorrectly set this
        // flag on comments for an extended period. So we'll also call comment_local_origin() which looks at
        // the hostname in the message_id and provides a second (fallback) opinion.
        $relay_to_owner = !$top_level_post && $target_item['item_flags'] & ITEM_ORIGIN && comment_local_origin($target_item) ? true : false;
        $uplink = false;
        // $cmd === 'relay' indicates the owner is sending it to the original recipients
        // don't allow the item in the relay command to relay to owner under any circumstances, it will loop
        logger('notifier: relay_to_owner: ' . ($relay_to_owner ? 'true' : 'false'), LOGGER_DATA);
        logger('notifier: top_level_post: ' . ($top_level_post ? 'true' : 'false'), LOGGER_DATA);
        logger('notifier: target_item_flags: ' . $target_item['item_flags'] . ' ' . ($target_item['item_flags'] & ITEM_ORIGIN ? 'true' : 'false'), LOGGER_DATA);
        // tag_deliver'd post which needs to be sent back to the original author
        if ($cmd === 'uplink' && $parent_item['item_flags'] & ITEM_UPLINK && !$top_level_post) {
            logger('notifier: uplink');
            $uplink = true;
        }
        if (($relay_to_owner || $uplink) && $cmd !== 'relay') {
            logger('notifier: followup relay', LOGGER_DEBUG);
            $recipients = array($uplink ? $parent_item['source_xchan'] : $parent_item['owner_xchan']);
            $private = true;
            if (!$encoded_item['flags']) {
                $encoded_item['flags'] = array();
            }
            $encoded_item['flags'][] = 'relay';
        } else {
            logger('notifier: normal distribution', LOGGER_DEBUG);
            if ($cmd === 'relay') {
                logger('notifier: owner relay');
            }
            // if our parent is a tag_delivery recipient, uplink to the original author causing
            // a delivery fork.
            if ($parent_item['item_flags'] & ITEM_UPLINK && !$top_level_post && $cmd !== 'uplink') {
                logger('notifier: uplinking this item');
                proc_run('php', 'include/notifier.php', 'uplink', $item_id);
            }
            $private = false;
            $recipients = collect_recipients($parent_item, $private);
            // FIXME add any additional recipients such as mentions, etc.
            // don't send deletions onward for other people's stuff
            // TODO verify this is needed - copied logic from same place in old code
            if ($target_item['item_restrict'] & ITEM_DELETED && !($target_item['item_flags'] & ITEM_WALL)) {
                logger('notifier: ignoring delete notification for non-wall item');
                return;
            }
        }
    }
    $walltowall = $top_level_post && $channel['xchan_hash'] === $target_item['author_xchan'] ? true : false;
    // Generic delivery section, we have an encoded item and recipients
    // Now start the delivery process
    $x = $encoded_item;
    $x['title'] = 'private';
    $x['body'] = 'private';
    logger('notifier: encoded item: ' . print_r($x, true), LOGGER_DATA);
    stringify_array_elms($recipients);
    if (!$recipients) {
        return;
    }
    //	logger('notifier: recipients: ' . print_r($recipients,true));
    $env_recips = $private ? array() : null;
    $details = q("select xchan_hash, xchan_instance_url, xchan_network, xchan_addr, xchan_guid, xchan_guid_sig from xchan where xchan_hash in (" . implode(',', $recipients) . ")");
    $recip_list = array();
    if ($details) {
        foreach ($details as $d) {
            // If the recipient is federated from a traditional network they won't be able to
            // handle nomadic identity. If we're publishing from a site that they aren't
            // directly connected with, ignore them.
            // FIXME: make sure we run through a notifier loop on the hub they're connected
            // with if this post comes in from a different hub - so that we will deliver to them.
            // On the down side, these channels will stop working if the hub they connected with
            // goes down permanently, as they are (doh) not nomadic.
            if ($d['xchan_instance_url'] && $d['xchan_instance_url'] != z_root()) {
                continue;
            }
            $recip_list[] = $d['xchan_addr'] . ' (' . $d['xchan_hash'] . ')';
            if ($private) {
                $env_recips[] = array('guid' => $d['xchan_guid'], 'guid_sig' => $d['xchan_guid_sig'], 'hash' => $d['xchan_hash']);
            }
            if ($d['xchan_network'] === 'mail' && $normal_mode) {
                $delivery_options = get_xconfig($d['xchan_hash'], 'system', 'delivery_mode');
                if (!$delivery_options) {
                    format_and_send_email($channel, $d, $target_item);
                }
            }
        }
    }
    if ($private && !$env_recips) {
        // shouldn't happen
        logger('notifier: private message with no envelope recipients.' . print_r($argv, true));
    }
    logger('notifier: recipients (may be delivered to more if public): ' . print_r($recip_list, true), LOGGER_DEBUG);
    // Now we have collected recipients (except for external mentions, FIXME)
    // Let's reduce this to a set of hubs.
    logger('notifier: hub choice: ' . intval($relay_to_owner) . ' ' . intval($private) . ' ' . $cmd, LOGGER_DEBUG);
    // FIXME: I think we need to remove the private bit or this clause will never execute. Needs more coffee to think it through.
    // We may in fact have to send it to clones in case the one we pick recently died.
    if ($relay_to_owner && !$private && $cmd !== 'relay') {
        // If sending a followup to the post owner, only send it to one channel clone - to avoid race conditions.
        // In this case we'll pick the most recently contacted hub, as their primary might be down and the most
        // recently contacted has the best chance of being alive.
        // For private posts or uplinks we have to do things differently as only the sending clone will have the recipient list.
        // We have to send to all clone channels of the owner to find out who has the definitive list. Posts with
        // item_private set (but no ACL list) will return empty recipients (except for the sender and owner) in
        // collect_recipients() above. The end result is we should get only one delivery per delivery chain if we
        // aren't the owner or author.
        $r = q("select hubloc_guid, hubloc_url, hubloc_sitekey, hubloc_network, hubloc_flags, hubloc_callback, hubloc_host from hubloc \n\t\t\twhere hubloc_hash in (" . implode(',', $recipients) . ") order by hubloc_connected desc limit 1");
    } else {
        $r = q("select hubloc_guid, hubloc_url, hubloc_sitekey, hubloc_network, hubloc_flags, hubloc_callback, hubloc_host from hubloc \n\t\t\twhere hubloc_hash in (" . implode(',', $recipients) . ") and not (hubloc_flags & %d) > 0  and not (hubloc_status & %d) > 0", intval(HUBLOC_FLAGS_DELETED), intval(HUBLOC_OFFLINE));
    }
    if (!$r) {
        logger('notifier: no hubs');
        return;
    }
    $hubs = $r;
    /**
     * Reduce the hubs to those that are unique. For zot hubs, we need to verify uniqueness by the sitekey, since it may have been 
     * a re-install which has not yet been detected and pruned.
     * For other networks which don't have or require sitekeys, we'll have to use the URL
     */
    $hublist = array();
    // this provides an easily printable list for the logs
    $dhubs = array();
    // delivery hubs where we store our resulting unique array
    $keys = array();
    // array of keys to check uniquness for zot hubs
    $urls = array();
    // array of urls to check uniqueness of hubs from other networks
    foreach ($hubs as $hub) {
        if ($hub['hubloc_network'] == 'zot') {
            if (!in_array($hub['hubloc_sitekey'], $keys)) {
                $hublist[] = $hub['hubloc_host'];
                $dhubs[] = $hub;
                $keys[] = $hub['hubloc_sitekey'];
            }
        } else {
            if (!in_array($hub['hubloc_url'], $urls)) {
                $hublist[] = $hub['hubloc_host'];
                $dhubs[] = $hub;
                $urls[] = $hub['hubloc_url'];
            }
        }
    }
    logger('notifier: will notify/deliver to these hubs: ' . print_r($hublist, true), LOGGER_DEBUG);
    $interval = get_config('system', 'delivery_interval') !== false ? intval(get_config('system', 'delivery_interval')) : 2;
    $deliveries_per_process = intval(get_config('system', 'delivery_batch_count'));
    if ($deliveries_per_process <= 0) {
        $deliveries_per_process = 1;
    }
    $deliver = array();
    foreach ($dhubs as $hub) {
        if ($hub['hubloc_network'] === 'diaspora' || $hub['hubloc_network'] === 'friendica-over-diaspora') {
            if (!get_config('system', 'diaspora_enabled')) {
                continue;
            }
            // allow this to be set per message
            if (strpos($target_item['postopts'], 'nodspr') !== false) {
                continue;
            }
            require_once 'include/diaspora.php';
            diaspora_process_outbound(array('channel' => $channel, 'env_recips' => $env_recips, 'recipients' => $recipients, 'item' => $item, 'target_item' => $target_item, 'hub' => $hub, 'top_level_post' => $top_level_post, 'private' => $private, 'followup' => $followup, 'relay_to_owner' => $relay_to_owner, 'uplink' => $uplink, 'cmd' => $cmd, 'expire' => $expire, 'mail' => $mail, 'location' => $location, 'fsuggest' => $fsuggest, 'request' => $request, 'normal_mode' => $normal_mode, 'packet_type' => $packet_type, 'walltowall' => $walltowall));
            continue;
        }
        // default: zot protocol
        $hash = random_string();
        if ($packet_type === 'refresh' || $packet_type === 'purge') {
            $n = zot_build_packet($channel, $packet_type);
            q("insert into outq ( outq_hash, outq_account, outq_channel, outq_driver, outq_posturl, outq_async, outq_created, outq_updated, outq_notify, outq_msg ) values ( '%s', %d, %d, '%s', '%s', %d, '%s', '%s', '%s', '%s' )", dbesc($hash), intval($channel['channel_account_id']), intval($channel['channel_id']), dbesc('zot'), dbesc($hub['hubloc_callback']), intval(1), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($n), dbesc(''));
        } elseif ($packet_type === 'request') {
            $n = zot_build_packet($channel, 'request', $env_recips, $hub['hubloc_sitekey'], $hash, array('message_id' => $request_message_id));
            q("insert into outq ( outq_hash, outq_account, outq_channel, outq_driver, outq_posturl, outq_async, outq_created, outq_updated, outq_notify, outq_msg ) values ( '%s', %d, %d, '%s', '%s', %d, '%s', '%s', '%s', '%s' )", dbesc($hash), intval($channel['channel_account_id']), intval($channel['channel_id']), dbesc('zot'), dbesc($hub['hubloc_callback']), intval(1), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($n), dbesc(''));
        } else {
            $n = zot_build_packet($channel, 'notify', $env_recips, $private ? $hub['hubloc_sitekey'] : null, $hash);
            q("insert into outq ( outq_hash, outq_account, outq_channel, outq_driver, outq_posturl, outq_async, outq_created, outq_updated, outq_notify, outq_msg ) values ( '%s', %d, %d, '%s', '%s', %d, '%s', '%s', '%s', '%s' )", dbesc($hash), intval($target_item['aid']), intval($target_item['uid']), dbesc('zot'), dbesc($hub['hubloc_callback']), intval(1), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($n), dbesc(json_encode($encoded_item)));
        }
        $deliver[] = $hash;
        if (count($deliver) >= $deliveries_per_process) {
            proc_run('php', 'include/deliver.php', $deliver);
            $deliver = array();
            if ($interval) {
                @time_sleep_until(microtime(true) + (double) $interval);
            }
        }
    }
    // catch any stragglers
    if (count($deliver)) {
        proc_run('php', 'include/deliver.php', $deliver);
    }
    logger('notifier: basic loop complete.', LOGGER_DEBUG);
    if ($normal_mode) {
        call_hooks('notifier_normal', $target_item);
    }
    call_hooks('notifier_end', $target_item);
    logger('notifer: complete.');
    return;
}
Example #14
0
function directory_content(&$a)
{
    if (get_config('system', 'block_public') && !local_user() && !remote_user()) {
        notice(t('Public access denied.') . EOL);
        return;
    }
    $safe_mode = 1;
    $observer = get_observer_hash();
    if ($observer) {
        $safe_mode = get_xconfig($observer, 'directory', 'safe_mode');
    }
    if ($safe_mode === false) {
        $safe_mode = 1;
    } else {
        $safe_mode = intval($safe_mode);
    }
    if (x($_REQUEST, 'safe')) {
        $safe_mode = intval($_REQUEST['safe']);
    }
    $o = '';
    nav_set_selected('directory');
    if (x($_POST, 'search')) {
        $search = notags(trim($_POST['search']));
    } else {
        $search = x($_GET, 'search') ? notags(trim(rawurldecode($_GET['search']))) : '';
    }
    $advanced = x($_REQUEST, 'query') ? notags(trim($_REQUEST['query'])) : '';
    $keywords = $_GET['keywords'] ? $_GET['keywords'] : '';
    $tpl = get_markup_template('directory_header.tpl');
    $dirmode = intval(get_config('system', 'directory_mode'));
    if ($dirmode == DIRECTORY_MODE_PRIMARY || $dirmode == DIRECTORY_MODE_STANDALONE) {
        $url = z_root() . '/dirsearch';
    }
    if (!$url) {
        $directory = find_upstream_directory($dirmode);
        $url = $directory['url'] . '/dirsearch';
    }
    logger('mod_directory: URL = ' . $url, LOGGER_DEBUG);
    $contacts = array();
    if (local_user()) {
        $x = q("select abook_xchan from abook where abook_channel = %d", intval(local_user()));
        if ($x) {
            foreach ($x as $xx) {
                $contacts[] = $xx['abook_xchan'];
            }
        }
    }
    if ($url) {
        // We might want to make the tagadelic count (&kw=) configurable or turn it off completely.
        $numtags = get_config('system', 'directorytags');
        $kw = intval($numtags) ? $numtags : 24;
        $query = $url . '?f=&kw=' . $kw . ($safe_mode != 1 ? '&safe=' . $safe_mode : '');
        if ($search) {
            $query .= '&name=' . urlencode($search) . '&keywords=' . urlencode($search);
        }
        if (strpos($search, '@')) {
            $query .= '&address=' . urlencode($search);
        }
        if ($keywords) {
            $query .= '&keywords=' . urlencode($keywords);
        }
        if ($advanced) {
            $query .= '&query=' . urlencode($advanced);
        }
        $sort_order = x($_REQUEST, 'order') ? $_REQUEST['order'] : '';
        if ($sort_order) {
            $query .= '&order=' . urlencode($sort_order);
        }
        if ($a->pager['page'] != 1) {
            $query .= '&p=' . $a->pager['page'];
        }
        logger('mod_directory: query: ' . $query);
        $x = z_fetch_url($query);
        logger('directory: return from upstream: ' . print_r($x, true), LOGGER_DATA);
        if ($x['success']) {
            $t = 0;
            $j = json_decode($x['body'], true);
            if ($j) {
                if ($j['results']) {
                    $entries = array();
                    $photo = 'thumb';
                    foreach ($j['results'] as $rr) {
                        $profile_link = chanlink_url($rr['url']);
                        $pdesc = $rr['description'] ? $rr['description'] . '<br />' : '';
                        $connect_link = local_user() ? z_root() . '/follow?f=&url=' . urlencode($rr['address']) : '';
                        if (in_array($rr['hash'], $contacts)) {
                            $connect_link = '';
                        }
                        $details = '';
                        if (strlen($rr['locale'])) {
                            $details .= $rr['locale'];
                        }
                        if (strlen($rr['region'])) {
                            if (strlen($rr['locale'])) {
                                $details .= ', ';
                            }
                            $details .= $rr['region'];
                        }
                        if (strlen($rr['country'])) {
                            if (strlen($details)) {
                                $details .= ', ';
                            }
                            $details .= $rr['country'];
                        }
                        if (strlen($rr['birthday'])) {
                            if (($years = age($rr['birthday'], 'UTC', '')) != 0) {
                                $details .= '<br />' . t('Age: ') . $years;
                            }
                        }
                        if (strlen($rr['gender'])) {
                            $details .= '<br />' . t('Gender: ') . $rr['gender'];
                        }
                        $page_type = '';
                        $profile = $rr;
                        if (x($profile, 'locale') == 1 || x($profile, 'region') == 1 || x($profile, 'postcode') == 1 || x($profile, 'country') == 1) {
                            $location = t('Location:');
                        }
                        $gender = x($profile, 'gender') == 1 ? t('Gender:') : False;
                        $marital = x($profile, 'marital') == 1 ? t('Status:') : False;
                        $homepage = x($profile, 'homepage') == 1 ? t('Homepage:') : False;
                        $about = x($profile, 'about') == 1 ? t('About:') : False;
                        $entry = array('id' => ++$t, 'profile_link' => $profile_link, 'photo' => $rr['photo'], 'hash' => $rr['hash'], 'alttext' => $rr['name'] . ' ' . $rr['address'], 'name' => $rr['name'], 'details' => $pdesc . $details, 'profile' => $profile, 'address' => $rr['address'], 'location' => $location, 'gender' => $gender, 'pdesc' => $pdesc, 'marital' => $marital, 'homepage' => $homepage, 'about' => $about, 'conn_label' => t('Connect'), 'connect' => $connect_link);
                        $arr = array('contact' => $rr, 'entry' => $entry);
                        call_hooks('directory_item', $arr);
                        $entries[] = $arr['entry'];
                        unset($profile);
                        unset($location);
                    }
                    if ($j['keywords']) {
                        $a->data['directory_keywords'] = $j['keywords'];
                    }
                    logger('mod_directory: entries: ' . print_r($entries, true), LOGGER_DATA);
                    if ($_REQUEST['aj']) {
                        if ($entries) {
                            $o = replace_macros(get_markup_template('directajax.tpl'), array('$entries' => $entries));
                        } else {
                            $o = '<div id="content-complete"></div>';
                        }
                        echo $o;
                        killme();
                    } else {
                        $o .= "<script> var page_query = '" . $_GET['q'] . "'; var extra_args = '" . extra_query_args() . "' ; </script>";
                        $o .= replace_macros($tpl, array('$search' => $search, '$desc' => t('Find'), '$finddsc' => t('Finding:'), '$safetxt' => htmlspecialchars($search, ENT_QUOTES, 'UTF-8'), '$entries' => $entries, '$dirlbl' => t('Directory'), '$submit' => t('Find'), '$next' => alt_pager($a, $j['records'], t('next page'), t('previous page'))));
                    }
                } else {
                    if ($_REQUEST['aj']) {
                        $o = '<div id="content-complete"></div>';
                        echo $o;
                        killme();
                    }
                    if ($a->pager['page'] == 1 && $j['records'] == 0 && strpos($search, '@')) {
                        goaway(z_root() . '/chanview/?f=&address=' . $search);
                    }
                    info(t("No entries (some entries may be hidden).") . EOL);
                }
            }
        }
    }
    return $o;
}