/** * API Login via basic-auth or OAuth */ function api_login(&$a) { $record = null; require_once 'include/oauth.php'; // login with oauth try { $oauth = new ZotOAuth1(); $req = OAuth1Request::from_request(); list($consumer, $token) = $oauth->verify_request($req); if (!is_null($token)) { $oauth->loginUser($token->uid); App::set_oauth_key($consumer->key); call_hooks('logged_in', App::$user); return; } killme(); } catch (Exception $e) { logger($e->getMessage()); } // workarounds for HTTP-auth in CGI mode if (x($_SERVER, 'REDIRECT_REMOTE_USER')) { $userpass = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"], 6)); if (strlen($userpass)) { list($name, $password) = explode(':', $userpass); $_SERVER['PHP_AUTH_USER'] = $name; $_SERVER['PHP_AUTH_PW'] = $password; } } if (x($_SERVER, 'HTTP_AUTHORIZATION')) { $userpass = base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"], 6)); if (strlen($userpass)) { list($name, $password) = explode(':', $userpass); $_SERVER['PHP_AUTH_USER'] = $name; $_SERVER['PHP_AUTH_PW'] = $password; } } require_once 'include/auth.php'; require_once 'include/security.php'; // process normal login request if (isset($_SERVER['PHP_AUTH_USER'])) { $channel_login = 0; $record = account_verify_password($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']); if ($record && $record['channel']) { $channel_login = $record['channel']['channel_id']; } } if ($record['account']) { authenticate_success($record['account']); if ($channel_login) { change_channel($channel_login); } $_SESSION['allow_api'] = true; return true; } else { $_SERVER['PHP_AUTH_PW'] = '*****'; logger('API_login failure: ' . print_r($_SERVER, true), LOGGER_DEBUG); log_failed_login('API login failure'); retry_basic_auth(); } }
/** * @param int $user_record The account_id * @param bool $login_initial default false * @param bool $interactive default false * @param bool $return * @param bool $update_lastlog */ function authenticate_success($user_record, $channel = null, $login_initial = false, $interactive = false, $return = false, $update_lastlog = false) { $_SESSION['addr'] = $_SERVER['REMOTE_ADDR']; $lastlog_updated = false; if (x($user_record, 'account_id')) { App::$account = $user_record; $_SESSION['account_id'] = $user_record['account_id']; $_SESSION['authenticated'] = 1; if ($channel) { $uid_to_load = $channel['channel_id']; } if (!$uid_to_load) { $uid_to_load = x($_SESSION, 'uid') && intval($_SESSION['uid']) ? intval($_SESSION['uid']) : intval(App::$account['account_default_channel']); } if ($uid_to_load) { change_channel($uid_to_load); } if ($login_initial || $update_lastlog) { q("update account set account_lastlog = '%s' where account_id = %d", dbesc(datetime_convert()), intval($_SESSION['account_id'])); App::$account['account_lastlog'] = datetime_convert(); $lastlog_updated = true; call_hooks('logged_in', App::$account); } } if ($login_initial && !$lastlog_updated) { call_hooks('logged_in', $user_record); // might want to log success here } if ($return || x($_SESSION, 'workflow')) { unset($_SESSION['workflow']); return; } if (App::$module !== 'home' && x($_SESSION, 'login_return_url') && strlen($_SESSION['login_return_url'])) { $return_url = $_SESSION['login_return_url']; // don't let members get redirected to a raw ajax page update - this can happen // if DHCP changes the IP address at an unfortunate time and paranoia is turned on if (strstr($return_url, 'update_')) { $return_url = ''; } unset($_SESSION['login_return_url']); goaway(z_root() . '/' . $return_url); } /* This account has never created a channel. Send them to new_channel by default */ if (App::$module === 'login') { $r = q("select count(channel_id) as total from channel where channel_account_id = %d and channel_removed = 0 ", intval(App::$account['account_id'])); if ($r && !$r[0]['total']) { goaway(z_root() . '/new_channel'); } } /* else just return */ }
function api_user() { $aid = get_account_id(); $channel = get_app()->get_channel(); if ($aid && x($_REQUEST, 'channel')) { // Only change channel if it is different than the current channel if ($channel && x($channel, 'channel_address') && $channel['channel_address'] != $_REQUEST['channel']) { $c = q("select channel_id from channel where channel_address = '%s' and channel_account_id = %d limit 1", dbesc($_REQUEST['channel']), intval($aid)); if (!$c || !change_channel($c[0]['channel_id'])) { return false; } } } if ($_SESSION["allow_api"]) { return local_channel(); } return false; }
function new_channel_post(&$a) { $arr = $_POST; if (($arr['account_id'] = get_account_id()) === false) { notice(t('Permission denied.') . EOL); return; } $result = create_identity($arr); if (!$result['success']) { notice($result['message']); return; } $newuid = $result['channel']['channel_id']; change_channel($result['channel']['channel_id']); if (!strlen($next_page = get_config('system', 'workflow_channel_next'))) { $next_page = 'settings'; } goaway(z_root() . '/' . $next_page); }
/** @file */ function authenticate_success($user_record, $login_initial = false, $interactive = false, $return = false, $update_lastlog = false) { $a = get_app(); $_SESSION['addr'] = $_SERVER['REMOTE_ADDR']; if (x($user_record, 'account_id')) { $a->account = $user_record; $_SESSION['account_id'] = $user_record['account_id']; $_SESSION['authenticated'] = 1; if ($login_initial || $update_lastlog) { q("update account set account_lastlog = '%s' where account_id = %d limit 1", dbesc(datetime_convert()), intval($_SESSION['account_id'])); $a->account['account_lastlog'] = datetime_convert(); call_hooks('logged_in', $a->account); } $uid_to_load = x($_SESSION, 'uid') && intval($_SESSION['uid']) ? intval($_SESSION['uid']) : intval($a->account['account_default_channel']); if ($uid_to_load) { change_channel($uid_to_load); } } if ($login_initial) { call_hooks('logged_in', $user_record); // might want to log success here } if ($return || x($_SESSION, 'workflow')) { unset($_SESSION['workflow']); return; } if ($a->module !== 'home' && x($_SESSION, 'login_return_url') && strlen($_SESSION['login_return_url'])) { $return_url = $_SESSION['login_return_url']; unset($_SESSION['login_return_url']); goaway($a->get_baseurl() . '/' . $return_url); } /* This account has never created a channel. Send them to new_channel by default */ if ($a->module === 'login') { $r = q("select count(channel_id) as total from channel where channel_account_id = %d and not ( channel_pageflags & %d)", intval($a->account['account_id']), intval(PAGE_REMOVED)); if ($r && !$r[0]['total']) { goaway(z_root() . '/new_channel'); } } /* else just return */ }
function new_channel_post(&$a) { $arr = $_POST; $acc = $a->get_account(); $arr['account_id'] = get_account_id(); // prevent execution by delegated channels as well as those not logged in. // get_account_id() returns the account_id from the session. But $a->account // may point to the original authenticated account. if (!$acc || $acc['account_id'] != $arr['account_id']) { notice(t('Permission denied.') . EOL); return; } $result = create_identity($arr); if (!$result['success']) { notice($result['message']); return; } $newuid = $result['channel']['channel_id']; change_channel($result['channel']['channel_id']); if (!strlen($next_page = get_config('system', 'workflow_channel_next'))) { $next_page = 'settings'; } goaway(z_root() . '/' . $next_page); }
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'); }
function post() { $max_dailies = intval(get_config('system', 'max_daily_registrations')); if ($max_dailies) { $r = q("select count(account_id) as total from account where account_created > %s - INTERVAL %s", db_utcnow(), db_quoteinterval('1 day')); if ($r && $r[0]['total'] >= $max_dailies) { notice(t('Maximum daily site registrations exceeded. Please try again tomorrow.') . EOL); return; } } if (!x($_POST, 'tos')) { notice(t('Please indicate acceptance of the Terms of Service. Registration failed.') . EOL); return; } $policy = get_config('system', 'register_policy'); $email_verify = get_config('system', 'verify_email'); switch ($policy) { case REGISTER_OPEN: $flags = ACCOUNT_OK; break; case REGISTER_APPROVE: $flags = ACCOUNT_BLOCKED | ACCOUNT_PENDING; break; default: case REGISTER_CLOSED: if (!is_site_admin()) { notice(t('Permission denied.') . EOL); return; } $flags = ACCOUNT_BLOCKED; break; } if ($email_verify && $policy == REGISTER_OPEN) { $flags = $flags | ACCOUNT_UNVERIFIED; } if (!$_POST['password'] || $_POST['password'] !== $_POST['password2']) { notice(t('Passwords do not match.') . EOL); return; } $arr = $_POST; $arr['account_flags'] = $flags; $result = create_account($arr); if (!$result['success']) { notice($result['message']); return; } require_once 'include/security.php'; if ($_REQUEST['name']) { set_aconfig($result['account']['account_id'], 'register', 'channel_name', $_REQUEST['name']); } if ($_REQUEST['nickname']) { set_aconfig($result['account']['account_id'], 'register', 'channel_address', $_REQUEST['nickname']); } if ($_REQUEST['permissions_role']) { set_aconfig($result['account']['account_id'], 'register', 'permissions_role', $_REQUEST['permissions_role']); } $using_invites = intval(get_config('system', 'invitation_only')); $num_invites = intval(get_config('system', 'number_invites')); $invite_code = x($_POST, 'invite_code') ? notags(trim($_POST['invite_code'])) : ''; if ($using_invites && $invite_code) { q("delete * from register where hash = '%s'", dbesc($invite_code)); // @FIXME - this also needs to be considered when using 'invites_remaining' in mod/invite.php set_aconfig($result['account']['account_id'], 'system', 'invites_remaining', $num_invites); } if ($policy == REGISTER_OPEN) { if ($email_verify) { $res = verify_email_address($result); } else { $res = send_register_success_email($result['email'], $result['password']); } if ($res) { info(t('Registration successful. Please check your email for validation instructions.') . EOL); } } elseif ($policy == REGISTER_APPROVE) { $res = send_reg_approval_email($result); if ($res) { info(t('Your registration is pending approval by the site owner.') . EOL); } else { notice(t('Your registration can not be processed.') . EOL); } goaway(z_root()); } if ($email_verify) { goaway(z_root()); } authenticate_success($result['account'], null, true, false, true); $new_channel = false; $next_page = 'new_channel'; if (get_config('system', 'auto_channel_create') || UNO) { $new_channel = auto_channel_create($result['account']['account_id']); if ($new_channel['success']) { $channel_id = $new_channel['channel']['channel_id']; change_channel($channel_id); $next_page = '~'; } else { $new_channel = false; } } $x = get_config('system', 'workflow_register_next'); if ($x) { $next_page = $x; $_SESSION['workflow'] = true; } goaway(z_root() . '/' . $next_page); }
function Verify($channel, $hubloc) { logger('auth request received from ' . $hubloc['hubloc_addr']); $this->remote = remote_channel(); $this->remote_service_class = ''; $this->remote_level = 0; $this->remote_hub = $hubloc['hubloc_url']; $this->dnt = 0; // check credentials and access // If they are already authenticated and haven't changed credentials, // we can save an expensive network round trip and improve performance. // Also check that they are coming from the same site as they authenticated with originally. $already_authed = remote_channel() && $hubloc['hubloc_hash'] == remote_channel() && $hubloc['hubloc_url'] === $_SESSION['remote_hub'] ? true : false; if ($this->delegate && $this->delegate !== $_SESSION['delegate_channel']) { $already_authed = false; } if ($already_authed) { return true; } if (local_channel()) { // tell them to logout if they're logged in locally as anything but the target remote account // in which case just shut up because they don't need to be doing this at all. if (\App::$channel['channel_hash'] == $hubloc['xchan_hash']) { return true; } else { logger('already authenticated locally as somebody else.'); notice(t('Remote authentication blocked. You are logged into this site locally. Please logout and retry.') . EOL); if ($this->test) { $this->Debug('already logged in locally with a conflicting identity.'); return false; } } return false; } // Auth packets MUST use ultra top-secret hush-hush mode - e.g. the entire packet is encrypted using the // site private key // The actual channel sending the packet ($c[0]) is not important, but this provides a // generic zot packet with a sender which can be verified $p = zot_build_packet($channel, $type = 'auth_check', array(array('guid' => $hubloc['hubloc_guid'], 'guid_sig' => $hubloc['hubloc_guid_sig'])), $hubloc['hubloc_sitekey'], $this->sec); $this->Debug('auth check packet created using sitekey ' . $hubloc['hubloc_sitekey']); $this->Debug('packet contents: ' . $p); $result = zot_zot($hubloc['hubloc_callback'], $p); if (!$result['success']) { logger('auth_check callback failed.'); if ($this->test) { $this->Debug('auth check request to your site returned .' . print_r($result, true)); } return false; } $j = json_decode($result['body'], true); if (!$j) { logger('auth_check json data malformed.'); if ($this->test) { $this->Debug('json malformed: ' . $result['body']); } return false; } $this->Debug('auth check request returned .' . print_r($j, true)); if (!$j['success']) { return false; } // legit response, but we do need to check that this wasn't answered by a man-in-middle if (!rsa_verify($this->sec . $hubloc['xchan_hash'], base64url_decode($j['confirm']), $hubloc['xchan_pubkey'])) { logger('final confirmation failed.'); if ($this->test) { $this->Debug('final confirmation failed. ' . $sec . print_r($j, true) . print_r($hubloc, true)); } return false; } if (array_key_exists('service_class', $j)) { $this->remote_service_class = $j['service_class']; } if (array_key_exists('level', $j)) { $this->remote_level = $j['level']; } if (array_key_exists('DNT', $j)) { $this->dnt = $j['DNT']; } // log them in if ($this->test) { // testing only - return the success result $this->test_results['success'] = true; $this->Debug('Authentication Success!'); $this->Finalise(); } $_SESSION['authenticated'] = 1; // check for delegation and if all is well, log them in locally with delegation restrictions $this->delegate_success = false; if ($this->delegate) { $r = q("select * from channel left join xchan on channel_hash = xchan_hash where xchan_addr = '%s' limit 1", dbesc($this->delegate)); if ($r && intval($r[0]['channel_id'])) { $allowed = perm_is_allowed($r[0]['channel_id'], $hubloc['xchan_hash'], 'delegate'); if ($allowed) { $_SESSION['delegate_channel'] = $r[0]['channel_id']; $_SESSION['delegate'] = $hubloc['xchan_hash']; $_SESSION['account_id'] = intval($r[0]['channel_account_id']); require_once 'include/security.php'; // this will set the local_channel authentication in the session change_channel($r[0]['channel_id']); $this->delegate_success = true; } } } if (!$this->delegate_success) { // normal visitor (remote_channel) login session credentials $_SESSION['visitor_id'] = $hubloc['xchan_hash']; $_SESSION['my_url'] = $hubloc['xchan_url']; $_SESSION['my_address'] = $this->address; $_SESSION['remote_service_class'] = $this->remote_service_class; $_SESSION['remote_level'] = $this->remote_level; $_SESSION['remote_hub'] = $this->remote_hub; $_SESSION['DNT'] = $this->dnt; } $arr = array('xchan' => $hubloc, 'url' => $this->desturl, 'session' => $_SESSION); call_hooks('magic_auth_success', $arr); \App::set_observer($hubloc); require_once 'include/security.php'; \App::set_groups(init_groups_visitor($_SESSION['visitor_id'])); info(sprintf(t('Welcome %s. Remote authentication successful.'), $hubloc['xchan_name'])); logger('mod_zot: auth success from ' . $hubloc['xchan_addr']); $this->success = true; return true; }
function get() { if (!get_account_id() || $_SESSION['delegate']) { notice(t('Permission denied.') . EOL); return; } require_once 'include/security.php'; $change_channel = argc() > 1 ? intval(argv(1)) : 0; if (argc() > 2 && argv(2) === 'default') { $r = q("select channel_id from channel where channel_id = %d and channel_account_id = %d limit 1", intval($change_channel), intval(get_account_id())); if ($r) { q("update account set account_default_channel = %d where account_id = %d", intval($change_channel), intval(get_account_id())); } goaway(z_root() . '/manage'); } if ($change_channel) { $r = change_channel($change_channel); if (argc() > 2 && !(argv(2) === 'default')) { goaway(z_root() . '/' . implode('/', array_slice(\App::$argv, 2))); // Go to whatever is after /manage/, but with the new channel } else { if ($r && $r['channel_startpage']) { goaway(z_root() . '/' . $r['channel_startpage']); } // If nothing extra is specified, go to the default page } goaway(z_root()); } $channels = null; if (local_channel()) { $r = q("select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel.channel_account_id = %d and channel_removed = 0 order by channel_name ", intval(get_account_id())); $account = \App::get_account(); if ($r && count($r)) { $channels = $r; for ($x = 0; $x < count($channels); $x++) { $channels[$x]['link'] = 'manage/' . intval($channels[$x]['channel_id']); $channels[$x]['default'] = $channels[$x]['channel_id'] == $account['account_default_channel'] ? "1" : ''; $channels[$x]['default_links'] = '1'; $c = q("SELECT id, item_wall FROM item\n\t\t\t\t\t\tWHERE item_unseen = 1 and uid = %d " . item_normal(), intval($channels[$x]['channel_id'])); if ($c) { foreach ($c as $it) { if (intval($it['item_wall'])) { $channels[$x]['home']++; } else { $channels[$x]['network']++; } } } $intr = q("SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ", intval($channels[$x]['channel_id'])); if ($intr) { $channels[$x]['intros'] = intval($intr[0]['total']); } $mails = q("SELECT count(id) as total from mail WHERE channel_id = %d AND mail_seen = 0 and from_xchan != '%s' ", intval($channels[$x]['channel_id']), dbesc($channels[$x]['channel_hash'])); if ($mails) { $channels[$x]['mail'] = intval($mails[0]['total']); } $events = q("SELECT type, start, adjust FROM `event`\n\t\t\t\t\t\tWHERE `event`.`uid` = %d AND start < '%s' AND start > '%s' and `ignore` = 0\n\t\t\t\t\t\tORDER BY `start` ASC ", intval($channels[$x]['channel_id']), dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + 7 days')), dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days'))); if ($events) { $channels[$x]['all_events'] = count($events); if ($channels[$x]['all_events']) { $str_now = datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y-m-d'); foreach ($events as $e) { $bd = false; if ($e['type'] === 'birthday') { $channels[$x]['birthdays']++; $bd = true; } else { $channels[$x]['events']++; } if (datetime_convert('UTC', intval($e['adjust']) ? date_default_timezone_get() : 'UTC', $e['start'], 'Y-m-d') === $str_now) { $channels[$x]['all_events_today']++; if ($bd) { $channels[$x]['birthdays_today']++; } else { $channels[$x]['events_today']++; } } } } } } } $r = q("select count(channel_id) as total from channel where channel_account_id = %d and channel_removed = 0", intval(get_account_id())); $limit = account_service_class_fetch(get_account_id(), 'total_identities'); if ($limit !== false) { $channel_usage_message = sprintf(t("You have created %1\$.0f of %2\$.0f allowed channels."), $r[0]['total'], $limit); } else { $channel_usage_message = ''; } } $create = array('new_channel', t('Create a new channel'), t('Create New')); $delegates = q("select * from abook left join xchan on abook_xchan = xchan_hash where \n\t\t\tabook_channel = %d and (abook_their_perms & %d) > 0", intval(local_channel()), intval(PERMS_A_DELEGATE)); if ($delegates) { for ($x = 0; $x < count($delegates); $x++) { $delegates[$x]['link'] = 'magic?f=&dest=' . urlencode($delegates[$x]['xchan_url']) . '&delegate=' . urlencode($delegates[$x]['xchan_addr']); $delegates[$x]['channel_name'] = $delegates[$x]['xchan_name']; $delegates[$x]['delegate'] = 1; } } else { $delegates = null; } $o = replace_macros(get_markup_template('channels.tpl'), array('$header' => t('Channel Manager'), '$msg_selected' => t('Current Channel'), '$selected' => local_channel(), '$desc' => t('Switch to one of your channels by selecting it.'), '$msg_default' => t('Default Channel'), '$msg_make_default' => t('Make Default'), '$create' => $create, '$all_channels' => $channels, '$mail_format' => t('%d new messages'), '$intros_format' => t('%d new introductions'), '$channel_usage_message' => $channel_usage_message, '$delegated_desc' => t('Delegated Channel'), '$delegates' => $delegates)); return $o; }
function manage_content(&$a) { if (!get_account_id()) { notice(t('Permission denied.') . EOL); return; } require_once 'include/security.php'; $change_channel = argc() > 1 ? intval(argv(1)) : 0; if (argc() > 2 && argv(2) === 'default') { $r = q("select channel_id from channel where channel_id = %d and channel_account_id = %d limit 1", intval($change_channel), intval(get_account_id())); if ($r) { q("update account set account_default_channel = %d where account_id = %d limit 1", intval($change_channel), intval(get_account_id())); } goaway(z_root() . '/manage'); } if ($change_channel) { $r = change_channel($change_channel); if ($r && $r['channel_startpage']) { goaway(z_root() . '/' . $r['channel_startpage']); } goaway(z_root()); } $channels = null; if (local_user()) { $r = q("select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel.channel_account_id = %d and not ( channel_pageflags & %d ) order by channel_name ", intval(get_account_id()), intval(PAGE_REMOVED)); $selected_channel = null; $account = get_app()->get_account(); if ($r && count($r)) { $channels = $r; for ($x = 0; $x < count($channels); $x++) { $channels[$x]['link'] = 'manage/' . intval($channels[$x]['channel_id']); if ($channels[$x]['channel_id'] == local_user()) { $selected_channel = $channels[$x]; } $channels[$x]['default'] = $channels[$x]['channel_id'] == $account['account_default_channel'] ? "1" : ''; $channels[$x]['default_links'] = '1'; $c = q("SELECT id, item_restrict, item_flags FROM item\n\t\t\t\t\tWHERE (item_restrict = %d) and ( item_flags & %d ) and uid = %d", intval(ITEM_VISIBLE), intval(ITEM_UNSEEN), intval($channels[$x]['channel_id'])); if ($c) { foreach ($c as $it) { if ($it['item_flags'] & ITEM_WALL) { $channels[$x]['home']++; } else { $channels[$x]['network']++; } } } $intr = q("SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and (abook_flags & %d) and not ((abook_flags & %d) or (xchan_flags & %d))", intval($channels[$x]['channel_id']), intval(ABOOK_FLAG_PENDING), intval(ABOOK_FLAG_SELF | ABOOK_FLAG_IGNORED), intval(XCHAN_FLAGS_DELETED | XCHAN_FLAGS_ORPHAN)); if ($intr) { $channels[$x]['intros'] = intval($intr[0]['total']); } $mails = q("SELECT count(id) as total from mail WHERE channel_id = %d AND not (mail_flags & %d) and from_xchan != '%s' ", intval($channels[$x]['channel_id']), intval(MAIL_SEEN), dbesc($channels[$x]['channel_hash'])); if ($mails) { $channels[$x]['mail'] = intval($mails[0]['total']); } $events = q("SELECT type, start, adjust FROM `event`\n\t\t\t\t\tWHERE `event`.`uid` = %d AND start < '%s' AND start > '%s' and `ignore` = 0\n\t\t\t\t\tORDER BY `start` ASC ", intval($channels[$x]['channel_id']), dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + 7 days')), dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days'))); if ($events) { $channels[$x]['all_events'] = count($events); if ($channels[$x]['all_events']) { $str_now = datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y-m-d'); foreach ($events as $e) { $bd = false; if ($e['type'] === 'birthday') { $channels[$x]['birthdays']++; $bd = true; } else { $channels[$x]['events']++; } if (datetime_convert('UTC', intval($e['adjust']) ? date_default_timezone_get() : 'UTC', $e['start'], 'Y-m-d') === $str_now) { $channels[$x]['all_events_today']++; if ($bd) { $channels[$x]['birthdays_today']++; } else { $channels[$x]['events_today']++; } } } } } } } $r = q("select count(channel_id) as total from channel where channel_account_id = %d and not ( channel_pageflags & %d )", intval(get_account_id()), intval(PAGE_REMOVED)); $limit = service_class_fetch(local_user(), 'total_identities'); if ($limit !== false) { $channel_usage_message = sprintf(t("You have created %1\$.0f of %2\$.0f allowed channels."), $r[0]['total'], $limit); } else { $channel_usage_message = ''; } } $links = array(array('new_channel', t('Create a new channel'), t('Create a new channel'))); $o = replace_macros(get_markup_template('channels.tpl'), array('$header' => t('Channel Manager'), '$msg_selected' => t('Current Channel'), '$selected' => $selected_channel, '$desc' => t('Attach to one of your channels by selecting it.'), '$msg_default' => t('Default Channel'), '$msg_make_default' => t('Make Default'), '$links' => $links, '$all_channels' => $channels, '$channel_usage_message' => $channel_usage_message)); return $o; }
function import_diaspora($data) { $a = get_app(); $account = $a->get_account(); if (!$account) { return false; } $address = escape_tags($data['user']['username']); if (!$address) { notice(t('No username found in import file.') . EOL); return false; } $r = q("select * from channel where channel_address = '%s' limit 1", dbesc($address)); if ($r) { // try at most ten times to generate a unique address. $x = 0; $found_unique = false; do { $tmp = $address . mt_rand(1000, 9999); $r = q("select * from channel where channel_address = '%s' limit 1", dbesc($tmp)); if (!$r) { $address = $tmp; $found_unique = true; break; } $x++; } while ($x < 10); if (!$found_unique) { logger('import_diaspora: duplicate channel address. randomisation failed.'); notice(t('Unable to create a unique channel address. Import failed.') . EOL); return; } } $c = create_identity(array('name' => escape_tags($data['user']['name']), 'nickname' => $address, 'account_id' => $account['account_id'], 'permissions_role' => 'social')); if (!$c['success']) { return; } $channel_id = $c['channel']['channel_id']; // todo - add auto follow settings, (and strip exif in hubzilla) $location = escape_tags($data['user']['profile']['location']); if (!$location) { $location = ''; } q("update channel set channel_location = '%s' where channel_id = %d", dbesc($location), intval($channel_id)); if ($data['user']['profile']['nsfw']) { // fixme for hubzilla which doesn't use pageflags any more q("update channel set channel_pageflags = (channel_pageflags | %d) where channel_id = %d", intval(PAGE_ADULT), intval($channel_id)); } if ($data['user']['profile']['image_url']) { $p = z_fetch_url($data['user']['profile']['image_url'], true); if ($p['success']) { $rawbytes = $p['body']; $type = guess_image_type('dummyfile', $p['header']); import_channel_photo($rawbytes, $type, $c['channel']['channel_account_id'], $channel_id); } } $gender = escape_tags($data['user']['profile']['gender']); $about = diaspora2bb($data['user']['profile']['bio']); $publish = intval($data['user']['profile']['searchable']); if ($data['user']['profile']['birthday']) { $dob = datetime_convert('UTC', 'UTC', $data['user']['profile']['birthday'], 'Y-m-d'); } else { $dob = '0000-00-00'; } // we're relying on the fact that this channel was just created and will only // have the default profile currently $r = q("update profile set gender = '%s', about = '%s', dob = '%s', publish = %d where uid = %d", dbesc($gender), dbesc($about), dbesc($dob), dbesc($publish), intval($channel_id)); if ($data['user']['aspects']) { foreach ($data['user']['aspects'] as $aspect) { group_add($channel_id, escape_tags($aspect['name']), intval($aspect['contacts_visible'])); } } // now add connections and send friend requests if ($data['user']['contacts']) { foreach ($data['user']['contacts'] as $contact) { $result = new_contact($channel_id, $contact['person_diaspora_handle'], $c['channel']); if ($result['success']) { if ($contact['aspects']) { foreach ($contact['aspects'] as $aspect) { group_add_member($channel_id, $aspect['name'], $result['abook']['xchan_hash']); } } } } } // Then add items - note this can't be done until Diaspora adds guids to exported // items and comments // This will indirectly perform a refresh_all *and* update the directory proc_run('php', 'include/directory.php', $channel_id); notice(t('Import completed.') . EOL); change_channel($channel_id); goaway(z_root() . '/network'); }
/** * Simple HTTP Login */ function api_login(&$a) { // login with oauth try { $oauth = new FKOAuth1(); $req = OAuthRequest::from_request(); list($consumer, $token) = $oauth->verify_request($req); // list($consumer,$token) = $oauth->verify_request(OAuthRequest::from_request()); if (!is_null($token)) { $oauth->loginUser($token->uid); $a->set_oauth_key($consumer->key); call_hooks('logged_in', $a->user); return; } echo __FILE__ . __LINE__ . __FUNCTION__ . "<pre>"; // var_dump($consumer, $token); die; } catch (Exception $e) { logger(__FILE__ . __LINE__ . __FUNCTION__ . "\n" . $e); } // workaround for HTTP-auth in CGI mode if (x($_SERVER, 'REDIRECT_REMOTE_USER')) { $userpass = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"], 6)); if (strlen($userpass)) { list($name, $password) = explode(':', $userpass); $_SERVER['PHP_AUTH_USER'] = $name; $_SERVER['PHP_AUTH_PW'] = $password; } } if (x($_SERVER, 'HTTP_AUTHORIZATION')) { $userpass = base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"], 6)); if (strlen($userpass)) { list($name, $password) = explode(':', $userpass); $_SERVER['PHP_AUTH_USER'] = $name; $_SERVER['PHP_AUTH_PW'] = $password; } } if (!isset($_SERVER['PHP_AUTH_USER'])) { logger('API_login: '******'WWW-Authenticate: Basic realm="Red"'); header('HTTP/1.0 401 Unauthorized'); die('This api requires login'); } // process normal login request require_once 'include/auth.php'; $channel_login = 0; $record = account_verify_password($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']); if (!$record) { $r = q("select * from channel where channel_address = '%s' limit 1", dbesc($_SERVER['PHP_AUTH_USER'])); if ($r) { $x = q("select * from account where account_id = %d limit 1", intval($r[0]['channel_account_id'])); if ($x) { $record = account_verify_password($x[0]['account_email'], $_SERVER['PHP_AUTH_PW']); if ($record) { $channel_login = $r[0]['channel_id']; } } } if (!$record) { logger('API_login failure: ' . print_r($_SERVER, true), LOGGER_DEBUG); header('WWW-Authenticate: Basic realm="Red"'); header('HTTP/1.0 401 Unauthorized'); die('This api requires login'); } } require_once 'include/security.php'; authenticate_success($record); if ($channel_login) { change_channel($channel_login); } $_SESSION['allow_api'] = true; }
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 loginUser($uid) { logger("RedOAuth1::loginUser {$uid}"); $a = get_app(); $r = q("SELECT * FROM channel WHERE channel_id = %d LIMIT 1", intval($uid)); if (count($r)) { $record = $r[0]; } else { logger('FKOAuth1::loginUser failure: ' . print_r($_SERVER, true), LOGGER_DEBUG); header('HTTP/1.0 401 Unauthorized'); die('This api requires login'); } $_SESSION['uid'] = $record['channel_id']; $_SESSION['theme'] = $record['channel_theme']; $_SESSION['account_id'] = $record['channel_account_id']; $_SESSION['mobile_theme'] = get_pconfig($record['channel_id'], 'system', 'mobile_theme'); $_SESSION['authenticated'] = 1; $_SESSION['my_url'] = $a->get_baseurl() . '/channel/' . $record['channel_address']; $_SESSION['addr'] = $_SERVER['REMOTE_ADDR']; $_SESSION['allow_api'] = true; $x = q("select * from account where account_id = %d limit 1", intval($record['channel_account_id'])); if ($x) { $a->account = $x[0]; } change_channel($record['channel_id']); $a->channel = $record; if (strlen($a->channel['channel_timezone'])) { date_default_timezone_set($a->channel['channel_timezone']); // $a->timezone = $a->user['timezone']; } // $r = q("SELECT * FROM `contact` WHERE `uid` = %s AND `self` = 1 LIMIT 1", // intval($_SESSION['uid'])); // if(count($r)) { // $a->contact = $r[0]; // $a->cid = $r[0]['id']; // $_SESSION['cid'] = $a->cid; // } // q("UPDATE `user` SET `login_date` = '%s' WHERE `uid` = %d LIMIT 1", // dbesc(datetime_convert()), // intval($_SESSION['uid']) // ); // // call_hooks('logged_in', $a->user); }
/** * remote post * * https://yoursite/rpost?f=&title=&body=&remote_return= * * This can be called via either GET or POST, use POST for long body content as suhosin often limits GET parameter length * * f= placeholder, often required * title= Title of post * body= Body of post * url= URL which will be parsed and the results appended to the body * source= Source application * remote_return= absolute URL to return after posting is finished * type= choices are 'html' or 'bbcode', default is 'bbcode' * */ function rpost_content(&$a) { $o = ''; if (!local_channel()) { if (remote_channel()) { // redirect to your own site. // We can only do this with a GET request so you'll need to keep the text short or risk getting truncated // by the wretched beast called 'suhosin'. All the browsers now allow long GET requests, but suhosin // blocks them. $url = get_rpost_path($a->get_observer()); // make sure we're not looping to our own hub if ($url && !stristr($url, $a->get_hostname())) { foreach ($_REQUEST as $key => $arg) { $url .= '&' . $key . '=' . $arg; } goaway($url); } } // The login procedure is going to bugger our $_REQUEST variables // so save them in the session. if (array_key_exists('body', $_REQUEST)) { $_SESSION['rpost'] = $_REQUEST; } return login(); } // If we have saved rpost session variables, but nothing in the current $_REQUEST, recover the saved variables if (!array_key_exists('body', $_REQUEST) && array_key_exists('rpost', $_SESSION)) { $_REQUEST = $_SESSION['rpost']; unset($_SESSION['rpost']); } if (array_key_exists('channel', $_REQUEST)) { $r = q("select channel_id from channel where channel_account_id = %d and channel_address = '%s' limit 1", intval(get_account_id()), dbesc($_REQUEST['channel'])); if ($r) { require_once 'include/security.php'; $change = change_channel($r[0]['channel_id']); } } if ($_REQUEST['remote_return']) { $_SESSION['remote_return'] = $_REQUEST['remote_return']; } if (argc() > 1 && argv(1) === 'return') { if ($_SESSION['remote_return']) { goaway($_SESSION['remote_return']); } goaway(z_root() . '/network'); } $plaintext = true; // if(feature_enabled(local_channel(),'richtext')) // $plaintext = false; if (array_key_exists('type', $_REQUEST) && $_REQUEST['type'] === 'html') { require_once 'include/html2bbcode.php'; $_REQUEST['body'] = html2bbcode($_REQUEST['body']); } $channel = $a->get_channel(); $channel_acl = array('allow_cid' => $channel['channel_allow_cid'], 'allow_gid' => $channel['channel_allow_gid'], 'deny_cid' => $channel['channel_deny_cid'], 'deny_gid' => $channel['channel_deny_gid']); if ($_REQUEST['url']) { $x = z_fetch_url(z_root() . '/parse_url?f=&url=' . urlencode($_REQUEST['url'])); if ($x['success']) { $_REQUEST['body'] = $_REQUEST['body'] . $x['body']; } } $x = array('is_owner' => true, 'allow_location' => intval(get_pconfig($channel['channel_id'], 'system', 'use_browser_location')) ? '1' : '', 'default_location' => $channel['channel_location'], 'nickname' => $channel['channel_address'], 'lockstate' => $channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid'] ? 'lock' : 'unlock', 'acl' => populate_acl($channel_acl), 'bang' => '', 'visitor' => true, 'profile_uid' => local_channel(), 'title' => $_REQUEST['title'], 'body' => $_REQUEST['body'], 'attachment' => $_REQUEST['attachment'], 'source' => x($_REQUEST, 'source') ? strip_tags($_REQUEST['source']) : '', 'return_path' => 'rpost/return'); $editor = status_editor($a, $x); $o .= replace_macros(get_markup_template('edpost_head.tpl'), array('$title' => t('Edit post'), '$editor' => $editor)); return $o; }
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 init() { $ret = array('success' => false, 'url' => '', 'message' => ''); logger('mod_magic: invoked', LOGGER_DEBUG); logger('mod_magic: args: ' . print_r($_REQUEST, true), LOGGER_DATA); $addr = x($_REQUEST, 'addr') ? $_REQUEST['addr'] : ''; $dest = x($_REQUEST, 'dest') ? $_REQUEST['dest'] : ''; $test = x($_REQUEST, 'test') ? intval($_REQUEST['test']) : 0; $rev = x($_REQUEST, 'rev') ? intval($_REQUEST['rev']) : 0; $delegate = x($_REQUEST, 'delegate') ? $_REQUEST['delegate'] : ''; $parsed = parse_url($dest); if (!$parsed) { if ($test) { $ret['message'] .= 'could not parse ' . $dest . EOL; return $ret; } goaway($dest); } $basepath = $parsed['scheme'] . '://' . $parsed['host'] . ($parsed['port'] ? ':' . $parsed['port'] : ''); $x = q("select * from hubloc where hubloc_url = '%s' order by hubloc_connected desc limit 1", dbesc($basepath)); if (!$x) { /* * We have no records for, or prior communications with this hub. * If an address was supplied, let's finger them to create a hub record. * Otherwise we'll use the special address '[system]' which will return * either a system channel or the first available normal channel. We don't * really care about what channel is returned - we need the hub information * from that response so that we can create signed auth packets destined * for that hub. * */ $ret = zot_finger($addr ? $addr : '[system]@' . $parsed['host'], null); if ($ret['success']) { $j = json_decode($ret['body'], true); if ($j) { import_xchan($j); } // Now try again $x = q("select * from hubloc where hubloc_url = '%s' order by hubloc_connected desc limit 1", dbesc($basepath)); } } if (!$x) { if ($rev) { goaway($dest); } else { logger('mod_magic: no channels found for requested hub.' . print_r($_REQUEST, true)); if ($test) { $ret['message'] .= 'This site has no previous connections with ' . $basepath . EOL; return $ret; } notice(t('Hub not found.') . EOL); return; } } // This is ready-made for a plugin that provides a blacklist or "ask me" before blindly authenticating. // By default, we'll proceed without asking. $arr = array('channel_id' => local_channel(), 'xchan' => $x[0], 'destination' => $dest, 'proceed' => true); call_hooks('magic_auth', $arr); $dest = $arr['destination']; if (!$arr['proceed']) { if ($test) { $ret['message'] .= 'cancelled by plugin.' . EOL; return $ret; } goaway($dest); } if (get_observer_hash() && $x[0]['hubloc_url'] === z_root()) { // We are already authenticated on this site and a registered observer. // Just redirect. if ($test) { $ret['success'] = true; $ret['message'] .= 'Local site - you are already authenticated.' . EOL; return $ret; } $delegation_success = false; if ($delegate) { $r = q("select * from channel left join hubloc on channel_hash = hubloc_hash where hubloc_addr = '%s' limit 1", dbesc($delegate)); if ($r && intval($r[0]['channel_id'])) { $allowed = perm_is_allowed($r[0]['channel_id'], get_observer_hash(), 'delegate'); if ($allowed) { $_SESSION['delegate_channel'] = $r[0]['channel_id']; $_SESSION['delegate'] = get_observer_hash(); $_SESSION['account_id'] = intval($r[0]['channel_account_id']); change_channel($r[0]['channel_id']); $delegation_success = true; } } } // FIXME: check and honour local delegation goaway($dest); } if (local_channel()) { $channel = \App::get_channel(); $token = random_string(); $token_sig = base64url_encode(rsa_sign($token, $channel['channel_prvkey'])); $channel['token'] = $token; $channel['token_sig'] = $token_sig; \Zotlabs\Zot\Verify::create('auth', $channel['channel_id'], $token, $x[0]['hubloc_url']); $target_url = $x[0]['hubloc_callback'] . '/?f=&auth=' . urlencode($channel['channel_address'] . '@' . \App::get_hostname()) . '&sec=' . $token . '&dest=' . urlencode($dest) . '&version=' . ZOT_REVISION; if ($delegate) { $target_url .= '&delegate=' . urlencode($delegate); } logger('mod_magic: redirecting to: ' . $target_url, LOGGER_DEBUG); if ($test) { $ret['success'] = true; $ret['url'] = $target_url; $ret['message'] = 'token ' . $token . ' created for channel ' . $channel['channel_id'] . ' for url ' . $x[0]['hubloc_url'] . EOL; return $ret; } goaway($target_url); } if ($test) { $ret['message'] = 'Not authenticated or invalid arguments to mod_magic' . EOL; return $ret; } goaway($dest); }
/** * @brief HTTP POST entry point for Zot. * * Most access to this endpoint is via the post method. * Here we will pick out the magic auth params which arrive as a get request, * and the only communications to arrive this way. * * Magic Auth * ========== * * So-called "magic auth" takes place by a special exchange. On the site where the "channel to be authenticated" lives (e.g. $mysite), * a redirection is made via $mysite/magic to the zot endpoint of the remote site ($remotesite) with special GET parameters. * * The endpoint is typically https://$remotesite/post - or whatever was specified as the callback url in prior communications * (we will bootstrap an address and fetch a zot info packet if possible where no prior communications exist) * * Five GET parameters are supplied: * * auth => the urlencoded webbie (channel@host.domain) of the channel requesting access * * dest => the desired destination URL (urlencoded) * * sec => a random string which is also stored on $mysite for use during the verification phase. * * version => the zot revision * * delegate => optional urlencoded webbie of a local channel to invoke delegation rights for * * When this packet is received, an "auth-check" zot message is sent to $mysite. * (e.g. if $_GET['auth'] is foobar@podunk.edu, a zot packet is sent to the podunk.edu zot endpoint, which is typically /post) * If no information has been recorded about the requesting identity a zot information packet will be retrieved before * continuing. * * The sender of this packet is an arbitrary/random site channel. The recipients will be a single recipient corresponding * to the guid and guid_sig we have associated with the requesting auth identity * * \code{.json} * { * "type":"auth_check", * "sender":{ * "guid":"kgVFf_...", * "guid_sig":"PT9-TApz...", * "url":"http:\/\/podunk.edu", * "url_sig":"T8Bp7j..." * }, * "recipients":{ * { * "guid":"ZHSqb...", * "guid_sig":"JsAAXi..." * } * } * "callback":"\/post", * "version":1, * "secret":"1eaa661", * "secret_sig":"eKV968b1..." * } * \endcode * * auth_check messages MUST use encapsulated encryption. This message is sent to the origination site, which checks the 'secret' to see * if it is the same as the 'sec' which it passed originally. It also checks the secret_sig which is the secret signed by the * destination channel's private key and base64url encoded. If everything checks out, a json packet is returned: * * \code{.json} * { * "success":1, * "confirm":"q0Ysovd1u...", * "service_class":(optional) * "level":(optional) * } * \endcode * * 'confirm' in this case is the base64url encoded RSA signature of the concatenation of 'secret' with the * base64url encoded whirlpool hash of the requestor's guid and guid_sig; signed with the source channel private key. * This prevents a man-in-the-middle from inserting a rogue success packet. Upon receipt and successful * verification of this packet, the destination site will redirect to the original destination URL and indicate a successful remote login. * Service_class can be used by cooperating sites to provide different access rights based on account rights and subscription plans. It is * a string whose contents are not defined by protocol. Example: "basic" or "gold". * * @param[in,out] App &$a */ function post_init(&$a) { if (array_key_exists('auth', $_REQUEST)) { $ret = array('success' => false, 'message' => ''); logger('mod_zot: auth request received.'); $address = $_REQUEST['auth']; $desturl = $_REQUEST['dest']; $sec = $_REQUEST['sec']; $version = $_REQUEST['version']; $delegate = $_REQUEST['delegate']; $test = x($_REQUEST, 'test') ? intval($_REQUEST['test']) : 0; // They are authenticating ultimately to the site and not to a particular channel. // Any channel will do, providing it's currently active. We just need to have an // identity to attach to the packet we send back. So find one. $c = q("select * from channel where channel_removed = 0 limit 1"); if (!$c) { // nobody here logger('mod_zot: auth: unable to find a response channel'); if ($test) { $ret['message'] .= 'no local channels found.' . EOL; json_return_and_die($ret); } goaway($desturl); } // Try and find a hubloc for the person attempting to auth $x = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash where hubloc_addr = '%s' order by hubloc_id desc", dbesc($address)); if (!$x) { // finger them if they can't be found. $ret = zot_finger($address, null); if ($ret['success']) { $j = json_decode($ret['body'], true); if ($j) { import_xchan($j); } $x = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash where hubloc_addr = '%s' order by hubloc_id desc", dbesc($address)); } } if (!$x) { logger('mod_zot: auth: unable to finger ' . $address); if ($test) { $ret['message'] .= 'no hubloc found for ' . $address . ' and probing failed.' . EOL; json_return_and_die($ret); } goaway($desturl); } foreach ($x as $xx) { logger('mod_zot: auth request received from ' . $xx['hubloc_addr']); // check credentials and access // If they are already authenticated and haven't changed credentials, // we can save an expensive network round trip and improve performance. $remote = remote_channel(); $result = null; $remote_service_class = ''; $remote_level = 0; $remote_hub = $xx['hubloc_url']; $DNT = 0; // Also check that they are coming from the same site as they authenticated with originally. $already_authed = $remote && $xx['hubloc_hash'] == $remote && $xx['hubloc_url'] === $_SESSION['remote_hub'] ? true : false; if ($delegate && $delegate !== $_SESSION['delegate_channel']) { $already_authed = false; } $j = array(); if (!$already_authed) { // Auth packets MUST use ultra top-secret hush-hush mode - e.g. the entire packet is encrypted using the site private key // The actual channel sending the packet ($c[0]) is not important, but this provides a generic zot packet with a sender // which can be verified $p = zot_build_packet($c[0], $type = 'auth_check', array(array('guid' => $xx['hubloc_guid'], 'guid_sig' => $xx['hubloc_guid_sig'])), $xx['hubloc_sitekey'], $sec); if ($test) { $ret['message'] .= 'auth check packet created using sitekey ' . $xx['hubloc_sitekey'] . EOL; $ret['message'] .= 'packet contents: ' . $p . EOL; } $result = zot_zot($xx['hubloc_callback'], $p); if (!$result['success']) { logger('mod_zot: auth_check callback failed.'); if ($test) { $ret['message'] .= 'auth check request to your site returned .' . print_r($result, true) . EOL; continue; } continue; } $j = json_decode($result['body'], true); if (!$j) { logger('mod_zot: auth_check json data malformed.'); if ($test) { $ret['message'] .= 'json malformed: ' . $result['body'] . EOL; continue; } } } if ($test) { $ret['message'] .= 'auth check request returned .' . print_r($j, true) . EOL; } if ($already_authed || $j['success']) { if ($j['success']) { // legit response, but we do need to check that this wasn't answered by a man-in-middle if (!rsa_verify($sec . $xx['xchan_hash'], base64url_decode($j['confirm']), $xx['xchan_pubkey'])) { logger('mod_zot: auth: final confirmation failed.'); if ($test) { $ret['message'] .= 'final confirmation failed. ' . $sec . print_r($j, true) . print_r($xx, true); continue; } continue; } if (array_key_exists('service_class', $j)) { $remote_service_class = $j['service_class']; } if (array_key_exists('level', $j)) { $remote_level = $j['level']; } if (array_key_exists('DNT', $j)) { $DNT = $j['DNT']; } } // everything is good... maybe if (local_channel()) { // tell them to logout if they're logged in locally as anything but the target remote account // in which case just shut up because they don't need to be doing this at all. if ($a->channel['channel_hash'] != $xx['xchan_hash']) { logger('mod_zot: auth: already authenticated locally as somebody else.'); notice(t('Remote authentication blocked. You are logged into this site locally. Please logout and retry.') . EOL); if ($test) { $ret['message'] .= 'already logged in locally with a conflicting identity.' . EOL; continue; } } continue; } // log them in if ($test) { $ret['success'] = true; $ret['message'] .= 'Authentication Success!' . EOL; json_return_and_die($ret); } $delegation_success = false; if ($delegate) { $r = q("select * from channel left join xchan on channel_hash = xchan_hash where xchan_addr = '%s' limit 1", dbesc($delegate)); if ($r && intval($r[0]['channel_id'])) { $allowed = perm_is_allowed($r[0]['channel_id'], $xx['xchan_hash'], 'delegate'); if ($allowed) { $_SESSION['delegate_channel'] = $r[0]['channel_id']; $_SESSION['delegate'] = $xx['xchan_hash']; $_SESSION['account_id'] = intval($r[0]['channel_account_id']); require_once 'include/security.php'; change_channel($r[0]['channel_id']); $delegation_success = true; } } } $_SESSION['authenticated'] = 1; if (!$delegation_success) { $_SESSION['visitor_id'] = $xx['xchan_hash']; $_SESSION['my_url'] = $xx['xchan_url']; $_SESSION['my_address'] = $address; $_SESSION['remote_service_class'] = $remote_service_class; $_SESSION['remote_level'] = $remote_level; $_SESSION['remote_hub'] = $remote_hub; $_SESSION['DNT'] = $DNT; } $arr = array('xchan' => $xx, 'url' => $desturl, 'session' => $_SESSION); call_hooks('magic_auth_success', $arr); $a->set_observer($xx); require_once 'include/security.php'; $a->set_groups(init_groups_visitor($_SESSION['visitor_id'])); info(sprintf(t('Welcome %s. Remote authentication successful.'), $xx['xchan_name'])); logger('mod_zot: auth success from ' . $xx['xchan_addr']); } else { if ($test) { $ret['message'] .= 'auth failure. ' . print_r($_REQUEST, true) . print_r($j, true) . EOL; continue; } logger('mod_zot: magic-auth failure - not authenticated: ' . $xx['xchan_addr']); } if ($test) { $ret['message'] .= 'auth failure fallthrough ' . print_r($_REQUEST, true) . print_r($j, true) . EOL; continue; } } /** * @FIXME we really want to save the return_url in the session before we * visit rmagic. This does however prevent a recursion if you visit * rmagic directly, as it would otherwise send you back here again. * But z_root() probably isn't where you really want to go. */ if (strstr($desturl, z_root() . '/rmagic')) { goaway(z_root()); } if ($test) { json_return_and_die($ret); } goaway($desturl); } }