Exemple #1
0
function _well_known_init(&$a)
{
    if (argc() > 1) {
        $arr = array('server' => $_SERVER, 'request' => $_REQUEST);
        call_hooks('well_known', $arr);
        if (!check_siteallowed($_SERVER['REMOTE_ADDR'])) {
            logger('well_known: site not allowed. ' . $_SERVER['REMOTE_ADDR']);
            killme();
        }
        // from php.net re: REMOTE_HOST:
        //     Note: Your web server must be configured to create this variable. For example in Apache
        // you'll need HostnameLookups On inside httpd.conf for it to exist. See also gethostbyaddr().
        if (get_config('system', 'siteallowed_remote_host') && !check_siteallowed($_SERVER['REMOTE_HOST'])) {
            logger('well_known: site not allowed. ' . $_SERVER['REMOTE_HOST']);
            killme();
        }
        switch (argv(1)) {
            case 'zot-info':
                $a->argc -= 1;
                array_shift($a->argv);
                $a->argv[0] = 'zfinger';
                require_once 'mod/zfinger.php';
                zfinger_init($a);
                break;
            case 'webfinger':
                $a->argc -= 1;
                array_shift($a->argv);
                $a->argv[0] = 'wfinger';
                require_once 'mod/wfinger.php';
                wfinger_init($a);
                break;
            case 'host-meta':
                $a->argc -= 1;
                array_shift($a->argv);
                $a->argv[0] = 'hostxrd';
                require_once 'mod/hostxrd.php';
                hostxrd_init($a);
                break;
            default:
                // look in $WEBROOT/well_known for the requested file in case it is
                // something a site requires and for which we do not have a module
                // @fixme - we may need to determine the content-type and stick it in the header
                // for now this can be done with a php script masquerading as the requested file
                $wk_file = str_replace('.well-known', 'well_known', $a->cmd);
                if (file_exists($wk_file)) {
                    echo file_get_contents($wk_file);
                    killme();
                } elseif (file_exists($wk_file . '.php')) {
                    require_once $wk_file . '.php';
                }
                break;
        }
    }
    http_status_exit(404);
}
Exemple #2
0
function _well_known_init(&$a)
{
    if (argc() > 1) {
        $arr = array('server' => $_SERVER, 'request' => $_REQUEST);
        call_hooks('well_known', $arr);
        if (!check_siteallowed($_SERVER['REMOTE_ADDR'])) {
            logger('well_known: site not allowed. ' . $_SERVER['REMOTE_ADDR']);
            killme();
        }
        // from php.net re: REMOTE_HOST:
        //     Note: Your web server must be configured to create this variable. For example in Apache
        // you'll need HostnameLookups On inside httpd.conf for it to exist. See also gethostbyaddr().
        if (get_config('system', 'siteallowed_remote_host') && !check_siteallowed($_SERVER['REMOTE_HOST'])) {
            logger('well_known: site not allowed. ' . $_SERVER['REMOTE_HOST']);
            killme();
        }
        switch (argv(1)) {
            case 'zot-info':
                $a->argc -= 1;
                array_shift($a->argv);
                $a->argv[0] = 'zfinger';
                require_once 'mod/zfinger.php';
                zfinger_init($a);
                break;
            case 'webfinger':
                $a->argc -= 1;
                array_shift($a->argv);
                $a->argv[0] = 'wfinger';
                require_once 'mod/wfinger.php';
                wfinger_init($a);
                break;
            case 'host-meta':
                $a->argc -= 1;
                array_shift($a->argv);
                $a->argv[0] = 'hostxrd';
                require_once 'mod/hostxrd.php';
                hostxrd_init($a);
                break;
            default:
                if (file_exists($a->cmd)) {
                    echo file_get_contents($a->cmd);
                    killme();
                } elseif (file_exists($a->cmd . '.php')) {
                    require_once $a->cmd . '.php';
                }
                break;
        }
    }
    http_status_exit(404);
}
Exemple #3
0
/**
 * @brief Look up if channel is known and previously verified.
 *
 * A guid and a url, both signed by the sender, distinguish a known sender at a
 * known location.
 * This function looks these up to see if the channel is known and therefore
 * previously verified. If not, we will need to verify it.
 *
 * @param array $arr an associative array which must contain:
 *  * \e string \b guid => guid of conversant
 *  * \e string \b guid_sig => guid signed with conversant's private key
 *  * \e string \b url => URL of the origination hub of this communication
 *  * \e string \b url_sig => URL signed with conversant's private key
 *
 * @returns array|null null if site is blacklisted or not found, otherwise an
 *  array with an hubloc record
 */
function zot_gethub($arr, $multiple = false)
{
    if ($arr['guid'] && $arr['guid_sig'] && $arr['url'] && $arr['url_sig']) {
        if (!check_siteallowed($arr['url'])) {
            logger('blacklisted site: ' . $arr['url']);
            return null;
        }
        $limit = $multiple ? '' : ' limit 1 ';
        $sitekey = array_key_exists('sitekey', $arr) && $arr['sitekey'] ? " and hubloc_sitekey = '" . protect_sprintf($arr['sitekey']) . "' " : '';
        $r = q("select * from hubloc\n\t\t\t\twhere hubloc_guid = '%s' and hubloc_guid_sig = '%s'\n\t\t\t\tand hubloc_url = '%s' and hubloc_url_sig = '%s'\n\t\t\t\t{$sitekey} {$limit}", dbesc($arr['guid']), dbesc($arr['guid_sig']), dbesc($arr['url']), dbesc($arr['url_sig']));
        if ($r) {
            logger('zot_gethub: found', LOGGER_DEBUG);
            return $multiple ? $r : $r[0];
        }
    }
    logger('zot_gethub: not found: ' . print_r($arr, true), LOGGER_DEBUG);
    return null;
}
function diaspora_is_blacklisted($s)
{
    if (!check_siteallowed($s)) {
        logger('blacklisted site: ' . $s);
        return true;
    }
    return false;
}
Exemple #5
0
function externals_run($argv, $argc)
{
    cli_startup();
    $a = get_app();
    $total = 0;
    $attempts = 0;
    logger('externals: startup', LOGGER_DEBUG);
    // pull in some public posts
    while ($total == 0 && $attempts < 3) {
        $arr = array('url' => '');
        call_hooks('externals_url_select', $arr);
        if ($arr['url']) {
            $url = $arr['url'];
        } else {
            $randfunc = db_getfunc('RAND');
            // fixme this query does not deal with directory realms.
            $r = q("select site_url, site_pull from site where site_url != '%s' and site_flags != %d and site_type = %d and site_dead = 0 order by {$randfunc} limit 1", dbesc(z_root()), intval(DIRECTORY_MODE_STANDALONE), intval(SITE_TYPE_ZOT));
            if ($r) {
                $url = $r[0]['site_url'];
            }
        }
        $blacklisted = false;
        if (!check_siteallowed($url)) {
            logger('blacklisted site: ' . $url);
            $blacklisted = true;
        }
        $attempts++;
        // make sure we can eventually break out if somebody blacklists all known sites
        if ($blacklisted) {
            if ($attempts > 20) {
                break;
            }
            $attempts--;
            continue;
        }
        if ($url) {
            if ($r[0]['site_pull'] !== NULL_DATE) {
                $mindate = urlencode(datetime_convert('', '', $r[0]['site_pull'] . ' - 1 day'));
            } else {
                $days = get_config('externals', 'since_days');
                if ($days === false) {
                    $days = 15;
                }
                $mindate = urlencode(datetime_convert('', '', 'now - ' . intval($days) . ' days'));
            }
            $feedurl = $url . '/zotfeed?f=&mindate=' . $mindate;
            logger('externals: pulling public content from ' . $feedurl, LOGGER_DEBUG);
            $x = z_fetch_url($feedurl);
            if ($x && $x['success']) {
                q("update site set site_pull = '%s' where site_url = '%s'", dbesc(datetime_convert()), dbesc($url));
                $j = json_decode($x['body'], true);
                if ($j['success'] && $j['messages']) {
                    $sys = get_sys_channel();
                    foreach ($j['messages'] as $message) {
                        // on these posts, clear any route info.
                        $message['route'] = '';
                        $results = process_delivery(array('hash' => 'undefined'), get_item_elements($message), array(array('hash' => $sys['xchan_hash'])), false, true);
                        $total++;
                    }
                    logger('externals: import_public_posts: ' . $total . ' messages imported', LOGGER_DEBUG);
                }
            }
        }
    }
}
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);
}