public static function run($argc, $argv)
 {
     /**
      * Cron Weekly
      * 
      * Actions in the following block are executed once per day only on Sunday (once per week).
      *
      */
     call_hooks('cron_weekly', datetime_convert());
     z_check_cert();
     require_once 'include/hubloc.php';
     prune_hub_reinstalls();
     mark_orphan_hubsxchans();
     // get rid of really old poco records
     q("delete from xlink where xlink_updated < %s - INTERVAL %s and xlink_static = 0 ", db_utcnow(), db_quoteinterval('14 DAY'));
     $dirmode = intval(get_config('system', 'directory_mode'));
     if ($dirmode === DIRECTORY_MODE_SECONDARY || $dirmode === DIRECTORY_MODE_PRIMARY) {
         logger('regdir: ' . print_r(z_fetch_url(get_directory_primary() . '/regdir?f=&url=' . urlencode(z_root()) . '&realm=' . urlencode(get_directory_realm())), true));
     }
     // Check for dead sites
     Master::Summon(array('Checksites'));
     // update searchable doc indexes
     Master::Summon(array('Importdoc'));
     /**
      * End Cron Weekly
      */
 }
Example #2
0
function sync_directories($dirmode)
{
    if ($dirmode == DIRECTORY_MODE_STANDALONE || $dirmode == DIRECTORY_MODE_NORMAL) {
        return;
    }
    $realm = get_directory_realm();
    if ($realm == DIRECTORY_REALM) {
        $r = q("select * from site where (site_flags & %d) and site_url != '%s' and ( site_realm = '%s' or site_realm = '') ", intval(DIRECTORY_MODE_PRIMARY | DIRECTORY_MODE_SECONDARY), dbesc(z_root()), dbesc($realm));
    } else {
        $r = q("select * from site where (site_flags & %d) and site_url != '%s' and site_realm like '%s' ", intval(DIRECTORY_MODE_PRIMARY | DIRECTORY_MODE_SECONDARY), dbesc(z_root()), dbesc(protect_sprintf('%' . $realm . '%')));
    }
    // If there are no directory servers, setup the fallback master
    // FIXME - what to do if we're in a different realm?
    if (!$r && z_root() != DIRECTORY_FALLBACK_MASTER) {
        $r = array('site_url' => DIRECTORY_FALLBACK_MASTER, 'site_flags' => DIRECTORY_MODE_PRIMARY, 'site_update' => NULL_DATE, 'site_directory' => DIRECTORY_FALLBACK_MASTER . '/dirsearch', 'site_realm' => DIRECTORY_REALM);
        $x = q("insert into site ( site_url, site_flags, site_update, site_directory, site_realm )\n\t\t\tvalues ( '%s', %d', '%s', '%s', '%s' ) ", dbesc($r[0]['site_url']), intval($r[0]['site_flags']), dbesc($r[0]['site_update']), dbesc($r[0]['site_directory']), dbesc($r[0]['site_realm']));
        $r = q("select * from site where (site_flags & %d) and site_url != '%s'", intval(DIRECTORY_MODE_PRIMARY | DIRECTORY_MODE_SECONDARY), dbesc(z_root()));
    }
    if (!$r) {
        return;
    }
    foreach ($r as $rr) {
        if (!$rr['site_directory']) {
            continue;
        }
        logger('sync directories: ' . $rr['site_directory']);
        // for brand new directory servers, only load the last couple of days. Everything before that will be repeats.
        $syncdate = $rr['site_sync'] === NULL_DATE ? datetime_convert('UTC', 'UTC', 'now - 2 days') : $rr['site_sync'];
        $x = z_fetch_url($rr['site_directory'] . '?f=&sync=' . urlencode($syncdate));
        if (!$x['success']) {
            continue;
        }
        $j = json_decode($x['body'], true);
        if (!$j['transactions'] || !is_array($j['transactions'])) {
            continue;
        }
        q("update site set site_sync = '%s' where site_url = '%s' limit 1", dbesc(datetime_convert()), dbesc($rr['site_url']));
        logger('sync_directories: ' . $rr['site_url'] . ': ' . print_r($j, true), LOGGER_DATA);
        if (count($j['transactions'])) {
            foreach ($j['transactions'] as $t) {
                $r = q("select * from updates where ud_guid = '%s' limit 1", dbesc($t['transaction_id']));
                if ($r) {
                    continue;
                }
                $ud_flags = 0;
                if (is_array($t['flags']) && in_array('deleted', $t['flags'])) {
                    $ud_flags |= UPDATE_FLAGS_DELETED;
                }
                if (is_array($t['flags']) && in_array('forced', $t['flags'])) {
                    $ud_flags |= UPDATE_FLAGS_FORCED;
                }
                $z = q("insert into updates ( ud_hash, ud_guid, ud_date, ud_flags, ud_addr )\n\t\t\t\t\tvalues ( '%s', '%s', '%s', %d, '%s' ) ", dbesc($t['hash']), dbesc($t['transaction_id']), dbesc($t['timestamp']), intval($ud_flags), dbesc($t['address']));
            }
        }
    }
}
Example #3
0
 function init()
 {
     $start = $_REQUEST['start'] ? intval($_REQUEST['start']) : 0;
     $limit = intval($_REQUEST['limit']) ? intval($_REQUEST['limit']) : 30;
     $order = $_REQUEST['order'] ? $_REQUEST['order'] : 'random';
     $open = $_REQUEST['open'] ? intval($_REQUEST['open']) : false;
     $sql_order = " order by site_url ";
     $rand = db_getfunc('rand');
     if ($order == 'random') {
         $sql_order = " order by {$rand} ";
     }
     $sql_limit = " LIMIT {$limit} OFFSET {$start} ";
     $sql_extra = "";
     if ($open) {
         $sql_extra = " and site_register = " . intval(REGISTER_OPEN) . " ";
     }
     $realm = get_directory_realm();
     if ($realm == DIRECTORY_REALM) {
         $sql_extra .= " and ( site_realm = '" . dbesc($realm) . "' or site_realm = '') ";
     } else {
         $sql_extra .= " and site_realm = '" . dbesc($realm) . "' ";
     }
     $result = array('success' => false);
     $r = q("select count(site_url) as total from site where site_type = %d {$sql_extra} ", intval(SITE_TYPE_ZOT));
     if ($r) {
         $result['total'] = intval($r[0]['total']);
     }
     $result['start'] = $start;
     $result['limit'] = $limit;
     $r = q("select * from site where site_type = %d {$sql_extra} {$sql_order} {$sql_limit}", intval(SITE_TYPE_ZOT));
     $result['results'] = 0;
     $result['entries'] = array();
     if ($r) {
         $result['success'] = true;
         $result['results'] = count($r);
         foreach ($r as $rr) {
             $result['entries'][] = array('url' => $rr['site_url']);
         }
     }
     echo json_encode($result);
     killme();
 }
Example #4
0
File: zot.php Project: 23n/hubzilla
function zotinfo($arr)
{
    $ret = array('success' => false);
    $zhash = x($arr, 'guid_hash') ? $arr['guid_hash'] : '';
    $zguid = x($arr, 'guid') ? $arr['guid'] : '';
    $zguid_sig = x($arr, 'guid_sig') ? $arr['guid_sig'] : '';
    $zaddr = x($arr, 'address') ? $arr['address'] : '';
    $ztarget = x($arr, 'target') ? $arr['target'] : '';
    $zsig = x($arr, 'target_sig') ? $arr['target_sig'] : '';
    $zkey = x($arr, 'key') ? $arr['key'] : '';
    $mindate = x($arr, 'mindate') ? $arr['mindate'] : '';
    $feed = x($arr, 'feed') ? intval($arr['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");
            return $ret;
        }
    }
    $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_system = 1 order by channel_id limit 1");
            if (!$r) {
                $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash\n\t\t\t\t\twhere channel_removed = 0 order by channel_id limit 1");
            }
        }
    } else {
        $ret['message'] = 'Invalid request';
        return $ret;
    }
    if (!$r) {
        $ret['message'] = 'Item not found.';
        return $ret;
    }
    $e = $r[0];
    $id = $e['channel_id'];
    $sys_channel = intval($e['channel_system']) ? true : false;
    $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 = intval($e['xchan_deleted']) ? true : false;
    if ($deleted || $censored || $sys_channel) {
        $searchable = false;
    }
    $public_forum = false;
    $role = get_pconfig($e['channel_id'], 'system', 'permissions_role');
    if ($role === 'forum' || $role === 'repository') {
        $public_forum = true;
    } else {
        // check if it has characteristics of a public forum based on custom permissions.
        $t = q("select abook_my_perms from abook where abook_channel = %d and abook_self = 1 limit 1", intval($e['channel_id']));
        if ($t && ($t[0]['abook_my_perms'] & PERMS_W_TAGWALL && !($t[0]['abook_my_perms'] & PERMS_W_STREAM))) {
            $public_forum = true;
        }
    }
    //  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;
    $ret['public_forum'] = $public_forum;
    if ($deleted) {
        $ret['deleted'] = $deleted;
    }
    if (intval($e['channel_removed'])) {
        $ret['deleted_locally'] = true;
    }
    // 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
    $x = zot_encode_locations($e);
    if ($x) {
        $ret['locations'] = $x;
    }
    $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'] = PLATFORM_NAME . ' ' . RED_VERSION . '[' . DB_UPDATE_VERSION . ']';
        $ret['site']['admin'] = get_config('system', 'admin_email');
        $a = get_app();
        $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();
        $ret['site']['project'] = PLATFORM_NAME;
    }
    check_zotinfo($e, $x, $ret);
    call_hooks('zot_finger', $ret);
    return $ret;
}
Example #5
0
/**
 * @brief Checks the directory mode of this hub.
 *
 * Checks the directory mode of this hub to see if it is some form of directory server. If it is,
 * get the directory realm of this hub. Fetch a list of all other directory servers in this realm and request
 * a directory sync packet. This will contain both directory updates and new ratings. Store these all in the DB. 
 * In the case of updates, we will query each of them asynchronously from a poller task. Ratings are stored 
 * directly if the rater's signature matches.
 *
 * @param int $dirmode;
 */
function sync_directories($dirmode)
{
    if ($dirmode == DIRECTORY_MODE_STANDALONE || $dirmode == DIRECTORY_MODE_NORMAL) {
        return;
    }
    $realm = get_directory_realm();
    if ($realm == DIRECTORY_REALM) {
        $r = q("select * from site where (site_flags & %d) > 0 and site_url != '%s' and site_type = %d and ( site_realm = '%s' or site_realm = '') ", intval(DIRECTORY_MODE_PRIMARY | DIRECTORY_MODE_SECONDARY), dbesc(z_root()), intval(SITE_TYPE_ZOT), dbesc($realm));
    } else {
        $r = q("select * from site where (site_flags & %d) > 0 and site_url != '%s' and site_realm like '%s' and site_type = %d ", intval(DIRECTORY_MODE_PRIMARY | DIRECTORY_MODE_SECONDARY), dbesc(z_root()), dbesc(protect_sprintf('%' . $realm . '%')), intval(SITE_TYPE_ZOT));
    }
    // If there are no directory servers, setup the fallback master
    /** @FIXME What to do if we're in a different realm? */
    if (!$r && z_root() != DIRECTORY_FALLBACK_MASTER) {
        $r = array();
        $r[] = array('site_url' => DIRECTORY_FALLBACK_MASTER, 'site_flags' => DIRECTORY_MODE_PRIMARY, 'site_update' => NULL_DATE, 'site_directory' => DIRECTORY_FALLBACK_MASTER . '/dirsearch', 'site_realm' => DIRECTORY_REALM, 'site_valid' => 1);
        $x = q("insert into site ( site_url, site_flags, site_update, site_directory, site_realm, site_valid )\n\t\t\tvalues ( '%s', %d, '%s', '%s', '%s', %d ) ", dbesc($r[0]['site_url']), intval($r[0]['site_flags']), dbesc($r[0]['site_update']), dbesc($r[0]['site_directory']), dbesc($r[0]['site_realm']), intval($r[0]['site_valid']));
        $r = q("select * from site where site_flags in (%d, %d) and site_url != '%s' and site_type = %d ", intval(DIRECTORY_MODE_PRIMARY), intval(DIRECTORY_MODE_SECONDARY), dbesc(z_root()), intval(SITE_TYPE_ZOT));
    }
    if (!$r) {
        return;
    }
    foreach ($r as $rr) {
        if (!$rr['site_directory']) {
            continue;
        }
        logger('sync directories: ' . $rr['site_directory']);
        // for brand new directory servers, only load the last couple of days.
        // It will take about a month for a new directory to obtain the full current repertoire of channels.
        /** @FIXME Go back and pick up earlier ratings if this is a new directory server. These do not get refreshed. */
        $token = get_config('system', 'realm_token');
        $syncdate = $rr['site_sync'] === NULL_DATE ? datetime_convert('UTC', 'UTC', 'now - 2 days') : $rr['site_sync'];
        $x = z_fetch_url($rr['site_directory'] . '?f=&sync=' . urlencode($syncdate) . ($token ? '&t=' . $token : ''));
        if (!$x['success']) {
            continue;
        }
        $j = json_decode($x['body'], true);
        if (!$j['transactions'] || $j['ratings']) {
            continue;
        }
        q("update site set site_sync = '%s' where site_url = '%s'", dbesc(datetime_convert()), dbesc($rr['site_url']));
        logger('sync_directories: ' . $rr['site_url'] . ': ' . print_r($j, true), LOGGER_DATA);
        if (is_array($j['transactions']) && count($j['transactions'])) {
            foreach ($j['transactions'] as $t) {
                $r = q("select * from updates where ud_guid = '%s' limit 1", dbesc($t['transaction_id']));
                if ($r) {
                    continue;
                }
                $ud_flags = 0;
                if (is_array($t['flags']) && in_array('deleted', $t['flags'])) {
                    $ud_flags |= UPDATE_FLAGS_DELETED;
                }
                if (is_array($t['flags']) && in_array('forced', $t['flags'])) {
                    $ud_flags |= UPDATE_FLAGS_FORCED;
                }
                $z = q("insert into updates ( ud_hash, ud_guid, ud_date, ud_flags, ud_addr )\n\t\t\t\t\tvalues ( '%s', '%s', '%s', %d, '%s' ) ", dbesc($t['hash']), dbesc($t['transaction_id']), dbesc($t['timestamp']), intval($ud_flags), dbesc($t['address']));
            }
        }
        if (is_array($j['ratings']) && count($j['ratings'])) {
            foreach ($j['ratings'] as $rr) {
                $x = q("select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' and xlink_static = 1", dbesc($rr['channel']), dbesc($rr['target']));
                if ($x && $x[0]['xlink_updated'] >= $rr['edited']) {
                    continue;
                }
                // Ratings are signed by the rater. We need to verify before we can accept it.
                /** @TODO Queue or defer if the xchan is not yet present on our site */
                $y = q("select xchan_pubkey from xchan where xchan_hash = '%s' limit 1", dbesc($rr['channel']));
                if (!$y) {
                    logger('key unavailable on this site for ' . $rr['channel']);
                    continue;
                }
                if (!rsa_verify($rr['target'] . '.' . $rr['rating'] . '.' . $rr['rating_text'], base64url_decode($rr['signature']), $y[0]['xchan_pubkey'])) {
                    logger('failed to verify rating');
                    continue;
                }
                if ($x) {
                    $z = q("update xlink set xlink_rating = %d, xlink_rating_text = '%s', xlink_sig = '%s', xlink_updated = '%s' where xlink_id = %d", intval($rr['rating']), dbesc($rr['rating_text']), dbesc($rr['signature']), dbesc(datetime_convert()), intval($x[0]['xlink_id']));
                    logger('rating updated');
                } else {
                    $z = q("insert into xlink ( xlink_xchan, xlink_link, xlink_rating, xlink_rating_text, xlink_sig, xlink_updated, xlink_static ) values( '%s', '%s', %d, '%s', '%s', '%s', 1 ) ", dbesc($rr['channel']), dbesc($rr['target']), intval($rr['rating']), dbesc($rr['rating_text']), dbesc($rr['signature']), dbesc(datetime_convert()));
                    logger('rating created');
                }
            }
        }
    }
}
Example #6
0
 function init()
 {
     $result = array('success' => false);
     $url = $_REQUEST['url'];
     $access_token = $_REQUEST['t'];
     $valid = 0;
     // we probably don't need the realm as we will find out in the probe.
     // What we may want to die is throw an error if you're trying to register in a different realm
     // so this configuration issue can be discovered.
     $realm = $_REQUEST['realm'];
     if (!$realm) {
         $realm = DIRECTORY_REALM;
     }
     if ($realm === DIRECTORY_REALM) {
         $valid = 1;
     } else {
         $token = get_config('system', 'realm_token');
         if ($token && $access_token != $token) {
             $result['message'] = 'This realm requires an access token';
             return;
         }
         $valid = 1;
     }
     $dirmode = intval(get_config('system', 'directory_mode'));
     if ($dirmode == DIRECTORY_MODE_NORMAL) {
         $ret['message'] = t('This site is not a directory server');
         json_return_and_die($ret);
     }
     $m = null;
     if ($url) {
         $m = parse_url($url);
         if (!$m || !@dns_get_record($m['host'], DNS_A + DNS_CNAME + DNS_PTR) && !filter_var($m['host'], FILTER_VALIDATE_IP)) {
             $result['message'] = 'unparseable url';
             json_return_and_die($result);
         }
         $j = \Zotlabs\Zot\Finger::run('[system]@' . $m['host']);
         if ($j['success'] && $j['guid']) {
             $x = import_xchan($j);
             if ($x['success']) {
                 $result['success'] = true;
             }
         }
         if (!$result['success']) {
             $valid = 0;
         }
         q("update site set site_valid = %d where site_url = '%s' limit 1", intval($valid), strtolower($url));
         json_return_and_die($result);
     } else {
         // We can put this in the sql without the condition after 31 august 2015 assuming
         // most directory servers will have updated by then
         // This just makes sure it happens if I forget
         $sql_extra = datetime_convert() > datetime_convert('UTC', 'UTC', '2015-08-31') ? ' and site_valid = 1 ' : '';
         if ($dirmode == DIRECTORY_MODE_STANDALONE) {
             $r = array(array('site_url' => z_root()));
         } else {
             $r = q("select site_url from site where site_flags in ( 1, 2 ) and site_realm = '%s' and site_type = %d {$sql_extra} ", dbesc(get_directory_realm()), intval(SITE_TYPE_ZOT));
         }
         if ($r) {
             $result['success'] = true;
             $result['directories'] = array();
             foreach ($r as $rr) {
                 $result['directories'][] = $rr['site_url'];
             }
             json_return_and_die($result);
         }
     }
     json_return_and_die($result);
 }
Example #7
0
/**
 * @brief
 *
 * @param array $arr
 * @param string $pubkey
 * @return boolean true if updated or inserted
 */
function import_site($arr, $pubkey)
{
    if (!is_array($arr) || !$arr['url'] || !$arr['url_sig']) {
        return false;
    }
    if (!rsa_verify($arr['url'], base64url_decode($arr['url_sig']), $pubkey)) {
        logger('import_site: bad url_sig');
        return false;
    }
    $update = false;
    $exists = false;
    $r = q("select * from site where site_url = '%s' limit 1", dbesc($arr['url']));
    if ($r) {
        $exists = true;
        $siterecord = $r[0];
    }
    $site_directory = 0;
    if ($arr['directory_mode'] == 'normal') {
        $site_directory = DIRECTORY_MODE_NORMAL;
    }
    if ($arr['directory_mode'] == 'primary') {
        $site_directory = DIRECTORY_MODE_PRIMARY;
    }
    if ($arr['directory_mode'] == 'secondary') {
        $site_directory = DIRECTORY_MODE_SECONDARY;
    }
    if ($arr['directory_mode'] == 'standalone') {
        $site_directory = DIRECTORY_MODE_STANDALONE;
    }
    $register_policy = 0;
    if ($arr['register_policy'] == 'closed') {
        $register_policy = REGISTER_CLOSED;
    }
    if ($arr['register_policy'] == 'open') {
        $register_policy = REGISTER_OPEN;
    }
    if ($arr['register_policy'] == 'approve') {
        $register_policy = REGISTER_APPROVE;
    }
    $access_policy = 0;
    if (array_key_exists('access_policy', $arr)) {
        if ($arr['access_policy'] === 'private') {
            $access_policy = ACCESS_PRIVATE;
        }
        if ($arr['access_policy'] === 'paid') {
            $access_policy = ACCESS_PAID;
        }
        if ($arr['access_policy'] === 'free') {
            $access_policy = ACCESS_FREE;
        }
        if ($arr['access_policy'] === 'tiered') {
            $access_policy = ACCESS_TIERED;
        }
    }
    // don't let insecure sites register as public hubs
    if (strpos($arr['url'], 'https://') === false) {
        $access_policy = ACCESS_PRIVATE;
    }
    if ($access_policy != ACCESS_PRIVATE) {
        $x = z_fetch_url($arr['url'] . '/siteinfo/json');
        if (!$x['success']) {
            $access_policy = ACCESS_PRIVATE;
        }
    }
    $directory_url = htmlspecialchars($arr['directory_url'], ENT_COMPAT, 'UTF-8', false);
    $url = htmlspecialchars(strtolower($arr['url']), ENT_COMPAT, 'UTF-8', false);
    $sellpage = htmlspecialchars($arr['sellpage'], ENT_COMPAT, 'UTF-8', false);
    $site_location = htmlspecialchars($arr['location'], ENT_COMPAT, 'UTF-8', false);
    $site_realm = htmlspecialchars($arr['realm'], ENT_COMPAT, 'UTF-8', false);
    // You can have one and only one primary directory per realm.
    // Downgrade any others claiming to be primary. As they have
    // flubbed up this badly already, don't let them be directory servers at all.
    if ($site_directory === DIRECTORY_MODE_PRIMARY && $site_realm === get_directory_realm() && $arr['url'] != get_directory_primary()) {
        $site_directory = DIRECTORY_MODE_NORMAL;
    }
    if ($exists) {
        if ($siterecord['site_flags'] != $site_directory || $siterecord['site_access'] != $access_policy || $siterecord['site_directory'] != $directory_url || $siterecord['site_sellpage'] != $sellpage || $siterecord['site_location'] != $site_location || $siterecord['site_register'] != $register_policy || $siterecord['site_realm'] != $site_realm) {
            $update = true;
            //			logger('import_site: input: ' . print_r($arr,true));
            //			logger('import_site: stored: ' . print_r($siterecord,true));
            $r = q("update site set site_location = '%s', site_flags = %d, site_access = %d, site_directory = '%s', site_register = %d, site_update = '%s', site_sellpage = '%s', site_realm = '%s'\n\t\t\t\twhere site_url = '%s'", dbesc($site_location), intval($site_directory), intval($access_policy), dbesc($directory_url), intval($register_policy), dbesc(datetime_convert()), dbesc($sellpage), dbesc($site_realm), dbesc($url));
            if (!$r) {
                logger('import_site: update failed. ' . print_r($arr, true));
            }
        } else {
            // update the timestamp to indicate we communicated with this site
            q("update site set site_update = '%s' where site_url = '%s'", dbesc(datetime_convert()), dbesc($url));
        }
    } else {
        $update = true;
        $r = q("insert into site ( site_location, site_url, site_access, site_flags, site_update, site_directory, site_register, site_sellpage, site_realm )\n\t\t\tvalues ( '%s', '%s', %d, %d, '%s', '%s', %d, '%s', '%s' )", dbesc($site_location), dbesc($url), intval($access_policy), intval($site_directory), dbesc(datetime_convert()), dbesc($directory_url), intval($register_policy), dbesc($sellpage), dbesc($site_realm));
        if (!$r) {
            logger('import_site: record create failed. ' . print_r($arr, true));
        }
    }
    return $update;
}
Example #8
0
function list_public_sites()
{
    $rand = db_getfunc('rand');
    $realm = get_directory_realm();
    if ($realm == DIRECTORY_REALM) {
        $r = q("select * from site where site_access != 0 and site_register !=0 and ( site_realm = '%s' or site_realm = '') and site_type = %d order by {$rand}", dbesc($realm), intval(SITE_TYPE_ZOT));
    } else {
        $r = q("select * from site where site_access != 0 and site_register !=0 and site_realm = '%s' and site_type = %d order by {$rand}", dbesc($realm), intval(SITE_TYPE_ZOT));
    }
    $ret = array('success' => false);
    if ($r) {
        $ret['success'] = true;
        $ret['sites'] = array();
        $insecure = array();
        foreach ($r as $rr) {
            if ($rr['site_access'] == ACCESS_FREE) {
                $access = 'free';
            } elseif ($rr['site_access'] == ACCESS_PAID) {
                $access = 'paid';
            } elseif ($rr['site_access'] == ACCESS_TIERED) {
                $access = 'tiered';
            } else {
                $access = 'private';
            }
            if ($rr['site_register'] == REGISTER_OPEN) {
                $register = 'open';
            } elseif ($rr['site_register'] == REGISTER_APPROVE) {
                $register = 'approve';
            } else {
                $register = 'closed';
            }
            if (strpos($rr['site_url'], 'https://') !== false) {
                $ret['sites'][] = array('url' => $rr['site_url'], 'access' => $access, 'register' => $register, 'sellpage' => $rr['site_sellpage'], 'location' => $rr['site_location'], 'project' => $rr['site_project']);
            } else {
                $insecure[] = array('url' => $rr['site_url'], 'access' => $access, 'register' => $register, 'sellpage' => $rr['site_sellpage'], 'location' => $rr['site_location'], 'project' => $rr['site_project']);
            }
        }
        if ($insecure) {
            $ret['sites'] = array_merge($ret['sites'], $insecure);
        }
    }
    return $ret;
}
Example #9
0
 /**
  * @brief Admin page site.
  *
  * @param  App $a
  * @return string
  */
 function admin_page_site(&$a)
 {
     /* Installed langs */
     $lang_choices = array();
     $langs = glob('view/*/hstrings.php');
     if (is_array($langs) && count($langs)) {
         if (!in_array('view/en/hstrings.php', $langs)) {
             $langs[] = 'view/en/';
         }
         asort($langs);
         foreach ($langs as $l) {
             $t = explode("/", $l);
             $lang_choices[$t[1]] = $t[1];
         }
     }
     /* Installed themes */
     $theme_choices_mobile["---"] = t("Default");
     $theme_choices = array();
     $files = glob('view/theme/*');
     if ($files) {
         foreach ($files as $file) {
             $vars = '';
             $f = basename($file);
             if (file_exists($file . '/library')) {
                 continue;
             }
             if (file_exists($file . '/mobile')) {
                 $vars = t('mobile');
             }
             if (file_exists($file . '/experimental')) {
                 $vars .= t('experimental');
             }
             if (file_exists($file . '/unsupported')) {
                 $vars .= t('unsupported');
             }
             if ($vars) {
                 $theme_choices[$f] = $f . ' (' . $vars . ')';
                 $theme_choices_mobile[$f] = $f . ' (' . $vars . ')';
             } else {
                 $theme_choices[$f] = $f;
                 $theme_choices_mobile[$f] = $f;
             }
         }
     }
     $dir_choices = null;
     $dirmode = get_config('system', 'directory_mode');
     $realm = get_directory_realm();
     // directory server should not be set or settable unless we are a directory client
     if ($dirmode == DIRECTORY_MODE_NORMAL) {
         $x = q("select site_url from site where site_flags in (%d,%d) and site_realm = '%s'", intval(DIRECTORY_MODE_SECONDARY), intval(DIRECTORY_MODE_PRIMARY), dbesc($realm));
         if ($x) {
             $dir_choices = array();
             foreach ($x as $xx) {
                 $dir_choices[$xx['site_url']] = $xx['site_url'];
             }
         }
     }
     /* Banner */
     $banner = get_config('system', 'banner');
     if ($banner === false) {
         $banner = get_config('system', 'sitename');
     }
     $banner = htmlspecialchars($banner);
     /* Admin Info */
     $admininfo = get_config('system', 'admininfo');
     /* Register policy */
     $register_choices = array(REGISTER_CLOSED => t("No"), REGISTER_APPROVE => t("Yes - with approval"), REGISTER_OPEN => t("Yes"));
     /* Acess policy */
     $access_choices = array(ACCESS_PRIVATE => t("My site is not a public server"), ACCESS_PAID => t("My site has paid access only"), ACCESS_FREE => t("My site has free access only"), ACCESS_TIERED => t("My site offers free accounts with optional paid upgrades"));
     //	$ssl_choices = array(
     //		SSL_POLICY_NONE     => t("No SSL policy, links will track page SSL state"),
     //		SSL_POLICY_FULL     => t("Force all links to use SSL")
     //	);
     $discover_tab = get_config('system', 'disable_discover_tab');
     // $disable public streams by default
     if ($discover_tab === false) {
         $discover_tab = 1;
     }
     // now invert the logic for the setting.
     $discover_tab = 1 - $discover_tab;
     $homelogin = get_config('system', 'login_on_homepage');
     $enable_context_help = get_config('system', 'enable_context_help');
     $t = get_markup_template("admin_site.tpl");
     return replace_macros($t, array('$title' => t('Administration'), '$page' => t('Site'), '$submit' => t('Submit'), '$registration' => t('Registration'), '$upload' => t('File upload'), '$corporate' => t('Policies'), '$advanced' => t('Advanced'), '$baseurl' => z_root(), '$sitename' => array('sitename', t("Site name"), htmlspecialchars(get_config('system', 'sitename'), ENT_QUOTES, 'UTF-8'), ''), '$banner' => array('banner', t("Banner/Logo"), $banner, ""), '$admininfo' => array('admininfo', t("Administrator Information"), $admininfo, t("Contact information for site administrators.  Displayed on siteinfo page.  BBCode can be used here")), '$language' => array('language', t("System language"), get_config('system', 'language'), "", $lang_choices), '$theme' => array('theme', t("System theme"), get_config('system', 'theme'), t("Default system theme - may be over-ridden by user profiles - <a href='#' id='cnftheme'>change theme settings</a>"), $theme_choices), '$theme_mobile' => array('theme_mobile', t("Mobile system theme"), get_config('system', 'mobile_theme'), t("Theme for mobile devices"), $theme_choices_mobile), '$feed_contacts' => array('feed_contacts', t('Allow Feeds as Connections'), get_config('system', 'feed_contacts'), t('(Heavy system resource usage)')), '$maximagesize' => array('maximagesize', t("Maximum image size"), intval(get_config('system', 'maximagesize')), t("Maximum size in bytes of uploaded images. Default is 0, which means no limits.")), '$register_policy' => array('register_policy', t("Does this site allow new member registration?"), get_config('system', 'register_policy'), "", $register_choices), '$invite_only' => array('invite_only', t("Invitation only"), get_config('system', 'invitation_only'), t("Only allow new member registrations with an invitation code. Above register policy must be set to Yes.")), '$access_policy' => array('access_policy', t("Which best describes the types of account offered by this hub?"), get_config('system', 'access_policy'), "This is displayed on the public server site list.", $access_choices), '$register_text' => array('register_text', t("Register text"), htmlspecialchars(get_config('system', 'register_text'), ENT_QUOTES, 'UTF-8'), t("Will be displayed prominently on the registration page.")), '$frontpage' => array('frontpage', t("Site homepage to show visitors (default: login box)"), get_config('system', 'frontpage'), t("example: 'public' to show public stream, 'page/sys/home' to show a system webpage called 'home' or 'include:home.html' to include a file.")), '$mirror_frontpage' => array('mirror_frontpage', t("Preserve site homepage URL"), get_config('system', 'mirror_frontpage'), t('Present the site homepage in a frame at the original location instead of redirecting')), '$abandon_days' => array('abandon_days', t('Accounts abandoned after x days'), get_config('system', 'account_abandon_days'), t('Will not waste system resources polling external sites for abandonded accounts. Enter 0 for no time limit.')), '$allowed_sites' => array('allowed_sites', t("Allowed friend domains"), get_config('system', 'allowed_sites'), t("Comma separated list of domains which are allowed to establish friendships with this site. Wildcards are accepted. Empty to allow any domains")), '$allowed_email' => array('allowed_email', t("Allowed email domains"), get_config('system', 'allowed_email'), t("Comma separated list of domains which are allowed in email addresses for registrations to this site. Wildcards are accepted. Empty to allow any domains")), '$not_allowed_email' => array('not_allowed_email', t("Not allowed email domains"), get_config('system', 'not_allowed_email'), t("Comma separated list of domains which are not allowed in email addresses for registrations to this site. Wildcards are accepted. Empty to allow any domains, unless allowed domains have been defined.")), '$verify_email' => array('verify_email', t("Verify Email Addresses"), get_config('system', 'verify_email'), t("Check to verify email addresses used in account registration (recommended).")), '$force_publish' => array('publish_all', t("Force publish"), get_config('system', 'publish_all'), t("Check to force all profiles on this site to be listed in the site directory.")), '$disable_discover_tab' => array('disable_discover_tab', t('Import Public Streams'), $discover_tab, t('Import and allow access to public content pulled from other sites. Warning: this content is unmoderated.')), '$login_on_homepage' => array('login_on_homepage', t("Login on Homepage"), intval($homelogin) || $homelogin === false ? 1 : '', t("Present a login box to visitors on the home page if no other content has been configured.")), '$enable_context_help' => array('enable_context_help', t("Enable context help"), intval($enable_context_help) === 1 || $enable_context_help === false ? 1 : 0, t("Display contextual help for the current page when the help button is pressed.")), '$directory_server' => $dir_choices ? array('directory_server', t("Directory Server URL"), get_config('system', 'directory_server'), t("Default directory server"), $dir_choices) : null, '$proxyuser' => array('proxyuser', t("Proxy user"), get_config('system', 'proxyuser'), ""), '$proxy' => array('proxy', t("Proxy URL"), get_config('system', 'proxy'), ""), '$timeout' => array('timeout', t("Network timeout"), x(get_config('system', 'curl_timeout')) ? get_config('system', 'curl_timeout') : 60, t("Value is in seconds. Set to 0 for unlimited (not recommended).")), '$delivery_interval' => array('delivery_interval', t("Delivery interval"), x(get_config('system', 'delivery_interval')) ? get_config('system', 'delivery_interval') : 2, t("Delay background delivery processes by this many seconds to reduce system load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 for large dedicated servers.")), '$delivery_batch_count' => array('delivery_batch_count', t('Deliveries per process'), x(get_config('system', 'delivery_batch_count')) ? get_config('system', 'delivery_batch_count') : 1, t("Number of deliveries to attempt in a single operating system process. Adjust if necessary to tune system performance. Recommend: 1-5.")), '$poll_interval' => array('poll_interval', t("Poll interval"), x(get_config('system', 'poll_interval')) ? get_config('system', 'poll_interval') : 2, t("Delay background polling processes by this many seconds to reduce system load. If 0, use delivery interval.")), '$maxloadavg' => array('maxloadavg', t("Maximum Load Average"), intval(get_config('system', 'maxloadavg')) > 0 ? get_config('system', 'maxloadavg') : 50, t("Maximum system load before delivery and poll processes are deferred - default 50.")), '$default_expire_days' => array('default_expire_days', t('Expiration period in days for imported (grid/network) content'), intval(get_config('system', 'default_expire_days')), t('0 for no expiration of imported content')), '$form_security_token' => get_form_security_token("admin_site")));
 }
Example #10
0
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);
}
Example #11
0
function poller_run($argv, $argc)
{
    cli_startup();
    $a = get_app();
    $maxsysload = intval(get_config('system', 'maxloadavg'));
    if ($maxsysload < 1) {
        $maxsysload = 50;
    }
    if (function_exists('sys_getloadavg')) {
        $load = sys_getloadavg();
        if (intval($load[0]) > $maxsysload) {
            logger('system: load ' . $load . ' too high. Poller deferred to next scheduled run.');
            return;
        }
    }
    $interval = intval(get_config('system', 'poll_interval'));
    if (!$interval) {
        $interval = get_config('system', 'delivery_interval') === false ? 3 : intval(get_config('system', 'delivery_interval'));
    }
    // Check for a lockfile.  If it exists, but is over an hour old, it's stale.  Ignore it.
    $lockfile = 'store/[data]/poller';
    if (file_exists($lockfile) && filemtime($lockfile) > time() - 3600 && !get_config('system', 'override_poll_lockfile')) {
        logger("poller: Already running");
        return;
    }
    // Create a lockfile.  Needs two vars, but $x doesn't need to contain anything.
    file_put_contents($lockfile, $x);
    logger('poller: start');
    // run queue delivery process in the background
    proc_run('php', "include/queue.php");
    // maintenance for mod sharedwithme - check for updated items and remove them
    require_once 'include/sharedwithme.php';
    apply_updates();
    // expire any expired mail
    q("delete from mail where expires != '%s' and expires < %s ", dbesc(NULL_DATE), db_utcnow());
    // expire any expired items
    $r = q("select id from item where expires != '%s' and expires < %s \n\t\tand item_deleted = 0 ", dbesc(NULL_DATE), db_utcnow());
    if ($r) {
        require_once 'include/items.php';
        foreach ($r as $rr) {
            drop_item($rr['id'], false);
        }
    }
    // Ensure that every channel pings a directory server once a month. This way we can discover
    // channels and sites that quietly vanished and prevent the directory from accumulating stale
    // or dead entries.
    $r = q("select channel_id from channel where channel_dirdate < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('30 DAY'));
    if ($r) {
        foreach ($r as $rr) {
            proc_run('php', 'include/directory.php', $rr['channel_id'], 'force');
            if ($interval) {
                @time_sleep_until(microtime(true) + (double) $interval);
            }
        }
    }
    // publish any applicable items that were set to be published in the future
    // (time travel posts). Restrict to items that have come of age in the last
    // couple of days to limit the query to something reasonable.
    $r = q("select id from item where item_delayed = 1 and created <= %s  and created > '%s' ", db_utcnow(), dbesc(datetime_convert('UTC', 'UTC', 'now - 2 days')));
    if ($r) {
        foreach ($r as $rr) {
            $x = q("update item set item_delayed = 0 where id = %d", intval($rr['id']));
            if ($x) {
                proc_run('php', 'include/notifier.php', 'wall-new', $rr['id']);
            }
        }
    }
    $abandon_days = intval(get_config('system', 'account_abandon_days'));
    if ($abandon_days < 1) {
        $abandon_days = 0;
    }
    // once daily run birthday_updates and then expire in background
    // FIXME: add birthday updates, both locally and for xprof for use
    // by directory servers
    $d1 = intval(get_config('system', 'last_expire_day'));
    $d2 = intval(datetime_convert('UTC', 'UTC', 'now', 'd'));
    // Allow somebody to staggger daily activities if they have more than one site on their server,
    // or if it happens at an inconvenient (busy) hour.
    $h1 = intval(get_config('system', 'cron_hour'));
    $h2 = intval(datetime_convert('UTC', 'UTC', 'now', 'G'));
    $dirmode = get_config('system', 'directory_mode');
    /**
     * Cron Daily
     *
     * Actions in the following block are executed once per day, not on every poller run
     *
     */
    if ($d2 != $d1 && $h1 == $h2) {
        require_once 'include/dir_fns.php';
        check_upstream_directory();
        call_hooks('cron_daily', datetime_convert());
        $d3 = intval(datetime_convert('UTC', 'UTC', 'now', 'N'));
        if ($d3 == 7) {
            /**
             * Cron Weekly
             * 
             * Actions in the following block are executed once per day only on Sunday (once per week).
             *
             */
            call_hooks('cron_weekly', datetime_convert());
            z_check_cert();
            require_once 'include/hubloc.php';
            prune_hub_reinstalls();
            require_once 'include/Contact.php';
            mark_orphan_hubsxchans();
            // get rid of really old poco records
            q("delete from xlink where xlink_updated < %s - INTERVAL %s and xlink_static = 0 ", db_utcnow(), db_quoteinterval('14 DAY'));
            $dirmode = intval(get_config('system', 'directory_mode'));
            if ($dirmode === DIRECTORY_MODE_SECONDARY || $dirmode === DIRECTORY_MODE_PRIMARY) {
                logger('regdir: ' . print_r(z_fetch_url(get_directory_primary() . '/regdir?f=&url=' . urlencode(z_root()) . '&realm=' . urlencode(get_directory_realm())), true));
            }
            // Check for dead sites
            proc_run('php', 'include/checksites.php');
            // update searchable doc indexes
            proc_run('php', 'include/importdoc.php');
            /**
             * End Cron Weekly
             */
        }
        update_birthdays();
        //update statistics in config
        require_once 'include/statistics_fns.php';
        update_channels_total_stat();
        update_channels_active_halfyear_stat();
        update_channels_active_monthly_stat();
        update_local_posts_stat();
        // expire any read notifications over a month old
        q("delete from notify where seen = 1 and date < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('30 DAY'));
        // expire old delivery reports
        $keep_reports = intval(get_config('system', 'expire_delivery_reports'));
        if ($keep_reports === 0) {
            $keep_reports = 30;
        }
        q("delete from dreport where dreport_time < %s - INTERVAL %s", db_utcnow(), db_quoteinterval($keep_reports . ' DAY'));
        // expire any expired accounts
        downgrade_accounts();
        // If this is a directory server, request a sync with an upstream
        // directory at least once a day, up to once every poll interval.
        // Pull remote changes and push local changes.
        // potential issue: how do we keep from creating an endless update loop?
        if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
            require_once 'include/dir_fns.php';
            sync_directories($dirmode);
        }
        set_config('system', 'last_expire_day', $d2);
        proc_run('php', 'include/expire.php');
        proc_run('php', 'include/cli_suggest.php');
        require_once 'include/hubloc.php';
        remove_obsolete_hublocs();
        /**
         * End Cron Daily
         */
    }
    // update any photos which didn't get imported properly
    // This should be rare
    $r = q("select xchan_photo_l, xchan_hash from xchan where xchan_photo_l != '' and xchan_photo_m = '' \n\t\tand xchan_photo_date < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('1 DAY'));
    if ($r) {
        require_once 'include/photo/photo_driver.php';
        foreach ($r as $rr) {
            $photos = import_xchan_photo($rr['xchan_photo_l'], $rr['xchan_hash']);
            $x = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s'\n\t\t\t\twhere xchan_hash = '%s'", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($rr['xchan_hash']));
        }
    }
    // pull in some public posts
    if (!get_config('system', 'disable_discover_tab')) {
        proc_run('php', 'include/externals.php');
    }
    $manual_id = 0;
    $generation = 0;
    $force = false;
    $restart = false;
    if ($argc > 1 && $argv[1] == 'force') {
        $force = true;
    }
    if ($argc > 1 && $argv[1] == 'restart') {
        $restart = true;
        $generation = intval($argv[2]);
        if (!$generation) {
            killme();
        }
    }
    if ($argc > 1 && intval($argv[1])) {
        $manual_id = intval($argv[1]);
        $force = true;
    }
    $sql_extra = $manual_id ? " AND abook_id = " . intval($manual_id) . " " : "";
    reload_plugins();
    $d = datetime_convert();
    // TODO check to see if there are any cronhooks before wasting a process
    if (!$restart) {
        proc_run('php', 'include/cronhooks.php');
    }
    // Only poll from those with suitable relationships
    $abandon_sql = $abandon_days ? sprintf(" AND account_lastlog > %s - INTERVAL %s ", db_utcnow(), db_quoteinterval(intval($abandon_days) . ' DAY')) : '';
    $randfunc = db_getfunc('RAND');
    $contacts = q("SELECT * FROM abook LEFT JOIN xchan on abook_xchan = xchan_hash \n\t\tLEFT JOIN account on abook_account = account_id\n\t\twhere abook_self = 0\n\t\t{$sql_extra} \n\t\tAND (( account_flags = %d ) OR ( account_flags = %d )) {$abandon_sql} ORDER BY {$randfunc}", intval(ACCOUNT_OK), intval(ACCOUNT_UNVERIFIED));
    if ($contacts) {
        foreach ($contacts as $contact) {
            $update = false;
            $t = $contact['abook_updated'];
            $c = $contact['abook_connected'];
            if (intval($contact['abook_feed'])) {
                $min = service_class_fetch($contact['abook_channel'], 'minimum_feedcheck_minutes');
                if (!$min) {
                    $min = intval(get_config('system', 'minimum_feedcheck_minutes'));
                }
                if (!$min) {
                    $min = 60;
                }
                $x = datetime_convert('UTC', 'UTC', "now - {$min} minutes");
                if ($c < $x) {
                    proc_run('php', 'include/onepoll.php', $contact['abook_id']);
                    if ($interval) {
                        @time_sleep_until(microtime(true) + (double) $interval);
                    }
                }
                continue;
            }
            if ($contact['xchan_network'] !== 'zot') {
                continue;
            }
            if ($c == $t) {
                if (datetime_convert('UTC', 'UTC', 'now') > datetime_convert('UTC', 'UTC', $t . " + 1 day")) {
                    $update = true;
                }
            } else {
                // if we've never connected with them, start the mark for death countdown from now
                if ($c == NULL_DATE) {
                    $r = q("update abook set abook_connected = '%s'  where abook_id = %d", dbesc(datetime_convert()), intval($contact['abook_id']));
                    $c = datetime_convert();
                    $update = true;
                }
                // He's dead, Jim
                if (strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $c . " + 30 day")) > 0) {
                    $r = q("update abook set abook_archived = 1 where abook_id = %d", intval($contact['abook_id']));
                    $update = false;
                    continue;
                }
                if (intval($contact['abook_archived'])) {
                    $update = false;
                    continue;
                }
                // might be dead, so maybe don't poll quite so often
                // recently deceased, so keep up the regular schedule for 3 days
                if (strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $c . " + 3 day")) > 0 && strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $t . " + 1 day")) > 0) {
                    $update = true;
                }
                // After that back off and put them on a morphine drip
                if (strcmp(datetime_convert('UTC', 'UTC', 'now'), datetime_convert('UTC', 'UTC', $t . " + 2 day")) > 0) {
                    $update = true;
                }
            }
            if (intval($contact['abook_pending']) || intval($contact['abook_archived']) || intval($contact['abook_ignored']) || intval($contact['abook_blocked'])) {
                continue;
            }
            if (!$update && !$force) {
                continue;
            }
            proc_run('php', 'include/onepoll.php', $contact['abook_id']);
            if ($interval) {
                @time_sleep_until(microtime(true) + (double) $interval);
            }
        }
    }
    if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
        $r = q("SELECT u.ud_addr, u.ud_id, u.ud_last FROM updates AS u INNER JOIN (SELECT ud_addr, max(ud_id) AS ud_id FROM updates WHERE ( ud_flags & %d ) = 0 AND ud_addr != '' AND ( ud_last = '%s' OR ud_last > %s - INTERVAL %s ) GROUP BY ud_addr) AS s ON s.ud_id = u.ud_id ", intval(UPDATE_FLAGS_UPDATED), dbesc(NULL_DATE), db_utcnow(), db_quoteinterval('7 DAY'));
        if ($r) {
            foreach ($r as $rr) {
                // If they didn't respond when we attempted before, back off to once a day
                // After 7 days we won't bother anymore
                if ($rr['ud_last'] != NULL_DATE) {
                    if ($rr['ud_last'] > datetime_convert('UTC', 'UTC', 'now - 1 day')) {
                        continue;
                    }
                }
                proc_run('php', 'include/onedirsync.php', $rr['ud_id']);
                if ($interval) {
                    @time_sleep_until(microtime(true) + (double) $interval);
                }
            }
        }
    }
    set_config('system', 'lastpoll', datetime_convert());
    //All done - clear the lockfile
    @unlink($lockfile);
    return;
}
Example #12
0
File: zot.php Project: 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;
}