/** * @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; }
function import_channel($channel, $account_id, $seize) { if (!array_key_exists('channel_system', $channel)) { $channel['channel_system'] = $channel['channel_pageflags'] & 0x1000 ? 1 : 0; $channel['channel_removed'] = $channel['channel_pageflags'] & 0x8000 ? 1 : 0; } // Ignore the hash provided and re-calculate $channel['channel_hash'] = make_xchan_hash($channel['channel_guid'], $channel['channel_guid_sig']); // Check for duplicate channels $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'])); if ($r && $r[0]['channel_guid'] == $channel['channel_guid'] && $r[0]['channel_pubkey'] === $channel['channel_pubkey'] && $r[0]['channel_hash'] === $channel['channel_hash']) { return $r[0]; } if ($r || check_webbie(array($channel['channel_address'])) !== $channel['channel_address']) { 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 false; } 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 false; } } } unset($channel['channel_id']); $channel['channel_account_id'] = $account_id; $channel['channel_primary'] = $seize ? 1 : 0; if ($channel['channel_pageflags'] & PAGE_ALLOWCODE) { if (!is_site_admin()) { $channel['channel_pageflags'] = $channel['channel_pageflags'] ^ PAGE_ALLOWCODE; } } 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 false; } $r = q("select * from channel where channel_account_id = %d and channel_guid = '%s' limit 1", intval($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 false; } // reset $channel = $r[0]; set_default_login_identity($account_id, $channel['channel_id'], false); logger('import step 1'); $_SESSION['import_step'] = 1; return $channel; }
function import_post(&$a) { if (!get_account_id()) { return; } $data = null; $seize = x($_REQUEST, 'make_primary') ? intval($_REQUEST['make_primary']) : 0; $src = $_FILES['filename']['tmp_name']; $filename = basename($_FILES['filename']['name']); $filesize = intval($_FILES['filename']['size']); $filetype = $_FILES['filename']['type']; if ($src) { 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; $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); // 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) { logger('mod_import: duplicate channel. ', print_r($channel, true)); notice(t('Cannot create a duplicate channel identifier on this system. 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['guid_sig'], 'url' => $hubloc['hubloc_url'], 'url_sig' => $hubloc['hubloc_url_sig']); 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) 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 our existing xchan if we're seizing control $r = q("delete from xchan where xchan_hash = '%s' limit 1", 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) { $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' limit 1", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($photodate), dbesc($xchan_hash)); } } // FIXME - ensure we have an xchan if somebody is trying to pull a fast one // import contacts $abooks = $data['abook']; if ($abooks) { foreach ($abooks as $abook) { 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)) . "')"); } } $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 group (`" . 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 ($groups_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)) . "')"); } } // FIXME - ensure we have a self entry if somebody is trying to pull a fast one if ($seize) { // notify old server that it is no longer primary. } // This will indirectly perform a refresh_all *and* update the directory proc_run('php', 'include/directory.php', $channel['channel_id']); // send out refresh requests notice(t('Import completed.') . EOL); change_channel($channel['channel_id']); goaway(z_root() . '/network'); }
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['guid_sig'], 'url' => $hubloc['hubloc_url'], 'url_sig' => $hubloc['hubloc_url_sig']); 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) { $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 ($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'); }
function import_channel($channel, $account_id, $seize) { if (!array_key_exists('channel_system', $channel)) { $channel['channel_system'] = $channel['channel_pageflags'] & 0x1000 ? 1 : 0; $channel['channel_removed'] = $channel['channel_pageflags'] & 0x8000 ? 1 : 0; } // Ignore the hash provided and re-calculate $channel['channel_hash'] = make_xchan_hash($channel['channel_guid'], $channel['channel_guid_sig']); // Check for duplicate channels $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'])); if ($r && $r[0]['channel_guid'] == $channel['channel_guid'] && $r[0]['channel_pubkey'] === $channel['channel_pubkey'] && $r[0]['channel_hash'] === $channel['channel_hash']) { return $r[0]; } if ($r || check_webbie(array($channel['channel_address'])) !== $channel['channel_address']) { 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 false; } 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 false; } } } unset($channel['channel_id']); $channel['channel_account_id'] = $account_id; $channel['channel_primary'] = $seize ? 1 : 0; if ($channel['channel_pageflags'] & PAGE_ALLOWCODE) { if (!is_site_admin()) { $channel['channel_pageflags'] = $channel['channel_pageflags'] ^ PAGE_ALLOWCODE; } } // remove all the permissions related settings, we will import/upgrade them after the channel // is created. $disallowed = ['channel_id', 'channel_r_stream', 'channel_r_profile', 'channel_r_abook', 'channel_r_storage', 'channel_r_pages', 'channel_w_stream', 'channel_w_wall', 'channel_w_comment', 'channel_w_mail', 'channel_w_like', 'channel_w_tagwall', 'channel_w_chat', 'channel_w_storage', 'channel_w_pages', 'channel_a_republish', 'channel_a_delegate', 'perm_limits']; $clean = array(); foreach ($channel as $k => $v) { if (in_array($k, $disallowed)) { continue; } $clean[$k] = $v; } if ($clean) { dbesc_array($clean); $r = dbq("INSERT INTO channel (`" . implode("`, `", array_keys($clean)) . "`) VALUES ('" . implode("', '", array_values($clean)) . "')"); } if (!$r) { logger('mod_import: channel clone failed. ', print_r($channel, true)); notice(t('Channel clone failed. Import failed.') . EOL); return false; } $r = q("select * from channel where channel_account_id = %d and channel_guid = '%s' limit 1", intval($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 false; } // extract the permissions from the original imported array and use our new channel_id to set them // These could be in the old channel permission stule or the new pconfig. We have a function to // translate and store them no matter which they throw at us. $channel['channel_id'] = $r[0]['channel_id']; translate_channel_perms_inbound($channel); // reset $channel = $r[0]; set_default_login_identity($account_id, $channel['channel_id'], false); logger('import step 1'); $_SESSION['import_step'] = 1; return $channel; }
/** * @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; }