Пример #1
0
function hubwall_post(&$a)
{
    if (!is_site_admin()) {
        return;
    }
    $text = trim($_REQUEST['text']);
    if (!$text) {
        return;
    }
    $sender_name = sprintf(t('$1%s Administrator'), \Zotlabs\Lib\System::get_site_name());
    $sender_email = $_REQUEST['sender'];
    $subject = $_REQUEST['subject'];
    $textversion = strip_tags(html_entity_decode(bbcode(stripslashes(str_replace(array("\\r", "\\n"), array("", "\n"), $text))), ENT_QUOTES, 'UTF-8'));
    $htmlversion = bbcode(stripslashes(str_replace(array("\\r", "\\n"), array("", "<br />\n"), $text)));
    $sql_extra = intval($_REQUEST['test']) ? sprintf(" and account_email = '%s' ", get_config('system', 'admin_email')) : '';
    $recips = q("select account_email from account where account_flags = %d {$sql_extra}", intval(ACCOUNT_OK));
    if (!$recips) {
        notice(t('No recipients found.') . EOL);
        return;
    }
    $total_recips = count($recips);
    $total_delivered = 0;
    foreach ($recips as $recip) {
        $x = \Zotlabs\Lib\Enotify::send(array('fromName' => $sender_name, 'fromEmail' => $sender_email, 'replyTo' => $sender_email, 'toEmail' => $recip['account_email'], 'messageSubject' => $subject, 'htmlVersion' => $htmlversion, 'textVersion' => $textversion));
        if ($x) {
            $total_delivered++;
        }
    }
    info(sprintf(t('%1$d of %2$d messages sent.'), $total_delivered, $total_recips) . EOL);
}
Пример #2
0
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']);
        }
    }
}
Пример #3
0
function gnusoc_follow_from_feed(&$a, &$b)
{
    $item = $b['item'];
    $importer = $b['channel'];
    $xchan = $b['xchan'];
    $author = $b['author'];
    $b['caught'] = true;
    logger('follow activity received');
    if ($author && !$xchan) {
        $r = q("select * from xchan where xchan_guid = '%s' limit 1", dbesc($author['author_link']));
        if (!$r) {
            if (discover_by_webbie($author['author_link'])) {
                $r = q("select * from xchan where xchan_guid = '%s' limit 1", dbesc($author['author_link']));
                if (!$r) {
                    logger('discovery failed');
                    return;
                }
            }
            $xchan = $r[0];
        }
        $x = \Zotlabs\Access\PermissionRoles::role_perms('social');
        $their_perms = \Zotlabs\Access\Permissions::FilledPerms($x['perms_connect']);
        $r = q("select * from abook where abook_channel = %d and abook_xchan = '%s' limit 1", intval($importer['channel_id']), dbesc($xchan['xchan_hash']));
        if ($r) {
            $contact = $r[0];
            $abook_instance = $contact['abook_instance'];
            if ($abook_instance) {
                $abook_instance .= ',';
            }
            $abook_instance .= z_root();
            $r = q("update abook set abook_instance = '%s' where abook_id = %d and abook_channel = %d", dbesc($abook_instance), intval($contact['abook_id']), intval($importer['channel_id']));
            foreach ($their_perms as $k => $v) {
                set_abconfig($importer['channel_id'], $contact['abook_xchan'], 'their_perms', $k, $v);
            }
        } else {
            $role = get_pconfig($importer['channel_id'], 'system', 'permissions_role');
            if ($role) {
                $x = \Zotlabs\Access\PermissionRoles::role_perms($role);
                if ($x['perms_auto']) {
                    $my_perms = \Zotlabs\Access\Permissions::FilledPerms($x['perms_connect']);
                }
            }
            if (!$my_perms) {
                $my_perms = \Zotlabs\Access\Permissions::FilledAutoperms($importer['channel_id']);
            }
            $closeness = get_pconfig($importer['channel_id'], 'system', 'new_abook_closeness');
            if ($closeness === false) {
                $closeness = 80;
            }
            $r = q("insert into abook ( abook_account, abook_channel, abook_xchan, abook_closeness, abook_created, abook_updated, abook_connected, abook_dob, abook_pending, abook_instance ) values ( %d, %d, '%s', %d, '%s', '%s', '%s', '%s', %d, '%s' )", intval($importer['channel_account_id']), intval($importer['channel_id']), dbesc($xchan['xchan_hash']), intval($closeness), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(NULL_DATE), intval($my_perms ? 0 : 1), dbesc(z_root()));
            if ($r) {
                if ($my_perms) {
                    foreach ($my_perms as $k => $v) {
                        set_abconfig($importer['channel_id'], $xchan['xchan_hash'], 'my_perms', $k, $v);
                    }
                }
                if ($their_perms) {
                    foreach ($their_perms as $k => $v) {
                        set_abconfig($importer['channel_id'], $xchan['xchan_hash'], 'their_perms', $k, $v);
                    }
                }
                logger("New GNU-Social follower received for {$importer['channel_name']}");
                $new_connection = q("select * from abook left join xchan on abook_xchan = xchan_hash left join hubloc on hubloc_hash = xchan_hash where abook_channel = %d and abook_xchan = '%s' order by abook_created desc limit 1", intval($importer['channel_id']), dbesc($xchan['xchan_hash']));
                if ($new_connection) {
                    \Zotlabs\Lib\Enotify::submit(array('type' => NOTIFY_INTRO, 'from_xchan' => $xchan['xchan_hash'], 'to_xchan' => $importer['channel_hash'], 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id']));
                    if ($default_perms) {
                        // Send back a sharing notification to them
                        $deliver = gnusoc_remote_follow($importer, $new_connection[0]);
                        if ($deliver) {
                            Zotlabs\Daemon\Master::Summon(array('Deliver', $deliver));
                        }
                    }
                    $clone = array();
                    foreach ($new_connection[0] as $k => $v) {
                        if (strpos($k, 'abook_') === 0) {
                            $clone[$k] = $v;
                        }
                    }
                    unset($clone['abook_id']);
                    unset($clone['abook_account']);
                    unset($clone['abook_channel']);
                    $abconfig = load_abconfig($importer['channel_id'], $clone['abook_xchan']);
                    if ($abconfig) {
                        $clone['abconfig'] = $abconfig;
                    }
                    build_sync_packet($importer['channel_id'], array('abook' => array($clone)));
                }
            }
        }
        return;
    }
}
Пример #4
0
function mail_store($arr)
{
    if (!$arr['channel_id']) {
        logger('mail_store: no uid');
        return 0;
    }
    if (!$arr['mail_obscured']) {
        if (strpos($arr['body'], '<') !== false || strpos($arr['body'], '>') !== false) {
            $arr['body'] = escape_tags($arr['body']);
        }
    }
    if (array_key_exists('attach', $arr) && is_array($arr['attach'])) {
        $arr['attach'] = json_encode($arr['attach']);
    }
    $arr['account_id'] = x($arr, 'account_id') ? intval($arr['account_id']) : 0;
    $arr['mid'] = x($arr, 'mid') ? notags(trim($arr['mid'])) : random_string();
    $arr['from_xchan'] = x($arr, 'from_xchan') ? notags(trim($arr['from_xchan'])) : '';
    $arr['to_xchan'] = x($arr, 'to_xchan') ? notags(trim($arr['to_xchan'])) : '';
    $arr['created'] = x($arr, 'created') !== false ? datetime_convert('UTC', 'UTC', $arr['created']) : datetime_convert();
    $arr['expires'] = x($arr, 'expires') !== false ? datetime_convert('UTC', 'UTC', $arr['expires']) : NULL_DATE;
    $arr['title'] = x($arr, 'title') ? trim($arr['title']) : '';
    $arr['parent_mid'] = x($arr, 'parent_mid') ? notags(trim($arr['parent_mid'])) : '';
    $arr['body'] = x($arr, 'body') ? trim($arr['body']) : '';
    $arr['conv_guid'] = x($arr, 'conv_guid') ? trim($arr['conv_guid']) : '';
    $arr['mail_flags'] = x($arr, 'mail_flags') ? intval($arr['mail_flags']) : 0;
    if (!$arr['parent_mid']) {
        logger('mail_store: missing parent');
        $arr['parent_mid'] = $arr['mid'];
    }
    $r = q("SELECT `id` FROM mail WHERE `mid` = '%s' AND channel_id = %d LIMIT 1", dbesc($arr['mid']), intval($arr['channel_id']));
    if ($r) {
        logger('mail_store: duplicate item ignored. ' . print_r($arr, true));
        return 0;
    }
    if (!$r && $arr['mail_recalled'] == 1) {
        logger('mail_store: recalled item not found. ' . print_r($arr, true));
        return 0;
    }
    call_hooks('post_mail', $arr);
    if (x($arr, 'cancel')) {
        logger('mail_store: post cancelled by plugin.');
        return 0;
    }
    dbesc_array($arr);
    logger('mail_store: ' . print_r($arr, true), LOGGER_DATA);
    $r = dbq("INSERT INTO mail (`" . implode("`, `", array_keys($arr)) . "`) VALUES ('" . implode("', '", array_values($arr)) . "')");
    // find the item we just created
    $r = q("SELECT `id` FROM mail WHERE `mid` = '%s' AND `channel_id` = %d ORDER BY `id` ASC ", $arr['mid'], intval($arr['channel_id']));
    if ($r) {
        $current_post = $r[0]['id'];
        logger('mail_store: created item ' . $current_post, LOGGER_DEBUG);
        $arr['id'] = $current_post;
        // for notification
    } else {
        logger('mail_store: could not locate created item');
        return 0;
    }
    if (count($r) > 1) {
        logger('mail_store: duplicated post occurred. Removing duplicates.');
        q("DELETE FROM mail WHERE `mid` = '%s' AND `channel_id` = %d AND `id` != %d ", $arr['mid'], intval($arr['channel_id']), intval($current_post));
    } else {
        $notif_params = array('from_xchan' => $arr['from_xchan'], 'to_xchan' => $arr['to_xchan'], 'type' => NOTIFY_MAIL, 'item' => $arr, 'verb' => ACTIVITY_POST, 'otype' => 'mail');
        Zlib\Enotify::submit($notif_params);
    }
    call_hooks('post_mail_end', $arr);
    return $current_post;
}
Пример #5
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 `dismissed` = 1 where `dismissed` = 0 and uid = %d AND dtstart < '%s' AND dtstart > '%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 created 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 created desc limit 50", intval(local_channel()));
             $z2 = q("select * from notify where uid = %d\n\t\t\t\t\tand seen = 1 order by created 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['xname'], 'url' => $zz['url'], 'photo' => $zz['photo'], 'when' => relative_date($zz['created']), '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[] = \Zotlabs\Lib\Enotify::format($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 dtstart < '%s' AND dtstart > '%s' and `dismissed` = 0\n\t\t\t\tand etype in ( 'event', 'birthday' )\n\t\t\t\tORDER BY `dtstart` 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['dtstart'], 'Y/m');
                 } else {
                     $md = datetime_convert('UTC', 'UTC', $rr['dtstart'], 'Y/m');
                 }
                 $strt = datetime_convert('UTC', $rr['adjust'] ? date_default_timezone_get() : 'UTC', $rr['dtstart']);
                 $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['dtstart'], $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 etype, dtstart, adjust FROM `event`\n\t\t\t\tWHERE `event`.`uid` = %d AND dtstart < '%s' AND dtstart > '%s' and `dismissed` = 0\n\t\t\t\tand etype in ( 'event', 'birthday' )\n\t\t\t\tORDER BY `dtstart` 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['etype'] === 'birthday') {
                         $result['birthdays']++;
                         $bd = true;
                     } else {
                         $result['events']++;
                     }
                     if (datetime_convert('UTC', intval($x['adjust']) ? date_default_timezone_get() : 'UTC', $x['dtstart'], '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();
 }
Пример #6
0
function diaspora_message($importer, $xml, $msg)
{
    $a = get_app();
    $msg_guid = notags(unxmlify($xml['guid']));
    $msg_parent_guid = notags(unxmlify($xml['parent_guid']));
    $msg_parent_author_signature = notags(unxmlify($xml['parent_author_signature']));
    $msg_author_signature = notags(unxmlify($xml['author_signature']));
    $msg_text = unxmlify($xml['text']);
    $msg_created_at = datetime_convert('UTC', 'UTC', notags(unxmlify($xml['created_at'])));
    $msg_diaspora_handle = notags(diaspora_get_author($xml));
    $msg_conversation_guid = notags(unxmlify($xml['conversation_guid']));
    $parent_uri = $msg_parent_guid;
    $contact = diaspora_get_contact_by_handle($importer['channel_id'], $msg_diaspora_handle);
    if (!$contact) {
        logger('diaspora_message: cannot find contact: ' . $msg_diaspora_handle);
        return;
    }
    if (!perm_is_allowed($importer['channel_id'], $contact['xchan_hash'], 'post_mail')) {
        logger('Ignoring this author.');
        return 202;
    }
    $conversation = null;
    $c = q("select * from conv where uid = %d and guid = '%s' limit 1", intval($importer['channel_id']), dbesc($msg_conversation_guid));
    if ($c) {
        $conversation = $c[0];
    } else {
        logger('diaspora_message: conversation not available.');
        return;
    }
    $reply = 0;
    $subject = $conversation['subject'];
    //this is already encoded
    $body = diaspora2bb($msg_text);
    $maxlen = get_max_import_size();
    if ($maxlen && mb_strlen($body) > $maxlen) {
        $body = mb_substr($body, 0, $maxlen, 'UTF-8');
        logger('message length exceeds max_import_size: truncated');
    }
    $parent_ptr = $msg_parent_guid;
    if ($parent_ptr === $conversation['guid']) {
        // this should always be the case
        $x = q("select mid from mail where conv_guid = '%s' and channel_id = %d order by id asc limit 1", dbesc($conversation['guid']), intval($importer['channel_id']));
        if ($x) {
            $parent_ptr = $x[0]['mid'];
        }
    }
    $author_signed_data = $msg_guid . ';' . $msg_parent_guid . ';' . $msg_text . ';' . unxmlify($xml['created_at']) . ';' . $msg_diaspora_handle . ';' . $msg_conversation_guid;
    $author_signature = base64_decode($msg_author_signature);
    $person = find_diaspora_person_by_handle($msg_diaspora_handle);
    if (is_array($person) && x($person, 'xchan_pubkey')) {
        $key = $person['xchan_pubkey'];
    } else {
        logger('diaspora_message: unable to find author details');
        return;
    }
    if (!rsa_verify($author_signed_data, $author_signature, $key, 'sha256')) {
        logger('diaspora_message: verification failed.');
        return;
    }
    $r = q("select id from mail where mid = '%s' and channel_id = %d limit 1", dbesc($msg_guid), intval($importer['channel_id']));
    if ($r) {
        logger('diaspora_message: duplicate message already delivered.', LOGGER_DEBUG);
        return;
    }
    $key = get_config('system', 'pubkey');
    // $subject is a copy of the already obscured subject from the conversation structure
    if ($body) {
        $body = str_rot47(base64url_encode($body));
    }
    q("insert into mail ( `account_id`, `channel_id`, `convid`, `conv_guid`, `from_xchan`,`to_xchan`,`title`,`body`,`mail_obscured`,`mid`,`parent_mid`,`created`, mail_isreply) values ( %d, %d, %d, '%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', %d)", intval($importer['channel_account_id']), intval($importer['channel_id']), intval($conversation['id']), dbesc($conversation['guid']), dbesc($person['xchan_hash']), dbesc($importer['xchan_hash']), dbesc($subject), dbesc($body), intval(1), dbesc($msg_guid), dbesc($parent_ptr), dbesc($msg_created_at), intval(1));
    q("update conv set updated = '%s' where id = %d", dbesc(datetime_convert()), intval($conversation['id']));
    $z = q("select * from mail where mid = '%s' and channel_id = %d limit 1", dbesc($msg_guid), intval($importer['channel_id']));
    \Zotlabs\Lib\Enotify::submit(array('from_xchan' => $person['xchan_hash'], 'to_xchan' => $importer['channel_hash'], 'type' => NOTIFY_MAIL, 'item' => $z[0], 'verb' => ACTIVITY_POST, 'otype' => 'mail'));
    return;
}
Пример #7
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
 }