function p_init(&$a) { if (argc() < 2) { http_status_exit(401); } $mid = str_replace('.xml', '', argv(1)); $r = q("select * from item where mid = '%s' and item_wall = 1 and item_private = 0 limit 1", dbesc($mid)); if (!$r || !perm_is_allowed($r[0]['uid'], '', 'view_stream')) { http_status_exit(404); } $c = q("select * from channel where channel_id = %d limit 1", intval($r[0]['uid'])); if (!$c) { http_status_exit(404); } $myaddr = channel_reddress($c[0]); $item = $r[0]; $title = $item['title']; $body = bb2diaspora_itembody($item); $created = datetime_convert('UTC', 'UTC', $item['created'], 'Y-m-d H:i:s \\U\\T\\C'); $tpl = get_markup_template('diaspora_post.tpl', 'addon/diaspora'); $msg = replace_macros($tpl, array('$body' => xmlify($body), '$guid' => $item['mid'], '$handle' => xmlify($myaddr), '$public' => 'true', '$created' => $created, '$provider' => $item['app'] ? $item['app'] : t('$projectname'))); header('Content-type: text/xml'); echo $msg; killme(); }
function init() { $uri = urldecode(notags(trim($_GET['uri']))); logger('xrd: ' . $uri, LOGGER_DEBUG); $resource = $uri; if (substr($uri, 0, 4) === 'http') { $uri = str_replace('~', '', $uri); $name = basename($uri); } else { $local = str_replace('acct:', '', $uri); if (substr($local, 0, 2) == '//') { $local = substr($local, 2); } $name = substr($local, 0, strpos($local, '@')); } $r = q("SELECT * FROM channel WHERE channel_address = '%s' LIMIT 1", dbesc($name)); if (!$r) { killme(); } $dspr = replace_macros(get_markup_template('xrd_diaspora.tpl'), array('$baseurl' => z_root(), '$dspr_guid' => $r[0]['channel_guid'] . str_replace('.', '', \App::get_hostname()), '$dspr_key' => base64_encode(pemtorsa($r[0]['channel_pubkey'])))); $salmon_key = salmon_key($r[0]['channel_pubkey']); header('Access-Control-Allow-Origin: *'); header("Content-type: application/xrd+xml"); $aliases = array('acct:' . channel_reddress($r[0]), z_root() . '/channel/' . $r[0]['channel_address'], z_root() . '/~' . $r[0]['channel_address']); for ($x = 0; $x < count($aliases); $x++) { if ($aliases[$x] === $resource) { unset($aliases[$x]); } } $o = replace_macros(get_markup_template('xrd_person.tpl'), array('$nick' => $r[0]['channel_address'], '$accturi' => $resource, '$aliases' => $aliases, '$profile_url' => z_root() . '/channel/' . $r[0]['channel_address'], '$hcard_url' => z_root() . '/hcard/' . $r[0]['channel_address'], '$atom' => z_root() . '/feed/' . $r[0]['channel_address'], '$zot_post' => z_root() . '/post/' . $r[0]['channel_address'], '$poco_url' => z_root() . '/poco/' . $r[0]['channel_address'], '$photo' => z_root() . '/photo/profile/l/' . $r[0]['channel_id'], '$dspr' => $dspr, '$modexp' => 'data:application/magic-public-key,' . $salmon_key, '$subscribe' => z_root() . '/follow?url={uri}', '$bigkey' => salmon_key($r[0]['channel_pubkey']))); $arr = array('user' => $r[0], 'xml' => $o); call_hooks('personal_xrd', $arr); echo $arr['xml']; killme(); }
function get() { if (!is_site_admin()) { return; } $o = ''; $r = q("select * from channel where channel_removed = 0"); $sitekey = get_config('system', 'pubkey'); if ($r) { foreach ($r as $rr) { $found = false; $primary_address = ''; $x = zot_get_hublocs($rr['channel_hash']); if ($x) { foreach ($x as $xx) { if ($xx['hubloc_url'] === z_root() && $xx['hubloc_sitekey'] === $sitekey) { $found = true; break; } } if ($found) { $o .= 'Hubloc exists for ' . $rr['channel_name'] . EOL; continue; } } $y = q("select xchan_addr from xchan where xchan_hash = '%s' limit 1", dbesc($rr['channel_hash'])); if ($y) { $primary_address = $y[0]['xchan_addr']; } $hub_address = channel_reddress($rr['channel']); $primary = $hub_address === $primary_address ? 1 : 0; if (!$y) { $primary = 1; } $m = q("delete from hubloc where hubloc_hash = '%s' and hubloc_url = '%s' ", dbesc($rr['channel_hash']), dbesc(z_root())); // Create a verified hub location pointing to this site. $h = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_primary, hubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey, hubloc_network )\n\t\t\t\t\tvalues ( '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s' )", dbesc($rr['channel_guid']), dbesc($rr['channel_guid_sig']), dbesc($rr['channel_hash']), dbesc(channel_reddress($rr)), intval($primary), dbesc(z_root()), dbesc(base64url_encode(rsa_sign(z_root(), $rr['channel_prvkey']))), dbesc(\App::get_hostname()), dbesc(z_root() . '/post'), dbesc($sitekey), dbesc('zot')); if ($h) { $o . 'local hubloc created for ' . $rr['channel_name'] . EOL; } else { $o .= 'DB update failed for ' . $rr['channel_name'] . EOL; } } return $o; } }
function post() { if (!array_key_exists('channel', \App::$data)) { return; } $edit = local_channel() && local_channel() == \App::$data['channel']['channel_id'] ? true : false; if ($edit) { $has_premium = \App::$data['channel']['channel_pageflags'] & PAGE_PREMIUM ? 1 : 0; $premium = $_POST['premium'] ? intval($_POST['premium']) : 0; $text = escape_tags($_POST['text']); if ($has_premium != $premium) { $r = q("update channel set channel_pageflags = ( channel_pageflags %s %d ) where channel_id = %d", db_getfunc('^'), intval(PAGE_PREMIUM), intval(local_channel())); \Zotlabs\Daemon\Master::Summon(array('Notifier', 'refresh_all', \App::$data['channel']['channel_id'])); } set_pconfig(\App::$data['channel']['channel_id'], 'system', 'selltext', $text); // reload the page completely to get fresh data goaway(z_root() . '/' . \App::$query_string); } $url = ''; $observer = \App::get_observer(); if ($observer && $_POST['submit'] === t('Continue')) { if ($observer['xchan_follow']) { $url = sprintf($observer['xchan_follow'], urlencode(channel_reddress(\App::$data['channel']))); } if (!$url) { $r = q("select * from hubloc where hubloc_hash = '%s' order by hubloc_id desc limit 1", dbesc($observer['xchan_hash'])); if ($r) { $url = $r[0]['hubloc_url'] . '/follow?f=&url=' . urlencode(channel_reddress(\App::$data['channel'])); } } } if ($url) { goaway($url . '&confirm=1'); } else { notice('Unable to connect to your home hub location.'); } }
function exec() { $opts = $this->curlopts; $url = $this->url; if ($this->auth) { $opts['http_auth'] = $this->auth; } if ($this->magicauth) { $opts['cookiejar'] = 'store/[data]/cookie_' . $this->magicauth; $opts['cookiefile'] = 'store/[data]/cookie_' . $this->magicauth; $opts['cookie'] = 'PHPSESSID=' . trim(file_get_contents('store/[data]/cookien_' . $this->magicauth)); $c = channelx_by_n($this->magicauth); if ($c) { $url = zid($this->url, channel_reddress($c)); } } if ($this->custom) { $opts['custom'] = $this->custom; } if ($this->headers) { $opts['headers'] = $this->headers; } if ($this->upload) { $opts['upload'] = true; $opts['infile'] = $this->filehandle; $opts['infilesize'] = strlen($this->request_data); $opts['readfunc'] = [$this, 'curl_read']; } $recurse = 0; return z_fetch_url($this->url, true, $recurse, $opts ? $opts : null); }
function diaspora_post_local(&$a, &$item) { /** * If all the conditions are met, generate an instance of the Diaspora Comment Virus * * Previously all comments from any Hubzilla source (including those who have not opted in to * Diaspora federation), were required to locally generate a Diaspora comment signature. * The only exception was wall-to-wall posts which have no local signing authority. * * Going forward, if we are asked to propagate the virus and it is not present (due to the post author * not opting in to Diaspora federation); we will generate a "wall-to-wall" comment and not require * a source signature. This allows hubs and communities to opt-out of Diaspora federation and not be * forced to generate the comment virus regardless. This is necessary because Diaspora now requires * the virus not just to provide a stored signature and Diaspora formatted text body, but must also * include all XML fields presented by the Diaspora protocol when transmitting the comment, while * maintaining their source order. This is fine for federated communities using UNO, but it makes * no sense to require this low-level baggage in channels and communities that have chosen not to use * the Diaspora protocol and services. * */ require_once 'include/bb2diaspora.php'; if ($item['mid'] === $item['parent_mid']) { return; } if ($item['created'] != $item['edited']) { return; } $meta = null; $author = channelx_by_hash($item['author_xchan']); if ($author) { // The author has a local channel, If they have this connector installed, // sign the comment and create a Diaspora Comment Virus. $dspr_allowed = get_pconfig($author['channel_id'], 'system', 'diaspora_allowed'); if (!$dspr_allowed) { return; } $handle = channel_reddress($author); if ($item['verb'] === ACTIVITY_LIKE) { if ($item['thr_parent'] == $item['parent_mid'] && $item['obj_type'] == ACTIVITY_OBJ_NOTE) { $meta = ['positive' => 'true', 'guid' => $item['mid'], 'target_type' => 'Post', 'parent_guid' => $item['parent_mid'], 'diaspora_handle' => $handle]; } } else { $body = bb2diaspora_itembody($item, true, true); $meta = ['guid' => $item['mid'], 'parent_guid' => $item['parent_mid'], 'text' => $body, 'diaspora_handle' => $handle]; } $meta['author_signature'] = diaspora_sign_fields($meta, $author['channel_prvkey']); if ($item['author_xchan'] === $item['owner_xchan']) { $meta['parent_author_signature'] = diaspora_sign_fields($meta, $author['channel_prvkey']); } } if (!$meta && $item['author_xchan'] !== $item['owner_xchan']) { // A local comment arrived but the commenter does not have a local channel // or the commenter doesn't have the Diaspora plugin enabled. // The owner *should* have a local channel // Find the owner and if the owner has this addon installed, turn the comment into // a 'wall-to-wall' message containing the author attribution, // with the comment signed by the owner. $owner = channelx_by_hash($item['owner_xchan']); if (!$owner) { return; } $dspr_allowed = get_pconfig($owner['channel_id'], 'system', 'diaspora_allowed'); if (!$dspr_allowed) { return; } $handle = channel_reddress($owner); if ($item['verb'] === ACTIVITY_LIKE) { if ($item['thr_parent'] == $item['parent_mid'] && $item['obj_type'] == ACTIVITY_OBJ_NOTE) { $meta = ['positive' => 'true', 'guid' => $item['mid'], 'target_type' => 'Post', 'parent_guid' => $item['parent_mid'], 'diaspora_handle' => $handle]; } } else { $body = bb2diaspora_itembody($item, true, false); $meta = ['guid' => $item['mid'], 'parent_guid' => $item['parent_mid'], 'text' => $body, 'diaspora_handle' => $handle]; } $meta['author_signature'] = diaspora_sign_fields($meta, $owner['channel_prvkey']); $meta['parent_author_signature'] = diaspora_sign_fields($meta, $owner['channel_prvkey']); } if ($meta) { set_iconfig($item, 'diaspora', 'fields', $meta, true); } // otherwise, neither the author or owner have this plugin installed. Do nothing. // logger('ditem: ' . print_r($item,true)); }
function store_diaspora_comment_sig($datarray, $channel, $parent_item, $post_id, $walltowall = false) { // We won't be able to sign Diaspora comments for authenticated visitors // - we don't have their private key // since Diaspora doesn't handle edits we can only do this for the original text and not update it. require_once 'include/bb2diaspora.php'; $signed_body = bb2diaspora_itembody($datarray, $walltowall); if ($walltowall) { logger('wall to wall comment', LOGGER_DEBUG); // post will come across with the owner's identity. Throw a preamble onto the post to indicate the true author. $signed_body = "\n\n" . '![' . $datarray['author']['xchan_name'] . '](' . $datarray['author']['xchan_photo_m'] . ')' . '[' . $datarray['author']['xchan_name'] . '](' . $datarray['author']['xchan_url'] . ')' . "\n\n" . $signed_body; } logger('storing diaspora comment signature', LOGGER_DEBUG); $diaspora_handle = channel_reddress($channel); $signed_text = $datarray['mid'] . ';' . $parent_item['mid'] . ';' . $signed_body . ';' . $diaspora_handle; if ($channel && $channel['channel_prvkey']) { $authorsig = base64_encode(rsa_sign($signed_text, $channel['channel_prvkey'], 'sha256')); } else { $authorsig = ''; } $x = array('signer' => $diaspora_handle, 'body' => $signed_body, 'signed_text' => $signed_text, 'signature' => $authorsig); $y = json_encode($x); $r = q("update item set diaspora_meta = '%s' where id = %d", dbesc($y), intval($post_id)); if (!$r) { logger('store_diaspora_comment_sig: DB write failed'); } return; }
function get_zcard_embed($channel, $observer_hash = '', $args = array()) { logger('get_zcard_embed'); $maxwidth = $args['width'] ? intval($args['width']) : 0; $maxheight = $args['height'] ? intval($args['height']) : 0; if ($maxwidth > 1200 || $maxwidth < 1) { $maxwidth = 1200; } if ($maxwidth <= 425) { $width = 425; $size = 'hz_small'; $cover_size = PHOTO_RES_COVER_425; $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 80, 'height' => 80, 'href' => $channel['xchan_photo_m']); } elseif ($maxwidth <= 900) { $width = 900; $size = 'hz_medium'; $cover_size = PHOTO_RES_COVER_850; $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 160, 'height' => 160, 'href' => $channel['xchan_photo_l']); } elseif ($maxwidth <= 1200) { $width = 1200; $size = 'hz_large'; $cover_size = PHOTO_RES_COVER_1200; $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 300, 'height' => 300, 'href' => $channel['xchan_photo_l']); } $channel['channel_addr'] = channel_reddress($channel); $zcard = array('chan' => $channel); $r = q("select height, width, resource_id, imgscale, mimetype from photo where uid = %d and imgscale = %d and photo_usage = %d", intval($channel['channel_id']), intval($cover_size), intval(PHOTO_COVER)); if ($r) { $cover = $r[0]; $cover['href'] = z_root() . '/photo/' . $r[0]['resource_id'] . '-' . $r[0]['imgscale']; } else { $cover = $pphoto; } $o .= replace_macros(get_markup_template('zcard_embed.tpl'), array('$maxwidth' => $maxwidth, '$scale' => $scale, '$translate' => $translate, '$size' => $size, '$cover' => $cover, '$pphoto' => $pphoto, '$zcard' => $zcard)); return $o; }
function diaspora_build_status($item, $owner) { $myaddr = channel_reddress($owner); if (intval($item['id']) != intval($item['parent'])) { logger('attempted to send a comment as a top-level post'); return; } $images = array(); $title = $item['title']; $body = bb2diaspora_itembody($item, true); $poll = ''; $public = $item['item_private'] ? 'false' : 'true'; $created = datetime_convert('UTC', 'UTC', $item['created'], 'Y-m-d H:i:s \\U\\T\\C'); // Detect a share element and do a reshare if (!$item['item_private'] && ($ret = diaspora_is_reshare($item['body']))) { $msg = replace_macros(get_markup_template('diaspora_reshare.tpl', 'addon/diaspora'), ['$root_handle' => xmlify($ret['root_handle']), '$root_guid' => $ret['root_guid'], '$guid' => $item['mid'], '$handle' => xmlify($myaddr), '$public' => $public, '$created' => $created, '$provider' => $item['app'] ? $item['app'] : t('$projectname')]); } else { $msg = replace_macros(get_markup_template('diaspora_post.tpl', 'addon/diaspora'), ['$body' => xmlify($body), '$guid' => $item['mid'], '$poll' => $poll, '$handle' => xmlify($myaddr), '$public' => $public, '$created' => $created, '$provider' => $item['app'] ? $item['app'] : t('$projectname')]); } return $msg; }
/** * @brief Change to another channel with current logged-in account. * * @param int $change_channel The channel_id of the channel you want to change to * * @return bool|array false or channel record of the new channel */ function change_channel($change_channel) { $ret = false; if ($change_channel) { $r = q("select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel_id = %d and channel_account_id = %d and channel_removed = 0 limit 1", intval($change_channel), intval(get_account_id())); // It's not there. Is this an administrator, and is this the sys channel? if (is_developer()) { if (!$r) { if (is_site_admin()) { $r = q("select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel_id = %d and channel_system = 1 and channel_removed = 0 limit 1", intval($change_channel)); } } } if ($r) { $hash = $r[0]['channel_hash']; $_SESSION['uid'] = intval($r[0]['channel_id']); App::set_channel($r[0]); $_SESSION['theme'] = $r[0]['channel_theme']; $_SESSION['mobile_theme'] = get_pconfig(local_channel(), 'system', 'mobile_theme'); date_default_timezone_set($r[0]['channel_timezone']); $ret = $r[0]; } $x = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($hash)); if ($x) { $_SESSION['my_url'] = $x[0]['xchan_url']; $_SESSION['my_address'] = channel_reddress($r[0]); App::set_observer($x[0]); App::set_perms(get_all_perms(local_channel(), $hash)); } if (!is_dir('store/' . $r[0]['channel_address'])) { @os_mkdir('store/' . $r[0]['channel_address'], STORAGE_DEFAULT_PERMISSIONS, true); } $arr = ['channel_id' => $change_channel, 'chanx' => $ret]; call_hooks('change_channel', $arr); } return $ret; }
function diaspora_profile_change($channel, $recip, $public_batch = false) { $channel_id = $channel['channel_id']; $r = q("SELECT profile.uid AS profile_uid, profile.* , channel.* FROM profile\n\t\tleft join channel on profile.uid = channel.channel_id\n\t\tWHERE channel.channel_id = %d and profile.is_default = 1 ", intval($channel_id)); $profile_visible = perm_is_allowed($channel_id, '', 'view_profile'); if (!$r) { return; } $profile = $r[0]; $handle = xmlify(channel_reddress($channel)); $first = xmlify(strpos($profile['channel_name'], ' ') ? trim(substr($profile['channel_name'], 0, strpos($profile['channel_name'], ' '))) : $profile['channel_name']); $last = xmlify($first === $profile['channel_name'] ? '' : trim(substr($profile['channel_name'], strlen($first)))); $large = xmlify(z_root() . '/photo/profile/300/' . $profile['profile_uid'] . '.jpg'); $medium = xmlify(z_root() . '/photo/profile/100/' . $profile['profile_uid'] . '.jpg'); $small = xmlify(z_root() . '/photo/profile/50/' . $profile['profile_uid'] . '.jpg'); $searchable = xmlify($profile_visible ? 'true' : 'false'); $nsfw = $channel['channel_pageflags'] & (PAGE_ADULT | PAGE_CENSORED) ? 'true' : 'false'; if ($searchable === 'true') { $dob = '1000-00-00'; if ($profile['dob'] && $profile['dob'] != '0000-00-00') { $dob = (intval($profile['dob']) ? intval($profile['dob']) : '1000') . '-' . datetime_convert('UTC', 'UTC', $profile['dob'], 'm-d'); } if ($dob === '1000-00-00') { $dob = ''; } $gender = xmlify($profile['gender']); $about = $profile['about']; require_once 'include/bbcode.php'; $about = xmlify(strip_tags(bbcode($about))); $location = ''; if ($profile['locality']) { $location .= $profile['locality']; } if ($profile['region']) { if ($location) { $location .= ', '; } $location .= $profile['region']; } if ($profile['country_name']) { if ($location) { $location .= ', '; } $location .= $profile['country_name']; } $location = xmlify($location); $tags = ''; if ($profile['keywords']) { $kw = str_replace(',', ' ', $profile['keywords']); $kw = str_replace(' ', ' ', $kw); $arr = explode(' ', $profile['keywords']); if (count($arr)) { for ($x = 0; $x < 5; $x++) { if (trim($arr[$x])) { $tags .= '#' . trim($arr[$x]) . ' '; } } } } $tags = xmlify(trim($tags)); } $tpl = get_markup_template('diaspora_profile.tpl', 'addon/diaspora'); $msg = replace_macros($tpl, array('$handle' => $handle, '$first' => $first, '$last' => $last, '$large' => $large, '$medium' => $medium, '$small' => $small, '$dob' => $dob, '$gender' => $gender, '$about' => $about, '$location' => $location, '$searchable' => $searchable, '$nsfw' => $nsfw, '$tags' => $tags)); logger('profile_change: ' . $msg, LOGGER_ALL, LOG_DEBUG); $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg, $channel, $recip, $channel['channel_prvkey'], $recip['xchan_pubkey'], $public_batch))); return diaspora_queue($channel, $recip, $slap, $public_batch); }
function init() { $ret = array('success' => false, 'url' => '', 'message' => ''); logger('mod_magic: invoked', LOGGER_DEBUG); logger('mod_magic: args: ' . print_r($_REQUEST, true), LOGGER_DATA); $addr = x($_REQUEST, 'addr') ? $_REQUEST['addr'] : ''; $dest = x($_REQUEST, 'dest') ? $_REQUEST['dest'] : ''; $test = x($_REQUEST, 'test') ? intval($_REQUEST['test']) : 0; $rev = x($_REQUEST, 'rev') ? intval($_REQUEST['rev']) : 0; $delegate = x($_REQUEST, 'delegate') ? $_REQUEST['delegate'] : ''; $parsed = parse_url($dest); if (!$parsed) { if ($test) { $ret['message'] .= 'could not parse ' . $dest . EOL; return $ret; } goaway($dest); } $basepath = $parsed['scheme'] . '://' . $parsed['host'] . ($parsed['port'] ? ':' . $parsed['port'] : ''); $x = q("select * from hubloc where hubloc_url = '%s' order by hubloc_connected desc limit 1", dbesc($basepath)); if (!$x) { /* * We have no records for, or prior communications with this hub. * If an address was supplied, let's finger them to create a hub record. * Otherwise we'll use the special address '[system]' which will return * either a system channel or the first available normal channel. We don't * really care about what channel is returned - we need the hub information * from that response so that we can create signed auth packets destined * for that hub. * */ $j = \Zotlabs\Zot\Finger::run($addr ? $addr : '[system]@' . $parsed['host'], null); if ($j['success']) { import_xchan($j); // Now try again $x = q("select * from hubloc where hubloc_url = '%s' order by hubloc_connected desc limit 1", dbesc($basepath)); } } if (!$x) { if ($rev) { goaway($dest); } else { logger('mod_magic: no channels found for requested hub.' . print_r($_REQUEST, true)); if ($test) { $ret['message'] .= 'This site has no previous connections with ' . $basepath . EOL; return $ret; } notice(t('Hub not found.') . EOL); return; } } // This is ready-made for a plugin that provides a blacklist or "ask me" before blindly authenticating. // By default, we'll proceed without asking. $arr = array('channel_id' => local_channel(), 'xchan' => $x[0], 'destination' => $dest, 'proceed' => true); call_hooks('magic_auth', $arr); $dest = $arr['destination']; if (!$arr['proceed']) { if ($test) { $ret['message'] .= 'cancelled by plugin.' . EOL; return $ret; } goaway($dest); } if (get_observer_hash() && $x[0]['hubloc_url'] === z_root()) { // We are already authenticated on this site and a registered observer. // Just redirect. if ($test) { $ret['success'] = true; $ret['message'] .= 'Local site - you are already authenticated.' . EOL; return $ret; } $delegation_success = false; if ($delegate) { $r = q("select * from channel left join hubloc on channel_hash = hubloc_hash where hubloc_addr = '%s' limit 1", dbesc($delegate)); if ($r && intval($r[0]['channel_id'])) { $allowed = perm_is_allowed($r[0]['channel_id'], get_observer_hash(), 'delegate'); if ($allowed) { $_SESSION['delegate_channel'] = $r[0]['channel_id']; $_SESSION['delegate'] = get_observer_hash(); $_SESSION['account_id'] = intval($r[0]['channel_account_id']); change_channel($r[0]['channel_id']); $delegation_success = true; } } } // FIXME: check and honour local delegation goaway($dest); } if (local_channel()) { $channel = \App::get_channel(); $token = random_string(); $token_sig = base64url_encode(rsa_sign($token, $channel['channel_prvkey'])); $channel['token'] = $token; $channel['token_sig'] = $token_sig; \Zotlabs\Zot\Verify::create('auth', $channel['channel_id'], $token, $x[0]['hubloc_url']); $target_url = $x[0]['hubloc_callback'] . '/?f=&auth=' . urlencode(channel_reddress($channel)) . '&sec=' . $token . '&dest=' . urlencode($dest) . '&version=' . ZOT_REVISION; if ($delegate) { $target_url .= '&delegate=' . urlencode($delegate); } logger('mod_magic: redirecting to: ' . $target_url, LOGGER_DEBUG); if ($test) { $ret['success'] = true; $ret['url'] = $target_url; $ret['message'] = 'token ' . $token . ' created for channel ' . $channel['channel_id'] . ' for url ' . $x[0]['hubloc_url'] . EOL; return $ret; } goaway($target_url); } if ($test) { $ret['message'] = 'Not authenticated or invalid arguments to mod_magic' . EOL; return $ret; } goaway($dest); }
/** * @brief Push local channel updates to a local directory server. * * This is called from include/directory.php if a profile is to be pushed to the * directory and the local hub in this case is any kind of directory server. * * @param int $uid * @param boolean $force */ function local_dir_update($uid, $force) { logger('local_dir_update: uid: ' . $uid, LOGGER_DEBUG); $p = q("select channel.channel_hash, channel_address, channel_timezone, profile.* from profile left join channel on channel_id = uid where uid = %d and is_default = 1", intval($uid)); $profile = array(); $profile['encoding'] = 'zot'; if ($p) { $hash = $p[0]['channel_hash']; $profile['description'] = $p[0]['pdesc']; $profile['birthday'] = $p[0]['dob']; if ($age = age($p[0]['dob'], $p[0]['channel_timezone'], '')) { $profile['age'] = $age; } $profile['gender'] = $p[0]['gender']; $profile['marital'] = $p[0]['marital']; $profile['sexual'] = $p[0]['sexual']; $profile['locale'] = $p[0]['locality']; $profile['region'] = $p[0]['region']; $profile['postcode'] = $p[0]['postal_code']; $profile['country'] = $p[0]['country_name']; $profile['about'] = $p[0]['about']; $profile['homepage'] = $p[0]['homepage']; $profile['hometown'] = $p[0]['hometown']; if ($p[0]['keywords']) { $tags = array(); $k = explode(' ', $p[0]['keywords']); if ($k) { foreach ($k as $kk) { if (trim($kk)) { $tags[] = trim($kk); } } } if ($tags) { $profile['keywords'] = $tags; } } $hidden = 1 - intval($p[0]['publish']); logger('hidden: ' . $hidden); $r = q("select xchan_hidden from xchan where xchan_hash = '%s' limit 1", dbesc($p[0]['channel_hash'])); if (intval($r[0]['xchan_hidden']) != $hidden) { $r = q("update xchan set xchan_hidden = %d where xchan_hash = '%s'", intval($hidden), dbesc($p[0]['channel_hash'])); } $arr = array('channel_id' => $uid, 'hash' => $hash, 'profile' => $profile); call_hooks('local_dir_update', $arr); $address = channel_reddress($p[0]); if (perm_is_allowed($uid, '', 'view_profile')) { import_directory_profile($hash, $arr['profile'], $address, 0); } else { // they may have made it private $r = q("delete from xprof where xprof_hash = '%s'", dbesc($hash)); $r = q("delete from xtag where xtag_hash = '%s'", dbesc($hash)); } } $ud_hash = random_string() . '@' . App::get_hostname(); update_modtime($hash, $ud_hash, channel_reddress($p[0]), $force ? UPDATE_FLAGS_FORCED : UPDATE_FLAGS_UPDATED); }
function check_zotinfo($channel, $locations, &$ret) { // logger('locations: ' . print_r($locations,true),LOGGER_DATA, LOG_DEBUG); // This function will likely expand as we find more things to detect and fix. // 1. Because magic-auth is reliant on it, ensure that the system channel has a valid hubloc // Force this to be the case if anything is found to be wrong with it. // @FIXME ensure that the system channel exists in the first place and has an xchan if ($channel['channel_system']) { // the sys channel must have a location (hubloc) $valid_location = false; if (count($locations) === 1 && $locations[0]['primary'] && !$locations[0]['deleted']) { if (rsa_verify($locations[0]['url'], base64url_decode($locations[0]['url_sig']), $channel['channel_pubkey']) && $locations[0]['sitekey'] === get_config('system', 'pubkey') && $locations[0]['url'] === z_root()) { $valid_location = true; } else { logger('sys channel: invalid url signature'); } } if (!$locations || !$valid_location) { logger('System channel locations are not valid. Attempting repair.'); // Don't trust any existing records. Just get rid of them, but only do this // for the sys channel as normal channels will be trickier. q("delete from hubloc where hubloc_hash = '%s'", dbesc($channel['channel_hash'])); $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_primary,\n\t\t\t\thubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey, hubloc_network )\n\t\t\t\tvalues ( '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s' )", dbesc($channel['channel_guid']), dbesc($channel['channel_guid_sig']), dbesc($channel['channel_hash']), dbesc(channel_reddress($channel)), intval(1), dbesc(z_root()), dbesc(base64url_encode(rsa_sign(z_root(), $channel['channel_prvkey']))), dbesc(App::get_hostname()), dbesc(z_root() . '/post'), dbesc(get_config('system', 'pubkey')), dbesc('zot')); if ($r) { $x = zot_encode_locations($channel); if ($x) { $ret['locations'] = $x; } } else { logger('Unable to store sys hub location'); } } } }
function get_site_info() { $register_policy = array('REGISTER_CLOSED', 'REGISTER_APPROVE', 'REGISTER_OPEN'); $directory_mode = array('DIRECTORY_MODE_NORMAL', 'DIRECTORY_MODE_PRIMARY', 'DIRECTORY_MODE_SECONDARY', 256 => 'DIRECTORY_MODE_STANDALONE'); $sql_extra = ''; $r = q("select * from channel left join account on account_id = channel_account_id where ( account_roles & 4096 )>0 and account_default_channel = channel_id"); if ($r) { $admin = array(); foreach ($r as $rr) { if ($rr['channel_pageflags'] & PAGE_HUBADMIN) { $admin[] = array('name' => $rr['channel_name'], 'address' => channel_reddress($rr), 'channel' => z_root() . '/channel/' . $rr['channel_address']); } } if (!$admin) { foreach ($r as $rr) { $admin[] = array('name' => $rr['channel_name'], 'address' => channel_reddress($rr), 'channel' => z_root() . '/channel/' . $rr['channel_address']); } } } else { $admin = false; } $def_service_class = get_config('system', 'default_service_class'); if ($def_service_class) { $service_class = get_config('service_class', $def_service_class); } else { $service_class = false; } $visible_plugins = visible_plugin_list(); if (@is_dir('.git') && function_exists('shell_exec')) { $commit = trim(@shell_exec('git log -1 --format="%h"')); } if (!isset($commit) || strlen($commit) > 16) { $commit = ''; } $site_info = get_config('system', 'info'); $site_name = get_config('system', 'sitename'); if (!get_config('system', 'hidden_version_siteinfo')) { $version = Zotlabs\Lib\System::get_project_version(); $tag = Zotlabs\Lib\System::get_std_version(); if (@is_dir('.git') && function_exists('shell_exec')) { $commit = trim(@shell_exec('git log -1 --format="%h"')); } if (!isset($commit) || strlen($commit) > 16) { $commit = ''; } } else { $version = $commit = ''; } //Statistics $channels_total_stat = intval(get_config('system', 'channels_total_stat')); $channels_active_halfyear_stat = intval(get_config('system', 'channels_active_halfyear_stat')); $channels_active_monthly_stat = intval(get_config('system', 'channels_active_monthly_stat')); $local_posts_stat = intval(get_config('system', 'local_posts_stat')); $hide_in_statistics = intval(get_config('system', 'hide_in_statistics')); $site_expire = intval(get_config('system', 'default_expire_days')); load_config('feature_lock'); $locked_features = array(); if (is_array(App::$config['feature_lock']) && count(App::$config['feature_lock'])) { foreach (App::$config['feature_lock'] as $k => $v) { if ($k === 'config_loaded') { continue; } $locked_features[$k] = intval($v); } } $data = array('version' => $version, 'version_tag' => $tag, 'server_role' => Zotlabs\Lib\System::get_server_role(), 'commit' => $commit, 'url' => z_root(), 'plugins' => $visible_plugins, 'register_policy' => $register_policy[get_config('system', 'register_policy')], 'invitation_only' => intval(get_config('system', 'invitation_only')), 'directory_mode' => $directory_mode[get_config('system', 'directory_mode')], 'language' => get_config('system', 'language'), 'rss_connections' => intval(get_config('system', 'feed_contacts')), 'expiration' => $site_expire, 'default_service_restrictions' => $service_class, 'locked_features' => $locked_features, 'admin' => $admin, 'site_name' => $site_name ? $site_name : '', 'platform' => Zotlabs\Lib\System::get_platform_name(), 'dbdriver' => DBA::$dba->getdriver(), 'lastpoll' => get_config('system', 'lastpoll'), 'info' => $site_info ? $site_info : '', 'channels_total' => $channels_total_stat, 'channels_active_halfyear' => $channels_active_halfyear_stat, 'channels_active_monthly' => $channels_active_monthly_stat, 'local_posts' => $local_posts_stat, 'hide_in_statistics' => $hide_in_statistics); return $data; }
function import_account($account_id) { if (!$account_id) { logger("import_account: No account ID supplied"); return; } $max_identities = account_service_class_fetch($account_id, 'total_identities'); $max_friends = account_service_class_fetch($account_id, 'total_channels'); $max_feeds = account_service_class_fetch($account_id, 'total_feeds'); if ($max_identities !== false) { $r = q("select channel_id from channel where channel_account_id = %d", intval($account_id)); if ($r && count($r) > $max_identities) { notice(sprintf(t('Your service plan only allows %d channels.'), $max_identities) . EOL); return; } } $data = null; $seize = x($_REQUEST, 'make_primary') ? intval($_REQUEST['make_primary']) : 0; $import_posts = x($_REQUEST, 'import_posts') ? intval($_REQUEST['import_posts']) : 0; $src = $_FILES['filename']['tmp_name']; $filename = basename($_FILES['filename']['name']); $filesize = intval($_FILES['filename']['size']); $filetype = $_FILES['filename']['type']; $completed = array_key_exists('import_step', $_SESSION) ? intval($_SESSION['import_step']) : 0; if ($completed) { logger('saved import step: ' . $_SESSION['import_step']); } if ($src) { // This is OS specific and could also fail if your tmpdir isn't very large // mostly used for Diaspora which exports gzipped files. if (strpos($filename, '.gz')) { @rename($src, $src . '.gz'); @system('gunzip ' . escapeshellarg($src . '.gz')); } if ($filesize) { $data = @file_get_contents($src); } unlink($src); } if (!$src) { $old_address = x($_REQUEST, 'old_address') ? $_REQUEST['old_address'] : ''; if (!$old_address) { logger('mod_import: nothing to import.'); notice(t('Nothing to import.') . EOL); return; } $email = x($_REQUEST, 'email') ? $_REQUEST['email'] : ''; $password = x($_REQUEST, 'password') ? $_REQUEST['password'] : ''; $channelname = substr($old_address, 0, strpos($old_address, '@')); $servername = substr($old_address, strpos($old_address, '@') + 1); $scheme = 'https://'; $api_path = '/api/red/channel/export/basic?f=&channel=' . $channelname; if ($import_posts) { $api_path .= '&posts=1'; } $binary = false; $redirects = 0; $opts = array('http_auth' => $email . ':' . $password); $url = $scheme . $servername . $api_path; $ret = z_fetch_url($url, $binary, $redirects, $opts); if (!$ret['success']) { $ret = z_fetch_url('http://' . $servername . $api_path, $binary, $redirects, $opts); } if ($ret['success']) { $data = $ret['body']; } else { notice(t('Unable to download data from old server') . EOL); } } if (!$data) { logger('mod_import: empty file.'); notice(t('Imported file is empty.') . EOL); return; } $data = json_decode($data, true); // logger('import: data: ' . print_r($data,true)); // print_r($data); if (array_key_exists('user', $data) && array_key_exists('version', $data)) { require_once 'include/Import/import_diaspora.php'; import_diaspora($data); return; } $moving = false; if (array_key_exists('compatibility', $data) && array_key_exists('database', $data['compatibility'])) { $v1 = substr($data['compatibility']['database'], -4); $v2 = substr(DB_UPDATE_VERSION, -4); if ($v2 > $v1) { $t = sprintf(t('Warning: Database versions differ by %1$d updates.'), $v2 - $v1); notice($t); } if (array_key_exists('server_role', $data['compatibility']) && $data['compatibility']['server_role'] == 'basic') { $moving = true; } } if ($moving) { $seize = 1; } // import channel $relocate = array_key_exists('relocate', $data) ? $data['relocate'] : null; if (array_key_exists('channel', $data)) { if ($completed < 1) { $channel = import_channel($data['channel'], $account_id, $seize); } else { $r = q("select * from channel where channel_account_id = %d and channel_guid = '%s' limit 1", intval($account_id), dbesc($channel['channel_guid'])); if ($r) { $channel = $r[0]; } } if (!$channel) { logger('mod_import: channel not found. ', print_r($channel, true)); notice(t('Cloned channel not found. Import failed.') . EOL); return; } } if (!$channel) { $channel = \App::get_channel(); } if (!$channel) { logger('mod_import: channel not found. ', print_r($channel, true)); notice(t('No channel. Import failed.') . EOL); return; } if ($completed < 2) { if (is_array($data['config'])) { import_config($channel, $data['config']); } logger('import step 2'); $_SESSION['import_step'] = 2; } if ($completed < 3) { if ($data['photo']) { require_once 'include/photo/photo_driver.php'; import_channel_photo(base64url_decode($data['photo']['data']), $data['photo']['type'], $account_id, $channel['channel_id']); } if (is_array($data['profile'])) { import_profiles($channel, $data['profile']); } logger('import step 3'); $_SESSION['import_step'] = 3; } if ($completed < 4) { if (is_array($data['hubloc']) && !$moving) { import_hublocs($channel, $data['hubloc'], $seize); } logger('import step 4'); $_SESSION['import_step'] = 4; } if ($completed < 5) { // create new hubloc for the new channel at this site $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_network, hubloc_primary, \n\t\t\t\thubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey )\n\t\t\t\tvalues ( '%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s' )", dbesc($channel['channel_guid']), dbesc($channel['channel_guid_sig']), dbesc($channel['channel_hash']), dbesc(channel_reddress($channel)), dbesc('zot'), intval($seize ? 1 : 0), dbesc(z_root()), dbesc(base64url_encode(rsa_sign(z_root(), $channel['channel_prvkey']))), dbesc(\App::get_hostname()), dbesc(z_root() . '/post'), dbesc(get_config('system', 'pubkey'))); // reset the original primary hubloc if it is being seized if ($seize) { $r = q("update hubloc set hubloc_primary = 0 where hubloc_primary = 1 and hubloc_hash = '%s' and hubloc_url != '%s' ", dbesc($channel['channel_hash']), dbesc(z_root())); } logger('import step 5'); $_SESSION['import_step'] = 5; } if ($completed < 6) { // import xchans and contact photos if ($seize) { // replace any existing xchan we may have on this site if we're seizing control $r = q("delete from xchan where xchan_hash = '%s'", dbesc($channel['channel_hash'])); $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_guid_sig, xchan_pubkey, xchan_photo_l, xchan_photo_m, xchan_photo_s, xchan_addr, xchan_url, xchan_follow, xchan_connurl, xchan_name, xchan_network, xchan_photo_date, xchan_name_date, xchan_hidden, xchan_orphan, xchan_censored, xchan_selfcensored, xchan_system, xchan_pubforum, xchan_deleted ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, %d, %d, %d )", dbesc($channel['channel_hash']), dbesc($channel['channel_guid']), dbesc($channel['channel_guid_sig']), dbesc($channel['channel_pubkey']), dbesc(z_root() . "/photo/profile/l/" . $channel['channel_id']), dbesc(z_root() . "/photo/profile/m/" . $channel['channel_id']), dbesc(z_root() . "/photo/profile/s/" . $channel['channel_id']), dbesc(channel_reddress($channel)), dbesc(z_root() . '/channel/' . $channel['channel_address']), dbesc(z_root() . '/follow?f=&url=%s'), dbesc(z_root() . '/poco/' . $channel['channel_address']), dbesc($channel['channel_name']), dbesc('zot'), dbesc(datetime_convert()), dbesc(datetime_convert()), 0, 0, 0, 0, 0, 0, 0); } logger('import step 6'); $_SESSION['import_step'] = 6; } if ($completed < 7) { $xchans = $data['xchan']; if ($xchans) { foreach ($xchans as $xchan) { $hash = make_xchan_hash($xchan['xchan_guid'], $xchan['xchan_guid_sig']); if ($xchan['xchan_network'] === 'zot' && $hash !== $xchan['xchan_hash']) { logger('forged xchan: ' . print_r($xchan, true)); continue; } if (!array_key_exists('xchan_hidden', $xchan)) { $xchan['xchan_hidden'] = $xchan['xchan_flags'] & 0x1 ? 1 : 0; $xchan['xchan_orphan'] = $xchan['xchan_flags'] & 0x2 ? 1 : 0; $xchan['xchan_censored'] = $xchan['xchan_flags'] & 0x4 ? 1 : 0; $xchan['xchan_selfcensored'] = $xchan['xchan_flags'] & 0x8 ? 1 : 0; $xchan['xchan_system'] = $xchan['xchan_flags'] & 0x10 ? 1 : 0; $xchan['xchan_pubforum'] = $xchan['xchan_flags'] & 0x20 ? 1 : 0; $xchan['xchan_deleted'] = $xchan['xchan_flags'] & 0x1000 ? 1 : 0; } $r = q("select xchan_hash from xchan where xchan_hash = '%s' limit 1", dbesc($xchan['xchan_hash'])); if ($r) { continue; } dbesc_array($xchan); $r = dbq("INSERT INTO xchan (`" . implode("`, `", array_keys($xchan)) . "`) VALUES ('" . implode("', '", array_values($xchan)) . "')"); require_once 'include/photo/photo_driver.php'; $photos = import_xchan_photo($xchan['xchan_photo_l'], $xchan['xchan_hash']); if ($photos[4]) { $photodate = NULL_DATE; } else { $photodate = $xchan['xchan_photo_date']; } $r = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s', xchan_photo_date = '%s'\n\t\t\t\t\t\twhere xchan_hash = '%s'", dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($photodate), dbesc($xchan['xchan_hash'])); } } logger('import step 7'); $_SESSION['import_step'] = 7; } // FIXME - ensure we have an xchan if somebody is trying to pull a fast one if ($completed < 8) { $friends = 0; $feeds = 0; // import contacts $abooks = $data['abook']; if ($abooks) { foreach ($abooks as $abook) { $abook_copy = $abook; $abconfig = null; if (array_key_exists('abconfig', $abook) && is_array($abook['abconfig']) && count($abook['abconfig'])) { $abconfig = $abook['abconfig']; } unset($abook['abook_id']); unset($abook['abook_rating']); unset($abook['abook_rating_text']); unset($abook['abconfig']); unset($abook['abook_their_perms']); unset($abook['abook_my_perms']); $abook['abook_account'] = $account_id; $abook['abook_channel'] = $channel['channel_id']; if (!array_key_exists('abook_blocked', $abook)) { $abook['abook_blocked'] = $abook['abook_flags'] & 0x1 ? 1 : 0; $abook['abook_ignored'] = $abook['abook_flags'] & 0x2 ? 1 : 0; $abook['abook_hidden'] = $abook['abook_flags'] & 0x4 ? 1 : 0; $abook['abook_archived'] = $abook['abook_flags'] & 0x8 ? 1 : 0; $abook['abook_pending'] = $abook['abook_flags'] & 0x10 ? 1 : 0; $abook['abook_unconnected'] = $abook['abook_flags'] & 0x20 ? 1 : 0; $abook['abook_self'] = $abook['abook_flags'] & 0x80 ? 1 : 0; $abook['abook_feed'] = $abook['abook_flags'] & 0x100 ? 1 : 0; } if ($abook['abook_self']) { $role = get_pconfig($channel['channel_id'], 'system', 'permissions_role'); if ($role === 'forum' || $abook['abook_my_perms'] & PERMS_W_TAGWALL) { q("update xchan set xchan_pubforum = 1 where xchan_hash = '%s' ", dbesc($abook['abook_xchan'])); } } else { if ($max_friends !== false && $friends > $max_friends) { continue; } if ($max_feeds !== false && intval($abook['abook_feed']) && $feeds > $max_feeds) { continue; } } dbesc_array($abook); $r = dbq("INSERT INTO abook (`" . implode("`, `", array_keys($abook)) . "`) VALUES ('" . implode("', '", array_values($abook)) . "')"); $friends++; if (intval($abook['abook_feed'])) { $feeds++; } translate_abook_perms_inbound($channel, $abook_copy); if ($abconfig) { // @fixme does not handle sync of del_abconfig foreach ($abconfig as $abc) { set_abconfig($channel['channel_id'], $abc['xchan'], $abc['cat'], $abc['k'], $abc['v']); } } } } logger('import step 8'); $_SESSION['import_step'] = 8; } if ($completed < 9) { $groups = $data['group']; if ($groups) { $saved = array(); foreach ($groups as $group) { $saved[$group['hash']] = array('old' => $group['id']); if (array_key_exists('name', $group)) { $group['gname'] = $group['name']; unset($group['name']); } unset($group['id']); $group['uid'] = $channel['channel_id']; dbesc_array($group); $r = dbq("INSERT INTO groups (`" . implode("`, `", array_keys($group)) . "`) VALUES ('" . implode("', '", array_values($group)) . "')"); } $r = q("select * from `groups` where uid = %d", intval($channel['channel_id'])); if ($r) { foreach ($r as $rr) { $saved[$rr['hash']]['new'] = $rr['id']; } } } $group_members = $data['group_member']; if ($group_members) { foreach ($group_members as $group_member) { unset($group_member['id']); $group_member['uid'] = $channel['channel_id']; foreach ($saved as $x) { if ($x['old'] == $group_member['gid']) { $group_member['gid'] = $x['new']; } } dbesc_array($group_member); $r = dbq("INSERT INTO group_member (`" . implode("`, `", array_keys($group_member)) . "`) VALUES ('" . implode("', '", array_values($group_member)) . "')"); } } logger('import step 9'); $_SESSION['import_step'] = 9; } if (is_array($data['obj'])) { import_objs($channel, $data['obj']); } if (is_array($data['likes'])) { import_likes($channel, $data['likes']); } if (is_array($data['app'])) { import_apps($channel, $data['app']); } if (is_array($data['chatroom'])) { import_chatrooms($channel, $data['chatroom']); } if (is_array($data['conv'])) { import_conv($channel, $data['conv']); } if (is_array($data['mail'])) { import_mail($channel, $data['mail']); } if (is_array($data['event'])) { import_events($channel, $data['event']); } if (is_array($data['event_item'])) { import_items($channel, $data['event_item'], false, $relocate); } if (is_array($data['menu'])) { import_menus($channel, $data['menu']); } $addon = array('channel' => $channel, 'data' => $data); call_hooks('import_channel', $addon); $saved_notification_flags = notifications_off($channel['channel_id']); if ($import_posts && array_key_exists('item', $data) && $data['item']) { import_items($channel, $data['item'], false, $relocate); } notifications_on($channel['channel_id'], $saved_notification_flags); if (array_key_exists('item_id', $data) && $data['item_id']) { import_item_ids($channel, $data['item_id']); } // FIXME - ensure we have a self entry if somebody is trying to pull a fast one // send out refresh requests // notify old server that it may no longer be primary. \Zotlabs\Daemon\Master::Summon(array('Notifier', 'location', $channel['channel_id'])); // This will indirectly perform a refresh_all *and* update the directory \Zotlabs\Daemon\Master::Summon(array('Directory', $channel['channel_id'])); notice(t('Import completed.') . EOL); change_channel($channel['channel_id']); unset($_SESSION['import_step']); goaway(z_root() . '/network'); }
/** * @brief * * @param array $params an assoziative array with: * * \e string \b from_xchan sender xchan hash * * \e string \b to_xchan recipient xchan hash * * \e array \b item an assoziative array * * \e int \b type one of the NOTIFY_* constants from boot.php * * \e string \b link * * \e string \b parent_mid * * \e string \b otype * * \e string \b verb * * \e string \b activity */ public static function submit($params) { logger('notification: entry', LOGGER_DEBUG); // throw a small amount of entropy into the system to breakup duplicates arriving at the same precise instant. usleep(mt_rand(0, 10000)); if ($params['from_xchan']) { $x = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($params['from_xchan'])); } if ($params['to_xchan']) { $y = q("select channel.*, account.* from channel left join account on channel_account_id = account_id\n\t\t\t\twhere channel_hash = '%s' and channel_removed = 0 limit 1", dbesc($params['to_xchan'])); } if ($x & $y) { $sender = $x[0]; $recip = $y[0]; } else { logger('notification: no sender or recipient.'); logger('sender: ' . $params['from_xchan']); logger('recip: ' . $params['to_xchan']); return; } // from here on everything is in the recipients language push_lang($recip['account_language']); // should probably have a channel language $banner = t('$Projectname Notification'); $product = t('$projectname'); // PLATFORM_NAME; $siteurl = z_root(); $thanks = t('Thank You,'); $sitename = get_config('system', 'sitename'); $site_admin = sprintf(t('%s Administrator'), $sitename); $sender_name = $product; $hostname = \App::get_hostname(); if (strpos($hostname, ':')) { $hostname = substr($hostname, 0, strpos($hostname, ':')); } // Do not translate 'noreply' as it must be a legal 7-bit email address $reply_email = get_config('system', 'reply_address'); if (!$reply_email) { $reply_email = 'noreply' . '@' . $hostname; } $sender_email = get_config('system', 'from_email'); if (!$sender_email) { $sender_email = 'Administrator' . '@' . \App::get_hostname(); } $sender_name = get_config('system', 'from_email_name'); if (!$sender_name) { $sender_name = \Zotlabs\Lib\System::get_site_name(); } $additional_mail_header = ""; if (array_key_exists('item', $params)) { require_once 'include/conversation.php'; // if it's a normal item... if (array_key_exists('verb', $params['item'])) { // localize_item() alters the original item so make a copy first $i = $params['item']; logger('calling localize'); localize_item($i); $title = $i['title']; $body = $i['body']; $private = $i['item_private'] || intval($i['item_obscured']); } else { $title = $params['item']['title']; $body = $params['item']['body']; } } else { $title = $body = ''; } // e.g. "your post", "David's photo", etc. $possess_desc = t('%s <!item_type!>'); if ($params['type'] == NOTIFY_MAIL) { logger('notification: mail'); $subject = sprintf(t('[$Projectname:Notify] New mail received at %s'), $sitename); $preamble = sprintf(t('%1$s, %2$s sent you a new private message at %3$s.'), $recip['channel_name'], $sender['xchan_name'], $sitename); $epreamble = sprintf(t('%1$s sent you %2$s.'), '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', '[zrl=$itemlink]' . t('a private message') . '[/zrl]'); $sitelink = t('Please visit %s to view and/or reply to your private messages.'); $tsitelink = sprintf($sitelink, $siteurl . '/mail/' . $params['item']['id']); $hsitelink = sprintf($sitelink, '<a href="' . $siteurl . '/mail/' . $params['item']['id'] . '">' . $sitename . '</a>'); $itemlink = $siteurl . '/mail/' . $params['item']['id']; } if ($params['type'] == NOTIFY_COMMENT) { // logger("notification: params = " . print_r($params, true), LOGGER_DEBUG); $itemlink = $params['link']; // ignore like/unlike activity on posts - they probably require a separate notification preference if (array_key_exists('item', $params) && !visible_activity($params['item'])) { logger('notification: not a visible activity. Ignoring.'); pop_lang(); return; } $parent_mid = $params['parent_mid']; // Check to see if there was already a notify for this post. // If so don't create a second notification $p = null; $p = q("select id from notify where link = '%s' and uid = %d limit 1", dbesc($params['link']), intval($recip['channel_id'])); if ($p) { logger('notification: comment already notified'); pop_lang(); return; } // if it's a post figure out who's post it is. $p = null; if ($params['otype'] === 'item' && $parent_mid) { $p = q("select * from item where mid = '%s' and uid = %d limit 1", dbesc($parent_mid), intval($recip['channel_id'])); } xchan_query($p); $item_post_type = item_post_type($p[0]); // $private = $p[0]['item_private']; $parent_id = $p[0]['id']; $parent_item = $p[0]; //$possess_desc = str_replace('<!item_type!>',$possess_desc); // "a post" $dest_str = sprintf(t('%1$s, %2$s commented on [zrl=%3$s]a %4$s[/zrl]'), $recip['channel_name'], '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', $itemlink, $item_post_type); // "George Bull's post" if ($p) { $dest_str = sprintf(t('%1$s, %2$s commented on [zrl=%3$s]%4$s\'s %5$s[/zrl]'), $recip['channel_name'], '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', $itemlink, $p[0]['author']['xchan_name'], $item_post_type); } // "your post" if ($p[0]['owner']['xchan_name'] == $p[0]['author']['xchan_name'] && intval($p[0]['item_wall'])) { $dest_str = sprintf(t('%1$s, %2$s commented on [zrl=%3$s]your %4$s[/zrl]'), $recip['channel_name'], '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', $itemlink, $item_post_type); } // Some mail softwares relies on subject field for threading. // So, we cannot have different subjects for notifications of the same thread. // Before this we have the name of the replier on the subject rendering // differents subjects for messages on the same thread. $subject = sprintf(t('[$Projectname:Notify] Comment to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']); $preamble = sprintf(t('%1$s, %2$s commented on an item/conversation you have been following.'), $recip['channel_name'], $sender['xchan_name']); $epreamble = $dest_str; $sitelink = t('Please visit %s to view and/or reply to the conversation.'); $tsitelink = sprintf($sitelink, $siteurl); $hsitelink = sprintf($sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>'); } if ($params['type'] == NOTIFY_WALL) { $subject = sprintf(t('[$Projectname:Notify] %s posted to your profile wall'), $sender['xchan_name']); $preamble = sprintf(t('%1$s, %2$s posted to your profile wall at %3$s'), $recip['channel_name'], $sender['xchan_name'], $sitename); $epreamble = sprintf(t('%1$s, %2$s posted to [zrl=%3$s]your wall[/zrl]'), $recip['channel_name'], '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', $params['link']); $sitelink = t('Please visit %s to view and/or reply to the conversation.'); $tsitelink = sprintf($sitelink, $siteurl); $hsitelink = sprintf($sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>'); $itemlink = $params['link']; } if ($params['type'] == NOTIFY_TAGSELF) { $p = null; $p = q("select id from notify where link = '%s' and uid = %d limit 1", dbesc($params['link']), intval($recip['channel_id'])); if ($p) { logger('enotify: tag: already notified about this post'); pop_lang(); return; } $subject = sprintf(t('[$Projectname:Notify] %s tagged you'), $sender['xchan_name']); $preamble = sprintf(t('%1$s, %2$s tagged you at %3$s'), $recip['channel_name'], $sender['xchan_name'], $sitename); $epreamble = sprintf(t('%1$s, %2$s [zrl=%3$s]tagged you[/zrl].'), $recip['channel_name'], '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', $params['link']); $sitelink = t('Please visit %s to view and/or reply to the conversation.'); $tsitelink = sprintf($sitelink, $siteurl); $hsitelink = sprintf($sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>'); $itemlink = $params['link']; } if ($params['type'] == NOTIFY_POKE) { $subject = sprintf(t('[$Projectname:Notify] %1$s poked you'), $sender['xchan_name']); $preamble = sprintf(t('%1$s, %2$s poked you at %3$s'), $recip['channel_name'], $sender['xchan_name'], $sitename); $epreamble = sprintf(t('%1$s, %2$s [zrl=%2$s]poked you[/zrl].'), $recip['channel_name'], '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', $params['link']); $subject = str_replace('poked', t($params['activity']), $subject); $preamble = str_replace('poked', t($params['activity']), $preamble); $epreamble = str_replace('poked', t($params['activity']), $epreamble); $sitelink = t('Please visit %s to view and/or reply to the conversation.'); $tsitelink = sprintf($sitelink, $siteurl); $hsitelink = sprintf($sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>'); $itemlink = $params['link']; } if ($params['type'] == NOTIFY_TAGSHARE) { $subject = sprintf(t('[$Projectname:Notify] %s tagged your post'), $sender['xchan_name']); $preamble = sprintf(t('%1$s, %2$s tagged your post at %3$s'), $recip['channel_name'], $sender['xchan_name'], $sitename); $epreamble = sprintf(t('%1$s, %2$s tagged [zrl=%3$s]your post[/zrl]'), $recip['channel_name'], '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', $itemlink); $sitelink = t('Please visit %s to view and/or reply to the conversation.'); $tsitelink = sprintf($sitelink, $siteurl); $hsitelink = sprintf($sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>'); $itemlink = $params['link']; } if ($params['type'] == NOTIFY_INTRO) { $subject = sprintf(t('[$Projectname:Notify] Introduction received')); $preamble = sprintf(t('%1$s, you\'ve received an new connection request from \'%2$s\' at %3$s'), $recip['channel_name'], $sender['xchan_name'], $sitename); $epreamble = sprintf(t('%1$s, you\'ve received [zrl=%2$s]a new connection request[/zrl] from %3$s.'), $recip['channel_name'], $siteurl . '/connections/ifpending', '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]'); $body = sprintf(t('You may visit their profile at %s'), $sender['xchan_url']); $sitelink = t('Please visit %s to approve or reject the connection request.'); $tsitelink = sprintf($sitelink, $siteurl . '/connections/ifpending'); $hsitelink = sprintf($sitelink, '<a href="' . $siteurl . '/connections/ifpending">' . $sitename . '</a>'); $itemlink = $params['link']; } if ($params['type'] == NOTIFY_SUGGEST) { $subject = sprintf(t('[$Projectname:Notify] Friend suggestion received')); $preamble = sprintf(t('%1$s, you\'ve received a friend suggestion from \'%2$s\' at %3$s'), $recip['channel_name'], $sender['xchan_name'], $sitename); $epreamble = sprintf(t('%1$s, you\'ve received [zrl=%2$s]a friend suggestion[/zrl] for %3$s from %4$s.'), $recip['channel_name'], $itemlink, '[zrl=' . $params['item']['url'] . ']' . $params['item']['name'] . '[/zrl]', '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]'); $body = t('Name:') . ' ' . $params['item']['name'] . "\n"; $body .= t('Photo:') . ' ' . $params['item']['photo'] . "\n"; $body .= sprintf(t('You may visit their profile at %s'), $params['item']['url']); $sitelink = t('Please visit %s to approve or reject the suggestion.'); $tsitelink = sprintf($sitelink, $siteurl); $hsitelink = sprintf($sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>'); $itemlink = $params['link']; } if ($params['type'] == NOTIFY_CONFIRM) { // ? } if ($params['type'] == NOTIFY_SYSTEM) { // ? } $h = array('params' => $params, 'subject' => $subject, 'preamble' => $preamble, 'epreamble' => $epreamble, 'body' => $body, 'sitelink' => $sitelink, 'sitename' => $sitename, 'tsitelink' => $tsitelink, 'hsitelink' => $hsitelink, 'itemlink' => $itemlink, 'sender' => $sender, 'recipient' => $recip); call_hooks('enotify', $h); $subject = $h['subject']; $preamble = $h['preamble']; $epreamble = $h['epreamble']; $body = $h['body']; $sitelink = $h['sitelink']; $tsitelink = $h['tsitelink']; $hsitelink = $h['hsitelink']; $itemlink = $h['itemlink']; require_once 'include/html2bbcode.php'; do { $dups = false; $hash = random_string(); $r = q("SELECT `id` FROM `notify` WHERE `hash` = '%s' LIMIT 1", dbesc($hash)); if ($r) { $dups = true; } } while ($dups === true); $datarray = array(); $datarray['hash'] = $hash; $datarray['sender_hash'] = $sender['xchan_hash']; $datarray['xname'] = $sender['xchan_name']; $datarray['url'] = $sender['xchan_url']; $datarray['photo'] = $sender['xchan_photo_s']; $datarray['created'] = datetime_convert(); $datarray['aid'] = $recip['channel_account_id']; $datarray['uid'] = $recip['channel_id']; $datarray['link'] = $itemlink; $datarray['parent'] = $parent_mid; $datarray['parent_item'] = $parent_item; $datarray['ntype'] = $params['type']; $datarray['verb'] = $params['verb']; $datarray['otype'] = $params['otype']; $datarray['abort'] = false; $datarray['item'] = $params['item']; call_hooks('enotify_store', $datarray); if ($datarray['abort']) { pop_lang(); return; } // create notification entry in DB $seen = 0; // Mark some notifications as seen right away // Note! The notification have to be created, because they are used to send emails // So easiest solution to hide them from Notices is to mark them as seen right away. // Another option would be to not add them to the DB, and change how emails are handled // (probably would be better that way) $always_show_in_notices = get_pconfig($recip['channel_id'], 'system', 'always_show_in_notices'); if (!$always_show_in_notices) { if ($params['type'] == NOTIFY_WALL || $params['type'] == NOTIFY_MAIL || $params['type'] == NOTIFY_INTRO) { $seen = 1; } } $r = q("insert into notify (hash,xname,url,photo,created,aid,uid,link,parent,seen,ntype,verb,otype)\n\t\tvalues('%s','%s','%s','%s','%s',%d,%d,'%s','%s',%d,%d,'%s','%s')", dbesc($datarray['hash']), dbesc($datarray['xname']), dbesc($datarray['url']), dbesc($datarray['photo']), dbesc($datarray['created']), intval($datarray['aid']), intval($datarray['uid']), dbesc($datarray['link']), dbesc($datarray['parent']), intval($seen), intval($datarray['ntype']), dbesc($datarray['verb']), dbesc($datarray['otype'])); $r = q("select id from notify where hash = '%s' and uid = %d limit 1", dbesc($hash), intval($recip['channel_id'])); if ($r) { $notify_id = $r[0]['id']; } else { logger('notification not found.'); pop_lang(); return; } $itemlink = z_root() . '/notify/view/' . $notify_id; $msg = str_replace('$itemlink', $itemlink, $epreamble); // wretched hack, but we don't want to duplicate all the preamble variations and we also don't want to screw up a translation if ((\App::$language === 'en' || !\App::$language) && strpos($msg, ', ')) { $msg = substr($msg, strpos($msg, ', ') + 1); } $r = q("update notify set msg = '%s' where id = %d and uid = %d", dbesc($msg), intval($notify_id), intval($datarray['uid'])); // send email notification if notification preferences permit require_once 'bbcode.php'; if (intval($recip['channel_notifyflags']) & intval($params['type']) || $params['type'] == NOTIFY_SYSTEM) { logger('notification: sending notification email'); $hn = get_pconfig($recip['channel_id'], 'system', 'email_notify_host'); if ($hn && !stristr(\App::get_hostname(), $hn)) { // this isn't the email notification host pop_lang(); return; } $textversion = strip_tags(html_entity_decode(bbcode(stripslashes(str_replace(array("\\r", "\\n"), array("", "\n"), $body))), ENT_QUOTES, 'UTF-8')); $htmlversion = bbcode(stripslashes(str_replace(array("\\r", "\\n"), array("", "<br />\n"), $body))); // use $_SESSION['zid_override'] to force zid() to use // the recipient address instead of the current observer $_SESSION['zid_override'] = channel_reddress($recip); $_SESSION['zrl_override'] = z_root() . '/channel/' . $recip['channel_address']; $textversion = zidify_links($textversion); $htmlversion = zidify_links($htmlversion); // unset when done to revert to normal behaviour unset($_SESSION['zid_override']); unset($_SESSION['zrl_override']); $datarray = array(); $datarray['banner'] = $banner; $datarray['product'] = $product; $datarray['preamble'] = $preamble; $datarray['sitename'] = $sitename; $datarray['siteurl'] = $siteurl; $datarray['type'] = $params['type']; $datarray['parent'] = $params['parent_mid']; $datarray['source_name'] = $sender['xchan_name']; $datarray['source_link'] = $sender['xchan_url']; $datarray['source_photo'] = $sender['xchan_photo_s']; $datarray['uid'] = $recip['channel_id']; $datarray['username'] = $recip['channel_name']; $datarray['hsitelink'] = $hsitelink; $datarray['tsitelink'] = $tsitelink; $datarray['hitemlink'] = '<a href="' . $itemlink . '">' . $itemlink . '</a>'; $datarray['titemlink'] = $itemlink; $datarray['thanks'] = $thanks; $datarray['site_admin'] = $site_admin; $datarray['title'] = stripslashes($title); $datarray['htmlversion'] = $htmlversion; $datarray['textversion'] = $textversion; $datarray['subject'] = $subject; $datarray['headers'] = $additional_mail_header; $datarray['email_secure'] = false; call_hooks('enotify_mail', $datarray); // Default to private - don't disclose message contents over insecure channels (such as email) // Might be interesting to use GPG,PGP,S/MIME encryption instead // but we'll save that for a clever plugin developer to implement $private_activity = false; if (!$datarray['email_secure']) { switch ($params['type']) { case NOTIFY_WALL: case NOTIFY_TAGSELF: case NOTIFY_POKE: case NOTIFY_COMMENT: if (!$private) { break; } $private_activity = true; case NOTIFY_MAIL: $datarray['textversion'] = $datarray['htmlversion'] = $datarray['title'] = ''; $datarray['subject'] = preg_replace('/' . preg_quote(t('[$Projectname:Notify]')) . '/', '$0*', $datarray['subject']); break; default: break; } } if ($private_activity && intval(get_pconfig($datarray['uid'], 'system', 'ignore_private_notifications'))) { pop_lang(); return; } // load the template for private message notifications $tpl = get_markup_template('email_notify_html.tpl'); $email_html_body = replace_macros($tpl, array('$banner' => $datarray['banner'], '$notify_icon' => \Zotlabs\Lib\System::get_notify_icon(), '$product' => $datarray['product'], '$preamble' => $datarray['preamble'], '$sitename' => $datarray['sitename'], '$siteurl' => $datarray['siteurl'], '$source_name' => $datarray['source_name'], '$source_link' => $datarray['source_link'], '$source_photo' => $datarray['source_photo'], '$username' => $datarray['to_name'], '$hsitelink' => $datarray['hsitelink'], '$hitemlink' => $datarray['hitemlink'], '$thanks' => $datarray['thanks'], '$site_admin' => $datarray['site_admin'], '$title' => $datarray['title'], '$htmlversion' => $datarray['htmlversion'])); // load the template for private message notifications $tpl = get_markup_template('email_notify_text.tpl'); $email_text_body = replace_macros($tpl, array('$banner' => $datarray['banner'], '$product' => $datarray['product'], '$preamble' => $datarray['preamble'], '$sitename' => $datarray['sitename'], '$siteurl' => $datarray['siteurl'], '$source_name' => $datarray['source_name'], '$source_link' => $datarray['source_link'], '$source_photo' => $datarray['source_photo'], '$username' => $datarray['to_name'], '$tsitelink' => $datarray['tsitelink'], '$titemlink' => $datarray['titemlink'], '$thanks' => $datarray['thanks'], '$site_admin' => $datarray['site_admin'], '$title' => $datarray['title'], '$textversion' => $datarray['textversion'])); // logger('text: ' . $email_text_body); // use the EmailNotification library to send the message self::send(array('fromName' => $sender_name, 'fromEmail' => $sender_email, 'replyTo' => $reply_email, 'toEmail' => $recip['account_email'], 'messageSubject' => $datarray['subject'], 'htmlVersion' => $email_html_body, 'textVersion' => $email_text_body, 'additionalMailHeader' => $datarray['headers'])); } pop_lang(); }
function send_message($uid = 0, $recipient = '', $body = '', $subject = '', $replyto = '', $expires = '') { $ret = array('success' => false); $is_reply = false; $a = get_app(); $observer_hash = get_observer_hash(); if (!$recipient) { $ret['message'] = t('No recipient provided.'); return $ret; } if (!strlen($subject)) { $subject = t('[no subject]'); } // if(! $expires) // $expires = NULL_DATE; // else // $expires = datetime_convert(date_default_timezone_get(),'UTC',$expires); if ($uid) { $r = q("select * from channel where channel_id = %d limit 1", intval($uid)); if ($r) { $channel = $r[0]; } } else { $channel = App::get_channel(); } if (!$channel) { $ret['message'] = t('Unable to determine sender.'); return $ret; } // look for any existing conversation structure $conv_guid = ''; if (strlen($replyto)) { $is_reply = true; $r = q("select conv_guid from mail where channel_id = %d and ( mid = '%s' or parent_mid = '%s' ) limit 1", intval(local_channel()), dbesc($replyto), dbesc($replyto)); if ($r) { $conv_guid = $r[0]['conv_guid']; } } if (!$conv_guid) { // create a new conversation $conv_guid = random_string(); $recip = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($recipient)); if ($recip) { $recip_handle = $recip[0]['xchan_addr']; } $sender_handle = channel_reddress($channel); $handles = $recip_handle . ';' . $sender_handle; if ($subject) { $nsubject = str_rot47(base64url_encode($subject)); } $r = q("insert into conv (uid,guid,creator,created,updated,subject,recips) values(%d, '%s', '%s', '%s', '%s', '%s', '%s') ", intval(local_channel()), dbesc($conv_guid), dbesc($sender_handle), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($nsubject), dbesc($handles)); $r = q("select * from conv where guid = '%s' and uid = %d limit 1", dbesc($conv_guid), intval(local_channel())); if ($r) { $retconv = $r[0]; $retconv['subject'] = base64url_decode(str_rot47($retconv['subject'])); } } if (!$retconv) { $r = q("select * from conv where guid = '%s' and uid = %d limit 1", dbesc($conv_guid), intval(local_channel())); if ($r) { $retconv = $r[0]; $retconv['subject'] = base64url_decode(str_rot47($retconv['subject'])); } } if (!$retconv) { $ret['message'] = 'conversation not found'; return $ret; } // generate a unique message_id do { $dups = false; $hash = random_string(); $mid = $hash . '@' . App::get_hostname(); $r = q("SELECT id FROM mail WHERE mid = '%s' LIMIT 1", dbesc($mid)); if (count($r)) { $dups = true; } } while ($dups == true); if (!strlen($replyto)) { $replyto = $mid; } /** * * When a photo was uploaded into the message using the (profile wall) ajax * uploader, The permissions are initially set to disallow anybody but the * owner from seeing it. This is because the permissions may not yet have been * set for the post. If it's private, the photo permissions should be set * appropriately. But we didn't know the final permissions on the post until * now. So now we'll look for links of uploaded messages that are in the * post and set them to the same permissions as the post itself. * */ $match = null; $images = null; if (preg_match_all("/\\[zmg\\=([0-9]*)x([0-9]*)\\](.*?)\\[\\/zmg\\]/", strpos($body, '[/crypt]') ? $_POST['media_str'] : $body, $match)) { $images = $match[3]; } $match = false; if (preg_match_all("/\\[attachment\\](.*?)\\[\\/attachment\\]/", strpos($body, '[/crypt]') ? $_POST['media_str'] : $body, $match)) { $attaches = $match[1]; } $attachments = ''; if (preg_match_all('/(\\[attachment\\](.*?)\\[\\/attachment\\])/', $body, $match)) { $attachments = array(); foreach ($match[2] as $mtch) { $hash = substr($mtch, 0, strpos($mtch, ',')); $rev = intval(substr($mtch, strpos($mtch, ','))); $r = attach_by_hash_nodata($hash, get_observer_hash(), $rev); if ($r['success']) { $attachments[] = array('href' => z_root() . '/attach/' . $r['data']['hash'], 'length' => $r['data']['filesize'], 'type' => $r['data']['filetype'], 'title' => urlencode($r['data']['filename']), 'revision' => $r['data']['revision']); } $body = trim(str_replace($match[1], '', $body)); } } $jattach = $attachments ? json_encode($attachments) : ''; if ($subject) { $subject = str_rot47(base64url_encode($subject)); } if ($body) { $body = str_rot47(base64url_encode($body)); } $r = q("INSERT INTO mail ( account_id, conv_guid, mail_obscured, channel_id, from_xchan, to_xchan, title, body, attach, mid, parent_mid, created, expires, mail_isreply )\n\t\tVALUES ( %d, '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d )", intval($channel['channel_account_id']), dbesc($conv_guid), intval(1), intval($channel['channel_id']), dbesc($channel['channel_hash']), dbesc($recipient), dbesc($subject), dbesc($body), dbesc($jattach), dbesc($mid), dbesc($replyto), dbesc(datetime_convert()), dbescdate($expires), intval($is_reply)); // verify the save $r = q("SELECT * FROM mail WHERE mid = '%s' and channel_id = %d LIMIT 1", dbesc($mid), intval($channel['channel_id'])); if ($r) { $post_id = $r[0]['id']; $retmail = $r[0]; xchan_mail_query($retmail); } else { $ret['message'] = t('Stored post could not be verified.'); return $ret; } if (count($images)) { foreach ($images as $image) { if (!stristr($image, z_root() . '/photo/')) { continue; } $image_uri = substr($image, strrpos($image, '/') + 1); $image_uri = substr($image_uri, 0, strpos($image_uri, '-')); $r = q("UPDATE photo SET allow_cid = '%s' WHERE resource_id = '%s' AND uid = %d and allow_cid = '%s'", dbesc('<' . $recipient . '>'), dbesc($image_uri), intval($channel['channel_id']), dbesc('<' . $channel['channel_hash'] . '>')); $r = q("UPDATE attach SET allow_cid = '%s' WHERE hash = '%s' AND is_photo = 1 and uid = %d and allow_cid = '%s'", dbesc('<' . $recipient . '>'), dbesc($image_uri), intval($channel['channel_id']), dbesc('<' . $channel['channel_hash'] . '>')); } } if ($attaches) { foreach ($attaches as $attach) { $hash = substr($attach, 0, strpos($attach, ',')); $rev = intval(substr($attach, strpos($attach, ','))); attach_store($channel, $observer_hash, $options = 'update', array('hash' => $hash, 'revision' => $rev, 'allow_cid' => '<' . $recipient . '>')); } } Zotlabs\Daemon\Master::Summon(array('Notifier', 'mail', $post_id)); $ret['success'] = true; $ret['message_item'] = intval($post_id); $ret['conv'] = $retconv; $ret['mail'] = $retmail; return $ret; }