Example #1
0
function pubsub_post(&$a)
{
    $sys_disabled = true;
    if (!get_config('system', 'disable_discover_tab')) {
        $sys_disabled = get_config('system', 'disable_diaspora_discover_tab');
    }
    $sys = $sys_disabled ? null : get_sys_channel();
    if ($sys) {
        $sys['system'] = true;
    }
    $xml = file_get_contents('php://input');
    logger('pubsub: feed arrived from ' . $_SERVER['REMOTE_ADDR'] . ' for ' . App::$cmd);
    logger('pubsub: user-agent: ' . $_SERVER['HTTP_USER_AGENT']);
    logger('pubsub: data: ' . $xml, LOGGER_DATA);
    $nick = argc() > 1 ? escape_tags(trim(argv(1))) : '';
    $contact_id = argc() > 2 ? intval(argv(2)) : 0;
    $channel = channelx_by_nick($nick);
    if (!$channel) {
        http_status_exit(200, 'OK');
    }
    $importer_arr = array($channel);
    if ($sys) {
        $importer_arr[] = $sys;
    }
    foreach ($importer_arr as $channel) {
        if (!$channel['system']) {
            $connections = abook_connections($channel['channel_id'], ' and abook_id = ' . $contact_id);
        } else {
            $connections = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d", intval($contact_id));
        }
        if ($connections) {
            $xchan = $connections[0];
        } else {
            logger('connection ' . $contact_id . ' not found.');
            continue;
        }
        if (!perm_is_allowed($channel['channel_id'], $xchan['xchan_hash'], 'send_stream') && !$channel['system']) {
            logger('permission denied.');
            continue;
        }
        consume_feed($xml, $channel, $xchan, 1);
        consume_feed($xml, $channel, $xchan, 2);
    }
    http_status_exit(200, 'OK');
}
Example #2
0
function photos_init(&$a)
{
    if (get_config('system', 'block_public') && !local_channel() && !remote_channel()) {
        return;
    }
    $o = '';
    if (argc() > 1) {
        $nick = argv(1);
        profile_load($a, $nick);
        $channelx = channelx_by_nick($nick);
        if (!$channelx) {
            return;
        }
        $a->data['channel'] = $channelx;
        $observer = $a->get_observer();
        $a->data['observer'] = $observer;
        $observer_xchan = $observer ? $observer['xchan_hash'] : '';
        head_set_icon($a->data['channel']['xchan_photo_s']);
        $a->page['htmlhead'] .= "<script> var ispublic = '" . t('everybody') . "'; var profile_uid = " . ($a->data['channel'] ? $a->data['channel']['channel_id'] : 0) . "; </script>";
    }
    return;
}
Example #3
0
 function init()
 {
     if (observer_prohibited()) {
         return;
     }
     $o = '';
     if (argc() > 1) {
         $nick = argv(1);
         profile_load($nick);
         $channelx = channelx_by_nick($nick);
         if (!$channelx) {
             return;
         }
         \App::$data['channel'] = $channelx;
         $observer = \App::get_observer();
         \App::$data['observer'] = $observer;
         $observer_xchan = $observer ? $observer['xchan_hash'] : '';
         head_set_icon(\App::$data['channel']['xchan_photo_s']);
         \App::$page['htmlhead'] .= "<script> var profile_uid = " . (\App::$data['channel'] ? \App::$data['channel']['channel_id'] : 0) . "; </script>";
     }
     return;
 }
Example #4
0
 function init()
 {
     if (get_config('system', 'block_public') && !local_channel() && !remote_channel()) {
         return;
     }
     $o = '';
     if (argc() > 1) {
         $nick = argv(1);
         profile_load($a, $nick);
         $channelx = channelx_by_nick($nick);
         if (!$channelx) {
             return;
         }
         \App::$data['channel'] = $channelx;
         $observer = \App::get_observer();
         \App::$data['observer'] = $observer;
         $observer_xchan = $observer ? $observer['xchan_hash'] : '';
         head_set_icon(\App::$data['channel']['xchan_photo_s']);
         \App::$page['htmlhead'] .= "<script> var profile_uid = " . (\App::$data['channel'] ? \App::$data['channel']['channel_id'] : 0) . "; </script>";
     }
     return;
 }
Example #5
0
 function get()
 {
     if (observer_prohibited()) {
         return;
     }
     $channel = null;
     if (argc() > 1) {
         $channel = channelx_by_nick(argv(1));
     }
     if (!$channel) {
         notice(t('Channel not found.') . EOL);
         return;
     }
     // since we don't currently have an event permission - use the stream permission
     if (!perm_is_allowed($channel['channel_id'], get_observer_hash(), 'view_stream')) {
         notice(t('Permissions denied.') . EOL);
         return;
     }
     $sql_extra = permissions_sql($channel['channel_id'], get_observer_hash(), 'event');
     $first_day = get_pconfig(local_channel(), 'system', 'cal_first_day');
     $first_day = $first_day ? $first_day : 0;
     $htpl = get_markup_template('event_head.tpl');
     \App::$page['htmlhead'] .= replace_macros($htpl, array('$baseurl' => z_root(), '$module_url' => '/cal/' . $channel['channel_address'], '$modparams' => 2, '$lang' => \App::$language, '$first_day' => $first_day));
     $o = '';
     $tabs = profile_tabs($a, True, $channel['channel_address']);
     $mode = 'view';
     $y = 0;
     $m = 0;
     $ignored = x($_REQUEST, 'ignored') ? " and dismissed = " . intval($_REQUEST['ignored']) . " " : '';
     // logger('args: ' . print_r(\App::$argv,true));
     if (argc() > 3 && intval(argv(2)) && intval(argv(3))) {
         $mode = 'view';
         $y = intval(argv(2));
         $m = intval(argv(3));
     }
     if (argc() <= 3) {
         $mode = 'view';
         $event_id = argv(2);
     }
     if ($mode == 'view') {
         /* edit/create form */
         if ($event_id) {
             $r = q("SELECT * FROM `event` WHERE event_hash = '%s' AND `uid` = %d LIMIT 1", dbesc($event_id), intval($channel['channel_id']));
             if (count($r)) {
                 $orig_event = $r[0];
             }
         }
         // Passed parameters overrides anything found in the DB
         if (!x($orig_event)) {
             $orig_event = array();
         }
         $tz = date_default_timezone_get();
         if (x($orig_event)) {
             $tz = $orig_event['adjust'] ? date_default_timezone_get() : 'UTC';
         }
         $syear = datetime_convert('UTC', $tz, $sdt, 'Y');
         $smonth = datetime_convert('UTC', $tz, $sdt, 'm');
         $sday = datetime_convert('UTC', $tz, $sdt, 'd');
         $shour = datetime_convert('UTC', $tz, $sdt, 'H');
         $sminute = datetime_convert('UTC', $tz, $sdt, 'i');
         $stext = datetime_convert('UTC', $tz, $sdt);
         $stext = substr($stext, 0, 14) . "00:00";
         $fyear = datetime_convert('UTC', $tz, $fdt, 'Y');
         $fmonth = datetime_convert('UTC', $tz, $fdt, 'm');
         $fday = datetime_convert('UTC', $tz, $fdt, 'd');
         $fhour = datetime_convert('UTC', $tz, $fdt, 'H');
         $fminute = datetime_convert('UTC', $tz, $fdt, 'i');
         $ftext = datetime_convert('UTC', $tz, $fdt);
         $ftext = substr($ftext, 0, 14) . "00:00";
         $type = x($orig_event) ? $orig_event['etype'] : 'event';
         $f = get_config('system', 'event_input_format');
         if (!$f) {
             $f = 'ymd';
         }
         $catsenabled = feature_enabled($channel['channel_id'], 'categories');
         $show_bd = perm_is_allowed($channel['channel_id'], get_observer_hash(), 'view_contacts');
         if (!$show_bd) {
             $sql_extra .= " and event.etype != 'birthday' ";
         }
         $category = '';
         $thisyear = datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y');
         $thismonth = datetime_convert('UTC', date_default_timezone_get(), 'now', 'm');
         if (!$y) {
             $y = intval($thisyear);
         }
         if (!$m) {
             $m = intval($thismonth);
         }
         // Put some limits on dates. The PHP date functions don't seem to do so well before 1900.
         // An upper limit was chosen to keep search engines from exploring links millions of years in the future.
         if ($y < 1901) {
             $y = 1900;
         }
         if ($y > 2099) {
             $y = 2100;
         }
         $nextyear = $y;
         $nextmonth = $m + 1;
         if ($nextmonth > 12) {
             $nextmonth = 1;
             $nextyear++;
         }
         $prevyear = $y;
         if ($m > 1) {
             $prevmonth = $m - 1;
         } else {
             $prevmonth = 12;
             $prevyear--;
         }
         $dim = get_dim($y, $m);
         $start = sprintf('%d-%d-%d %d:%d:%d', $y, $m, 1, 0, 0, 0);
         $finish = sprintf('%d-%d-%d %d:%d:%d', $y, $m, $dim, 23, 59, 59);
         if (argv(2) === 'json') {
             if (x($_GET, 'start')) {
                 $start = $_GET['start'];
             }
             if (x($_GET, 'end')) {
                 $finish = $_GET['end'];
             }
         }
         $start = datetime_convert('UTC', 'UTC', $start);
         $finish = datetime_convert('UTC', 'UTC', $finish);
         $adjust_start = datetime_convert('UTC', date_default_timezone_get(), $start);
         $adjust_finish = datetime_convert('UTC', date_default_timezone_get(), $finish);
         if (x($_GET, 'id')) {
             $r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan\n\t                                from event left join item on resource_id = event_hash where resource_type = 'event' and event.uid = %d and event.id = %d {$sql_extra} limit 1", intval($channel['channel_id']), intval($_GET['id']));
         } else {
             // fixed an issue with "nofinish" events not showing up in the calendar.
             // There's still an issue if the finish date crosses the end of month.
             // Noting this for now - it will need to be fixed here and in Friendica.
             // Ultimately the finish date shouldn't be involved in the query.
             $r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan\n\t                              from event left join item on event_hash = resource_id \n\t\t\t\t\twhere resource_type = 'event' and event.uid = %d {$ignored} \n\t\t\t\t\tAND (( adjust = 0 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' ) \n\t\t\t\t\tOR  (  adjust = 1 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' )) {$sql_extra} ", intval($channel['channel_id']), dbesc($start), dbesc($finish), dbesc($adjust_start), dbesc($adjust_finish));
         }
         $links = array();
         if ($r) {
             xchan_query($r);
             $r = fetch_post_tags($r, true);
             $r = sort_by_date($r);
         }
         if ($r) {
             foreach ($r as $rr) {
                 $j = $rr['adjust'] ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtstart'], 'j') : datetime_convert('UTC', 'UTC', $rr['dtstart'], 'j');
                 if (!x($links, $j)) {
                     $links[$j] = z_root() . '/' . \App::$cmd . '#link-' . $j;
                 }
             }
         }
         $events = array();
         $last_date = '';
         $fmt = t('l, F j');
         if ($r) {
             foreach ($r as $rr) {
                 $j = $rr['adjust'] ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtstart'], 'j') : datetime_convert('UTC', 'UTC', $rr['dtstart'], 'j');
                 $d = $rr['adjust'] ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtstart'], $fmt) : datetime_convert('UTC', 'UTC', $rr['dtstart'], $fmt);
                 $d = day_translate($d);
                 $start = $rr['adjust'] ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtstart'], 'c') : datetime_convert('UTC', 'UTC', $rr['dtstart'], 'c');
                 if ($rr['nofinish']) {
                     $end = null;
                 } else {
                     $end = $rr['adjust'] ? datetime_convert('UTC', date_default_timezone_get(), $rr['dtend'], 'c') : datetime_convert('UTC', 'UTC', $rr['dtend'], 'c');
                 }
                 $is_first = $d !== $last_date;
                 $last_date = $d;
                 $edit = false;
                 $drop = false;
                 $title = strip_tags(html_entity_decode(bbcode($rr['summary']), ENT_QUOTES, 'UTF-8'));
                 if (!$title) {
                     list($title, $_trash) = explode("<br", bbcode($rr['desc']), 2);
                     $title = strip_tags(html_entity_decode($title, ENT_QUOTES, 'UTF-8'));
                 }
                 $html = format_event_html($rr);
                 $rr['desc'] = bbcode($rr['desc']);
                 $rr['location'] = bbcode($rr['location']);
                 $events[] = array('id' => $rr['id'], 'hash' => $rr['event_hash'], 'start' => $start, 'end' => $end, 'drop' => $drop, 'allDay' => false, 'title' => $title, 'j' => $j, 'd' => $d, 'edit' => $edit, 'is_first' => $is_first, 'item' => $rr, 'html' => $html, 'plink' => array($rr['plink'], t('Link to Source'), '', ''));
             }
         }
         if (argv(2) === 'json') {
             echo json_encode($events);
             killme();
         }
         // links: array('href', 'text', 'extra css classes', 'title')
         if (x($_GET, 'id')) {
             $tpl = get_markup_template("event_cal.tpl");
         } else {
             $tpl = get_markup_template("events_cal-js.tpl");
         }
         $nick = $channel['channel_address'];
         $o = replace_macros($tpl, array('$baseurl' => z_root(), '$new_event' => array(z_root() . '/cal', $event_id ? t('Edit Event') : t('Create Event'), '', ''), '$previus' => array(z_root() . "/cal/{$nick}/{$prevyear}/{$prevmonth}", t('Previous'), '', ''), '$next' => array(z_root() . "/cal/{$nick}/{$nextyear}/{$nextmonth}", t('Next'), '', ''), '$export' => array(z_root() . "/cal/{$nick}/{$y}/{$m}/export", t('Export'), '', ''), '$calendar' => cal($y, $m, $links, ' eventcal'), '$events' => $events, '$upload' => t('Import'), '$submit' => t('Submit'), '$prev' => t('Previous'), '$next' => t('Next'), '$today' => t('Today'), '$form' => $form, '$expandform' => x($_GET, 'expandform') ? true : false, '$tabs' => $tabs));
         if (x($_GET, 'id')) {
             echo $o;
             killme();
         }
         return $o;
     }
 }
Example #6
0
 function delAssoc($handle)
 {
     logger('delAssoc');
     $channel = channelx_by_nick(basename($handle));
     if ($channel) {
         return del_pconfig($channel['channel_id'], 'openid', 'associate');
     }
 }
Example #7
0
 function oep_profile_reply($args)
 {
     require_once 'include/identity.php';
     require_once 'include/Contact.php';
     $url = $args['url'];
     if (preg_match('#//(.*?)/(.*?)/(.*?)(/|\\?|&|$)#', $url, $matches)) {
         $chn = $matches[3];
     }
     if (!$chn) {
         return;
     }
     $c = channelx_by_nick($chn);
     if (!$c) {
         return;
     }
     $maxwidth = intval($args['maxwidth']);
     $maxheight = intval($args['maxheight']);
     $width = 800;
     $height = 375;
     if ($maxwidth) {
         $width = $maxwidth;
         $height = 375 / 800 * $width;
     }
     if ($maxheight) {
         if ($maxheight < $height) {
             $width = 800 / 375 * $maxheight;
             $height = $maxheight;
         }
     }
     $ret = array();
     $ret['type'] = 'rich';
     $ret['width'] = intval($width);
     $ret['height'] = intval($height);
     $ret['html'] = get_zcard_embed($c, get_observer_hash(), array('width' => $width, 'height' => $height));
     return $ret;
 }
function pubsubhubbub_init(&$a)
{
    // PuSH subscription must be considered "public" so just block it
    // if public access isn't enabled.
    if (get_config('system', 'block_public')) {
        http_status_exit(403);
    }
    // Subscription request from subscriber
    // https://pubsubhubbub.googlecode.com/git/pubsubhubbub-core-0.4.html#anchor4
    // Example from GNU Social:
    // [hub_mode] => subscribe
    // [hub_callback] => http://status.local/main/push/callback/1
    // [hub_verify] => sync
    // [hub_verify_token] => af11...
    // [hub_secret] => af11...
    // [hub_topic] => http://friendica.local/dfrn_poll/sazius
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $hub_mode = push_post_var('hub_mode');
        $hub_callback = push_post_var('hub_callback');
        $hub_verify = push_post_var('hub_verify');
        $hub_verify_token = push_post_var('hub_verify_token');
        $hub_secret = push_post_var('hub_secret');
        $hub_topic = push_post_var('hub_topic');
        // check for valid hub_mode
        if ($hub_mode === 'subscribe') {
            $subscribe = 1;
        } else {
            if ($hub_mode === 'unsubscribe') {
                $subscribe = 0;
            } else {
                logger("pubsubhubbub: invalid hub_mode={$hub_mode}, ignoring.");
                http_status_exit(404);
            }
        }
        logger("pubsubhubbub: {$hub_mode} request from " . $_SERVER['REMOTE_ADDR']);
        // get the nick name from the topic, a bit hacky but needed
        $nick = substr(strrchr($hub_topic, "/"), 1);
        if (!$nick) {
            logger('pubsubhubbub: bad hub_topic=$hub_topic, ignoring.');
            http_status_exit(404);
        }
        // fetch user from database given the nickname
        $owner = channelx_by_nick($nick);
        if (!$owner) {
            logger('pubsubhubbub: local account not found: ' . $nick);
            http_status_exit(404);
        }
        if (!perm_is_allowed($owner['channel_id'], '', 'view_stream')) {
            logger('pubsubhubbub: local channel ' . $nick . 'has chosen to hide wall, ignoring.');
            http_status_exit(403);
        }
        // sanity check that topic URLs are the same
        if (!link_compare($hub_topic, z_root() . '/feed/' . $nick)) {
            logger('pubsubhubbub: not a valid hub topic ' . $hub_topic);
            http_status_exit(404);
        }
        // do subscriber verification according to the PuSH protocol
        $hub_challenge = random_string(40);
        $params = 'hub.mode=' . ($subscribe == 1 ? 'subscribe' : 'unsubscribe') . '&hub.topic=' . urlencode($hub_topic) . '&hub.challenge=' . $hub_challenge . '&hub.lease_seconds=604800' . '&hub.verify_token=' . $hub_verify_token;
        // lease time is hard coded to one week (in seconds)
        // we don't actually enforce the lease time because GNU
        // Social/StatusNet doesn't honour it (yet)
        $x = z_fetch_url($hub_callback . "?" . $params);
        if (!$x['success']) {
            logger("pubsubhubbub: subscriber verification at {$hub_callback} " . "returned {$ret}, ignoring.");
            http_status_exit(404);
        }
        // check that the correct hub_challenge code was echoed back
        if (trim($x['body']) !== $hub_challenge) {
            logger("pubsubhubbub: subscriber did not echo back " . "hub.challenge, ignoring.");
            logger("\"{$hub_challenge}\" != \"" . trim($x['body']) . "\"");
            http_status_exit(404);
        }
        // fetch the old subscription if it exists
        $orig = q("SELECT * FROM `push_subscriber` WHERE `callback_url` = '%s'", dbesc($hub_callback));
        // delete old subscription if it exists
        q("DELETE FROM push_subscriber WHERE callback_url = '%s' and topic = '%s'", dbesc($hub_callback), dbesc($hub_topic));
        if ($subscribe) {
            $last_update = datetime_convert('UTC', 'UTC', 'now', 'Y-m-d H:i:s');
            // if we are just updating an old subscription, keep the
            // old values for last_update
            if ($orig) {
                $last_update = $orig[0]['last_update'];
            }
            // subscribe means adding the row to the table
            q("INSERT INTO push_subscriber ( callback_url, topic, last_update, secret) values ('%s', '%s', '%s', '%s') ", dbesc($hub_callback), dbesc($hub_topic), dbesc($last_update), dbesc($hub_secret));
            logger("pubsubhubbub: successfully subscribed [{$hub_callback}].");
        } else {
            logger("pubsubhubbub: successfully unsubscribed [{$hub_callback}].");
            // we do nothing here, since the row was already deleted
        }
        http_status_exit(202);
    }
    killme();
}
Example #9
0
/**
 * @brief Verify login credentials.
 *
 * If system.authlog is set a log entry will be added for failed login
 * attempts.
 *
 * @param string $login
 *  The login to verify (channel address, account email or guest login token).
 * @param string $pass
 *  The provided password to verify.
 * @return array|null
 *  Returns account record on success, null on failure.
 *  The return array is dependent on the login mechanism.
 *    $ret['account'] will be set if either an email or channel address validation was successful (local login).
 *    $ret['channel'] will be set if a channel address validation was successful.
 *    $ret['xchan'] will be set if a guest access token validation was successful. 
 *   Keys will exist for invalid return arrays but will be set to null. 
 *   This function does not perform a login. It merely validates systems passwords and tokens.  
 *
 */
function account_verify_password($login, $pass)
{
    $ret = ['account' => null, 'channel' => null, 'xchan' => null];
    $email_verify = get_config('system', 'verify_email');
    $register_policy = get_config('system', 'register_policy');
    if (!$login) {
        return null;
    }
    $account = null;
    $channel = null;
    $xchan = null;
    if (!strpos($login, '@')) {
        $channel = channelx_by_nick($login);
        if (!$channel) {
            $x = q("select * from atoken where atoken_name = '%s' and atoken_token = '%s' limit 1", dbesc($login), dbesc($pass));
            if ($x) {
                $ret['xchan'] = atoken_xchan($x[0]);
                atoken_create_xchan($ret['xchan']);
                return $ret;
            }
        }
    }
    if ($channel) {
        $where = " where account_id = " . intval($channel['channel_account_id']) . " ";
    } else {
        $where = " where account_email = '" . dbesc($login) . "' ";
    }
    $a = q("select * from account {$where}");
    if (!$a) {
        return null;
    }
    $account = $a[0];
    // Currently we only verify email address if there is an open registration policy.
    // This isn't because of any policy - it's because the workflow gets too complicated if
    // you have to verify the email and then go through the account approval workflow before
    // letting them login.
    if ($email_verify && $register_policy == REGISTER_OPEN && $account['account_flags'] & ACCOUNT_UNVERIFIED) {
        logger('email verification required for ' . $login);
        return null;
    }
    if ($account['account_flags'] == ACCOUNT_OK && hash('whirlpool', $account['account_salt'] . $pass) === $account['account_password']) {
        logger('password verified for ' . $login);
        $ret['account'] = $account;
        if ($channel) {
            $ret['channel'] = $channel;
        }
        return $ret;
    }
    $error = 'password failed for ' . $login;
    logger($error);
    if ($account['account_flags'] & ACCOUNT_UNVERIFIED) {
        logger('Account is unverified. account_flags = ' . $account['account_flags']);
    }
    if ($account['account_flags'] & ACCOUNT_BLOCKED) {
        logger('Account is blocked. account_flags = ' . $account['account_flags']);
    }
    if ($account['account_flags'] & ACCOUNT_EXPIRED) {
        logger('Account is expired. account_flags = ' . $account['account_flags']);
    }
    if ($account['account_flags'] & ACCOUNT_REMOVED) {
        logger('Account is removed. account_flags = ' . $account['account_flags']);
    }
    if ($account['account_flags'] & ACCOUNT_PENDING) {
        logger('Account is pending. account_flags = ' . $account['account_flags']);
    }
    log_failed_login($error);
    return null;
}
Example #10
0
function salmon_post(&$a)
{
    $sys_disabled = true;
    if (!get_config('system', 'disable_discover_tab')) {
        $sys_disabled = get_config('system', 'disable_diaspora_discover_tab');
    }
    $sys = $sys_disabled ? null : get_sys_channel();
    if (App::$data['salmon_test']) {
        $xml = file_get_contents('test.xml');
        App::$argv[1] = 'gnusoc';
    } else {
        $xml = file_get_contents('php://input');
    }
    logger('mod-salmon: new salmon ' . $xml, LOGGER_DATA);
    $nick = argc() > 1 ? trim(argv(1)) : '';
    //	$mentions   = ((App::$argc > 2 && App::$argv[2] === 'mention') ? true : false);
    $importer = channelx_by_nick($nick);
    if (!$importer) {
        http_status_exit(500);
    }
    // @fixme check that this channel has the GNU-Social protocol enabled
    // parse the xml
    $dom = simplexml_load_string($xml, 'SimpleXMLElement', 0, NAMESPACE_SALMON_ME);
    // figure out where in the DOM tree our data is hiding
    if ($dom->provenance->data) {
        $base = $dom->provenance;
    } elseif ($dom->env->data) {
        $base = $dom->env;
    } elseif ($dom->data) {
        $base = $dom;
    }
    if (!$base) {
        logger('mod-salmon: unable to locate salmon data in xml ');
        http_status_exit(400);
    }
    logger('data: ' . $xml, LOGGER_DATA);
    // Stash the signature away for now. We have to find their key or it won't be good for anything.
    logger('sig: ' . $base->sig);
    $signature = base64url_decode($base->sig);
    logger('sig: ' . $base->sig . ' decoded length: ' . strlen($signature));
    // unpack the  data
    // strip whitespace so our data element will return to one big base64 blob
    $data = str_replace(array(" ", "\t", "\r", "\n"), array("", "", "", ""), $base->data);
    // stash away some other stuff for later
    $type = $base->data[0]->attributes()->type[0];
    $keyhash = $base->sig[0]->attributes()->keyhash[0];
    $encoding = $base->encoding;
    $alg = $base->alg;
    // Salmon magic signatures have evolved and there is no way of knowing ahead of time which
    // flavour we have. We'll try and verify it regardless.
    $stnet_signed_data = $data;
    $signed_data = $data . '.' . base64url_encode($type, false) . '.' . base64url_encode($encoding, false) . '.' . base64url_encode($alg, false);
    $compliant_format = str_replace('=', '', $signed_data);
    // decode the data
    $data = base64url_decode($data);
    logger('decoded: ' . $data, LOGGER_DATA);
    // GNU-Social doesn't send a legal Atom feed over salmon, only an Atom entry. Unfortunately
    // our parser is a bit strict about compliance so we'll insert just enough of a feed
    // tag to trick it into believing it's a compliant feed.
    if (!strstr($data, '<feed')) {
        $data = str_replace('<entry ', '<feed xmlns="http://www.w3.org/2005/Atom"><entry ', $data);
        $data .= '</feed>';
    }
    $datarray = process_salmon_feed($data, $importer);
    $author_link = $datarray['author']['author_link'];
    $item = $datarray['item'];
    if (!$author_link) {
        logger('mod-salmon: Could not retrieve author URI.');
        http_status_exit(400);
    }
    $r = q("select xchan_pubkey from xchan where xchan_guid = '%s' limit 1", dbesc($author_link));
    if ($r) {
        $pubkey = $r[0]['xchan_pubkey'];
    } else {
        // Once we have the author URI, go to the web and try to find their public key
        logger('mod-salmon: Fetching key for ' . $author_link);
        $pubkey = get_salmon_key($author_link, $keyhash);
        if (!$pubkey) {
            logger('mod-salmon: Could not retrieve author key.');
            http_status_exit(400);
        }
        logger('mod-salmon: key details: ' . print_r($pubkey, true), LOGGER_DEBUG);
    }
    $pubkey = rtrim($pubkey);
    // We should have everything we need now. Let's see if it verifies.
    $verify = rsa_verify($signed_data, $signature, $pubkey);
    if (!$verify) {
        logger('mod-salmon: message did not verify using protocol. Trying padding hack.');
        $verify = rsa_verify($compliant_format, $signature, $pubkey);
    }
    if (!$verify) {
        logger('mod-salmon: message did not verify using padding. Trying old statusnet hack.');
        $verify = rsa_verify($stnet_signed_data, $signature, $pubkey);
    }
    if (!$verify) {
        logger('mod-salmon: Message did not verify. Discarding.');
        http_status_exit(400);
    }
    logger('mod-salmon: Message verified.');
    /* lookup the author */
    if (!$datarray['author']['author_link']) {
        logger('unable to probe - no author identifier');
        http_status_exit(400);
    }
    $r = q("select * from xchan where xchan_guid = '%s' limit 1", dbesc($datarray['author']['author_link']));
    if (!$r) {
        if (discover_by_webbie($datarray['author']['author_link'])) {
            $r = q("select * from xchan where xchan_guid = '%s' limit 1", dbesc($datarray['author']['author_link']));
            if (!$r) {
                logger('discovery failed');
                http_status_exit(400);
            }
        }
    }
    $xchan = $r[0];
    /*
     *
     * If we reached this point, the message is good. Now let's figure out if the author is allowed to send us stuff.
     *
     */
    // First check for and process follow activity
    if (activity_match($item['verb'], ACTIVITY_FOLLOW) && $item['obj_type'] === ACTIVITY_OBJ_PERSON) {
        $cb = array('item' => $item, 'channel' => $importer, 'xchan' => $xchan, 'author' => $datarray['author'], 'caught' => false);
        call_hooks('follow_from_feed', $cb);
        if ($cb['caught']) {
            http_status_exit(200);
        }
    }
    $m = parse_url($xchan['xchan_url']);
    if ($m) {
        $host = $m['scheme'] . '://' . $m['host'];
        q("update site set site_dead = 0, site_update = '%s' where site_type = %d and site_url = '%s'", dbesc(datetime_convert()), intval(SITE_TYPE_NOTZOT), dbesc($url));
        if (!check_siteallowed($host)) {
            logger('blacklisted site: ' . $host);
            http_status_exit(403, 'permission denied.');
        }
    }
    $importer_arr = array($importer);
    if (!$sys_disabled) {
        $sys['system'] = true;
        $importer_arr[] = $sys;
    }
    unset($datarray['author']);
    // we will only set and return the status code for operations
    // on an importer channel and not for the sys channel
    $status = 200;
    foreach ($importer_arr as $importer) {
        if (!$importer['system']) {
            $allowed = get_pconfig($importer['channel_id'], 'system', 'gnusoc_allowed');
            if (!intval($allowed)) {
                logger('mod-salmon: disallowed for channel ' . $importer['channel_name']);
                $status = 202;
                continue;
            }
        }
        // Otherwise check general permissions
        if (!perm_is_allowed($importer['channel_id'], $xchan['xchan_hash'], 'send_stream') && !$importer['system']) {
            // check for and process ostatus autofriend
            // ... fixme
            // otherwise
            logger('mod-salmon: Ignoring this author.');
            $status = 202;
            continue;
        }
        $parent_item = null;
        if ($item['parent_mid']) {
            $r = q("select * from item where mid = '%s' and uid = %d limit 1", dbesc($item['parent_mid']), intval($importer['channel_id']));
            if (!$r) {
                logger('mod-salmon: parent item not found.');
                if (!$importer['system']) {
                    $status = 202;
                }
                continue;
            }
            $parent_item = $r[0];
        }
        if (!$item['author_xchan']) {
            $item['author_xchan'] = $xchan['xchan_hash'];
        }
        $item['owner_xchan'] = $parent_item ? $parent_item['owner_xchan'] : $xchan['xchan_hash'];
        $r = q("SELECT edited FROM item WHERE mid = '%s' AND uid = %d LIMIT 1", dbesc($item['mid']), intval($importer['channel_id']));
        // Update content if 'updated' changes
        // currently a no-op @fixme
        if ($r) {
            if (x($item, 'edited') !== false && datetime_convert('UTC', 'UTC', $item['edited']) !== $r[0]['edited']) {
                // do not accept (ignore) an earlier edit than one we currently have.
                if (datetime_convert('UTC', 'UTC', $item['edited']) > $r[0]['edited']) {
                    update_feed_item($importer['channel_id'], $item);
                }
            }
            if (!$importer['system']) {
                $status = 200;
            }
            continue;
        }
        if (!$item['parent_mid']) {
            $item['parent_mid'] = $item['mid'];
        }
        $item['aid'] = $importer['channel_account_id'];
        $item['uid'] = $importer['channel_id'];
        logger('consume_feed: ' . print_r($item, true), LOGGER_DATA);
        $xx = item_store($item);
        $r = $xx['item_id'];
        if (!$importer['system']) {
            $status = 200;
        }
        continue;
    }
    http_status_exit($status);
}