Esempio n. 1
0
File: zot.php Progetto: 23n/hubzilla
/**
 * @brief
 *
 * @param array $sender
 * @param array $arr
 * @param array $deliveries
 * @return array
 */
function process_channel_sync_delivery($sender, $arr, $deliveries)
{
    require_once 'include/import.php';
    /** @FIXME this will sync red structures (channel, pconfig and abook). Eventually we need to make this application agnostic. */
    $result = array();
    foreach ($deliveries as $d) {
        $r = q("select * from channel where channel_hash = '%s' limit 1", dbesc($d['hash']));
        if (!$r) {
            $result[] = array($d['hash'], 'not found');
            continue;
        }
        $channel = $r[0];
        $max_friends = service_class_fetch($channel['channel_id'], 'total_channels');
        $max_feeds = account_service_class_fetch($channel['channel_account_id'], 'total_feeds');
        if ($channel['channel_hash'] != $sender['hash']) {
            logger('process_channel_sync_delivery: possible forgery. Sender ' . $sender['hash'] . ' is not ' . $channel['channel_hash']);
            $result[] = array($d['hash'], 'channel mismatch', $channel['channel_name'], '');
            continue;
        }
        if (array_key_exists('config', $arr) && is_array($arr['config']) && count($arr['config'])) {
            foreach ($arr['config'] as $cat => $k) {
                foreach ($arr['config'][$cat] as $k => $v) {
                    set_pconfig($channel['channel_id'], $cat, $k, $v);
                }
            }
        }
        if (array_key_exists('obj', $arr) && $arr['obj']) {
            sync_objs($channel, $arr['obj']);
        }
        if (array_key_exists('likes', $arr) && $arr['likes']) {
            import_likes($channel, $arr['likes']);
        }
        if (array_key_exists('app', $arr) && $arr['app']) {
            sync_apps($channel, $arr['app']);
        }
        if (array_key_exists('chatroom', $arr) && $arr['chatroom']) {
            sync_chatrooms($channel, $arr['chatroom']);
        }
        if (array_key_exists('conv', $arr) && $arr['conv']) {
            import_conv($channel, $arr['conv']);
        }
        if (array_key_exists('mail', $arr) && $arr['mail']) {
            import_mail($channel, $arr['mail']);
        }
        if (array_key_exists('event', $arr) && $arr['event']) {
            sync_events($channel, $arr['event']);
        }
        if (array_key_exists('event_item', $arr) && $arr['event_item']) {
            sync_items($channel, $arr['event_item']);
        }
        if (array_key_exists('item', $arr) && $arr['item']) {
            sync_items($channel, $arr['item']);
        }
        if (array_key_exists('item_id', $arr) && $arr['item_id']) {
            sync_items($channel, $arr['item_id']);
        }
        if (array_key_exists('menu', $arr) && $arr['menu']) {
            sync_menus($channel, $arr['menu']);
        }
        if (array_key_exists('channel', $arr) && is_array($arr['channel']) && count($arr['channel'])) {
            if (array_key_exists('channel_pageflags', $arr['channel']) && intval($arr['channel']['channel_pageflags'])) {
                // These flags cannot be sync'd.
                // remove the bits from the incoming flags.
                // These correspond to PAGE_REMOVED and PAGE_SYSTEM on redmatrix
                if ($arr['channel']['channel_pageflags'] & 0x8000) {
                    $arr['channel']['channel_pageflags'] = $arr['channel']['channel_pageflags'] - 0x8000;
                }
                if ($arr['channel']['channel_pageflags'] & 0x1000) {
                    $arr['channel']['channel_pageflags'] = $arr['channel']['channel_pageflags'] - 0x1000;
                }
            }
            $disallowed = array('channel_id', 'channel_account_id', 'channel_primary', 'channel_prvkey', 'channel_address', 'channel_notifyflags', 'channel_removed', 'channel_deleted', 'channel_system');
            $clean = array();
            foreach ($arr['channel'] as $k => $v) {
                if (in_array($k, $disallowed)) {
                    continue;
                }
                $clean[$k] = $v;
            }
            if (count($clean)) {
                foreach ($clean as $k => $v) {
                    $r = dbq("UPDATE channel set " . dbesc($k) . " = '" . dbesc($v) . "' where channel_id = " . intval($channel['channel_id']));
                }
            }
        }
        if (array_key_exists('abook', $arr) && is_array($arr['abook']) && count($arr['abook'])) {
            $total_friends = 0;
            $total_feeds = 0;
            $r = q("select abook_id, abook_feed from abook where abook_channel = %d", intval($channel['channel_id']));
            if ($r) {
                // don't count yourself
                $total_friends = count($r) > 0 ? count($r) - 1 : 0;
                foreach ($r as $rr) {
                    if (intval($rr['abook_feed'])) {
                        $total_feeds++;
                    }
                }
            }
            $disallowed = array('abook_id', 'abook_account', 'abook_channel', 'abook_rating', 'abook_rating_text');
            foreach ($arr['abook'] as $abook) {
                if (!array_key_exists('abook_blocked', $abook)) {
                    // convert from redmatrix
                    $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;
                }
                $clean = array();
                if ($abook['abook_xchan'] && $abook['entry_deleted']) {
                    logger('process_channel_sync_delivery: removing abook entry for ' . $abook['abook_xchan']);
                    require_once 'include/Contact.php';
                    $r = q("select abook_id, abook_feed from abook where abook_xchan = '%s' and abook_channel = %d and abook_self = 0 limit 1", dbesc($abook['abook_xchan']), intval($channel['channel_id']));
                    if ($r) {
                        contact_remove($channel['channel_id'], $r[0]['abook_id']);
                        if ($total_friends) {
                            $total_friends--;
                        }
                        if (intval($r[0]['abook_feed'])) {
                            $total_feeds--;
                        }
                    }
                    continue;
                }
                // Perform discovery if the referenced xchan hasn't ever been seen on this hub.
                // This relies on the undocumented behaviour that red sites send xchan info with the abook
                // and import_author_xchan will look them up on all federated networks
                if ($abook['abook_xchan'] && $abook['xchan_addr']) {
                    $h = zot_get_hublocs($abook['abook_xchan']);
                    if (!$h) {
                        $xhash = import_author_xchan(encode_item_xchan($abook));
                        if (!$xhash) {
                            logger('process_channel_sync_delivery: import of ' . $abook['xchan_addr'] . ' failed.');
                            continue;
                        }
                    }
                }
                foreach ($abook as $k => $v) {
                    if (in_array($k, $disallowed) || strpos($k, 'abook') !== 0) {
                        continue;
                    }
                    $clean[$k] = $v;
                }
                if (!array_key_exists('abook_xchan', $clean)) {
                    continue;
                }
                $r = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($clean['abook_xchan']), intval($channel['channel_id']));
                // make sure we have an abook entry for this xchan on this system
                if (!$r) {
                    if ($max_friends !== false && $total_friends > $max_friends) {
                        logger('process_channel_sync_delivery: total_channels service class limit exceeded');
                        continue;
                    }
                    if ($max_feeds !== false && intval($clean['abook_feed']) && $total_feeds > $max_feeds) {
                        logger('process_channel_sync_delivery: total_feeds service class limit exceeded');
                        continue;
                    }
                    q("insert into abook ( abook_xchan, abook_channel ) values ('%s', %d ) ", dbesc($clean['abook_xchan']), intval($channel['channel_id']));
                    $total_friends++;
                    if (intval($clean['abook_feed'])) {
                        $total_feeds++;
                    }
                }
                if (count($clean)) {
                    foreach ($clean as $k => $v) {
                        if ($k == 'abook_dob') {
                            $v = dbescdate($v);
                        }
                        $r = dbq("UPDATE abook set " . dbesc($k) . " = '" . dbesc($v) . "' where abook_xchan = '" . dbesc($clean['abook_xchan']) . "' and abook_channel = " . intval($channel['channel_id']));
                    }
                }
            }
        }
        // sync collections (privacy groups) oh joy...
        if (array_key_exists('collections', $arr) && is_array($arr['collections']) && count($arr['collections'])) {
            $x = q("select * from groups where uid = %d", intval($channel['channel_id']));
            foreach ($arr['collections'] as $cl) {
                $found = false;
                if ($x) {
                    foreach ($x as $y) {
                        if ($cl['collection'] == $y['hash']) {
                            $found = true;
                            break;
                        }
                    }
                    if ($found) {
                        if ($y['name'] != $cl['name'] || $y['visible'] != $cl['visible'] || $y['deleted'] != $cl['deleted']) {
                            q("update groups set name = '%s', visible = %d, deleted = %d where hash = '%s' and uid = %d", dbesc($cl['name']), intval($cl['visible']), intval($cl['deleted']), dbesc($cl['hash']), intval($channel['channel_id']));
                        }
                        if (intval($cl['deleted']) && !intval($y['deleted'])) {
                            q("delete from group_member where gid = %d", intval($y['id']));
                        }
                    }
                }
                if (!$found) {
                    $r = q("INSERT INTO `groups` ( hash, uid, visible, deleted, name )\n\t\t\t\t\t\tVALUES( '%s', %d, %d, %d, '%s' ) ", dbesc($cl['collection']), intval($channel['channel_id']), intval($cl['visible']), intval($cl['deleted']), dbesc($cl['name']));
                }
                // now look for any collections locally which weren't in the list we just received.
                // They need to be removed by marking deleted and removing the members.
                // This shouldn't happen except for clones created before this function was written.
                if ($x) {
                    $found_local = false;
                    foreach ($x as $y) {
                        foreach ($arr['collections'] as $cl) {
                            if ($cl['collection'] == $y['hash']) {
                                $found_local = true;
                                break;
                            }
                        }
                        if (!$found_local) {
                            q("delete from group_member where gid = %d", intval($y['id']));
                            q("update groups set deleted = 1 where id = %d and uid = %d", intval($y['id']), intval($channel['channel_id']));
                        }
                    }
                }
            }
            // reload the group list with any updates
            $x = q("select * from groups where uid = %d", intval($channel['channel_id']));
            // now sync the members
            if (array_key_exists('collection_members', $arr) && is_array($arr['collection_members']) && count($arr['collection_members'])) {
                // first sort into groups keyed by the group hash
                $members = array();
                foreach ($arr['collection_members'] as $cm) {
                    if (!array_key_exists($cm['collection'], $members)) {
                        $members[$cm['collection']] = array();
                    }
                    $members[$cm['collection']][] = $cm['member'];
                }
                // our group list is already synchronised
                if ($x) {
                    foreach ($x as $y) {
                        // for each group, loop on members list we just received
                        foreach ($members[$y['hash']] as $member) {
                            $found = false;
                            $z = q("select xchan from group_member where gid = %d and uid = %d and xchan = '%s' limit 1", intval($y['id']), intval($channel['channel_id']), dbesc($member));
                            if ($z) {
                                $found = true;
                            }
                            // if somebody is in the group that wasn't before - add them
                            if (!$found) {
                                q("INSERT INTO `group_member` (`uid`, `gid`, `xchan`)\n\t\t\t\t\t\t\t\t\tVALUES( %d, %d, '%s' ) ", intval($channel['channel_id']), intval($y['id']), dbesc($member));
                            }
                        }
                        // now retrieve a list of members we have on this site
                        $m = q("select xchan from group_member where gid = %d and uid = %d", intval($y['id']), intval($channel['channel_id']));
                        if ($m) {
                            foreach ($m as $mm) {
                                // if the local existing member isn't in the list we just received - remove them
                                if (!in_array($mm['xchan'], $members[$y['hash']])) {
                                    q("delete from group_member where xchan = '%s' and gid = %d and uid = %d", dbesc($mm['xchan']), intval($y['id']), intval($channel['channel_id']));
                                }
                            }
                        }
                    }
                }
            }
        }
        if (array_key_exists('profile', $arr) && is_array($arr['profile']) && count($arr['profile'])) {
            $disallowed = array('id', 'aid', 'uid');
            foreach ($arr['profile'] as $profile) {
                $x = q("select * from profile where profile_guid = '%s' and uid = %d limit 1", dbesc($profile['profile_guid']), intval($channel['channel_id']));
                if (!$x) {
                    q("insert into profile ( profile_guid, aid, uid ) values ('%s', %d, %d)", dbesc($profile['profile_guid']), intval($channel['channel_account_id']), intval($channel['channel_id']));
                    $x = q("select * from profile where profile_guid = '%s' and uid = %d limit 1", dbesc($profile['profile_guid']), intval($channel['channel_id']));
                    if (!$x) {
                        continue;
                    }
                }
                $clean = array();
                foreach ($profile as $k => $v) {
                    if (in_array($k, $disallowed)) {
                        continue;
                    }
                    $clean[$k] = $v;
                    /**
                     * @TODO check if these are allowed, otherwise we'll error
                     * We also need to import local photos if a custom photo is selected
                     */
                }
                if (count($clean)) {
                    foreach ($clean as $k => $v) {
                        $r = dbq("UPDATE profile set `" . dbesc($k) . "` = '" . dbesc($v) . "' where profile_guid = '" . dbesc($profile['profile_guid']) . "' and uid = " . intval($channel['channel_id']));
                    }
                }
            }
        }
        if (array_key_exists('item', $arr) && $arr['item']) {
            sync_items($channel, $arr['item']);
        }
        if (array_key_exists('item_id', $arr) && $arr['item_id']) {
            sync_items($channel, $arr['item_id']);
        }
        $addon = array('channel' => $channel, 'data' => $arr);
        call_hooks('process_channel_sync_delivery', $addon);
        // we should probably do this for all items, but usually we only send one.
        require_once 'include/DReport.php';
        if (array_key_exists('item', $arr) && is_array($arr['item'][0])) {
            $DR = new DReport(z_root(), $d['hash'], $d['hash'], $arr['item'][0]['message_id'], 'channel sync processed');
            $DR->addto_recipient($channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>');
        } else {
            $DR = new DReport(z_root(), $d['hash'], $d['hash'], 'sync packet', 'channel sync delivered');
        }
        $result[] = $DR->get();
    }
    return $result;
}
Esempio n. 2
0
 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');
 }