Example #1
0
function probe_content(&$a)
{
    $o .= '<h3>Probe Diagnostic</h3>';
    $o .= '<form action="probe" method="get">';
    $o .= 'Lookup address: <input type="text" style="width: 250px;" name="addr" value="' . $_GET['addr'] . '" />';
    $o .= '<input type="submit" name="submit" value="Submit" /></form>';
    $o .= '<br /><br />';
    if (x($_GET, 'addr')) {
        $channel = $a->get_channel();
        $addr = trim($_GET['addr']);
        $res = zot_finger($addr, $channel, false);
        $o .= '<pre>';
        if ($res['success']) {
            $j = json_decode($res['body'], true);
        } else {
            $o .= sprintf(t('Fetching URL returns error: %1$s'), $res['error'] . "\r\n\r\n");
            $o .= "<strong>https connection failed. Trying again with auto failover to http.</strong>\r\n\r\n";
            $res = zot_finger($addr, $channel, true);
            if ($res['success']) {
                $j = json_decode($res['body'], true);
            } else {
                $o .= sprintf(t('Fetching URL returns error: %1$s'), $res['error'] . "\r\n\r\n");
            }
        }
        if ($j && $j['permissions'] && $j['permissions']['iv']) {
            $j['permissions'] = json_decode(crypto_unencapsulate($j['permissions'], $channel['channel_prvkey']), true);
        }
        $o .= str_replace("\n", '<br />', print_r($j, true));
        $o .= '</pre>';
    }
    return $o;
}
Example #2
0
function viewsrc_content(&$a)
{
    $o = '';
    $item_id = argc() > 1 ? intval(argv(1)) : 0;
    $json = argc() > 2 && argv(2) === 'json' ? true : false;
    if (!local_user()) {
        notice(t('Permission denied.') . EOL);
    }
    if (!$item_id) {
        $a->error = 404;
        notice(t('Item not found.') . EOL);
    }
    if (local_user() && $item_id) {
        $r = q("select item_flags, body from item where item_restrict = 0 and uid = %d and id = %d limit 1", intval(local_user()), intval($item_id));
        if ($r) {
            if ($r[0]['item_flags'] & ITEM_OBSCURED) {
                $r[0]['body'] = crypto_unencapsulate(json_decode($r[0]['body'], true), get_config('system', 'prvkey'));
            }
            $o = $json ? json_encode($r[0]['body']) : str_replace("\n", '<br />', $r[0]['body']);
        }
    }
    if (is_ajax()) {
        echo $o;
        killme();
    }
    return $o;
}
Example #3
0
 function get()
 {
     $o .= '<h3>Probe Diagnostic</h3>';
     $o .= '<form action="probe" method="get">';
     $o .= 'Lookup address: <input type="text" style="width: 250px;" name="addr" value="' . $_GET['addr'] . '" />';
     $o .= '<input type="submit" name="submit" value="Submit" /></form>';
     $o .= '<br /><br />';
     if (x($_GET, 'addr')) {
         $channel = \App::get_channel();
         $addr = trim($_GET['addr']);
         $do_import = intval($_GET['import']) && is_site_admin() ? true : false;
         $j = \Zotlabs\Zot\Finger::run($addr, $channel, false);
         //			$res = zot_finger($addr,$channel,false);
         $o .= '<pre>';
         if (!$j['success']) {
             $o .= sprintf(t('Fetching URL returns error: %1$s'), $res['error'] . "\r\n\r\n");
             $o .= "<strong>https connection failed. Trying again with auto failover to http.</strong>\r\n\r\n";
             $j = \Zotlabs\Zot\Finger::run($addr, $channel, true);
             if (!$j['success']) {
                 $o .= sprintf(t('Fetching URL returns error: %1$s'), $res['error'] . "\r\n\r\n");
             }
         }
         if ($do_import && $j) {
             $x = import_xchan($j);
         }
         if ($j && $j['permissions'] && $j['permissions']['iv']) {
             $j['permissions'] = json_decode(crypto_unencapsulate($j['permissions'], $channel['channel_prvkey']), true);
         }
         $o .= str_replace("\n", '<br />', print_r($j, true));
         $o .= '</pre>';
     }
     return $o;
 }
Example #4
0
 function __construct($data, $prvkey, $handler)
 {
     $this->error = false;
     $this->validated = false;
     $this->messagetype = '';
     $this->response = array('success' => false);
     $this->handler = $handler;
     if (!is_array($data)) {
         $data = json_decode($data, true);
     }
     if ($data && is_array($data)) {
         $this->encrypted = array_key_exists('iv', $data) ? true : false;
         if ($this->encrypted) {
             $this->data = @json_decode(@crypto_unencapsulate($data, $prvkey), true);
         }
         if (!$this->data) {
             $this->data = $data;
         }
         if ($this->data && is_array($this->data) && array_key_exists('type', $this->data)) {
             $this->messagetype = $this->data['type'];
         }
     }
     if (!$this->messagetype) {
         $this->error = true;
     }
     $this->sender = array_key_exists('sender', $this->data) ? $this->data['sender'] : null;
     $this->recipients = array_key_exists('recipients', $this->data) ? $this->data['recipients'] : null;
     if ($this->sender) {
         $this->ValidateSender();
     }
     $this->Dispatch();
 }
Example #5
0
function viewsrc_content(&$a)
{
    $o = '';
    $sys = get_sys_channel();
    $item_id = argc() > 1 ? intval(argv(1)) : 0;
    $json = argc() > 2 && argv(2) === 'json' ? true : false;
    if (!local_channel()) {
        notice(t('Permission denied.') . EOL);
    }
    if (!$item_id) {
        App::$error = 404;
        notice(t('Item not found.') . EOL);
    }
    $item_normal = item_normal();
    if (local_channel() && $item_id) {
        $r = q("select id, item_flags, item_obscured, body from item where uid in (%d , %d) and id = %d {$item_normal} limit 1", intval(local_channel()), intval($sys['channel_id']), intval($item_id));
        if ($r) {
            if (intval($r[0]['item_obscured'])) {
                $r[0]['body'] = crypto_unencapsulate(json_decode($r[0]['body'], true), get_config('system', 'prvkey'));
            }
            $o = $json ? json_encode($r[0]['body']) : str_replace("\n", '<br />', $r[0]['body']);
        }
    }
    if (is_ajax()) {
        print '<div><i class="icon-pencil"> ' . t('Source of Item') . ' ' . $r[0]['id'] . '</i></div>';
        echo $o;
        killme();
    }
    return $o;
}
Example #6
0
 function get()
 {
     $o = '';
     if (!local_channel()) {
         notice(t('Permission denied.') . EOL);
         return;
     }
     $post_id = argc() > 1 ? intval(argv(1)) : 0;
     if (!$post_id) {
         notice(t('Item not found') . EOL);
         return;
     }
     $itm = q("SELECT * FROM `item` WHERE `id` = %d AND ( owner_xchan = '%s' OR author_xchan = '%s' ) LIMIT 1", intval($post_id), dbesc(get_observer_hash()), dbesc(get_observer_hash()));
     if (!count($itm)) {
         notice(t('Item is not editable') . EOL);
         return;
     }
     if ($itm[0]['resource_type'] === 'event' && $itm[0]['resource_id']) {
         goaway(z_root() . '/events/' . $itm[0]['resource_id'] . '?expandform=1');
     }
     $owner_uid = $itm[0]['uid'];
     $channel = \App::get_channel();
     if (intval($itm[0]['item_obscured'])) {
         $key = get_config('system', 'prvkey');
         if ($itm[0]['title']) {
             $itm[0]['title'] = crypto_unencapsulate(json_decode_plus($itm[0]['title']), $key);
         }
         if ($itm[0]['body']) {
             $itm[0]['body'] = crypto_unencapsulate(json_decode_plus($itm[0]['body']), $key);
         }
     }
     $category = '';
     $catsenabled = feature_enabled($owner_uid, 'categories') ? 'categories' : '';
     if ($catsenabled) {
         $itm = fetch_post_tags($itm);
         $cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY);
         foreach ($cats as $cat) {
             if (strlen($category)) {
                 $category .= ', ';
             }
             $category .= $cat['term'];
         }
     }
     if ($itm[0]['attach']) {
         $j = json_decode($itm[0]['attach'], true);
         if ($j) {
             foreach ($j as $jj) {
                 $itm[0]['body'] .= "\n" . '[attachment]' . basename($jj['href']) . ',' . $jj['revision'] . '[/attachment]' . "\n";
             }
         }
     }
     $x = array('nickname' => $channel['channel_address'], 'editor_autocomplete' => true, 'bbco_autocomplete' => 'bbcode', 'return_path' => $_SESSION['return_url'], 'button' => t('Edit'), 'hide_voting' => true, 'hide_future' => true, 'hide_location' => true, 'mimetype' => $itm[0]['mimetype'], 'ptyp' => $itm[0]['obj_type'], 'body' => undo_post_tagging($itm[0]['body']), 'post_id' => $post_id, 'defloc' => $channel['channel_location'], 'visitor' => true, 'title' => htmlspecialchars($itm[0]['title'], ENT_COMPAT, 'UTF-8'), 'category' => $category, 'showacl' => false, 'profile_uid' => $owner_uid, 'catsenabled' => $catsenabled, 'hide_expire' => true, 'bbcode' => true);
     $editor = status_editor($a, $x);
     $o .= replace_macros(get_markup_template('edpost_head.tpl'), array('$title' => t('Edit post'), '$editor' => $editor));
     return $o;
 }
Example #7
0
function new_contact($uid, $url, $channel, $interactive = false, $confirm = false)
{
    $result = array('success' => false, 'message' => '');
    $a = get_app();
    $is_red = false;
    $is_http = strpos($url, '://') !== false ? true : false;
    if ($is_http && substr($url, -1, 1) === '/') {
        $url = substr($url, 0, -1);
    }
    if (!allowed_url($url)) {
        $result['message'] = t('Channel is blocked on this site.');
        return $result;
    }
    if (!$url) {
        $result['message'] = t('Channel location missing.');
        return $result;
    }
    // check service class limits
    $r = q("select count(*) as total from abook where abook_channel = %d and abook_self = 0 ", intval($uid));
    if ($r) {
        $total_channels = $r[0]['total'];
    }
    if (!service_class_allows($uid, 'total_channels', $total_channels)) {
        $result['message'] = upgrade_message();
        return $result;
    }
    $arr = array('url' => $url, 'channel' => array());
    call_hooks('follow', $arr);
    if ($arr['channel']['success']) {
        $ret = $arr['channel'];
    } elseif (!$is_http) {
        $ret = zot_finger($url, $channel);
    }
    if ($ret && $ret['success']) {
        $is_red = true;
        $j = json_decode($ret['body'], true);
    }
    $my_perms = get_channel_default_perms($uid);
    $role = get_pconfig($uid, 'system', 'permissions_role');
    if ($role) {
        $x = get_role_perms($role);
        if ($x['perms_follow']) {
            $my_perms = $x['perms_follow'];
        }
    }
    if ($is_red && $j) {
        logger('follow: ' . $url . ' ' . print_r($j, true), LOGGER_DEBUG);
        if (!($j['success'] && $j['guid'])) {
            $result['message'] = t('Response from remote channel was incomplete.');
            logger('mod_follow: ' . $result['message']);
            return $result;
        }
        // Premium channel, set confirm before callback to avoid recursion
        if (array_key_exists('connect_url', $j) && $interactive && !$confirm) {
            goaway(zid($j['connect_url']));
        }
        // do we have an xchan and hubloc?
        // If not, create them.
        $x = import_xchan($j);
        if (array_key_exists('deleted', $j) && intval($j['deleted'])) {
            $result['message'] = t('Channel was deleted and no longer exists.');
            return $result;
        }
        if (!$x['success']) {
            return $x;
        }
        $xchan_hash = $x['hash'];
        $their_perms = 0;
        $global_perms = get_perms();
        if (array_key_exists('permissions', $j) && array_key_exists('data', $j['permissions'])) {
            $permissions = crypto_unencapsulate(array('data' => $j['permissions']['data'], 'key' => $j['permissions']['key'], 'iv' => $j['permissions']['iv']), $channel['channel_prvkey']);
            if ($permissions) {
                $permissions = json_decode($permissions, true);
            }
            logger('decrypted permissions: ' . print_r($permissions, true), LOGGER_DATA);
        } else {
            $permissions = $j['permissions'];
        }
        foreach ($permissions as $k => $v) {
            if ($v) {
                $their_perms = $their_perms | intval($global_perms[$k][1]);
            }
        }
    } else {
        $their_perms = 0;
        $xchan_hash = '';
        $r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' limit 1", dbesc($url), dbesc($url));
        if (!$r) {
            // attempt network auto-discovery
            if (strpos($url, '@') && !$is_http) {
                $r = discover_by_webbie($url);
            } elseif ($is_http) {
                $r = discover_by_url($url);
                $r['allowed'] = intval(get_config('system', 'feed_contacts'));
            }
            if ($r) {
                $r['channel_id'] = $uid;
                call_hooks('follow_allow', $r);
                if (!$r['allowed']) {
                    $result['message'] = t('Protocol disabled.');
                    return $result;
                }
                $r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' limit 1", dbesc($url), dbesc($url));
            }
        }
        if ($r) {
            $xchan_hash = $r[0]['xchan_hash'];
            $their_perms = 0;
        }
    }
    if (!$xchan_hash) {
        $result['message'] = t('Channel discovery failed.');
        logger('follow: ' . $result['message']);
        return $result;
    }
    if (local_channel() && $uid == local_channel()) {
        $aid = get_account_id();
        $hash = get_observer_hash();
        $ch = $a->get_channel();
        $default_group = $ch['channel_default_group'];
    } else {
        $r = q("select * from channel where channel_id = %d limit 1", intval($uid));
        if (!$r) {
            $result['message'] = t('local account not found.');
            return $result;
        }
        $aid = $r[0]['channel_account_id'];
        $hash = $r[0]['channel_hash'];
        $default_group = $r[0]['channel_default_group'];
    }
    if ($is_http) {
        $r = q("select count(*) as total from abook where abook_account = %d and abook_feed = 1 ", intval($aid));
        if ($r) {
            $total_feeds = $r[0]['total'];
        }
        if (!service_class_allows($uid, 'total_feeds', $total_feeds)) {
            $result['message'] = upgrade_message();
            return $result;
        }
    }
    if ($hash == $xchan_hash) {
        $result['message'] = t('Cannot connect to yourself.');
        return $result;
    }
    $r = q("select abook_xchan from abook where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($xchan_hash), intval($uid));
    if ($r) {
        $x = q("update abook set abook_their_perms = %d where abook_id = %d", intval($their_perms), intval($r[0]['abook_id']));
    } else {
        $closeness = get_pconfig($uid, 'system', 'new_abook_closeness');
        if ($closeness === false) {
            $closeness = 80;
        }
        $r = q("insert into abook ( abook_account, abook_channel, abook_closeness, abook_xchan, abook_feed, abook_their_perms, abook_my_perms, abook_created, abook_updated )\n\t\t\tvalues( %d, %d, %d, '%s', %d, %d, %d, '%s', '%s' ) ", intval($aid), intval($uid), intval($closeness), dbesc($xchan_hash), intval($is_http ? 1 : 0), intval($is_http ? $their_perms | PERMS_R_STREAM | PERMS_A_REPUBLISH : $their_perms), intval($my_perms), dbesc(datetime_convert()), dbesc(datetime_convert()));
    }
    if (!$r) {
        logger('mod_follow: abook creation failed');
    }
    $r = q("select abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash \n\t\twhere abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($xchan_hash), intval($uid));
    if ($r) {
        $result['abook'] = $r[0];
        proc_run('php', 'include/notifier.php', 'permission_update', $result['abook']['abook_id']);
    }
    $arr = array('channel_id' => $uid, 'abook' => $result['abook']);
    call_hooks('follow', $arr);
    /** If there is a default group for this channel, add this member to it */
    if ($default_group) {
        require_once 'include/group.php';
        $g = group_rec_byhash($uid, $default_group);
        if ($g) {
            group_add_member($uid, '', $xchan_hash, $g['id']);
        }
    }
    $result['success'] = true;
    return $result;
}
 function get()
 {
     if (!\App::$profile) {
         notice(t('Requested profile is not available.') . EOL);
         \App::$error = 404;
         return;
     }
     $which = argv(1);
     $uid = local_channel();
     $owner = 0;
     $channel = null;
     $observer = \App::get_observer();
     $channel = \App::get_channel();
     if (\App::$is_sys && is_site_admin()) {
         $sys = get_sys_channel();
         if ($sys && intval($sys['channel_id'])) {
             $uid = $owner = intval($sys['channel_id']);
             $channel = $sys;
             $observer = $sys;
         }
     }
     if (!$owner) {
         // Figure out who the page owner is.
         $r = q("select channel_id from channel where channel_address = '%s'", dbesc($which));
         if ($r) {
             $owner = intval($r[0]['channel_id']);
         }
     }
     $ob_hash = $observer ? $observer['xchan_hash'] : '';
     if (!perm_is_allowed($owner, $ob_hash, 'write_pages')) {
         notice(t('Permission denied.') . EOL);
         return;
     }
     $is_owner = $uid && $uid == $owner ? true : false;
     $o = '';
     // Figure out which post we're editing
     $post_id = argc() > 2 ? intval(argv(2)) : 0;
     if (!$post_id) {
         notice(t('Item not found') . EOL);
         return;
     }
     $ob_hash = $observer ? $observer['xchan_hash'] : '';
     $perms = get_all_perms($owner, $ob_hash);
     if (!$perms['write_pages']) {
         notice(t('Permission denied.') . EOL);
         return;
     }
     // We've already figured out which item we want and whose copy we need,
     // so we don't need anything fancy here
     $sql_extra = item_permissions_sql($owner);
     $itm = q("SELECT * FROM `item` WHERE `id` = %d and uid = %s {$sql_extra} LIMIT 1", intval($post_id), intval($owner));
     if (!$itm) {
         notice(t('Permission denied.') . EOL);
         return;
     }
     if (intval($itm[0]['item_obscured'])) {
         $key = get_config('system', 'prvkey');
         if ($itm[0]['title']) {
             $itm[0]['title'] = crypto_unencapsulate(json_decode_plus($itm[0]['title']), $key);
         }
         if ($itm[0]['body']) {
             $itm[0]['body'] = crypto_unencapsulate(json_decode_plus($itm[0]['body']), $key);
         }
     }
     $item_id = q("select * from item_id where service = 'WEBPAGE' and iid = %d limit 1", intval($itm[0]['id']));
     if ($item_id) {
         $page_title = $item_id[0]['sid'];
     }
     $mimetype = $itm[0]['mimetype'];
     if ($mimetype === 'application/x-php') {
         if (!$uid || $uid != $itm[0]['uid']) {
             notice(t('Permission denied.') . EOL);
             return;
         }
     }
     $layout = $itm[0]['layout_mid'];
     $tpl = get_markup_template("jot.tpl");
     $rp = 'webpages/' . $which;
     $x = array('nickname' => $channel['channel_address'], 'bbco_autocomplete' => $mimetype == 'text/bbcode' ? 'bbcode' : '', 'return_path' => $rp, 'webpage' => ITEM_TYPE_WEBPAGE, 'ptlabel' => t('Page link'), 'pagetitle' => $page_title, 'writefiles' => $mimetype == 'text/bbcode' ? perm_is_allowed($owner, get_observer_hash(), 'write_storage') : false, 'button' => t('Edit'), 'weblink' => $mimetype == 'text/bbcode' ? t('Insert web link') : false, 'hide_location' => true, 'hide_voting' => true, 'ptyp' => $itm[0]['type'], 'body' => undo_post_tagging($itm[0]['body']), 'post_id' => $post_id, 'visitor' => $is_owner ? true : false, 'acl' => populate_acl($itm[0], false, \PermissionDescription::fromGlobalPermission('view_pages')), 'showacl' => $is_owner ? true : false, 'mimetype' => $mimetype, 'mimeselect' => true, 'layout' => $layout, 'layoutselect' => true, 'title' => htmlspecialchars($itm[0]['title'], ENT_COMPAT, 'UTF-8'), 'lockstate' => strlen($itm[0]['allow_cid']) || strlen($itm[0]['allow_gid']) || strlen($itm[0]['deny_cid']) || strlen($itm[0]['deny_gid']) ? 'lock' : 'unlock', 'profile_uid' => intval($owner), 'bbcode' => $mimetype == 'text/bbcode' ? true : false);
     $editor = status_editor($a, $x);
     $o .= replace_macros(get_markup_template('edpost_head.tpl'), array('$title' => t('Edit Webpage'), '$delete' => $itm[0]['author_xchan'] === $ob_hash || $itm[0]['owner_xchan'] === $ob_hash ? t('Delete') : false, '$editor' => $editor, '$id' => $itm[0]['id']));
     return $o;
 }
function editwebpage_content(&$a)
{
    if (!App::$profile) {
        notice(t('Requested profile is not available.') . EOL);
        App::$error = 404;
        return;
    }
    $which = argv(1);
    $uid = local_channel();
    $owner = 0;
    $channel = null;
    $observer = App::get_observer();
    $channel = App::get_channel();
    if (App::$is_sys && is_site_admin()) {
        $sys = get_sys_channel();
        if ($sys && intval($sys['channel_id'])) {
            $uid = $owner = intval($sys['channel_id']);
            $channel = $sys;
            $observer = $sys;
        }
    }
    if (!$owner) {
        // Figure out who the page owner is.
        $r = q("select channel_id from channel where channel_address = '%s'", dbesc($which));
        if ($r) {
            $owner = intval($r[0]['channel_id']);
        }
    }
    $ob_hash = $observer ? $observer['xchan_hash'] : '';
    if (!perm_is_allowed($owner, $ob_hash, 'write_pages')) {
        notice(t('Permission denied.') . EOL);
        return;
    }
    $is_owner = $uid && $uid == $owner ? true : false;
    $o = '';
    // Figure out which post we're editing
    $post_id = argc() > 2 ? intval(argv(2)) : 0;
    if (!$post_id) {
        notice(t('Item not found') . EOL);
        return;
    }
    $ob_hash = $observer ? $observer['xchan_hash'] : '';
    $perms = get_all_perms($owner, $ob_hash);
    if (!$perms['write_pages']) {
        notice(t('Permission denied.') . EOL);
        return;
    }
    // We've already figured out which item we want and whose copy we need,
    // so we don't need anything fancy here
    $sql_extra = item_permissions_sql($owner);
    $itm = q("SELECT * FROM `item` WHERE `id` = %d and uid = %s {$sql_extra} LIMIT 1", intval($post_id), intval($owner));
    if (!$itm) {
        notice(t('Permission denied.') . EOL);
        return;
    }
    if (intval($itm[0]['item_obscured'])) {
        $key = get_config('system', 'prvkey');
        if ($itm[0]['title']) {
            $itm[0]['title'] = crypto_unencapsulate(json_decode_plus($itm[0]['title']), $key);
        }
        if ($itm[0]['body']) {
            $itm[0]['body'] = crypto_unencapsulate(json_decode_plus($itm[0]['body']), $key);
        }
    }
    $item_id = q("select * from item_id where service = 'WEBPAGE' and iid = %d limit 1", intval($itm[0]['id']));
    if ($item_id) {
        $page_title = $item_id[0]['sid'];
    }
    $plaintext = true;
    $mimetype = $itm[0]['mimetype'];
    if ($mimetype === 'application/x-php') {
        if (!$uid || $uid != $itm[0]['uid']) {
            notice(t('Permission denied.') . EOL);
            return;
        }
    }
    $mimeselect = '';
    if ($mimetype != 'text/bbcode') {
        $plaintext = true;
    }
    if (get_config('system', 'page_mimetype')) {
        $mimeselect = '<input type="hidden" name="mimetype" value="' . $mimetype . '" />';
    } else {
        $mimeselect = mimetype_select($itm[0]['uid'], $mimetype);
    }
    $layout = get_config('system', 'page_layout');
    if ($layout) {
        $layoutselect = '<input type="hidden" name="layout_mid" value="' . $layout . '" />';
    } else {
        $layoutselect = layout_select($itm[0]['uid'], $itm[0]['layout_mid']);
    }
    App::$page['htmlhead'] .= replace_macros(get_markup_template('jot-header.tpl'), array('$baseurl' => z_root(), '$editselect' => $plaintext ? 'none' : '/(profile-jot-text|prvmail-text)/', '$pretext' => '', '$ispublic' => '&nbsp;', '$geotag' => $geotag, '$nickname' => $channel['channel_address'], '$confirmdelete' => t('Delete webpage?'), '$bbco_autocomplete' => $mimetype == 'text/bbcode' ? 'bbcode' : ''));
    $tpl = get_markup_template("jot.tpl");
    $jotplugins = '';
    $jotnets = '';
    call_hooks('jot_tool', $jotplugins);
    call_hooks('jot_networks', $jotnets);
    // FIXME A return path with $_SESSION doesn't always work for observer - it may WSoD
    // instead of loading a sensible page.  So, send folk to the webpage list.
    $rp = 'webpages/' . $which;
    $editor = replace_macros($tpl, array('$return_path' => $rp, '$webpage' => ITEM_TYPE_WEBPAGE, '$placeholdpagetitle' => t('Page link title'), '$pagetitle' => $page_title, '$writefiles' => perm_is_allowed($owner, get_observer_hash(), 'write_storage'), '$action' => 'item', '$share' => t('Edit'), '$bold' => t('Bold'), '$italic' => t('Italic'), '$underline' => t('Underline'), '$quote' => t('Quote'), '$code' => t('Code'), '$upload' => t('Upload photo'), '$attach' => t('Attach file'), '$weblink' => t('Insert web link'), '$youtube' => t('Insert YouTube video'), '$video' => t('Insert Vorbis [.ogg] video'), '$audio' => t('Insert Vorbis [.ogg] audio'), '$setloc' => t('Set your location'), '$noloc' => get_pconfig($uid, 'system', 'use_browser_location') ? t('Clear browser location') : '', '$wait' => t('Please wait'), '$permset' => t('Permission settings'), '$ptyp' => $itm[0]['type'], '$content' => undo_post_tagging($itm[0]['body']), '$post_id' => $post_id, '$baseurl' => z_root(), '$defloc' => $itm[0]['location'], '$visitor' => $is_owner ? true : false, '$acl' => populate_acl($itm[0], false), '$showacl' => $is_owner ? true : false, '$public' => t('Public post'), '$jotnets' => $jotnets, '$mimeselect' => $mimeselect, '$layoutselect' => $layoutselect, '$title' => htmlspecialchars($itm[0]['title'], ENT_COMPAT, 'UTF-8'), '$placeholdertitle' => t('Title (optional)'), '$category' => '', '$placeholdercategory' => t('Categories (optional, comma-separated list)'), '$emtitle' => t('Example: bob@example.com, mary@example.com'), 'lockstate' => strlen($itm[0]['allow_cid']) || strlen($itm[0]['allow_gid']) || strlen($itm[0]['deny_cid']) || strlen($itm[0]['deny_gid']) ? 'lock' : 'unlock', '$bang' => '', '$profile_uid' => intval($owner), '$preview' => t('Preview'), '$jotplugins' => $jotplugins, '$sourceapp' => App::$sourcename, '$defexpire' => '', '$feature_expire' => false, '$expires' => t('Set expiration date'), '$bbcode' => $mimetype == 'text/bbcode' ? true : false));
    $o .= replace_macros(get_markup_template('edpost_head.tpl'), array('$title' => t('Edit Webpage'), '$delete' => $itm[0]['author_xchan'] === $ob_hash || $itm[0]['owner_xchan'] === $ob_hash ? t('Delete') : false, '$editor' => $editor, '$id' => $itm[0]['id']));
    return $o;
}
Example #10
0
/**
 * Sourced and tag-delivered posts are re-targetted for delivery to the connections of the channel
 * receiving the post. This starts the second delivery chain, by resetting permissions and ensuring
 * that ITEM_UPLINK is set on the parent post, and storing the current owner_xchan as the source_xchan.
 * We'll become the new owner. If called without $parent, this *is* the parent post.
 *
 * @param array $channel
 * @param array $item
 * @param int $item_id
 * @param boolean $parent
 */
function start_delivery_chain($channel, $item, $item_id, $parent)
{
    // Change this copy of the post to a forum head message and deliver to all the tgroup members
    // also reset all the privacy bits to the forum default permissions
    $private = $channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid'] ? 1 : 0;
    $new_public_policy = map_scope($channel['channel_r_stream'], true);
    if (!$private && $new_public_policy) {
        $private = 1;
    }
    $flag_bits = $item['item_flags'] | ITEM_WALL;
    // The message didn't necessarily originate on this site, (we'll honour it if it did),
    // but the parent post of this thread will be reset as a local post, as it is the top of
    // this delivery chain and is coming from this site, regardless of where the original
    // originated.
    if (!$parent) {
        $flag_bits = $flag_bits | ITEM_ORIGIN;
    }
    // unset the nocomment bit if it's there.
    if ($flag_bits & ITEM_NOCOMMENT) {
        $flag_bits = $flag_bits ^ ITEM_NOCOMMENT;
    }
    // maintain the original source, which will be the original item owner and was stored in source_xchan
    // when we created the delivery fork
    if ($parent) {
        $r = q("update item set source_xchan = '%s' where id = %d", dbesc($parent['source_xchan']), intval($item_id));
    } else {
        $flag_bits = $flag_bits | ITEM_UPLINK;
        $r = q("update item set source_xchan = owner_xchan where id = %d", intval($item_id));
    }
    $title = $item['title'];
    $body = $item['body'];
    if ($private) {
        if (!($flag_bits & ITEM_OBSCURED)) {
            $key = get_config('system', 'pubkey');
            $flag_bits = $flag_bits | ITEM_OBSCURED;
            if ($title) {
                $title = json_encode(crypto_encapsulate($title, $key));
            }
            if ($body) {
                $body = json_encode(crypto_encapsulate($body, $key));
            }
        }
    } else {
        if ($flag_bits & ITEM_OBSCURED) {
            $key = get_config('system', 'prvkey');
            $flag_bits = $flag_bits ^ ITEM_OBSCURED;
            if ($title) {
                $title = crypto_unencapsulate(json_decode($title, true), $key);
            }
            if ($body) {
                $body = crypto_unencapsulate(json_decode($body, true), $key);
            }
        }
    }
    $r = q("update item set item_flags = %d, owner_xchan = '%s', allow_cid = '%s', allow_gid = '%s',\n\t\tdeny_cid = '%s', deny_gid = '%s', item_private = %d, public_policy = '%s', comment_policy = '%s', title = '%s', body = '%s'  where id = %d", intval($flag_bits), dbesc($channel['channel_hash']), dbesc($channel['channel_allow_cid']), dbesc($channel['channel_allow_gid']), dbesc($channel['channel_deny_cid']), dbesc($channel['channel_deny_gid']), intval($private), dbesc($new_public_policy), dbesc(map_scope($channel['channel_w_comment'])), dbesc($title), dbesc($body), intval($item_id));
    if ($r) {
        proc_run('php', 'include/notifier.php', 'tgroup', $item_id);
    } else {
        logger('start_delivery_chain: failed to update item');
    }
}
Example #11
0
File: mail.php Project: Mauru/red
function mail_post(&$a)
{
    if (!local_user()) {
        return;
    }
    $replyto = x($_REQUEST, 'replyto') ? notags(trim($_REQUEST['replyto'])) : '';
    $subject = x($_REQUEST, 'subject') ? notags(trim($_REQUEST['subject'])) : '';
    $body = x($_REQUEST, 'body') ? escape_tags(trim($_REQUEST['body'])) : '';
    $recipient = x($_REQUEST, 'messageto') ? notags(trim($_REQUEST['messageto'])) : '';
    $rstr = x($_REQUEST, 'messagerecip') ? notags(trim($_REQUEST['messagerecip'])) : '';
    $expires = x($_REQUEST, 'expires') ? datetime_convert(date_default_timezone_get(), 'UTC', $_REQUEST['expires']) : NULL_DATE;
    // If we have a raw string for a recipient which hasn't been auto-filled,
    // it means they probably aren't in our address book, hence we don't know
    // if we have permission to send them private messages.
    // finger them and find out before we try and send it.
    if (!$recipient) {
        $channel = $a->get_channel();
        $ret = zot_finger($rstr, $channel);
        if (!$ret['success']) {
            notice(t('Unable to lookup recipient.') . EOL);
            return;
        }
        $j = json_decode($ret['body'], true);
        logger('message_post: lookup: ' . $url . ' ' . print_r($j, true));
        if (!($j['success'] && $j['guid'])) {
            notice(t('Unable to communicate with requested channel.'));
            return;
        }
        $x = import_xchan($j);
        if (!$x['success']) {
            notice(t('Cannot verify requested channel.'));
            return;
        }
        $recipient = $x['hash'];
        $their_perms = 0;
        $global_perms = get_perms();
        if ($j['permissions']['data']) {
            $permissions = crypto_unencapsulate($j['permissions'], $channel['channel_prvkey']);
            if ($permissions) {
                $permissions = json_decode($permissions);
            }
            logger('decrypted permissions: ' . print_r($permissions, true), LOGGER_DATA);
        } else {
            $permissions = $j['permissions'];
        }
        foreach ($permissions as $k => $v) {
            if ($v) {
                $their_perms = $their_perms | intval($global_perms[$k][1]);
            }
        }
        if (!($their_perms & PERMS_W_MAIL)) {
            notice(t('Selected channel has private message restrictions. Send failed.'));
            return;
        }
    }
    //	if(feature_enabled(local_user(),'richtext')) {
    //		$body = fix_mce_lf($body);
    //	}
    if (!$recipient) {
        notice('No recipient found.');
        $a->argc = 2;
        $a->argv[1] = 'new';
        return;
    }
    // We have a local_user, let send_message use the session channel and save a lookup
    $ret = send_message(0, $recipient, $body, $subject, $replyto, $expires);
    if (!$ret['success']) {
        notice($ret['message']);
    }
    goaway(z_root() . '/message');
}
Example #12
0
function editwebpage_content(&$a)
{
    // We first need to figure out who owns the webpage, grab it from an argument
    $which = argv(1);
    // $a->get_channel() and stuff don't work here, so we've got to find the owner for ourselves.
    $r = q("select channel_id from channel where channel_address = '%s'", dbesc($which));
    if ($r) {
        $owner = intval($r[0]['channel_id']);
        //logger('owner: ' . print_r($owner,true));
    }
    $is_owner = local_user() && local_user() == $owner ? true : false;
    $o = '';
    // Figure out which post we're editing
    $post_id = argc() > 2 ? intval(argv(2)) : 0;
    if (!$post_id) {
        notice(t('Item not found') . EOL);
        return;
    }
    // Now we've got a post and an owner, let's find out if we're allowed to edit it
    $observer = $a->get_observer();
    $ob_hash = $observer ? $observer['xchan_hash'] : '';
    $perms = get_all_perms($owner, $ob_hash);
    if (!$perms['write_pages']) {
        notice(t('Permission denied.') . EOL);
        return;
    }
    // We've already figured out which item we want and whose copy we need, so we don't need anything fancy here
    $itm = q("SELECT * FROM `item` WHERE `id` = %d and uid = %s LIMIT 1", intval($post_id), intval($owner));
    if ($itm[0]['item_flags'] & ITEM_OBSCURED) {
        $key = get_config('system', 'prvkey');
        if ($itm[0]['title']) {
            $itm[0]['title'] = crypto_unencapsulate(json_decode_plus($itm[0]['title']), $key);
        }
        if ($itm[0]['body']) {
            $itm[0]['body'] = crypto_unencapsulate(json_decode_plus($itm[0]['body']), $key);
        }
    }
    $item_id = q("select * from item_id where service = 'WEBPAGE' and iid = %d limit 1", $itm[0]['id']);
    if ($item_id) {
        $page_title = $item_id[0]['sid'];
    }
    $plaintext = true;
    //	if(feature_enabled($itm[0]['uid'],'richtext'))
    //		$plaintext = false;
    $mimetype = $itm[0]['mimetype'];
    if ($mimetype === 'application/x-php') {
        if (!local_user() || local_user() != $itm[0]['uid']) {
            notice(t('Permission denied.') . EOL);
            return;
        }
    }
    $mimeselect = '';
    if ($mimetype != 'text/bbcode') {
        $plaintext = true;
    }
    if (get_config('system', 'page_mimetype')) {
        $mimeselect = '<input type="hidden" name="mimetype" value="' . $mimetype . '" />';
    } else {
        $mimeselect = mimetype_select($itm[0]['uid'], $mimetype);
    }
    $layout = get_config('system', 'page_layout');
    if ($layout) {
        $layoutselect = '<input type="hidden" name="layout_mid" value="' . $layout . '" />';
    } else {
        $layoutselect = layout_select($itm[0]['uid'], $itm[0]['layout_mid']);
    }
    $o .= replace_macros(get_markup_template('edpost_head.tpl'), array('$title' => t('Edit Webpage')));
    $a->page['htmlhead'] .= replace_macros(get_markup_template('jot-header.tpl'), array('$baseurl' => $a->get_baseurl(), '$editselect' => $plaintext ? 'none' : '/(profile-jot-text|prvmail-text)/', '$ispublic' => '&nbsp;', '$geotag' => $geotag, '$nickname' => $a->user['nickname'], '$confirmdelete' => t('Delete webpage?')));
    $tpl = get_markup_template("jot.tpl");
    $jotplugins = '';
    $jotnets = '';
    call_hooks('jot_tool', $jotplugins);
    call_hooks('jot_networks', $jotnets);
    $channel = $a->get_channel();
    //$tpl = replace_macros($tpl,array('$jotplugins' => $jotplugins));
    //FIXME A return path with $_SESSION doesn't always work for observer - it may WSoD instead of loading a sensible page.  So, send folk to the webpage list.
    $rp = 'webpages/' . $which;
    $o .= replace_macros($tpl, array('$return_path' => $rp, '$webpage' => ITEM_WEBPAGE, '$placeholdpagetitle' => t('Page link title'), '$pagetitle' => $page_title, '$action' => 'item', '$share' => t('Edit'), '$upload' => t('Upload photo'), '$attach' => t('Attach file'), '$weblink' => t('Insert web link'), '$youtube' => t('Insert YouTube video'), '$video' => t('Insert Vorbis [.ogg] video'), '$audio' => t('Insert Vorbis [.ogg] audio'), '$setloc' => t('Set your location'), '$noloc' => t('Clear browser location'), '$wait' => t('Please wait'), '$permset' => t('Permission settings'), '$ptyp' => $itm[0]['type'], '$content' => undo_post_tagging($itm[0]['body']), '$post_id' => $post_id, '$baseurl' => $a->get_baseurl(), '$defloc' => $itm[0]['location'], '$visitor' => $is_owner ? true : false, '$acl' => populate_acl($itm[0], false), '$showacl' => $is_owner ? true : false, '$public' => t('Public post'), '$jotnets' => $jotnets, '$mimeselect' => $mimeselect, '$layoutselect' => $layoutselect, '$title' => htmlspecialchars($itm[0]['title'], ENT_COMPAT, 'UTF-8'), '$placeholdertitle' => t('Set title'), '$category' => '', '$placeholdercategory' => t('Categories (comma-separated list)'), '$emtitle' => t('Example: bob@example.com, mary@example.com'), 'lockstate' => strlen($itm[0]['allow_cid']) || strlen($itm[0]['allow_gid']) || strlen($itm[0]['deny_cid']) || strlen($itm[0]['deny_gid']) ? 'lock' : 'unlock', '$bang' => '', '$profile_uid' => intval($owner), '$preview' => feature_enabled(local_user(), 'preview') ? t('Preview') : '', '$jotplugins' => $jotplugins, '$sourceapp' => t($a->sourcename), '$defexpire' => '', '$feature_expire' => false, '$expires' => t('Set expiration date')));
    $ob = get_observer_hash();
    if ($itm[0]['author_xchan'] === $ob || $itm[0]['owner_xchan'] === $ob) {
        $o .= '<br /><br /><a class="page-delete-link" href="item/drop/' . $itm[0]['id'] . '" >' . t('Delete Webpage') . '</a><br />';
    }
    return $o;
}
Example #13
0
function editpost_content(&$a)
{
    $o = '';
    if (!local_channel()) {
        notice(t('Permission denied.') . EOL);
        return;
    }
    $post_id = argc() > 1 ? intval(argv(1)) : 0;
    if (!$post_id) {
        notice(t('Item not found') . EOL);
        return;
    }
    $itm = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d and author_xchan = '%s' LIMIT 1", intval($post_id), intval(local_channel()), dbesc(get_observer_hash()));
    if (!count($itm)) {
        notice(t('Item is not editable') . EOL);
        return;
    }
    $plaintext = true;
    //	if(feature_enabled(local_channel(),'richtext'))
    //		$plaintext = false;
    $channel = $a->get_channel();
    $a->page['htmlhead'] .= replace_macros(get_markup_template('jot-header.tpl'), array('$baseurl' => $a->get_baseurl(), '$editselect' => $plaintext ? 'none' : '/(profile-jot-text|prvmail-text)/', '$ispublic' => '&nbsp;', '$geotag' => $geotag, '$nickname' => $channel['channel_address'], '$expireswhen' => t('Expires YYYY-MM-DD HH:MM'), '$confirmdelete' => t('Delete item?')));
    if ($itm[0]['item_flags'] & ITEM_OBSCURED) {
        $key = get_config('system', 'prvkey');
        if ($itm[0]['title']) {
            $itm[0]['title'] = crypto_unencapsulate(json_decode_plus($itm[0]['title']), $key);
        }
        if ($itm[0]['body']) {
            $itm[0]['body'] = crypto_unencapsulate(json_decode_plus($itm[0]['body']), $key);
        }
    }
    $tpl = get_markup_template("jot.tpl");
    $jotplugins = '';
    $jotnets = '';
    call_hooks('jot_tool', $jotplugins);
    call_hooks('jot_networks', $jotnets);
    $channel = $a->get_channel();
    //$tpl = replace_macros($tpl,array('$jotplugins' => $jotplugins));
    $voting = feature_enabled(local_channel(), 'consensus_tools');
    $category = '';
    $catsenabled = feature_enabled(local_channel(), 'categories') ? 'categories' : '';
    if ($catsenabled) {
        $itm = fetch_post_tags($itm);
        $cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY);
        foreach ($cats as $cat) {
            if (strlen($category)) {
                $category .= ', ';
            }
            $category .= $cat['term'];
        }
    }
    if ($itm[0]['attach']) {
        $j = json_decode($itm[0]['attach'], true);
        if ($j) {
            foreach ($j as $jj) {
                $itm[0]['body'] .= "\n" . '[attachment]' . basename($jj['href']) . ',' . $jj['revision'] . '[/attachment]' . "\n";
            }
        }
    }
    $cipher = get_pconfig(get_app()->profile['profile_uid'], 'system', 'default_cipher');
    if (!$cipher) {
        $cipher = 'aes256';
    }
    $editor = replace_macros($tpl, array('$return_path' => $_SESSION['return_url'], '$action' => 'item', '$share' => t('Edit'), '$bold' => t('Bold'), '$italic' => t('Italic'), '$underline' => t('Underline'), '$quote' => t('Quote'), '$code' => t('Code'), '$upload' => t('Upload photo'), '$attach' => t('Attach file'), '$weblink' => t('Insert web link'), '$youtube' => t('Insert YouTube video'), '$video' => t('Insert Vorbis [.ogg] video'), '$audio' => t('Insert Vorbis [.ogg] audio'), '$setloc' => t('Set your location'), '$noloc' => t('Clear browser location'), '$voting' => t('Toggle voting'), '$feature_voting' => $voting, '$consensus' => $itm[0]['item_flags'] & ITEM_CONSENSUS ? 1 : 0, '$wait' => t('Please wait'), '$permset' => t('Permission settings'), '$ptyp' => $itm[0]['type'], '$content' => undo_post_tagging($itm[0]['body']), '$post_id' => $post_id, '$parent' => $itm[0]['parent'] != $itm[0]['id'] ? $itm[0]['parent'] : '', '$baseurl' => $a->get_baseurl(), '$defloc' => $channel['channel_location'], '$visitor' => false, '$public' => t('Public post'), '$jotnets' => $jotnets, '$title' => htmlspecialchars($itm[0]['title'], ENT_COMPAT, 'UTF-8'), '$placeholdertitle' => t('Title (optional)'), '$category' => $category, '$placeholdercategory' => t('Categories (optional, comma-separated list)'), '$emtitle' => t('Example: bob@example.com, mary@example.com'), '$lockstate' => $lockstate, '$acl' => '', '$bang' => '', '$profile_uid' => local_channel(), '$preview' => t('Preview'), '$jotplugins' => $jotplugins, '$sourceapp' => t($a->sourcename), '$catsenabled' => $catsenabled, '$defexpire' => datetime_convert('UTC', date_default_timezone_get(), $itm[0]['expires']), '$feature_expire' => feature_enabled(get_app()->profile['profile_uid'], 'content_expire') && !$webpage ? true : false, '$expires' => t('Set expiration date'), '$feature_encrypt' => feature_enabled(get_app()->profile['profile_uid'], 'content_encrypt') && !$webpage ? true : false, '$encrypt' => t('Encrypt text'), '$cipher' => $cipher, '$expiryModalOK' => t('OK'), '$expiryModalCANCEL' => t('Cancel')));
    $o .= replace_macros(get_markup_template('edpost_head.tpl'), array('$title' => t('Edit post'), '$editor' => $editor));
    return $o;
}
Example #14
0
File: post.php Project: Mauru/red
/**
 * @function post_post(&$a)
 *     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)
 *
 * {
 *  "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..."
 * }
 * 
 * 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
 * 
 * {
 *  "type":"pickup",
 *  "url":"http:\/\/example.com",
 *  "callback":"http:\/\/example.com\/post",
 *  "callback_sig":"teE1_fLI...",
 *  "secret":"1eaa...",
 *  "secret_sig":"O7nB4_..."
 * }
 *
 * 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.
 * 
 * 
 * {
 * "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..."
 *     }
 *   }
 * }
 *} 
 *
 * Currently defined message types are 'activity', 'mail', 'profile' 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.
 * 
 * {
 *  "type": "ping"
 * }
 * 
 * On receipt of a ping packet a ping response will be returned:
 *
 * {
 *   "success" : 1,
 *   "site" {
 *       "url":"http:\/\/podunk.edu",
 *       "url_sig":"T8Bp7j5...",
 *       "sitekey": "-----BEGIN PUBLIC KEY-----
 *                  MIICIjANBgkqhkiG9w0BAQE..."
 *    }
 * }
 * 
 * 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.  
 *
 */
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 (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: succesful pickup message received from ' . $data['callback'] . ' ' . count($r) . ' message(s) picked up', LOGGER_DEBUG);
            $ret['success'] = true;
            $ret['pickup'] = array();
            foreach ($r as $rr) {
                $ret['pickup'][] = array('notify' => json_decode($rr['outq_notify'], true), 'message' => json_decode($rr['outq_msg'], true));
                $x = q("delete from outq where outq_hash = '%s' limit 1", 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 */
    $hub = zot_gethub($sender);
    if (!$hub) {
        /** 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'] || !($hub = zot_gethub($sender))) {
            $ret['message'] = 'Hub not available.';
            logger('mod_zot: no hub');
            json_return_and_die($ret);
        }
    }
    // 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 limit 1", dbesc(datetime_convert()), intval($hub['hubloc_id']));
    // a dead hub came back to life - reset any tombstones we might have
    if ($hub['hubloc_status'] & HUBLOC_OFFLINE) {
        q("update hubloc set hubloc_status = (hubloc_status ^ %d) where hubloc_id = %d limit 1", intval(HUBLOC_OFFLINE), intval($hub['hubloc_id']));
        if ($r[0]['hubloc_flags'] & HUBLOC_FLAGS_ORPHANCHECK) {
            q("update hubloc set hubloc_flags = (hubloc_flags ^ %d) where hubloc_id = %d limit 1", intval(HUBLOC_FLAGS_ORPHANCHECK), intval($hub['hubloc_id']));
        }
        q("update xchan set xchan_flags = (xchan_flags ^ %d) where (xchan_flags & %d) and xchan_hash = '%s' limit 1", intval(XCHAN_FLAGS_ORPHAN), intval(XCHAN_FLAGS_ORPHAN), dbesc($hub['hubloc_hash']));
    }
    /** 
     * 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_flags = ( hubloc_flags | %d ) where hubloc_url = '%s' and hubloc_sitekey != '%s' ", intval(HUBLOC_FLAGS_DELETED), dbesc($hub['hubloc_url']), dbesc($hub['hubloc_sitekey']));
    // 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
        q("delete from verify where type = 'auth' and created < UTC_TIMESTAMP() - INTERVAL 10 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 limit 1", 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
            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;
            }
            json_return_and_die($ret);
        }
        json_return_and_die($ret);
    }
    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') {
        $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);
}
Example #15
0
function mail_post(&$a)
{
    if (!local_channel()) {
        return;
    }
    $replyto = x($_REQUEST, 'replyto') ? notags(trim($_REQUEST['replyto'])) : '';
    $subject = x($_REQUEST, 'subject') ? notags(trim($_REQUEST['subject'])) : '';
    $body = x($_REQUEST, 'body') ? escape_tags(trim($_REQUEST['body'])) : '';
    $recipient = x($_REQUEST, 'messageto') ? notags(trim($_REQUEST['messageto'])) : '';
    $rstr = x($_REQUEST, 'messagerecip') ? notags(trim($_REQUEST['messagerecip'])) : '';
    $preview = x($_REQUEST, 'preview') ? intval($_REQUEST['preview']) : 0;
    $expires = x($_REQUEST, 'expires') ? datetime_convert(date_default_timezone_get(), 'UTC', $_REQUEST['expires']) : NULL_DATE;
    // If we have a raw string for a recipient which hasn't been auto-filled,
    // it means they probably aren't in our address book, hence we don't know
    // if we have permission to send them private messages.
    // finger them and find out before we try and send it.
    if (!$recipient) {
        $channel = App::get_channel();
        $ret = zot_finger($rstr, $channel);
        if (!$ret['success']) {
            notice(t('Unable to lookup recipient.') . EOL);
            return;
        }
        $j = json_decode($ret['body'], true);
        logger('message_post: lookup: ' . $url . ' ' . print_r($j, true));
        if (!($j['success'] && $j['guid'])) {
            notice(t('Unable to communicate with requested channel.'));
            return;
        }
        $x = import_xchan($j);
        if (!$x['success']) {
            notice(t('Cannot verify requested channel.'));
            return;
        }
        $recipient = $x['hash'];
        $their_perms = 0;
        $global_perms = get_perms();
        if ($j['permissions']['data']) {
            $permissions = crypto_unencapsulate($j['permissions'], $channel['channel_prvkey']);
            if ($permissions) {
                $permissions = json_decode($permissions);
            }
            logger('decrypted permissions: ' . print_r($permissions, true), LOGGER_DATA);
        } else {
            $permissions = $j['permissions'];
        }
        foreach ($permissions as $k => $v) {
            if ($v) {
                $their_perms = $their_perms | intval($global_perms[$k][1]);
            }
        }
        if (!($their_perms & PERMS_W_MAIL)) {
            notice(t('Selected channel has private message restrictions. Send failed.'));
            // reported issue: let's still save the message and continue. We'll just tell them
            // that nothing useful is likely to happen. They might have spent hours on it.
            //			return;
        }
    }
    //	if(feature_enabled(local_channel(),'richtext')) {
    //		$body = fix_mce_lf($body);
    //	}
    require_once 'include/text.php';
    linkify_tags($a, $body, local_channel());
    if ($preview) {
    }
    if (!$recipient) {
        notice('No recipient found.');
        App::$argc = 2;
        App::$argv[1] = 'new';
        return;
    }
    // We have a local_channel, let send_message use the session channel and save a lookup
    $ret = send_message(0, $recipient, $body, $subject, $replyto, $expires);
    if ($ret['success']) {
        xchan_mail_query($ret['mail']);
        build_sync_packet(0, array('conv' => array($ret['conv']), 'mail' => array(encode_mail($ret['mail'], true))));
    } else {
        notice($ret['message']);
    }
    goaway(z_root() . '/mail/combined');
}
Example #16
0
function bb2diaspora_itembody($item, $force_update = false)
{
    $matches = array();
    if ($item['diaspora_meta'] && !$force_update) {
        $diaspora_meta = json_decode($item['diaspora_meta'], true);
        if ($diaspora_meta) {
            if (array_key_exists('iv', $diaspora_meta)) {
                $key = get_config('system', 'prvkey');
                $meta = json_decode(crypto_unencapsulate($diaspora_meta, $key), true);
            } else {
                $meta = $diaspora_meta;
            }
            if ($meta) {
                logger('bb2diaspora_itembody: cached ');
                $newitem = $item;
                $newitem['body'] = $meta['body'];
                return $newitem['body'];
            }
        }
    }
    create_export_photo_body($item);
    $newitem = $item;
    if (array_key_exists('item_obscured', $item) && intval($item['item_obscured'])) {
        $key = get_config('system', 'prvkey');
        $b = json_decode($item['body'], true);
        // if called from diaspora_process_outbound, this decoding has already been done.
        // Everything else that calls us will not yet be decoded.
        if ($b && is_array($b) && array_key_exists('iv', $b)) {
            $newitem['title'] = $item['title'] ? crypto_unencapsulate(json_decode($item['title'], true), $key) : '';
            $newitem['body'] = $item['body'] ? crypto_unencapsulate(json_decode($item['body'], true), $key) : '';
        }
    }
    bb2diaspora_itemwallwall($newitem);
    $title = $newitem['title'];
    $body = preg_replace('/\\#\\^http/i', 'http', $newitem['body']);
    // protect tags and mentions from hijacking
    if (intval(get_pconfig($item['uid'], 'system', 'prevent_tag_hijacking'))) {
        $new_tag = html_entity_decode('&#x22d5;', ENT_COMPAT, 'UTF-8');
        $new_mention = html_entity_decode('&#xff20;', ENT_COMPAT, 'UTF-8');
        // #-tags
        $body = preg_replace('/\\#\\[url/i', $new_tag . '[url', $body);
        $body = preg_replace('/\\#\\[zrl/i', $new_tag . '[zrl', $body);
        // @-mentions
        $body = preg_replace('/\\@\\!?\\[url/i', $new_mention . '[url', $body);
        $body = preg_replace('/\\@\\!?\\[zrl/i', $new_mention . '[zrl', $body);
    }
    // remove multiple newlines
    do {
        $oldbody = $body;
        $body = str_replace("\n\n\n", "\n\n", $body);
    } while ($oldbody != $body);
    $body = bb2diaspora($body);
    if (strlen($title)) {
        $body = "## " . $title . "\n\n" . $body;
    }
    if ($item['attach']) {
        $cnt = preg_match_all('/href=\\"(.*?)\\"(.*?)title=\\"(.*?)\\"/ism', $item['attach'], $matches, PREG_SET_ORDER);
        if ($cnt) {
            $body .= "\n" . t('Attachments:') . "\n";
            foreach ($matches as $mtch) {
                $body .= '[' . $mtch[3] . '](' . $mtch[1] . ')' . "\n";
            }
        }
    }
    //	logger('bb2diaspora_itembody : ' . $body, LOGGER_DATA);
    return html_entity_decode($body);
}
Example #17
0
function bb2diaspora_itembody($item)
{
    if ($item['diaspora_meta']) {
        $j = json_decode($item['diaspora_meta'], true);
        if ($j && $j['body']) {
            logger('bb2diaspora_itembody: cached ');
            return $j['body'];
        }
    }
    $body = $item['body'];
    if (array_key_exists('item_flags', $item) && $item['item_flags'] & ITEM_OBSCURED) {
        $key = get_config('system', 'prvkey');
        $title = $item['title'] ? crypto_unencapsulate(json_decode($item['title'], true), $key) : '';
        $body = $item['body'] ? crypto_unencapsulate(json_decode($item['body'], true), $key) : '';
    }
    $body = preg_replace('/\\#\\^http/i', 'http', $body);
    // protect tags and mentions from hijacking
    if (intval(get_pconfig($item['uid'], 'system', 'prevent_tag_hijacking'))) {
        $new_tag = html_entity_decode('&#x22d5;', ENT_COMPAT, 'UTF-8');
        $new_mention = html_entity_decode('&#xff20;', ENT_COMPAT, 'UTF-8');
        // #-tags
        $body = preg_replace('/\\#\\[url/i', $new_tag . '[url', $body);
        $body = preg_replace('/\\#\\[zrl/i', $new_tag . '[zrl', $body);
        // @-mentions
        $body = preg_replace('/\\@\\!?\\[url/i', $new_mention . '[url', $body);
        $body = preg_replace('/\\@\\!?\\[zrl/i', $new_mention . '[zrl', $body);
    }
    // remove multiple newlines
    do {
        $oldbody = $body;
        $body = str_replace("\n\n\n", "\n\n", $body);
    } while ($oldbody != $body);
    $body = bb2diaspora($body);
    if (strlen($title)) {
        $body = "## " . $title . "\n\n" . $body;
    }
    if ($item['attach']) {
        $cnt = preg_match_all('/href=\\"(.*?)\\"(.*?)title=\\"(.*?)\\"/ism', $item['attach'], $matches, PREG_SET_ORDER);
        if (cnt) {
            $body .= "\n" . t('Attachments:') . "\n";
            foreach ($matches as $mtch) {
                $body .= '[' . $mtch[3] . '](' . $mtch[1] . ')' . "\n";
            }
        }
    }
    logger('bb2diaspora_itembody : ' . $body);
    return html_entity_decode($body);
}
Example #18
0
function unobscure(&$item)
{
    if (array_key_exists('item_obscured', $item) && intval($item['item_obscured'])) {
        $key = get_config('system', 'prvkey');
        if ($item['title']) {
            $item['title'] = crypto_unencapsulate(json_decode_plus($item['title']), $key);
        }
        if ($item['body']) {
            $item['body'] = crypto_unencapsulate(json_decode_plus($item['body']), $key);
        }
        if (get_config('system', 'item_cache')) {
            q("update item set title = '%s', body = '%s', item_obscured = 0 where id = %d", dbesc($item['title']), dbesc($item['body']), intval($item['id']));
        }
    }
}
Example #19
0
function private_messages_fetch_conversation($channel_id, $messageitem_id, $updateseen = false)
{
    // find the parent_mid of the message being requested
    $r = q("SELECT parent_mid from mail WHERE channel_id = %d and id = %d limit 1", intval($channel_id), intval($messageitem_id));
    if (!$r) {
        return array();
    }
    $messages = q("select * from mail where parent_mid = '%s' and channel_id = %d order by created asc", dbesc($r[0]['parent_mid']), intval($channel_id));
    if (!$messages) {
        return array();
    }
    $chans = array();
    foreach ($messages as $rr) {
        $s = "'" . dbesc(trim($rr['from_xchan'])) . "'";
        if (!in_array($s, $chans)) {
            $chans[] = $s;
        }
        $s = "'" . dbesc(trim($rr['to_xchan'])) . "'";
        if (!in_array($s, $chans)) {
            $chans[] = $s;
        }
    }
    $c = q("select * from xchan where xchan_hash in (" . implode(',', $chans) . ")");
    foreach ($messages as $k => $message) {
        $messages[$k]['from'] = find_xchan_in_array($message['from_xchan'], $c);
        $messages[$k]['to'] = find_xchan_in_array($message['to_xchan'], $c);
        if ($messages[$k]['mail_flags'] & MAIL_OBSCURED) {
            $key = get_config('system', 'prvkey');
            if ($messages[$k]['title']) {
                $messages[$k]['title'] = crypto_unencapsulate(json_decode_plus($messages[$k]['title']), $key);
            }
            if ($messages[$k]['body']) {
                $messages[$k]['body'] = crypto_unencapsulate(json_decode_plus($messages[$k]['body']), $key);
            }
        }
    }
    if ($updateseen) {
        $r = q("UPDATE `mail` SET mail_flags = (mail_flags ^ %d) where not (mail_flags & %d) and parent_mid = '%s' AND channel_id = %d", intval(MAIL_SEEN), intval(MAIL_SEEN), dbesc($r[0]['parent_mid']), intval($channel_id));
    }
    return $messages;
}
Example #20
0
function diaspora_process_outbound(&$a, &$arr)
{
    /*
    
    	We are passed the following array from the notifier, providing everything we need to make delivery decisions.
    
    			$arr = array(
    				'channel' => $channel,
    				'env_recips' => $env_recips,
    				'packet_recips' => $packet_recips,
    				'recipients' => $recipients,
    				'item' => $item,
    				'target_item' => $target_item,
    				'hub' => $hub,
    				'top_level_post' => $top_level_post,
    				'private' => $private,
    				'relay_to_owner' => $relay_to_owner,
    				'uplink' => $uplink,
    				'cmd' => $cmd,
    				'mail' => $mail,
    				'location' => $location,
    				'normal_mode' => $normal_mode,
    				'packet_type' => $packet_type,
    				'walltowall' => $walltowall,
    				'queued' => pass these queued items (outq_hash) back to notifier.php for delivery
    			);
    */
    //	logger('notifier_array: ' . print_r($arr,true), LOGGER_ALL, LOG_INFO);
    // allow this to be set per message
    if (strpos($arr['target_item']['postopts'], 'nodspr') !== false) {
        return;
    }
    $allowed = get_pconfig($arr['channel']['channel_id'], 'system', 'diaspora_allowed');
    if (!intval($allowed)) {
        logger('mod-diaspora: disallowed for channel ' . $arr['channel']['channel_name']);
        return;
    }
    if ($arr['location']) {
        return;
    }
    // send to public relay server - not ready for prime time
    if ($arr['top_level_post'] && !$arr['env_recips']) {
        // Add the relay server to the list of hubs.
        // = array('hubloc_callback' => 'https://relay.iliketoast.net/receive', 'xchan_pubkey' => 'bogus');
    }
    $target_item = $arr['target_item'];
    if ($target_item && array_key_exists('item_obscured', $target_item) && intval($target_item['item_obscured'])) {
        $key = get_config('system', 'prvkey');
        if ($target_item['title']) {
            $target_item['title'] = crypto_unencapsulate(json_decode($target_item['title'], true), $key);
        }
        if ($target_item['body']) {
            $target_item['body'] = crypto_unencapsulate(json_decode($target_item['body'], true), $key);
        }
    }
    $prv_recips = $arr['env_recips'];
    // The Diaspora profile message is unusual in that it is sent privately.
    if ($arr['cmd'] === 'refresh_all' && $arr['recipients']) {
        $prv_recips = array();
        foreach ($arr['recipients'] as $r) {
            $prv_recips[] = array('hash' => trim($r, "'"));
        }
    }
    if ($prv_recips) {
        $hashes = array();
        // re-explode the recipients, but only for this hub/pod
        foreach ($prv_recips as $recip) {
            $hashes[] = "'" . $recip['hash'] . "'";
        }
        $r = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_url = '%s' \n\t\t\tand xchan_hash in (" . implode(',', $hashes) . ") and xchan_network in ('diaspora', 'friendica-over-diaspora') ", dbesc($arr['hub']['hubloc_url']));
        if (!$r) {
            logger('diaspora_process_outbound: no recipients');
            return;
        }
        foreach ($r as $contact) {
            if (!deliverable_singleton($arr['channel']['channel_id'], $contact)) {
                logger('not deliverable from this hub');
                continue;
            }
            if ($arr['packet_type'] == 'refresh') {
                $qi = diaspora_profile_change($arr['channel'], $contact);
                if ($qi) {
                    $arr['queued'][] = $qi;
                }
                return;
            }
            if ($arr['mail']) {
                $qi = diaspora_send_mail($arr['item'], $arr['channel'], $contact);
                if ($qi) {
                    $arr['queued'][] = $qi;
                }
                continue;
            }
            if (!$arr['normal_mode']) {
                continue;
            }
            // special handling for send_upstream to public post
            // all other public posts processed as public batches further below
            if (!$arr['private'] && $arr['relay_to_owner']) {
                $qi = diaspora_send_upstream($target_item, $arr['channel'], $contact, true);
                if ($qi) {
                    $arr['queued'][] = $qi;
                }
                continue;
            }
            if (!$contact['xchan_pubkey']) {
                continue;
            }
            if (intval($target_item['item_deleted']) && ($target_item['mid'] === $target_item['parent_mid'] || $arr['relay_to_owner'])) {
                // send both top-level retractions and relayable retractions for owner to relay
                $qi = diaspora_send_retraction($target_item, $arr['channel'], $contact);
                if ($qi) {
                    $arr['queued'][] = $qi;
                }
                continue;
            } elseif ($arr['relay_to_owner'] || $arr['uplink']) {
                // send comments and likes to owner to relay
                $qi = diaspora_send_upstream($target_item, $arr['channel'], $contact, false, $arr['uplink'] && !$arr['relay_to_owner'] ? true : false);
                if ($qi) {
                    $arr['queued'][] = $qi;
                }
                continue;
            } elseif ($target_item['mid'] !== $target_item['parent_mid']) {
                // we are the relay - send comments, likes and relayable_retractions
                // (of comments and likes) to our conversants
                $qi = diaspora_send_downstream($target_item, $arr['channel'], $contact);
                if ($qi) {
                    $arr['queued'][] = $qi;
                }
                continue;
            } elseif ($arr['top_level_post']) {
                $qi = diaspora_send_status($target_item, $arr['channel'], $contact);
                if ($qi) {
                    foreach ($qi as $q) {
                        $arr['queued'][] = $q;
                    }
                }
                continue;
            }
        }
    } else {
        // public message
        $contact = $arr['hub'];
        if (intval($target_item['item_deleted']) && $target_item['mid'] === $target_item['parent_mid']) {
            // top-level retraction
            logger('delivery: diaspora retract: ' . $loc);
            $qi = diaspora_send_retraction($target_item, $arr['channel'], $contact, true);
            if ($qi) {
                $arr['queued'][] = $qi;
            }
            return;
        } elseif ($target_item['mid'] !== $target_item['parent_mid']) {
            // we are the relay - send comments, likes and relayable_retractions to our conversants
            logger('delivery: diaspora relay: ' . $loc);
            $qi = diaspora_send_downstream($target_item, $arr['channel'], $contact, true);
            if ($qi) {
                $arr['queued'][] = $qi;
            }
            return;
        } elseif ($arr['top_level_post']) {
            if (perm_is_allowed($arr['channel']['channel_id'], '', 'view_stream')) {
                logger('delivery: diaspora status: ' . $loc);
                $qi = diaspora_send_status($target_item, $arr['channel'], $contact, true);
                if ($qi) {
                    foreach ($qi as $q) {
                        $arr['queued'][] = $q;
                    }
                }
                return;
            }
        }
    }
}
Example #21
0
/**
 * @brief This function is called pre-deliver to see if a post matches the criteria to be tag delivered.
 *
 * We don't actually do anything except check that it matches the criteria.
 * This is so that the channel with tag_delivery enabled can receive the post even if they turn off
 * permissions for the sender to send their stream. tag_deliver() can't be called until the post is actually stored.
 * By then it would be too late to reject it.
 */
function tgroup_check($uid, $item)
{
    $mention = false;
    // check that the message originated elsewhere and is a top-level post
    // or is a followup and we have already accepted the top level post as an uplink
    if ($item['mid'] != $item['parent_mid']) {
        $r = q("select id from item where mid = '%s' and uid = %d and item_uplink = 1 limit 1", dbesc($item['parent_mid']), intval($uid));
        if ($r) {
            return true;
        }
        return false;
    }
    if (!perm_is_allowed($uid, $item['author_xchan'], 'tag_deliver')) {
        return false;
    }
    $u = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1", intval($uid));
    if (!$u) {
        return false;
    }
    $terms = get_terms_oftype($item['term'], TERM_MENTION);
    if ($terms) {
        logger('tgroup_check: post mentions: ' . print_r($terms, true), LOGGER_DATA);
    }
    $link = normalise_link($u[0]['xchan_url']);
    if ($terms) {
        foreach ($terms as $term) {
            if (link_compare($term['url'], $link)) {
                $mention = true;
                break;
            }
        }
    }
    if ($mention) {
        logger('tgroup_check: mention found for ' . $u[0]['channel_name']);
    } else {
        return false;
    }
    // At this point we've determined that the person receiving this post was mentioned in it.
    // Now let's check if this mention was inside a reshare so we don't spam a forum
    // note: $term has been set to the matching term
    $body = $item['body'];
    if (array_key_exists('item_obscured', $item) && intval($item['item_obscured']) && $body) {
        $key = get_config('system', 'prvkey');
        $body = crypto_unencapsulate(json_decode($body, true), $key);
    }
    $body = preg_replace('/\\[share(.*?)\\[\\/share\\]/', '', $body);
    //	$pattern = '/@\!?\[zrl\=' . preg_quote($term['url'],'/') . '\]' . preg_quote($term['term'] . '+','/') . '\[\/zrl\]/';
    $pattern = '/@\\!?\\[zrl\\=([^\\]]*?)\\]((?:.(?!\\[zrl\\=))*?)\\+\\[\\/zrl\\]/';
    $found = false;
    $matches = array();
    if (preg_match_all($pattern, $body, $matches, PREG_SET_ORDER)) {
        $max_forums = get_config('system', 'max_tagged_forums');
        if (!$max_forums) {
            $max_forums = 2;
        }
        $matched_forums = 0;
        foreach ($matches as $match) {
            $matched_forums++;
            if ($term['url'] === $match[1] && $term['term'] === $match[2]) {
                if ($matched_forums <= $max_forums) {
                    $found = true;
                    break;
                }
                logger('forum ' . $term['term'] . ' exceeded max_tagged_forums - ignoring');
            }
        }
    }
    if (!$found) {
        logger('tgroup_check: mention was in a reshare or exceeded max_tagged_forums - ignoring');
        return false;
    }
    return true;
}
Example #22
0
function diaspora_send_relay($item, $owner, $contact, $public_batch = false)
{
    $a = get_app();
    $myaddr = $owner['channel_address'] . '@' . get_app()->get_hostname();
    $text = bb2diaspora_itembody($item);
    $body = $text;
    // Diaspora doesn't support threaded comments, but some
    // versions of Diaspora (i.e. Diaspora-pistos) support
    // likes on comments
    // That version is now dead so detect a "sublike" and
    // just send it as an activity.
    $sublike = false;
    if ($item['verb'] === ACTIVITY_LIKE) {
        if ($item['thr_parent'] && $item['thr_parent'] !== $item['parent_mid']) {
            $sublike = true;
        }
    }
    // The first item in the `item` table with the parent id is the parent. However, MySQL doesn't always
    // return the items ordered by `item`.`id`, in which case the wrong item is chosen as the parent.
    // The only item with `parent` and `id` as the parent id is the parent item.
    $p = q("select * from item where parent = %d and id = %d limit 1", intval($item['parent']), intval($item['parent']));
    if ($p) {
        $parent = $p[0];
    } else {
        logger('diaspora_send_relay: no parent');
        return;
    }
    $like = false;
    $relay_retract = false;
    $sql_sign_id = 'iid';
    if (intval($item['item_deleted'])) {
        $relay_retract = true;
        $target_type = $item['verb'] === ACTIVITY_LIKE && !$sublike ? 'Like' : 'Comment';
        $sql_sign_id = 'retract_iid';
        $tpl = get_markup_template('diaspora_relayable_retraction.tpl', 'addon/diaspora');
    } elseif ($item['verb'] === ACTIVITY_LIKE && !$sublike) {
        $like = true;
        $target_type = $parent['mid'] === $parent['parent_mid'] ? 'Post' : 'Comment';
        //		$positive = (intval($item['item_deleted']) ? 'false' : 'true');
        $positive = 'true';
        $tpl = get_markup_template('diaspora_like_relay.tpl', 'addon/diaspora');
    } else {
        // item is a comment
        $tpl = get_markup_template('diaspora_comment_relay.tpl', 'addon/diaspora');
    }
    $diaspora_meta = $item['diaspora_meta'] ? json_decode($item['diaspora_meta'], true) : '';
    if ($diaspora_meta) {
        if (array_key_exists('iv', $diaspora_meta)) {
            $key = get_config('system', 'prvkey');
            $meta = json_decode(crypto_unencapsulate($diaspora_meta, $key), true);
        } else {
            $meta = $diaspora_meta;
        }
        $sender_signed_text = $meta['signed_text'];
        $authorsig = $meta['signature'];
        $handle = $meta['signer'];
        $text = $meta['body'];
    } else {
        logger('diaspora_send_relay: original author signature not found');
    }
    /* Since the author signature is only checked by the parent, not by the relay recipients,
     * I think it may not be necessary for us to do so much work to preserve all the original
     * signatures. The important thing that Diaspora DOES need is the original creator's handle.
     * Let's just generate that and forget about all the original author signature stuff.
     *
     * Note: this might be more of an problem if we want to support likes on comments for older
     * versions of Diaspora (diaspora-pistos), but since there are a number of problems with
     * doing that, let's ignore it for now.
     *
     *
     */
    // bug - nomadic identity may/will affect diaspora_handle_from_contact
    if (!$handle) {
        if ($item['author_xchan'] === $owner['channel_hash']) {
            $handle = $owner['channel_address'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(), '://') + 3);
        } else {
            $handle = diaspora_handle_from_contact($item['author_xchan']);
        }
    }
    if (!$handle) {
        logger('diaspora_send_relay: no handle');
        return;
    }
    if (!$sender_signed_text) {
        if ($relay_retract) {
            $sender_signed_text = $item['mid'] . ';' . $target_type;
        } elseif ($like) {
            $sender_signed_text = $positive . ';' . $item['mid'] . ';' . $target_type . ';' . $parent['mid'] . ';' . $handle;
        } else {
            $sender_signed_text = $item['mid'] . ';' . $parent['mid'] . ';' . $text . ';' . $handle;
        }
    }
    // Sign the relayable with the top-level owner's signature
    //
    // We'll use the $sender_signed_text that we just created, instead of the $signed_text
    // stored in the database, because that provides the best chance that Diaspora will
    // be able to reconstruct the signed text the same way we did. This is particularly a
    // concern for the comment, whose signed text includes the text of the comment. The
    // smallest change in the text of the comment, including removing whitespace, will
    // make the signature verification fail. Since we translate from BB code to Diaspora's
    // markup at the top of this function, which is AFTER we placed the original $signed_text
    // in the database, it's hazardous to trust the original $signed_text.
    $parentauthorsig = base64_encode(rsa_sign($sender_signed_text, $owner['channel_prvkey'], 'sha256'));
    if (!$text) {
        logger('diaspora_send_relay: no text');
    }
    $msg = replace_macros($tpl, array('$guid' => xmlify($item['mid']), '$parent_guid' => xmlify($parent['mid']), '$target_type' => xmlify($target_type), '$authorsig' => xmlify($authorsig), '$parentsig' => xmlify($parentauthorsig), '$body' => xmlify($text), '$positive' => xmlify($positive), '$handle' => xmlify($handle)));
    logger('diaspora_send_relay: base message: ' . $msg, LOGGER_DATA);
    $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg, $owner, $contact, $owner['channel_prvkey'], $contact['xchan_pubkey'], $public_batch)));
    return diaspora_queue($owner, $contact, $slap, $public_batch, $item['mid']);
}
Example #23
0
function diaspora_process_outbound($arr)
{
    /*
    
    	We are passed the following array from the notifier, providing everything we need to make delivery decisions.
    
    			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,
    				'fsuggest' => $fsuggest,
    				'normal_mode' => $normal_mode,
    				'packet_type' => $packet_type,
    				'walltowall' => $walltowall,
    			));
    */
    $target_item = $arr['target_item'];
    if ($target_item && array_key_exists('item_flags', $target_item) && $target_item['item_flags'] & ITEM_OBSCURED) {
        $key = get_config('system', 'prvkey');
        if ($target_item['title']) {
            $target_item['title'] = crypto_unencapsulate(json_decode($target_item['title'], true), $key);
        }
        if ($target_item['body']) {
            $target_item['body'] = crypto_unencapsulate(json_decode($target_item['body'], true), $key);
        }
    }
    if ($arr['walltowall']) {
        return;
    }
    if ($arr['env_recips']) {
        $hashes = array();
        // re-explode the recipients, but only for this hub/pod
        foreach ($arr['env_recips'] as $recip) {
            $hashes[] = "'" . $recip['hash'] . "'";
        }
        $r = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_url = '%s' \n\t\t\tand xchan_hash in (" . implode(',', $hashes) . ") and xchan_network in ('diaspora', 'friendica-over-diaspora') ", dbesc($arr['hub']['hubloc_url']));
        if (!$r) {
            logger('diaspora_process_outbound: no recipients');
            return;
        }
        foreach ($r as $contact) {
            if ($arr['mail']) {
                diaspora_send_mail($arr['item'], $arr['channel'], $contact);
                continue;
            }
            if (!$arr['normal_mode']) {
                continue;
            }
            // special handling for followup to public post
            // all other public posts processed as public batches further below
            if (!$arr['private'] && $arr['followup']) {
                diaspora_send_followup($target_item, $arr['channel'], $contact, true);
                continue;
            }
            if (!$contact['xchan_pubkey']) {
                continue;
            }
            if (activity_match($target_item['verb'], ACTIVITY_DISLIKE)) {
                continue;
            } elseif ($target_item['item_restrict'] & ITEM_DELETED && ($target_item['mid'] === $target_item['parent_mid'] || $arr['followup'])) {
                // send both top-level retractions and relayable retractions for owner to relay
                diaspora_send_retraction($target_item, $arr['channel'], $contact);
                continue;
            } elseif ($arr['followup']) {
                // send comments and likes to owner to relay
                diaspora_send_followup($target_item, $arr['channel'], $contact);
                continue;
            } elseif ($target_item['mid'] !== $target_item['parent_mid']) {
                // we are the relay - send comments, likes and relayable_retractions
                // (of comments and likes) to our conversants
                diaspora_send_relay($target_item, $arr['channel'], $contact);
                continue;
            } elseif ($arr['top_level_post']) {
                diaspora_send_status($target_item, $arr['channel'], $contact);
                continue;
            }
        }
    } else {
        // public message
        $contact = $arr['hub'];
        if ($target_item['verb'] === ACTIVITY_DISLIKE) {
            // unsupported
            return;
        } elseif ($target_item['deleted'] && $target_item['mid'] === $target_item['parent_mod']) {
            // top-level retraction
            logger('delivery: diaspora retract: ' . $loc);
            diaspora_send_retraction($target_item, $arr['channel'], $contact, true);
            return;
        } elseif ($target_item['mid'] !== $target_item['parent_mid']) {
            // we are the relay - send comments, likes and relayable_retractions to our conversants
            logger('delivery: diaspora relay: ' . $loc);
            diaspora_send_relay($target_item, $arr['channel'], $contact, true);
            return;
        } elseif ($arr['top_level_post']) {
            // currently no workable solution for sending walltowall
            logger('delivery: diaspora status: ' . $loc);
            diaspora_send_status($target_item, $arr['channel'], $contact, true);
            return;
        }
    }
}
Example #24
0
function diaspora_send_downstream($item, $owner, $contact, $public_batch = false)
{
    $a = get_app();
    $myaddr = $owner['channel_address'] . '@' . App::get_hostname();
    $text = bb2diaspora_itembody($item);
    $body = $text;
    // Diaspora doesn't support threaded comments, but some
    // versions of Diaspora (i.e. Diaspora-pistos) support
    // likes on comments
    // That version is now dead so detect a "sublike" and
    // just send it as an activity.
    $sublike = false;
    if ($item['verb'] === ACTIVITY_LIKE) {
        if ($item['thr_parent'] && $item['thr_parent'] !== $item['parent_mid']) {
            $sublike = true;
        }
    }
    // The first item in the `item` table with the parent id is the parent. However, MySQL doesn't always
    // return the items ordered by `item`.`id`, in which case the wrong item is chosen as the parent.
    // The only item with `parent` and `id` as the parent id is the parent item.
    $p = q("select * from item where parent = %d and id = %d limit 1", intval($item['parent']), intval($item['parent']));
    if ($p) {
        $parent = $p[0];
    } else {
        logger('diaspora_send_downstream: no parent');
        return;
    }
    $xmlout = diaspora_fields_to_xml(get_iconfig($item, 'diaspora', 'fields'));
    $like = false;
    $relay_retract = false;
    $sql_sign_id = 'iid';
    if (intval($item['item_deleted'])) {
        $relay_retract = true;
        $target_type = $item['verb'] === ACTIVITY_LIKE && !$sublike ? 'Like' : 'Comment';
        $sql_sign_id = 'retract_iid';
        $tpl = get_markup_template('diaspora_relayable_retraction.tpl', 'addon/diaspora');
    } elseif ($item['verb'] === ACTIVITY_LIKE && !$sublike && $xmlout) {
        $like = true;
        $target_type = $parent['mid'] === $parent['parent_mid'] ? 'Post' : 'Comment';
        //		$positive = (intval($item['item_deleted']) ? 'false' : 'true');
        $positive = 'true';
        $tpl = get_markup_template('diaspora_like_relay.tpl', 'addon/diaspora');
    } else {
        // item is a comment
        $tpl = get_markup_template('diaspora_comment_relay.tpl', 'addon/diaspora');
    }
    $diaspora_meta = $item['diaspora_meta'] ? json_decode($item['diaspora_meta'], true) : '';
    if ($diaspora_meta) {
        if (array_key_exists('iv', $diaspora_meta)) {
            $key = get_config('system', 'prvkey');
            $meta = json_decode(crypto_unencapsulate($diaspora_meta, $key), true);
        } else {
            $meta = $diaspora_meta;
        }
        $sender_signed_text = $meta['signed_text'];
        $authorsig = $meta['signature'];
        $handle = $meta['signer'];
        $text = $meta['body'];
    } else {
        logger('diaspora_send_downstream: original author signature not found');
    }
    /* Since the author signature is only checked by the parent, not by the relay recipients,
     * I think it may not be necessary for us to do so much work to preserve all the original
     * signatures. The important thing that Diaspora DOES need is the original creator's handle.
     * Let's just generate that and forget about all the original author signature stuff.
     *
     * Note: this might be more of an problem if we want to support likes on comments for older
     * versions of Diaspora (diaspora-pistos), but since there are a number of problems with
     * doing that, let's ignore it for now.
     *
     *
     */
    // bug - nomadic identity may/will affect diaspora_handle_from_contact
    if (!$handle) {
        $handle = $owner['channel_address'] . '@' . App::get_hostname();
    }
    if (!$sender_signed_text) {
        if ($relay_retract) {
            $sender_signed_text = $item['mid'] . ';' . $target_type;
        } elseif ($like) {
            $sender_signed_text = $positive . ';' . $item['mid'] . ';' . $target_type . ';' . $parent['mid'] . ';' . $handle;
        } else {
            $sender_signed_text = $item['mid'] . ';' . $parent['mid'] . ';' . $text . ';' . $handle;
        }
    }
    // The relayable may have arrived from somebody who provided no Diaspora Comment Virus.
    // We check for this above in bb2diaspora_itembody. In that case we will have generated
    // the body as a "wall-to-wall" post, and the author_signature will now be our own.
    if (!$xmlout && !$authorsig) {
        $authorsig = base64_encode(rsa_sign($sender_signed_text, $owner['channel_prvkey'], 'sha256'));
    }
    // Sign the relayable with the top-level owner's signature
    $parentauthorsig = base64_encode(rsa_sign($sender_signed_text, $owner['channel_prvkey'], 'sha256'));
    if (!$text) {
        logger('diaspora_send_downstream: no text');
    }
    $msg = replace_macros($tpl, array('$xml' => $xmlout, '$guid' => xmlify($item['mid']), '$parent_guid' => xmlify($parent['mid']), '$target_type' => xmlify($target_type), '$authorsig' => xmlify($authorsig), '$parentsig' => xmlify($parentauthorsig), '$body' => xmlify($text), '$positive' => xmlify($positive), '$handle' => xmlify($handle)));
    logger('diaspora_send_downstream: base message: ' . $msg, LOGGER_DATA);
    $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg, $owner, $contact, $owner['channel_prvkey'], $contact['xchan_pubkey'], $public_batch)));
    return diaspora_queue($owner, $contact, $slap, $public_batch, $item['mid']);
}
Example #25
0
function new_contact($uid, $url, $channel, $interactive = false, $confirm = false)
{
    $result = array('success' => false, 'message' => '');
    $is_red = false;
    $is_http = strpos($url, '://') !== false ? true : false;
    if ($is_http && substr($url, -1, 1) === '/') {
        $url = substr($url, 0, -1);
    }
    if (!allowed_url($url)) {
        $result['message'] = t('Channel is blocked on this site.');
        return $result;
    }
    if (!$url) {
        $result['message'] = t('Channel location missing.');
        return $result;
    }
    // check service class limits
    $r = q("select count(*) as total from abook where abook_channel = %d and abook_self = 0 ", intval($uid));
    if ($r) {
        $total_channels = $r[0]['total'];
    }
    if (!service_class_allows($uid, 'total_channels', $total_channels)) {
        $result['message'] = upgrade_message();
        return $result;
    }
    $arr = array('url' => $url, 'channel' => array());
    call_hooks('follow', $arr);
    if ($arr['channel']['success']) {
        $ret = $arr['channel'];
    } elseif (!$is_http) {
        $ret = Zotlabs\Zot\Finger::run($url, $channel);
    }
    if ($ret && is_array($ret) && $ret['success']) {
        $is_red = true;
        $j = $ret;
    }
    $my_perms = get_channel_default_perms($uid);
    $role = get_pconfig($uid, 'system', 'permissions_role');
    if ($role) {
        $x = \Zotlabs\Access\PermissionRoles::role_perms($role);
        if ($x['perms_connect']) {
            $my_perms = $x['perms_connect'];
        }
    }
    if ($is_red && $j) {
        logger('follow: ' . $url . ' ' . print_r($j, true), LOGGER_DEBUG);
        if (!($j['success'] && $j['guid'])) {
            $result['message'] = t('Response from remote channel was incomplete.');
            logger('mod_follow: ' . $result['message']);
            return $result;
        }
        // Premium channel, set confirm before callback to avoid recursion
        if (array_key_exists('connect_url', $j) && $interactive && !$confirm) {
            goaway(zid($j['connect_url']));
        }
        // do we have an xchan and hubloc?
        // If not, create them.
        $x = import_xchan($j);
        if (array_key_exists('deleted', $j) && intval($j['deleted'])) {
            $result['message'] = t('Channel was deleted and no longer exists.');
            return $result;
        }
        if (!$x['success']) {
            return $x;
        }
        $xchan_hash = $x['hash'];
        if (array_key_exists('permissions', $j) && array_key_exists('data', $j['permissions'])) {
            $permissions = crypto_unencapsulate(array('data' => $j['permissions']['data'], 'key' => $j['permissions']['key'], 'iv' => $j['permissions']['iv']), $channel['channel_prvkey']);
            if ($permissions) {
                $permissions = json_decode($permissions, true);
            }
            logger('decrypted permissions: ' . print_r($permissions, true), LOGGER_DATA);
        } else {
            $permissions = $j['permissions'];
        }
        if (is_array($permissions) && $permissions) {
            foreach ($permissions as $k => $v) {
                set_abconfig($channel['channel_uid'], $xchan_hash, 'their_perms', $k, intval($v));
            }
        }
    } else {
        $xchan_hash = '';
        $r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' limit 1", dbesc($url), dbesc($url));
        if (!$r) {
            // attempt network auto-discovery
            $d = discover_by_webbie($url);
            if (!$d && $is_http) {
                // try RSS discovery
                if (get_config('system', 'feed_contacts')) {
                    $d = discover_by_url($url);
                } else {
                    $result['message'] = t('Protocol disabled.');
                    return $result;
                }
            }
            if ($d) {
                $r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' limit 1", dbesc($url), dbesc($url));
            }
        }
        // if discovery was a success we should have an xchan record in $r
        if ($r) {
            $xchan = $r[0];
            $xchan_hash = $r[0]['xchan_hash'];
            $their_perms = 0;
        }
    }
    if (!$xchan_hash) {
        $result['message'] = t('Channel discovery failed.');
        logger('follow: ' . $result['message']);
        return $result;
    }
    $allowed = $is_red || $r[0]['xchan_network'] === 'rss' ? 1 : 0;
    $x = array('channel_id' => $uid, 'follow_address' => $url, 'xchan' => $r[0], 'allowed' => $allowed, 'singleton' => 0);
    call_hooks('follow_allow', $x);
    if (!$x['allowed']) {
        $result['message'] = t('Protocol disabled.');
        return $result;
    }
    $singleton = intval($x['singleton']);
    $aid = $channel['channel_account_id'];
    $hash = get_observer_hash();
    $default_group = $channel['channel_default_group'];
    if ($xchan['xchan_network'] === 'rss') {
        // check service class feed limits
        $r = q("select count(*) as total from abook where abook_account = %d and abook_feed = 1 ", intval($aid));
        if ($r) {
            $total_feeds = $r[0]['total'];
        }
        if (!service_class_allows($uid, 'total_feeds', $total_feeds)) {
            $result['message'] = upgrade_message();
            return $result;
        }
    }
    if ($hash == $xchan_hash) {
        $result['message'] = t('Cannot connect to yourself.');
        return $result;
    }
    $r = q("select abook_xchan, abook_instance from abook where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($xchan_hash), intval($uid));
    if ($is_http) {
        // Always set these "remote" permissions for feeds since we cannot interact with them
        // to negotiate a suitable permission response
        set_abconfig($uid, $xchan_hash, 'their_perms', 'view_stream', 1);
        set_abconfig($uid, $xchan_hash, 'their_perms', 'republish', 1);
    }
    if ($r) {
        $abook_instance = $r[0]['abook_instance'];
        if ($singleton && strpos($abook_instance, z_root()) === false) {
            if ($abook_instance) {
                $abook_instance .= ',';
            }
            $abook_instance .= z_root();
        }
        $x = q("update abook set abook_instance = '%s' where abook_id = %d", dbesc($abook_instance), intval($r[0]['abook_id']));
    } else {
        $closeness = get_pconfig($uid, 'system', 'new_abook_closeness');
        if ($closeness === false) {
            $closeness = 80;
        }
        $r = q("insert into abook ( abook_account, abook_channel, abook_closeness, abook_xchan, abook_feed, abook_created, abook_updated, abook_instance )\n\t\t\tvalues( %d, %d, %d, '%s', %d, '%s', '%s', '%s' ) ", intval($aid), intval($uid), intval($closeness), dbesc($xchan_hash), intval($is_http ? 1 : 0), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($singleton ? z_root() : ''));
    }
    if (!$r) {
        logger('mod_follow: abook creation failed');
    }
    $all_perms = \Zotlabs\Access\Permissions::Perms();
    if ($all_perms) {
        foreach ($all_perms as $k => $v) {
            if (in_array($k, $my_perms)) {
                set_abconfig($uid, $xchan_hash, 'my_perms', $k, 1);
            } else {
                set_abconfig($uid, $xchan_hash, 'my_perms', $k, 0);
            }
        }
    }
    $r = q("select abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash \n\t\twhere abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($xchan_hash), intval($uid));
    if ($r) {
        $result['abook'] = $r[0];
        Zotlabs\Daemon\Master::Summon(array('Notifier', 'permission_create', $result['abook']['abook_id']));
    }
    $arr = array('channel_id' => $uid, 'channel' => $channel, 'abook' => $result['abook']);
    call_hooks('follow', $arr);
    /** If there is a default group for this channel, add this connection to it */
    if ($default_group) {
        require_once 'include/group.php';
        $g = group_rec_byhash($uid, $default_group);
        if ($g) {
            group_add_member($uid, '', $xchan_hash, $g['id']);
        }
    }
    $result['success'] = true;
    return $result;
}
Example #26
0
function unobscure_mail(&$item)
{
    if (array_key_exists('mail_flags', $item) && $item['mail_flags'] & MAIL_OBSCURED) {
        $key = get_config('system', 'prvkey');
        if ($item['title']) {
            $item['title'] = crypto_unencapsulate(json_decode_plus($item['title']), $key);
        }
        if ($item['body']) {
            $item['body'] = crypto_unencapsulate(json_decode_plus($item['body']), $key);
        }
    }
}
Example #27
0
File: zot.php Project: 23n/hubzilla
/**
 * @brief Process incoming array of messages.
 *
 * Process an incoming array of messages which were obtained via pickup, and
 * import, update, delete as directed.
 *
 * The message types handled here are 'activity' (e.g. posts), 'mail' ,
 * 'profile', 'location' and 'channel_sync'.
 *
 * @param array $arr
 *  'pickup' structure returned from remote site
 * @param string $sender_url
 *  the url specified by the sender in the initial communication.
 *  We will verify the sender and url in each returned message structure and
 *  also verify that all the messages returned match the site url that we are
 *  currently processing.
 *
 * @returns array
 * suitable for logging remotely, enumerating the processing results of each message/recipient combination
 *  * [0] => \e string $channel_hash
 *  * [1] => \e string $delivery_status
 *  * [2] => \e string $address
 */
function zot_import($arr, $sender_url)
{
    $data = json_decode($arr['body'], true);
    if (!$data) {
        logger('zot_import: empty body');
        return array();
    }
    if (array_key_exists('iv', $data)) {
        $data = json_decode(crypto_unencapsulate($data, get_config('system', 'prvkey')), true);
    }
    if (!$data['success']) {
        if ($data['message']) {
            logger('remote pickup failed: ' . $data['message']);
        }
        return false;
    }
    $incoming = $data['pickup'];
    $return = array();
    if (is_array($incoming)) {
        foreach ($incoming as $i) {
            if (!is_array($i)) {
                logger('incoming is not an array');
                continue;
            }
            $result = null;
            if (array_key_exists('iv', $i['notify'])) {
                $i['notify'] = json_decode(crypto_unencapsulate($i['notify'], get_config('system', 'prvkey')), true);
            }
            logger('zot_import: notify: ' . print_r($i['notify'], true), LOGGER_DATA);
            $hub = zot_gethub($i['notify']['sender']);
            if (!$hub || $hub['hubloc_url'] != $sender_url) {
                logger('zot_import: potential forgery: wrong site for sender: ' . $sender_url . ' != ' . print_r($i['notify'], true));
                continue;
            }
            $message_request = array_key_exists('message_id', $i['notify']) ? true : false;
            if ($message_request) {
                logger('processing message request');
            }
            $i['notify']['sender']['hash'] = make_xchan_hash($i['notify']['sender']['guid'], $i['notify']['sender']['guid_sig']);
            $deliveries = null;
            if (array_key_exists('message', $i) && array_key_exists('type', $i['message']) && $i['message']['type'] === 'rating') {
                // rating messages are processed only by directory servers
                logger('Rating received: ' . print_r($arr, true), LOGGER_DATA);
                $result = process_rating_delivery($i['notify']['sender'], $i['message']);
                continue;
            }
            if (array_key_exists('recipients', $i['notify']) && count($i['notify']['recipients'])) {
                logger('specific recipients');
                $recip_arr = array();
                foreach ($i['notify']['recipients'] as $recip) {
                    if (is_array($recip)) {
                        $recip_arr[] = make_xchan_hash($recip['guid'], $recip['guid_sig']);
                    }
                }
                $r = false;
                if ($recip_arr) {
                    stringify_array_elms($recip_arr);
                    $recips = implode(',', $recip_arr);
                    $r = q("select channel_hash as hash from channel where channel_hash in ( " . $recips . " ) \n\t\t\t\t\t\tand channel_removed = 0 ");
                }
                if (!$r) {
                    logger('recips: no recipients on this site');
                    continue;
                }
                // It's a specifically targetted post. If we were sent a public_scope hint (likely),
                // get rid of it so that it doesn't get stored and cause trouble.
                if ($i && is_array($i) && array_key_exists('message', $i) && is_array($i['message']) && $i['message']['type'] === 'activity' && array_key_exists('public_scope', $i['message'])) {
                    unset($i['message']['public_scope']);
                }
                $deliveries = $r;
                // We found somebody on this site that's in the recipient list.
            } else {
                if ($i['message'] && array_key_exists('flags', $i['message']) && in_array('private', $i['message']['flags']) && $i['message']['type'] === 'activity') {
                    if (array_key_exists('public_scope', $i['message']) && $i['message']['public_scope'] === 'public') {
                        // This should not happen but until we can stop it...
                        logger('private message was delivered with no recipients.');
                        continue;
                    }
                }
                logger('public post');
                // Public post. look for any site members who are or may be accepting posts from this sender
                // and who are allowed to see them based on the sender's permissions
                $deliveries = allowed_public_recips($i);
                if ($i['message'] && array_key_exists('type', $i['message']) && $i['message']['type'] === 'location') {
                    $sys = get_sys_channel();
                    $deliveries = array(array('hash' => $sys['xchan_hash']));
                }
                // if the scope is anything but 'public' we're going to store it as private regardless
                // of the private flag on the post.
                if ($i['message'] && array_key_exists('public_scope', $i['message']) && $i['message']['public_scope'] !== 'public') {
                    if (!array_key_exists('flags', $i['message'])) {
                        $i['message']['flags'] = array();
                    }
                    if (!in_array('private', $i['message']['flags'])) {
                        $i['message']['flags'][] = 'private';
                    }
                }
            }
            // Go through the hash array and remove duplicates. array_unique() won't do this because the array is more than one level.
            $no_dups = array();
            if ($deliveries) {
                foreach ($deliveries as $d) {
                    if (!in_array($d['hash'], $no_dups)) {
                        $no_dups[] = $d['hash'];
                    }
                }
                if ($no_dups) {
                    $deliveries = array();
                    foreach ($no_dups as $n) {
                        $deliveries[] = array('hash' => $n);
                    }
                }
            }
            if (!$deliveries) {
                logger('zot_import: no deliveries on this site');
                continue;
            }
            if ($i['message']) {
                if ($i['message']['type'] === 'activity') {
                    $arr = get_item_elements($i['message']);
                    $v = validate_item_elements($i['message'], $arr);
                    if (!$v['success']) {
                        logger('Activity rejected: ' . $v['message'] . ' ' . print_r($i['message'], true));
                        continue;
                    }
                    logger('Activity received: ' . print_r($arr, true), LOGGER_DATA);
                    logger('Activity recipients: ' . print_r($deliveries, true), LOGGER_DATA);
                    $relay = array_key_exists('flags', $i['message']) && in_array('relay', $i['message']['flags']) ? true : false;
                    $result = process_delivery($i['notify']['sender'], $arr, $deliveries, $relay, false, $message_request);
                } elseif ($i['message']['type'] === 'mail') {
                    $arr = get_mail_elements($i['message']);
                    logger('Mail received: ' . print_r($arr, true), LOGGER_DATA);
                    logger('Mail recipients: ' . print_r($deliveries, true), LOGGER_DATA);
                    $result = process_mail_delivery($i['notify']['sender'], $arr, $deliveries);
                } elseif ($i['message']['type'] === 'profile') {
                    $arr = get_profile_elements($i['message']);
                    logger('Profile received: ' . print_r($arr, true), LOGGER_DATA);
                    logger('Profile recipients: ' . print_r($deliveries, true), LOGGER_DATA);
                    $result = process_profile_delivery($i['notify']['sender'], $arr, $deliveries);
                } elseif ($i['message']['type'] === 'channel_sync') {
                    // $arr = get_channelsync_elements($i['message']);
                    $arr = $i['message'];
                    logger('Channel sync received: ' . print_r($arr, true), LOGGER_DATA);
                    logger('Channel sync recipients: ' . print_r($deliveries, true), LOGGER_DATA);
                    $result = process_channel_sync_delivery($i['notify']['sender'], $arr, $deliveries);
                } elseif ($i['message']['type'] === 'location') {
                    $arr = $i['message'];
                    logger('Location message received: ' . print_r($arr, true), LOGGER_DATA);
                    logger('Location message recipients: ' . print_r($deliveries, true), LOGGER_DATA);
                    $result = process_location_delivery($i['notify']['sender'], $arr, $deliveries);
                }
            }
            if ($result) {
                $return = array_merge($return, $result);
            }
        }
    }
    return $return;
}
Example #28
0
function diaspora_send_mail($item, $owner, $contact)
{
    $a = get_app();
    $myaddr = $owner['channel_address'] . '@' . get_app()->get_hostname();
    $r = q("select * from conv where id = %d and uid = %d limit 1", intval($item['convid']), intval($item['channel_id']));
    if (!count($r)) {
        logger('diaspora_send_mail: conversation not found.');
        return;
    }
    $cnv = $r[0];
    $conv = array('guid' => xmlify($cnv['guid']), 'subject' => xmlify($cnv['subject']), 'created_at' => xmlify(datetime_convert('UTC', 'UTC', $cnv['created'], 'Y-m-d H:i:s \\U\\T\\C')), 'diaspora_handle' => xmlify($cnv['creator']), 'participant_handles' => xmlify($cnv['recips']));
    if (array_key_exists('mail_flags', $item) && $item['mail_flags'] & MAIL_OBSCURED) {
        $key = get_config('system', 'prvkey');
        //		if($item['title'])
        //			$item['title'] = crypto_unencapsulate(json_decode_plus($item['title']),$key);
        if ($item['body']) {
            $item['body'] = crypto_unencapsulate(json_decode_plus($item['body']), $key);
        }
    }
    $body = bb2diaspora($item['body']);
    $created = datetime_convert('UTC', 'UTC', $item['created'], 'Y-m-d H:i:s \\U\\T\\C');
    $signed_text = $item['mid'] . ';' . $cnv['guid'] . ';' . $body . ';' . $created . ';' . $myaddr . ';' . $cnv['guid'];
    $sig = base64_encode(rsa_sign($signed_text, $owner['channel_prvkey'], 'sha256'));
    $msg = array('guid' => xmlify($item['mid']), 'parent_guid' => xmlify($cnv['guid']), 'parent_author_signature' => $item['reply'] ? null : xmlify($sig), 'author_signature' => xmlify($sig), 'text' => xmlify($body), 'created_at' => xmlify($created), 'diaspora_handle' => xmlify($myaddr), 'conversation_guid' => xmlify($cnv['guid']));
    if ($item['reply']) {
        $tpl = get_markup_template('diaspora_message.tpl');
        $xmsg = replace_macros($tpl, array('$msg' => $msg));
    } else {
        $conv['messages'] = array($msg);
        $tpl = get_markup_template('diaspora_conversation.tpl');
        $xmsg = replace_macros($tpl, array('$conv' => $conv));
    }
    logger('diaspora_conversation: ' . print_r($xmsg, true), LOGGER_DATA);
    $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($xmsg, $owner, $contact, $owner['channel_prvkey'], $contact['xchan_pubkey'], false)));
    return diaspora_transmit($owner, $contact, $slap, false);
}