예제 #1
0
파일: zot.php 프로젝트: 23n/hubzilla
/**
 * @brief Takes an associative array of a fetched discovery packet and updates
 *   all internal data structures which need to be updated as a result.
 *
 * @param array $arr => json_decoded discovery packet
 * @param int $ud_flags
 *    Determines whether to create a directory update record if any changes occur, default is UPDATE_FLAGS_UPDATED
 *    $ud_flags = UPDATE_FLAGS_FORCED indicates a forced refresh where we unconditionally create a directory update record
 *      this typically occurs once a month for each channel as part of a scheduled ping to notify the directory
 *      that the channel still exists
 * @param array $ud_arr
 *    If set [typically by update_directory_entry()] indicates a specific update table row and more particularly
 *    contains a particular address (ud_addr) which needs to be updated in that table.
 *
 * @return associative array
 *   * \e boolean \b success boolean true or false
 *   * \e string \b message (optional) error string only if success is false
 */
function import_xchan($arr, $ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null)
{
    call_hooks('import_xchan', $arr);
    $ret = array('success' => false);
    $dirmode = intval(get_config('system', 'directory_mode'));
    $changed = false;
    $what = '';
    if (!(is_array($arr) && array_key_exists('success', $arr) && $arr['success'])) {
        logger('import_xchan: invalid data packet: ' . print_r($arr, true));
        $ret['message'] = t('Invalid data packet');
        return $ret;
    }
    if (!($arr['guid'] && $arr['guid_sig'])) {
        logger('import_xchan: no identity information provided. ' . print_r($arr, true));
        return $ret;
    }
    $xchan_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']);
    $arr['hash'] = $xchan_hash;
    $import_photos = false;
    if (!rsa_verify($arr['guid'], base64url_decode($arr['guid_sig']), $arr['key'])) {
        logger('import_xchan: Unable to verify channel signature for ' . $arr['address']);
        $ret['message'] = t('Unable to verify channel signature');
        return $ret;
    }
    logger('import_xchan: ' . $xchan_hash, LOGGER_DEBUG);
    $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($xchan_hash));
    if (!array_key_exists('connect_url', $arr)) {
        $arr['connect_url'] = '';
    }
    if (strpos($arr['address'], '/') !== false) {
        $arr['address'] = substr($arr['address'], 0, strpos($arr['address'], '/'));
    }
    if ($r) {
        if ($r[0]['xchan_photo_date'] != $arr['photo_updated']) {
            $import_photos = true;
        }
        // if we import an entry from a site that's not ours and either or both of us is off the grid - hide the entry.
        /** @TODO: check if we're the same directory realm, which would mean we are allowed to see it */
        $dirmode = get_config('system', 'directory_mode');
        if (($arr['site']['directory_mode'] === 'standalone' || $dirmode & DIRECTORY_MODE_STANDALONE) && $arr['site']['url'] != z_root()) {
            $arr['searchable'] = false;
        }
        $hidden = 1 - intval($arr['searchable']);
        $hidden_changed = $adult_changed = $deleted_changed = $pubforum_changed = 0;
        if (intval($r[0]['xchan_hidden']) != 1 - intval($arr['searchable'])) {
            $hidden_changed = 1;
        }
        if (intval($r[0]['xchan_selfcensored']) != intval($arr['adult_content'])) {
            $adult_changed = 1;
        }
        if (intval($r[0]['xchan_deleted']) != intval($arr['deleted'])) {
            $deleted_changed = 1;
        }
        if (intval($r[0]['xchan_pubforum']) != intval($arr['public_forum'])) {
            $pubforum_changed = 1;
        }
        if ($r[0]['xchan_name_date'] != $arr['name_updated'] || $r[0]['xchan_connurl'] != $arr['connections_url'] || $r[0]['xchan_addr'] != $arr['address'] || $r[0]['xchan_follow'] != $arr['follow_url'] || $r[0]['xchan_connpage'] != $arr['connect_url'] || $r[0]['xchan_url'] != $arr['url'] || $hidden_changed || $adult_changed || $deleted_changed || $pubforum_changed) {
            $rup = q("update xchan set xchan_name = '%s', xchan_name_date = '%s', xchan_connurl = '%s', xchan_follow = '%s', \n\t\t\t\txchan_connpage = '%s', xchan_hidden = %d, xchan_selfcensored = %d, xchan_deleted = %d, xchan_pubforum = %d, \n\t\t\t\txchan_addr = '%s', xchan_url = '%s' where xchan_hash = '%s'", dbesc($arr['name'] ? $arr['name'] : '-'), dbesc($arr['name_updated']), dbesc($arr['connections_url']), dbesc($arr['follow_url']), dbesc($arr['connect_url']), intval(1 - intval($arr['searchable'])), intval($arr['adult_content']), intval($arr['deleted']), intval($arr['public_forum']), dbesc($arr['address']), dbesc($arr['url']), dbesc($xchan_hash));
            logger('import_xchan: update: existing: ' . print_r($r[0], true), LOGGER_DATA);
            logger('import_xchan: update: new: ' . print_r($arr, true), LOGGER_DATA);
            $what .= 'xchan ';
            $changed = true;
        }
    } else {
        $import_photos = true;
        if (($arr['site']['directory_mode'] === 'standalone' || $dirmode & DIRECTORY_MODE_STANDALONE) && $arr['site']['url'] != z_root()) {
            $arr['searchable'] = false;
        }
        $x = q("insert into xchan ( xchan_hash, xchan_guid, xchan_guid_sig, xchan_pubkey, xchan_photo_mimetype,\n\t\t\t\txchan_photo_l, xchan_addr, xchan_url, xchan_connurl, xchan_follow, xchan_connpage, xchan_name, xchan_network, xchan_photo_date, xchan_name_date, xchan_hidden, xchan_selfcensored, xchan_deleted, xchan_pubforum )\n\t\t\t\tvalues ( '%s', '%s', '%s', '%s' , '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d) ", dbesc($xchan_hash), dbesc($arr['guid']), dbesc($arr['guid_sig']), dbesc($arr['key']), dbesc($arr['photo_mimetype']), dbesc($arr['photo']), dbesc($arr['address']), dbesc($arr['url']), dbesc($arr['connections_url']), dbesc($arr['follow_url']), dbesc($arr['connect_url']), dbesc($arr['name'] ? $arr['name'] : '-'), dbesc('zot'), dbescdate($arr['photo_updated']), dbescdate($arr['name_updated']), intval(1 - intval($arr['searchable'])), intval($arr['adult_content']), intval($arr['deleted']), intval($arr['public_forum']));
        $what .= 'new_xchan';
        $changed = true;
    }
    if ($import_photos) {
        require_once 'include/photo/photo_driver.php';
        // see if this is a channel clone that's hosted locally - which we treat different from other xchans/connections
        $local = q("select channel_account_id, channel_id from channel where channel_hash = '%s' limit 1", dbesc($xchan_hash));
        if ($local) {
            $ph = z_fetch_url($arr['photo'], true);
            if ($ph['success']) {
                $hash = import_channel_photo($ph['body'], $arr['photo_mimetype'], $local[0]['channel_account_id'], $local[0]['channel_id']);
                if ($hash) {
                    // unless proven otherwise
                    $is_default_profile = 1;
                    $profile = q("select is_default from profile where aid = %d and uid = %d limit 1", intval($local[0]['channel_account_id']), intval($local[0]['channel_id']));
                    if ($profile) {
                        if (!intval($profile[0]['is_default'])) {
                            $is_default_profile = 0;
                        }
                    }
                    // If setting for the default profile, unset the profile photo flag from any other photos I own
                    if ($is_default_profile) {
                        q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND resource_id != '%s' AND aid = %d AND uid = %d", intval(PHOTO_NORMAL), intval(PHOTO_PROFILE), dbesc($hash), intval($local[0]['channel_account_id']), intval($local[0]['channel_id']));
                    }
                }
                // reset the names in case they got messed up when we had a bug in this function
                $photos = array(z_root() . '/photo/profile/l/' . $local[0]['channel_id'], z_root() . '/photo/profile/m/' . $local[0]['channel_id'], z_root() . '/photo/profile/s/' . $local[0]['channel_id'], $arr['photo_mimetype'], false);
            }
        } else {
            $photos = import_xchan_photo($arr['photo'], $xchan_hash);
        }
        if ($photos) {
            if ($photos[4]) {
                // importing the photo failed somehow. Leave the photo_date alone so we can try again at a later date.
                // This often happens when somebody joins the matrix with a bad cert.
                $r = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s'\n\t\t\t\t\twhere xchan_hash = '%s'", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($xchan_hash));
            } else {
                $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s'\n\t\t\t\t\twhere xchan_hash = '%s'", dbescdate(datetime_convert('UTC', 'UTC', $arr['photo_updated'])), dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($xchan_hash));
            }
            $what .= 'photo ';
            $changed = true;
        }
    }
    // what we are missing for true hub independence is for any changes in the primary hub to
    // get reflected not only in the hublocs, but also to update the URLs and addr in the appropriate xchan
    $s = sync_locations($arr, $arr);
    if ($s) {
        if ($s['change_message']) {
            $what .= $s['change_message'];
        }
        if ($s['changed']) {
            $changed = $s['changed'];
        }
        if ($s['message']) {
            $ret['message'] .= $s['message'];
        }
    }
    // Which entries in the update table are we interested in updating?
    $address = $ud_arr && $ud_arr['ud_addr'] ? $ud_arr['ud_addr'] : $arr['address'];
    // Are we a directory server of some kind?
    $other_realm = false;
    $realm = get_directory_realm();
    if (array_key_exists('site', $arr) && array_key_exists('realm', $arr['site']) && strpos($arr['site']['realm'], $realm) === false) {
        $other_realm = true;
    }
    if ($dirmode != DIRECTORY_MODE_NORMAL) {
        // We're some kind of directory server. However we can only add directory information
        // if the entry is in the same realm (or is a sub-realm). Sub-realms are denoted by
        // including the parent realm in the name. e.g. 'RED_GLOBAL:foo' would allow an entry to
        // be in directories for the local realm (foo) and also the RED_GLOBAL realm.
        if (array_key_exists('profile', $arr) && is_array($arr['profile']) && !$other_realm) {
            $profile_changed = import_directory_profile($xchan_hash, $arr['profile'], $address, $ud_flags, 1);
            if ($profile_changed) {
                $what .= 'profile ';
                $changed = true;
            }
        } else {
            logger('import_xchan: profile not available - hiding');
            // they may have made it private
            $r = q("delete from xprof where xprof_hash = '%s'", dbesc($xchan_hash));
            $r = q("delete from xtag where xtag_hash = '%s' and xtag_flags = 0", dbesc($xchan_hash));
        }
    }
    if (array_key_exists('site', $arr) && is_array($arr['site'])) {
        $profile_changed = import_site($arr['site'], $arr['key']);
        if ($profile_changed) {
            $what .= 'site ';
            $changed = true;
        }
    }
    if ($changed || $ud_flags == UPDATE_FLAGS_FORCED) {
        $guid = random_string() . '@' . get_app()->get_hostname();
        update_modtime($xchan_hash, $guid, $address, $ud_flags);
        logger('import_xchan: changed: ' . $what, LOGGER_DEBUG);
    } elseif (!$ud_flags) {
        // nothing changed but we still need to update the updates record
        q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d)>0 ", intval(UPDATE_FLAGS_UPDATED), dbesc($address), intval(UPDATE_FLAGS_UPDATED));
    }
    if (!x($ret, 'message')) {
        $ret['success'] = true;
        $ret['hash'] = $xchan_hash;
    }
    logger('import_xchan: result: ' . print_r($ret, true), LOGGER_DATA);
    return $ret;
}
예제 #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');
 }
예제 #3
0
파일: import.php 프로젝트: Mauru/red
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');
}
예제 #4
0
function import_diaspora($data)
{
    $a = get_app();
    $account = $a->get_account();
    if (!$account) {
        return false;
    }
    $address = escape_tags($data['user']['username']);
    if (!$address) {
        notice(t('No username found in import file.') . EOL);
        return false;
    }
    $r = q("select * from channel where channel_address = '%s' limit 1", dbesc($address));
    if ($r) {
        // try at most ten times to generate a unique address.
        $x = 0;
        $found_unique = false;
        do {
            $tmp = $address . mt_rand(1000, 9999);
            $r = q("select * from channel where channel_address = '%s' limit 1", dbesc($tmp));
            if (!$r) {
                $address = $tmp;
                $found_unique = true;
                break;
            }
            $x++;
        } while ($x < 10);
        if (!$found_unique) {
            logger('import_diaspora: duplicate channel address. randomisation failed.');
            notice(t('Unable to create a unique channel address. Import failed.') . EOL);
            return;
        }
    }
    $c = create_identity(array('name' => escape_tags($data['user']['name']), 'nickname' => $address, 'account_id' => $account['account_id'], 'permissions_role' => 'social'));
    if (!$c['success']) {
        return;
    }
    $channel_id = $c['channel']['channel_id'];
    // todo - add auto follow settings, (and strip exif in hubzilla)
    $location = escape_tags($data['user']['profile']['location']);
    if (!$location) {
        $location = '';
    }
    q("update channel set channel_location = '%s' where channel_id = %d", dbesc($location), intval($channel_id));
    if ($data['user']['profile']['nsfw']) {
        // fixme for hubzilla which doesn't use pageflags any more
        q("update channel set channel_pageflags = (channel_pageflags | %d) where channel_id = %d", intval(PAGE_ADULT), intval($channel_id));
    }
    if ($data['user']['profile']['image_url']) {
        $p = z_fetch_url($data['user']['profile']['image_url'], true);
        if ($p['success']) {
            $rawbytes = $p['body'];
            $type = guess_image_type('dummyfile', $p['header']);
            import_channel_photo($rawbytes, $type, $c['channel']['channel_account_id'], $channel_id);
        }
    }
    $gender = escape_tags($data['user']['profile']['gender']);
    $about = diaspora2bb($data['user']['profile']['bio']);
    $publish = intval($data['user']['profile']['searchable']);
    if ($data['user']['profile']['birthday']) {
        $dob = datetime_convert('UTC', 'UTC', $data['user']['profile']['birthday'], 'Y-m-d');
    } else {
        $dob = '0000-00-00';
    }
    // we're relying on the fact that this channel was just created and will only
    // have the default profile currently
    $r = q("update profile set gender = '%s', about = '%s', dob = '%s', publish = %d where uid = %d", dbesc($gender), dbesc($about), dbesc($dob), dbesc($publish), intval($channel_id));
    if ($data['user']['aspects']) {
        foreach ($data['user']['aspects'] as $aspect) {
            group_add($channel_id, escape_tags($aspect['name']), intval($aspect['contacts_visible']));
        }
    }
    // now add connections and send friend requests
    if ($data['user']['contacts']) {
        foreach ($data['user']['contacts'] as $contact) {
            $result = new_contact($channel_id, $contact['person_diaspora_handle'], $c['channel']);
            if ($result['success']) {
                if ($contact['aspects']) {
                    foreach ($contact['aspects'] as $aspect) {
                        group_add_member($channel_id, $aspect['name'], $result['abook']['xchan_hash']);
                    }
                }
            }
        }
    }
    // Then add items - note this can't be done until Diaspora adds guids to exported
    // items and comments
    // This will indirectly perform a refresh_all *and* update the directory
    proc_run('php', 'include/directory.php', $channel_id);
    notice(t('Import completed.') . EOL);
    change_channel($channel_id);
    goaway(z_root() . '/network');
}
예제 #5
0
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');
}
예제 #6
0
파일: zot.php 프로젝트: Mauru/red
/**
 * @function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED)
 *   Takes an associative array of a fetched discovery packet and updates
 *   all internal data structures which need to be updated as a result.
 * 
 * @param array $arr => json_decoded discovery packet
 * @param int $ud_flags
 *    Determines whether to create a directory update record if any changes occur, default is UPDATE_FLAGS_UPDATED
 *    $ud_flags = UPDATE_FLAGS_FORCED indicates a forced refresh where we unconditionally create a directory update record
 *      this typically occurs once a month for each channel as part of a scheduled ping to notify the directory
 *      that the channel still exists
 * @param array $ud_arr
 *    If set [typically by update_directory_entry()] indicates a specific update table row and more particularly 
 *    contains a particular address (ud_addr) which needs to be updated in that table.
 *
 * @returns array =>  'success' (boolean true or false)
 *                    'message' (optional error string only if success is false)
 */
function import_xchan($arr, $ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null)
{
    call_hooks('import_xchan', $arr);
    $ret = array('success' => false);
    $dirmode = intval(get_config('system', 'directory_mode'));
    $changed = false;
    $what = '';
    if (!(is_array($arr) && array_key_exists('success', $arr) && $arr['success'])) {
        logger('import_xchan: invalid data packet: ' . print_r($arr, true));
        $ret['message'] = t('Invalid data packet');
        return $ret;
    }
    if (!($arr['guid'] && $arr['guid_sig'])) {
        logger('import_xchan: no identity information provided. ' . print_r($arr, true));
        return $ret;
    }
    $xchan_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']);
    $import_photos = false;
    if (!rsa_verify($arr['guid'], base64url_decode($arr['guid_sig']), $arr['key'])) {
        logger('import_xchan: Unable to verify channel signature for ' . $arr['address']);
        $ret['message'] = t('Unable to verify channel signature');
        return $ret;
    }
    logger('import_xchan: ' . $xchan_hash, LOGGER_DEBUG);
    $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($xchan_hash));
    if (!array_key_exists('connect_url', $arr)) {
        $arr['connect_url'] = '';
    }
    if (strpos($arr['address'], '/') !== false) {
        $arr['address'] = substr($arr['address'], 0, strpos($arr['address'], '/'));
    }
    if ($r) {
        if ($r[0]['xchan_photo_date'] != $arr['photo_updated']) {
            $import_photos = true;
        }
        // if we import an entry from a site that's not ours and either or both of us is off the grid - hide the entry.
        // TODO: check if we're the same directory realm, which would mean we are allowed to see it
        $dirmode = get_config('system', 'directory_mode');
        if (($arr['site']['directory_mode'] === 'standalone' || $dirmode & DIRECTORY_MODE_STANDALONE) && $arr['site']['url'] != z_root()) {
            $arr['searchable'] = false;
        }
        $hidden = 1 - intval($arr['searchable']);
        // Be careful - XCHAN_FLAGS_HIDDEN should evaluate to 1
        if (($r[0]['xchan_flags'] & XCHAN_FLAGS_HIDDEN) != $hidden) {
            $new_flags = $r[0]['xchan_flags'] ^ XCHAN_FLAGS_HIDDEN;
        } else {
            $new_flags = $r[0]['xchan_flags'];
        }
        $adult = $r[0]['xchan_flags'] & XCHAN_FLAGS_SELFCENSORED ? true : false;
        $adult_changed = intval($adult) != intval($arr['adult_content']) ? true : false;
        if ($adult_changed) {
            $new_flags = $new_flags ^ XCHAN_FLAGS_SELFCENSORED;
        }
        $deleted = $r[0]['xchan_flags'] & XCHAN_FLAGS_DELETED ? true : false;
        $deleted_changed = intval($deleted) != intval($arr['deleted']) ? true : false;
        if ($deleted_changed) {
            $new_flags = $new_flags ^ XCHAN_FLAGS_DELETED;
        }
        if ($r[0]['xchan_name_date'] != $arr['name_updated'] || $r[0]['xchan_connurl'] != $arr['connections_url'] || $r[0]['xchan_flags'] != $new_flags || $r[0]['xchan_addr'] != $arr['address'] || $r[0]['xchan_follow'] != $arr['follow_url'] || $r[0]['xchan_connpage'] != $arr['connect_url'] || $r[0]['xchan_url'] != $arr['url']) {
            $r = q("update xchan set xchan_name = '%s', xchan_name_date = '%s', xchan_connurl = '%s', xchan_follow = '%s', \n\t\t\t\txchan_connpage = '%s', xchan_flags = %d,\n\t\t\t\txchan_addr = '%s', xchan_url = '%s' where xchan_hash = '%s' limit 1", dbesc($arr['name']), dbesc($arr['name_updated']), dbesc($arr['connections_url']), dbesc($arr['follow_url']), dbesc($arr['connect_url']), intval($new_flags), dbesc($arr['address']), dbesc($arr['url']), dbesc($xchan_hash));
            logger('import_xchan: existing: ' . print_r($r[0], true), LOGGER_DATA);
            logger('import_xchan: new: ' . print_r($arr, true), LOGGER_DATA);
            $what .= 'xchan ';
            $changed = true;
        }
    } else {
        $import_photos = true;
        if (($arr['site']['directory_mode'] === 'standalone' || $dirmode & DIRECTORY_MODE_STANDALONE) && $arr['site']['url'] != z_root()) {
            $arr['searchable'] = false;
        }
        $hidden = 1 - intval($arr['searchable']);
        if ($hidden) {
            $new_flags = XCHAN_FLAGS_HIDDEN;
        } else {
            $new_flags = 0;
        }
        if ($arr['adult_content']) {
            $new_flags |= XCHAN_FLAGS_SELFCENSORED;
        }
        if (array_key_exists('deleted', $arr) && $arr['deleted']) {
            $new_flags |= XCHAN_FLAGS_DELETED;
        }
        $x = q("insert into xchan ( xchan_hash, xchan_guid, xchan_guid_sig, xchan_pubkey, xchan_photo_mimetype,\n\t\t\t\txchan_photo_l, xchan_addr, xchan_url, xchan_connurl, xchan_follow, xchan_connpage, xchan_name, xchan_network, xchan_photo_date, xchan_name_date, xchan_flags)\n\t\t\t\tvalues ( '%s', '%s', '%s', '%s' , '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d) ", dbesc($xchan_hash), dbesc($arr['guid']), dbesc($arr['guid_sig']), dbesc($arr['key']), dbesc($arr['photo_mimetype']), dbesc($arr['photo']), dbesc($arr['address']), dbesc($arr['url']), dbesc($arr['connections_url']), dbesc($arr['follow_url']), dbesc($arr['connect_url']), dbesc($arr['name']), dbesc('zot'), dbesc($arr['photo_updated']), dbesc($arr['name_updated']), intval($new_flags));
        $what .= 'new_xchan';
        $changed = true;
    }
    if ($import_photos) {
        require_once 'include/photo/photo_driver.php';
        // see if this is a channel clone that's hosted locally - which we treat different from other xchans/connections
        $local = q("select channel_account_id, channel_id from channel where channel_hash = '%s' limit 1", dbesc($xchan_hash));
        if ($local) {
            $ph = z_fetch_url($arr['photo'], true);
            if ($ph['success']) {
                import_channel_photo($ph['body'], $arr['photo_mimetype'], $local[0]['channel_account_id'], $local[0]['channel_id']);
                // reset the names in case they got messed up when we had a bug in this function
                $photos = array(z_root() . '/photo/profile/l/' . $local[0]['channel_id'], z_root() . '/photo/profile/m/' . $local[0]['channel_id'], z_root() . '/photo/profile/s/' . $local[0]['channel_id'], $arr['photo_mimetype'], false);
            }
        } else {
            $photos = import_profile_photo($arr['photo'], $xchan_hash);
        }
        if ($photos) {
            if ($photos[4]) {
                // importing the photo failed somehow. Leave the photo_date alone so we can try again at a later date.
                // This often happens when somebody joins the matrix with a bad cert.
                $r = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s'\n\t\t\t\t\twhere xchan_hash = '%s' limit 1", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($xchan_hash));
            } else {
                $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s'\n\t\t\t\t\twhere xchan_hash = '%s' limit 1", dbesc(datetime_convert('UTC', 'UTC', $arr['photo_updated'])), dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($xchan_hash));
            }
            $what .= 'photo ';
            $changed = true;
        }
    }
    // what we are missing for true hub independence is for any changes in the primary hub to
    // get reflected not only in the hublocs, but also to update the URLs and addr in the appropriate xchan
    if ($arr['locations']) {
        $xisting = q("select hubloc_id, hubloc_url, hubloc_sitekey from hubloc where hubloc_hash = '%s'", dbesc($xchan_hash));
        // See if a primary is specified
        $has_primary = false;
        foreach ($arr['locations'] as $location) {
            if ($location['primary']) {
                $has_primary = true;
                break;
            }
        }
        foreach ($arr['locations'] as $location) {
            if (!rsa_verify($location['url'], base64url_decode($location['url_sig']), $arr['key'])) {
                logger('import_xchan: Unable to verify site signature for ' . $location['url']);
                $ret['message'] .= sprintf(t('Unable to verify site signature for %s'), $location['url']) . EOL;
                continue;
            }
            // Ensure that they have one primary hub
            if (!$has_primary) {
                $location['primary'] = true;
            }
            for ($x = 0; $x < count($xisting); $x++) {
                if ($xisting[$x]['hubloc_url'] === $location['url'] && $xisting[$x]['hubloc_sitekey'] === $location['sitekey']) {
                    $xisting[$x]['updated'] = true;
                }
            }
            if (!$location['sitekey']) {
                logger('import_xchan: empty hubloc sitekey. ' . print_r($location, true));
                continue;
            }
            // Catch some malformed entries from the past which still exist
            if (strpos($location['address'], '/') !== false) {
                $location['address'] = substr($location['address'], 0, strpos($location['address'], '/'));
            }
            // match as many fields as possible in case anything at all changed.
            $r = q("select * from hubloc where hubloc_hash = '%s' and hubloc_guid = '%s' and hubloc_guid_sig = '%s' and hubloc_url = '%s' and hubloc_url_sig = '%s' and hubloc_host = '%s' and hubloc_addr = '%s' and hubloc_callback = '%s' and hubloc_sitekey = '%s' ", dbesc($xchan_hash), dbesc($arr['guid']), dbesc($arr['guid_sig']), dbesc($location['url']), dbesc($location['url_sig']), dbesc($location['host']), dbesc($location['address']), dbesc($location['callback']), dbesc($location['sitekey']));
            if ($r) {
                logger('import_xchan: hub exists: ' . $location['url']);
                // update connection timestamp if this is the site we're talking to
                if ($location['url'] == $arr['site']['url']) {
                    q("update hubloc set hubloc_connected = '%s', hubloc_updated = '%s' where hubloc_id = %d limit 1", dbesc(datetime_convert()), dbesc(datetime_convert()), intval($r[0]['hubloc_id']));
                }
                if ($r[0]['hubloc_status'] & HUBLOC_OFFLINE) {
                    q("update hubloc set hubloc_status = (hubloc_status ^ %d) where hubloc_id = %d limit 1", intval(HUBLOC_OFFLINE), intval($r[0]['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($r[0]['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($xchan_hash));
                }
                // Remove pure duplicates
                if (count($r) > 1) {
                    for ($h = 1; $h < count($r); $h++) {
                        q("delete from hubloc where hubloc_id = %d limit 1", intval($r[$h]['hubloc_id']));
                    }
                }
                if ($r[0]['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY && !$location['primary'] || !($r[0]['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY) && $location['primary']) {
                    $r = q("update hubloc set hubloc_flags = (hubloc_flags ^ %d), hubloc_updated = '%s' where hubloc_id = %d limit 1", intval(HUBLOC_FLAGS_PRIMARY), dbesc(datetime_convert()), intval($r[0]['hubloc_id']));
                    $what = 'primary_hub ';
                    $changed = true;
                }
                if ($r[0]['hubloc_flags'] & HUBLOC_FLAGS_DELETED && !$location['deleted'] || !($r[0]['hubloc_flags'] & HUBLOC_FLAGS_DELETED) && $location['deleted']) {
                    $r = q("update hubloc set hubloc_flags = (hubloc_flags ^ %d), hubloc_updated = '%s' where hubloc_id = %d limit 1", intval(HUBLOC_FLAGS_DELETED), dbesc(datetime_convert()), intval($r[0]['hubloc_id']));
                    $what = 'delete_hub ';
                    $changed = true;
                }
                continue;
            }
            // new hub claiming to be primary. Make it so.
            if (intval($location['primary'])) {
                $r = q("update hubloc set hubloc_flags = (hubloc_flags ^ %d), hubloc_updated = '%s' where hubloc_hash = '%s' and (hubloc_flags & %d )", intval(HUBLOC_FLAGS_PRIMARY), dbesc(datetime_convert()), dbesc($xchan_hash), intval(HUBLOC_FLAGS_PRIMARY));
            }
            logger('import_xchan: new hub: ' . $location['url']);
            $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_network, hubloc_flags, hubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey, hubloc_updated, hubloc_connected)\n\t\t\t\t\tvalues ( '%s','%s','%s','%s', '%s', %d ,'%s','%s','%s','%s','%s','%s','%s')", dbesc($arr['guid']), dbesc($arr['guid_sig']), dbesc($xchan_hash), dbesc($location['address']), dbesc('zot'), intval(intval($location['primary']) ? HUBLOC_FLAGS_PRIMARY : 0), dbesc($location['url']), dbesc($location['url_sig']), dbesc($location['host']), dbesc($location['callback']), dbesc($location['sitekey']), dbesc(datetime_convert()), dbesc(datetime_convert()));
            $what .= 'newhub ';
            $changed = true;
        }
        // get rid of any hubs we have for this channel which weren't reported.
        // This was needed at one time to resolve complicated cross-site inconsistencies, but can cause sync conflict.
        // currently disabled.
        //		if($xisting) {
        //			foreach($xisting as $x) {
        //				if(! array_key_exists('updated',$x)) {
        //					logger('import_xchan: removing unreferenced hub location ' . $x['hubloc_url']);
        //					$r = q("delete from hubloc where hubloc_id = %d limit 1",
        //						intval($x['hubloc_id'])
        //					);
        //					$what .= 'removed_hub';
        //					$changed = true;
        //				}
        //			}
        //		}
    }
    // Which entries in the update table are we interested in updating?
    $address = $ud_arr && $ud_arr['ud_addr'] ? $ud_arr['ud_addr'] : $arr['address'];
    // Are we a directory server of some kind?
    $other_realm = false;
    $realm = get_directory_realm();
    if (array_key_exists('site', $arr) && array_key_exists('realm', $arr['site']) && strpos($arr['site']['realm'], $realm) === false) {
        $other_realm = true;
    }
    if ($dirmode != DIRECTORY_MODE_NORMAL) {
        // We're some kind of directory server. However we can only add directory information
        // if the entry is in the same realm (or is a sub-realm). Sub-realms are denoted by
        // including the parent realm in the name. e.g. 'RED_GLOBAL:foo' would allow an entry to
        // be in directories for the local realm (foo) and also the RED_GLOBAL realm.
        if (array_key_exists('profile', $arr) && is_array($arr['profile']) && !$other_realm) {
            $profile_changed = import_directory_profile($xchan_hash, $arr['profile'], $address, $ud_flags, 1);
            if ($profile_changed) {
                $what .= 'profile ';
                $changed = true;
            }
        } else {
            logger('import_xchan: profile not available - hiding');
            // they may have made it private
            $r = q("delete from xprof where xprof_hash = '%s' limit 1", dbesc($xchan_hash));
            $r = q("delete from xtag where xtag_hash = '%s' limit 1", dbesc($xchan_hash));
        }
    }
    if (array_key_exists('site', $arr) && is_array($arr['site'])) {
        $profile_changed = import_site($arr['site'], $arr['key']);
        if ($profile_changed) {
            $what .= 'site ';
            $changed = true;
        }
    }
    if ($changed || $ud_flags == UPDATE_FLAGS_FORCED) {
        $guid = random_string() . '@' . get_app()->get_hostname();
        update_modtime($xchan_hash, $guid, $address, $ud_flags);
        logger('import_xchan: changed: ' . $what, LOGGER_DEBUG);
    } elseif (!$ud_flags) {
        // nothing changed but we still need to update the updates record
        q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d) ", intval(UPDATE_FLAGS_UPDATED), dbesc($address), intval(UPDATE_FLAGS_UPDATED));
    }
    if (!x($ret, 'message')) {
        $ret['success'] = true;
        $ret['hash'] = $xchan_hash;
    }
    logger('import_xchan: result: ' . print_r($ret, true), LOGGER_DATA);
    return $ret;
}