function ValidateSender() { $hubs = zot_gethub($this->sender, true); if (!$hubs) { /* Have never seen this guid or this guid coming from this location. Check it and register it. */ /* (!!) this will validate the sender. */ $result = zot_register_hub($this->sender); if (!$result['success'] || !($hubs = zot_gethub($this->sender, true))) { $this->response['message'] = 'Hub not available.'; json_return_and_die($this->response); } } foreach ($hubs as $hub) { update_hub_connected($hub, array_key_exists('sitekey', $this->sender) ? $this->sender['sitekey'] : ''); } $this->validated = true; }
/** * @brief Process incoming array of messages. * * Process an incoming array of messages which were obtained via pickup, and * import, update, delete as directed. * * The message types handled here are 'activity' (e.g. posts), 'mail' , * 'profile', 'location' and 'channel_sync'. * * @param array $arr * 'pickup' structure returned from remote site * @param string $sender_url * the url specified by the sender in the initial communication. * We will verify the sender and url in each returned message structure and * also verify that all the messages returned match the site url that we are * currently processing. * * @returns array * suitable for logging remotely, enumerating the processing results of each message/recipient combination * * [0] => \e string $channel_hash * * [1] => \e string $delivery_status * * [2] => \e string $address */ function zot_import($arr, $sender_url) { $data = json_decode($arr['body'], true); if (!$data) { logger('zot_import: empty body'); return array(); } if (array_key_exists('iv', $data)) { $data = json_decode(crypto_unencapsulate($data, get_config('system', 'prvkey')), true); } if (!$data['success']) { if ($data['message']) { logger('remote pickup failed: ' . $data['message']); } return false; } $incoming = $data['pickup']; $return = array(); if (is_array($incoming)) { foreach ($incoming as $i) { if (!is_array($i)) { logger('incoming is not an array'); continue; } $result = null; if (array_key_exists('iv', $i['notify'])) { $i['notify'] = json_decode(crypto_unencapsulate($i['notify'], get_config('system', 'prvkey')), true); } logger('zot_import: notify: ' . print_r($i['notify'], true), LOGGER_DATA); $hub = zot_gethub($i['notify']['sender']); if (!$hub || $hub['hubloc_url'] != $sender_url) { logger('zot_import: potential forgery: wrong site for sender: ' . $sender_url . ' != ' . print_r($i['notify'], true)); continue; } $message_request = array_key_exists('message_id', $i['notify']) ? true : false; if ($message_request) { logger('processing message request'); } $i['notify']['sender']['hash'] = make_xchan_hash($i['notify']['sender']['guid'], $i['notify']['sender']['guid_sig']); $deliveries = null; if (array_key_exists('message', $i) && array_key_exists('type', $i['message']) && $i['message']['type'] === 'rating') { // rating messages are processed only by directory servers logger('Rating received: ' . print_r($arr, true), LOGGER_DATA); $result = process_rating_delivery($i['notify']['sender'], $i['message']); continue; } if (array_key_exists('recipients', $i['notify']) && count($i['notify']['recipients'])) { logger('specific recipients'); $recip_arr = array(); foreach ($i['notify']['recipients'] as $recip) { if (is_array($recip)) { $recip_arr[] = make_xchan_hash($recip['guid'], $recip['guid_sig']); } } $r = false; if ($recip_arr) { stringify_array_elms($recip_arr); $recips = implode(',', $recip_arr); $r = q("select channel_hash as hash from channel where channel_hash in ( " . $recips . " ) \n\t\t\t\t\t\tand channel_removed = 0 "); } if (!$r) { logger('recips: no recipients on this site'); continue; } // It's a specifically targetted post. If we were sent a public_scope hint (likely), // get rid of it so that it doesn't get stored and cause trouble. if ($i && is_array($i) && array_key_exists('message', $i) && is_array($i['message']) && $i['message']['type'] === 'activity' && array_key_exists('public_scope', $i['message'])) { unset($i['message']['public_scope']); } $deliveries = $r; // We found somebody on this site that's in the recipient list. } else { if ($i['message'] && array_key_exists('flags', $i['message']) && in_array('private', $i['message']['flags']) && $i['message']['type'] === 'activity') { if (array_key_exists('public_scope', $i['message']) && $i['message']['public_scope'] === 'public') { // This should not happen but until we can stop it... logger('private message was delivered with no recipients.'); continue; } } logger('public post'); // Public post. look for any site members who are or may be accepting posts from this sender // and who are allowed to see them based on the sender's permissions $deliveries = allowed_public_recips($i); if ($i['message'] && array_key_exists('type', $i['message']) && $i['message']['type'] === 'location') { $sys = get_sys_channel(); $deliveries = array(array('hash' => $sys['xchan_hash'])); } // if the scope is anything but 'public' we're going to store it as private regardless // of the private flag on the post. if ($i['message'] && array_key_exists('public_scope', $i['message']) && $i['message']['public_scope'] !== 'public') { if (!array_key_exists('flags', $i['message'])) { $i['message']['flags'] = array(); } if (!in_array('private', $i['message']['flags'])) { $i['message']['flags'][] = 'private'; } } } // Go through the hash array and remove duplicates. array_unique() won't do this because the array is more than one level. $no_dups = array(); if ($deliveries) { foreach ($deliveries as $d) { if (!in_array($d['hash'], $no_dups)) { $no_dups[] = $d['hash']; } } if ($no_dups) { $deliveries = array(); foreach ($no_dups as $n) { $deliveries[] = array('hash' => $n); } } } if (!$deliveries) { logger('zot_import: no deliveries on this site'); continue; } if ($i['message']) { if ($i['message']['type'] === 'activity') { $arr = get_item_elements($i['message']); $v = validate_item_elements($i['message'], $arr); if (!$v['success']) { logger('Activity rejected: ' . $v['message'] . ' ' . print_r($i['message'], true)); continue; } logger('Activity received: ' . print_r($arr, true), LOGGER_DATA); logger('Activity recipients: ' . print_r($deliveries, true), LOGGER_DATA); $relay = array_key_exists('flags', $i['message']) && in_array('relay', $i['message']['flags']) ? true : false; $result = process_delivery($i['notify']['sender'], $arr, $deliveries, $relay, false, $message_request); } elseif ($i['message']['type'] === 'mail') { $arr = get_mail_elements($i['message']); logger('Mail received: ' . print_r($arr, true), LOGGER_DATA); logger('Mail recipients: ' . print_r($deliveries, true), LOGGER_DATA); $result = process_mail_delivery($i['notify']['sender'], $arr, $deliveries); } elseif ($i['message']['type'] === 'profile') { $arr = get_profile_elements($i['message']); logger('Profile received: ' . print_r($arr, true), LOGGER_DATA); logger('Profile recipients: ' . print_r($deliveries, true), LOGGER_DATA); $result = process_profile_delivery($i['notify']['sender'], $arr, $deliveries); } elseif ($i['message']['type'] === 'channel_sync') { // $arr = get_channelsync_elements($i['message']); $arr = $i['message']; logger('Channel sync received: ' . print_r($arr, true), LOGGER_DATA); logger('Channel sync recipients: ' . print_r($deliveries, true), LOGGER_DATA); $result = process_channel_sync_delivery($i['notify']['sender'], $arr, $deliveries); } elseif ($i['message']['type'] === 'location') { $arr = $i['message']; logger('Location message received: ' . print_r($arr, true), LOGGER_DATA); logger('Location message recipients: ' . print_r($deliveries, true), LOGGER_DATA); $result = process_location_delivery($i['notify']['sender'], $arr, $deliveries); } } if ($result) { $return = array_merge($return, $result); } } } return $return; }
function import_hublocs($channel, $hublocs, $seize) { if ($channel && $hublocs) { foreach ($hublocs as $hubloc) { $hash = make_xchan_hash($hubloc['hubloc_guid'], $hubloc['hubloc_guid_sig']); if ($hubloc['hubloc_network'] === 'zot' && $hash !== $hubloc['hubloc_hash']) { logger('forged hubloc: ' . print_r($hubloc, true)); continue; } if (!array_key_exists('hubloc_primary', $hubloc)) { $hubloc['hubloc_primary'] = $hubloc['hubloc_flags'] & 0x1 ? 1 : 0; $hubloc['hubloc_orphancheck'] = $hubloc['hubloc_flags'] & 0x4 ? 1 : 0; $hubloc['hubloc_error'] = $hubloc['hubloc_status'] & 0x3 ? 1 : 0; $hubloc['hubloc_deleted'] = $hubloc['hubloc_flags'] & 0x1000 ? 1 : 0; } $arr = array('guid' => $hubloc['hubloc_guid'], 'guid_sig' => $hubloc['hubloc_guid_sig'], 'url' => $hubloc['hubloc_url'], 'url_sig' => $hubloc['hubloc_url_sig']); if ($hubloc['hubloc_hash'] === $channel['channel_hash'] && intval($hubloc['hubloc_primary']) && $seize) { $hubloc['hubloc_primary'] = 0; } if (!zot_gethub($arr)) { unset($hubloc['hubloc_id']); dbesc_array($hubloc); $r = dbq("INSERT INTO hubloc (`" . implode("`, `", array_keys($hubloc)) . "`) VALUES ('" . implode("', '", array_values($hubloc)) . "')"); } } } }
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 post_post(&$a) * zot communications and messaging * * Sender HTTP posts to this endpoint ($site/post typically) with 'data' parameter set to json zot message packet. * This packet is optionally encrypted, which we will discover if the json has an 'iv' element. * $contents => array( 'alg' => 'aes256cbc', 'iv' => initialisation vector, 'key' => decryption key, 'data' => encrypted data); * $contents->iv and $contents->key are random strings encrypted with this site's RSA public key and then base64url encoded. * Currently only 'aes256cbc' is used, but this is extensible should that algorithm prove inadequate. * * Once decrypted, one will find the normal json_encoded zot message packet. * * Defined packet types are: notify, purge, refresh, force_refresh, auth_check, ping, and pickup * * Standard packet: (used by notify, purge, refresh, force_refresh, and auth_check) * * { * "type": "notify", * "sender":{ * "guid":"kgVFf_1...", * "guid_sig":"PT9-TApzp...", * "url":"http:\/\/podunk.edu", * "url_sig":"T8Bp7j5...", * }, * "recipients": { optional recipient array }, * "callback":"\/post", * "version":1, * "secret":"1eaa...", * "secret_sig": "df89025470fac8..." * } * * Signature fields are all signed with the sender channel private key and base64url encoded. * Recipients are arrays of guid and guid_sig, which were previously signed with the recipients private * key and base64url encoded and later obtained via channel discovery. Absence of recipients indicates * a public message or visible to all potential listeners on this site. * * "pickup" packet: * The pickup packet is sent in response to a notify packet from another site * * { * "type":"pickup", * "url":"http:\/\/example.com", * "callback":"http:\/\/example.com\/post", * "callback_sig":"teE1_fLI...", * "secret":"1eaa...", * "secret_sig":"O7nB4_..." * } * * In the pickup packet, the sig fields correspond to the respective data element signed with this site's system * private key and then base64url encoded. * The "secret" is the same as the original secret from the notify packet. * * If verification is successful, a json structure is returned * containing a success indicator and an array of type 'pickup'. * Each pickup element contains the original notify request and a message field whose contents are * dependent on the message type * * This JSON array is AES encapsulated using the site public key of the site that sent the initial zot pickup packet. * Using the above example, this would be example.com. * * * { * "success":1, * "pickup":{ * "notify":{ * "type":"notify", * "sender":{ * "guid":"kgVFf_...", * "guid_sig":"PT9-TApz...", * "url":"http:\/\/z.podunk.edu", * "url_sig":"T8Bp7j5D..." * }, * "callback":"\/post", * "version":1, * "secret":"1eaa661..." * }, * "message":{ * "type":"activity", * "message_id":"*****@*****.**", * "message_top":"*****@*****.**", * "message_parent":"*****@*****.**", * "created":"2012-11-20 04:04:16", * "edited":"2012-11-20 04:04:16", * "title":"", * "body":"Hi Nickordo", * "app":"", * "verb":"post", * "object_type":"", * "target_type":"", * "permalink":"", * "location":"", * "longlat":"", * "owner":{ * "name":"Indigo", * "address":"*****@*****.**", * "url":"http:\/\/podunk.edu", * "photo":{ * "mimetype":"image\/jpeg", * "src":"http:\/\/podunk.edu\/photo\/profile\/m\/5" * }, * "guid":"kgVFf_...", * "guid_sig":"PT9-TAp...", * }, * "author":{ * "name":"Indigo", * "address":"*****@*****.**", * "url":"http:\/\/podunk.edu", * "photo":{ * "mimetype":"image\/jpeg", * "src":"http:\/\/podunk.edu\/photo\/profile\/m\/5" * }, * "guid":"kgVFf_...", * "guid_sig":"PT9-TAp..." * } * } * } *} * * Currently defined message types are 'activity', 'mail', 'profile' and 'channel_sync', which each have * different content schemas. * * Ping packet: * A ping packet does not require any parameters except the type. It may or may not be encrypted. * * { * "type": "ping" * } * * On receipt of a ping packet a ping response will be returned: * * { * "success" : 1, * "site" { * "url":"http:\/\/podunk.edu", * "url_sig":"T8Bp7j5...", * "sitekey": "-----BEGIN PUBLIC KEY----- * MIICIjANBgkqhkiG9w0BAQE..." * } * } * * The ping packet can be used to verify that a site has not been re-installed, and to * initiate corrective action if it has. The url_sig is signed with the site private key * and base64url encoded - and this should verify with the enclosed sitekey. Failure to * verify indicates the site is corrupt or otherwise unable to communicate using zot. * This return packet is not otherwise verified, so should be compared with other * results obtained from this site which were verified prior to taking action. For instance * if you have one verified result with this signature and key, and other records for this * url which have different signatures and keys, it indicates that the site was re-installed * and corrective action may commence (remove or mark invalid any entries with different * signatures). * If you have no records which match this url_sig and key - no corrective action should * be taken as this packet may have been returned by an imposter. * */ function post_post(&$a) { $encrypted_packet = false; $ret = array('success' => false); $data = json_decode($_REQUEST['data'], true); /** * Many message packets will arrive encrypted. The existence of an 'iv' element * tells us we need to unencapsulate the AES-256-CBC content using the site private key */ if (array_key_exists('iv', $data)) { $encrypted_packet = true; $data = crypto_unencapsulate($data, get_config('system', 'prvkey')); logger('mod_zot: decrypt1: ' . $data, LOGGER_DATA); $data = json_decode($data, true); } if (!$data) { // possible Bleichenbacher's attack, just treat it as a // message we have no handler for. It should fail a bit // further along with "no hub". Our public key is public // knowledge. There's no reason why anybody should get the // encryption wrong unless they're fishing or hacking. If // they're developing and made a goof, this can be discovered // in the logs of the destination site. If they're fishing or // hacking, the bottom line is we can't verify their hub. // That's all we're going to tell them. $data = array('type' => 'bogus'); } $msgtype = array_key_exists('type', $data) ? $data['type'] : ''; if ($msgtype === 'ping') { // Useful to get a health check on a remote site. // This will let us know if any important communication details // that we may have stored are no longer valid, regardless of xchan details. logger('POST: got ping send pong now back: ' . z_root(), LOGGER_DEBUG); $ret['success'] = true; $ret['site'] = array(); $ret['site']['url'] = z_root(); $ret['site']['url_sig'] = base64url_encode(rsa_sign(z_root(), get_config('system', 'prvkey'))); $ret['site']['sitekey'] = get_config('system', 'pubkey'); json_return_and_die($ret); } if ($msgtype === 'pickup') { /** * The 'pickup' message arrives with a tracking ID which is associated with a particular outq_hash * First verify that that the returned signatures verify, then check that we have an outbound queue item * with the correct hash. * If everything verifies, find any/all outbound messages in the queue for this hubloc and send them back * */ if (!$data['secret'] || !$data['secret_sig']) { $ret['message'] = 'no verification signature'; logger('mod_zot: pickup: ' . $ret['message'], LOGGER_DEBUG); json_return_and_die($ret); } $r = q("select distinct hubloc_sitekey from hubloc where hubloc_url = '%s' and hubloc_callback = '%s' and hubloc_sitekey != '' group by hubloc_sitekey ", dbesc($data['url']), dbesc($data['callback'])); if (!$r) { $ret['message'] = 'site not found'; logger('mod_zot: pickup: ' . $ret['message']); json_return_and_die($ret); } foreach ($r as $hubsite) { // verify the url_sig // If the server was re-installed at some point, there could be multiple hubs with the same url and callback. // Only one will have a valid key. $forgery = true; $secret_fail = true; $sitekey = $hubsite['hubloc_sitekey']; logger('mod_zot: Checking sitekey: ' . $sitekey, LOGGER_DATA); if (rsa_verify($data['callback'], base64url_decode($data['callback_sig']), $sitekey)) { $forgery = false; } if (rsa_verify($data['secret'], base64url_decode($data['secret_sig']), $sitekey)) { $secret_fail = false; } if (!$forgery && !$secret_fail) { break; } } if ($forgery) { $ret['message'] = 'possible site forgery'; logger('mod_zot: pickup: ' . $ret['message']); json_return_and_die($ret); } if ($secret_fail) { $ret['message'] = 'secret validation failed'; logger('mod_zot: pickup: ' . $ret['message']); json_return_and_die($ret); } /** * If we made it to here, the signatures verify, but we still don't know if the tracking ID is valid. * It wouldn't be an error if the tracking ID isn't found, because we may have sent this particular * queue item with another pickup (after the tracking ID for the other pickup was verified). */ $r = q("select outq_posturl from outq where outq_hash = '%s' and outq_posturl = '%s' limit 1", dbesc($data['secret']), dbesc($data['callback'])); if (!$r) { $ret['message'] = 'nothing to pick up'; logger('mod_zot: pickup: ' . $ret['message']); json_return_and_die($ret); } /** * Everything is good if we made it here, so find all messages that are going to this location * and send them all. */ $r = q("select * from outq where outq_posturl = '%s'", dbesc($data['callback'])); if ($r) { logger('mod_zot: succesful pickup message received from ' . $data['callback'] . ' ' . count($r) . ' message(s) picked up', LOGGER_DEBUG); $ret['success'] = true; $ret['pickup'] = array(); foreach ($r as $rr) { $ret['pickup'][] = array('notify' => json_decode($rr['outq_notify'], true), 'message' => json_decode($rr['outq_msg'], true)); $x = q("delete from outq where outq_hash = '%s' limit 1", dbesc($rr['outq_hash'])); } } $encrypted = crypto_encapsulate(json_encode($ret), $sitekey); json_return_and_die($encrypted); /** pickup: end */ } /** * All other message types require us to verify the sender. This is a generic check, so we * will do it once here and bail if anything goes wrong. */ if (array_key_exists('sender', $data)) { $sender = $data['sender']; } /** Check if the sender is already verified here */ $hub = zot_gethub($sender); if (!$hub) { /** Have never seen this guid or this guid coming from this location. Check it and register it. */ // (!!) this will validate the sender $result = zot_register_hub($sender); if (!$result['success'] || !($hub = zot_gethub($sender))) { $ret['message'] = 'Hub not available.'; logger('mod_zot: no hub'); json_return_and_die($ret); } } // Update our DB to show when we last communicated successfully with this hub // This will allow us to prune dead hubs from using up resources $r = q("update hubloc set hubloc_connected = '%s' where hubloc_id = %d limit 1", dbesc(datetime_convert()), intval($hub['hubloc_id'])); // a dead hub came back to life - reset any tombstones we might have if ($hub['hubloc_status'] & HUBLOC_OFFLINE) { q("update hubloc set hubloc_status = (hubloc_status ^ %d) where hubloc_id = %d limit 1", intval(HUBLOC_OFFLINE), intval($hub['hubloc_id'])); if ($r[0]['hubloc_flags'] & HUBLOC_FLAGS_ORPHANCHECK) { q("update hubloc set hubloc_flags = (hubloc_flags ^ %d) where hubloc_id = %d limit 1", intval(HUBLOC_FLAGS_ORPHANCHECK), intval($hub['hubloc_id'])); } q("update xchan set xchan_flags = (xchan_flags ^ %d) where (xchan_flags & %d) and xchan_hash = '%s' limit 1", intval(XCHAN_FLAGS_ORPHAN), intval(XCHAN_FLAGS_ORPHAN), dbesc($hub['hubloc_hash'])); } /** * This hub has now been proven to be valid. * Any hub with the same URL and a different sitekey cannot be valid. * Get rid of them (mark them deleted). There's a good chance they were re-installs. * */ q("update hubloc set hubloc_flags = ( hubloc_flags | %d ) where hubloc_url = '%s' and hubloc_sitekey != '%s' ", intval(HUBLOC_FLAGS_DELETED), dbesc($hub['hubloc_url']), dbesc($hub['hubloc_sitekey'])); // TODO: check which hub is primary and take action if mismatched if (array_key_exists('recipients', $data)) { $recipients = $data['recipients']; } if ($msgtype === 'auth_check') { /** * Requestor visits /magic/?dest=somewhere on their own site with a browser * magic redirects them to $destsite/post [with auth args....] * $destsite sends an auth_check packet to originator site * The auth_check packet is handled here by the originator's site * - the browser session is still waiting * inside $destsite/post for everything to verify * If everything checks out we'll return a token to $destsite * and then $destsite will verify the token, authenticate the browser * session and then redirect to the original destination. * If authentication fails, the redirection to the original destination * will still take place but without authentication. */ logger('mod_zot: auth_check', LOGGER_DEBUG); if (!$encrypted_packet) { logger('mod_zot: auth_check packet was not encrypted.'); $ret['message'] .= 'no packet encryption' . EOL; json_return_and_die($ret); } $arr = $data['sender']; $sender_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']); // garbage collect any old unused notifications q("delete from verify where type = 'auth' and created < UTC_TIMESTAMP() - INTERVAL 10 MINUTE"); $y = q("select xchan_pubkey from xchan where xchan_hash = '%s' limit 1", dbesc($sender_hash)); // We created a unique hash in mod/magic.php when we invoked remote auth, and stored it in // the verify table. It is now coming back to us as 'secret' and is signed by a channel at the other end. // First verify their signature. We will have obtained a zot-info packet from them as part of the sender // verification. if (!$y || !rsa_verify($data['secret'], base64url_decode($data['secret_sig']), $y[0]['xchan_pubkey'])) { logger('mod_zot: auth_check: sender not found or secret_sig invalid.'); $ret['message'] .= 'sender not found or sig invalid ' . print_r($y, true) . EOL; json_return_and_die($ret); } // There should be exactly one recipient, the original auth requestor $ret['message'] .= 'recipients ' . print_r($recipients, true) . EOL; if ($data['recipients']) { $arr = $data['recipients'][0]; $recip_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']); $c = q("select channel_id, channel_account_id, channel_prvkey from channel where channel_hash = '%s' limit 1", dbesc($recip_hash)); if (!$c) { logger('mod_zot: auth_check: recipient channel not found.'); $ret['message'] .= 'recipient not found.' . EOL; json_return_and_die($ret); } $confirm = base64url_encode(rsa_sign($data['secret'] . $recip_hash, $c[0]['channel_prvkey'])); // This additionally checks for forged sites since we already stored the expected result in meta // and we've already verified that this is them via zot_gethub() and that their key signed our token $z = q("select id from verify where channel = %d and type = 'auth' and token = '%s' and meta = '%s' limit 1", intval($c[0]['channel_id']), dbesc($data['secret']), dbesc($data['sender']['url'])); if (!$z) { logger('mod_zot: auth_check: verification key not found.'); $ret['message'] .= 'verification key not found' . EOL; json_return_and_die($ret); } $r = q("delete from verify where id = %d limit 1", intval($z[0]['id'])); $u = q("select account_service_class from account where account_id = %d limit 1", intval($c[0]['channel_account_id'])); logger('mod_zot: auth_check: success', LOGGER_DEBUG); $ret['success'] = true; $ret['confirm'] = $confirm; if ($u && $u[0]['account_service_class']) { $ret['service_class'] = $u[0]['account_service_class']; } // Set "do not track" flag if this site or this channel's profile is restricted if (intval(get_config('system', 'block_public'))) { $ret['DNT'] = true; } if (!perm_is_allowed($c[0]['channel_id'], '', 'view_profile')) { $ret['DNT'] = true; } if (get_pconfig($c[0]['channel_id'], 'system', 'do_not_track')) { $ret['DNT'] = true; } json_return_and_die($ret); } json_return_and_die($ret); } if ($msgtype === 'purge') { if ($recipients) { // basically this means "unfriend" foreach ($recipients as $recip) { $r = q("select channel.*,xchan.* from channel \n\t\t\t\t\tleft join xchan on channel_hash = xchan_hash\n\t\t\t\t\twhere channel_guid = '%s' and channel_guid_sig = '%s' limit 1", dbesc($recip['guid']), dbesc($recip['guid_sig'])); if ($r) { $r = q("select abook_id from abook where uid = %d and abook_xchan = '%s' limit 1", intval($r[0]['channel_id']), dbesc(make_xchan_hash($sender['guid'], $sender['guid_sig']))); if ($r) { contact_remove($r[0]['channel_id'], $r[0]['abook_id']); } } } } else { // Unfriend everybody - basically this means the channel has committed suicide $arr = $data['sender']; $sender_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']); require_once 'include/Contact.php'; remove_all_xchan_resources($sender_hash); $ret['success'] = true; json_return_and_die($ret); } } if ($msgtype === 'refresh' || $msgtype === 'force_refresh') { // remote channel info (such as permissions or photo or something) // has been updated. Grab a fresh copy and sync it. // The difference between refresh and force_refresh is that // force_refresh unconditionally creates a directory update record, // even if no changes were detected upon processing. if ($recipients) { // This would be a permissions update, typically for one connection foreach ($recipients as $recip) { $r = q("select channel.*,xchan.* from channel \n\t\t\t\t\tleft join xchan on channel_hash = xchan_hash\n\t\t\t\t\twhere channel_guid = '%s' and channel_guid_sig = '%s' limit 1", dbesc($recip['guid']), dbesc($recip['guid_sig'])); $x = zot_refresh(array('xchan_guid' => $sender['guid'], 'xchan_guid_sig' => $sender['guid_sig'], 'hubloc_url' => $sender['url']), $r[0], $msgtype === 'force_refresh' ? true : false); } } else { // system wide refresh $x = zot_refresh(array('xchan_guid' => $sender['guid'], 'xchan_guid_sig' => $sender['guid_sig'], 'hubloc_url' => $sender['url']), null, $msgtype === 'force_refresh' ? true : false); } $ret['success'] = true; json_return_and_die($ret); } if ($msgtype === 'notify') { $async = get_config('system', 'queued_fetch'); if ($async) { // add to receive queue // qreceive_add($data); } else { $x = zot_fetch($data); $ret['delivery_report'] = $x; } $ret['success'] = true; json_return_and_die($ret); } // catchall json_return_and_die($ret); }
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'); }