コード例 #1
0
ファイル: items.php プロジェクト: einervonvielen/redmatrix
function get_item_elements($x)
{
    $arr = array();
    $arr['body'] = $x['body'] ? htmlspecialchars($x['body'], ENT_COMPAT, 'UTF-8', false) : '';
    $key = get_config('system', 'pubkey');
    $maxlen = get_max_import_size();
    if ($maxlen && mb_strlen($arr['body']) > $maxlen) {
        $arr['body'] = mb_substr($arr['body'], 0, $maxlen, 'UTF-8');
        logger('get_item_elements: message length exceeds max_import_size: truncated');
    }
    $arr['created'] = datetime_convert('UTC', 'UTC', $x['created']);
    $arr['edited'] = datetime_convert('UTC', 'UTC', $x['edited']);
    if ($arr['created'] > datetime_convert()) {
        $arr['created'] = datetime_convert();
    }
    if ($arr['edited'] > datetime_convert()) {
        $arr['edited'] = datetime_convert();
    }
    $arr['expires'] = x($x, 'expires') && $x['expires'] ? datetime_convert('UTC', 'UTC', $x['expires']) : NULL_DATE;
    $arr['commented'] = x($x, 'commented') && $x['commented'] ? datetime_convert('UTC', 'UTC', $x['commented']) : $arr['created'];
    $arr['comments_closed'] = x($x, 'comments_closed') && $x['comments_closed'] ? datetime_convert('UTC', 'UTC', $x['comments_closed']) : NULL_DATE;
    $arr['title'] = $x['title'] ? htmlspecialchars($x['title'], ENT_COMPAT, 'UTF-8', false) : '';
    if (mb_strlen($arr['title']) > 255) {
        $arr['title'] = mb_substr($arr['title'], 0, 255);
    }
    $arr['app'] = $x['app'] ? htmlspecialchars($x['app'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['route'] = $x['route'] ? htmlspecialchars($x['route'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['mid'] = $x['message_id'] ? htmlspecialchars($x['message_id'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['parent_mid'] = $x['message_top'] ? htmlspecialchars($x['message_top'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['thr_parent'] = $x['message_parent'] ? htmlspecialchars($x['message_parent'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['plink'] = $x['permalink'] ? htmlspecialchars($x['permalink'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['location'] = $x['location'] ? htmlspecialchars($x['location'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['coord'] = $x['longlat'] ? htmlspecialchars($x['longlat'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['verb'] = $x['verb'] ? htmlspecialchars($x['verb'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['mimetype'] = $x['mimetype'] ? htmlspecialchars($x['mimetype'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['obj_type'] = $x['object_type'] ? htmlspecialchars($x['object_type'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['tgt_type'] = $x['target_type'] ? htmlspecialchars($x['target_type'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['public_policy'] = $x['public_scope'] ? htmlspecialchars($x['public_scope'], ENT_COMPAT, 'UTF-8', false) : '';
    if ($arr['public_policy'] === 'public') {
        $arr['public_policy'] = '';
    }
    $arr['comment_policy'] = $x['comment_scope'] ? htmlspecialchars($x['comment_scope'], ENT_COMPAT, 'UTF-8', false) : 'contacts';
    $arr['sig'] = $x['signature'] ? htmlspecialchars($x['signature'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['diaspora_meta'] = $x['diaspora_signature'] ? json_encode(crypto_encapsulate($x['diaspora_signature'], $key)) : '';
    $arr['object'] = activity_sanitise($x['object']);
    $arr['target'] = activity_sanitise($x['target']);
    $arr['attach'] = activity_sanitise($x['attach']);
    $arr['term'] = decode_tags($x['tags']);
    $arr['item_private'] = array_key_exists('flags', $x) && is_array($x['flags']) && in_array('private', $x['flags']) ? 1 : 0;
    $arr['item_flags'] = 0;
    if (array_key_exists('flags', $x) && in_array('consensus', $x['flags'])) {
        $arr['item_flags'] |= ITEM_CONSENSUS;
    }
    if (array_key_exists('flags', $x) && in_array('deleted', $x['flags'])) {
        $arr['item_restrict'] |= ITEM_DELETED;
    }
    if (array_key_exists('flags', $x) && in_array('hidden', $x['flags'])) {
        $arr['item_restrict'] |= ITEM_HIDDEN;
    }
    // Here's the deal - the site might be down or whatever but if there's a new person you've never
    // seen before sending stuff to your stream, we MUST be able to look them up and import their data from their
    // hub and verify that they are legit - or else we're going to toss the post. We only need to do this
    // once, and after that your hub knows them. Sure some info is in the post, but it's only a transit identifier
    // and not enough info to be able to look you up from your hash - which is the only thing stored with the post.
    if (($xchan_hash = import_author_xchan($x['author'])) !== false) {
        $arr['author_xchan'] = $xchan_hash;
    } else {
        return array();
    }
    // save a potentially expensive lookup if author == owner
    if ($arr['author_xchan'] === make_xchan_hash($x['owner']['guid'], $x['owner']['guid_sig'])) {
        $arr['owner_xchan'] = $arr['author_xchan'];
    } else {
        if (($xchan_hash = import_author_xchan($x['owner'])) !== false) {
            $arr['owner_xchan'] = $xchan_hash;
        } else {
            return array();
        }
    }
    if ($arr['sig']) {
        $r = q("select xchan_pubkey from xchan where xchan_hash = '%s' limit 1", dbesc($arr['author_xchan']));
        if ($r && rsa_verify($x['body'], base64url_decode($arr['sig']), $r[0]['xchan_pubkey'])) {
            $arr['item_flags'] |= ITEM_VERIFIED;
        } else {
            logger('get_item_elements: message verification failed.');
        }
    }
    // if it's a private post, encrypt it in the DB.
    // We have to do that here because we need to cleanse the input and prevent bad stuff from getting in,
    // and we need plaintext to do that.
    if (intval($arr['item_private'])) {
        $arr['item_flags'] = $arr['item_flags'] | ITEM_OBSCURED;
        if ($arr['title']) {
            $arr['title'] = json_encode(crypto_encapsulate($arr['title'], $key));
        }
        if ($arr['body']) {
            $arr['body'] = json_encode(crypto_encapsulate($arr['body'], $key));
        }
    }
    if (array_key_exists('revision', $x)) {
        // extended export encoding
        $arr['revision'] = $x['revision'];
        $arr['allow_cid'] = $x['allow_cid'];
        $arr['allow_gid'] = $x['allow_gid'];
        $arr['deny_cid'] = $x['deny_cid'];
        $arr['deny_gid'] = $x['deny_gid'];
        $arr['layout_mid'] = $x['layout_mid'];
        $arr['postopts'] = $x['postopts'];
        $arr['resource_id'] = $x['resource_id'];
        $arr['resource_type'] = $x['resource_type'];
        $arr['item_restrict'] = $x['item_restrict'];
        $arr['item_flags'] = $x['item_flags'];
        $arr['attach'] = $x['attach'];
    }
    return $arr;
}
コード例 #2
0
ファイル: zot.php プロジェクト: 23n/hubzilla
function zotinfo($arr)
{
    $ret = array('success' => false);
    $zhash = x($arr, 'guid_hash') ? $arr['guid_hash'] : '';
    $zguid = x($arr, 'guid') ? $arr['guid'] : '';
    $zguid_sig = x($arr, 'guid_sig') ? $arr['guid_sig'] : '';
    $zaddr = x($arr, 'address') ? $arr['address'] : '';
    $ztarget = x($arr, 'target') ? $arr['target'] : '';
    $zsig = x($arr, 'target_sig') ? $arr['target_sig'] : '';
    $zkey = x($arr, 'key') ? $arr['key'] : '';
    $mindate = x($arr, 'mindate') ? $arr['mindate'] : '';
    $feed = x($arr, 'feed') ? intval($arr['feed']) : 0;
    if ($ztarget) {
        if (!$zkey || !$zsig || !rsa_verify($ztarget, base64url_decode($zsig), $zkey)) {
            logger('zfinger: invalid target signature');
            $ret['message'] = t("invalid target signature");
            return $ret;
        }
    }
    $r = null;
    if (strlen($zhash)) {
        $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash \n\t\t\twhere channel_hash = '%s' limit 1", dbesc($zhash));
    } elseif (strlen($zguid) && strlen($zguid_sig)) {
        $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash \n\t\t\twhere channel_guid = '%s' and channel_guid_sig = '%s' limit 1", dbesc($zguid), dbesc($zguid_sig));
    } elseif (strlen($zaddr)) {
        if (strpos($zaddr, '[system]') === false) {
            /* normal address lookup */
            $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash\n\t\t\t\twhere ( channel_address = '%s' or xchan_addr = '%s' ) limit 1", dbesc($zaddr), dbesc($zaddr));
        } else {
            /**
             * The special address '[system]' will return a system channel if one has been defined,
             * Or the first valid channel we find if there are no system channels. 
             *
             * This is used by magic-auth if we have no prior communications with this site - and
             * returns an identity on this site which we can use to create a valid hub record so that
             * we can exchange signed messages. The precise identity is irrelevant. It's the hub
             * information that we really need at the other end - and this will return it.
             *
             */
            $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash\n\t\t\t\twhere channel_system = 1 order by channel_id limit 1");
            if (!$r) {
                $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash\n\t\t\t\t\twhere channel_removed = 0 order by channel_id limit 1");
            }
        }
    } else {
        $ret['message'] = 'Invalid request';
        return $ret;
    }
    if (!$r) {
        $ret['message'] = 'Item not found.';
        return $ret;
    }
    $e = $r[0];
    $id = $e['channel_id'];
    $sys_channel = intval($e['channel_system']) ? true : false;
    $special_channel = $e['channel_pageflags'] & PAGE_PREMIUM ? true : false;
    $adult_channel = $e['channel_pageflags'] & PAGE_ADULT ? true : false;
    $censored = $e['channel_pageflags'] & PAGE_CENSORED ? true : false;
    $searchable = $e['channel_pageflags'] & PAGE_HIDDEN ? false : true;
    $deleted = intval($e['xchan_deleted']) ? true : false;
    if ($deleted || $censored || $sys_channel) {
        $searchable = false;
    }
    $public_forum = false;
    $role = get_pconfig($e['channel_id'], 'system', 'permissions_role');
    if ($role === 'forum' || $role === 'repository') {
        $public_forum = true;
    } else {
        // check if it has characteristics of a public forum based on custom permissions.
        $t = q("select abook_my_perms from abook where abook_channel = %d and abook_self = 1 limit 1", intval($e['channel_id']));
        if ($t && ($t[0]['abook_my_perms'] & PERMS_W_TAGWALL && !($t[0]['abook_my_perms'] & PERMS_W_STREAM))) {
            $public_forum = true;
        }
    }
    //  This is for birthdays and keywords, but must check access permissions
    $p = q("select * from profile where uid = %d and is_default = 1", intval($e['channel_id']));
    $profile = array();
    if ($p) {
        if (!intval($p[0]['publish'])) {
            $searchable = false;
        }
        $profile['description'] = $p[0]['pdesc'];
        $profile['birthday'] = $p[0]['dob'];
        if ($profile['birthday'] != '0000-00-00' && ($bd = z_birthday($p[0]['dob'], $e['channel_timezone'])) !== '') {
            $profile['next_birthday'] = $bd;
        }
        if ($age = age($p[0]['dob'], $e['channel_timezone'], '')) {
            $profile['age'] = $age;
        }
        $profile['gender'] = $p[0]['gender'];
        $profile['marital'] = $p[0]['marital'];
        $profile['sexual'] = $p[0]['sexual'];
        $profile['locale'] = $p[0]['locality'];
        $profile['region'] = $p[0]['region'];
        $profile['postcode'] = $p[0]['postal_code'];
        $profile['country'] = $p[0]['country_name'];
        $profile['about'] = $p[0]['about'];
        $profile['homepage'] = $p[0]['homepage'];
        $profile['hometown'] = $p[0]['hometown'];
        if ($p[0]['keywords']) {
            $tags = array();
            $k = explode(' ', $p[0]['keywords']);
            if ($k) {
                foreach ($k as $kk) {
                    if (trim($kk, " \t\n\r\v,")) {
                        $tags[] = trim($kk, " \t\n\r\v,");
                    }
                }
            }
            if ($tags) {
                $profile['keywords'] = $tags;
            }
        }
    }
    $ret['success'] = true;
    // Communication details
    $ret['guid'] = $e['xchan_guid'];
    $ret['guid_sig'] = $e['xchan_guid_sig'];
    $ret['key'] = $e['xchan_pubkey'];
    $ret['name'] = $e['xchan_name'];
    $ret['name_updated'] = $e['xchan_name_date'];
    $ret['address'] = $e['xchan_addr'];
    $ret['photo_mimetype'] = $e['xchan_photo_mimetype'];
    $ret['photo'] = $e['xchan_photo_l'];
    $ret['photo_updated'] = $e['xchan_photo_date'];
    $ret['url'] = $e['xchan_url'];
    $ret['connections_url'] = $e['xchan_connurl'] ? $e['xchan_connurl'] : z_root() . '/poco/' . $e['channel_address'];
    $ret['target'] = $ztarget;
    $ret['target_sig'] = $zsig;
    $ret['searchable'] = $searchable;
    $ret['adult_content'] = $adult_channel;
    $ret['public_forum'] = $public_forum;
    if ($deleted) {
        $ret['deleted'] = $deleted;
    }
    if (intval($e['channel_removed'])) {
        $ret['deleted_locally'] = true;
    }
    // premium or other channel desiring some contact with potential followers before connecting.
    // This is a template - %s will be replaced with the follow_url we discover for the return channel.
    if ($special_channel) {
        $ret['connect_url'] = z_root() . '/connect/' . $e['channel_address'];
    }
    // This is a template for our follow url, %s will be replaced with a webbie
    $ret['follow_url'] = z_root() . '/follow?f=&url=%s';
    $ztarget_hash = $ztarget && $zsig ? make_xchan_hash($ztarget, $zsig) : '';
    $permissions = get_all_perms($e['channel_id'], $ztarget_hash, false);
    if ($ztarget_hash) {
        $permissions['connected'] = false;
        $b = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($ztarget_hash), intval($e['channel_id']));
        if ($b) {
            $permissions['connected'] = true;
        }
    }
    $ret['permissions'] = $ztarget && $zkey ? crypto_encapsulate(json_encode($permissions), $zkey) : $permissions;
    if ($permissions['view_profile']) {
        $ret['profile'] = $profile;
    }
    // array of (verified) hubs this channel uses
    $x = zot_encode_locations($e);
    if ($x) {
        $ret['locations'] = $x;
    }
    $ret['site'] = array();
    $ret['site']['url'] = z_root();
    $ret['site']['url_sig'] = base64url_encode(rsa_sign(z_root(), $e['channel_prvkey']));
    $dirmode = get_config('system', 'directory_mode');
    if ($dirmode === false || $dirmode == DIRECTORY_MODE_NORMAL) {
        $ret['site']['directory_mode'] = 'normal';
    }
    if ($dirmode == DIRECTORY_MODE_PRIMARY) {
        $ret['site']['directory_mode'] = 'primary';
    } elseif ($dirmode == DIRECTORY_MODE_SECONDARY) {
        $ret['site']['directory_mode'] = 'secondary';
    } elseif ($dirmode == DIRECTORY_MODE_STANDALONE) {
        $ret['site']['directory_mode'] = 'standalone';
    }
    if ($dirmode != DIRECTORY_MODE_NORMAL) {
        $ret['site']['directory_url'] = z_root() . '/dirsearch';
    }
    // hide detailed site information if you're off the grid
    if ($dirmode != DIRECTORY_MODE_STANDALONE) {
        $register_policy = intval(get_config('system', 'register_policy'));
        if ($register_policy == REGISTER_CLOSED) {
            $ret['site']['register_policy'] = 'closed';
        }
        if ($register_policy == REGISTER_APPROVE) {
            $ret['site']['register_policy'] = 'approve';
        }
        if ($register_policy == REGISTER_OPEN) {
            $ret['site']['register_policy'] = 'open';
        }
        $access_policy = intval(get_config('system', 'access_policy'));
        if ($access_policy == ACCESS_PRIVATE) {
            $ret['site']['access_policy'] = 'private';
        }
        if ($access_policy == ACCESS_PAID) {
            $ret['site']['access_policy'] = 'paid';
        }
        if ($access_policy == ACCESS_FREE) {
            $ret['site']['access_policy'] = 'free';
        }
        if ($access_policy == ACCESS_TIERED) {
            $ret['site']['access_policy'] = 'tiered';
        }
        $ret['site']['accounts'] = account_total();
        require_once 'include/identity.php';
        $ret['site']['channels'] = channel_total();
        $ret['site']['version'] = PLATFORM_NAME . ' ' . RED_VERSION . '[' . DB_UPDATE_VERSION . ']';
        $ret['site']['admin'] = get_config('system', 'admin_email');
        $a = get_app();
        $visible_plugins = array();
        if (is_array($a->plugins) && count($a->plugins)) {
            $r = q("select * from addon where hidden = 0");
            if ($r) {
                foreach ($r as $rr) {
                    $visible_plugins[] = $rr['name'];
                }
            }
        }
        $ret['site']['plugins'] = $visible_plugins;
        $ret['site']['sitehash'] = get_config('system', 'location_hash');
        $ret['site']['sitename'] = get_config('system', 'sitename');
        $ret['site']['sellpage'] = get_config('system', 'sellpage');
        $ret['site']['location'] = get_config('system', 'site_location');
        $ret['site']['realm'] = get_directory_realm();
        $ret['site']['project'] = PLATFORM_NAME;
    }
    check_zotinfo($e, $x, $ret);
    call_hooks('zot_finger', $ret);
    return $ret;
}
コード例 #3
0
ファイル: identity.php プロジェクト: 23n/hubzilla
/**
 * @brief Create a new channel.
 *
 * Also creates the related xchan, hubloc, profile, and "self" abook records,
 * and an empty "Friends" group/collection for the new channel.
 *
 * @param array $arr assoziative array with:
 *  * \e string \b name full name of channel
 *  * \e string \b nickname "email/url-compliant" nickname
 *  * \e int \b account_id to attach with this channel
 *  * [other identity fields as desired]
 *
 * @returns array
 *     'success' => boolean true or false
 *     'message' => optional error text if success is false
 *     'channel' => if successful the created channel array
 */
function create_identity($arr)
{
    $a = get_app();
    $ret = array('success' => false);
    if (!$arr['account_id']) {
        $ret['message'] = t('No account identifier');
        return $ret;
    }
    $ret = identity_check_service_class($arr['account_id']);
    if (!$ret['success']) {
        return $ret;
    }
    // save this for auto_friending
    $total_identities = $ret['total_identities'];
    $nick = mb_strtolower(trim($arr['nickname']));
    if (!$nick) {
        $ret['message'] = t('Nickname is required.');
        return $ret;
    }
    $name = escape_tags($arr['name']);
    $pageflags = x($arr, 'pageflags') ? intval($arr['pageflags']) : PAGE_NORMAL;
    $system = x($arr, 'system') ? intval($arr['system']) : 0;
    $name_error = validate_channelname($arr['name']);
    if ($name_error) {
        $ret['message'] = $name_error;
        return $ret;
    }
    if ($nick === 'sys' && !$system) {
        $ret['message'] = t('Reserved nickname. Please choose another.');
        return $ret;
    }
    if (check_webbie(array($nick)) !== $nick) {
        $ret['message'] = t('Nickname has unsupported characters or is already being used on this site.');
        return $ret;
    }
    $guid = zot_new_uid($nick);
    $key = new_keypair(4096);
    $sig = base64url_encode(rsa_sign($guid, $key['prvkey']));
    $hash = make_xchan_hash($guid, $sig);
    // Force a few things on the short term until we can provide a theme or app with choice
    $publish = 1;
    if (array_key_exists('publish', $arr)) {
        $publish = intval($arr['publish']);
    }
    $primary = true;
    if (array_key_exists('primary', $arr)) {
        $primary = intval($arr['primary']);
    }
    $role_permissions = null;
    $global_perms = get_perms();
    if (array_key_exists('permissions_role', $arr) && $arr['permissions_role']) {
        $role_permissions = get_role_perms($arr['permissions_role']);
        if ($role_permissions) {
            foreach ($role_permissions as $p => $v) {
                if (strpos($p, 'channel_') !== false) {
                    $perms_keys .= ', ' . $p;
                    $perms_vals .= ', ' . intval($v);
                }
                if ($p === 'directory_publish') {
                    $publish = intval($v);
                }
            }
        }
    } else {
        $defperms = site_default_perms();
        foreach ($defperms as $p => $v) {
            $perms_keys .= ', ' . $global_perms[$p][0];
            $perms_vals .= ', ' . intval($v);
        }
    }
    $expire = 0;
    $r = q("insert into channel ( channel_account_id, channel_primary, \n\t\tchannel_name, channel_address, channel_guid, channel_guid_sig,\n\t\tchannel_hash, channel_prvkey, channel_pubkey, channel_pageflags, channel_system, channel_expire_days, channel_timezone {$perms_keys} )\n\t\tvalues ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s' {$perms_vals} ) ", intval($arr['account_id']), intval($primary), dbesc($name), dbesc($nick), dbesc($guid), dbesc($sig), dbesc($hash), dbesc($key['prvkey']), dbesc($key['pubkey']), intval($pageflags), intval($system), intval($expire), dbesc($a->timezone));
    $r = q("select * from channel where channel_account_id = %d \n\t\tand channel_guid = '%s' limit 1", intval($arr['account_id']), dbesc($guid));
    if (!$r) {
        $ret['message'] = t('Unable to retrieve created identity');
        return $ret;
    }
    $ret['channel'] = $r[0];
    if (intval($arr['account_id'])) {
        set_default_login_identity($arr['account_id'], $ret['channel']['channel_id'], false);
    }
    // Create a verified hub location pointing to this site.
    $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_primary, \n\t\thubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey, hubloc_network )\n\t\tvalues ( '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s' )", dbesc($guid), dbesc($sig), dbesc($hash), dbesc($ret['channel']['channel_address'] . '@' . get_app()->get_hostname()), intval($primary), dbesc(z_root()), dbesc(base64url_encode(rsa_sign(z_root(), $ret['channel']['channel_prvkey']))), dbesc(get_app()->get_hostname()), dbesc(z_root() . '/post'), dbesc(get_config('system', 'pubkey')), dbesc('zot'));
    if (!$r) {
        logger('create_identity: Unable to store hub location');
    }
    $newuid = $ret['channel']['channel_id'];
    $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_guid_sig, xchan_pubkey, xchan_photo_l, xchan_photo_m, xchan_photo_s, xchan_addr, xchan_url, xchan_follow, xchan_connurl, xchan_name, xchan_network, xchan_photo_date, xchan_name_date, xchan_system ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d)", dbesc($hash), dbesc($guid), dbesc($sig), dbesc($key['pubkey']), dbesc($a->get_baseurl() . "/photo/profile/l/{$newuid}"), dbesc($a->get_baseurl() . "/photo/profile/m/{$newuid}"), dbesc($a->get_baseurl() . "/photo/profile/s/{$newuid}"), dbesc($ret['channel']['channel_address'] . '@' . get_app()->get_hostname()), dbesc(z_root() . '/channel/' . $ret['channel']['channel_address']), dbesc(z_root() . '/follow?f=&url=%s'), dbesc(z_root() . '/poco/' . $ret['channel']['channel_address']), dbesc($ret['channel']['channel_name']), dbesc('zot'), dbesc(datetime_convert()), dbesc(datetime_convert()), intval($system));
    // Not checking return value.
    // It's ok for this to fail if it's an imported channel, and therefore the hash is a duplicate
    $r = q("INSERT INTO profile ( aid, uid, profile_guid, profile_name, is_default, publish, name, photo, thumb)\n\t\tVALUES ( %d, %d, '%s', '%s', %d, %d, '%s', '%s', '%s') ", intval($ret['channel']['channel_account_id']), intval($newuid), dbesc(random_string()), t('Default Profile'), 1, $publish, dbesc($ret['channel']['channel_name']), dbesc($a->get_baseurl() . "/photo/profile/l/{$newuid}"), dbesc($a->get_baseurl() . "/photo/profile/m/{$newuid}"));
    if ($role_permissions) {
        $myperms = array_key_exists('perms_auto', $role_permissions) && $role_permissions['perms_auto'] ? intval($role_permissions['perms_accept']) : 0;
    } else {
        $myperms = PERMS_R_STREAM | PERMS_R_PROFILE | PERMS_R_PHOTOS | PERMS_R_ABOOK | PERMS_W_STREAM | PERMS_W_WALL | PERMS_W_COMMENT | PERMS_W_MAIL | PERMS_W_CHAT | PERMS_R_STORAGE | PERMS_R_PAGES | PERMS_W_LIKE;
    }
    $r = q("insert into abook ( abook_account, abook_channel, abook_xchan, abook_closeness, abook_created, abook_updated, abook_self, abook_my_perms )\n\t\tvalues ( %d, %d, '%s', %d, '%s', '%s', %d, %d ) ", intval($ret['channel']['channel_account_id']), intval($newuid), dbesc($hash), intval(0), dbesc(datetime_convert()), dbesc(datetime_convert()), intval(1), intval($myperms));
    if (intval($ret['channel']['channel_account_id'])) {
        // Save our permissions role so we can perhaps call it up and modify it later.
        if ($role_permissions) {
            set_pconfig($newuid, 'system', 'permissions_role', $arr['permissions_role']);
            if (array_key_exists('online', $role_permissions)) {
                set_pconfig($newuid, 'system', 'hide_presence', 1 - intval($role_permissions['online']));
            }
            if (array_key_exists('perms_auto', $role_permissions)) {
                set_pconfig($newuid, 'system', 'autoperms', $role_permissions['perms_auto'] ? $role_permissions['perms_accept'] : 0);
            }
        }
        // Create a group with yourself as a member. This allows somebody to use it
        // right away as a default group for new contacts.
        require_once 'include/group.php';
        group_add($newuid, t('Friends'));
        group_add_member($newuid, t('Friends'), $ret['channel']['channel_hash']);
        // if our role_permissions indicate that we're using a default collection ACL, add it.
        if (is_array($role_permissions) && $role_permissions['default_collection']) {
            $r = q("select hash from groups where uid = %d and name = '%s' limit 1", intval($newuid), dbesc(t('Friends')));
            if ($r) {
                q("update channel set channel_default_group = '%s', channel_allow_gid = '%s' where channel_id = %d", dbesc($r[0]['hash']), dbesc('<' . $r[0]['hash'] . '>'), intval($newuid));
            }
        }
        if (!$system) {
            set_pconfig($ret['channel']['channel_id'], 'system', 'photo_path', '%Y-%m');
            set_pconfig($ret['channel']['channel_id'], 'system', 'attach_path', '%Y-%m');
        }
        // auto-follow any of the hub's pre-configured channel choices.
        // Only do this if it's the first channel for this account;
        // otherwise it could get annoying. Don't make this list too big
        // or it will impact registration time.
        $accts = get_config('system', 'auto_follow');
        if ($accts && !$total_identities) {
            require_once 'include/follow.php';
            if (!is_array($accts)) {
                $accts = array($accts);
            }
            foreach ($accts as $acct) {
                if (trim($acct)) {
                    new_contact($newuid, trim($acct), $ret['channel'], false);
                }
            }
        }
        call_hooks('create_identity', $newuid);
        proc_run('php', 'include/directory.php', $ret['channel']['channel_id']);
    }
    $ret['success'] = true;
    return $ret;
}
コード例 #4
0
ファイル: Import.php プロジェクト: phellmes/hubzilla
 function import_account($account_id)
 {
     if (!$account_id) {
         logger("import_account: No account ID supplied");
         return;
     }
     $max_identities = account_service_class_fetch($account_id, 'total_identities');
     $max_friends = account_service_class_fetch($account_id, 'total_channels');
     $max_feeds = account_service_class_fetch($account_id, 'total_feeds');
     if ($max_identities !== false) {
         $r = q("select channel_id from channel where channel_account_id = %d", intval($account_id));
         if ($r && count($r) > $max_identities) {
             notice(sprintf(t('Your service plan only allows %d channels.'), $max_identities) . EOL);
             return;
         }
     }
     $data = null;
     $seize = x($_REQUEST, 'make_primary') ? intval($_REQUEST['make_primary']) : 0;
     $import_posts = x($_REQUEST, 'import_posts') ? intval($_REQUEST['import_posts']) : 0;
     $src = $_FILES['filename']['tmp_name'];
     $filename = basename($_FILES['filename']['name']);
     $filesize = intval($_FILES['filename']['size']);
     $filetype = $_FILES['filename']['type'];
     $completed = array_key_exists('import_step', $_SESSION) ? intval($_SESSION['import_step']) : 0;
     if ($completed) {
         logger('saved import step: ' . $_SESSION['import_step']);
     }
     if ($src) {
         // This is OS specific and could also fail if your tmpdir isn't very large
         // mostly used for Diaspora which exports gzipped files.
         if (strpos($filename, '.gz')) {
             @rename($src, $src . '.gz');
             @system('gunzip ' . escapeshellarg($src . '.gz'));
         }
         if ($filesize) {
             $data = @file_get_contents($src);
         }
         unlink($src);
     }
     if (!$src) {
         $old_address = x($_REQUEST, 'old_address') ? $_REQUEST['old_address'] : '';
         if (!$old_address) {
             logger('mod_import: nothing to import.');
             notice(t('Nothing to import.') . EOL);
             return;
         }
         $email = x($_REQUEST, 'email') ? $_REQUEST['email'] : '';
         $password = x($_REQUEST, 'password') ? $_REQUEST['password'] : '';
         $channelname = substr($old_address, 0, strpos($old_address, '@'));
         $servername = substr($old_address, strpos($old_address, '@') + 1);
         $scheme = 'https://';
         $api_path = '/api/red/channel/export/basic?f=&channel=' . $channelname;
         if ($import_posts) {
             $api_path .= '&posts=1';
         }
         $binary = false;
         $redirects = 0;
         $opts = array('http_auth' => $email . ':' . $password);
         $url = $scheme . $servername . $api_path;
         $ret = z_fetch_url($url, $binary, $redirects, $opts);
         if (!$ret['success']) {
             $ret = z_fetch_url('http://' . $servername . $api_path, $binary, $redirects, $opts);
         }
         if ($ret['success']) {
             $data = $ret['body'];
         } else {
             notice(t('Unable to download data from old server') . EOL);
         }
     }
     if (!$data) {
         logger('mod_import: empty file.');
         notice(t('Imported file is empty.') . EOL);
         return;
     }
     $data = json_decode($data, true);
     //	logger('import: data: ' . print_r($data,true));
     //	print_r($data);
     if (array_key_exists('user', $data) && array_key_exists('version', $data)) {
         require_once 'include/Import/import_diaspora.php';
         import_diaspora($data);
         return;
     }
     $moving = false;
     if (array_key_exists('compatibility', $data) && array_key_exists('database', $data['compatibility'])) {
         $v1 = substr($data['compatibility']['database'], -4);
         $v2 = substr(DB_UPDATE_VERSION, -4);
         if ($v2 > $v1) {
             $t = sprintf(t('Warning: Database versions differ by %1$d updates.'), $v2 - $v1);
             notice($t);
         }
         if (array_key_exists('server_role', $data['compatibility']) && $data['compatibility']['server_role'] == 'basic') {
             $moving = true;
         }
     }
     if ($moving) {
         $seize = 1;
     }
     // import channel
     $relocate = array_key_exists('relocate', $data) ? $data['relocate'] : null;
     if (array_key_exists('channel', $data)) {
         if ($completed < 1) {
             $channel = import_channel($data['channel'], $account_id, $seize);
         } else {
             $r = q("select * from channel where channel_account_id = %d and channel_guid = '%s' limit 1", intval($account_id), dbesc($channel['channel_guid']));
             if ($r) {
                 $channel = $r[0];
             }
         }
         if (!$channel) {
             logger('mod_import: channel not found. ', print_r($channel, true));
             notice(t('Cloned channel not found. Import failed.') . EOL);
             return;
         }
     }
     if (!$channel) {
         $channel = \App::get_channel();
     }
     if (!$channel) {
         logger('mod_import: channel not found. ', print_r($channel, true));
         notice(t('No channel. Import failed.') . EOL);
         return;
     }
     if ($completed < 2) {
         if (is_array($data['config'])) {
             import_config($channel, $data['config']);
         }
         logger('import step 2');
         $_SESSION['import_step'] = 2;
     }
     if ($completed < 3) {
         if ($data['photo']) {
             require_once 'include/photo/photo_driver.php';
             import_channel_photo(base64url_decode($data['photo']['data']), $data['photo']['type'], $account_id, $channel['channel_id']);
         }
         if (is_array($data['profile'])) {
             import_profiles($channel, $data['profile']);
         }
         logger('import step 3');
         $_SESSION['import_step'] = 3;
     }
     if ($completed < 4) {
         if (is_array($data['hubloc']) && !$moving) {
             import_hublocs($channel, $data['hubloc'], $seize);
         }
         logger('import step 4');
         $_SESSION['import_step'] = 4;
     }
     if ($completed < 5) {
         // create new hubloc for the new channel at this site
         $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_network, hubloc_primary, \n\t\t\t\thubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey )\n\t\t\t\tvalues ( '%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s' )", dbesc($channel['channel_guid']), dbesc($channel['channel_guid_sig']), dbesc($channel['channel_hash']), dbesc(channel_reddress($channel)), dbesc('zot'), intval($seize ? 1 : 0), dbesc(z_root()), dbesc(base64url_encode(rsa_sign(z_root(), $channel['channel_prvkey']))), dbesc(\App::get_hostname()), dbesc(z_root() . '/post'), dbesc(get_config('system', 'pubkey')));
         // reset the original primary hubloc if it is being seized
         if ($seize) {
             $r = q("update hubloc set hubloc_primary = 0 where hubloc_primary = 1 and hubloc_hash = '%s' and hubloc_url != '%s' ", dbesc($channel['channel_hash']), dbesc(z_root()));
         }
         logger('import step 5');
         $_SESSION['import_step'] = 5;
     }
     if ($completed < 6) {
         // import xchans and contact photos
         if ($seize) {
             // replace any existing xchan we may have on this site if we're seizing control
             $r = q("delete from xchan where xchan_hash = '%s'", dbesc($channel['channel_hash']));
             $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_guid_sig, xchan_pubkey, xchan_photo_l, xchan_photo_m, xchan_photo_s, xchan_addr, xchan_url, xchan_follow, xchan_connurl, xchan_name, xchan_network, xchan_photo_date, xchan_name_date, xchan_hidden, xchan_orphan, xchan_censored, xchan_selfcensored, xchan_system, xchan_pubforum, xchan_deleted ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, %d, %d, %d )", dbesc($channel['channel_hash']), dbesc($channel['channel_guid']), dbesc($channel['channel_guid_sig']), dbesc($channel['channel_pubkey']), dbesc(z_root() . "/photo/profile/l/" . $channel['channel_id']), dbesc(z_root() . "/photo/profile/m/" . $channel['channel_id']), dbesc(z_root() . "/photo/profile/s/" . $channel['channel_id']), dbesc(channel_reddress($channel)), dbesc(z_root() . '/channel/' . $channel['channel_address']), dbesc(z_root() . '/follow?f=&url=%s'), dbesc(z_root() . '/poco/' . $channel['channel_address']), dbesc($channel['channel_name']), dbesc('zot'), dbesc(datetime_convert()), dbesc(datetime_convert()), 0, 0, 0, 0, 0, 0, 0);
         }
         logger('import step 6');
         $_SESSION['import_step'] = 6;
     }
     if ($completed < 7) {
         $xchans = $data['xchan'];
         if ($xchans) {
             foreach ($xchans as $xchan) {
                 $hash = make_xchan_hash($xchan['xchan_guid'], $xchan['xchan_guid_sig']);
                 if ($xchan['xchan_network'] === 'zot' && $hash !== $xchan['xchan_hash']) {
                     logger('forged xchan: ' . print_r($xchan, true));
                     continue;
                 }
                 if (!array_key_exists('xchan_hidden', $xchan)) {
                     $xchan['xchan_hidden'] = $xchan['xchan_flags'] & 0x1 ? 1 : 0;
                     $xchan['xchan_orphan'] = $xchan['xchan_flags'] & 0x2 ? 1 : 0;
                     $xchan['xchan_censored'] = $xchan['xchan_flags'] & 0x4 ? 1 : 0;
                     $xchan['xchan_selfcensored'] = $xchan['xchan_flags'] & 0x8 ? 1 : 0;
                     $xchan['xchan_system'] = $xchan['xchan_flags'] & 0x10 ? 1 : 0;
                     $xchan['xchan_pubforum'] = $xchan['xchan_flags'] & 0x20 ? 1 : 0;
                     $xchan['xchan_deleted'] = $xchan['xchan_flags'] & 0x1000 ? 1 : 0;
                 }
                 $r = q("select xchan_hash from xchan where xchan_hash = '%s' limit 1", dbesc($xchan['xchan_hash']));
                 if ($r) {
                     continue;
                 }
                 dbesc_array($xchan);
                 $r = dbq("INSERT INTO xchan (`" . implode("`, `", array_keys($xchan)) . "`) VALUES ('" . implode("', '", array_values($xchan)) . "')");
                 require_once 'include/photo/photo_driver.php';
                 $photos = import_xchan_photo($xchan['xchan_photo_l'], $xchan['xchan_hash']);
                 if ($photos[4]) {
                     $photodate = NULL_DATE;
                 } else {
                     $photodate = $xchan['xchan_photo_date'];
                 }
                 $r = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s', xchan_photo_date = '%s'\n\t\t\t\t\t\twhere xchan_hash = '%s'", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($photodate), dbesc($xchan['xchan_hash']));
             }
         }
         logger('import step 7');
         $_SESSION['import_step'] = 7;
     }
     // FIXME - ensure we have an xchan if somebody is trying to pull a fast one
     if ($completed < 8) {
         $friends = 0;
         $feeds = 0;
         // import contacts
         $abooks = $data['abook'];
         if ($abooks) {
             foreach ($abooks as $abook) {
                 $abook_copy = $abook;
                 $abconfig = null;
                 if (array_key_exists('abconfig', $abook) && is_array($abook['abconfig']) && count($abook['abconfig'])) {
                     $abconfig = $abook['abconfig'];
                 }
                 unset($abook['abook_id']);
                 unset($abook['abook_rating']);
                 unset($abook['abook_rating_text']);
                 unset($abook['abconfig']);
                 unset($abook['abook_their_perms']);
                 unset($abook['abook_my_perms']);
                 $abook['abook_account'] = $account_id;
                 $abook['abook_channel'] = $channel['channel_id'];
                 if (!array_key_exists('abook_blocked', $abook)) {
                     $abook['abook_blocked'] = $abook['abook_flags'] & 0x1 ? 1 : 0;
                     $abook['abook_ignored'] = $abook['abook_flags'] & 0x2 ? 1 : 0;
                     $abook['abook_hidden'] = $abook['abook_flags'] & 0x4 ? 1 : 0;
                     $abook['abook_archived'] = $abook['abook_flags'] & 0x8 ? 1 : 0;
                     $abook['abook_pending'] = $abook['abook_flags'] & 0x10 ? 1 : 0;
                     $abook['abook_unconnected'] = $abook['abook_flags'] & 0x20 ? 1 : 0;
                     $abook['abook_self'] = $abook['abook_flags'] & 0x80 ? 1 : 0;
                     $abook['abook_feed'] = $abook['abook_flags'] & 0x100 ? 1 : 0;
                 }
                 if ($abook['abook_self']) {
                     $role = get_pconfig($channel['channel_id'], 'system', 'permissions_role');
                     if ($role === 'forum' || $abook['abook_my_perms'] & PERMS_W_TAGWALL) {
                         q("update xchan set xchan_pubforum = 1 where xchan_hash = '%s' ", dbesc($abook['abook_xchan']));
                     }
                 } else {
                     if ($max_friends !== false && $friends > $max_friends) {
                         continue;
                     }
                     if ($max_feeds !== false && intval($abook['abook_feed']) && $feeds > $max_feeds) {
                         continue;
                     }
                 }
                 dbesc_array($abook);
                 $r = dbq("INSERT INTO abook (`" . implode("`, `", array_keys($abook)) . "`) VALUES ('" . implode("', '", array_values($abook)) . "')");
                 $friends++;
                 if (intval($abook['abook_feed'])) {
                     $feeds++;
                 }
                 translate_abook_perms_inbound($channel, $abook_copy);
                 if ($abconfig) {
                     // @fixme does not handle sync of del_abconfig
                     foreach ($abconfig as $abc) {
                         set_abconfig($channel['channel_id'], $abc['xchan'], $abc['cat'], $abc['k'], $abc['v']);
                     }
                 }
             }
         }
         logger('import step 8');
         $_SESSION['import_step'] = 8;
     }
     if ($completed < 9) {
         $groups = $data['group'];
         if ($groups) {
             $saved = array();
             foreach ($groups as $group) {
                 $saved[$group['hash']] = array('old' => $group['id']);
                 if (array_key_exists('name', $group)) {
                     $group['gname'] = $group['name'];
                     unset($group['name']);
                 }
                 unset($group['id']);
                 $group['uid'] = $channel['channel_id'];
                 dbesc_array($group);
                 $r = dbq("INSERT INTO groups (`" . implode("`, `", array_keys($group)) . "`) VALUES ('" . implode("', '", array_values($group)) . "')");
             }
             $r = q("select * from `groups` where uid = %d", intval($channel['channel_id']));
             if ($r) {
                 foreach ($r as $rr) {
                     $saved[$rr['hash']]['new'] = $rr['id'];
                 }
             }
         }
         $group_members = $data['group_member'];
         if ($group_members) {
             foreach ($group_members as $group_member) {
                 unset($group_member['id']);
                 $group_member['uid'] = $channel['channel_id'];
                 foreach ($saved as $x) {
                     if ($x['old'] == $group_member['gid']) {
                         $group_member['gid'] = $x['new'];
                     }
                 }
                 dbesc_array($group_member);
                 $r = dbq("INSERT INTO group_member (`" . implode("`, `", array_keys($group_member)) . "`) VALUES ('" . implode("', '", array_values($group_member)) . "')");
             }
         }
         logger('import step 9');
         $_SESSION['import_step'] = 9;
     }
     if (is_array($data['obj'])) {
         import_objs($channel, $data['obj']);
     }
     if (is_array($data['likes'])) {
         import_likes($channel, $data['likes']);
     }
     if (is_array($data['app'])) {
         import_apps($channel, $data['app']);
     }
     if (is_array($data['chatroom'])) {
         import_chatrooms($channel, $data['chatroom']);
     }
     if (is_array($data['conv'])) {
         import_conv($channel, $data['conv']);
     }
     if (is_array($data['mail'])) {
         import_mail($channel, $data['mail']);
     }
     if (is_array($data['event'])) {
         import_events($channel, $data['event']);
     }
     if (is_array($data['event_item'])) {
         import_items($channel, $data['event_item'], false, $relocate);
     }
     if (is_array($data['menu'])) {
         import_menus($channel, $data['menu']);
     }
     $addon = array('channel' => $channel, 'data' => $data);
     call_hooks('import_channel', $addon);
     $saved_notification_flags = notifications_off($channel['channel_id']);
     if ($import_posts && array_key_exists('item', $data) && $data['item']) {
         import_items($channel, $data['item'], false, $relocate);
     }
     notifications_on($channel['channel_id'], $saved_notification_flags);
     if (array_key_exists('item_id', $data) && $data['item_id']) {
         import_item_ids($channel, $data['item_id']);
     }
     // FIXME - ensure we have a self entry if somebody is trying to pull a fast one
     // send out refresh requests
     // notify old server that it may no longer be primary.
     \Zotlabs\Daemon\Master::Summon(array('Notifier', 'location', $channel['channel_id']));
     // This will indirectly perform a refresh_all *and* update the directory
     \Zotlabs\Daemon\Master::Summon(array('Directory', $channel['channel_id']));
     notice(t('Import completed.') . EOL);
     change_channel($channel['channel_id']);
     unset($_SESSION['import_step']);
     goaway(z_root() . '/network');
 }
コード例 #5
0
ファイル: zot.php プロジェクト: HaakonME/redmatrix
/**
 * @brief Process a message request.
 *
 * If a site receives a comment to a post but finds they have no parent to attach it with, they
 * may send a 'request' packet containing the message_id of the missing parent. This is the handler
 * for that packet. We will create a message_list array of the entire conversation starting with
 * the missing parent and invoke delivery to the sender of the packet.
 *
 * include/deliver.php (for local delivery) and mod/post.php (for web delivery) detect the existence of
 * this 'message_list' at the destination and split it into individual messages which are
 * processed/delivered in order.
 *
 * Called from mod/post.php
 *
 * @param array $data
 * @return array
 */
function zot_process_message_request($data)
{
    $ret = array('success' => false);
    if (!$data['message_id']) {
        $ret['message'] = 'no message_id';
        logger('no message_id');
        return $ret;
    }
    $sender = $data['sender'];
    $sender_hash = make_xchan_hash($sender['guid'], $sender['guid_sig']);
    /*
     * Find the local channel in charge of this post (the first and only recipient of the request packet)
     */
    $arr = $data['recipients'][0];
    $recip_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']);
    $c = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_hash = '%s' limit 1", dbesc($recip_hash));
    if (!$c) {
        logger('recipient channel not found.');
        $ret['message'] .= 'recipient not found.' . EOL;
        return $ret;
    }
    /*
     * fetch the requested conversation
     */
    $messages = zot_feed($c[0]['channel_id'], $sender_hash, array('message_id' => $data['message_id']));
    if ($messages) {
        $env_recips = null;
        $r = q("select hubloc_guid, hubloc_url, hubloc_sitekey, hubloc_network, hubloc_flags, hubloc_callback, hubloc_host\n\t\t\tfrom hubloc where hubloc_hash = '%s' and not (hubloc_flags & %d)>0\n\t\t\tand not (hubloc_status & %d)>0 ", dbesc($sender_hash), intval(HUBLOC_FLAGS_DELETED), intval(HUBLOC_OFFLINE));
        if (!$r) {
            logger('no hubs');
            return $ret;
        }
        $hubs = $r;
        $private = array_key_exists('flags', $messages[0]) && in_array('private', $messages[0]['flags']) ? true : false;
        if ($private) {
            $env_recips = array('guid' => $sender['guid'], 'guid_sig' => $sender['guid_sig'], 'hash' => $sender_hash);
        }
        $data_packet = json_encode(array('message_list' => $messages));
        foreach ($hubs as $hub) {
            $hash = random_string();
            /*
             * create a notify packet and drop the actual message packet in the queue for pickup
             */
            $n = zot_build_packet($c[0], 'notify', $env_recips, $private ? $hub['hubloc_sitekey'] : null, $hash, array('message_id' => $data['message_id']));
            q("insert into outq ( outq_hash, outq_account, outq_channel, outq_driver, outq_posturl, outq_async,\n\t\t\t\toutq_created, outq_updated, outq_notify, outq_msg )\n\t\t\t\tvalues ( '%s', %d, %d, '%s', '%s', %d, '%s', '%s', '%s', '%s' )", dbesc($hash), intval($c[0]['channel_account_id']), intval($c[0]['channel_id']), dbesc('zot'), dbesc($hub['hubloc_callback']), intval(1), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($n), dbesc($data_packet));
            /*
             * invoke delivery to send out the notify packet
             */
            proc_run('php', 'include/deliver.php', $hash);
        }
    }
    $ret['success'] = true;
    return $ret;
}
コード例 #6
0
ファイル: items.php プロジェクト: bashrc/hubzilla
function get_item_elements($x, $allow_code = false)
{
    $arr = array();
    if ($allow_code) {
        $arr['body'] = $x['body'];
    } else {
        $arr['body'] = $x['body'] ? htmlspecialchars($x['body'], ENT_COMPAT, 'UTF-8', false) : '';
    }
    $key = get_config('system', 'pubkey');
    $maxlen = get_max_import_size();
    if ($maxlen && mb_strlen($arr['body']) > $maxlen) {
        $arr['body'] = mb_substr($arr['body'], 0, $maxlen, 'UTF-8');
        logger('get_item_elements: message length exceeds max_import_size: truncated');
    }
    $arr['created'] = datetime_convert('UTC', 'UTC', $x['created']);
    $arr['edited'] = datetime_convert('UTC', 'UTC', $x['edited']);
    if ($arr['created'] > datetime_convert()) {
        $arr['created'] = datetime_convert();
    }
    if ($arr['edited'] > datetime_convert()) {
        $arr['edited'] = datetime_convert();
    }
    $arr['expires'] = x($x, 'expires') && $x['expires'] ? datetime_convert('UTC', 'UTC', $x['expires']) : NULL_DATE;
    $arr['commented'] = x($x, 'commented') && $x['commented'] ? datetime_convert('UTC', 'UTC', $x['commented']) : $arr['created'];
    $arr['comments_closed'] = x($x, 'comments_closed') && $x['comments_closed'] ? datetime_convert('UTC', 'UTC', $x['comments_closed']) : NULL_DATE;
    $arr['title'] = $x['title'] ? htmlspecialchars($x['title'], ENT_COMPAT, 'UTF-8', false) : '';
    if (mb_strlen($arr['title']) > 255) {
        $arr['title'] = mb_substr($arr['title'], 0, 255);
    }
    $arr['app'] = $x['app'] ? htmlspecialchars($x['app'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['route'] = $x['route'] ? htmlspecialchars($x['route'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['mid'] = $x['message_id'] ? htmlspecialchars($x['message_id'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['parent_mid'] = $x['message_top'] ? htmlspecialchars($x['message_top'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['thr_parent'] = $x['message_parent'] ? htmlspecialchars($x['message_parent'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['plink'] = $x['permalink'] ? htmlspecialchars($x['permalink'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['location'] = $x['location'] ? htmlspecialchars($x['location'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['coord'] = $x['longlat'] ? htmlspecialchars($x['longlat'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['verb'] = $x['verb'] ? htmlspecialchars($x['verb'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['mimetype'] = $x['mimetype'] ? htmlspecialchars($x['mimetype'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['obj_type'] = $x['object_type'] ? htmlspecialchars($x['object_type'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['tgt_type'] = $x['target_type'] ? htmlspecialchars($x['target_type'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['public_policy'] = $x['public_scope'] ? htmlspecialchars($x['public_scope'], ENT_COMPAT, 'UTF-8', false) : '';
    if ($arr['public_policy'] === 'public') {
        $arr['public_policy'] = '';
    }
    $arr['comment_policy'] = $x['comment_scope'] ? htmlspecialchars($x['comment_scope'], ENT_COMPAT, 'UTF-8', false) : 'contacts';
    $arr['sig'] = $x['signature'] ? htmlspecialchars($x['signature'], ENT_COMPAT, 'UTF-8', false) : '';
    if (array_key_exists('diaspora_signature', $x) && is_array($x['diaspora_signature'])) {
        $x['diaspora_signature'] = json_encode($x['diaspora_signature']);
    }
    $arr['diaspora_meta'] = $x['diaspora_signature'] ? $x['diaspora_signature'] : '';
    $arr['object'] = activity_sanitise($x['object']);
    $arr['target'] = activity_sanitise($x['target']);
    $arr['attach'] = activity_sanitise($x['attach']);
    $arr['term'] = decode_tags($x['tags']);
    $arr['item_private'] = array_key_exists('flags', $x) && is_array($x['flags']) && in_array('private', $x['flags']) ? 1 : 0;
    $arr['item_flags'] = 0;
    if (array_key_exists('flags', $x) && in_array('consensus', $x['flags'])) {
        $arr['item_consensus'] = 1;
    }
    if (array_key_exists('flags', $x) && in_array('deleted', $x['flags'])) {
        $arr['item_deleted'] = 1;
    }
    if (array_key_exists('flags', $x) && in_array('hidden', $x['flags'])) {
        $arr['item_hidden'] = 1;
    }
    // Here's the deal - the site might be down or whatever but if there's a new person you've never
    // seen before sending stuff to your stream, we MUST be able to look them up and import their data from their
    // hub and verify that they are legit - or else we're going to toss the post. We only need to do this
    // once, and after that your hub knows them. Sure some info is in the post, but it's only a transit identifier
    // and not enough info to be able to look you up from your hash - which is the only thing stored with the post.
    if (($xchan_hash = import_author_xchan($x['author'])) !== false) {
        $arr['author_xchan'] = $xchan_hash;
    } else {
        return array();
    }
    // save a potentially expensive lookup if author == owner
    if ($arr['author_xchan'] === make_xchan_hash($x['owner']['guid'], $x['owner']['guid_sig'])) {
        $arr['owner_xchan'] = $arr['author_xchan'];
    } else {
        if (($xchan_hash = import_author_xchan($x['owner'])) !== false) {
            $arr['owner_xchan'] = $xchan_hash;
        } else {
            return array();
        }
    }
    if ($arr['sig']) {
        $r = q("select xchan_pubkey from xchan where xchan_hash = '%s' limit 1", dbesc($arr['author_xchan']));
        if ($r && rsa_verify($x['body'], base64url_decode($arr['sig']), $r[0]['xchan_pubkey'])) {
            $arr['item_verified'] = 1;
        } else {
            logger('get_item_elements: message verification failed.');
        }
    }
    if (array_key_exists('revision', $x)) {
        // extended export encoding
        $arr['revision'] = $x['revision'];
        $arr['allow_cid'] = $x['allow_cid'];
        $arr['allow_gid'] = $x['allow_gid'];
        $arr['deny_cid'] = $x['deny_cid'];
        $arr['deny_gid'] = $x['deny_gid'];
        $arr['layout_mid'] = $x['layout_mid'];
        $arr['postopts'] = $x['postopts'];
        $arr['resource_id'] = $x['resource_id'];
        $arr['resource_type'] = $x['resource_type'];
        $arr['attach'] = $x['attach'];
        $arr['item_origin'] = $x['item_origin'];
        $arr['item_unseen'] = $x['item_unseen'];
        $arr['item_starred'] = $x['item_starred'];
        $arr['item_uplink'] = $x['item_uplink'];
        $arr['item_consensus'] = $x['item_consensus'];
        $arr['item_wall'] = $x['item_wall'];
        $arr['item_thread_top'] = $x['item_thread_top'];
        $arr['item_notshown'] = $x['item_notshown'];
        $arr['item_nsfw'] = $x['item_nsfw'];
        // local only		$arr['item_relay'] = $x['item_relay'];
        $arr['item_mentionsme'] = $x['item_mentionsme'];
        $arr['item_nocomment'] = $x['item_nocomment'];
        // local only $arr['item_obscured'] = $x['item_obscured'];
        // local only $arr['item_verified'] = $x['item_verified'];
        $arr['item_retained'] = $x['item_retained'];
        $arr['item_rss'] = $x['item_rss'];
        $arr['item_deleted'] = $x['item_deleted'];
        $arr['item_type'] = $x['item_type'];
        $arr['item_hidden'] = $x['item_hidden'];
        $arr['item_unpublished'] = $x['item_unpublished'];
        $arr['item_delayed'] = $x['item_delayed'];
        $arr['item_pending_remove'] = $x['item_pending_remove'];
        $arr['item_blocked'] = $x['item_blocked'];
        if (array_key_exists('item_flags', $x)) {
            if ($x['item_flags'] & 0x4) {
                $arr['item_starred'] = 1;
            }
            if ($x['item_flags'] & 0x8) {
                $arr['item_uplink'] = 1;
            }
            if ($x['item_flags'] & 0x10) {
                $arr['item_consensus'] = 1;
            }
            if ($x['item_flags'] & 0x20) {
                $arr['item_wall'] = 1;
            }
            if ($x['item_flags'] & 0x40) {
                $arr['item_thread_top'] = 1;
            }
            if ($x['item_flags'] & 0x80) {
                $arr['item_notshown'] = 1;
            }
            if ($x['item_flags'] & 0x100) {
                $arr['item_nsfw'] = 1;
            }
            if ($x['item_flags'] & 0x400) {
                $arr['item_mentionsme'] = 1;
            }
            if ($x['item_flags'] & 0x800) {
                $arr['item_nocomment'] = 1;
            }
            if ($x['item_flags'] & 0x4000) {
                $arr['item_retained'] = 1;
            }
            if ($x['item_flags'] & 0x8000) {
                $arr['item_rss'] = 1;
            }
        }
        if (array_key_exists('item_restrict', $x)) {
            if ($x['item_restrict'] & 0x1) {
                $arr['item_hidden'] = 1;
            }
            if ($x['item_restrict'] & 0x2) {
                $arr['item_blocked'] = 1;
            }
            if ($x['item_restrict'] & 0x10) {
                $arr['item_deleted'] = 1;
            }
            if ($x['item_restrict'] & 0x20) {
                $arr['item_unpublished'] = 1;
            }
            if ($x['item_restrict'] & 0x40) {
                $arr['item_type'] = ITEM_TYPE_WEBPAGE;
            }
            if ($x['item_restrict'] & 0x80) {
                $arr['item_delayed'] = 1;
            }
            if ($x['item_restrict'] & 0x100) {
                $arr['item_type'] = ITEM_TYPE_BLOCK;
            }
            if ($x['item_restrict'] & 0x200) {
                $arr['item_type'] = ITEM_TYPE_PDL;
            }
            if ($x['item_restrict'] & 0x400) {
                $arr['item_type'] = ITEM_TYPE_BUG;
            }
            if ($x['item_restrict'] & 0x800) {
                $arr['item_pending_remove'] = 1;
            }
            if ($x['item_restrict'] & 0x1000) {
                $arr['item_type'] = ITEM_TYPE_DOC;
            }
        }
    }
    return $arr;
}
コード例 #7
0
ファイル: zot.php プロジェクト: royalterra/hubzilla
function zot_reply_purge($sender, $recipients)
{
    $ret = array('success' => false);
    if ($recipients) {
        // basically this means "unfriend"
        foreach ($recipients as $recip) {
            $r = q("select channel.*,xchan.* from channel \n\t\t\t\tleft join xchan on channel_hash = xchan_hash\n\t\t\t\twhere channel_guid = '%s' and channel_guid_sig = '%s' limit 1", dbesc($recip['guid']), dbesc($recip['guid_sig']));
            if ($r) {
                $r = q("select abook_id from abook where uid = %d and abook_xchan = '%s' limit 1", intval($r[0]['channel_id']), dbesc(make_xchan_hash($sender['guid'], $sender['guid_sig'])));
                if ($r) {
                    contact_remove($r[0]['channel_id'], $r[0]['abook_id']);
                }
            }
        }
        $ret['success'] = true;
    } else {
        // Unfriend everybody - basically this means the channel has committed suicide
        $arr = $sender;
        $sender_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']);
        require_once 'include/Contact.php';
        remove_all_xchan_resources($sender_hash);
        $ret['success'] = true;
    }
    json_return_and_die($ret);
}
コード例 #8
0
ファイル: import.php プロジェクト: einervonvielen/hubzilla
function import_hublocs($channel, $hublocs, $seize)
{
    if ($channel && $hublocs) {
        foreach ($hublocs as $hubloc) {
            $hash = make_xchan_hash($hubloc['hubloc_guid'], $hubloc['hubloc_guid_sig']);
            if ($hubloc['hubloc_network'] === 'zot' && $hash !== $hubloc['hubloc_hash']) {
                logger('forged hubloc: ' . print_r($hubloc, true));
                continue;
            }
            if (!array_key_exists('hubloc_primary', $hubloc)) {
                $hubloc['hubloc_primary'] = $hubloc['hubloc_flags'] & 0x1 ? 1 : 0;
                $hubloc['hubloc_orphancheck'] = $hubloc['hubloc_flags'] & 0x4 ? 1 : 0;
                $hubloc['hubloc_error'] = $hubloc['hubloc_status'] & 0x3 ? 1 : 0;
                $hubloc['hubloc_deleted'] = $hubloc['hubloc_flags'] & 0x1000 ? 1 : 0;
            }
            $arr = array('guid' => $hubloc['hubloc_guid'], 'guid_sig' => $hubloc['hubloc_guid_sig'], 'url' => $hubloc['hubloc_url'], 'url_sig' => $hubloc['hubloc_url_sig']);
            if ($hubloc['hubloc_hash'] === $channel['channel_hash'] && intval($hubloc['hubloc_primary']) && $seize) {
                $hubloc['hubloc_primary'] = 0;
            }
            if (!zot_gethub($arr)) {
                unset($hubloc['hubloc_id']);
                dbesc_array($hubloc);
                $r = dbq("INSERT INTO hubloc (`" . implode("`, `", array_keys($hubloc)) . "`) VALUES ('" . implode("', '", array_values($hubloc)) . "')");
            }
        }
    }
}
コード例 #9
0
ファイル: post.php プロジェクト: Mauru/red
/**
 * @function post_post(&$a)
 *     zot communications and messaging
 *
 *     Sender HTTP posts to this endpoint ($site/post typically) with 'data' parameter set to json zot message packet.
 *     This packet is optionally encrypted, which we will discover if the json has an 'iv' element.
 *     $contents => array( 'alg' => 'aes256cbc', 'iv' => initialisation vector, 'key' => decryption key, 'data' => encrypted data);
 *     $contents->iv and $contents->key are random strings encrypted with this site's RSA public key and then base64url encoded.
 *     Currently only 'aes256cbc' is used, but this is extensible should that algorithm prove inadequate.
 *
 *     Once decrypted, one will find the normal json_encoded zot message packet. 
 * 
 * Defined packet types are: notify, purge, refresh, force_refresh, auth_check, ping, and pickup 
 *
 * Standard packet: (used by notify, purge, refresh, force_refresh, and auth_check)
 *
 * {
 *  "type": "notify",
 *  "sender":{
 *       "guid":"kgVFf_1...",
 *       "guid_sig":"PT9-TApzp...",
 *       "url":"http:\/\/podunk.edu",
 *       "url_sig":"T8Bp7j5...",
 *    },
 *  "recipients": { optional recipient array },
 *  "callback":"\/post",
 *  "version":1,
 *  "secret":"1eaa...",
 *  "secret_sig": "df89025470fac8..."
 * }
 * 
 * Signature fields are all signed with the sender channel private key and base64url encoded.
 * Recipients are arrays of guid and guid_sig, which were previously signed with the recipients private 
 * key and base64url encoded and later obtained via channel discovery. Absence of recipients indicates
 * a public message or visible to all potential listeners on this site.
 *
 * "pickup" packet:
 * The pickup packet is sent in response to a notify packet from another site
 * 
 * {
 *  "type":"pickup",
 *  "url":"http:\/\/example.com",
 *  "callback":"http:\/\/example.com\/post",
 *  "callback_sig":"teE1_fLI...",
 *  "secret":"1eaa...",
 *  "secret_sig":"O7nB4_..."
 * }
 *
 * In the pickup packet, the sig fields correspond to the respective data element signed with this site's system 
 * private key and then base64url encoded.
 * The "secret" is the same as the original secret from the notify packet. 
 *
 * If verification is successful, a json structure is returned
 * containing a success indicator and an array of type 'pickup'.
 * Each pickup element contains the original notify request and a message field whose contents are 
 * dependent on the message type
 *
 * This JSON array is AES encapsulated using the site public key of the site that sent the initial zot pickup packet.
 * Using the above example, this would be example.com.
 * 
 * 
 * {
 * "success":1,
 * "pickup":{
 *   "notify":{
 *     "type":"notify",
 *     "sender":{
 *       "guid":"kgVFf_...",
 *       "guid_sig":"PT9-TApz...",
 *       "url":"http:\/\/z.podunk.edu",
 *       "url_sig":"T8Bp7j5D..."
 *     },
 *     "callback":"\/post",
 *     "version":1,
 *     "secret":"1eaa661..."
 *   },
 *   "message":{
 *     "type":"activity",
 *     "message_id":"*****@*****.**",
 *     "message_top":"*****@*****.**",
 *     "message_parent":"*****@*****.**",
 *     "created":"2012-11-20 04:04:16",
 *     "edited":"2012-11-20 04:04:16",
 *     "title":"",
 *     "body":"Hi Nickordo",
 *     "app":"",
 *     "verb":"post",
 *     "object_type":"",
 *     "target_type":"",
 *     "permalink":"",
 *     "location":"",
 *     "longlat":"",
 *     "owner":{
 *       "name":"Indigo",
 *       "address":"*****@*****.**",
 *       "url":"http:\/\/podunk.edu",
 *       "photo":{
 *         "mimetype":"image\/jpeg",
 *         "src":"http:\/\/podunk.edu\/photo\/profile\/m\/5"
 *       },
 *       "guid":"kgVFf_...",
 *       "guid_sig":"PT9-TAp...",
 *     },
 *     "author":{
 *       "name":"Indigo",
 *       "address":"*****@*****.**",
 *       "url":"http:\/\/podunk.edu",
 *       "photo":{
 *         "mimetype":"image\/jpeg",
 *         "src":"http:\/\/podunk.edu\/photo\/profile\/m\/5"
 *       },
 *       "guid":"kgVFf_...",
 *       "guid_sig":"PT9-TAp..."
 *     }
 *   }
 * }
 *} 
 *
 * Currently defined message types are 'activity', 'mail', 'profile' and 'channel_sync', which each have 
 * different content schemas.
 *
 * Ping packet:
 * A ping packet does not require any parameters except the type. It may or may not be encrypted.
 * 
 * {
 *  "type": "ping"
 * }
 * 
 * On receipt of a ping packet a ping response will be returned:
 *
 * {
 *   "success" : 1,
 *   "site" {
 *       "url":"http:\/\/podunk.edu",
 *       "url_sig":"T8Bp7j5...",
 *       "sitekey": "-----BEGIN PUBLIC KEY-----
 *                  MIICIjANBgkqhkiG9w0BAQE..."
 *    }
 * }
 * 
 * The ping packet can be used to verify that a site has not been re-installed, and to 
 * initiate corrective action if it has. The url_sig is signed with the site private key
 * and base64url encoded - and this should verify with the enclosed sitekey. Failure to
 * verify indicates the site is corrupt or otherwise unable to communicate using zot.
 * This return packet is not otherwise verified, so should be compared with other
 * results obtained from this site which were verified prior to taking action. For instance
 * if you have one verified result with this signature and key, and other records for this 
 * url which have different signatures and keys, it indicates that the site was re-installed
 * and corrective action may commence (remove or mark invalid any entries with different
 * signatures).
 * If you have no records which match this url_sig and key - no corrective action should
 * be taken as this packet may have been returned by an imposter.  
 *
 */
function post_post(&$a)
{
    $encrypted_packet = false;
    $ret = array('success' => false);
    $data = json_decode($_REQUEST['data'], true);
    /**
     * Many message packets will arrive encrypted. The existence of an 'iv' element 
     * tells us we need to unencapsulate the AES-256-CBC content using the site private key
     */
    if (array_key_exists('iv', $data)) {
        $encrypted_packet = true;
        $data = crypto_unencapsulate($data, get_config('system', 'prvkey'));
        logger('mod_zot: decrypt1: ' . $data, LOGGER_DATA);
        $data = json_decode($data, true);
    }
    if (!$data) {
        // possible Bleichenbacher's attack, just treat it as a
        // message we have no handler for. It should fail a bit
        // further along with "no hub". Our public key is public
        // knowledge. There's no reason why anybody should get the
        // encryption wrong unless they're fishing or hacking. If
        // they're developing and made a goof, this can be discovered
        // in the logs of the destination site. If they're fishing or
        // hacking, the bottom line is we can't verify their hub.
        // That's all we're going to tell them.
        $data = array('type' => 'bogus');
    }
    $msgtype = array_key_exists('type', $data) ? $data['type'] : '';
    if ($msgtype === 'ping') {
        // Useful to get a health check on a remote site.
        // This will let us know if any important communication details
        // that we may have stored are no longer valid, regardless of xchan details.
        logger('POST: got ping send pong now back: ' . z_root(), LOGGER_DEBUG);
        $ret['success'] = true;
        $ret['site'] = array();
        $ret['site']['url'] = z_root();
        $ret['site']['url_sig'] = base64url_encode(rsa_sign(z_root(), get_config('system', 'prvkey')));
        $ret['site']['sitekey'] = get_config('system', 'pubkey');
        json_return_and_die($ret);
    }
    if ($msgtype === 'pickup') {
        /**
         * The 'pickup' message arrives with a tracking ID which is associated with a particular outq_hash
         * First verify that that the returned signatures verify, then check that we have an outbound queue item
         * with the correct hash.
         * If everything verifies, find any/all outbound messages in the queue for this hubloc and send them back
         *
         */
        if (!$data['secret'] || !$data['secret_sig']) {
            $ret['message'] = 'no verification signature';
            logger('mod_zot: pickup: ' . $ret['message'], LOGGER_DEBUG);
            json_return_and_die($ret);
        }
        $r = q("select distinct hubloc_sitekey from hubloc where hubloc_url = '%s' and hubloc_callback = '%s' and hubloc_sitekey != '' group by hubloc_sitekey ", dbesc($data['url']), dbesc($data['callback']));
        if (!$r) {
            $ret['message'] = 'site not found';
            logger('mod_zot: pickup: ' . $ret['message']);
            json_return_and_die($ret);
        }
        foreach ($r as $hubsite) {
            // verify the url_sig
            // If the server was re-installed at some point, there could be multiple hubs with the same url and callback.
            // Only one will have a valid key.
            $forgery = true;
            $secret_fail = true;
            $sitekey = $hubsite['hubloc_sitekey'];
            logger('mod_zot: Checking sitekey: ' . $sitekey, LOGGER_DATA);
            if (rsa_verify($data['callback'], base64url_decode($data['callback_sig']), $sitekey)) {
                $forgery = false;
            }
            if (rsa_verify($data['secret'], base64url_decode($data['secret_sig']), $sitekey)) {
                $secret_fail = false;
            }
            if (!$forgery && !$secret_fail) {
                break;
            }
        }
        if ($forgery) {
            $ret['message'] = 'possible site forgery';
            logger('mod_zot: pickup: ' . $ret['message']);
            json_return_and_die($ret);
        }
        if ($secret_fail) {
            $ret['message'] = 'secret validation failed';
            logger('mod_zot: pickup: ' . $ret['message']);
            json_return_and_die($ret);
        }
        /**
         * If we made it to here, the signatures verify, but we still don't know if the tracking ID is valid.
         * It wouldn't be an error if the tracking ID isn't found, because we may have sent this particular
         * queue item with another pickup (after the tracking ID for the other pickup  was verified). 
         */
        $r = q("select outq_posturl from outq where outq_hash = '%s' and outq_posturl = '%s' limit 1", dbesc($data['secret']), dbesc($data['callback']));
        if (!$r) {
            $ret['message'] = 'nothing to pick up';
            logger('mod_zot: pickup: ' . $ret['message']);
            json_return_and_die($ret);
        }
        /**
         * Everything is good if we made it here, so find all messages that are going to this location
         * and send them all.
         */
        $r = q("select * from outq where outq_posturl = '%s'", dbesc($data['callback']));
        if ($r) {
            logger('mod_zot: succesful pickup message received from ' . $data['callback'] . ' ' . count($r) . ' message(s) picked up', LOGGER_DEBUG);
            $ret['success'] = true;
            $ret['pickup'] = array();
            foreach ($r as $rr) {
                $ret['pickup'][] = array('notify' => json_decode($rr['outq_notify'], true), 'message' => json_decode($rr['outq_msg'], true));
                $x = q("delete from outq where outq_hash = '%s' limit 1", dbesc($rr['outq_hash']));
            }
        }
        $encrypted = crypto_encapsulate(json_encode($ret), $sitekey);
        json_return_and_die($encrypted);
        /** pickup: end */
    }
    /**
     * All other message types require us to verify the sender. This is a generic check, so we 
     * will do it once here and bail if anything goes wrong.
     */
    if (array_key_exists('sender', $data)) {
        $sender = $data['sender'];
    }
    /** Check if the sender is already verified here */
    $hub = zot_gethub($sender);
    if (!$hub) {
        /** Have never seen this guid or this guid coming from this location. Check it and register it. */
        // (!!) this will validate the sender
        $result = zot_register_hub($sender);
        if (!$result['success'] || !($hub = zot_gethub($sender))) {
            $ret['message'] = 'Hub not available.';
            logger('mod_zot: no hub');
            json_return_and_die($ret);
        }
    }
    // Update our DB to show when we last communicated successfully with this hub
    // This will allow us to prune dead hubs from using up resources
    $r = q("update hubloc set hubloc_connected = '%s' where hubloc_id = %d limit 1", dbesc(datetime_convert()), intval($hub['hubloc_id']));
    // a dead hub came back to life - reset any tombstones we might have
    if ($hub['hubloc_status'] & HUBLOC_OFFLINE) {
        q("update hubloc set hubloc_status = (hubloc_status ^ %d) where hubloc_id = %d limit 1", intval(HUBLOC_OFFLINE), intval($hub['hubloc_id']));
        if ($r[0]['hubloc_flags'] & HUBLOC_FLAGS_ORPHANCHECK) {
            q("update hubloc set hubloc_flags = (hubloc_flags ^ %d) where hubloc_id = %d limit 1", intval(HUBLOC_FLAGS_ORPHANCHECK), intval($hub['hubloc_id']));
        }
        q("update xchan set xchan_flags = (xchan_flags ^ %d) where (xchan_flags & %d) and xchan_hash = '%s' limit 1", intval(XCHAN_FLAGS_ORPHAN), intval(XCHAN_FLAGS_ORPHAN), dbesc($hub['hubloc_hash']));
    }
    /** 
     * This hub has now been proven to be valid.
     * Any hub with the same URL and a different sitekey cannot be valid.
     * Get rid of them (mark them deleted). There's a good chance they were re-installs.
     *
     */
    q("update hubloc set hubloc_flags = ( hubloc_flags | %d ) where hubloc_url = '%s' and hubloc_sitekey != '%s' ", intval(HUBLOC_FLAGS_DELETED), dbesc($hub['hubloc_url']), dbesc($hub['hubloc_sitekey']));
    // TODO: check which hub is primary and take action if mismatched
    if (array_key_exists('recipients', $data)) {
        $recipients = $data['recipients'];
    }
    if ($msgtype === 'auth_check') {
        /**
         * Requestor visits /magic/?dest=somewhere on their own site with a browser
         * magic redirects them to $destsite/post [with auth args....]
         * $destsite sends an auth_check packet to originator site
         * The auth_check packet is handled here by the originator's site 
         * - the browser session is still waiting
         * inside $destsite/post for everything to verify
         * If everything checks out we'll return a token to $destsite
         * and then $destsite will verify the token, authenticate the browser
         * session and then redirect to the original destination.
         * If authentication fails, the redirection to the original destination
         * will still take place but without authentication.
         */
        logger('mod_zot: auth_check', LOGGER_DEBUG);
        if (!$encrypted_packet) {
            logger('mod_zot: auth_check packet was not encrypted.');
            $ret['message'] .= 'no packet encryption' . EOL;
            json_return_and_die($ret);
        }
        $arr = $data['sender'];
        $sender_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']);
        // garbage collect any old unused notifications
        q("delete from verify where type = 'auth' and created < UTC_TIMESTAMP() - INTERVAL 10 MINUTE");
        $y = q("select xchan_pubkey from xchan where xchan_hash = '%s' limit 1", dbesc($sender_hash));
        // We created a unique hash in mod/magic.php when we invoked remote auth, and stored it in
        // the verify table. It is now coming back to us as 'secret' and is signed by a channel at the other end.
        // First verify their signature. We will have obtained a zot-info packet from them as part of the sender
        // verification.
        if (!$y || !rsa_verify($data['secret'], base64url_decode($data['secret_sig']), $y[0]['xchan_pubkey'])) {
            logger('mod_zot: auth_check: sender not found or secret_sig invalid.');
            $ret['message'] .= 'sender not found or sig invalid ' . print_r($y, true) . EOL;
            json_return_and_die($ret);
        }
        // There should be exactly one recipient, the original auth requestor
        $ret['message'] .= 'recipients ' . print_r($recipients, true) . EOL;
        if ($data['recipients']) {
            $arr = $data['recipients'][0];
            $recip_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']);
            $c = q("select channel_id, channel_account_id, channel_prvkey from channel where channel_hash = '%s' limit 1", dbesc($recip_hash));
            if (!$c) {
                logger('mod_zot: auth_check: recipient channel not found.');
                $ret['message'] .= 'recipient not found.' . EOL;
                json_return_and_die($ret);
            }
            $confirm = base64url_encode(rsa_sign($data['secret'] . $recip_hash, $c[0]['channel_prvkey']));
            // This additionally checks for forged sites since we already stored the expected result in meta
            // and we've already verified that this is them via zot_gethub() and that their key signed our token
            $z = q("select id from verify where channel = %d and type = 'auth' and token = '%s' and meta = '%s' limit 1", intval($c[0]['channel_id']), dbesc($data['secret']), dbesc($data['sender']['url']));
            if (!$z) {
                logger('mod_zot: auth_check: verification key not found.');
                $ret['message'] .= 'verification key not found' . EOL;
                json_return_and_die($ret);
            }
            $r = q("delete from verify where id = %d limit 1", intval($z[0]['id']));
            $u = q("select account_service_class from account where account_id = %d limit 1", intval($c[0]['channel_account_id']));
            logger('mod_zot: auth_check: success', LOGGER_DEBUG);
            $ret['success'] = true;
            $ret['confirm'] = $confirm;
            if ($u && $u[0]['account_service_class']) {
                $ret['service_class'] = $u[0]['account_service_class'];
            }
            // Set "do not track" flag if this site or this channel's profile is restricted
            if (intval(get_config('system', 'block_public'))) {
                $ret['DNT'] = true;
            }
            if (!perm_is_allowed($c[0]['channel_id'], '', 'view_profile')) {
                $ret['DNT'] = true;
            }
            if (get_pconfig($c[0]['channel_id'], 'system', 'do_not_track')) {
                $ret['DNT'] = true;
            }
            json_return_and_die($ret);
        }
        json_return_and_die($ret);
    }
    if ($msgtype === 'purge') {
        if ($recipients) {
            // basically this means "unfriend"
            foreach ($recipients as $recip) {
                $r = q("select channel.*,xchan.* from channel \n\t\t\t\t\tleft join xchan on channel_hash = xchan_hash\n\t\t\t\t\twhere channel_guid = '%s' and channel_guid_sig = '%s' limit 1", dbesc($recip['guid']), dbesc($recip['guid_sig']));
                if ($r) {
                    $r = q("select abook_id from abook where uid = %d and abook_xchan = '%s' limit 1", intval($r[0]['channel_id']), dbesc(make_xchan_hash($sender['guid'], $sender['guid_sig'])));
                    if ($r) {
                        contact_remove($r[0]['channel_id'], $r[0]['abook_id']);
                    }
                }
            }
        } else {
            // Unfriend everybody - basically this means the channel has committed suicide
            $arr = $data['sender'];
            $sender_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']);
            require_once 'include/Contact.php';
            remove_all_xchan_resources($sender_hash);
            $ret['success'] = true;
            json_return_and_die($ret);
        }
    }
    if ($msgtype === 'refresh' || $msgtype === 'force_refresh') {
        // remote channel info (such as permissions or photo or something)
        // has been updated. Grab a fresh copy and sync it.
        // The difference between refresh and force_refresh is that
        // force_refresh unconditionally creates a directory update record,
        // even if no changes were detected upon processing.
        if ($recipients) {
            // This would be a permissions update, typically for one connection
            foreach ($recipients as $recip) {
                $r = q("select channel.*,xchan.* from channel \n\t\t\t\t\tleft join xchan on channel_hash = xchan_hash\n\t\t\t\t\twhere channel_guid = '%s' and channel_guid_sig = '%s' limit 1", dbesc($recip['guid']), dbesc($recip['guid_sig']));
                $x = zot_refresh(array('xchan_guid' => $sender['guid'], 'xchan_guid_sig' => $sender['guid_sig'], 'hubloc_url' => $sender['url']), $r[0], $msgtype === 'force_refresh' ? true : false);
            }
        } else {
            // system wide refresh
            $x = zot_refresh(array('xchan_guid' => $sender['guid'], 'xchan_guid_sig' => $sender['guid_sig'], 'hubloc_url' => $sender['url']), null, $msgtype === 'force_refresh' ? true : false);
        }
        $ret['success'] = true;
        json_return_and_die($ret);
    }
    if ($msgtype === 'notify') {
        $async = get_config('system', 'queued_fetch');
        if ($async) {
            // add to receive queue
            // qreceive_add($data);
        } else {
            $x = zot_fetch($data);
            $ret['delivery_report'] = $x;
        }
        $ret['success'] = true;
        json_return_and_die($ret);
    }
    // catchall
    json_return_and_die($ret);
}
コード例 #10
0
ファイル: zfinger.php プロジェクト: Mauru/red
function zfinger_init(&$a)
{
    require_once 'include/zot.php';
    require_once 'include/crypto.php';
    $ret = array('success' => false);
    $zhash = x($_REQUEST, 'guid_hash') ? $_REQUEST['guid_hash'] : '';
    $zguid = x($_REQUEST, 'guid') ? $_REQUEST['guid'] : '';
    $zguid_sig = x($_REQUEST, 'guid_sig') ? $_REQUEST['guid_sig'] : '';
    $zaddr = x($_REQUEST, 'address') ? $_REQUEST['address'] : '';
    $ztarget = x($_REQUEST, 'target') ? $_REQUEST['target'] : '';
    $zsig = x($_REQUEST, 'target_sig') ? $_REQUEST['target_sig'] : '';
    $zkey = x($_REQUEST, 'key') ? $_REQUEST['key'] : '';
    $mindate = x($_REQUEST, 'mindate') ? $_REQUEST['mindate'] : '';
    $feed = x($_REQUEST, 'feed') ? intval($_REQUEST['feed']) : 0;
    if ($ztarget) {
        if (!$zkey || !$zsig || !rsa_verify($ztarget, base64url_decode($zsig), $zkey)) {
            logger('zfinger: invalid target signature');
            $ret['message'] = t("invalid target signature");
            json_return_and_die($ret);
        }
    }
    // allow re-written domains so bob@foo.example.com can provide an address of bob@example.com
    // The top-level domain also needs to redirect .well-known/zot-info to the sub-domain with a 301 or 308
    // TODO: Make 308 work in include/network.php for zot_fetch_url and zot_post_url
    if ($zaddr && ($s = get_config('system', 'zotinfo_domainrewrite'))) {
        $arr = explode('^', $s);
        if (count($arr) == 2) {
            $zaddr = str_replace($arr[0], $arr[1], $zaddr);
        }
    }
    $r = null;
    if (strlen($zhash)) {
        $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash \n\t\t\twhere channel_hash = '%s' limit 1", dbesc($zhash));
    } elseif (strlen($zguid) && strlen($zguid_sig)) {
        $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash \n\t\t\twhere channel_guid = '%s' and channel_guid_sig = '%s' limit 1", dbesc($zguid), dbesc($zguid_sig));
    } elseif (strlen($zaddr)) {
        if (strpos($zaddr, '[system]') === false) {
            /* normal address lookup */
            $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash\n\t\t\t\twhere ( channel_address = '%s' or xchan_addr = '%s' ) limit 1", dbesc($zaddr), dbesc($zaddr));
        } else {
            /**
             * The special address '[system]' will return a system channel if one has been defined,
             * Or the first valid channel we find if there are no system channels. 
             *
             * This is used by magic-auth if we have no prior communications with this site - and
             * returns an identity on this site which we can use to create a valid hub record so that
             * we can exchange signed messages. The precise identity is irrelevant. It's the hub
             * information that we really need at the other end - and this will return it.
             *
             */
            $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash\n\t\t\t\twhere ( channel_pageflags & %d ) order by channel_id limit 1", intval(PAGE_SYSTEM));
            if (!$r) {
                $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash\n\t\t\t\t\twhere not ( channel_pageflags & %d ) order by channel_id limit 1", intval(PAGE_REMOVED));
            }
        }
    } else {
        $ret['message'] = 'Invalid request';
        json_return_and_die($ret);
    }
    if (!$r) {
        $ret['message'] = 'Item not found.';
        json_return_and_die($ret);
    }
    $e = $r[0];
    $id = $e['channel_id'];
    $special_channel = $e['channel_pageflags'] & PAGE_PREMIUM ? true : false;
    $adult_channel = $e['channel_pageflags'] & PAGE_ADULT ? true : false;
    $censored = $e['channel_pageflags'] & PAGE_CENSORED ? true : false;
    $searchable = $e['channel_pageflags'] & PAGE_HIDDEN ? false : true;
    $deleted = $e['xchan_flags'] & XCHAN_FLAGS_DELETED ? true : false;
    if ($deleted || $censored) {
        $searchable = false;
    }
    //  This is for birthdays and keywords, but must check access permissions
    $p = q("select * from profile where uid = %d and is_default = 1", intval($e['channel_id']));
    $profile = array();
    if ($p) {
        if (!intval($p[0]['publish'])) {
            $searchable = false;
        }
        $profile['description'] = $p[0]['pdesc'];
        $profile['birthday'] = $p[0]['dob'];
        if ($profile['birthday'] != '0000-00-00' && ($bd = z_birthday($p[0]['dob'], $e['channel_timezone'])) !== '') {
            $profile['next_birthday'] = $bd;
        }
        if ($age = age($p[0]['dob'], $e['channel_timezone'], '')) {
            $profile['age'] = $age;
        }
        $profile['gender'] = $p[0]['gender'];
        $profile['marital'] = $p[0]['marital'];
        $profile['sexual'] = $p[0]['sexual'];
        $profile['locale'] = $p[0]['locality'];
        $profile['region'] = $p[0]['region'];
        $profile['postcode'] = $p[0]['postal_code'];
        $profile['country'] = $p[0]['country_name'];
        $profile['about'] = $p[0]['about'];
        $profile['homepage'] = $p[0]['homepage'];
        $profile['hometown'] = $p[0]['hometown'];
        if ($p[0]['keywords']) {
            $tags = array();
            $k = explode(' ', $p[0]['keywords']);
            if ($k) {
                foreach ($k as $kk) {
                    if (trim($kk, " \t\n\r\v,")) {
                        $tags[] = trim($kk, " \t\n\r\v,");
                    }
                }
            }
            if ($tags) {
                $profile['keywords'] = $tags;
            }
        }
    }
    $ret['success'] = true;
    // Communication details
    $ret['guid'] = $e['xchan_guid'];
    $ret['guid_sig'] = $e['xchan_guid_sig'];
    $ret['key'] = $e['xchan_pubkey'];
    $ret['name'] = $e['xchan_name'];
    $ret['name_updated'] = $e['xchan_name_date'];
    $ret['address'] = $e['xchan_addr'];
    $ret['photo_mimetype'] = $e['xchan_photo_mimetype'];
    $ret['photo'] = $e['xchan_photo_l'];
    $ret['photo_updated'] = $e['xchan_photo_date'];
    $ret['url'] = $e['xchan_url'];
    $ret['connections_url'] = $e['xchan_connurl'] ? $e['xchan_connurl'] : z_root() . '/poco/' . $e['channel_address'];
    $ret['target'] = $ztarget;
    $ret['target_sig'] = $zsig;
    $ret['searchable'] = $searchable;
    $ret['adult_content'] = $adult_channel;
    if ($deleted) {
        $ret['deleted'] = $deleted;
    }
    // premium or other channel desiring some contact with potential followers before connecting.
    // This is a template - %s will be replaced with the follow_url we discover for the return channel.
    if ($special_channel) {
        $ret['connect_url'] = z_root() . '/connect/' . $e['channel_address'];
    }
    // This is a template for our follow url, %s will be replaced with a webbie
    $ret['follow_url'] = z_root() . '/follow?f=&url=%s';
    $ztarget_hash = $ztarget && $zsig ? make_xchan_hash($ztarget, $zsig) : '';
    $permissions = get_all_perms($e['channel_id'], $ztarget_hash, false);
    if ($ztarget_hash) {
        $permissions['connected'] = false;
        $b = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($ztarget_hash), intval($e['channel_id']));
        if ($b) {
            $permissions['connected'] = true;
        }
    }
    $ret['permissions'] = $ztarget && $zkey ? crypto_encapsulate(json_encode($permissions), $zkey) : $permissions;
    if ($permissions['view_profile']) {
        $ret['profile'] = $profile;
    }
    // array of (verified) hubs this channel uses
    $ret['locations'] = array();
    $x = zot_get_hublocs($e['channel_hash']);
    if ($x && count($x)) {
        foreach ($x as $hub) {
            if (!($hub['hubloc_flags'] & HUBLOC_FLAGS_UNVERIFIED)) {
                $ret['locations'][] = array('host' => $hub['hubloc_host'], 'address' => $hub['hubloc_addr'], 'primary' => $hub['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY ? true : false, 'url' => $hub['hubloc_url'], 'url_sig' => $hub['hubloc_url_sig'], 'callback' => $hub['hubloc_callback'], 'sitekey' => $hub['hubloc_sitekey'], 'deleted' => $hub['hubloc_flags'] & HUBLOC_FLAGS_DELETED ? true : false);
            }
        }
    }
    $ret['site'] = array();
    $ret['site']['url'] = z_root();
    $ret['site']['url_sig'] = base64url_encode(rsa_sign(z_root(), $e['channel_prvkey']));
    $dirmode = get_config('system', 'directory_mode');
    if ($dirmode === false || $dirmode == DIRECTORY_MODE_NORMAL) {
        $ret['site']['directory_mode'] = 'normal';
    }
    if ($dirmode == DIRECTORY_MODE_PRIMARY) {
        $ret['site']['directory_mode'] = 'primary';
    } elseif ($dirmode == DIRECTORY_MODE_SECONDARY) {
        $ret['site']['directory_mode'] = 'secondary';
    } elseif ($dirmode == DIRECTORY_MODE_STANDALONE) {
        $ret['site']['directory_mode'] = 'standalone';
    }
    if ($dirmode != DIRECTORY_MODE_NORMAL) {
        $ret['site']['directory_url'] = z_root() . '/dirsearch';
    }
    // hide detailed site information if you're off the grid
    if ($dirmode != DIRECTORY_MODE_STANDALONE) {
        $register_policy = intval(get_config('system', 'register_policy'));
        if ($register_policy == REGISTER_CLOSED) {
            $ret['site']['register_policy'] = 'closed';
        }
        if ($register_policy == REGISTER_APPROVE) {
            $ret['site']['register_policy'] = 'approve';
        }
        if ($register_policy == REGISTER_OPEN) {
            $ret['site']['register_policy'] = 'open';
        }
        $access_policy = intval(get_config('system', 'access_policy'));
        if ($access_policy == ACCESS_PRIVATE) {
            $ret['site']['access_policy'] = 'private';
        }
        if ($access_policy == ACCESS_PAID) {
            $ret['site']['access_policy'] = 'paid';
        }
        if ($access_policy == ACCESS_FREE) {
            $ret['site']['access_policy'] = 'free';
        }
        if ($access_policy == ACCESS_TIERED) {
            $ret['site']['access_policy'] = 'tiered';
        }
        $ret['site']['accounts'] = account_total();
        require_once 'include/identity.php';
        $ret['site']['channels'] = channel_total();
        $ret['site']['version'] = RED_PLATFORM . ' ' . RED_VERSION . '[' . DB_UPDATE_VERSION . ']';
        $ret['site']['admin'] = get_config('system', 'admin_email');
        $visible_plugins = array();
        if (is_array($a->plugins) && count($a->plugins)) {
            $r = q("select * from addon where hidden = 0");
            if ($r) {
                foreach ($r as $rr) {
                    $visible_plugins[] = $rr['name'];
                }
            }
        }
        $ret['site']['plugins'] = $visible_plugins;
        $ret['site']['sitehash'] = get_config('system', 'location_hash');
        $ret['site']['sitename'] = get_config('system', 'sitename');
        $ret['site']['sellpage'] = get_config('system', 'sellpage');
        $ret['site']['location'] = get_config('system', 'site_location');
        $ret['site']['realm'] = get_directory_realm();
    }
    call_hooks('zot_finger', $ret);
    json_return_and_die($ret);
}
コード例 #11
0
ファイル: identity.php プロジェクト: Mauru/red
/**
 * @function create_identity($arr)
 *     Create a new channel
 * Also creates the related xchan, hubloc, profile, and "self" abook records, and an 
 * empty "Friends" group/collection for the new channel
 *
 * @param array $arr
 *       'name'       => full name of channel
 *       'nickname'   => "email/url-compliant" nickname
 *       'account_id' => account_id to attach with this channel
 *       [other identity fields as desired]
 *
 * @returns array
 *     'success' => boolean true or false
 *     'message' => optional error text if success is false
 *     'channel' => if successful the created channel array
 */
function create_identity($arr)
{
    $a = get_app();
    $ret = array('success' => false);
    if (!$arr['account_id']) {
        $ret['message'] = t('No account identifier');
        return $ret;
    }
    $ret = identity_check_service_class($arr['account_id']);
    if (!$ret['success']) {
        return $ret;
    }
    $nick = mb_strtolower(trim($arr['nickname']));
    if (!$nick) {
        $ret['message'] = t('Nickname is required.');
        return $ret;
    }
    $name = escape_tags($arr['name']);
    $pageflags = x($arr, 'pageflags') ? intval($arr['pageflags']) : PAGE_NORMAL;
    $xchanflags = x($arr, 'xchanflags') ? intval($arr['xchanflags']) : XCHAN_FLAGS_NORMAL;
    $name_error = validate_channelname($arr['name']);
    if ($name_error) {
        $ret['message'] = $name_error;
        return $ret;
    }
    if ($nick === 'sys' && !($pageflags & PAGE_SYSTEM)) {
        $ret['message'] = t('Reserved nickname. Please choose another.');
        return $ret;
    }
    if (check_webbie(array($nick)) !== $nick) {
        $ret['message'] = t('Nickname has unsupported characters or is already being used on this site.');
        return $ret;
    }
    $guid = zot_new_uid($nick);
    $key = new_keypair(4096);
    $sig = base64url_encode(rsa_sign($guid, $key['prvkey']));
    $hash = make_xchan_hash($guid, $sig);
    // Force a few things on the short term until we can provide a theme or app with choice
    $publish = 1;
    if (array_key_exists('publish', $arr)) {
        $publish = intval($arr['publish']);
    }
    $primary = true;
    if (array_key_exists('primary', $arr)) {
        $primary = intval($arr['primary']);
    }
    $perms_sql = '';
    $defperms = site_default_perms();
    $global_perms = get_perms();
    foreach ($defperms as $p => $v) {
        $perms_keys .= ', ' . $global_perms[$p][0];
        $perms_vals .= ', ' . intval($v);
    }
    $expire = get_config('system', 'default_expire_days');
    $expire = $expire === false ? '0' : $expire;
    $r = q("insert into channel ( channel_account_id, channel_primary, \n\t\tchannel_name, channel_address, channel_guid, channel_guid_sig,\n\t\tchannel_hash, channel_prvkey, channel_pubkey, channel_pageflags, channel_expire_days {$perms_keys} )\n\t\tvalues ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d {$perms_vals} ) ", intval($arr['account_id']), intval($primary), dbesc($name), dbesc($nick), dbesc($guid), dbesc($sig), dbesc($hash), dbesc($key['prvkey']), dbesc($key['pubkey']), intval($pageflags), intval($expire));
    $r = q("select * from channel where channel_account_id = %d \n\t\tand channel_guid = '%s' limit 1", intval($arr['account_id']), dbesc($guid));
    if (!$r) {
        $ret['message'] = t('Unable to retrieve created identity');
        return $ret;
    }
    $ret['channel'] = $r[0];
    if (intval($arr['account_id'])) {
        set_default_login_identity($arr['account_id'], $ret['channel']['channel_id'], false);
    }
    // Create a verified hub location pointing to this site.
    $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_flags, \n\t\thubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey )\n\t\tvalues ( '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s' )", dbesc($guid), dbesc($sig), dbesc($hash), dbesc($ret['channel']['channel_address'] . '@' . get_app()->get_hostname()), intval($primary ? HUBLOC_FLAGS_PRIMARY : 0), dbesc(z_root()), dbesc(base64url_encode(rsa_sign(z_root(), $ret['channel']['channel_prvkey']))), dbesc(get_app()->get_hostname()), dbesc(z_root() . '/post'), dbesc(get_config('system', 'pubkey')));
    if (!$r) {
        logger('create_identity: Unable to store hub location');
    }
    $newuid = $ret['channel']['channel_id'];
    $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_guid_sig, xchan_pubkey, xchan_photo_l, xchan_photo_m, xchan_photo_s, xchan_addr, xchan_url, xchan_follow, xchan_connurl, xchan_name, xchan_network, xchan_photo_date, xchan_name_date, xchan_flags ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d)", dbesc($hash), dbesc($guid), dbesc($sig), dbesc($key['pubkey']), dbesc($a->get_baseurl() . "/photo/profile/l/{$newuid}"), dbesc($a->get_baseurl() . "/photo/profile/m/{$newuid}"), dbesc($a->get_baseurl() . "/photo/profile/s/{$newuid}"), dbesc($ret['channel']['channel_address'] . '@' . get_app()->get_hostname()), dbesc(z_root() . '/channel/' . $ret['channel']['channel_address']), dbesc(z_root() . '/follow?f=&url=%s'), dbesc(z_root() . '/poco/' . $ret['channel']['channel_address']), dbesc($ret['channel']['channel_name']), dbesc('zot'), dbesc(datetime_convert()), dbesc(datetime_convert()), intval($xchanflags));
    // Not checking return value.
    // It's ok for this to fail if it's an imported channel, and therefore the hash is a duplicate
    $r = q("INSERT INTO profile ( aid, uid, profile_guid, profile_name, is_default, publish, name, photo, thumb)\n\t\tVALUES ( %d, %d, '%s', '%s', %d, %d, '%s', '%s', '%s') ", intval($ret['channel']['channel_account_id']), intval($newuid), dbesc(random_string()), t('Default Profile'), 1, $publish, dbesc($ret['channel']['channel_name']), dbesc($a->get_baseurl() . "/photo/profile/l/{$newuid}"), dbesc($a->get_baseurl() . "/photo/profile/m/{$newuid}"));
    $r = q("insert into abook ( abook_account, abook_channel, abook_xchan, abook_closeness, abook_created, abook_updated, abook_flags )\n\t\tvalues ( %d, %d, '%s', %d, '%s', '%s', %d ) ", intval($ret['channel']['channel_account_id']), intval($newuid), dbesc($hash), intval(0), dbesc(datetime_convert()), dbesc(datetime_convert()), intval(ABOOK_FLAG_SELF));
    if (intval($ret['channel']['channel_account_id'])) {
        // Create a group with no members. This allows somebody to use it
        // right away as a default group for new contacts.
        require_once 'include/group.php';
        group_add($newuid, t('Friends'));
        call_hooks('register_account', $newuid);
        proc_run('php', 'include/directory.php', $ret['channel']['channel_id']);
    }
    $ret['success'] = true;
    return $ret;
}
コード例 #12
0
ファイル: import.php プロジェクト: HaakonME/redmatrix
function import_post(&$a)
{
    $account_id = get_account_id();
    if (!$account_id) {
        return;
    }
    $max_identities = account_service_class_fetch($account_id, 'total_identities');
    $max_friends = account_service_class_fetch($account_id, 'total_channels');
    $max_feeds = account_service_class_fetch($account_id, 'total_feeds');
    if ($max_identities !== false) {
        $r = q("select channel_id from channel where channel_account_id = %d", intval($account_id));
        if ($r && count($r) > $max_identities) {
            notice(sprintf(t('Your service plan only allows %d channels.'), $max_identities) . EOL);
            return;
        }
    }
    $data = null;
    $seize = x($_REQUEST, 'make_primary') ? intval($_REQUEST['make_primary']) : 0;
    $import_posts = x($_REQUEST, 'import_posts') ? intval($_REQUEST['import_posts']) : 0;
    $src = $_FILES['filename']['tmp_name'];
    $filename = basename($_FILES['filename']['name']);
    $filesize = intval($_FILES['filename']['size']);
    $filetype = $_FILES['filename']['type'];
    if ($src) {
        // This is OS specific and could also fail if your tmpdir isn't very large
        // mostly used for Diaspora which exports gzipped files.
        if (strpos($filename, '.gz')) {
            @rename($src, $src . '.gz');
            @system('gunzip ' . escapeshellarg($src . '.gz'));
        }
        if ($filesize) {
            $data = @file_get_contents($src);
        }
        unlink($src);
    }
    if (!$src) {
        $old_address = x($_REQUEST, 'old_address') ? $_REQUEST['old_address'] : '';
        if (!$old_address) {
            logger('mod_import: nothing to import.');
            notice(t('Nothing to import.') . EOL);
            return;
        }
        $email = x($_REQUEST, 'email') ? $_REQUEST['email'] : '';
        $password = x($_REQUEST, 'password') ? $_REQUEST['password'] : '';
        $channelname = substr($old_address, 0, strpos($old_address, '@'));
        $servername = substr($old_address, strpos($old_address, '@') + 1);
        $scheme = 'https://';
        $api_path = '/api/red/channel/export/basic?f=&channel=' . $channelname;
        if ($import_posts) {
            $api_path .= '&posts=1';
        }
        $binary = false;
        $redirects = 0;
        $opts = array('http_auth' => $email . ':' . $password);
        $url = $scheme . $servername . $api_path;
        $ret = z_fetch_url($url, $binary, $redirects, $opts);
        if (!$ret['success']) {
            $ret = z_fetch_url('http://' . $servername . $api_path, $binary, $redirects, $opts);
        }
        if ($ret['success']) {
            $data = $ret['body'];
        } else {
            notice(t('Unable to download data from old server') . EOL);
        }
    }
    if (!$data) {
        logger('mod_import: empty file.');
        notice(t('Imported file is empty.') . EOL);
        return;
    }
    $data = json_decode($data, true);
    //	logger('import: data: ' . print_r($data,true));
    //	print_r($data);
    if (array_key_exists('user', $data) && array_key_exists('version', $data)) {
        require_once 'include/Import/import_diaspora.php';
        import_diaspora($data);
        return;
    }
    if (array_key_exists('compatibility', $data) && array_key_exists('database', $data['compatibility'])) {
        $v1 = substr($data['compatibility']['database'], -4);
        $v2 = substr(DB_UPDATE_VERSION, -4);
        if ($data['compatibility']['project'] !== PLATFORM_NAME) {
            notice(t('The data provided is not compatible with this project.'));
            return;
        }
    }
    if ($v2 > $v1) {
        $t = sprintf(t('Warning: Database versions differ by %1$d updates.'), $v2 - $v1);
        notice($t);
    }
    // import channel
    $channel = $data['channel'];
    $r = q("select * from channel where (channel_guid = '%s' or channel_hash = '%s' or channel_address = '%s' ) limit 1", dbesc($channel['channel_guid']), dbesc($channel['channel_hash']), dbesc($channel['channel_address']));
    // We should probably also verify the hash
    if ($r) {
        if ($r[0]['channel_guid'] === $channel['channel_guid'] || $r[0]['channel_hash'] === $channel['channel_hash']) {
            logger('mod_import: duplicate channel. ', print_r($channel, true));
            notice(t('Cannot create a duplicate channel identifier on this system. Import failed.') . EOL);
            return;
        } else {
            // try at most ten times to generate a unique address.
            $x = 0;
            $found_unique = false;
            do {
                $tmp = $channel['channel_address'] . mt_rand(1000, 9999);
                $r = q("select * from channel where channel_address = '%s' limit 1", dbesc($tmp));
                if (!$r) {
                    $channel['channel_address'] = $tmp;
                    $found_unique = true;
                    break;
                }
                $x++;
            } while ($x < 10);
            if (!$found_unique) {
                logger('mod_import: duplicate channel. randomisation failed.', print_r($channel, true));
                notice(t('Unable to create a unique channel address. Import failed.') . EOL);
                return;
            }
        }
    }
    unset($channel['channel_id']);
    $channel['channel_account_id'] = get_account_id();
    $channel['channel_primary'] = $seize ? 1 : 0;
    dbesc_array($channel);
    $r = dbq("INSERT INTO channel (`" . implode("`, `", array_keys($channel)) . "`) VALUES ('" . implode("', '", array_values($channel)) . "')");
    if (!$r) {
        logger('mod_import: channel clone failed. ', print_r($channel, true));
        notice(t('Channel clone failed. Import failed.') . EOL);
        return;
    }
    $r = q("select * from channel where channel_account_id = %d and channel_guid = '%s' limit 1", intval(get_account_id()), $channel['channel_guid']);
    if (!$r) {
        logger('mod_import: channel not found. ', print_r($channel, true));
        notice(t('Cloned channel not found. Import failed.') . EOL);
        return;
    }
    // reset
    $channel = $r[0];
    set_default_login_identity(get_account_id(), $channel['channel_id'], false);
    if ($data['photo']) {
        require_once 'include/photo/photo_driver.php';
        import_channel_photo(base64url_decode($data['photo']['data']), $data['photo']['type'], get_account_id(), $channel['channel_id']);
    }
    $profiles = $data['profile'];
    if ($profiles) {
        foreach ($profiles as $profile) {
            unset($profile['id']);
            $profile['aid'] = get_account_id();
            $profile['uid'] = $channel['channel_id'];
            // we are going to reset all profile photos to the original
            // somebody will have to fix this later and put all the applicable photos into the export
            $profile['photo'] = z_root() . '/photo/profile/l/' . $channel['channel_id'];
            $profile['thumb'] = z_root() . '/photo/profile/m/' . $channel['channel_id'];
            dbesc_array($profile);
            $r = dbq("INSERT INTO profile (`" . implode("`, `", array_keys($profile)) . "`) VALUES ('" . implode("', '", array_values($profile)) . "')");
        }
    }
    $hublocs = $data['hubloc'];
    if ($hublocs) {
        foreach ($hublocs as $hubloc) {
            $arr = array('guid' => $hubloc['hubloc_guid'], 'guid_sig' => $hubloc['hubloc_guid_sig'], 'url' => $hubloc['hubloc_url'], 'url_sig' => $hubloc['hubloc_url_sig']);
            $hash = make_xchan_hash($hubloc['hubloc_guid'], $hubloc['hubloc_guid_sig']);
            if ($hubloc['hubloc_network'] === 'zot' && $hash !== $hubloc['hubloc_hash']) {
                logger('forged hubloc: ' . print_r($hubloc, true));
                continue;
            }
            if (array_key_exists('hubloc_primary', $hubloc)) {
                if (intval($hubloc['hubloc_primary'])) {
                    $hubloc['hubloc_flags'] |= HUBLOC_FLAGS_PRIMARY;
                    unset($hubloc['hubloc_primary']);
                }
                if (intval($hubloc['hubloc_orphancheck'])) {
                    $hubloc['hubloc_flags'] |= HUBLOC_FLAGS_ORPHANCHECK;
                    unset($hubloc['hubloc_orphancheck']);
                }
                if (intval($hubloc['hubloc_deleted'])) {
                    $hubloc['hubloc_flags'] |= HUBLOC_FLAGS_DELETED;
                    unset($hubloc['hubloc_deleted']);
                }
                if (intval($hubloc['hubloc_error'])) {
                    $hubloc['hubloc_status'] |= HUBLOC_ERROR;
                    unset($hubloc['hubloc_error']);
                }
            }
            if ($hubloc['hubloc_hash'] === $channel['channel_hash'] && $hubloc['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY && $seize) {
                $hubloc['hubloc_flags'] = $hubloc['hubloc_flags'] ^ HUBLOC_FLAGS_PRIMARY;
            }
            if (!zot_gethub($arr)) {
                unset($hubloc['hubloc_id']);
                dbesc_array($hubloc);
                $r = dbq("INSERT INTO hubloc (`" . implode("`, `", array_keys($hubloc)) . "`) VALUES ('" . implode("', '", array_values($hubloc)) . "')");
            }
        }
    }
    // create new hubloc for the new channel at this site
    $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_network, hubloc_flags, \n\t\thubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey )\n\t\tvalues ( '%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s' )", dbesc($channel['channel_guid']), dbesc($channel['channel_guid_sig']), dbesc($channel['channel_hash']), dbesc($channel['channel_address'] . '@' . get_app()->get_hostname()), dbesc('zot'), intval($seize ? HUBLOC_FLAGS_PRIMARY : 0), dbesc(z_root()), dbesc(base64url_encode(rsa_sign(z_root(), $channel['channel_prvkey']))), dbesc(get_app()->get_hostname()), dbesc(z_root() . '/post'), dbesc(get_config('system', 'pubkey')));
    // reset the original primary hubloc if it is being seized
    if ($seize) {
        $r = q("update hubloc set hubloc_flags = (hubloc_flags & ~%d) where (hubloc_flags & %d)>0 and hubloc_hash = '%s' and hubloc_url != '%s' ", intval(HUBLOC_FLAGS_PRIMARY), intval(HUBLOC_FLAGS_PRIMARY), dbesc($channel['channel_hash']), dbesc(z_root()));
    }
    // import xchans and contact photos
    if ($seize) {
        // replace any existing xchan we may have on this site if we're seizing control
        $r = q("delete from xchan where xchan_hash = '%s'", dbesc($channel['channel_hash']));
        $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_guid_sig, xchan_pubkey, xchan_photo_l, xchan_photo_m, xchan_photo_s, xchan_addr, xchan_url, xchan_follow, xchan_connurl, xchan_name, xchan_network, xchan_photo_date, xchan_name_date ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')", dbesc($channel['channel_hash']), dbesc($channel['channel_guid']), dbesc($channel['channel_guid_sig']), dbesc($channel['channel_pubkey']), dbesc($a->get_baseurl() . "/photo/profile/l/" . $channel['channel_id']), dbesc($a->get_baseurl() . "/photo/profile/m/" . $channel['channel_id']), dbesc($a->get_baseurl() . "/photo/profile/s/" . $channel['channel_id']), dbesc($channel['channel_address'] . '@' . get_app()->get_hostname()), dbesc(z_root() . '/channel/' . $channel['channel_address']), dbesc(z_root() . '/follow?f=&url=%s'), dbesc(z_root() . '/poco/' . $channel['channel_address']), dbesc($channel['channel_name']), dbesc('zot'), dbesc(datetime_convert()), dbesc(datetime_convert()));
    }
    $xchans = $data['xchan'];
    if ($xchans) {
        foreach ($xchans as $xchan) {
            $hash = make_xchan_hash($xchan['xchan_guid'], $xchan['xchan_guid_sig']);
            if ($xchan['xchan_network'] === 'zot' && $hash !== $xchan['xchan_hash']) {
                logger('forged xchan: ' . print_r($xchan, true));
                continue;
            }
            $r = q("select xchan_hash from xchan where xchan_hash = '%s' limit 1", dbesc($xchan['xchan_hash']));
            if ($r) {
                continue;
            }
            dbesc_array($xchan);
            $r = dbq("INSERT INTO xchan (`" . implode("`, `", array_keys($xchan)) . "`) VALUES ('" . implode("', '", array_values($xchan)) . "')");
            require_once 'include/photo/photo_driver.php';
            $photos = import_profile_photo($xchan['xchan_photo_l'], $xchan['xchan_hash']);
            if ($photos[4]) {
                $photodate = NULL_DATE;
            } else {
                $photodate = $xchan['xchan_photo_date'];
            }
            $r = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s', xchan_photo_date = '%s'\n\t\t\t\twhere xchan_hash = '%s'", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($photodate), dbesc($xchan['xchan_hash']));
        }
    }
    // FIXME - ensure we have an xchan if somebody is trying to pull a fast one
    $friends = 0;
    $feeds = 0;
    // import contacts
    $abooks = $data['abook'];
    if ($abooks) {
        foreach ($abooks as $abook) {
            if (array_key_exists('abook_blocked', $abook) && intval($abook['abook_blocked'])) {
                $abook['abook_flags'] |= ABOOK_FLAG_BLOCKED;
            }
            if (array_key_exists('abook_ignored', $abook) && intval($abook['abook_ignored'])) {
                $abook['abook_flags'] |= ABOOK_FLAG_IGNORED;
            }
            if (array_key_exists('abook_hidden', $abook) && intval($abook['abook_hidden'])) {
                $abook['abook_flags'] |= ABOOK_FLAG_HIDDEN;
            }
            if (array_key_exists('abook_archived', $abook) && intval($abook['abook_archived'])) {
                $abook['abook_flags'] |= ABOOK_FLAG_ARCHIVED;
            }
            if (array_key_exists('abook_pending', $abook) && intval($abook['abook_pending'])) {
                $abook['abook_flags'] |= ABOOK_FLAG_PENDING;
            }
            if (array_key_exists('abook_unconnected', $abook) && intval($abook['abook_unconnected'])) {
                $abook['abook_flags'] |= ABOOK_FLAG_UNCONNECTED;
            }
            if (array_key_exists('abook_self', $abook) && intval($abook['abook_self'])) {
                $abook['abook_flags'] |= ABOOK_FLAG_SELF;
            }
            if (array_key_exists('abook_feed', $abook) && intval($abook['abook_feed'])) {
                $abook['abook_flags'] |= ABOOK_FLAG_FEED;
            }
            if (!($abook['abook_flags'] & ABOOK_FLAG_SELF)) {
                if ($max_friends !== false && $friends > $max_friends) {
                    continue;
                }
                if ($max_feeds !== false && $abook['abook_flags'] & ABOOK_FLAG_FEED && $feeds > $max_feeds) {
                    continue;
                }
            }
            unset($abook['abook_id']);
            $abook['abook_account'] = get_account_id();
            $abook['abook_channel'] = $channel['channel_id'];
            dbesc_array($abook);
            $r = dbq("INSERT INTO abook (`" . implode("`, `", array_keys($abook)) . "`) VALUES ('" . implode("', '", array_values($abook)) . "')");
            $friends++;
            if ($abook['abook_flags'] & ABOOK_FLAG_FEED) {
                $feeds++;
            }
        }
    }
    $configs = $data['config'];
    if ($configs) {
        foreach ($configs as $config) {
            unset($config['id']);
            $config['uid'] = $channel['channel_id'];
            dbesc_array($config);
            $r = dbq("INSERT INTO pconfig (`" . implode("`, `", array_keys($config)) . "`) VALUES ('" . implode("', '", array_values($config)) . "')");
        }
    }
    $groups = $data['group'];
    if ($groups) {
        $saved = array();
        foreach ($groups as $group) {
            $saved[$group['hash']] = array('old' => $group['id']);
            unset($group['id']);
            $group['uid'] = $channel['channel_id'];
            dbesc_array($group);
            $r = dbq("INSERT INTO groups (`" . implode("`, `", array_keys($group)) . "`) VALUES ('" . implode("', '", array_values($group)) . "')");
        }
        $r = q("select * from `groups` where uid = %d", intval($channel['channel_id']));
        if ($r) {
            foreach ($r as $rr) {
                $saved[$rr['hash']]['new'] = $rr['id'];
            }
        }
    }
    $group_members = $data['group_member'];
    if ($group_members) {
        foreach ($group_members as $group_member) {
            unset($group_member['id']);
            $group_member['uid'] = $channel['channel_id'];
            foreach ($saved as $x) {
                if ($x['old'] == $group_member['gid']) {
                    $group_member['gid'] = $x['new'];
                }
            }
            dbesc_array($group_member);
            $r = dbq("INSERT INTO group_member (`" . implode("`, `", array_keys($group_member)) . "`) VALUES ('" . implode("', '", array_values($group_member)) . "')");
        }
    }
    $saved_notification_flags = notifications_off($channel['channel_id']);
    if ($import_posts && array_key_exists('item', $data) && $data['item']) {
        foreach ($data['item'] as $i) {
            $item = get_item_elements($i);
            $r = q("select id, edited from item where mid = '%s' and uid = %d limit 1", dbesc($item['mid']), intval($channel['channel_id']));
            if ($r) {
                if ($item['edited'] > $r[0]['edited']) {
                    $item['id'] = $r[0]['id'];
                    $item['uid'] = $channel['channel_id'];
                    item_store_update($item);
                    continue;
                }
            } else {
                $item['aid'] = $channel['channel_account_id'];
                $item['uid'] = $channel['channel_id'];
                $item_result = item_store($item);
            }
        }
    }
    notifications_on($channel['channel_id'], $saved_notification_flags);
    if (array_key_exists('item_id', $data) && $data['item_id']) {
        foreach ($data['item_id'] as $i) {
            $r = q("select id from item where mid = '%s' and uid = %d limit 1", dbesc($i['mid']), intval($channel['channel_id']));
            if (!$r) {
                continue;
            }
            $z = q("select * from item_id where service = '%s' and sid = '%s' and iid = %d and uid = %d limit 1", dbesc($i['service']), dbesc($i['sid']), intval($r[0]['id']), intval($channel['channel_id']));
            if (!$z) {
                q("insert into item_id (iid,uid,sid,service) values(%d,%d,'%s','%s')", intval($r[0]['id']), intval($channel['channel_id']), dbesc($i['sid']), dbesc($i['service']));
            }
        }
    }
    // FIXME - ensure we have a self entry if somebody is trying to pull a fast one
    // send out refresh requests
    // notify old server that it may no longer be primary.
    proc_run('php', 'include/notifier.php', 'location', $channel['channel_id']);
    // This will indirectly perform a refresh_all *and* update the directory
    proc_run('php', 'include/directory.php', $channel['channel_id']);
    notice(t('Import completed.') . EOL);
    change_channel($channel['channel_id']);
    goaway(z_root() . '/network');
}
コード例 #13
0
ファイル: zot.php プロジェクト: Mauru/red
function import_author_zot($x)
{
    $hash = make_xchan_hash($x['guid'], $x['guid_sig']);
    $r = q("select hubloc_url from hubloc where hubloc_guid = '%s' and hubloc_guid_sig = '%s' and (hubloc_flags & %d) limit 1", dbesc($x['guid']), dbesc($x['guid_sig']), intval(HUBLOC_FLAGS_PRIMARY));
    if ($r) {
        logger('import_author_zot: in cache', LOGGER_DEBUG);
        return $hash;
    }
    logger('import_author_zot: entry not in cache - probing: ' . print_r($x, true), LOGGER_DEBUG);
    $them = array('hubloc_url' => $x['url'], 'xchan_guid' => $x['guid'], 'xchan_guid_sig' => $x['guid_sig']);
    if (zot_refresh($them)) {
        return $hash;
    }
    return false;
}