/** * @brief Create an array representing the important channel information * which would be necessary to create a nomadic identity clone. This includes * most channel resources and connection information with the exception of content. * * @param int $channel_id * Channel_id to export * @param boolean $items * Include channel posts (wall items), default false * * @returns array * See function for details */ function identity_basic_export($channel_id, $items = false) { /* * Red basic channel export */ $ret = array(); // use constants here as otherwise we will have no idea if we can import from a site // with a non-standard platform and version. $ret['compatibility'] = array('project' => PLATFORM_NAME, 'version' => STD_VERSION, 'database' => DB_UPDATE_VERSION, 'server_role' => Zotlabs\Lib\System::get_server_role()); $r = q("select * from channel where channel_id = %d limit 1", intval($channel_id)); if ($r) { translate_channel_perms_outbound($r[0]); $ret['channel'] = $r[0]; $ret['relocate'] = ['channel_address' => $r[0]['channel_address'], 'url' => z_root()]; } $r = q("select * from profile where uid = %d", intval($channel_id)); if ($r) { $ret['profile'] = $r; } $xchans = array(); $r = q("select * from abook where abook_channel = %d ", intval($channel_id)); if ($r) { $ret['abook'] = $r; for ($x = 0; $x < count($ret['abook']); $x++) { $xchans[] = $ret['abook'][$x]['abook_chan']; $abconfig = load_abconfig($channel_id, $ret['abook'][$x]['abook_xchan']); if ($abconfig) { $ret['abook'][$x]['abconfig'] = $abconfig; } translate_abook_perms_outbound($ret['abook'][$x]); } stringify_array_elms($xchans); } if ($xchans) { $r = q("select * from xchan where xchan_hash in ( " . implode(',', $xchans) . " ) "); if ($r) { $ret['xchan'] = $r; } $r = q("select * from hubloc where hubloc_hash in ( " . implode(',', $xchans) . " ) "); if ($r) { $ret['hubloc'] = $r; } } $r = q("select * from `groups` where uid = %d ", intval($channel_id)); if ($r) { $ret['group'] = $r; } $r = q("select * from group_member where uid = %d ", intval($channel_id)); if ($r) { $ret['group_member'] = $r; } $r = q("select * from pconfig where uid = %d", intval($channel_id)); if ($r) { $ret['config'] = $r; } $r = q("select mimetype, content, os_storage from photo where imgscale = 4 and photo_usage = %d and uid = %d limit 1", intval(PHOTO_PROFILE), intval($channel_id)); if ($r) { $ret['photo'] = array('type' => $r[0]['mimetype'], 'data' => $r[0]['os_storage'] ? base64url_encode(file_get_contents($r[0]['content'])) : base64url_encode($r[0]['content'])); } // All other term types will be included in items, if requested. $r = q("select * from term where ttype in (%d,%d) and uid = %d", intval(TERM_SAVEDSEARCH), intval(TERM_THING), intval($channel_id)); if ($r) { $ret['term'] = $r; } // add psuedo-column obj_baseurl to aid in relocations $r = q("select obj.*, '%s' as obj_baseurl from obj where obj_channel = %d", dbesc(z_root()), intval($channel_id)); if ($r) { $ret['obj'] = $r; } $r = q("select * from app where app_channel = %d and app_system = 0", intval($channel_id)); if ($r) { for ($x = 0; $x < count($r); $x++) { $r[$x]['term'] = q("select * from term where otype = %d and oid = %d", intval(TERM_OBJ_APP), intval($r[$x]['id'])); } $ret['app'] = $r; } $r = q("select * from chatroom where cr_uid = %d", intval($channel_id)); if ($r) { $ret['chatroom'] = $r; } $r = q("select * from event where uid = %d", intval($channel_id)); if ($r) { $ret['event'] = $r; } $r = q("select * from item where resource_type = 'event' and uid = %d", intval($channel_id)); if ($r) { $ret['event_item'] = array(); xchan_query($r); $r = fetch_post_tags($r, true); foreach ($r as $rr) { $ret['event_item'][] = encode_item($rr, true); } } $x = menu_list($channel_id); if ($x) { $ret['menu'] = array(); for ($y = 0; $y < count($x); $y++) { $m = menu_fetch($x[$y]['menu_name'], $channel_id, $ret['channel']['channel_hash']); if ($m) { $ret['menu'][] = menu_element($ret['channel'], $m); } } } $addon = array('channel_id' => $channel_id, 'data' => $ret); call_hooks('identity_basic_export', $addon); $ret = $addon['data']; if (!$items) { return $ret; } $r = q("select * from likes where channel_id = %d", intval($channel_id)); if ($r) { $ret['likes'] = $r; } $r = q("select * from conv where uid = %d", intval($channel_id)); if ($r) { for ($x = 0; $x < count($r); $x++) { $r[$x]['subject'] = base64url_decode(str_rot47($r[$x]['subject'])); } $ret['conv'] = $r; } $r = q("select * from mail where mail.uid = %d", intval($channel_id)); if ($r) { $m = array(); foreach ($r as $rr) { xchan_mail_query($rr); $m[] = mail_encode($rr, true); } $ret['mail'] = $m; } /** @warning this may run into memory limits on smaller systems */ /** export three months of posts. If you want to export and import all posts you have to start with * the first year and export/import them in ascending order. * * Don't export linked resource items. we'll have to pull those out separately. */ $r = q("select * from item where item_wall = 1 and item_deleted = 0 and uid = %d and created > %s - INTERVAL %s and resource_type = '' order by created", intval($channel_id), db_utcnow(), db_quoteinterval('3 MONTH')); if ($r) { $ret['item'] = array(); xchan_query($r); $r = fetch_post_tags($r, true); foreach ($r as $rr) { $ret['item'][] = encode_item($rr, true); } } return $ret; }
/** * Send a zot packet to all hubs where this channel is duplicated, refreshing * such things as personal settings, channel permissions, address book updates, etc. * * @param int $uid * @param array $packet (optional) default null * @param boolean $groups_changed (optional) default false */ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { if (get_config('system', 'server_role') === 'basic') { return; } logger('build_sync_packet'); if ($packet) { logger('packet: ' . print_r($packet, true), LOGGER_DATA, LOG_DEBUG); } if (!$uid) { $uid = local_channel(); } if (!$uid) { return; } $r = q("select * from channel where channel_id = %d limit 1", intval($uid)); if (!$r) { return; } $channel = $r[0]; translate_channel_perms_outbound($channel); if ($packet && array_key_exists('abook', $packet) && $packet['abook']) { for ($x = 0; $x < count($packet['abook']); $x++) { translate_abook_perms_outbound($packet['abook'][$x]); } } if (intval($channel['channel_removed'])) { return; } $h = q("select * from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0", dbesc($channel['channel_hash'])); if (!$h) { return; } $synchubs = array(); foreach ($h as $x) { if ($x['hubloc_host'] == App::get_hostname()) { continue; } $y = q("select site_dead from site where site_url = '%s' limit 1", dbesc($x['hubloc_url'])); if (!$y || $y[0]['site_dead'] == 0) { $synchubs[] = $x; } } if (!$synchubs) { return; } $r = q("select xchan_guid, xchan_guid_sig from xchan where xchan_hash = '%s' limit 1", dbesc($channel['channel_hash'])); if (!$r) { return; } $env_recips = array(); $env_recips[] = array('guid' => $r[0]['xchan_guid'], 'guid_sig' => $r[0]['xchan_guid_sig']); $info = $packet ? $packet : array(); $info['type'] = 'channel_sync'; $info['encoding'] = 'red'; // note: not zot, this packet is very platform specific $info['relocate'] = ['channel_address' => $channel['channel_address'], 'url' => z_root()]; if (array_key_exists($uid, App::$config) && array_key_exists('transient', App::$config[$uid])) { $settings = App::$config[$uid]['transient']; if ($settings) { $info['config'] = $settings; } } if ($channel) { $info['channel'] = array(); foreach ($channel as $k => $v) { // filter out any joined tables like xchan if (strpos($k, 'channel_') !== 0) { continue; } // don't pass these elements, they should not be synchronised $disallowed = array('channel_id', 'channel_account_id', 'channel_primary', 'channel_prvkey', 'channel_address', 'channel_deleted', 'channel_removed', 'channel_system'); if (in_array($k, $disallowed)) { continue; } $info['channel'][$k] = $v; } } if ($groups_changed) { $r = q("select hash as collection, visible, deleted, gname as name from groups where uid = %d", intval($uid)); if ($r) { $info['collections'] = $r; } $r = q("select groups.hash as collection, group_member.xchan as member from groups left join group_member on groups.id = group_member.gid where group_member.uid = %d", intval($uid)); if ($r) { $info['collection_members'] = $r; } } $interval = get_config('system', 'delivery_interval') !== false ? intval(get_config('system', 'delivery_interval')) : 2; logger('build_sync_packet: packet: ' . print_r($info, true), LOGGER_DATA, LOG_DEBUG); $total = count($synchubs); foreach ($synchubs as $hub) { $hash = random_string(); $n = zot_build_packet($channel, 'notify', $env_recips, $hub['hubloc_sitekey'], $hash); queue_insert(array('hash' => $hash, 'account_id' => $channel['channel_account_id'], 'channel_id' => $channel['channel_id'], 'posturl' => $hub['hubloc_callback'], 'notify' => $n, 'msg' => json_encode($info))); Zotlabs\Daemon\Master::Summon(array('Deliver', $hash)); $total = $total - 1; if ($interval && $total) { @time_sleep_until(microtime(true) + (double) $interval); } } }