function get() { if (!is_site_admin()) { return; } $o = ''; $r = q("select * from channel where channel_removed = 0"); $sitekey = get_config('system', 'pubkey'); if ($r) { foreach ($r as $rr) { $found = false; $primary_address = ''; $x = zot_get_hublocs($rr['channel_hash']); if ($x) { foreach ($x as $xx) { if ($xx['hubloc_url'] === z_root() && $xx['hubloc_sitekey'] === $sitekey) { $found = true; break; } } if ($found) { $o .= 'Hubloc exists for ' . $rr['channel_name'] . EOL; continue; } } $y = q("select xchan_addr from xchan where xchan_hash = '%s' limit 1", dbesc($rr['channel_hash'])); if ($y) { $primary_address = $y[0]['xchan_addr']; } $hub_address = $rr['channel']['channel_address'] . '@' . \App::get_hostname(); $primary = $hub_address === $primary_address ? 1 : 0; if (!$y) { $primary = 1; } $m = q("delete from hubloc where hubloc_hash = '%s' and hubloc_url = '%s' ", dbesc($rr['channel_hash']), dbesc(z_root())); // Create a verified hub location pointing to this site. $h = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_primary, hubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey, hubloc_network )\n\t\t\t\t\tvalues ( '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s' )", dbesc($rr['channel_guid']), dbesc($rr['channel_guid_sig']), dbesc($rr['channel_hash']), dbesc($rr['channel_address'] . '@' . \App::get_hostname()), intval($primary), dbesc(z_root()), dbesc(base64url_encode(rsa_sign(z_root(), $rr['channel_prvkey']))), dbesc(\App::get_hostname()), dbesc(z_root() . '/post'), dbesc($sitekey), dbesc('zot')); if ($h) { $o . 'local hubloc created for ' . $rr['channel_name'] . EOL; } else { $o .= 'DB update failed for ' . $rr['channel_name'] . EOL; } } return $o; } }
/** * @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; }
function zfinger_init(&$a) { require_once 'include/zot.php'; require_once 'include/crypto.php'; $ret = array('success' => false); $zhash = x($_REQUEST, 'guid_hash') ? $_REQUEST['guid_hash'] : ''; $zguid = x($_REQUEST, 'guid') ? $_REQUEST['guid'] : ''; $zguid_sig = x($_REQUEST, 'guid_sig') ? $_REQUEST['guid_sig'] : ''; $zaddr = x($_REQUEST, 'address') ? $_REQUEST['address'] : ''; $ztarget = x($_REQUEST, 'target') ? $_REQUEST['target'] : ''; $zsig = x($_REQUEST, 'target_sig') ? $_REQUEST['target_sig'] : ''; $zkey = x($_REQUEST, 'key') ? $_REQUEST['key'] : ''; $mindate = x($_REQUEST, 'mindate') ? $_REQUEST['mindate'] : ''; $feed = x($_REQUEST, 'feed') ? intval($_REQUEST['feed']) : 0; if ($ztarget) { if (!$zkey || !$zsig || !rsa_verify($ztarget, base64url_decode($zsig), $zkey)) { logger('zfinger: invalid target signature'); $ret['message'] = t("invalid target signature"); json_return_and_die($ret); } } // allow re-written domains so bob@foo.example.com can provide an address of bob@example.com // The top-level domain also needs to redirect .well-known/zot-info to the sub-domain with a 301 or 308 // TODO: Make 308 work in include/network.php for zot_fetch_url and zot_post_url if ($zaddr && ($s = get_config('system', 'zotinfo_domainrewrite'))) { $arr = explode('^', $s); if (count($arr) == 2) { $zaddr = str_replace($arr[0], $arr[1], $zaddr); } } $r = null; if (strlen($zhash)) { $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash \n\t\t\twhere channel_hash = '%s' limit 1", dbesc($zhash)); } elseif (strlen($zguid) && strlen($zguid_sig)) { $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash \n\t\t\twhere channel_guid = '%s' and channel_guid_sig = '%s' limit 1", dbesc($zguid), dbesc($zguid_sig)); } elseif (strlen($zaddr)) { if (strpos($zaddr, '[system]') === false) { /* normal address lookup */ $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash\n\t\t\t\twhere ( channel_address = '%s' or xchan_addr = '%s' ) limit 1", dbesc($zaddr), dbesc($zaddr)); } else { /** * The special address '[system]' will return a system channel if one has been defined, * Or the first valid channel we find if there are no system channels. * * This is used by magic-auth if we have no prior communications with this site - and * returns an identity on this site which we can use to create a valid hub record so that * we can exchange signed messages. The precise identity is irrelevant. It's the hub * information that we really need at the other end - and this will return it. * */ $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash\n\t\t\t\twhere ( channel_pageflags & %d ) order by channel_id limit 1", intval(PAGE_SYSTEM)); if (!$r) { $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash\n\t\t\t\t\twhere not ( channel_pageflags & %d ) order by channel_id limit 1", intval(PAGE_REMOVED)); } } } else { $ret['message'] = 'Invalid request'; json_return_and_die($ret); } if (!$r) { $ret['message'] = 'Item not found.'; json_return_and_die($ret); } $e = $r[0]; $id = $e['channel_id']; $special_channel = $e['channel_pageflags'] & PAGE_PREMIUM ? true : false; $adult_channel = $e['channel_pageflags'] & PAGE_ADULT ? true : false; $censored = $e['channel_pageflags'] & PAGE_CENSORED ? true : false; $searchable = $e['channel_pageflags'] & PAGE_HIDDEN ? false : true; $deleted = $e['xchan_flags'] & XCHAN_FLAGS_DELETED ? true : false; if ($deleted || $censored) { $searchable = false; } // This is for birthdays and keywords, but must check access permissions $p = q("select * from profile where uid = %d and is_default = 1", intval($e['channel_id'])); $profile = array(); if ($p) { if (!intval($p[0]['publish'])) { $searchable = false; } $profile['description'] = $p[0]['pdesc']; $profile['birthday'] = $p[0]['dob']; if ($profile['birthday'] != '0000-00-00' && ($bd = z_birthday($p[0]['dob'], $e['channel_timezone'])) !== '') { $profile['next_birthday'] = $bd; } if ($age = age($p[0]['dob'], $e['channel_timezone'], '')) { $profile['age'] = $age; } $profile['gender'] = $p[0]['gender']; $profile['marital'] = $p[0]['marital']; $profile['sexual'] = $p[0]['sexual']; $profile['locale'] = $p[0]['locality']; $profile['region'] = $p[0]['region']; $profile['postcode'] = $p[0]['postal_code']; $profile['country'] = $p[0]['country_name']; $profile['about'] = $p[0]['about']; $profile['homepage'] = $p[0]['homepage']; $profile['hometown'] = $p[0]['hometown']; if ($p[0]['keywords']) { $tags = array(); $k = explode(' ', $p[0]['keywords']); if ($k) { foreach ($k as $kk) { if (trim($kk, " \t\n\r\v,")) { $tags[] = trim($kk, " \t\n\r\v,"); } } } if ($tags) { $profile['keywords'] = $tags; } } } $ret['success'] = true; // Communication details $ret['guid'] = $e['xchan_guid']; $ret['guid_sig'] = $e['xchan_guid_sig']; $ret['key'] = $e['xchan_pubkey']; $ret['name'] = $e['xchan_name']; $ret['name_updated'] = $e['xchan_name_date']; $ret['address'] = $e['xchan_addr']; $ret['photo_mimetype'] = $e['xchan_photo_mimetype']; $ret['photo'] = $e['xchan_photo_l']; $ret['photo_updated'] = $e['xchan_photo_date']; $ret['url'] = $e['xchan_url']; $ret['connections_url'] = $e['xchan_connurl'] ? $e['xchan_connurl'] : z_root() . '/poco/' . $e['channel_address']; $ret['target'] = $ztarget; $ret['target_sig'] = $zsig; $ret['searchable'] = $searchable; $ret['adult_content'] = $adult_channel; if ($deleted) { $ret['deleted'] = $deleted; } // premium or other channel desiring some contact with potential followers before connecting. // This is a template - %s will be replaced with the follow_url we discover for the return channel. if ($special_channel) { $ret['connect_url'] = z_root() . '/connect/' . $e['channel_address']; } // This is a template for our follow url, %s will be replaced with a webbie $ret['follow_url'] = z_root() . '/follow?f=&url=%s'; $ztarget_hash = $ztarget && $zsig ? make_xchan_hash($ztarget, $zsig) : ''; $permissions = get_all_perms($e['channel_id'], $ztarget_hash, false); if ($ztarget_hash) { $permissions['connected'] = false; $b = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($ztarget_hash), intval($e['channel_id'])); if ($b) { $permissions['connected'] = true; } } $ret['permissions'] = $ztarget && $zkey ? crypto_encapsulate(json_encode($permissions), $zkey) : $permissions; if ($permissions['view_profile']) { $ret['profile'] = $profile; } // array of (verified) hubs this channel uses $ret['locations'] = array(); $x = zot_get_hublocs($e['channel_hash']); if ($x && count($x)) { foreach ($x as $hub) { if (!($hub['hubloc_flags'] & HUBLOC_FLAGS_UNVERIFIED)) { $ret['locations'][] = array('host' => $hub['hubloc_host'], 'address' => $hub['hubloc_addr'], 'primary' => $hub['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY ? true : false, 'url' => $hub['hubloc_url'], 'url_sig' => $hub['hubloc_url_sig'], 'callback' => $hub['hubloc_callback'], 'sitekey' => $hub['hubloc_sitekey'], 'deleted' => $hub['hubloc_flags'] & HUBLOC_FLAGS_DELETED ? true : false); } } } $ret['site'] = array(); $ret['site']['url'] = z_root(); $ret['site']['url_sig'] = base64url_encode(rsa_sign(z_root(), $e['channel_prvkey'])); $dirmode = get_config('system', 'directory_mode'); if ($dirmode === false || $dirmode == DIRECTORY_MODE_NORMAL) { $ret['site']['directory_mode'] = 'normal'; } if ($dirmode == DIRECTORY_MODE_PRIMARY) { $ret['site']['directory_mode'] = 'primary'; } elseif ($dirmode == DIRECTORY_MODE_SECONDARY) { $ret['site']['directory_mode'] = 'secondary'; } elseif ($dirmode == DIRECTORY_MODE_STANDALONE) { $ret['site']['directory_mode'] = 'standalone'; } if ($dirmode != DIRECTORY_MODE_NORMAL) { $ret['site']['directory_url'] = z_root() . '/dirsearch'; } // hide detailed site information if you're off the grid if ($dirmode != DIRECTORY_MODE_STANDALONE) { $register_policy = intval(get_config('system', 'register_policy')); if ($register_policy == REGISTER_CLOSED) { $ret['site']['register_policy'] = 'closed'; } if ($register_policy == REGISTER_APPROVE) { $ret['site']['register_policy'] = 'approve'; } if ($register_policy == REGISTER_OPEN) { $ret['site']['register_policy'] = 'open'; } $access_policy = intval(get_config('system', 'access_policy')); if ($access_policy == ACCESS_PRIVATE) { $ret['site']['access_policy'] = 'private'; } if ($access_policy == ACCESS_PAID) { $ret['site']['access_policy'] = 'paid'; } if ($access_policy == ACCESS_FREE) { $ret['site']['access_policy'] = 'free'; } if ($access_policy == ACCESS_TIERED) { $ret['site']['access_policy'] = 'tiered'; } $ret['site']['accounts'] = account_total(); require_once 'include/identity.php'; $ret['site']['channels'] = channel_total(); $ret['site']['version'] = RED_PLATFORM . ' ' . RED_VERSION . '[' . DB_UPDATE_VERSION . ']'; $ret['site']['admin'] = get_config('system', 'admin_email'); $visible_plugins = array(); if (is_array($a->plugins) && count($a->plugins)) { $r = q("select * from addon where hidden = 0"); if ($r) { foreach ($r as $rr) { $visible_plugins[] = $rr['name']; } } } $ret['site']['plugins'] = $visible_plugins; $ret['site']['sitehash'] = get_config('system', 'location_hash'); $ret['site']['sitename'] = get_config('system', 'sitename'); $ret['site']['sellpage'] = get_config('system', 'sellpage'); $ret['site']['location'] = get_config('system', 'site_location'); $ret['site']['realm'] = get_directory_realm(); } call_hooks('zot_finger', $ret); json_return_and_die($ret); }
function process_channel_sync_delivery($sender, $arr, $deliveries) { /** @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('channel', $arr) && is_array($arr['channel']) && count($arr['channel'])) { $disallowed = array('channel_id', 'channel_account_id', 'channel_primary', 'channel_prvkey', 'channel_address', 'channel_notifyflags'); $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_flags 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 ($rr['abook_flags'] & ABOOK_FLAG_FEED) { $total_feeds++; } } } $disallowed = array('abook_id', 'abook_account', 'abook_channel'); foreach ($arr['abook'] as $abook) { $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_flags from abook where abook_xchan = '%s' and abook_channel = %d and not ( abook_flags & %d )>0 limit 1", dbesc($abook['abook_xchan']), intval($channel['channel_id']), intval(ABOOK_FLAG_SELF)); if ($r) { contact_remove($channel['channel_id'], $r[0]['abook_id']); if ($total_friends) { $total_friends--; } if ($r[0]['abook_flags'] & ABOOK_FLAG_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 if ($abook['abook_xchan'] && $abook['xchan_address']) { $h = zot_get_hublocs($abook['abook_xchan']); if (!$h) { $f = zot_finger($abook['xchan_address'], $channel); if (!$f['success']) { logger('process_channel_sync_delivery: abook not probe-able' . $abook['xchan_address']); continue; } $j = json_decode($f['body'], true); if (!($j['success'] && $j['guid'])) { logger('process_channel_sync_delivery: probe failed.'); continue; } $x = import_xchan($j); if (!$x['success']) { logger('process_channel_sync_delivery: import 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 && $clean['abook_flags'] & ABOOK_FLAG_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 ($clean['abook_flags'] & ABOOK_FLAG_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'])); } } } } $result[] = array($d['hash'], 'channel sync updated', $channel['channel_name'], ''); } return $result; }