function testdrive_cron($a, $b)
{
    $r = q("select * from account where account_expires_on < %s + INTERVAL %s and\n\t\taccount_expire_notified = '%s' ", db_utcnow(), db_quoteinterval('5 DAY'), dbesc(NULL_DATE));
    if ($r) {
        foreach ($r as $rr) {
            $uid = $rr['account_default_channel'];
            if (!$uid) {
                continue;
            }
            $x = q("select * from channel where channel_id = %d limit 1", intval($uid));
            if (!$x) {
                continue;
            }
            \Zotlabs\Lib\Enotify::submit(array('type' => NOTIFY_SYSTEM, 'system_type' => 'testdrive_expire', 'from_xchan' => $x[0]['channel_hash'], 'to_xchan' => $x[0]['channel_hash']));
            q("update account set account_expire_notified = '%s' where account_id = %d", dbesc(datetime_convert()), intval($rr['account_id']));
        }
    }
    // give them a 5 day grace period. Then nuke the account.
    $r = q("select * from account where account_expired = 1 and account_expires < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('5 DAY'));
    if ($r) {
        foreach ($r as $rr) {
            account_remove($rr['account_id']);
        }
    }
}
Exemple #2
0
function checksites_run($argv, $argc)
{
    cli_startup();
    $a = get_app();
    logger('checksites: start');
    if ($argc > 1 && $argv[1]) {
        $site_id = $argv[1];
    }
    if ($site_id) {
        $sql_options = " and site_url = '" . dbesc($argv[1]) . "' ";
    }
    $days = intval(get_config('system', 'sitecheckdays'));
    if ($days < 1) {
        $days = 30;
    }
    $r = q("select * from site where site_dead = 0 and site_update < %s - INTERVAL %s and site_type = %d {$sql_options} ", db_utcnow(), db_quoteinterval($days . ' DAY'), intval(SITE_TYPE_ZOT));
    if (!$r) {
        return;
    }
    foreach ($r as $rr) {
        if (!strcasecmp($rr['site_url'], z_root())) {
            continue;
        }
        $x = ping_site($rr['site_url']);
        if ($x['success']) {
            logger('checksites: ' . $rr['site_url']);
            q("update site set site_update = '%s' where site_url = '%s' ", dbesc(datetime_convert()), dbesc($rr['site_url']));
        } else {
            logger('marking dead site: ' . $x['message']);
            q("update site set site_dead = 1 where site_url = '%s' ", dbesc($rr['site_url']));
        }
    }
    return;
}
 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
      */
 }
Exemple #4
0
function queue_run($argv, $argc)
{
    cli_startup();
    global $a;
    require_once 'include/items.php';
    require_once 'include/bbcode.php';
    if (argc() > 1) {
        $queue_id = argv(1);
    } else {
        $queue_id = 0;
    }
    logger('queue: start');
    // delete all queue items more than 3 days old
    // but first mark these sites dead if we haven't heard from them in a month
    $r = q("select outq_posturl from outq where outq_created < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('3 DAY'));
    if ($r) {
        foreach ($r as $rr) {
            $site_url = '';
            $h = parse_url($rr['outq_posturl']);
            $desturl = $h['scheme'] . '://' . $h['host'] . ($h['port'] ? ':' . $h['port'] : '');
            q("update site set site_dead = 1 where site_dead = 0 and site_url = '%s' and site_update < %s - INTERVAL %s", dbesc($desturl), db_utcnow(), db_quoteinterval('1 MONTH'));
        }
    }
    $r = q("DELETE FROM outq WHERE outq_created < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('3 DAY'));
    if ($queue_id) {
        $r = q("SELECT * FROM outq WHERE outq_hash = '%s' LIMIT 1", dbesc($queue_id));
    } else {
        // For the first 12 hours we'll try to deliver every 15 minutes
        // After that, we'll only attempt delivery once per hour.
        // This currently only handles the default queue drivers ('zot' or '') which we will group by posturl
        // so that we don't start off a thousand deliveries for a couple of dead hubs.
        // The zot driver will deliver everything destined for a single hub once contact is made (*if* contact is made).
        // Other drivers will have to do something different here and may need their own query.
        // Note: this requires some tweaking as new posts to long dead hubs once a day will keep them in the
        // "every 15 minutes" category. We probably need to prioritise them when inserted into the queue
        // or just prior to this query based on recent and long-term delivery history. If we have good reason to believe
        // the site is permanently down, there's no reason to attempt delivery at all, or at most not more than once
        // or twice a day.
        // FIXME: can we sort postgres on outq_priority and maintain the 'distinct' ?
        // The order by max(outq_priority) might be a dodgy query because of the group by.
        // The desired result is to return a sequence in the order most likely to be delivered in this run.
        // If a hub has already been sitting in the queue for a few days, they should be delivered last;
        // hence every failure should drop them further down the priority list.
        if (ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
            $prefix = 'DISTINCT ON (outq_posturl)';
            $suffix = 'ORDER BY outq_posturl';
        } else {
            $prefix = '';
            $suffix = 'GROUP BY outq_posturl ORDER BY max(outq_priority)';
        }
        $r = q("SELECT {$prefix} * FROM outq WHERE outq_delivered = 0 and (( outq_created > %s - INTERVAL %s and outq_updated < %s - INTERVAL %s ) OR ( outq_updated < %s - INTERVAL %s )) {$suffix}", db_utcnow(), db_quoteinterval('12 HOUR'), db_utcnow(), db_quoteinterval('15 MINUTE'), db_utcnow(), db_quoteinterval('1 HOUR'));
    }
    if (!$r) {
        return;
    }
    foreach ($r as $rr) {
        queue_deliver($rr);
    }
}
Exemple #5
0
 public static function run($argc, $argv)
 {
     logger('cron_daily: start');
     /**
      * Cron Daily
      *
      */
     require_once 'include/dir_fns.php';
     check_upstream_directory();
     // Fire off the Cron_weekly process if it's the correct day.
     $d3 = intval(datetime_convert('UTC', 'UTC', 'now', 'N'));
     if ($d3 == 7) {
         Master::Summon(array('Cron_weekly'));
     }
     // once daily run birthday_updates and then expire in background
     // FIXME: add birthday updates, both locally and for xprof for use
     // by directory servers
     update_birthdays();
     // expire any read notifications over a month old
     q("delete from notify where seen = 1 and created < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('30 DAY'));
     //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 old delivery reports
     $keep_reports = intval(get_config('system', 'expire_delivery_reports'));
     if ($keep_reports === 0) {
         $keep_reports = 10;
     }
     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?
     $dirmode = get_config('system', 'directory_mode');
     if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
         require_once 'include/dir_fns.php';
         sync_directories($dirmode);
     }
     Master::Summon(array('Expire'));
     Master::Summon(array('Cli_suggest'));
     require_once 'include/hubloc.php';
     remove_obsolete_hublocs();
     call_hooks('cron_daily', datetime_convert());
     set_config('system', 'last_expire_day', $d2);
     /**
      * End Cron Daily
      */
 }
Exemple #6
0
 /**
  * @brief Returns content for Admin Summary Page.
  *
  * @param App &$a
  * @return string HTML from parsed admin_summary.tpl
  */
 function admin_page_summary()
 {
     // list total user accounts, expirations etc.
     $accounts = array();
     $r = q("SELECT COUNT(*) AS total, COUNT(CASE WHEN account_expires > %s THEN 1 ELSE NULL END) AS expiring, COUNT(CASE WHEN account_expires < %s AND account_expires > '%s' THEN 1 ELSE NULL END) AS expired, COUNT(CASE WHEN (account_flags & %d)>0 THEN 1 ELSE NULL END) AS blocked FROM account", db_utcnow(), db_utcnow(), dbesc(NULL_DATE), intval(ACCOUNT_BLOCKED));
     if ($r) {
         $accounts['total'] = array('label' => t('# Accounts'), 'val' => $r[0]['total']);
         $accounts['blocked'] = array('label' => t('# blocked accounts'), 'val' => $r[0]['blocked']);
         $accounts['expired'] = array('label' => t('# expired accounts'), 'val' => $r[0]['expired']);
         $accounts['expiring'] = array('label' => t('# expiring accounts'), 'val' => $r[0]['expiring']);
     }
     // pending registrations
     $r = q("SELECT COUNT(id) AS `count` FROM `register` WHERE `uid` != '0'");
     $pending = $r[0]['count'];
     // available channels, primary and clones
     $channels = array();
     $r = q("SELECT COUNT(*) AS total, COUNT(CASE WHEN channel_primary = 1 THEN 1 ELSE NULL END) AS main, COUNT(CASE WHEN channel_primary = 0 THEN 1 ELSE NULL END) AS clones FROM channel WHERE channel_removed = 0");
     if ($r) {
         $channels['total'] = array('label' => t('# Channels'), 'val' => $r[0]['total']);
         $channels['main'] = array('label' => t('# primary'), 'val' => $r[0]['main']);
         $channels['clones'] = array('label' => t('# clones'), 'val' => $r[0]['clones']);
     }
     // We can do better, but this is a quick queue status
     $r = q("SELECT COUNT(outq_delivered) AS total FROM outq WHERE outq_delivered = 0");
     $queue = $r ? $r[0]['total'] : 0;
     $queues = array('label' => t('Message queues'), 'queue' => $queue);
     // If no plugins active return 0, otherwise list of plugin names
     $plugins = count(\App::$plugins) == 0 ? count(\App::$plugins) : \App::$plugins;
     // Could be extended to provide also other alerts to the admin
     $alertmsg = '';
     // annoy admin about upcoming unsupported PHP version
     if (version_compare(PHP_VERSION, '5.4', '<')) {
         $alertmsg = 'Your PHP version ' . PHP_VERSION . ' will not be supported with the next major release of $Projectname. You are strongly urged to upgrade to a current version.' . '<br>PHP 5.3 has reached its <a href="http://php.net/eol.php" class="alert-link">End of Life (EOL)</a> in August 2014.' . ' A list about current PHP versions can be found <a href="http://php.net/supported-versions.php" class="alert-link">here</a>.';
     }
     $vmaster = get_repository_version('master');
     $vdev = get_repository_version('dev');
     $upgrade = version_compare(STD_VERSION, $vmaster) < 0 ? t('Your software should be updated') : '';
     $t = get_markup_template('admin_summary.tpl');
     return replace_macros($t, array('$title' => t('Administration'), '$page' => t('Summary'), '$adminalertmsg' => $alertmsg, '$queues' => $queues, '$accounts' => array(t('Registered accounts'), $accounts), '$pending' => array(t('Pending registrations'), $pending), '$channels' => array(t('Registered channels'), $channels), '$plugins' => array(t('Active plugins'), $plugins), '$version' => array(t('Version'), STD_VERSION), '$vmaster' => array(t('Repository version (master)'), $vmaster), '$vdev' => array(t('Repository version (dev)'), $vdev), '$upgrade' => $upgrade, '$build' => get_config('system', 'db_version')));
 }
Exemple #7
0
function update_channels_active_monthly_stat()
{
    $r = q("select channel_id from channel left join account on account_id = channel_account_id\n\t\t\twhere account_flags = 0 and account_lastlog > %s - INTERVAL %s", db_utcnow(), db_quoteinterval('1 MONTH'));
    if ($r) {
        $s = '';
        foreach ($r as $rr) {
            if ($s) {
                $s .= ',';
            }
            $s .= intval($rr['channel_id']);
        }
        $x = q("select uid from item where uid in ( {$s} ) and item_wall = 1 and created > %s - INTERVAL %s group by uid", db_utcnow(), db_quoteinterval('1 MONTH'));
        if ($x) {
            $channels_active_monthly_stat = count($x);
            set_config('system', 'channels_active_monthly_stat', $channels_active_monthly_stat);
        } else {
            set_config('system', 'channels_active_monthly_stat', null);
        }
    } else {
        set_config('system', 'channels_active_monthly_stat', null);
    }
}
Exemple #8
0
 function get()
 {
     $registration_is = '';
     $other_sites = '';
     if (get_config('system', 'register_policy') == REGISTER_CLOSED) {
         if (get_config('system', 'directory_mode') == DIRECTORY_MODE_STANDALONE) {
             notice(t('Registration on this hub is disabled.') . EOL);
             return;
         }
         $mod = new Pubsites();
         return $mod->get();
     }
     if (get_config('system', 'register_policy') == REGISTER_APPROVE) {
         $registration_is = t('Registration on this hub is by approval only.');
         $other_sites = t('<a href="pubsites">Register at another affiliated hub.</a>');
     }
     $max_dailies = intval(get_config('system', 'max_daily_registrations'));
     if ($max_dailies) {
         $r = q("select count(account_id) as total from account where account_created > %s - INTERVAL %s", db_utcnow(), db_quoteinterval('1 day'));
         if ($r && $r[0]['total'] >= $max_dailies) {
             logger('max daily registrations exceeded.');
             notice(t('This site has exceeded the number of allowed daily account registrations. Please try again tomorrow.') . EOL);
             return;
         }
     }
     // Configurable terms of service link
     $tosurl = get_config('system', 'tos_url');
     if (!$tosurl) {
         $tosurl = z_root() . '/help/TermsOfService';
     }
     $toslink = '<a href="' . $tosurl . '" target="_blank">' . t('Terms of Service') . '</a>';
     // Configurable whether to restrict age or not - default is based on international legal requirements
     // This can be relaxed if you are on a restricted server that does not share with public servers
     if (get_config('system', 'no_age_restriction')) {
         $label_tos = sprintf(t('I accept the %s for this website'), $toslink);
     } else {
         $label_tos = sprintf(t('I am over 13 years of age and accept the %s for this website'), $toslink);
     }
     $enable_tos = 1 - intval(get_config('system', 'no_termsofservice'));
     $email = array('email', t('Your email address'), x($_REQUEST, 'email') ? strip_tags(trim($_REQUEST['email'])) : "");
     $password = array('password', t('Choose a password'), x($_REQUEST, 'password') ? trim($_REQUEST['password']) : "");
     $password2 = array('password2', t('Please re-enter your password'), x($_REQUEST, 'password2') ? trim($_REQUEST['password2']) : "");
     $invite_code = array('invite_code', t('Please enter your invitation code'), x($_REQUEST, 'invite_code') ? strip_tags(trim($_REQUEST['invite_code'])) : "");
     $name = array('name', t('Name or caption'), x($_REQUEST, 'name') ? $_REQUEST['name'] : '', t('Examples: "Bob Jameson", "Lisa and her Horses", "Soccer", "Aviation Group"'));
     $nickhub = '@' . str_replace(array('http://', 'https://', '/'), '', get_config('system', 'baseurl'));
     $nickname = array('nickname', t('Choose a short nickname'), x($_REQUEST, 'nickname') ? $_REQUEST['nickname'] : '', sprintf(t('Your nickname will be used to create an easy to remember channel address e.g. nickname%s'), $nickhub));
     $privacy_role = x($_REQUEST, 'permissions_role') ? $_REQUEST['permissions_role'] : "";
     $role = array('permissions_role', t('Channel role and privacy'), $privacy_role ? $privacy_role : 'social', t('Select a channel role with your privacy requirements.') . ' <a href="help/roles" target="_blank">' . t('Read more about roles') . '</a>', get_roles());
     $tos = array('tos', $label_tos, '', '', array(t('no'), t('yes')));
     $auto_create = UNO || get_config('system', 'auto_channel_create') ? true : false;
     $default_role = UNO ? 'social' : get_config('system', 'default_permissions_role');
     require_once 'include/bbcode.php';
     $o = replace_macros(get_markup_template('register.tpl'), array('$title' => t('Registration'), '$reg_is' => $registration_is, '$registertext' => bbcode(get_config('system', 'register_text')), '$other_sites' => $other_sites, '$invitations' => get_config('system', 'invitation_only'), '$invite_desc' => t('Membership on this site is by invitation only.'), '$invite_code' => $invite_code, '$auto_create' => $auto_create, '$name' => $name, '$role' => $role, '$default_role' => $default_role, '$nickname' => $nickname, '$enable_tos' => $enable_tos, '$tos' => $tos, '$email' => $email, '$pass1' => $password, '$pass2' => $password2, '$submit' => t('Register'), '$verify_note' => t('This site may require email verification after submitting this form. If you are returned to a login page, please check your email for instructions.')));
     return $o;
 }
Exemple #9
0
function chatroom_enter($observer_xchan, $room_id, $status, $client)
{
    if (!$room_id || !$observer_xchan) {
        return;
    }
    $r = q("select * from chatroom where cr_id = %d limit 1", intval($room_id));
    if (!$r) {
        notice(t('Room not found.') . EOL);
        return false;
    }
    require_once 'include/security.php';
    $sql_extra = permissions_sql($r[0]['cr_uid']);
    $x = q("select * from chatroom where cr_id = %d and cr_uid = %d {$sql_extra} limit 1", intval($room_id), intval($r[0]['cr_uid']));
    if (!$x) {
        notice(t('Permission denied.') . EOL);
        return false;
    }
    $limit = service_class_fetch($r[0]['cr_uid'], 'chatters_inroom');
    if ($limit !== false) {
        $y = q("select count(*) as total from chatpresence where cp_room = %d", intval($room_id));
        if ($y && $y[0]['total'] > $limit) {
            notice(t('Room is full') . EOL);
            return false;
        }
    }
    if (intval($x[0]['cr_expire'])) {
        $r = q("delete from chat where created < %s - INTERVAL %s and chat_room = %d", db_utcnow(), db_quoteinterval(intval($x[0]['cr_expire']) . ' MINUTE'), intval($x[0]['cr_id']));
    }
    $r = q("select * from chatpresence where cp_xchan = '%s' and cp_room = %d limit 1", dbesc($observer_xchan), intval($room_id));
    if ($r) {
        q("update chatpresence set cp_last = '%s' where cp_id = %d and cp_client = '%s'", dbesc(datetime_convert()), intval($r[0]['cp_id']), dbesc($client));
        return true;
    }
    $r = q("insert into chatpresence ( cp_room, cp_xchan, cp_last, cp_status, cp_client )\n\t\tvalues ( %d, '%s', '%s', '%s', '%s' )", intval($room_id), dbesc($observer_xchan), dbesc(datetime_convert()), dbesc($status), dbesc($client));
    return $r;
}
Exemple #10
0
function zot_reply_auth_check($data, $encrypted_packet)
{
    $ret = array('success' => false);
    /*
     * Requestor visits /magic/?dest=somewhere on their own site with a browser
     * magic redirects them to $destsite/post [with auth args....]
     * $destsite sends an auth_check packet to originator site
     * The auth_check packet is handled here by the originator's site 
     * - the browser session is still waiting
     * inside $destsite/post for everything to verify
     * If everything checks out we'll return a token to $destsite
     * and then $destsite will verify the token, authenticate the browser
     * session and then redirect to the original destination.
     * If authentication fails, the redirection to the original destination
     * will still take place but without authentication.
     */
    logger('mod_zot: auth_check', LOGGER_DEBUG);
    if (!$encrypted_packet) {
        logger('mod_zot: auth_check packet was not encrypted.');
        $ret['message'] .= 'no packet encryption' . EOL;
        json_return_and_die($ret);
    }
    $arr = $data['sender'];
    $sender_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']);
    // garbage collect any old unused notifications
    // This was and should be 10 minutes but my hosting provider has time lag between the DB and
    // the web server. We should probably convert this to webserver time rather than DB time so
    // that the different clocks won't affect it and allow us to keep the time short.
    q("delete from verify where type = 'auth' and created < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('30 MINUTE'));
    $y = q("select xchan_pubkey from xchan where xchan_hash = '%s' limit 1", dbesc($sender_hash));
    // We created a unique hash in mod/magic.php when we invoked remote auth, and stored it in
    // the verify table. It is now coming back to us as 'secret' and is signed by a channel at the other end.
    // First verify their signature. We will have obtained a zot-info packet from them as part of the sender
    // verification.
    if (!$y || !rsa_verify($data['secret'], base64url_decode($data['secret_sig']), $y[0]['xchan_pubkey'])) {
        logger('mod_zot: auth_check: sender not found or secret_sig invalid.');
        $ret['message'] .= 'sender not found or sig invalid ' . print_r($y, true) . EOL;
        json_return_and_die($ret);
    }
    // There should be exactly one recipient, the original auth requestor
    $ret['message'] .= 'recipients ' . print_r($recipients, true) . EOL;
    if ($data['recipients']) {
        $arr = $data['recipients'][0];
        $recip_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']);
        $c = q("select channel_id, channel_account_id, channel_prvkey from channel where channel_hash = '%s' limit 1", dbesc($recip_hash));
        if (!$c) {
            logger('mod_zot: auth_check: recipient channel not found.');
            $ret['message'] .= 'recipient not found.' . EOL;
            json_return_and_die($ret);
        }
        $confirm = base64url_encode(rsa_sign($data['secret'] . $recip_hash, $c[0]['channel_prvkey']));
        // This additionally checks for forged sites since we already stored the expected result in meta
        // and we've already verified that this is them via zot_gethub() and that their key signed our token
        $z = q("select id from verify where channel = %d and type = 'auth' and token = '%s' and meta = '%s' limit 1", intval($c[0]['channel_id']), dbesc($data['secret']), dbesc($data['sender']['url']));
        if (!$z) {
            logger('mod_zot: auth_check: verification key not found.');
            $ret['message'] .= 'verification key not found' . EOL;
            json_return_and_die($ret);
        }
        $r = q("delete from verify where id = %d", intval($z[0]['id']));
        $u = q("select account_service_class from account where account_id = %d limit 1", intval($c[0]['channel_account_id']));
        logger('mod_zot: auth_check: success', LOGGER_DEBUG);
        $ret['success'] = true;
        $ret['confirm'] = $confirm;
        if ($u && $u[0]['account_service_class']) {
            $ret['service_class'] = $u[0]['account_service_class'];
        }
        // Set "do not track" flag if this site or this channel's profile is restricted
        // in some way
        if (intval(get_config('system', 'block_public'))) {
            $ret['DNT'] = true;
        }
        if (!perm_is_allowed($c[0]['channel_id'], '', 'view_profile')) {
            $ret['DNT'] = true;
        }
        if (get_pconfig($c[0]['channel_id'], 'system', 'do_not_track')) {
            $ret['DNT'] = true;
        }
        if (get_pconfig($c[0]['channel_id'], 'system', 'hide_online_status')) {
            $ret['DNT'] = true;
        }
        json_return_and_die($ret);
    }
    json_return_and_die($ret);
}
Exemple #11
0
/**
 * @brief zot communications and messaging.
 *
 * Sender HTTP posts to this endpoint ($site/post typically) with 'data' parameter set to json zot message packet.
 * This packet is optionally encrypted, which we will discover if the json has an 'iv' element.
 * $contents => array( 'alg' => 'aes256cbc', 'iv' => initialisation vector, 'key' => decryption key, 'data' => encrypted data);
 * $contents->iv and $contents->key are random strings encrypted with this site's RSA public key and then base64url encoded.
 * Currently only 'aes256cbc' is used, but this is extensible should that algorithm prove inadequate.
 *
 * Once decrypted, one will find the normal json_encoded zot message packet.
 * 
 * Defined packet types are: notify, purge, refresh, force_refresh, auth_check, ping, and pickup 
 *
 * Standard packet: (used by notify, purge, refresh, force_refresh, and auth_check)
 * \code{.json}
 * {
 *   "type": "notify",
 *   "sender":{
 *     "guid":"kgVFf_1...",
 *     "guid_sig":"PT9-TApzp...",
 *     "url":"http:\/\/podunk.edu",
 *     "url_sig":"T8Bp7j5...",
 *   },
 *   "recipients": { optional recipient array },
 *   "callback":"\/post",
 *   "version":1,
 *   "secret":"1eaa...",
 *   "secret_sig": "df89025470fac8..."
 * }
 * \endcode
 *
 * Signature fields are all signed with the sender channel private key and base64url encoded.
 * Recipients are arrays of guid and guid_sig, which were previously signed with the recipients private 
 * key and base64url encoded and later obtained via channel discovery. Absence of recipients indicates
 * a public message or visible to all potential listeners on this site.
 *
 * "pickup" packet:
 * The pickup packet is sent in response to a notify packet from another site
 * \code{.json}
 * {
 *   "type":"pickup",
 *   "url":"http:\/\/example.com",
 *   "callback":"http:\/\/example.com\/post",
 *   "callback_sig":"teE1_fLI...",
 *   "secret":"1eaa...",
 *   "secret_sig":"O7nB4_..."
 * }
 * \endcode
 *
 * In the pickup packet, the sig fields correspond to the respective data
 * element signed with this site's system private key and then base64url encoded.
 * The "secret" is the same as the original secret from the notify packet. 
 *
 * If verification is successful, a json structure is returned containing a
 * success indicator and an array of type 'pickup'.
 * Each pickup element contains the original notify request and a message field
 * whose contents are dependent on the message type.
 *
 * This JSON array is AES encapsulated using the site public key of the site
 * that sent the initial zot pickup packet.
 * Using the above example, this would be example.com.
 *
 * \code{.json}
 * {
 *   "success":1,
 *   "pickup":{
 *     "notify":{
 *       "type":"notify",
 *       "sender":{
 *         "guid":"kgVFf_...",
 *         "guid_sig":"PT9-TApz...",
 *         "url":"http:\/\/z.podunk.edu",
 *         "url_sig":"T8Bp7j5D..."
 *       },
 *       "callback":"\/post",
 *       "version":1,
 *       "secret":"1eaa661..."
 *     },
 *     "message":{
 *       "type":"activity",
 *       "message_id":"*****@*****.**",
 *       "message_top":"*****@*****.**",
 *       "message_parent":"*****@*****.**",
 *       "created":"2012-11-20 04:04:16",
 *       "edited":"2012-11-20 04:04:16",
 *       "title":"",
 *       "body":"Hi Nickordo",
 *       "app":"",
 *       "verb":"post",
 *       "object_type":"",
 *       "target_type":"",
 *       "permalink":"",
 *       "location":"",
 *       "longlat":"",
 *       "owner":{
 *         "name":"Indigo",
 *         "address":"*****@*****.**",
 *         "url":"http:\/\/podunk.edu",
 *         "photo":{
 *           "mimetype":"image\/jpeg",
 *           "src":"http:\/\/podunk.edu\/photo\/profile\/m\/5"
 *         },
 *         "guid":"kgVFf_...",
 *         "guid_sig":"PT9-TAp...",
 *       },
 *       "author":{
 *         "name":"Indigo",
 *         "address":"*****@*****.**",
 *         "url":"http:\/\/podunk.edu",
 *         "photo":{
 *           "mimetype":"image\/jpeg",
 *           "src":"http:\/\/podunk.edu\/photo\/profile\/m\/5"
 *         },
 *         "guid":"kgVFf_...",
 *         "guid_sig":"PT9-TAp..."
 *       }
 *     }
 *   }
 * }
 * \endcode
 *
 * Currently defined message types are 'activity', 'mail', 'profile', 'location'
 * and 'channel_sync', which each have different content schemas.
 *
 * Ping packet:
 * A ping packet does not require any parameters except the type. It may or may
 * not be encrypted.
 *
 * \code{.json}
 * {
 *   "type": "ping"
 * }
 * \endcode
 *
 * On receipt of a ping packet a ping response will be returned:
 *
 * \code{.json}
 * {
 *   "success" : 1,
 *   "site" {
 *     "url": "http:\/\/podunk.edu",
 *     "url_sig": "T8Bp7j5...",
 *     "sitekey": "-----BEGIN PUBLIC KEY-----
 *                 MIICIjANBgkqhkiG9w0BAQE..."
 *   }
 * }
 * \endcode
 *
 * The ping packet can be used to verify that a site has not been re-installed, and to 
 * initiate corrective action if it has. The url_sig is signed with the site private key
 * and base64url encoded - and this should verify with the enclosed sitekey. Failure to
 * verify indicates the site is corrupt or otherwise unable to communicate using zot.
 * This return packet is not otherwise verified, so should be compared with other
 * results obtained from this site which were verified prior to taking action. For instance
 * if you have one verified result with this signature and key, and other records for this 
 * url which have different signatures and keys, it indicates that the site was re-installed
 * and corrective action may commence (remove or mark invalid any entries with different
 * signatures).
 * If you have no records which match this url_sig and key - no corrective action should
 * be taken as this packet may have been returned by an imposter.  
 *
 * @param[in,out] App &$a
 */
function post_post(&$a)
{
    $encrypted_packet = false;
    $ret = array('success' => false);
    $data = json_decode($_REQUEST['data'], true);
    /*
     * Many message packets will arrive encrypted. The existence of an 'iv'
     * element tells us we need to unencapsulate the AES-256-CBC content using
     * the site private key.
     */
    if ($data && array_key_exists('iv', $data)) {
        $encrypted_packet = true;
        $data = crypto_unencapsulate($data, get_config('system', 'prvkey'));
        logger('mod_zot: decrypt1: ' . $data, LOGGER_DATA);
        $data = json_decode($data, true);
    }
    if (!$data) {
        // possible Bleichenbacher's attack, just treat it as a
        // message we have no handler for. It should fail a bit
        // further along with "no hub". Our public key is public
        // knowledge. There's no reason why anybody should get the
        // encryption wrong unless they're fishing or hacking. If
        // they're developing and made a goof, this can be discovered
        // in the logs of the destination site. If they're fishing or
        // hacking, the bottom line is we can't verify their hub.
        // That's all we're going to tell them.
        $data = array('type' => 'bogus');
    }
    $msgtype = array_key_exists('type', $data) ? $data['type'] : '';
    if ($msgtype === 'ping') {
        // Useful to get a health check on a remote site.
        // This will let us know if any important communication details
        // that we may have stored are no longer valid, regardless of xchan details.
        logger('POST: got ping send pong now back: ' . z_root(), LOGGER_DEBUG);
        $ret['success'] = true;
        $ret['site'] = array();
        $ret['site']['url'] = z_root();
        $ret['site']['url_sig'] = base64url_encode(rsa_sign(z_root(), get_config('system', 'prvkey')));
        $ret['site']['sitekey'] = get_config('system', 'pubkey');
        json_return_and_die($ret);
    }
    if ($msgtype === 'pickup') {
        /*
         * The 'pickup' message arrives with a tracking ID which is associated with a particular outq_hash
         * First verify that that the returned signatures verify, then check that we have an outbound queue item
         * with the correct hash.
         * If everything verifies, find any/all outbound messages in the queue for this hubloc and send them back
         */
        if (!$data['secret'] || !$data['secret_sig']) {
            $ret['message'] = 'no verification signature';
            logger('mod_zot: pickup: ' . $ret['message'], LOGGER_DEBUG);
            json_return_and_die($ret);
        }
        $r = q("select distinct hubloc_sitekey from hubloc where hubloc_url = '%s' and hubloc_callback = '%s' and hubloc_sitekey != '' group by hubloc_sitekey ", dbesc($data['url']), dbesc($data['callback']));
        if (!$r) {
            $ret['message'] = 'site not found';
            logger('mod_zot: pickup: ' . $ret['message']);
            json_return_and_die($ret);
        }
        foreach ($r as $hubsite) {
            // verify the url_sig
            // If the server was re-installed at some point, there could be multiple hubs with the same url and callback.
            // Only one will have a valid key.
            $forgery = true;
            $secret_fail = true;
            $sitekey = $hubsite['hubloc_sitekey'];
            logger('mod_zot: Checking sitekey: ' . $sitekey, LOGGER_DATA);
            if (rsa_verify($data['callback'], base64url_decode($data['callback_sig']), $sitekey)) {
                $forgery = false;
            }
            if (rsa_verify($data['secret'], base64url_decode($data['secret_sig']), $sitekey)) {
                $secret_fail = false;
            }
            if (!$forgery && !$secret_fail) {
                break;
            }
        }
        if ($forgery) {
            $ret['message'] = 'possible site forgery';
            logger('mod_zot: pickup: ' . $ret['message']);
            json_return_and_die($ret);
        }
        if ($secret_fail) {
            $ret['message'] = 'secret validation failed';
            logger('mod_zot: pickup: ' . $ret['message']);
            json_return_and_die($ret);
        }
        /*
         * If we made it to here, the signatures verify, but we still don't know if the tracking ID is valid.
         * It wouldn't be an error if the tracking ID isn't found, because we may have sent this particular
         * queue item with another pickup (after the tracking ID for the other pickup  was verified). 
         */
        $r = q("select outq_posturl from outq where outq_hash = '%s' and outq_posturl = '%s' limit 1", dbesc($data['secret']), dbesc($data['callback']));
        if (!$r) {
            $ret['message'] = 'nothing to pick up';
            logger('mod_zot: pickup: ' . $ret['message']);
            json_return_and_die($ret);
        }
        /*
         * Everything is good if we made it here, so find all messages that are going to this location
         * and send them all.
         */
        $r = q("select * from outq where outq_posturl = '%s'", dbesc($data['callback']));
        if ($r) {
            logger('mod_zot: successful pickup message received from ' . $data['callback'] . ' ' . count($r) . ' message(s) picked up', LOGGER_DEBUG);
            $ret['success'] = true;
            $ret['pickup'] = array();
            foreach ($r as $rr) {
                if ($rr['outq_msg']) {
                    $x = json_decode($rr['outq_msg'], true);
                    if (!$x) {
                        continue;
                    }
                    if (array_key_exists('message_list', $x)) {
                        foreach ($x['message_list'] as $xx) {
                            $ret['pickup'][] = array('notify' => json_decode($rr['outq_notify'], true), 'message' => $xx);
                        }
                    } else {
                        $ret['pickup'][] = array('notify' => json_decode($rr['outq_notify'], true), 'message' => $x);
                    }
                    $x = q("delete from outq where outq_hash = '%s'", dbesc($rr['outq_hash']));
                }
            }
        }
        $encrypted = crypto_encapsulate(json_encode($ret), $sitekey);
        json_return_and_die($encrypted);
        /* pickup: end */
    }
    /*
     * All other message types require us to verify the sender. This is a generic check, so we 
     * will do it once here and bail if anything goes wrong.
     */
    if (array_key_exists('sender', $data)) {
        $sender = $data['sender'];
    }
    /* Check if the sender is already verified here */
    $hubs = zot_gethub($sender, true);
    if (!$hubs) {
        /* Have never seen this guid or this guid coming from this location. Check it and register it. */
        // (!!) this will validate the sender
        $result = zot_register_hub($sender);
        if (!$result['success'] || !($hubs = zot_gethub($sender, true))) {
            $ret['message'] = 'Hub not available.';
            logger('mod_zot: no hub');
            json_return_and_die($ret);
        }
    }
    foreach ($hubs as $hub) {
        $sitekey = $hub['hubloc_sitekey'];
        if (array_key_exists('sitekey', $sender) && $sender['sitekey']) {
            /*
             * This hub has now been proven to be valid.
             * Any hub with the same URL and a different sitekey cannot be valid.
             * Get rid of them (mark them deleted). There's a good chance they were re-installs.
             */
            q("update hubloc set hubloc_deleted = 1, hubloc_error = 1 where hubloc_url = '%s' and hubloc_sitekey != '%s' ", dbesc($hub['hubloc_url']), dbesc($sender['sitekey']));
            $sitekey = $sender['sitekey'];
        }
        // $sender['sitekey'] is a new addition to the protcol to distinguish
        // hublocs coming from re-installed sites. Older sites will not provide
        // this field and we have to still mark them valid, since we can't tell
        // if this hubloc has the same sitekey as the packet we received.
        // Update our DB to show when we last communicated successfully with this hub
        // This will allow us to prune dead hubs from using up resources
        $r = q("update hubloc set hubloc_connected = '%s' where hubloc_id = %d and hubloc_sitekey = '%s' ", dbesc(datetime_convert()), intval($hub['hubloc_id']), dbesc($sitekey));
        // a dead hub came back to life - reset any tombstones we might have
        if (intval($hub['hubloc_error'])) {
            q("update hubloc set hubloc_error = 0 where hubloc_id = %d and hubloc_sitekey = '%s' ", intval($hub['hubloc_id']), dbesc($sitekey));
            if (intval($r[0]['hubloc_orphancheck'])) {
                q("update hubloc set hubloc_orhpancheck = 0 where hubloc_id = %d and hubloc_sitekey = '%s' ", intval($hub['hubloc_id']), dbesc($sitekey));
            }
            q("update xchan set xchan_orphan = 0 where xchan_orphan = 1 and xchan_hash = '%s'", dbesc($hub['hubloc_hash']));
        }
        $connecting_url = $hub['hubloc_url'];
    }
    /** @TODO check which hub is primary and take action if mismatched */
    if (array_key_exists('recipients', $data)) {
        $recipients = $data['recipients'];
    }
    if ($msgtype === 'auth_check') {
        /*
         * Requestor visits /magic/?dest=somewhere on their own site with a browser
         * magic redirects them to $destsite/post [with auth args....]
         * $destsite sends an auth_check packet to originator site
         * The auth_check packet is handled here by the originator's site 
         * - the browser session is still waiting
         * inside $destsite/post for everything to verify
         * If everything checks out we'll return a token to $destsite
         * and then $destsite will verify the token, authenticate the browser
         * session and then redirect to the original destination.
         * If authentication fails, the redirection to the original destination
         * will still take place but without authentication.
         */
        logger('mod_zot: auth_check', LOGGER_DEBUG);
        if (!$encrypted_packet) {
            logger('mod_zot: auth_check packet was not encrypted.');
            $ret['message'] .= 'no packet encryption' . EOL;
            json_return_and_die($ret);
        }
        $arr = $data['sender'];
        $sender_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']);
        // garbage collect any old unused notifications
        // This was and should be 10 minutes but my hosting provider has time lag between the DB and
        // the web server. We should probably convert this to webserver time rather than DB time so
        // that the different clocks won't affect it and allow us to keep the time short.
        q("delete from verify where type = 'auth' and created < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('30 MINUTE'));
        $y = q("select xchan_pubkey from xchan where xchan_hash = '%s' limit 1", dbesc($sender_hash));
        // We created a unique hash in mod/magic.php when we invoked remote auth, and stored it in
        // the verify table. It is now coming back to us as 'secret' and is signed by a channel at the other end.
        // First verify their signature. We will have obtained a zot-info packet from them as part of the sender
        // verification.
        if (!$y || !rsa_verify($data['secret'], base64url_decode($data['secret_sig']), $y[0]['xchan_pubkey'])) {
            logger('mod_zot: auth_check: sender not found or secret_sig invalid.');
            $ret['message'] .= 'sender not found or sig invalid ' . print_r($y, true) . EOL;
            json_return_and_die($ret);
        }
        // There should be exactly one recipient, the original auth requestor
        $ret['message'] .= 'recipients ' . print_r($recipients, true) . EOL;
        if ($data['recipients']) {
            $arr = $data['recipients'][0];
            $recip_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']);
            $c = q("select channel_id, channel_account_id, channel_prvkey from channel where channel_hash = '%s' limit 1", dbesc($recip_hash));
            if (!$c) {
                logger('mod_zot: auth_check: recipient channel not found.');
                $ret['message'] .= 'recipient not found.' . EOL;
                json_return_and_die($ret);
            }
            $confirm = base64url_encode(rsa_sign($data['secret'] . $recip_hash, $c[0]['channel_prvkey']));
            // This additionally checks for forged sites since we already stored the expected result in meta
            // and we've already verified that this is them via zot_gethub() and that their key signed our token
            $z = q("select id from verify where channel = %d and type = 'auth' and token = '%s' and meta = '%s' limit 1", intval($c[0]['channel_id']), dbesc($data['secret']), dbesc($data['sender']['url']));
            if (!$z) {
                logger('mod_zot: auth_check: verification key not found.');
                $ret['message'] .= 'verification key not found' . EOL;
                json_return_and_die($ret);
            }
            $r = q("delete from verify where id = %d", intval($z[0]['id']));
            $u = q("select account_service_class from account where account_id = %d limit 1", intval($c[0]['channel_account_id']));
            logger('mod_zot: auth_check: success', LOGGER_DEBUG);
            $ret['success'] = true;
            $ret['confirm'] = $confirm;
            if ($u && $u[0]['account_service_class']) {
                $ret['service_class'] = $u[0]['account_service_class'];
            }
            // Set "do not track" flag if this site or this channel's profile is restricted
            // in some way
            if (intval(get_config('system', 'block_public'))) {
                $ret['DNT'] = true;
            }
            if (!perm_is_allowed($c[0]['channel_id'], '', 'view_profile')) {
                $ret['DNT'] = true;
            }
            if (get_pconfig($c[0]['channel_id'], 'system', 'do_not_track')) {
                $ret['DNT'] = true;
            }
            if (get_pconfig($c[0]['channel_id'], 'system', 'hide_online_status')) {
                $ret['DNT'] = true;
            }
            json_return_and_die($ret);
        }
        json_return_and_die($ret);
    }
    if ($msgtype === 'request') {
        // request a particular post/conversation by message_id
        $x = zot_process_message_request($data);
        json_return_and_die($x);
    }
    if ($msgtype === 'purge') {
        if ($recipients) {
            // basically this means "unfriend"
            foreach ($recipients as $recip) {
                $r = q("select channel.*,xchan.* from channel \n\t\t\t\t\tleft join xchan on channel_hash = xchan_hash\n\t\t\t\t\twhere channel_guid = '%s' and channel_guid_sig = '%s' limit 1", dbesc($recip['guid']), dbesc($recip['guid_sig']));
                if ($r) {
                    $r = q("select abook_id from abook where uid = %d and abook_xchan = '%s' limit 1", intval($r[0]['channel_id']), dbesc(make_xchan_hash($sender['guid'], $sender['guid_sig'])));
                    if ($r) {
                        contact_remove($r[0]['channel_id'], $r[0]['abook_id']);
                    }
                }
            }
        } else {
            // Unfriend everybody - basically this means the channel has committed suicide
            $arr = $data['sender'];
            $sender_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']);
            require_once 'include/Contact.php';
            remove_all_xchan_resources($sender_hash);
            $ret['success'] = true;
            json_return_and_die($ret);
        }
    }
    if ($msgtype === 'refresh' || $msgtype === 'force_refresh') {
        // remote channel info (such as permissions or photo or something)
        // has been updated. Grab a fresh copy and sync it.
        // The difference between refresh and force_refresh is that
        // force_refresh unconditionally creates a directory update record,
        // even if no changes were detected upon processing.
        if ($recipients) {
            // This would be a permissions update, typically for one connection
            foreach ($recipients as $recip) {
                $r = q("select channel.*,xchan.* from channel \n\t\t\t\t\tleft join xchan on channel_hash = xchan_hash\n\t\t\t\t\twhere channel_guid = '%s' and channel_guid_sig = '%s' limit 1", dbesc($recip['guid']), dbesc($recip['guid_sig']));
                $x = zot_refresh(array('xchan_guid' => $sender['guid'], 'xchan_guid_sig' => $sender['guid_sig'], 'hubloc_url' => $sender['url']), $r[0], $msgtype === 'force_refresh' ? true : false);
            }
        } else {
            // system wide refresh
            $x = zot_refresh(array('xchan_guid' => $sender['guid'], 'xchan_guid_sig' => $sender['guid_sig'], 'hubloc_url' => $sender['url']), null, $msgtype === 'force_refresh' ? true : false);
        }
        $ret['success'] = true;
        json_return_and_die($ret);
    }
    if ($msgtype === 'notify') {
        logger('notify received from ' . $connecting_url);
        $async = get_config('system', 'queued_fetch');
        if ($async) {
            // add to receive queue
            // qreceive_add($data);
        } else {
            $x = zot_fetch($data);
            $ret['delivery_report'] = $x;
        }
        $ret['success'] = true;
        json_return_and_die($ret);
    }
    // catchall
    json_return_and_die($ret);
}
Exemple #12
0
/**
 * @brief Create an array representing the important channel information
 * which would be necessary to create a nomadic identity clone. This includes
 * most channel resources and connection information with the exception of content.
 *
 * @param int $channel_id
 *     Channel_id to export
 * @param boolean $items
 *     Include channel posts (wall items), default false
 *
 * @returns array
 *     See function for details
 */
function identity_basic_export($channel_id, $items = false)
{
    /*
     * Red basic channel export
     */
    $ret = array();
    $ret['compatibility'] = array('project' => PLATFORM_NAME, 'version' => RED_VERSION, 'database' => DB_UPDATE_VERSION);
    $r = q("select * from channel where channel_id = %d limit 1", intval($channel_id));
    if ($r) {
        $ret['channel'] = $r[0];
    }
    $r = q("select * from profile where uid = %d", intval($channel_id));
    if ($r) {
        $ret['profile'] = $r;
    }
    $xchans = array();
    $r = q("select * from abook where abook_channel = %d ", intval($channel_id));
    if ($r) {
        $ret['abook'] = $r;
        foreach ($r as $rr) {
            $xchans[] = $rr['abook_xchan'];
        }
        stringify_array_elms($xchans);
    }
    if ($xchans) {
        $r = q("select * from xchan where xchan_hash in ( " . implode(',', $xchans) . " ) ");
        if ($r) {
            $ret['xchan'] = $r;
        }
        $r = q("select * from hubloc where hubloc_hash in ( " . implode(',', $xchans) . " ) ");
        if ($r) {
            $ret['hubloc'] = $r;
        }
    }
    $r = q("select * from `groups` where uid = %d ", intval($channel_id));
    if ($r) {
        $ret['group'] = $r;
    }
    $r = q("select * from group_member where uid = %d ", intval($channel_id));
    if ($r) {
        $ret['group_member'] = $r;
    }
    $r = q("select * from pconfig where uid = %d", intval($channel_id));
    if ($r) {
        $ret['config'] = $r;
    }
    $r = q("select type, data, os_storage from photo where scale = 4 and profile = 1 and uid = %d limit 1", intval($channel_id));
    if ($r) {
        $ret['photo'] = array('type' => $r[0]['type'], 'data' => $r[0]['os_storage'] ? base64url_encode(file_get_contents($r[0]['data'])) : base64url_encode($r[0]['data']));
    }
    // All other term types will be included in items, if requested.
    $r = q("select * from term where type in (%d,%d) and uid = %d", intval(TERM_SAVEDSEARCH), intval(TERM_THING), intval($channel_id));
    if ($r) {
        $ret['term'] = $r;
    }
    // add psuedo-column obj_baseurl to aid in relocations
    $r = q("select obj.*, '%s' as obj_baseurl from obj where obj_channel = %d", dbesc(z_root()), intval($channel_id));
    if ($r) {
        $ret['obj'] = $r;
    }
    $r = q("select * from app where app_channel = %d", intval($channel_id));
    if ($r) {
        $ret['app'] = $r;
    }
    $r = q("select * from chatroom where cr_uid = %d", intval($channel_id));
    if ($r) {
        $ret['chatroom'] = $r;
    }
    $r = q("select * from event where uid = %d", intval($channel_id));
    if ($r) {
        $ret['event'] = $r;
    }
    $r = q("select * from item where resource_type = 'event' and uid = %d", intval($channel_id));
    if ($r) {
        $ret['event_item'] = array();
        xchan_query($r);
        $r = fetch_post_tags($r, true);
        foreach ($r as $rr) {
            $ret['event_item'][] = encode_item($rr, true);
        }
    }
    $x = menu_list($channel_id);
    if ($x) {
        $ret['menu'] = array();
        for ($y = 0; $y < count($x); $y++) {
            $m = menu_fetch($x[$y]['menu_name'], $channel_id, $ret['channel']['channel_hash']);
            if ($m) {
                $ret['menu'][] = menu_element($m);
            }
        }
    }
    $x = menu_list($channel_id);
    if ($x) {
        $ret['menu'] = array();
        for ($y = 0; $y < count($x); $y++) {
            $m = menu_fetch($x[$y]['menu_name'], $channel_id, $ret['channel']['channel_hash']);
            if ($m) {
                $ret['menu'][] = menu_element($m);
            }
        }
    }
    $addon = array('channel_id' => $channel_id, 'data' => $ret);
    call_hooks('identity_basic_export', $addon);
    $ret = $addon['data'];
    if (!$items) {
        return $ret;
    }
    $r = q("select * from likes where channel_id = %d", intval($channel_id));
    if ($r) {
        $ret['likes'] = $r;
    }
    $r = q("select * from conv where uid = %d", intval($channel_id));
    if ($r) {
        for ($x = 0; $x < count($r); $x++) {
            $r[$x]['subject'] = base64url_decode(str_rot47($r[$x]['subject']));
        }
        $ret['conv'] = $r;
    }
    $r = q("select * from mail where mail.uid = %d", intval($channel_id));
    if ($r) {
        $m = array();
        foreach ($r as $rr) {
            xchan_mail_query($rr);
            $m[] = mail_encode($rr, true);
        }
        $ret['mail'] = $m;
    }
    $r = q("select item_id.*, item.mid from item_id left join item on item_id.iid = item.id where item_id.uid = %d", intval($channel_id));
    if ($r) {
        $ret['item_id'] = $r;
    }
    //$key = get_config('system','prvkey');
    /** @warning this may run into memory limits on smaller systems */
    /** export three months of posts. If you want to export and import all posts you have to start with 
     * the first year and export/import them in ascending order. 
     *
     * Don't export linked resource items. we'll have to pull those out separately.
     */
    $r = q("select * from item where item_wall = 1 and item_deleted = 0 and uid = %d and created > %s - INTERVAL %s and resource_type = '' order by created", intval($channel_id), db_utcnow(), db_quoteinterval('3 MONTH'));
    if ($r) {
        $ret['item'] = array();
        xchan_query($r);
        $r = fetch_post_tags($r, true);
        foreach ($r as $rr) {
            $ret['item'][] = encode_item($rr, true);
        }
    }
    return $ret;
}
Exemple #13
0
 function post()
 {
     // This will change. Figure out who the observer is and whether or not
     // they have permission to post here. Else ignore the post.
     if (!local_channel() && !remote_channel() && !x($_REQUEST, 'commenter')) {
         return;
     }
     require_once 'include/security.php';
     $uid = local_channel();
     $channel = null;
     $observer = null;
     /**
      * Is this a reply to something?
      */
     $parent = x($_REQUEST, 'parent') ? intval($_REQUEST['parent']) : 0;
     $parent_mid = x($_REQUEST, 'parent_mid') ? trim($_REQUEST['parent_mid']) : '';
     $remote_xchan = x($_REQUEST, 'remote_xchan') ? trim($_REQUEST['remote_xchan']) : false;
     $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($remote_xchan));
     if ($r) {
         $remote_observer = $r[0];
     } else {
         $remote_xchan = $remote_observer = false;
     }
     $profile_uid = x($_REQUEST, 'profile_uid') ? intval($_REQUEST['profile_uid']) : 0;
     require_once 'include/channel.php';
     $sys = get_sys_channel();
     if ($sys && $profile_uid && $sys['channel_id'] == $profile_uid && is_site_admin()) {
         $uid = intval($sys['channel_id']);
         $channel = $sys;
         $observer = $sys;
     }
     if (x($_REQUEST, 'dropitems')) {
         require_once 'include/items.php';
         $arr_drop = explode(',', $_REQUEST['dropitems']);
         drop_items($arr_drop);
         $json = array('success' => 1);
         echo json_encode($json);
         killme();
     }
     call_hooks('post_local_start', $_REQUEST);
     //	 logger('postvars ' . print_r($_REQUEST,true), LOGGER_DATA);
     $api_source = x($_REQUEST, 'api_source') && $_REQUEST['api_source'] ? true : false;
     $consensus = intval($_REQUEST['consensus']);
     // 'origin' (if non-zero) indicates that this network is where the message originated,
     // for the purpose of relaying comments to other conversation members.
     // If using the API from a device (leaf node) you must set origin to 1 (default) or leave unset.
     // If the API is used from another network with its own distribution
     // and deliveries, you may wish to set origin to 0 or false and allow the other
     // network to relay comments.
     // If you are unsure, it is prudent (and important) to leave it unset.
     $origin = $api_source && array_key_exists('origin', $_REQUEST) ? intval($_REQUEST['origin']) : 1;
     // To represent message-ids on other networks - this will create an iconfig record
     $namespace = $api_source && array_key_exists('namespace', $_REQUEST) ? strip_tags($_REQUEST['namespace']) : '';
     $remote_id = $api_source && array_key_exists('remote_id', $_REQUEST) ? strip_tags($_REQUEST['remote_id']) : '';
     $owner_hash = null;
     $message_id = x($_REQUEST, 'message_id') && $api_source ? strip_tags($_REQUEST['message_id']) : '';
     $created = x($_REQUEST, 'created') ? datetime_convert(date_default_timezone_get(), 'UTC', $_REQUEST['created']) : datetime_convert();
     $post_id = x($_REQUEST, 'post_id') ? intval($_REQUEST['post_id']) : 0;
     $app = x($_REQUEST, 'source') ? strip_tags($_REQUEST['source']) : '';
     $return_path = x($_REQUEST, 'return') ? $_REQUEST['return'] : '';
     $preview = x($_REQUEST, 'preview') ? intval($_REQUEST['preview']) : 0;
     $categories = x($_REQUEST, 'category') ? escape_tags($_REQUEST['category']) : '';
     $webpage = x($_REQUEST, 'webpage') ? intval($_REQUEST['webpage']) : 0;
     $pagetitle = x($_REQUEST, 'pagetitle') ? escape_tags(urlencode($_REQUEST['pagetitle'])) : '';
     $layout_mid = x($_REQUEST, 'layout_mid') ? escape_tags($_REQUEST['layout_mid']) : '';
     $plink = x($_REQUEST, 'permalink') ? escape_tags($_REQUEST['permalink']) : '';
     $obj_type = x($_REQUEST, 'obj_type') ? escape_tags($_REQUEST['obj_type']) : ACTIVITY_OBJ_NOTE;
     // allow API to bulk load a bunch of imported items with sending out a bunch of posts.
     $nopush = x($_REQUEST, 'nopush') ? intval($_REQUEST['nopush']) : 0;
     /*
      * Check service class limits
      */
     if ($uid && !x($_REQUEST, 'parent') && !x($_REQUEST, 'post_id')) {
         $ret = $this->item_check_service_class($uid, $_REQUEST['webpage'] == ITEM_TYPE_WEBPAGE ? true : false);
         if (!$ret['success']) {
             notice(t($ret['message']) . EOL);
             if (x($_REQUEST, 'return')) {
                 goaway(z_root() . "/" . $return_path);
             }
             killme();
         }
     }
     if ($pagetitle) {
         require_once 'library/urlify/URLify.php';
         $pagetitle = strtolower(\URLify::transliterate($pagetitle));
     }
     $item_flags = $item_restrict = 0;
     $route = '';
     $parent_item = null;
     $parent_contact = null;
     $thr_parent = '';
     $parid = 0;
     $r = false;
     if ($parent || $parent_mid) {
         if (!x($_REQUEST, 'type')) {
             $_REQUEST['type'] = 'net-comment';
         }
         if ($obj_type == ACTIVITY_OBJ_POST) {
             $obj_type = ACTIVITY_OBJ_COMMENT;
         }
         if ($parent) {
             $r = q("SELECT * FROM `item` WHERE `id` = %d LIMIT 1", intval($parent));
         } elseif ($parent_mid && $uid) {
             // This is coming from an API source, and we are logged in
             $r = q("SELECT * FROM `item` WHERE `mid` = '%s' AND `uid` = %d LIMIT 1", dbesc($parent_mid), intval($uid));
         }
         // if this isn't the real parent of the conversation, find it
         if ($r !== false && count($r)) {
             $parid = $r[0]['parent'];
             $parent_mid = $r[0]['mid'];
             if ($r[0]['id'] != $r[0]['parent']) {
                 $r = q("SELECT * FROM `item` WHERE `id` = `parent` AND `parent` = %d LIMIT 1", intval($parid));
             }
         }
         if ($r === false || !count($r)) {
             notice(t('Unable to locate original post.') . EOL);
             if (x($_REQUEST, 'return')) {
                 goaway(z_root() . "/" . $return_path);
             }
             killme();
         }
         // can_comment_on_post() needs info from the following xchan_query
         // This may be from the discover tab which means we need to correct the effective uid
         xchan_query($r, true, $r[0]['uid'] == local_channel() ? 0 : local_channel());
         $parent_item = $r[0];
         $parent = $r[0]['id'];
         // multi-level threading - preserve the info but re-parent to our single level threading
         $thr_parent = $parent_mid;
         $route = $parent_item['route'];
     }
     if (!$observer) {
         $observer = \App::get_observer();
     }
     if ($parent) {
         logger('mod_item: item_post parent=' . $parent);
         $can_comment = false;
         if (array_key_exists('owner', $parent_item) && intval($parent_item['owner']['abook_self'])) {
             $can_comment = perm_is_allowed($profile_uid, $observer['xchan_hash'], 'post_comments');
         } else {
             $can_comment = can_comment_on_post($observer['xchan_hash'], $parent_item);
         }
         if (!$can_comment) {
             notice(t('Permission denied.') . EOL);
             if (x($_REQUEST, 'return')) {
                 goaway(z_root() . "/" . $return_path);
             }
             killme();
         }
     } else {
         if (!perm_is_allowed($profile_uid, $observer['xchan_hash'], $webpage ? 'write_pages' : 'post_wall')) {
             notice(t('Permission denied.') . EOL);
             if (x($_REQUEST, 'return')) {
                 goaway(z_root() . "/" . $return_path);
             }
             killme();
         }
     }
     // is this an edited post?
     $orig_post = null;
     if ($namespace && $remote_id) {
         // It wasn't an internally generated post - see if we've got an item matching this remote service id
         $i = q("select iid from iconfig where cat = 'system' and k = '%s' and v = '%s' limit 1", dbesc($namespace), dbesc($remote_id));
         if ($i) {
             $post_id = $i[0]['iid'];
         }
     }
     $iconfig = null;
     if ($post_id) {
         $i = q("SELECT * FROM `item` WHERE `uid` = %d AND `id` = %d LIMIT 1", intval($profile_uid), intval($post_id));
         if (!count($i)) {
             killme();
         }
         $orig_post = $i[0];
         $iconfig = q("select * from iconfig where iid = %d", intval($post_id));
     }
     if (!$channel) {
         if ($uid && $uid == $profile_uid) {
             $channel = \App::get_channel();
         } else {
             // posting as yourself but not necessarily to a channel you control
             $r = q("select * from channel left join account on channel_account_id = account_id where channel_id = %d LIMIT 1", intval($profile_uid));
             if ($r) {
                 $channel = $r[0];
             }
         }
     }
     if (!$channel) {
         logger("mod_item: no channel.");
         if (x($_REQUEST, 'return')) {
             goaway(z_root() . "/" . $return_path);
         }
         killme();
     }
     $owner_xchan = null;
     $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($channel['channel_hash']));
     if ($r && count($r)) {
         $owner_xchan = $r[0];
     } else {
         logger("mod_item: no owner.");
         if (x($_REQUEST, 'return')) {
             goaway(z_root() . "/" . $return_path);
         }
         killme();
     }
     $walltowall = false;
     $walltowall_comment = false;
     if ($remote_xchan) {
         $observer = $remote_observer;
     }
     if ($observer) {
         logger('mod_item: post accepted from ' . $observer['xchan_name'] . ' for ' . $owner_xchan['xchan_name'], LOGGER_DEBUG);
         // wall-to-wall detection.
         // For top-level posts, if the author and owner are different it's a wall-to-wall
         // For comments, We need to additionally look at the parent and see if it's a wall post that originated locally.
         if ($observer['xchan_name'] != $owner_xchan['xchan_name']) {
             if ($parent_item && ($parent_item['item_wall'] && $parent_item['item_origin'])) {
                 $walltowall_comment = true;
                 $walltowall = true;
             }
             if (!$parent) {
                 $walltowall = true;
             }
         }
     }
     $acl = new \Zotlabs\Access\AccessList($channel);
     $public_policy = x($_REQUEST, 'public_policy') ? escape_tags($_REQUEST['public_policy']) : map_scope($channel['channel_r_stream'], true);
     if ($webpage) {
         $public_policy = '';
     }
     if ($public_policy) {
         $private = 1;
     }
     if ($orig_post) {
         $private = 0;
         // webpages are allowed to change ACLs after the fact. Normal conversation items aren't.
         if ($webpage) {
             $acl->set_from_array($_REQUEST);
         } else {
             $acl->set($orig_post);
             $public_policy = $orig_post['public_policy'];
             $private = $orig_post['item_private'];
         }
         if ($private || $public_policy || $acl->is_private()) {
             $private = 1;
         }
         $location = $orig_post['location'];
         $coord = $orig_post['coord'];
         $verb = $orig_post['verb'];
         $app = $orig_post['app'];
         $title = escape_tags(trim($_REQUEST['title']));
         $body = trim($_REQUEST['body']);
         $item_flags = $orig_post['item_flags'];
         $item_origin = $orig_post['item_origin'];
         $item_unseen = $orig_post['item_unseen'];
         $item_starred = $orig_post['item_starred'];
         $item_uplink = $orig_post['item_uplink'];
         $item_consensus = $orig_post['item_consensus'];
         $item_wall = $orig_post['item_wall'];
         $item_thread_top = $orig_post['item_thread_top'];
         $item_notshown = $orig_post['item_notshown'];
         $item_nsfw = $orig_post['item_nsfw'];
         $item_relay = $orig_post['item_relay'];
         $item_mentionsme = $orig_post['item_mentionsme'];
         $item_nocomment = $orig_post['item_nocomment'];
         $item_obscured = $orig_post['item_obscured'];
         $item_verified = $orig_post['item_verified'];
         $item_retained = $orig_post['item_retained'];
         $item_rss = $orig_post['item_rss'];
         $item_deleted = $orig_post['item_deleted'];
         $item_type = $orig_post['item_type'];
         $item_hidden = $orig_post['item_hidden'];
         $item_unpublished = $orig_post['item_unpublished'];
         $item_delayed = $orig_post['item_delayed'];
         $item_pending_remove = $orig_post['item_pending_remove'];
         $item_blocked = $orig_post['item_blocked'];
         $postopts = $orig_post['postopts'];
         $created = $orig_post['created'];
         $mid = $orig_post['mid'];
         $parent_mid = $orig_post['parent_mid'];
         $plink = $orig_post['plink'];
     } else {
         if (!$walltowall) {
             if (array_key_exists('contact_allow', $_REQUEST) || array_key_exists('group_allow', $_REQUEST) || array_key_exists('contact_deny', $_REQUEST) || array_key_exists('group_deny', $_REQUEST)) {
                 $acl->set_from_array($_REQUEST);
             } elseif (!$api_source) {
                 // if no ACL has been defined and we aren't using the API, the form
                 // didn't send us any parameters. This means there's no ACL or it has
                 // been reset to the default audience.
                 // If $api_source is set and there are no ACL parameters, we default
                 // to the channel permissions which were set in the ACL contructor.
                 $acl->set(array('allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => ''));
             }
         }
         $location = notags(trim($_REQUEST['location']));
         $coord = notags(trim($_REQUEST['coord']));
         $verb = notags(trim($_REQUEST['verb']));
         $title = escape_tags(trim($_REQUEST['title']));
         $body = trim($_REQUEST['body']);
         $body .= trim($_REQUEST['attachment']);
         $postopts = '';
         $private = intval($acl->is_private() || $public_policy);
         // If this is a comment, set the permissions from the parent.
         if ($parent_item) {
             $private = 0;
             $acl->set($parent_item);
             $private = intval($acl->is_private() || $parent_item['item_private']);
             $public_policy = $parent_item['public_policy'];
             $owner_hash = $parent_item['owner_xchan'];
         }
         if (!strlen($body)) {
             if ($preview) {
                 killme();
             }
             info(t('Empty post discarded.') . EOL);
             if (x($_REQUEST, 'return')) {
                 goaway(z_root() . "/" . $return_path);
             }
             killme();
         }
     }
     $expires = NULL_DATE;
     if (feature_enabled($profile_uid, 'content_expire')) {
         if (x($_REQUEST, 'expire')) {
             $expires = datetime_convert(date_default_timezone_get(), 'UTC', $_REQUEST['expire']);
             if ($expires <= datetime_convert()) {
                 $expires = NULL_DATE;
             }
         }
     }
     $mimetype = notags(trim($_REQUEST['mimetype']));
     if (!$mimetype) {
         $mimetype = 'text/bbcode';
     }
     if ($preview) {
         $body = z_input_filter($profile_uid, $body, $mimetype);
     }
     // Verify ability to use html or php!!!
     $execflag = false;
     if ($mimetype !== 'text/bbcode') {
         $z = q("select account_id, account_roles, channel_pageflags from account left join channel on channel_account_id = account_id where channel_id = %d limit 1", intval($profile_uid));
         if ($z && ($z[0]['account_roles'] & ACCOUNT_ROLE_ALLOWCODE || $z[0]['channel_pageflags'] & PAGE_ALLOWCODE)) {
             if ($uid && get_account_id() == $z[0]['account_id']) {
                 $execflag = true;
             } else {
                 notice(t('Executable content type not permitted to this channel.') . EOL);
                 if (x($_REQUEST, 'return')) {
                     goaway(z_root() . "/" . $return_path);
                 }
                 killme();
             }
         }
     }
     $gacl = $acl->get();
     $str_contact_allow = $gacl['allow_cid'];
     $str_group_allow = $gacl['allow_gid'];
     $str_contact_deny = $gacl['deny_cid'];
     $str_group_deny = $gacl['deny_gid'];
     if ($mimetype === 'text/bbcode') {
         require_once 'include/text.php';
         // Markdown doesn't work correctly. Do not re-enable unless you're willing to fix it and support it.
         // Sample that will probably give you grief - you must preserve the linebreaks
         // and provide the correct markdown interpretation and you cannot allow unfiltered HTML
         // Markdown
         // ========
         //
         // **bold** abcde
         // fghijkl
         // *italic*
         // <img src="javascript:alert('hacked');" />
         //		if($uid && $uid == $profile_uid && feature_enabled($uid,'markdown')) {
         //			require_once('include/bb2diaspora.php');
         //			$body = escape_tags(trim($body));
         //			$body = str_replace("\n",'<br />', $body);
         //			$body = preg_replace_callback('/\[share(.*?)\]/ism','\share_shield',$body);
         //			$body = diaspora2bb($body,true);
         //			$body = preg_replace_callback('/\[share(.*?)\]/ism','\share_unshield',$body);
         //		}
         // BBCODE alert: the following functions assume bbcode input
         // and will require alternatives for alternative content-types (text/html, text/markdown, text/plain, etc.)
         // we may need virtual or template classes to implement the possible alternatives
         // Work around doubled linefeeds in Tinymce 3.5b2
         // First figure out if it's a status post that would've been
         // created using tinymce. Otherwise leave it alone.
         $plaintext = true;
         //		$plaintext = ((feature_enabled($profile_uid,'richtext')) ? false : true);
         //		if((! $parent) && (! $api_source) && (! $plaintext)) {
         //			$body = fix_mce_lf($body);
         //		}
         // If we're sending a private top-level message with a single @-taggable channel as a recipient, @-tag it, if our pconfig is set.
         if (!$parent && get_pconfig($profile_uid, 'system', 'tagifonlyrecip') && substr_count($str_contact_allow, '<') == 1 && $str_group_allow == '' && $str_contact_deny == '' && $str_group_deny == '') {
             $x = q("select abook_id, abook_their_perms from abook where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc(str_replace(array('<', '>'), array('', ''), $str_contact_allow)), intval($profile_uid));
             if ($x && $x[0]['abook_their_perms'] & PERMS_W_TAGWALL) {
                 $body .= "\n\n@group+" . $x[0]['abook_id'] . "\n";
             }
         }
         /**
          * fix naked links by passing through a callback to see if this is a hubzilla site
          * (already known to us) which will get a zrl, otherwise link with url, add bookmark tag to both.
          * First protect any url inside certain bbcode tags so we don't double link it.
          */
         $body = preg_replace_callback('/\\[code(.*?)\\[\\/(code)\\]/ism', '\\red_escape_codeblock', $body);
         $body = preg_replace_callback('/\\[url(.*?)\\[\\/(url)\\]/ism', '\\red_escape_codeblock', $body);
         $body = preg_replace_callback('/\\[zrl(.*?)\\[\\/(zrl)\\]/ism', '\\red_escape_codeblock', $body);
         $body = preg_replace_callback("/([^\\]\\='" . '"' . "\\/]|^|\\#\\^)(https?\\:\\/\\/[a-zA-Z0-9\\:\\/\\-\\?\\&\\;\\.\\=\\@\\_\\~\\#\\%\$\\!\\+\\,]+)/ism", '\\red_zrl_callback', $body);
         $body = preg_replace_callback('/\\[\\$b64zrl(.*?)\\[\\/(zrl)\\]/ism', '\\red_unescape_codeblock', $body);
         $body = preg_replace_callback('/\\[\\$b64url(.*?)\\[\\/(url)\\]/ism', '\\red_unescape_codeblock', $body);
         $body = preg_replace_callback('/\\[\\$b64code(.*?)\\[\\/(code)\\]/ism', '\\red_unescape_codeblock', $body);
         // fix any img tags that should be zmg
         $body = preg_replace_callback('/\\[img(.*?)\\](.*?)\\[\\/img\\]/ism', '\\red_zrlify_img_callback', $body);
         $body = bb_translate_video($body);
         /**
          * Fold multi-line [code] sequences
          */
         $body = preg_replace('/\\[\\/code\\]\\s*\\[code\\]/ism', "\n", $body);
         $body = scale_external_images($body, false);
         // Look for tags and linkify them
         $results = linkify_tags($a, $body, $uid ? $uid : $profile_uid);
         if ($results) {
             // Set permissions based on tag replacements
             set_linkified_perms($results, $str_contact_allow, $str_group_allow, $profile_uid, $parent_item, $private);
             $post_tags = array();
             foreach ($results as $result) {
                 $success = $result['success'];
                 if ($success['replaced']) {
                     $post_tags[] = array('uid' => $profile_uid, 'ttype' => $success['termtype'], 'otype' => TERM_OBJ_POST, 'term' => $success['term'], 'url' => $success['url']);
                 }
             }
         }
         /**
          *
          * When a photo was uploaded into the message using the (profile wall) ajax 
          * uploader, The permissions are initially set to disallow anybody but the
          * owner from seeing it. This is because the permissions may not yet have been
          * set for the post. If it's private, the photo permissions should be set
          * appropriately. But we didn't know the final permissions on the post until
          * now. So now we'll look for links of uploaded photos and attachments that are in the
          * post and set them to the same permissions as the post itself.
          *
          * If the post was end-to-end encrypted we can't find images and attachments in the body,
          * use our media_str input instead which only contains these elements - but only do this
          * when encrypted content exists because the photo/attachment may have been removed from 
          * the post and we should keep it private. If it's encrypted we have no way of knowing
          * so we'll set the permissions regardless and realise that the media may not be 
          * referenced in the post. 
          *
          * What is preventing us from being able to upload photos into comments is dealing with
          * the photo and attachment permissions, since we don't always know who was in the 
          * distribution for the top level post.
          * 
          * We might be able to provide this functionality with a lot of fiddling:
          * - if the top level post is public (make the photo public)
          * - if the top level post was written by us or a wall post that belongs to us (match the top level post)
          * - if the top level post has privacy mentions, add those to the permissions.
          * - otherwise disallow the photo *or* make the photo public. This is the part that gets messy. 
          */
         if (!$preview) {
             $this->fix_attached_photo_permissions($profile_uid, $owner_xchan['xchan_hash'], strpos($body, '[/crypt]') ? $_POST['media_str'] : $body, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny);
             $this->fix_attached_file_permissions($channel, $observer['xchan_hash'], strpos($body, '[/crypt]') ? $_POST['media_str'] : $body, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny);
         }
         $attachments = '';
         $match = false;
         if (preg_match_all('/(\\[attachment\\](.*?)\\[\\/attachment\\])/', $body, $match)) {
             $attachments = array();
             $i = 0;
             foreach ($match[2] as $mtch) {
                 $attach_link = '';
                 $hash = substr($mtch, 0, strpos($mtch, ','));
                 $rev = intval(substr($mtch, strpos($mtch, ',')));
                 $r = attach_by_hash_nodata($hash, $rev);
                 if ($r['success']) {
                     $attachments[] = array('href' => z_root() . '/attach/' . $r['data']['hash'], 'length' => $r['data']['filesize'], 'type' => $r['data']['filetype'], 'title' => urlencode($r['data']['filename']), 'revision' => $r['data']['revision']);
                 }
                 $ext = substr($r['data']['filename'], strrpos($r['data']['filename'], '.'));
                 if (strpos($r['data']['filetype'], 'audio/') !== false) {
                     $attach_link = '[audio]' . z_root() . '/attach/' . $r['data']['hash'] . '/' . $r['data']['revision'] . ($ext ? $ext : '') . '[/audio]';
                 } elseif (strpos($r['data']['filetype'], 'video/') !== false) {
                     $attach_link = '[video]' . z_root() . '/attach/' . $r['data']['hash'] . '/' . $r['data']['revision'] . ($ext ? $ext : '') . '[/video]';
                 }
                 $body = str_replace($match[1][$i], $attach_link, $body);
                 $i++;
             }
         }
     }
     // BBCODE end alert
     if (strlen($categories)) {
         $cats = explode(',', $categories);
         foreach ($cats as $cat) {
             $post_tags[] = array('uid' => $profile_uid, 'ttype' => TERM_CATEGORY, 'otype' => TERM_OBJ_POST, 'term' => trim($cat), 'url' => $owner_xchan['xchan_url'] . '?f=&cat=' . urlencode(trim($cat)));
         }
     }
     if ($orig_post) {
         // preserve original tags
         $t = q("select * from term where oid = %d and otype = %d and uid = %d and ttype in ( %d, %d, %d )", intval($orig_post['id']), intval(TERM_OBJ_POST), intval($profile_uid), intval(TERM_UNKNOWN), intval(TERM_FILE), intval(TERM_COMMUNITYTAG));
         if ($t) {
             foreach ($t as $t1) {
                 $post_tags[] = array('uid' => $profile_uid, 'ttype' => $t1['type'], 'otype' => TERM_OBJ_POST, 'term' => $t1['term'], 'url' => $t1['url']);
             }
         }
     }
     $item_unseen = local_channel() != $profile_uid ? 1 : 0;
     $item_wall = $post_type === 'wall' || $post_type === 'wall-comment' ? 1 : 0;
     $item_origin = $origin ? 1 : 0;
     $item_consensus = $consensus ? 1 : 0;
     // determine if this is a wall post
     if ($parent) {
         $item_wall = $parent_item['item_wall'];
     } else {
         if (!$webpage) {
             $item_wall = 1;
         }
     }
     if ($moderated) {
         $item_blocked = ITEM_MODERATED;
     }
     if (!strlen($verb)) {
         $verb = ACTIVITY_POST;
     }
     $notify_type = $parent ? 'comment-new' : 'wall-new';
     if (!$mid) {
         $mid = $message_id ? $message_id : item_message_id();
     }
     if (!$parent_mid) {
         $parent_mid = $mid;
     }
     if ($parent_item) {
         $parent_mid = $parent_item['mid'];
     }
     // Fallback so that we alway have a thr_parent
     if (!$thr_parent) {
         $thr_parent = $mid;
     }
     $datarray = array();
     $item_thread_top = !$parent ? 1 : 0;
     if (!$plink && $item_thread_top) {
         $plink = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . $mid;
     }
     $datarray['aid'] = $channel['channel_account_id'];
     $datarray['uid'] = $profile_uid;
     $datarray['owner_xchan'] = $owner_hash ? $owner_hash : $owner_xchan['xchan_hash'];
     $datarray['author_xchan'] = $observer['xchan_hash'];
     $datarray['created'] = $created;
     $datarray['edited'] = $orig_post ? datetime_convert() : $created;
     $datarray['expires'] = $expires;
     $datarray['commented'] = $orig_post ? datetime_convert() : $created;
     $datarray['received'] = $orig_post ? datetime_convert() : $created;
     $datarray['changed'] = $orig_post ? datetime_convert() : $created;
     $datarray['mid'] = $mid;
     $datarray['parent_mid'] = $parent_mid;
     $datarray['mimetype'] = $mimetype;
     $datarray['title'] = $title;
     $datarray['body'] = $body;
     $datarray['app'] = $app;
     $datarray['location'] = $location;
     $datarray['coord'] = $coord;
     $datarray['verb'] = $verb;
     $datarray['obj_type'] = $obj_type;
     $datarray['allow_cid'] = $str_contact_allow;
     $datarray['allow_gid'] = $str_group_allow;
     $datarray['deny_cid'] = $str_contact_deny;
     $datarray['deny_gid'] = $str_group_deny;
     $datarray['item_private'] = $private;
     $datarray['item_wall'] = $item_wall;
     $datarray['attach'] = $attachments;
     $datarray['thr_parent'] = $thr_parent;
     $datarray['postopts'] = $postopts;
     $datarray['item_unseen'] = $item_unseen;
     $datarray['item_wall'] = $item_wall;
     $datarray['item_origin'] = $item_origin;
     $datarray['item_type'] = $webpage;
     $datarray['item_thread_top'] = $item_thread_top;
     $datarray['item_unseen'] = $item_unseen;
     $datarray['item_starred'] = $item_starred;
     $datarray['item_uplink'] = $item_uplink;
     $datarray['item_consensus'] = $item_consensus;
     $datarray['item_notshown'] = $item_notshown;
     $datarray['item_nsfw'] = $item_nsfw;
     $datarray['item_relay'] = $item_relay;
     $datarray['item_mentionsme'] = $item_mentionsme;
     $datarray['item_nocomment'] = $item_nocomment;
     $datarray['item_obscured'] = $item_obscured;
     $datarray['item_verified'] = $item_verified;
     $datarray['item_retained'] = $item_retained;
     $datarray['item_rss'] = $item_rss;
     $datarray['item_deleted'] = $item_deleted;
     $datarray['item_hidden'] = $item_hidden;
     $datarray['item_unpublished'] = $item_unpublished;
     $datarray['item_delayed'] = $item_delayed;
     $datarray['item_pending_remove'] = $item_pending_remove;
     $datarray['item_blocked'] = $item_blocked;
     $datarray['layout_mid'] = $layout_mid;
     $datarray['public_policy'] = $public_policy;
     $datarray['comment_policy'] = map_scope($channel['channel_w_comment']);
     $datarray['term'] = $post_tags;
     $datarray['plink'] = $plink;
     $datarray['route'] = $route;
     if ($iconfig) {
         $datarray['iconfig'] = $iconfig;
     }
     // preview mode - prepare the body for display and send it via json
     if ($preview) {
         require_once 'include/conversation.php';
         $datarray['owner'] = $owner_xchan;
         $datarray['author'] = $observer;
         $datarray['attach'] = json_encode($datarray['attach']);
         $o = conversation($a, array($datarray), 'search', false, 'preview');
         //		logger('preview: ' . $o, LOGGER_DEBUG);
         echo json_encode(array('preview' => $o));
         killme();
     }
     if ($orig_post) {
         $datarray['edit'] = true;
     }
     // suppress duplicates, *unless* you're editing an existing post. This could get picked up
     // as a duplicate if you're editing it very soon after posting it initially and you edited
     // some attribute besides the content, such as title or categories.
     if (feature_enabled($profile_uid, 'suppress_duplicates') && !$orig_post) {
         $z = q("select created from item where uid = %d and created > %s - INTERVAL %s and body = '%s' limit 1", intval($profile_uid), db_utcnow(), db_quoteinterval('2 MINUTE'), dbesc($body));
         if ($z) {
             $datarray['cancel'] = 1;
             notice(t('Duplicate post suppressed.') . EOL);
             logger('Duplicate post. Faking plugin cancel.');
         }
     }
     call_hooks('post_local', $datarray);
     if (x($datarray, 'cancel')) {
         logger('mod_item: post cancelled by plugin or duplicate suppressed.');
         if ($return_path) {
             goaway(z_root() . "/" . $return_path);
         }
         $json = array('cancel' => 1);
         $json['reload'] = z_root() . '/' . $_REQUEST['jsreload'];
         echo json_encode($json);
         killme();
     }
     if (mb_strlen($datarray['title']) > 255) {
         $datarray['title'] = mb_substr($datarray['title'], 0, 255);
     }
     if (array_key_exists('item_private', $datarray) && $datarray['item_private']) {
         $datarray['body'] = trim(z_input_filter($datarray['uid'], $datarray['body'], $datarray['mimetype']));
         if ($uid) {
             if ($channel['channel_hash'] === $datarray['author_xchan']) {
                 $datarray['sig'] = base64url_encode(rsa_sign($datarray['body'], $channel['channel_prvkey']));
                 $datarray['item_verified'] = 1;
             }
         }
     }
     if ($webpage) {
         Zlib\IConfig::Set($datarray, 'system', webpage_to_namespace($webpage), $pagetitle ? $pagetitle : substr($datarray['mid'], 0, 16), true);
     } elseif ($namespace) {
         Zlib\IConfig::Set($datarray, 'system', $namespace, $remote_id ? $remote_id : substr($datarray['mid'], 0, 16), true);
     }
     if ($orig_post) {
         $datarray['id'] = $post_id;
         $x = item_store_update($datarray, $execflag);
         if (!$parent) {
             $r = q("select * from item where id = %d", intval($post_id));
             if ($r) {
                 xchan_query($r);
                 $sync_item = fetch_post_tags($r);
                 build_sync_packet($profile_uid, array('item' => array(encode_item($sync_item[0], true))));
             }
         }
         if (!$nopush) {
             \Zotlabs\Daemon\Master::Summon(array('Notifier', 'edit_post', $post_id));
         }
         if (x($_REQUEST, 'return') && strlen($return_path)) {
             logger('return: ' . $return_path);
             goaway(z_root() . "/" . $return_path);
         }
         killme();
     } else {
         $post_id = 0;
     }
     $post = item_store($datarray, $execflag);
     $post_id = $post['item_id'];
     if ($post_id) {
         logger('mod_item: saved item ' . $post_id);
         if ($parent) {
             // only send comment notification if this is a wall-to-wall comment,
             // otherwise it will happen during delivery
             if ($datarray['owner_xchan'] != $datarray['author_xchan'] && intval($parent_item['item_wall'])) {
                 Zlib\Enotify::submit(array('type' => NOTIFY_COMMENT, 'from_xchan' => $datarray['author_xchan'], 'to_xchan' => $datarray['owner_xchan'], 'item' => $datarray, 'link' => z_root() . '/display/' . $datarray['mid'], 'verb' => ACTIVITY_POST, 'otype' => 'item', 'parent' => $parent, 'parent_mid' => $parent_item['mid']));
             }
         } else {
             $parent = $post_id;
             if ($datarray['owner_xchan'] != $datarray['author_xchan'] && $datarray['item_type'] == ITEM_TYPE_POST) {
                 Zlib\Enotify::submit(array('type' => NOTIFY_WALL, 'from_xchan' => $datarray['author_xchan'], 'to_xchan' => $datarray['owner_xchan'], 'item' => $datarray, 'link' => z_root() . '/display/' . $datarray['mid'], 'verb' => ACTIVITY_POST, 'otype' => 'item'));
             }
             if ($uid && $uid == $profile_uid && is_item_normal($datarray)) {
                 q("update channel set channel_lastpost = '%s' where channel_id = %d", dbesc(datetime_convert()), intval($uid));
             }
         }
         // photo comments turn the corresponding item visible to the profile wall
         // This way we don't see every picture in your new photo album posted to your wall at once.
         // They will show up as people comment on them.
         if (intval($parent_item['item_hidden'])) {
             $r = q("UPDATE item SET item_hidden = 0 WHERE id = %d", intval($parent_item['id']));
         }
     } else {
         logger('mod_item: unable to retrieve post that was just stored.');
         notice(t('System error. Post not saved.') . EOL);
         goaway(z_root() . "/" . $return_path);
         // NOTREACHED
     }
     if ($parent && $parent != $post_id) {
         // Store the comment signature information in case we need to relay to Diaspora
         //$ditem = $datarray;
         //$ditem['author'] = $observer;
         //store_diaspora_comment_sig($ditem,$channel,$parent_item, $post_id, (($walltowall_comment) ? 1 : 0));
     } else {
         $r = q("select * from item where id = %d", intval($post_id));
         if ($r) {
             xchan_query($r);
             $sync_item = fetch_post_tags($r);
             build_sync_packet($profile_uid, array('item' => array(encode_item($sync_item[0], true))));
         }
     }
     $datarray['id'] = $post_id;
     $datarray['llink'] = z_root() . '/display/' . $channel['channel_address'] . '/' . $post_id;
     call_hooks('post_local_end', $datarray);
     if (!$nopush) {
         \Zotlabs\Daemon\Master::Summon(array('Notifier', $notify_type, $post_id));
     }
     logger('post_complete');
     // figure out how to return, depending on from whence we came
     if ($api_source) {
         return $post;
     }
     if ($return_path) {
         goaway(z_root() . "/" . $return_path);
     }
     $json = array('success' => 1);
     if (x($_REQUEST, 'jsreload') && strlen($_REQUEST['jsreload'])) {
         $json['reload'] = z_root() . '/' . $_REQUEST['jsreload'];
     }
     logger('post_json: ' . print_r($json, true), LOGGER_DEBUG);
     echo json_encode($json);
     killme();
     // NOTREACHED
 }
Exemple #14
0
 public static function run($argc, $argv)
 {
     logger('onepoll: start');
     if ($argc > 1 && intval($argv[1])) {
         $contact_id = intval($argv[1]);
     }
     if (!$contact_id) {
         logger('onepoll: no contact');
         return;
     }
     $d = datetime_convert();
     $contacts = q("SELECT abook.*, xchan.*, account.*\n\t\t\tFROM abook LEFT JOIN account on abook_account = account_id left join xchan on xchan_hash = abook_xchan \n\t\t\twhere abook_id = %d\n\t\t\tand abook_pending = 0 and abook_archived = 0 and abook_blocked = 0 and abook_ignored = 0\n\t\t\tAND (( account_flags = %d ) OR ( account_flags = %d )) limit 1", intval($contact_id), intval(ACCOUNT_OK), intval(ACCOUNT_UNVERIFIED));
     if (!$contacts) {
         logger('onepoll: abook_id not found: ' . $contact_id);
         return;
     }
     $contact = $contacts[0];
     $t = $contact['abook_updated'];
     $importer_uid = $contact['abook_channel'];
     $r = q("SELECT * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1", intval($importer_uid));
     if (!$r) {
         return;
     }
     $importer = $r[0];
     logger("onepoll: poll: ({$contact['id']}) IMPORTER: {$importer['xchan_name']}, CONTACT: {$contact['xchan_name']}");
     $last_update = $contact['abook_updated'] === $contact['abook_created'] || $contact['abook_updated'] <= NULL_DATE ? datetime_convert('UTC', 'UTC', 'now - 7 days') : datetime_convert('UTC', 'UTC', $contact['abook_updated'] . ' - 2 days');
     if ($contact['xchan_network'] === 'rss') {
         logger('onepoll: processing feed ' . $contact['xchan_name'], LOGGER_DEBUG);
         handle_feed($importer['channel_id'], $contact_id, $contact['xchan_hash']);
         q("update abook set abook_connected = '%s' where abook_id = %d", dbesc(datetime_convert()), intval($contact['abook_id']));
         return;
     }
     if ($contact['xchan_network'] !== 'zot') {
         return;
     }
     // update permissions
     $x = zot_refresh($contact, $importer);
     $responded = false;
     $updated = datetime_convert();
     $connected = datetime_convert();
     if (!$x) {
         // mark for death by not updating abook_connected, this is caught in include/poller.php
         q("update abook set abook_updated = '%s' where abook_id = %d", dbesc($updated), intval($contact['abook_id']));
     } else {
         q("update abook set abook_updated = '%s', abook_connected = '%s' where abook_id = %d", dbesc($updated), dbesc($connected), intval($contact['abook_id']));
         $responded = true;
     }
     if (!$responded) {
         return;
     }
     if ($contact['xchan_connurl']) {
         $fetch_feed = true;
         $x = null;
         // They haven't given us permission to see their stream
         $can_view_stream = intval(get_abconfig($importer_uid, $contact['abook_xchan'], 'their_perms', 'view_stream'));
         if (!$can_view_stream) {
             $fetch_feed = false;
         }
         // we haven't given them permission to send us their stream
         $can_send_stream = intval(get_abconfig($importer_uid, $contact['abook_xchan'], 'my_perms', 'send_stream'));
         if (!$can_send_stream) {
             $fetch_feed = false;
         }
         if ($fetch_feed) {
             $feedurl = str_replace('/poco/', '/zotfeed/', $contact['xchan_connurl']);
             $feedurl .= '?f=&mindate=' . urlencode($last_update);
             $x = z_fetch_url($feedurl);
             logger('feed_update: ' . print_r($x, true), LOGGER_DATA);
         }
         if ($x && $x['success']) {
             $total = 0;
             logger('onepoll: feed update ' . $contact['xchan_name'] . ' ' . $feedurl);
             $j = json_decode($x['body'], true);
             if ($j['success'] && $j['messages']) {
                 foreach ($j['messages'] as $message) {
                     $results = process_delivery(array('hash' => $contact['xchan_hash']), get_item_elements($message), array(array('hash' => $importer['xchan_hash'])), false);
                     logger('onepoll: feed_update: process_delivery: ' . print_r($results, true), LOGGER_DATA);
                     $total++;
                 }
                 logger("onepoll: {$total} messages processed");
             }
         }
     }
     // update the poco details for this connection
     if ($contact['xchan_connurl']) {
         $r = q("SELECT xlink_id from xlink \n\t\t\t\twhere xlink_xchan = '%s' and xlink_updated > %s - INTERVAL %s and xlink_static = 0 limit 1", intval($contact['xchan_hash']), db_utcnow(), db_quoteinterval('1 DAY'));
         if (!$r) {
             poco_load($contact['xchan_hash'], $contact['xchan_connurl']);
         }
     }
     return;
 }
Exemple #15
0
/**
 * @brief Create a birthday event for any connections with a birthday in the next 1-2 weeks.
 *
 * Update the year so that we don't create another event until next year.
 */
function update_birthdays()
{
    require_once 'include/event.php';
    require_once 'include/permissions.php';
    $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash \n\t\tWHERE abook_dob > %s + interval %s and abook_dob < %s + interval %s", db_utcnow(), db_quoteinterval('7 day'), db_utcnow(), db_quoteinterval('14 day'));
    if ($r) {
        foreach ($r as $rr) {
            if (!perm_is_allowed($rr['abook_channel'], $rr['xchan_hash'], 'send_stream')) {
                continue;
            }
            $ev = array();
            $ev['uid'] = $rr['abook_channel'];
            $ev['account'] = $rr['abook_account'];
            $ev['event_xchan'] = $rr['xchan_hash'];
            $ev['dtstart'] = datetime_convert('UTC', 'UTC', $rr['abook_dob']);
            $ev['dtend'] = datetime_convert('UTC', 'UTC', $rr['abook_dob'] . ' + 1 day ');
            $ev['adjust'] = intval(feature_enabled($rr['abook_channel'], 'smart_birthdays'));
            $ev['summary'] = sprintf(t('%1$s\'s birthday'), $rr['xchan_name']);
            $ev['description'] = sprintf(t('Happy Birthday %1$s'), '[zrl=' . $rr['xchan_url'] . ']' . $rr['xchan_name'] . '[/zrl]');
            $ev['etype'] = 'birthday';
            $z = event_store_event($ev);
            if ($z) {
                $item_id = event_store_item($ev, $z);
                q("update abook set abook_dob = '%s' where abook_id = %d", dbesc(intval($rr['abook_dob']) + 1 . substr($rr['abook_dob'], 4)), intval($rr['abook_id']));
            }
        }
    }
}
Exemple #16
0
 public static function run($argc, $argv)
 {
     cli_startup();
     // perform final cleanup on previously delete items
     $r = q("select id from item where item_deleted = 1 and item_pending_remove = 0 and changed < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('10 DAY'));
     if ($r) {
         foreach ($r as $rr) {
             drop_item($rr['id'], false, DROPITEM_PHASE2);
         }
     }
     // physically remove anything that has been deleted for more than two months
     /** @FIXME - this is a wretchedly inefficient query */
     $r = q("delete from item where item_pending_remove = 1 and changed < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('36 DAY'));
     /** @FIXME make this optional as it could have a performance impact on large sites */
     if (intval(get_config('system', 'optimize_items'))) {
         q("optimize table item");
     }
     logger('expire: start', LOGGER_DEBUG);
     $site_expire = get_config('system', 'default_expire_days');
     logger('site_expire: ' . $site_expire);
     $r = q("SELECT channel_id, channel_system, channel_address, channel_expire_days from channel where true");
     if ($r) {
         foreach ($r as $rr) {
             // expire the sys channel separately
             if (intval($rr['channel_system'])) {
                 continue;
             }
             // service class default (if non-zero) over-rides the site default
             $service_class_expire = service_class_fetch($rr['channel_id'], 'expire_days');
             if (intval($service_class_expire)) {
                 $channel_expire = $service_class_expire;
             } else {
                 $channel_expire = $site_expire;
             }
             if (intval($channel_expire) && intval($channel_expire) < intval($rr['channel_expire_days']) || intval($rr['channel_expire_days'] == 0)) {
                 $expire_days = $channel_expire;
             } else {
                 $expire_days = $rr['channel_expire_days'];
             }
             // if the site or service class expiration is non-zero and less than person expiration, use that
             logger('Expire: ' . $rr['channel_address'] . ' interval: ' . $expire_days, LOGGER_DEBUG);
             item_expire($rr['channel_id'], $expire_days);
         }
     }
     $x = get_sys_channel();
     if ($x) {
         // this should probably just fetch the channel_expire_days from the sys channel,
         // but there's no convenient way to set it.
         $expire_days = get_config('system', 'sys_expire_days');
         if ($expire_days === false) {
             $expire_days = 30;
         }
         if (intval($site_expire) && intval($site_expire) < intval($expire_days)) {
             $expire_days = $site_expire;
         }
         logger('Expire: sys interval: ' . $expire_days, LOGGER_DEBUG);
         if ($expire_days) {
             item_expire($x['channel_id'], $expire_days);
         }
         logger('Expire: sys: done', LOGGER_DEBUG);
     }
 }
Exemple #17
0
 function purge($type, $interval)
 {
     q("delete from verify where type = '%s' and created < %s - INTERVAL %s", dbesc($type), db_utcnow(), db_quoteinterval($interval));
 }
function statistics_json_cron($a, $b)
{
    logger('statistics_json_cron: cron_start');
    $r = q("select count(channel_id) as total_users from channel left join account on account_id = channel_account_id\n\t\twhere account_flags = 0 ");
    if ($r) {
        $total_users = $r[0]['total_users'];
    }
    $r = q("select channel_id from channel left join account on account_id = channel_account_id\n\t\twhere account_flags = 0 and account_lastlog > %s - INTERVAL %s", db_utcnow(), db_quoteinterval('6 MONTH'));
    if ($r) {
        $s = '';
        foreach ($r as $rr) {
            if ($s) {
                $s .= ',';
            }
            $s .= intval($rr['channel_id']);
        }
        $x = q("select uid from item where uid in ( {$s} ) and item_wall !=  0 and created > %s - INTERVAL %s group by uid", db_utcnow(), db_quoteinterval('6 MONTH'));
        if ($x) {
            $active_users_halfyear = count($x);
        }
    }
    $r = q("select channel_id from channel left join account on account_id = channel_account_id\n\t\twhere account_flags = 0 and account_lastlog > %s - INTERVAL %s", db_utcnow(), db_quoteinterval('1 MONTH'));
    if ($r) {
        $s = '';
        foreach ($r as $rr) {
            if ($s) {
                $s .= ',';
            }
            $s .= intval($rr['channel_id']);
        }
        $x = q("select uid from item where uid in ( {$s} ) and item_wall != 0 and created > %s - INTERVAL %s group by uid", db_utcnow(), db_quoteinterval('1 MONTH'));
        if ($x) {
            $active_users_monthly = count($x);
        }
    }
    set_config('statistics_json', 'total_users', $total_users);
    set_config('statistics_json', 'active_users_halfyear', $active_users_halfyear);
    set_config('statistics_json', 'active_users_monthly', $active_users_monthly);
    $posts = q("SELECT COUNT(*) AS local_posts FROM `item` WHERE item_wall != 0 ");
    if (!is_array($posts)) {
        $local_posts = -1;
    } else {
        $local_posts = $posts[0]["local_posts"];
    }
    set_config('statistics_json', 'local_posts', $local_posts);
    $wordpress = false;
    $r = q("select * from addon where hidden = 0 and name = 'wppost'");
    if ($r) {
        $wordpress = true;
    }
    set_config('statistics_json', 'wordpress', intval($wordpress));
    $twitter = false;
    $r = q("select * from addon where hidden = 0 and name = 'twitter'");
    if ($r) {
        $twitter = true;
    }
    set_config('statistics_json', 'twitter', intval($twitter));
    // Now trying to register
    $url = "http://the-federation.info/register/" . $a->get_hostname();
    $ret = z_fetch_url($url);
    logger('statistics_json_cron: registering answer: ' . print_r($ret, true), LOGGER_DEBUG);
    logger('statistics_json_cron: cron_end');
}
Exemple #19
0
function register_content(&$a)
{
    $registration_is = '';
    $other_sites = '';
    if (get_config('system', 'register_policy') == REGISTER_CLOSED) {
        require_once 'mod/pubsites.php';
        return pubsites_content($a);
    }
    if (get_config('system', 'register_policy') == REGISTER_APPROVE) {
        $registration_is = t('Registration on this site/hub is by approval only.');
        $other_sites = t('<a href="pubsites">Register at another affiliated site/hub</a>');
    }
    $max_dailies = intval(get_config('system', 'max_daily_registrations'));
    if ($max_dailies) {
        $r = q("select count(account_id) as total from account where account_created > %s - INTERVAL %s", db_utcnow(), db_quoteinterval('1 day'));
        if ($r && $r[0]['total'] >= $max_dailies) {
            logger('max daily registrations exceeded.');
            notice(t('This site has exceeded the number of allowed daily account registrations. Please try again tomorrow.') . EOL);
            return;
        }
    }
    // Configurable terms of service link
    $tosurl = get_config('system', 'tos_url');
    if (!$tosurl) {
        $tosurl = $a->get_baseurl() . '/help/TermsOfService';
    }
    $toslink = '<a href="' . $tosurl . '" >' . t('Terms of Service') . '</a>';
    // Configurable whether to restrict age or not - default is based on international legal requirements
    // This can be relaxed if you are on a restricted server that does not share with public servers
    if (get_config('system', 'no_age_restriction')) {
        $label_tos = sprintf(t('I accept the %s for this website'), $toslink);
    } else {
        $label_tos = sprintf(t('I am over 13 years of age and accept the %s for this website'), $toslink);
    }
    $enable_tos = 1 - intval(get_config('system', 'no_termsofservice'));
    $email = x($_REQUEST, 'email') ? strip_tags(trim($_REQUEST['email'])) : "";
    $password = x($_REQUEST, 'password') ? trim($_REQUEST['password']) : "";
    $password2 = x($_REQUEST, 'password2') ? trim($_REQUEST['password2']) : "";
    $invite_code = x($_REQUEST, 'invite_code') ? strip_tags(trim($_REQUEST['invite_code'])) : "";
    require_once 'include/bbcode.php';
    $o = replace_macros(get_markup_template('register.tpl'), array('$title' => t('Registration'), '$reg_is' => $registration_is, '$registertext' => bbcode(get_config('system', 'register_text')), '$other_sites' => $other_sites, '$invitations' => get_config('system', 'invitation_only'), '$invite_desc' => t('Membership on this site is by invitation only.'), '$label_invite' => t('Please enter your invitation code'), '$invite_code' => $invite_code, '$label_email' => t('Your email address'), '$label_pass1' => t('Choose a password'), '$label_pass2' => t('Please re-enter your password'), '$label_tos' => $label_tos, '$enable_tos' => $enable_tos, '$email' => $email, '$pass1' => $password, '$pass2' => $password2, '$submit' => t('Register')));
    return $o;
}
Exemple #20
0
function update_suggestions()
{
    $dirmode = get_config('system', 'directory_mode');
    if ($dirmode === false) {
        $dirmode = DIRECTORY_MODE_NORMAL;
    }
    if ($dirmode == DIRECTORY_MODE_PRIMARY || $dirmode == DIRECTORY_MODE_STANDALONE) {
        $url = z_root() . '/sitelist';
    } else {
        $directory = find_upstream_directory($dirmode);
        $url = $directory['url'] . '/sitelist';
    }
    if (!$url) {
        return;
    }
    $ret = z_fetch_url($url);
    if ($ret['success']) {
        // We will grab fresh data once a day via the poller. Remove anything over a week old because
        // the targets may have changed their preferences and don't want to be suggested - and they
        // may have simply gone away.
        $r = q("delete from xlink where xlink_xchan = '' and xlink_updated < %s - INTERVAL %s and xlink_static = 0", db_utcnow(), db_quoteinterval('7 DAY'));
        $j = json_decode($ret['body'], true);
        if ($j && $j['success']) {
            foreach ($j['entries'] as $host) {
                poco_load('', $host['url'] . '/poco');
            }
        }
    }
}
Exemple #21
0
 public static function run($argc, $argv)
 {
     $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. Cron deferred to next scheduled run.');
             return;
         }
     }
     // Check for a lockfile.  If it exists, but is over an hour old, it's stale.  Ignore it.
     $lockfile = 'store/[data]/cron';
     if (file_exists($lockfile) && filemtime($lockfile) > time() - 3600 && !get_config('system', 'override_cron_lockfile')) {
         logger("cron: Already running");
         return;
     }
     // Create a lockfile.  Needs two vars, but $x doesn't need to contain anything.
     file_put_contents($lockfile, $x);
     logger('cron: start');
     // run queue delivery process in the background
     Master::Summon(array('Queue'));
     Master::Summon(array('Poller'));
     // 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 > '2001-01-01 00:00:00' and expires < %s \n\t\t\tand item_deleted = 0 ", db_utcnow());
     if ($r) {
         require_once 'include/items.php';
         foreach ($r as $rr) {
             drop_item($rr['id'], false);
         }
     }
     // delete expired access tokens
     $r = q("select atoken_id from atoken where atoken_expires > '%s' and atoken_expires < %s", dbesc(NULL_DATE), db_utcnow());
     if ($r) {
         require_once 'include/security.php';
         foreach ($r as $rr) {
             atoken_delete($rr['atoken_id']);
         }
     }
     // 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) {
             Master::Summon(array('Directory', $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) {
                 $z = q("select * from item where id = %d", intval($message_id));
                 if ($z) {
                     xchan_query($z);
                     $sync_item = fetch_post_tags($z);
                     build_sync_packet($sync_item[0]['uid'], ['item' => [encode_item($sync_item[0], true)]]);
                 }
                 Master::Summon(array('Notifier', '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'));
     if ($d2 != $d1 && $h1 == $h2) {
         Master::Summon(array('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\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\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')) {
         Master::Summon(array('Externals'));
     }
     $generation = 0;
     $restart = false;
     if ($argc > 1 && $argv[1] == 'restart') {
         $restart = true;
         $generation = intval($argv[2]);
         if (!$generation) {
             killme();
         }
     }
     reload_plugins();
     $d = datetime_convert();
     // TODO check to see if there are any cronhooks before wasting a process
     if (!$restart) {
         Master::Summon(array('Cronhooks'));
     }
     set_config('system', 'lastcron', datetime_convert());
     //All done - clear the lockfile
     @unlink($lockfile);
     return;
 }
Exemple #22
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;
}
Exemple #23
0
function item_expire($uid, $days)
{
    if (!$uid || $days < 1) {
        return;
    }
    // $expire_network_only = save your own wall posts
    // and just expire conversations started by others
    // do not enable this until we can pass bulk delete messages through zot
    //	$expire_network_only = get_pconfig($uid,'expire','network_only');
    $expire_network_only = 1;
    $sql_extra = intval($expire_network_only) ? " AND item_wall = 0 " : "";
    $expire_limit = get_config('system', 'expire_limit');
    if (!intval($expire_limit)) {
        $expire_limit = 5000;
    }
    $item_normal = item_normal();
    $r = q("SELECT id FROM item\n\t\tWHERE uid = %d\n\t\tAND created < %s - INTERVAL %s\n\t\tAND item_retained = 0\n\t\tAND item_thread_top = 1\n\t\tAND resource_type = ''\n\t\tAND item_starred = 0\n\t\t{$sql_extra} {$item_normal} LIMIT {$expire_limit} ", intval($uid), db_utcnow(), db_quoteinterval(intval($days) . ' DAY'));
    if (!$r) {
        return;
    }
    $r = fetch_post_tags($r, true);
    foreach ($r as $item) {
        // don't expire filed items
        $terms = get_terms_oftype($item['term'], TERM_FILE);
        if ($terms) {
            retain_item($item['id']);
            continue;
        }
        drop_item($item['id'], false);
    }
    //	Zotlabs\Daemon\Master::Summon(array('Notifier','expire',$uid));
}
Exemple #24
0
function register_content(&$a)
{
    $registration_is = '';
    $other_sites = '';
    if (get_config('system', 'register_policy') == REGISTER_CLOSED) {
        if (get_config('system', 'directory_mode') == DIRECTORY_MODE_STANDALONE) {
            notice(t('Registration on this site is disabled.') . EOL);
            return;
        }
        require_once 'mod/pubsites.php';
        return pubsites_content($a);
    }
    if (get_config('system', 'register_policy') == REGISTER_APPROVE) {
        $registration_is = t('Registration on this site/hub is by approval only.');
        $other_sites = t('<a href="pubsites">Register at another affiliated site/hub</a>');
    }
    $max_dailies = intval(get_config('system', 'max_daily_registrations'));
    if ($max_dailies) {
        $r = q("select count(account_id) as total from account where account_created > %s - INTERVAL %s", db_utcnow(), db_quoteinterval('1 day'));
        if ($r && $r[0]['total'] >= $max_dailies) {
            logger('max daily registrations exceeded.');
            notice(t('This site has exceeded the number of allowed daily account registrations. Please try again tomorrow.') . EOL);
            return;
        }
    }
    // Configurable terms of service link
    $tosurl = get_config('system', 'tos_url');
    if (!$tosurl) {
        $tosurl = $a->get_baseurl() . '/help/TermsOfService';
    }
    $toslink = '<a href="' . $tosurl . '" >' . t('Terms of Service') . '</a>';
    // Configurable whether to restrict age or not - default is based on international legal requirements
    // This can be relaxed if you are on a restricted server that does not share with public servers
    if (get_config('system', 'no_age_restriction')) {
        $label_tos = sprintf(t('I accept the %s for this website'), $toslink);
    } else {
        $label_tos = sprintf(t('I am over 13 years of age and accept the %s for this website'), $toslink);
    }
    $enable_tos = 1 - intval(get_config('system', 'no_termsofservice'));
    $email = x($_REQUEST, 'email') ? strip_tags(trim($_REQUEST['email'])) : "";
    $password = x($_REQUEST, 'password') ? trim($_REQUEST['password']) : "";
    $password2 = x($_REQUEST, 'password2') ? trim($_REQUEST['password2']) : "";
    $invite_code = x($_REQUEST, 'invite_code') ? strip_tags(trim($_REQUEST['invite_code'])) : "";
    $name = x($_REQUEST, 'name') ? escape_tags(trim($_REQUEST['name'])) : "";
    $nickname = x($_REQUEST, 'nickname') ? strip_tags(trim($_REQUEST['nickname'])) : "";
    $privacy_role = x($_REQUEST, 'permissions_role') ? $_REQUEST['permissions_role'] : "";
    $auto_create = get_config('system', 'auto_channel_create');
    $default_role = get_config('system', 'default_permissions_role');
    require_once 'include/bbcode.php';
    $o = replace_macros(get_markup_template('register.tpl'), array('$title' => t('Registration'), '$reg_is' => $registration_is, '$registertext' => bbcode(get_config('system', 'register_text')), '$other_sites' => $other_sites, '$invitations' => get_config('system', 'invitation_only'), '$invite_desc' => t('Membership on this site is by invitation only.'), '$label_invite' => t('Please enter your invitation code'), '$invite_code' => $invite_code, '$auto_create' => $auto_create, '$label_name' => t('Channel Name'), '$help_name' => t('Enter your name'), '$label_nick' => t('Choose a short nickname'), '$nick_desc' => t('Your nickname will be used to create an easily remembered channel address (like an email address) which you can share with others.'), '$name' => $name, '$help_role' => t('Please choose a channel type (such as social networking or community forum) and privacy requirements so we can select the best permissions for you'), '$role' => array('permissions_role', t('Channel Type'), $privacy_role ? $privacy_role : 'social', '<a href="help/roles" target="_blank">' . t('Read more about roles') . '</a>', get_roles()), '$default_role' => $default_role, '$nickname' => $nickname, '$submit' => t('Create'), '$label_email' => t('Your email address'), '$label_pass1' => t('Choose a password'), '$label_pass2' => t('Please re-enter your password'), '$label_tos' => $label_tos, '$enable_tos' => $enable_tos, '$email' => $email, '$pass1' => $password, '$pass2' => $password2, '$submit' => t('Register')));
    return $o;
}
Exemple #25
0
 function init()
 {
     $result = array();
     $notifs = array();
     $result['notify'] = 0;
     $result['home'] = 0;
     $result['network'] = 0;
     $result['intros'] = 0;
     $result['mail'] = 0;
     $result['register'] = 0;
     $result['events'] = 0;
     $result['events_today'] = 0;
     $result['birthdays'] = 0;
     $result['birthdays_today'] = 0;
     $result['all_events'] = 0;
     $result['all_events_today'] = 0;
     $result['notice'] = array();
     $result['info'] = array();
     $t0 = dba_timer();
     header("content-type: application/json");
     $vnotify = false;
     $item_normal = item_normal();
     if (local_channel()) {
         $vnotify = get_pconfig(local_channel(), 'system', 'vnotify');
         $evdays = intval(get_pconfig(local_channel(), 'system', 'evdays'));
         $ob_hash = get_observer_hash();
     }
     // if unset show all visual notification types
     if ($vnotify === false) {
         $vnotify = -1;
     }
     if ($evdays < 1) {
         $evdays = 3;
     }
     /**
      * If you have several windows open to this site and switch to a different channel
      * in one of them, the others may get into a confused state showing you a page or options 
      * on that page which were only valid under the old identity. You session has changed.
      * Therefore we send a notification of this fact back to the browser where it is picked up
      * in javascript and which reloads the page it is on so that it is valid under the context
      * of the now current channel. 
      */
     $result['invalid'] = intval($_GET['uid']) && intval($_GET['uid']) != local_channel() ? 1 : 0;
     /**
      * Send all system messages (alerts) to the browser.
      * Some are marked as informational and some represent
      * errors or serious notifications. These typically
      * will popup on the current page (no matter what page it is)
      */
     if (x($_SESSION, 'sysmsg')) {
         foreach ($_SESSION['sysmsg'] as $m) {
             $result['notice'][] = array('message' => $m);
         }
         unset($_SESSION['sysmsg']);
     }
     if (x($_SESSION, 'sysmsg_info')) {
         foreach ($_SESSION['sysmsg_info'] as $m) {
             $result['info'][] = array('message' => $m);
         }
         unset($_SESSION['sysmsg_info']);
     }
     if (!($vnotify & VNOTIFY_INFO)) {
         $result['info'] = array();
     }
     if (!($vnotify & VNOTIFY_ALERT)) {
         $result['notice'] = array();
     }
     if (\App::$install) {
         echo json_encode($result);
         killme();
     }
     /**
      * Update chat presence indication (if applicable)
      */
     if (get_observer_hash() && !$result['invalid']) {
         $r = q("select cp_id, cp_room from chatpresence where cp_xchan = '%s' and cp_client = '%s' and cp_room = 0 limit 1", dbesc(get_observer_hash()), dbesc($_SERVER['REMOTE_ADDR']));
         $basic_presence = false;
         if ($r) {
             $basic_presence = true;
             q("update chatpresence set cp_last = '%s' where cp_id = %d", dbesc(datetime_convert()), intval($r[0]['cp_id']));
         }
         if (!$basic_presence) {
             q("insert into chatpresence ( cp_xchan, cp_last, cp_status, cp_client)\n\t\t\t\t\tvalues( '%s', '%s', '%s', '%s' ) ", dbesc(get_observer_hash()), dbesc(datetime_convert()), dbesc('online'), dbesc($_SERVER['REMOTE_ADDR']));
         }
     }
     /**
      * Chatpresence continued... if somebody hasn't pinged recently, they've most likely left the page
      * and shouldn't count as online anymore. We allow an expection for bots.
      */
     q("delete from chatpresence where cp_last < %s - INTERVAL %s and cp_client != 'auto' ", db_utcnow(), db_quoteinterval('3 MINUTE'));
     if (!local_channel() || $result['invalid']) {
         echo json_encode($result);
         killme();
     }
     /**
      * Everything following is only permitted under the context of a locally authenticated site member.
      */
     /**
      * Handle "mark all xyz notifications read" requests.
      */
     // mark all items read
     if (x($_REQUEST, 'markRead') && local_channel()) {
         switch ($_REQUEST['markRead']) {
             case 'network':
                 $r = q("update item set item_unseen = 0 where item_unseen = 1 and uid = %d", intval(local_channel()));
                 break;
             case 'home':
                 $r = q("update item set item_unseen = 0 where item_unseen = 1 and item_wall = 1  and uid = %d", intval(local_channel()));
                 break;
             case 'messages':
                 $r = q("update mail set mail_seen = 1 where mail_seen = 0 and channel_id = %d ", intval(local_channel()));
                 break;
             case 'all_events':
                 $r = q("update event set `ignore` = 1 where `ignore` = 0 and uid = %d AND start < '%s' AND start > '%s' ", intval(local_channel()), dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + ' . intval($evdays) . ' days')), dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days')));
                 break;
             case 'notify':
                 $r = q("update notify set seen = 1 where uid = %d", intval(local_channel()));
                 break;
             default:
                 break;
         }
     }
     if (x($_REQUEST, 'markItemRead') && local_channel()) {
         $r = q("update item set item_unseen = 0 where parent = %d and uid = %d", intval($_REQUEST['markItemRead']), intval(local_channel()));
     }
     /**
      * URL ping/something will return detail for "something", e.g. a json list with which to populate a notification
      * dropdown menu.
      */
     if (argc() > 1 && argv(1) === 'notify') {
         $t = q("select count(*) as total from notify where uid = %d and seen = 0", intval(local_channel()));
         if ($t && intval($t[0]['total']) > 49) {
             $z = q("select * from notify where uid = %d\n\t\t\t\t\tand seen = 0 order by date desc limit 50", intval(local_channel()));
         } else {
             $z1 = q("select * from notify where uid = %d\n\t\t\t\t\tand seen = 0 order by date desc limit 50", intval(local_channel()));
             $z2 = q("select * from notify where uid = %d\n\t\t\t\t\tand seen = 1 order by date desc limit %d", intval(local_channel()), intval(50 - intval($t[0]['total'])));
             $z = array_merge($z1, $z2);
         }
         if (count($z)) {
             foreach ($z as $zz) {
                 $notifs[] = array('notify_link' => z_root() . '/notify/view/' . $zz['id'], 'name' => $zz['name'], 'url' => $zz['url'], 'photo' => $zz['photo'], 'when' => relative_date($zz['date']), 'hclass' => $zz['seen'] ? 'notify-seen' : 'notify-unseen', 'message' => strip_tags(bbcode($zz['msg'])));
             }
         }
         echo json_encode(array('notify' => $notifs));
         killme();
     }
     if (argc() > 1 && argv(1) === 'messages') {
         $channel = \App::get_channel();
         $t = q("select mail.*, xchan.* from mail left join xchan on xchan_hash = from_xchan \n\t\t\t\twhere channel_id = %d and mail_seen = 0 and mail_deleted = 0 \n\t\t\t\tand from_xchan != '%s' order by created desc limit 50", intval(local_channel()), dbesc($channel['channel_hash']));
         if ($t) {
             foreach ($t as $zz) {
                 $notifs[] = array('notify_link' => z_root() . '/mail/' . $zz['id'], 'name' => $zz['xchan_name'], 'url' => $zz['xchan_url'], 'photo' => $zz['xchan_photo_s'], 'when' => relative_date($zz['created']), 'hclass' => intval($zz['mail_seen']) ? 'notify-seen' : 'notify-unseen', 'message' => t('sent you a private message'));
             }
         }
         echo json_encode(array('notify' => $notifs));
         killme();
     }
     if (argc() > 1 && (argv(1) === 'network' || argv(1) === 'home')) {
         $result = array();
         $r = q("SELECT * FROM item\n\t\t\t\tWHERE item_unseen = 1 and uid = %d {$item_normal}\n\t\t\t\tand author_xchan != '%s' ORDER BY created DESC limit 300", intval(local_channel()), dbesc($ob_hash));
         if ($r) {
             xchan_query($r);
             foreach ($r as $item) {
                 if (argv(1) === 'home' && !intval($item['item_wall'])) {
                     continue;
                 }
                 $result[] = format_notification($item);
             }
         }
         //		logger('ping (network||home): ' . print_r($result, true), LOGGER_DATA);
         echo json_encode(array('notify' => $result));
         killme();
     }
     if (argc() > 1 && argv(1) === 'intros') {
         $result = array();
         $r = q("SELECT * FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ORDER BY abook_created DESC LIMIT 50", intval(local_channel()));
         if ($r) {
             foreach ($r as $rr) {
                 $result[] = array('notify_link' => z_root() . '/connections/ifpending', 'name' => $rr['xchan_name'], 'url' => $rr['xchan_url'], 'photo' => $rr['xchan_photo_s'], 'when' => relative_date($rr['abook_created']), 'hclass' => 'notify-unseen', 'message' => t('added your channel'));
             }
         }
         logger('ping (intros): ' . print_r($result, true), LOGGER_DATA);
         echo json_encode(array('notify' => $result));
         killme();
     }
     if (argc() > 1 && argv(1) === 'all_events') {
         $bd_format = t('g A l F d');
         // 8 AM Friday January 18
         $result = array();
         $r = q("SELECT * FROM event left join xchan on event_xchan = xchan_hash\n\t\t\t\tWHERE `event`.`uid` = %d AND start < '%s' AND start > '%s' and `ignore` = 0\n\t\t\t\tand type in ( 'event', 'birthday' )\n\t\t\t\tORDER BY `start` DESC LIMIT 1000", intval(local_channel()), dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + ' . intval($evdays) . ' days')), dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days')));
         if ($r) {
             foreach ($r as $rr) {
                 if ($rr['adjust']) {
                     $md = datetime_convert('UTC', date_default_timezone_get(), $rr['start'], 'Y/m');
                 } else {
                     $md = datetime_convert('UTC', 'UTC', $rr['start'], 'Y/m');
                 }
                 $strt = datetime_convert('UTC', $rr['adjust'] ? date_default_timezone_get() : 'UTC', $rr['start']);
                 $today = substr($strt, 0, 10) === datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y-m-d') ? true : false;
                 $when = day_translate(datetime_convert('UTC', $rr['adjust'] ? date_default_timezone_get() : 'UTC', $rr['start'], $bd_format)) . ($today ? ' ' . t('[today]') : '');
                 $result[] = array('notify_link' => z_root() . '/events', 'name' => $rr['xchan_name'], 'url' => $rr['xchan_url'], 'photo' => $rr['xchan_photo_s'], 'when' => $when, 'hclass' => 'notify-unseen', 'message' => t('posted an event'));
             }
         }
         logger('ping (all_events): ' . print_r($result, true), LOGGER_DATA);
         echo json_encode(array('notify' => $result));
         killme();
     }
     /**
      * Normal ping - just the counts, no detail
      */
     if ($vnotify & VNOTIFY_SYSTEM) {
         $t = q("select count(*) as total from notify where uid = %d and seen = 0", intval(local_channel()));
         if ($t) {
             $result['notify'] = intval($t[0]['total']);
         }
     }
     $t1 = dba_timer();
     if ($vnotify & (VNOTIFY_NETWORK | VNOTIFY_CHANNEL)) {
         $r = q("SELECT id, item_wall FROM item\n\t\t\t\tWHERE item_unseen = 1 and uid = %d\n\t\t\t\t{$item_normal}\n\t\t\t\tand author_xchan != '%s'", intval(local_channel()), dbesc($ob_hash));
         if ($r) {
             $arr = array('items' => $r);
             call_hooks('network_ping', $arr);
             foreach ($r as $it) {
                 if (intval($it['item_wall'])) {
                     $result['home']++;
                 } else {
                     $result['network']++;
                 }
             }
         }
     }
     if (!($vnotify & VNOTIFY_NETWORK)) {
         $result['network'] = 0;
     }
     if (!($vnotify & VNOTIFY_CHANNEL)) {
         $result['home'] = 0;
     }
     $t2 = dba_timer();
     if ($vnotify & VNOTIFY_INTRO) {
         $intr = q("SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ", intval(local_channel()));
         $t3 = dba_timer();
         if ($intr) {
             $result['intros'] = intval($intr[0]['total']);
         }
     }
     $t4 = dba_timer();
     $channel = \App::get_channel();
     if ($vnotify & VNOTIFY_MAIL) {
         $mails = q("SELECT count(id) as total from mail\n\t\t\t\tWHERE channel_id = %d AND mail_seen = 0 and from_xchan != '%s' ", intval(local_channel()), dbesc($channel['channel_hash']));
         if ($mails) {
             $result['mail'] = intval($mails[0]['total']);
         }
     }
     if ($vnotify & VNOTIFY_REGISTER) {
         if (\App::$config['system']['register_policy'] == REGISTER_APPROVE && is_site_admin()) {
             $regs = q("SELECT count(account_id) as total from account where (account_flags & %d) > 0", intval(ACCOUNT_PENDING));
             if ($regs) {
                 $result['register'] = intval($regs[0]['total']);
             }
         }
     }
     $t5 = dba_timer();
     if ($vnotify & (VNOTIFY_EVENT | VNOTIFY_EVENTTODAY | VNOTIFY_BIRTHDAY)) {
         $events = q("SELECT type, start, adjust FROM `event`\n\t\t\t\tWHERE `event`.`uid` = %d AND start < '%s' AND start > '%s' and `ignore` = 0\n\t\t\t\tand type in ( 'event', 'birthday' )\n\t\t\t\tORDER BY `start` ASC ", intval(local_channel()), dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now + ' . intval($evdays) . ' days')), dbesc(datetime_convert('UTC', date_default_timezone_get(), 'now - 1 days')));
         if ($events) {
             $result['all_events'] = count($events);
             if ($result['all_events']) {
                 $str_now = datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y-m-d');
                 foreach ($events as $x) {
                     $bd = false;
                     if ($x['type'] === 'birthday') {
                         $result['birthdays']++;
                         $bd = true;
                     } else {
                         $result['events']++;
                     }
                     if (datetime_convert('UTC', intval($x['adjust']) ? date_default_timezone_get() : 'UTC', $x['start'], 'Y-m-d') === $str_now) {
                         $result['all_events_today']++;
                         if ($bd) {
                             $result['birthdays_today']++;
                         } else {
                             $result['events_today']++;
                         }
                     }
                 }
             }
         }
     }
     if (!($vnotify & VNOTIFY_EVENT)) {
         $result['all_events'] = $result['events'] = 0;
     }
     if (!($vnotify & VNOTIFY_EVENTTODAY)) {
         $result['all_events_today'] = $result['events_today'] = 0;
     }
     if (!($vnotify & VNOTIFY_BIRTHDAY)) {
         $result['birthdays'] = 0;
     }
     $x = json_encode($result);
     $t6 = dba_timer();
     //	logger('ping timer: ' . sprintf('%01.4f %01.4f %01.4f %01.4f %01.4f %01.4f',$t6 - $t5, $t5 - $t4, $t4 - $t3, $t3 - $t2, $t2 - $t1, $t1 - $t0));
     echo $x;
     killme();
 }
Exemple #26
0
function notifier_run($argv, $argc)
{
    cli_startup();
    $a = get_app();
    require_once "session.php";
    require_once "datetime.php";
    require_once 'include/items.php';
    require_once 'include/bbcode.php';
    if ($argc < 3) {
        return;
    }
    logger('notifier: invoked: ' . print_r($argv, true), LOGGER_DEBUG);
    $cmd = $argv[1];
    $item_id = $argv[2];
    $extra = $argc > 3 ? $argv[3] : null;
    if (!$item_id) {
        return;
    }
    require_once 'include/identity.php';
    $sys = get_sys_channel();
    if ($cmd == 'permission_update') {
        // Get the recipient
        $r = q("select abook.*, hubloc.* from abook \n\t\t\tleft join hubloc on hubloc_hash = abook_xchan\n\t\t\twhere abook_id = %d and not ( abook_flags & %d ) > 0 \n\t\t\tand not (hubloc_flags & %d) > 0  and not (hubloc_status & %d) > 0 limit 1", intval($item_id), intval(ABOOK_FLAG_SELF), intval(HUBLOC_FLAGS_DELETED), intval(HUBLOC_OFFLINE));
        if ($r) {
            // Get the sender
            $s = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1", intval($r[0]['abook_channel']));
            if ($s) {
                if ($r[0]['hubloc_network'] === 'diaspora' || $r[0]['hubloc_network'] === 'friendica-over-diaspora') {
                    require_once 'include/diaspora.php';
                    diaspora_share($s[0], $r[0]);
                } else {
                    // send a refresh message to each hub they have registered here
                    $h = q("select * from hubloc where hubloc_hash = '%s' \n\t\t\t\t\t\tand not (hubloc_flags & %d) > 0  and not (hubloc_status & %d) > 0", dbesc($r[0]['hubloc_hash']), intval(HUBLOC_FLAGS_DELETED), intval(HUBLOC_OFFLINE));
                    if ($h) {
                        foreach ($h as $hh) {
                            $data = zot_build_packet($s[0], 'refresh', array(array('guid' => $hh['hubloc_guid'], 'guid_sig' => $hh['hubloc_guid_sig'], 'url' => $hh['hubloc_url'])));
                            if ($data) {
                                $result = zot_zot($hh['hubloc_callback'], $data);
                                // if immediate delivery failed, stick it in the queue to try again later.
                                if (!$result['success']) {
                                    $hash = random_string();
                                    q("insert into outq ( outq_hash, outq_account, outq_channel, outq_driver, outq_posturl, outq_async, outq_created, outq_updated, outq_notify, outq_msg ) \n\t\t\t\t\t\t\t\t\t\tvalues ( '%s', %d, %d, '%s', '%s', %d, '%s', '%s', '%s', '%s' )", dbesc($hash), intval($s[0]['channel_account_id']), intval($s[0]['channel_id']), dbesc('zot'), dbesc($hh['hubloc_callback']), intval(1), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($data), dbesc(''));
                                }
                            }
                        }
                    }
                }
            }
        }
        return;
    }
    $expire = false;
    $request = false;
    $mail = false;
    $fsuggest = false;
    $top_level = false;
    $location = false;
    $recipients = array();
    $url_recipients = array();
    $normal_mode = true;
    $packet_type = 'undefined';
    if ($cmd === 'mail') {
        $normal_mode = false;
        $mail = true;
        $private = true;
        $message = q("SELECT * FROM `mail` WHERE `id` = %d LIMIT 1", intval($item_id));
        if (!$message) {
            return;
        }
        xchan_mail_query($message[0]);
        $uid = $message[0]['channel_id'];
        $recipients[] = $message[0]['from_xchan'];
        // include clones
        $recipients[] = $message[0]['to_xchan'];
        $item = $message[0];
        $encoded_item = encode_mail($item);
        $s = q("select * from channel where channel_id = %d limit 1", intval($item['channel_id']));
        if ($s) {
            $channel = $s[0];
        }
    } elseif ($cmd === 'request') {
        $channel_id = $item_id;
        $xchan = $argv[3];
        $request_message_id = $argv[4];
        $s = q("select * from channel where channel_id = %d limit 1", intval($channel_id));
        if ($s) {
            $channel = $s[0];
        }
        $private = true;
        $recipients[] = $xchan;
        $packet_type = 'request';
        $normal_mode = false;
    } elseif ($cmd === 'expire') {
        // FIXME
        // This will require a special zot packet containing a list of item message_id's to be expired.
        // This packet will be public, since we cannot selectively deliver here.
        // We need the handling on this end to create the array, and the handling on the remote end
        // to verify permissions (for each item) and process it. Until this is complete, the expire feature will be disabled.
        return;
        $normal_mode = false;
        $expire = true;
        $items = q("SELECT * FROM item WHERE uid = %d AND ( item_flags & %d )>0\n\t\t\tAND ( item_restrict & %d )>0 AND `changed` > %s - INTERVAL %s", intval($item_id), intval(ITEM_WALL), intval(ITEM_DELETED), db_utcnow(), db_quoteinterval('10 MINUTE'));
        $uid = $item_id;
        $item_id = 0;
        if (!$items) {
            return;
        }
    } elseif ($cmd === 'suggest') {
        $normal_mode = false;
        $fsuggest = true;
        $suggest = q("SELECT * FROM `fsuggest` WHERE `id` = %d LIMIT 1", intval($item_id));
        if (!count($suggest)) {
            return;
        }
        $uid = $suggest[0]['uid'];
        $recipients[] = $suggest[0]['cid'];
        $item = $suggest[0];
    } elseif ($cmd === 'refresh_all') {
        logger('notifier: refresh_all: ' . $item_id);
        $s = q("select * from channel where channel_id = %d limit 1", intval($item_id));
        if ($s) {
            $channel = $s[0];
        }
        $uid = $item_id;
        $recipients = array();
        $r = q("select abook_xchan from abook where abook_channel = %d", intval($item_id));
        if ($r) {
            foreach ($r as $rr) {
                $recipients[] = $rr['abook_xchan'];
            }
        }
        $private = false;
        $packet_type = 'refresh';
    } elseif ($cmd === 'location') {
        logger('notifier: location: ' . $item_id);
        $s = q("select * from channel where channel_id = %d limit 1", intval($item_id));
        if ($s) {
            $channel = $s[0];
        }
        $uid = $item_id;
        $recipients = array();
        $r = q("select abook_xchan from abook where abook_channel = %d", intval($item_id));
        if ($r) {
            foreach ($r as $rr) {
                $recipients[] = $rr['abook_xchan'];
            }
        }
        $encoded_item = array('locations' => zot_encode_locations($channel), 'type' => 'location', 'encoding' => 'zot');
        $target_item = array('aid' => $channel['channel_account_id'], 'uid' => $channel['channel_id']);
        $private = false;
        $packet_type = 'location';
        $location = true;
    } elseif ($cmd === 'purge_all') {
        logger('notifier: purge_all: ' . $item_id);
        $s = q("select * from channel where channel_id = %d limit 1", intval($item_id));
        if ($s) {
            $channel = $s[0];
        }
        $uid = $item_id;
        $recipients = array();
        $r = q("select abook_xchan from abook where abook_channel = %d", intval($item_id));
        if ($r) {
            foreach ($r as $rr) {
                $recipients[] = $rr['abook_xchan'];
            }
        }
        $private = false;
        $packet_type = 'purge';
    } else {
        // Normal items
        // Fetch the target item
        $r = q("SELECT * FROM item WHERE id = %d and parent != 0 LIMIT 1", intval($item_id));
        if (!$r) {
            return;
        }
        xchan_query($r);
        $r = fetch_post_tags($r);
        $target_item = $r[0];
        $deleted_item = false;
        if ($target_item['item_restrict'] & ITEM_DELETED) {
            logger('notifier: target item ITEM_DELETED', LOGGER_DEBUG);
            $deleted_item = true;
        }
        $unforwardable = ITEM_UNPUBLISHED | ITEM_DELAYED_PUBLISH | ITEM_WEBPAGE | ITEM_BUILDBLOCK | ITEM_PDL;
        if ($target_item['item_restrict'] & $unforwardable) {
            logger('notifier: target item not forwardable: flags ' . $target_item['item_restrict'], LOGGER_DEBUG);
            return;
        }
        $s = q("select * from channel where channel_id = %d limit 1", intval($target_item['uid']));
        if ($s) {
            $channel = $s[0];
        }
        if ($channel['channel_hash'] !== $target_item['author_xchan'] && $channel['channel_hash'] !== $target_item['owner_xchan']) {
            logger("notifier: Sending channel {$channel['channel_hash']} is not owner {$target_item['owner_xchan']} or author {$target_item['author_xchan']}");
            return;
        }
        if ($target_item['id'] == $target_item['parent']) {
            $parent_item = $target_item;
            $top_level_post = true;
        } else {
            // fetch the parent item
            $r = q("SELECT * from item where id = %d order by id asc", intval($target_item['parent']));
            if (!$r) {
                return;
            }
            xchan_query($r);
            $r = fetch_post_tags($r);
            $parent_item = $r[0];
            $top_level_post = false;
        }
        // avoid looping of discover items 12/4/2014
        if ($sys && $parent_item['uid'] == $sys['channel_id']) {
            return;
        }
        $encoded_item = encode_item($target_item);
        // Send comments to the owner to re-deliver to everybody in the conversation
        // We only do this if the item in question originated on this site. This prevents looping.
        // To clarify, a site accepting a new comment is responsible for sending it to the owner for relay.
        // Relaying should never be initiated on a post that arrived from elsewhere.
        // We should normally be able to rely on ITEM_ORIGIN, but start_delivery_chain() incorrectly set this
        // flag on comments for an extended period. So we'll also call comment_local_origin() which looks at
        // the hostname in the message_id and provides a second (fallback) opinion.
        $relay_to_owner = !$top_level_post && $target_item['item_flags'] & ITEM_ORIGIN && comment_local_origin($target_item) ? true : false;
        $uplink = false;
        // $cmd === 'relay' indicates the owner is sending it to the original recipients
        // don't allow the item in the relay command to relay to owner under any circumstances, it will loop
        logger('notifier: relay_to_owner: ' . ($relay_to_owner ? 'true' : 'false'), LOGGER_DATA);
        logger('notifier: top_level_post: ' . ($top_level_post ? 'true' : 'false'), LOGGER_DATA);
        logger('notifier: target_item_flags: ' . $target_item['item_flags'] . ' ' . ($target_item['item_flags'] & ITEM_ORIGIN ? 'true' : 'false'), LOGGER_DATA);
        // tag_deliver'd post which needs to be sent back to the original author
        if ($cmd === 'uplink' && $parent_item['item_flags'] & ITEM_UPLINK && !$top_level_post) {
            logger('notifier: uplink');
            $uplink = true;
        }
        if (($relay_to_owner || $uplink) && $cmd !== 'relay') {
            logger('notifier: followup relay', LOGGER_DEBUG);
            $recipients = array($uplink ? $parent_item['source_xchan'] : $parent_item['owner_xchan']);
            $private = true;
            if (!$encoded_item['flags']) {
                $encoded_item['flags'] = array();
            }
            $encoded_item['flags'][] = 'relay';
        } else {
            logger('notifier: normal distribution', LOGGER_DEBUG);
            if ($cmd === 'relay') {
                logger('notifier: owner relay');
            }
            // if our parent is a tag_delivery recipient, uplink to the original author causing
            // a delivery fork.
            if ($parent_item['item_flags'] & ITEM_UPLINK && !$top_level_post && $cmd !== 'uplink') {
                logger('notifier: uplinking this item');
                proc_run('php', 'include/notifier.php', 'uplink', $item_id);
            }
            $private = false;
            $recipients = collect_recipients($parent_item, $private);
            // FIXME add any additional recipients such as mentions, etc.
            // don't send deletions onward for other people's stuff
            // TODO verify this is needed - copied logic from same place in old code
            if ($target_item['item_restrict'] & ITEM_DELETED && !($target_item['item_flags'] & ITEM_WALL)) {
                logger('notifier: ignoring delete notification for non-wall item');
                return;
            }
        }
    }
    $walltowall = $top_level_post && $channel['xchan_hash'] === $target_item['author_xchan'] ? true : false;
    // Generic delivery section, we have an encoded item and recipients
    // Now start the delivery process
    $x = $encoded_item;
    $x['title'] = 'private';
    $x['body'] = 'private';
    logger('notifier: encoded item: ' . print_r($x, true), LOGGER_DATA);
    stringify_array_elms($recipients);
    if (!$recipients) {
        return;
    }
    //	logger('notifier: recipients: ' . print_r($recipients,true));
    $env_recips = $private ? array() : null;
    $details = q("select xchan_hash, xchan_instance_url, xchan_network, xchan_addr, xchan_guid, xchan_guid_sig from xchan where xchan_hash in (" . implode(',', $recipients) . ")");
    $recip_list = array();
    if ($details) {
        foreach ($details as $d) {
            // If the recipient is federated from a traditional network they won't be able to
            // handle nomadic identity. If we're publishing from a site that they aren't
            // directly connected with, ignore them.
            // FIXME: make sure we run through a notifier loop on the hub they're connected
            // with if this post comes in from a different hub - so that we will deliver to them.
            // On the down side, these channels will stop working if the hub they connected with
            // goes down permanently, as they are (doh) not nomadic.
            if ($d['xchan_instance_url'] && $d['xchan_instance_url'] != z_root()) {
                continue;
            }
            $recip_list[] = $d['xchan_addr'] . ' (' . $d['xchan_hash'] . ')';
            if ($private) {
                $env_recips[] = array('guid' => $d['xchan_guid'], 'guid_sig' => $d['xchan_guid_sig'], 'hash' => $d['xchan_hash']);
            }
        }
    }
    if ($private && !$env_recips) {
        // shouldn't happen
        logger('notifier: private message with no envelope recipients.' . print_r($argv, true));
    }
    logger('notifier: recipients (may be delivered to more if public): ' . print_r($recip_list, true), LOGGER_DEBUG);
    // Now we have collected recipients (except for external mentions, FIXME)
    // Let's reduce this to a set of hubs.
    logger('notifier: hub choice: ' . intval($relay_to_owner) . ' ' . intval($private) . ' ' . $cmd, LOGGER_DEBUG);
    // FIXME: I think we need to remove the private bit or this clause will never execute. Needs more coffee to think it through.
    // We may in fact have to send it to clones in case the one we pick recently died.
    if ($relay_to_owner && !$private && $cmd !== 'relay') {
        // If sending a followup to the post owner, only send it to one channel clone - to avoid race conditions.
        // In this case we'll pick the most recently contacted hub, as their primary might be down and the most
        // recently contacted has the best chance of being alive.
        // For private posts or uplinks we have to do things differently as only the sending clone will have the recipient list.
        // We have to send to all clone channels of the owner to find out who has the definitive list. Posts with
        // item_private set (but no ACL list) will return empty recipients (except for the sender and owner) in
        // collect_recipients() above. The end result is we should get only one delivery per delivery chain if we
        // aren't the owner or author.
        $r = q("select hubloc_guid, hubloc_url, hubloc_sitekey, hubloc_network, hubloc_flags, hubloc_callback, hubloc_host from hubloc \n\t\t\twhere hubloc_hash in (" . implode(',', $recipients) . ") order by hubloc_connected desc limit 1");
    } else {
        $r = q("select hubloc_guid, hubloc_url, hubloc_sitekey, hubloc_network, hubloc_flags, hubloc_callback, hubloc_host from hubloc \n\t\t\twhere hubloc_hash in (" . implode(',', $recipients) . ") and not (hubloc_flags & %d) > 0  and not (hubloc_status & %d) > 0", intval(HUBLOC_FLAGS_DELETED), intval(HUBLOC_OFFLINE));
    }
    if (!$r) {
        logger('notifier: no hubs');
        return;
    }
    $hubs = $r;
    /**
     * Reduce the hubs to those that are unique. For zot hubs, we need to verify uniqueness by the sitekey, since it may have been 
     * a re-install which has not yet been detected and pruned.
     * For other networks which don't have or require sitekeys, we'll have to use the URL
     */
    $hublist = array();
    // this provides an easily printable list for the logs
    $dhubs = array();
    // delivery hubs where we store our resulting unique array
    $keys = array();
    // array of keys to check uniquness for zot hubs
    $urls = array();
    // array of urls to check uniqueness of hubs from other networks
    foreach ($hubs as $hub) {
        if ($hub['hubloc_network'] == 'zot') {
            if (!in_array($hub['hubloc_sitekey'], $keys)) {
                $hublist[] = $hub['hubloc_host'];
                $dhubs[] = $hub;
                $keys[] = $hub['hubloc_sitekey'];
            }
        } else {
            if (!in_array($hub['hubloc_url'], $urls)) {
                $hublist[] = $hub['hubloc_host'];
                $dhubs[] = $hub;
                $urls[] = $hub['hubloc_url'];
            }
        }
    }
    logger('notifier: will notify/deliver to these hubs: ' . print_r($hublist, true), LOGGER_DEBUG);
    $interval = get_config('system', 'delivery_interval') !== false ? intval(get_config('system', 'delivery_interval')) : 2;
    $deliveries_per_process = intval(get_config('system', 'delivery_batch_count'));
    if ($deliveries_per_process <= 0) {
        $deliveries_per_process = 1;
    }
    $deliver = array();
    foreach ($dhubs as $hub) {
        if (defined('DIASPORA_RELIABILITY_EMULATION')) {
            $cointoss = mt_rand(0, 2);
            if ($cointoss == 2) {
                continue;
            }
        }
        if ($hub['hubloc_network'] === 'diaspora' || $hub['hubloc_network'] === 'friendica-over-diaspora') {
            if (!get_config('system', 'diaspora_enabled')) {
                continue;
            }
            require_once 'include/diaspora.php';
            diaspora_process_outbound(array('channel' => $channel, 'env_recips' => $env_recips, 'recipients' => $recipients, 'item' => $item, 'target_item' => $target_item, 'hub' => $hub, 'top_level_post' => $top_level_post, 'private' => $private, 'followup' => $followup, 'relay_to_owner' => $relay_to_owner, 'uplink' => $uplink, 'cmd' => $cmd, 'expire' => $expire, 'mail' => $mail, 'location' => $location, 'fsuggest' => $fsuggest, 'request' => $request, 'normal_mode' => $normal_mode, 'packet_type' => $packet_type, 'walltowall' => $walltowall));
            continue;
        }
        // default: zot protocol
        $hash = random_string();
        if ($packet_type === 'refresh' || $packet_type === 'purge') {
            $n = zot_build_packet($channel, $packet_type);
            q("insert into outq ( outq_hash, outq_account, outq_channel, outq_driver, outq_posturl, outq_async, outq_created, outq_updated, outq_notify, outq_msg ) values ( '%s', %d, %d, '%s', '%s', %d, '%s', '%s', '%s', '%s' )", dbesc($hash), intval($channel['channel_account_id']), intval($channel['channel_id']), dbesc('zot'), dbesc($hub['hubloc_callback']), intval(1), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($n), dbesc(''));
        } elseif ($packet_type === 'request') {
            $n = zot_build_packet($channel, 'request', $env_recips, $hub['hubloc_sitekey'], $hash, array('message_id' => $request_message_id));
            q("insert into outq ( outq_hash, outq_account, outq_channel, outq_driver, outq_posturl, outq_async, outq_created, outq_updated, outq_notify, outq_msg ) values ( '%s', %d, %d, '%s', '%s', %d, '%s', '%s', '%s', '%s' )", dbesc($hash), intval($channel['channel_account_id']), intval($channel['channel_id']), dbesc('zot'), dbesc($hub['hubloc_callback']), intval(1), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($n), dbesc(''));
        } else {
            $n = zot_build_packet($channel, 'notify', $env_recips, $private ? $hub['hubloc_sitekey'] : null, $hash);
            q("insert into outq ( outq_hash, outq_account, outq_channel, outq_driver, outq_posturl, outq_async, outq_created, outq_updated, outq_notify, outq_msg ) values ( '%s', %d, %d, '%s', '%s', %d, '%s', '%s', '%s', '%s' )", dbesc($hash), intval($target_item['aid']), intval($target_item['uid']), dbesc('zot'), dbesc($hub['hubloc_callback']), intval(1), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($n), dbesc(json_encode($encoded_item)));
        }
        $deliver[] = $hash;
        if (count($deliver) >= $deliveries_per_process) {
            proc_run('php', 'include/deliver.php', $deliver);
            $deliver = array();
            if ($interval) {
                @time_sleep_until(microtime(true) + (double) $interval);
            }
        }
    }
    // catch any stragglers
    if (count($deliver)) {
        proc_run('php', 'include/deliver.php', $deliver);
    }
    logger('notifier: basic loop complete.', LOGGER_DEBUG);
    if ($normal_mode) {
        call_hooks('notifier_normal', $target_item);
    }
    call_hooks('notifier_end', $target_item);
    logger('notifer: complete.');
    return;
}
function random_profile()
{
    $randfunc = db_getfunc('rand');
    $checkrandom = get_config('randprofile', 'check');
    // False by default
    $retryrandom = intval(get_config('randprofile', 'retry'));
    if ($retryrandom == 0) {
        $retryrandom = 5;
    }
    for ($i = 0; $i < $retryrandom; $i++) {
        $r = q("select xchan_url from xchan left join hubloc on hubloc_hash = xchan_hash where hubloc_connected > %s - interval %s order by {$randfunc} limit 1", db_utcnow(), db_quoteinterval('30 day'));
        if (!$r) {
            return '';
        }
        // Couldn't get a random channel
        if ($checkrandom) {
            $x = z_fetch_url($r[0]['xchan_url']);
            if ($x['success']) {
                return $r[0]['xchan_url'];
            } else {
                logger('Random channel turned out to be bad.');
            }
        } else {
            return $r[0]['xchan_url'];
        }
    }
    return '';
}
Exemple #28
0
function notifier_run($argv, $argc)
{
    cli_startup();
    $a = get_app();
    require_once "session.php";
    require_once "datetime.php";
    require_once 'include/items.php';
    require_once 'include/bbcode.php';
    if ($argc < 3) {
        return;
    }
    logger('notifier: invoked: ' . print_r($argv, true), LOGGER_DEBUG);
    $cmd = $argv[1];
    $item_id = $argv[2];
    $extra = $argc > 3 ? $argv[3] : null;
    if (!$item_id) {
        return;
    }
    require_once 'include/identity.php';
    $sys = get_sys_channel();
    $deliveries = array();
    $dead_hubs = array();
    $dh = q("select site_url from site where site_dead = 1");
    if (dh) {
        foreach ($dh as $dead) {
            $dead_hubs[] = $dead['site_url'];
        }
    }
    if ($cmd == 'permission_update') {
        // Get the recipient
        $r = q("select abook.*, hubloc.* from abook \n\t\t\tleft join hubloc on hubloc_hash = abook_xchan\n\t\t\twhere abook_id = %d and abook_self = 0\n\t\t\tand not (hubloc_flags & %d) > 0  and not (hubloc_status & %d) > 0 limit 1", intval($item_id), intval(HUBLOC_FLAGS_DELETED), intval(HUBLOC_OFFLINE));
        if ($r) {
            // Get the sender
            $s = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1", intval($r[0]['abook_channel']));
            if ($s) {
                $perm_update = array('sender' => $s[0], 'recipient' => $r[0], 'success' => false, 'deliveries' => '');
                call_hooks('permissions_update', $perm_update);
                if ($perm_update['success'] && $perm_update['deliveries']) {
                    $deliveries[] = $perm_update['deliveries'];
                }
                if (!$perm_update['success']) {
                    // send a refresh message to each hub they have registered here
                    $h = q("select * from hubloc where hubloc_hash = '%s' \n\t\t\t\t\t\tand not (hubloc_flags & %d) > 0  and not (hubloc_status & %d) > 0", dbesc($r[0]['hubloc_hash']), intval(HUBLOC_FLAGS_DELETED), intval(HUBLOC_OFFLINE));
                    if ($h) {
                        foreach ($h as $hh) {
                            if (in_array($hh['hubloc_url'], $dead_hubs)) {
                                logger('skipping dead hub: ' . $hh['hubloc_url'], LOGGER_DEBUG);
                                continue;
                            }
                            $data = zot_build_packet($s[0], 'refresh', array(array('guid' => $hh['hubloc_guid'], 'guid_sig' => $hh['hubloc_guid_sig'], 'url' => $hh['hubloc_url'])));
                            if ($data) {
                                $hash = random_string();
                                q("insert into outq ( outq_hash, outq_account, outq_channel, outq_driver, outq_posturl, outq_async, outq_created, outq_updated, outq_notify, outq_msg ) \n\t\t\t\t\t\t\t\t\tvalues ( '%s', %d, %d, '%s', '%s', %d, '%s', '%s', '%s', '%s' )", dbesc($hash), intval($s[0]['channel_account_id']), intval($s[0]['channel_id']), dbesc('zot'), dbesc($hh['hubloc_callback']), intval(1), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($data), dbesc(''));
                                $deliveries[] = $hash;
                            }
                        }
                    }
                }
                if ($deliveries) {
                    do_delivery($deliveries);
                }
            }
        }
        return;
    }
    $expire = false;
    $request = false;
    $mail = false;
    $fsuggest = false;
    $top_level = false;
    $location = false;
    $recipients = array();
    $url_recipients = array();
    $normal_mode = true;
    $packet_type = 'undefined';
    if ($cmd === 'mail') {
        $normal_mode = false;
        $mail = true;
        $private = true;
        $message = q("SELECT * FROM `mail` WHERE `id` = %d LIMIT 1", intval($item_id));
        if (!$message) {
            return;
        }
        xchan_mail_query($message[0]);
        $uid = $message[0]['channel_id'];
        $recipients[] = $message[0]['from_xchan'];
        // include clones
        $recipients[] = $message[0]['to_xchan'];
        $item = $message[0];
        $encoded_item = encode_mail($item);
        $s = q("select * from channel where channel_id = %d limit 1", intval($item['channel_id']));
        if ($s) {
            $channel = $s[0];
        }
    } elseif ($cmd === 'request') {
        $channel_id = $item_id;
        $xchan = $argv[3];
        $request_message_id = $argv[4];
        $s = q("select * from channel where channel_id = %d limit 1", intval($channel_id));
        if ($s) {
            $channel = $s[0];
        }
        $private = true;
        $recipients[] = $xchan;
        $packet_type = 'request';
        $normal_mode = false;
    } elseif ($cmd === 'expire') {
        // FIXME
        // This will require a special zot packet containing a list of item message_id's to be expired.
        // This packet will be public, since we cannot selectively deliver here.
        // We need the handling on this end to create the array, and the handling on the remote end
        // to verify permissions (for each item) and process it. Until this is complete, the expire feature will be disabled.
        return;
        $normal_mode = false;
        $expire = true;
        $items = q("SELECT * FROM item WHERE uid = %d AND item_wall = 1\n\t\t\tAND item_deleted = 1 AND `changed` > %s - INTERVAL %s", intval($item_id), db_utcnow(), db_quoteinterval('10 MINUTE'));
        $uid = $item_id;
        $item_id = 0;
        if (!$items) {
            return;
        }
    } elseif ($cmd === 'suggest') {
        $normal_mode = false;
        $fsuggest = true;
        $suggest = q("SELECT * FROM `fsuggest` WHERE `id` = %d LIMIT 1", intval($item_id));
        if (!count($suggest)) {
            return;
        }
        $uid = $suggest[0]['uid'];
        $recipients[] = $suggest[0]['cid'];
        $item = $suggest[0];
    } elseif ($cmd === 'refresh_all') {
        logger('notifier: refresh_all: ' . $item_id);
        $s = q("select * from channel where channel_id = %d limit 1", intval($item_id));
        if ($s) {
            $channel = $s[0];
        }
        $uid = $item_id;
        $recipients = array();
        $r = q("select abook_xchan from abook where abook_channel = %d", intval($item_id));
        if ($r) {
            foreach ($r as $rr) {
                $recipients[] = $rr['abook_xchan'];
            }
        }
        $private = false;
        $packet_type = 'refresh';
    } elseif ($cmd === 'location') {
        logger('notifier: location: ' . $item_id);
        $s = q("select * from channel where channel_id = %d limit 1", intval($item_id));
        if ($s) {
            $channel = $s[0];
        }
        $uid = $item_id;
        $recipients = array();
        $r = q("select abook_xchan from abook where abook_channel = %d", intval($item_id));
        if ($r) {
            foreach ($r as $rr) {
                $recipients[] = $rr['abook_xchan'];
            }
        }
        $encoded_item = array('locations' => zot_encode_locations($channel), 'type' => 'location', 'encoding' => 'zot');
        $target_item = array('aid' => $channel['channel_account_id'], 'uid' => $channel['channel_id']);
        $private = false;
        $packet_type = 'location';
        $location = true;
    } elseif ($cmd === 'purge_all') {
        logger('notifier: purge_all: ' . $item_id);
        $s = q("select * from channel where channel_id = %d limit 1", intval($item_id));
        if ($s) {
            $channel = $s[0];
        }
        $uid = $item_id;
        $recipients = array();
        $r = q("select abook_xchan from abook where abook_channel = %d", intval($item_id));
        if ($r) {
            foreach ($r as $rr) {
                $recipients[] = $rr['abook_xchan'];
            }
        }
        $private = false;
        $packet_type = 'purge';
    } else {
        // Normal items
        // Fetch the target item
        $r = q("SELECT * FROM item WHERE id = %d and parent != 0 LIMIT 1", intval($item_id));
        if (!$r) {
            return;
        }
        xchan_query($r);
        $r = fetch_post_tags($r);
        $target_item = $r[0];
        $deleted_item = false;
        if (intval($target_item['item_deleted'])) {
            logger('notifier: target item ITEM_DELETED', LOGGER_DEBUG);
            $deleted_item = true;
        }
        if (intval($target_item['item_type']) != ITEM_TYPE_POST) {
            logger('notifier: target item not forwardable: type ' . $target_item['item_type'], LOGGER_DEBUG);
            return;
        }
        if (intval($target_item['item_unpublished']) || intval($target_item['item_delayed_publish'])) {
            logger('notifier: target item not published, so not forwardable', LOGGER_DEBUG);
            return;
        }
        if (strpos($target_item['postopts'], 'nodeliver') !== false) {
            logger('notifier: target item is undeliverable', LOGGER_DEBUG);
            return;
        }
        $s = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1", intval($target_item['uid']));
        if ($s) {
            $channel = $s[0];
        }
        if ($channel['channel_hash'] !== $target_item['author_xchan'] && $channel['channel_hash'] !== $target_item['owner_xchan']) {
            logger("notifier: Sending channel {$channel['channel_hash']} is not owner {$target_item['owner_xchan']} or author {$target_item['author_xchan']}");
            return;
        }
        if ($target_item['id'] == $target_item['parent']) {
            $parent_item = $target_item;
            $top_level_post = true;
        } else {
            // fetch the parent item
            $r = q("SELECT * from item where id = %d order by id asc", intval($target_item['parent']));
            if (!$r) {
                return;
            }
            if (strpos($r[0]['postopts'], 'nodeliver') !== false) {
                logger('notifier: target item is undeliverable', LOGGER_DEBUG);
                return;
            }
            xchan_query($r);
            $r = fetch_post_tags($r);
            $parent_item = $r[0];
            $top_level_post = false;
        }
        // avoid looping of discover items 12/4/2014
        if ($sys && $parent_item['uid'] == $sys['channel_id']) {
            return;
        }
        $encoded_item = encode_item($target_item);
        // Send comments to the owner to re-deliver to everybody in the conversation
        // We only do this if the item in question originated on this site. This prevents looping.
        // To clarify, a site accepting a new comment is responsible for sending it to the owner for relay.
        // Relaying should never be initiated on a post that arrived from elsewhere.
        // We should normally be able to rely on ITEM_ORIGIN, but start_delivery_chain() incorrectly set this
        // flag on comments for an extended period. So we'll also call comment_local_origin() which looks at
        // the hostname in the message_id and provides a second (fallback) opinion.
        $relay_to_owner = !$top_level_post && intval($target_item['item_origin']) && comment_local_origin($target_item) ? true : false;
        $uplink = false;
        // $cmd === 'relay' indicates the owner is sending it to the original recipients
        // don't allow the item in the relay command to relay to owner under any circumstances, it will loop
        logger('notifier: relay_to_owner: ' . ($relay_to_owner ? 'true' : 'false'), LOGGER_DATA);
        logger('notifier: top_level_post: ' . ($top_level_post ? 'true' : 'false'), LOGGER_DATA);
        // tag_deliver'd post which needs to be sent back to the original author
        if ($cmd === 'uplink' && intval($parent_item['item_uplink']) && !$top_level_post) {
            logger('notifier: uplink');
            $uplink = true;
        }
        if (($relay_to_owner || $uplink) && $cmd !== 'relay') {
            logger('notifier: followup relay', LOGGER_DEBUG);
            $recipients = array($uplink ? $parent_item['source_xchan'] : $parent_item['owner_xchan']);
            $private = true;
            if (!$encoded_item['flags']) {
                $encoded_item['flags'] = array();
            }
            $encoded_item['flags'][] = 'relay';
        } else {
            logger('notifier: normal distribution', LOGGER_DEBUG);
            if ($cmd === 'relay') {
                logger('notifier: owner relay');
            }
            // if our parent is a tag_delivery recipient, uplink to the original author causing
            // a delivery fork.
            if (intval($parent_item['item_uplink']) && !$top_level_post && $cmd !== 'uplink') {
                logger('notifier: uplinking this item');
                proc_run('php', 'include/notifier.php', 'uplink', $item_id);
            }
            $private = false;
            $recipients = collect_recipients($parent_item, $private);
            // FIXME add any additional recipients such as mentions, etc.
            // don't send deletions onward for other people's stuff
            // TODO verify this is needed - copied logic from same place in old code
            if (intval($target_item['item_deleted']) && !intval($target_item['item_wall'])) {
                logger('notifier: ignoring delete notification for non-wall item');
                return;
            }
        }
    }
    $walltowall = $top_level_post && $channel['xchan_hash'] === $target_item['author_xchan'] ? true : false;
    // Generic delivery section, we have an encoded item and recipients
    // Now start the delivery process
    $x = $encoded_item;
    $x['title'] = 'private';
    $x['body'] = 'private';
    logger('notifier: encoded item: ' . print_r($x, true), LOGGER_DATA);
    stringify_array_elms($recipients);
    if (!$recipients) {
        return;
    }
    //	logger('notifier: recipients: ' . print_r($recipients,true));
    $env_recips = $private ? array() : null;
    $details = q("select xchan_hash, xchan_instance_url, xchan_network, xchan_addr, xchan_guid, xchan_guid_sig from xchan where xchan_hash in (" . implode(',', $recipients) . ")");
    $recip_list = array();
    if ($details) {
        foreach ($details as $d) {
            $recip_list[] = $d['xchan_addr'] . ' (' . $d['xchan_hash'] . ')';
            if ($private) {
                $env_recips[] = array('guid' => $d['xchan_guid'], 'guid_sig' => $d['xchan_guid_sig'], 'hash' => $d['xchan_hash']);
            }
            if ($d['xchan_network'] === 'mail' && $normal_mode) {
                $delivery_options = get_xconfig($d['xchan_hash'], 'system', 'delivery_mode');
                if (!$delivery_options) {
                    format_and_send_email($channel, $d, $target_item);
                }
            }
        }
    }
    if ($private && !$env_recips) {
        // shouldn't happen
        logger('notifier: private message with no envelope recipients.' . print_r($argv, true));
    }
    logger('notifier: recipients (may be delivered to more if public): ' . print_r($recip_list, true), LOGGER_DEBUG);
    // Now we have collected recipients (except for external mentions, FIXME)
    // Let's reduce this to a set of hubs.
    $r = q("select * from hubloc where hubloc_hash in (" . implode(',', $recipients) . ") \n\t\tand hubloc_error = 0 and hubloc_deleted = 0");
    if (!$r) {
        logger('notifier: no hubs');
        return;
    }
    $hubs = $r;
    /**
     * Reduce the hubs to those that are unique. For zot hubs, we need to verify uniqueness by the sitekey, since it may have been 
     * a re-install which has not yet been detected and pruned.
     * For other networks which don't have or require sitekeys, we'll have to use the URL
     */
    $hublist = array();
    // this provides an easily printable list for the logs
    $dhubs = array();
    // delivery hubs where we store our resulting unique array
    $keys = array();
    // array of keys to check uniquness for zot hubs
    $urls = array();
    // array of urls to check uniqueness of hubs from other networks
    foreach ($hubs as $hub) {
        if (in_array($hub['hubloc_url'], $dead_hubs)) {
            logger('skipping dead hub: ' . $hub['hubloc_url'], LOGGER_DEBUG);
            continue;
        }
        if ($hub['hubloc_network'] == 'zot') {
            if (!in_array($hub['hubloc_sitekey'], $keys)) {
                $hublist[] = $hub['hubloc_host'];
                $dhubs[] = $hub;
                $keys[] = $hub['hubloc_sitekey'];
            }
        } else {
            if (!in_array($hub['hubloc_url'], $urls)) {
                $hublist[] = $hub['hubloc_host'];
                $dhubs[] = $hub;
                $urls[] = $hub['hubloc_url'];
            }
        }
    }
    logger('notifier: will notify/deliver to these hubs: ' . print_r($hublist, true), LOGGER_DEBUG);
    foreach ($dhubs as $hub) {
        if ($hub['hubloc_network'] !== 'zot') {
            $narr = array('channel' => $channel, 'env_recips' => $env_recips, 'recipients' => $recipients, 'item' => $item, 'target_item' => $target_item, 'hub' => $hub, 'top_level_post' => $top_level_post, 'private' => $private, 'followup' => $followup, 'relay_to_owner' => $relay_to_owner, 'uplink' => $uplink, 'cmd' => $cmd, 'expire' => $expire, 'mail' => $mail, 'location' => $location, 'fsuggest' => $fsuggest, 'request' => $request, 'normal_mode' => $normal_mode, 'packet_type' => $packet_type, 'walltowall' => $walltowall, 'queued' => array());
            call_hooks('notifier_hub', $narr);
            if ($narr['queued']) {
                foreach ($narr['queued'] as $pq) {
                    $deliveries[] = $pq;
                }
            }
            continue;
        }
        // default: zot protocol
        $hash = random_string();
        if ($packet_type === 'refresh' || $packet_type === 'purge') {
            $n = zot_build_packet($channel, $packet_type);
            q("insert into outq ( outq_hash, outq_account, outq_channel, outq_driver, outq_posturl, outq_async, outq_created, outq_updated, outq_notify, outq_msg ) values ( '%s', %d, %d, '%s', '%s', %d, '%s', '%s', '%s', '%s' )", dbesc($hash), intval($channel['channel_account_id']), intval($channel['channel_id']), dbesc('zot'), dbesc($hub['hubloc_callback']), intval(1), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($n), dbesc(''));
        } elseif ($packet_type === 'request') {
            $n = zot_build_packet($channel, 'request', $env_recips, $hub['hubloc_sitekey'], $hash, array('message_id' => $request_message_id));
            q("insert into outq ( outq_hash, outq_account, outq_channel, outq_driver, outq_posturl, outq_async, outq_created, outq_updated, outq_notify, outq_msg ) values ( '%s', %d, %d, '%s', '%s', %d, '%s', '%s', '%s', '%s' )", dbesc($hash), intval($channel['channel_account_id']), intval($channel['channel_id']), dbesc('zot'), dbesc($hub['hubloc_callback']), intval(1), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($n), dbesc(''));
        } else {
            $n = zot_build_packet($channel, 'notify', $env_recips, $private ? $hub['hubloc_sitekey'] : null, $hash);
            q("insert into outq ( outq_hash, outq_account, outq_channel, outq_driver, outq_posturl, outq_async, outq_created, outq_updated, outq_notify, outq_msg ) values ( '%s', %d, %d, '%s', '%s', %d, '%s', '%s', '%s', '%s' )", dbesc($hash), intval($target_item['aid']), intval($target_item['uid']), dbesc('zot'), dbesc($hub['hubloc_callback']), intval(1), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($n), dbesc(json_encode($encoded_item)));
            // only create delivery reports for normal undeleted items
            if (array_key_exists('postopts', $target_item) && !$target_item['item_deleted']) {
                q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_result, dreport_time, dreport_xchan, dreport_queue ) values ( '%s','%s','%s','%s','%s','%s','%s' ) ", dbesc($target_item['mid']), dbesc($hub['hubloc_host']), dbesc($hub['hubloc_host']), dbesc('queued'), dbesc(datetime_convert()), dbesc($channel['channel_hash']), dbesc($hash));
            }
        }
        $deliveries[] = $hash;
    }
    if ($normal_mode) {
        $x = q("select * from hook where hook = 'notifier_normal'");
        if ($x) {
            proc_run('php', 'include/deliver_hooks.php', $target_item['id']);
        }
    }
    if ($deliveries) {
        do_delivery($deliveries);
    }
    logger('notifier: basic loop complete.', LOGGER_DEBUG);
    call_hooks('notifier_end', $target_item);
    logger('notifer: complete.');
    return;
}
Exemple #29
0
function item_expire($uid, $days)
{
    if (!$uid || $days < 1) {
        return;
    }
    // $expire_network_only = save your own wall posts
    // and just expire conversations started by others
    // do not enable this until we can pass bulk delete messages through zot
    //	$expire_network_only = get_pconfig($uid,'expire','network_only');
    $expire_network_only = 1;
    $expire_limit = get_config('system', 'expire_limit');
    if (!intval($expire_limit)) {
        $expire_limit = 5000;
    }
    $sql_extra = intval($expire_network_only) ? " AND (item_flags & " . intval(ITEM_WALL) . ") = 0 " : "";
    $r = q("SELECT * FROM `item`\n\t\tWHERE `uid` = %d\n\t\tAND `created` < %s - INTERVAL %s\n\t\tAND `id` = `parent`\n\t\t{$sql_extra}\n\t\tAND ( item_flags & %d ) = 0\n\t\tAND ( item_restrict = 0 ) LIMIT {$expire_limit} ", intval($uid), db_utcnow(), db_quoteinterval(intval($days) . ' DAY'), intval(ITEM_RETAINED));
    if (!$r) {
        return;
    }
    $r = fetch_post_tags($r, true);
    foreach ($r as $item) {
        // don't expire filed items
        $terms = get_terms_oftype($item['term'], TERM_FILE);
        if ($terms) {
            retain_item($item['id']);
            continue;
        }
        // Only expire posts, not photos and photo comments
        if ($item['resource_type'] === 'photo') {
            retain_item($item['id']);
            continue;
        }
        if ($item['item_flags'] & ITEM_STARRED) {
            retain_item($item['id']);
            continue;
        }
        drop_item($item['id'], false);
    }
    //	proc_run('php',"include/notifier.php","expire","$uid");
}
Exemple #30
0
 public static function run($argc, $argv)
 {
     $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');
     $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();
     // 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\t\tLEFT JOIN account on abook_account = account_id\n\t\t\twhere abook_self = 0\n\t\t\t{$sql_extra} \n\t\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) {
                     Master::Summon(array('Onepoll', $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;
             }
             Master::Summon(array('Onepoll', $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;
                     }
                 }
                 Master::Summon(array('Onedirsync', $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;
 }