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 (intval($messages[$k]['mail_obscured'])) { if ($messages[$k]['title']) { $messages[$k]['title'] = base64url_decode(str_rot47($messages[$k]['title'])); } if ($messages[$k]['body']) { $messages[$k]['body'] = base64url_decode(str_rot47($messages[$k]['body'])); } } } if ($updateseen) { $r = q("UPDATE `mail` SET mail_seen = 1 where mail_seen = 0 and parent_mid = '%s' AND channel_id = %d", dbesc($r[0]['parent_mid']), intval($channel_id)); } return $messages; }
/** * @brief Create an array representing the important channel information * which would be necessary to create a nomadic identity clone. This includes * most channel resources and connection information with the exception of content. * * @param int $channel_id * Channel_id to export * @param boolean $items * Include channel posts (wall items), default false * * @returns array * See function for details */ function identity_basic_export($channel_id, $items = false) { /* * Red basic channel export */ $ret = array(); $ret['compatibility'] = array('project' => PLATFORM_NAME, 'version' => RED_VERSION, 'database' => DB_UPDATE_VERSION); $r = q("select * from channel where channel_id = %d limit 1", intval($channel_id)); if ($r) { $ret['channel'] = $r[0]; } $r = q("select * from profile where uid = %d", intval($channel_id)); if ($r) { $ret['profile'] = $r; } $xchans = array(); $r = q("select * from abook where abook_channel = %d ", intval($channel_id)); if ($r) { $ret['abook'] = $r; foreach ($r as $rr) { $xchans[] = $rr['abook_xchan']; } stringify_array_elms($xchans); } if ($xchans) { $r = q("select * from xchan where xchan_hash in ( " . implode(',', $xchans) . " ) "); if ($r) { $ret['xchan'] = $r; } $r = q("select * from hubloc where hubloc_hash in ( " . implode(',', $xchans) . " ) "); if ($r) { $ret['hubloc'] = $r; } } $r = q("select * from `groups` where uid = %d ", intval($channel_id)); if ($r) { $ret['group'] = $r; } $r = q("select * from group_member where uid = %d ", intval($channel_id)); if ($r) { $ret['group_member'] = $r; } $r = q("select * from pconfig where uid = %d", intval($channel_id)); if ($r) { $ret['config'] = $r; } $r = q("select type, data, os_storage from photo where scale = 4 and profile = 1 and uid = %d limit 1", intval($channel_id)); if ($r) { $ret['photo'] = array('type' => $r[0]['type'], 'data' => $r[0]['os_storage'] ? base64url_encode(file_get_contents($r[0]['data'])) : base64url_encode($r[0]['data'])); } // All other term types will be included in items, if requested. $r = q("select * from term where type in (%d,%d) and uid = %d", intval(TERM_SAVEDSEARCH), intval(TERM_THING), intval($channel_id)); if ($r) { $ret['term'] = $r; } // add psuedo-column obj_baseurl to aid in relocations $r = q("select obj.*, '%s' as obj_baseurl from obj where obj_channel = %d", dbesc(z_root()), intval($channel_id)); if ($r) { $ret['obj'] = $r; } $r = q("select * from app where app_channel = %d", intval($channel_id)); if ($r) { $ret['app'] = $r; } $r = q("select * from chatroom where cr_uid = %d", intval($channel_id)); if ($r) { $ret['chatroom'] = $r; } $r = q("select * from event where uid = %d", intval($channel_id)); if ($r) { $ret['event'] = $r; } $r = q("select * from item where resource_type = 'event' and uid = %d", intval($channel_id)); if ($r) { $ret['event_item'] = array(); xchan_query($r); $r = fetch_post_tags($r, true); foreach ($r as $rr) { $ret['event_item'][] = encode_item($rr, true); } } $x = menu_list($channel_id); if ($x) { $ret['menu'] = array(); for ($y = 0; $y < count($x); $y++) { $m = menu_fetch($x[$y]['menu_name'], $channel_id, $ret['channel']['channel_hash']); if ($m) { $ret['menu'][] = menu_element($m); } } } $x = menu_list($channel_id); if ($x) { $ret['menu'] = array(); for ($y = 0; $y < count($x); $y++) { $m = menu_fetch($x[$y]['menu_name'], $channel_id, $ret['channel']['channel_hash']); if ($m) { $ret['menu'][] = menu_element($m); } } } $addon = array('channel_id' => $channel_id, 'data' => $ret); call_hooks('identity_basic_export', $addon); $ret = $addon['data']; if (!$items) { return $ret; } $r = q("select * from likes where channel_id = %d", intval($channel_id)); if ($r) { $ret['likes'] = $r; } $r = q("select * from conv where uid = %d", intval($channel_id)); if ($r) { for ($x = 0; $x < count($r); $x++) { $r[$x]['subject'] = base64url_decode(str_rot47($r[$x]['subject'])); } $ret['conv'] = $r; } $r = q("select * from mail where mail.uid = %d", intval($channel_id)); if ($r) { $m = array(); foreach ($r as $rr) { xchan_mail_query($rr); $m[] = mail_encode($rr, true); } $ret['mail'] = $m; } $r = q("select item_id.*, item.mid from item_id left join item on item_id.iid = item.id where item_id.uid = %d", intval($channel_id)); if ($r) { $ret['item_id'] = $r; } //$key = get_config('system','prvkey'); /** @warning this may run into memory limits on smaller systems */ /** export three months of posts. If you want to export and import all posts you have to start with * the first year and export/import them in ascending order. * * Don't export linked resource items. we'll have to pull those out separately. */ $r = q("select * from item where item_wall = 1 and item_deleted = 0 and uid = %d and created > %s - INTERVAL %s and resource_type = '' order by created", intval($channel_id), db_utcnow(), db_quoteinterval('3 MONTH')); if ($r) { $ret['item'] = array(); xchan_query($r); $r = fetch_post_tags($r, true); foreach ($r as $rr) { $ret['item'][] = encode_item($rr, true); } } return $ret; }
function unobscure_mail(&$item) { if (array_key_exists('mail_obscured', $item) && intval($item['mail_obscured'])) { if ($item['title']) { $item['title'] = base64url_decode(str_rot47($item['title'])); } if ($item['body']) { $item['body'] = base64url_decode(str_rot47($item['body'])); } } }
function get_mail_elements($x) { $arr = array(); $arr['body'] = $x['body'] ? htmlspecialchars($x['body'], ENT_COMPAT, 'UTF-8', false) : ''; $arr['title'] = $x['title'] ? htmlspecialchars($x['title'], ENT_COMPAT, 'UTF-8', false) : ''; $arr['conv_guid'] = $x['conv_guid'] ? htmlspecialchars($x['conv_guid'], ENT_COMPAT, 'UTF-8', false) : ''; $arr['created'] = datetime_convert('UTC', 'UTC', $x['created']); if (!array_key_exists('expires', $x) || $x['expires'] === NULL_DATE) { $arr['expires'] = NULL_DATE; } else { $arr['expires'] = datetime_convert('UTC', 'UTC', $x['expires']); } $arr['mail_flags'] = 0; if ($x['flags'] && is_array($x['flags'])) { if (in_array('recalled', $x['flags'])) { $arr['mail_recalled'] = 1; } if (in_array('replied', $x['flags'])) { $arr['mail_replied'] = 1; } if (in_array('isreply', $x['flags'])) { $arr['mail_isreply'] = 1; } if (in_array('seen', $x['flags'])) { $arr['mail_seen'] = 1; } if (in_array('deleted', $x['flags'])) { $arr['mail_deleted'] = 1; } } $key = get_config('system', 'pubkey'); $arr['mail_obscured'] = 1; if ($arr['body']) { $arr['body'] = str_rot47(base64url_encode($arr['body'])); } if ($arr['title']) { $arr['title'] = str_rot47(base64url_encode($arr['title'])); } if ($arr['created'] > datetime_convert()) { $arr['created'] = datetime_convert(); } $arr['mid'] = $x['message_id'] ? htmlspecialchars($x['message_id'], ENT_COMPAT, 'UTF-8', false) : ''; $arr['parent_mid'] = $x['message_parent'] ? htmlspecialchars($x['message_parent'], ENT_COMPAT, 'UTF-8', false) : ''; if ($x['attach']) { $arr['attach'] = activity_sanitise($x['attach']); } if (($xchan_hash = import_author_xchan($x['from'])) !== false) { $arr['from_xchan'] = $xchan_hash; } else { return array(); } if (($xchan_hash = import_author_xchan($x['to'])) !== false) { $arr['to_xchan'] = $xchan_hash; } else { return array(); } return $arr; }
function import_conv($channel, $convs) { if ($channel && $convs) { foreach ($convs as $conv) { if ($conv['deleted']) { q("delete from conv where guid = '%s' and uid = %d limit 1", dbesc($conv['guid']), intval($channel['channel_id'])); continue; } unset($conv['id']); $conv['uid'] = $channel['channel_id']; $conv['subject'] = str_rot47(base64url_encode($conv['subject'])); $r = q("select id from conv where guid = '%s' and uid = %d limit 1", dbesc($conv['guid']), intval($channel['channel_id'])); if ($r) { continue; } dbesc_array($conv); $r = dbq("INSERT INTO conv (`" . implode("`, `", array_keys($conv)) . "`) VALUES ('" . implode("', '", array_values($conv)) . "')"); } } }
function diaspora_send_mail($item, $owner, $contact) { $a = get_app(); $myaddr = $owner['channel_address'] . '@' . App::get_hostname(); $r = q("select * from conv where guid = '%s' and uid = %d limit 1", dbesc($item['conv_guid']), intval($item['channel_id'])); if (!count($r)) { logger('diaspora_send_mail: conversation not found.'); return; } $z = q("select from_xchan from mail where conv_guid = '%s' and channel_id = %d and mid = parent_mid limit 1", dbesc($item['conv_guid']), intval($item['channel_id'])); $conv_owner = $z && $z[0]['from_xchan'] === $owner['channel_hash'] ? true : false; $cnv = $r[0]; $cnv['subject'] = base64url_decode(str_rot47($cnv['subject'])); $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_obscured', $item) && intval($item['mail_obscured'])) { if ($item['title']) { $item['title'] = base64url_decode(str_rot47($item['title'])); } if ($item['body']) { $item['body'] = base64url_decode(str_rot47($item['body'])); } } // the parent_guid needs to be the conversation guid $parent_ptr = $cnv['guid']; $body = bb2diaspora($item['body']); $created = datetime_convert('UTC', 'UTC', $item['created'], 'Y-m-d H:i:s \\U\\T\\C'); $signed_text = $item['mid'] . ';' . $parent_ptr . ';' . $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($parent_ptr), 'parent_author_signature' => $conv_owner ? xmlify($sig) : null, 'author_signature' => xmlify($sig), 'text' => xmlify($body), 'created_at' => xmlify($created), 'diaspora_handle' => xmlify($myaddr), 'conversation_guid' => xmlify($cnv['guid'])); if ($item['mail_isreply']) { $tpl = get_markup_template('diaspora_message.tpl', 'addon/diaspora'); $xmsg = replace_macros($tpl, array('$msg' => $msg)); } else { $conv['messages'] = array($msg); $tpl = get_markup_template('diaspora_conversation.tpl', 'addon/diaspora'); $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_queue($owner, $contact, $slap, false, $item['mid']); }
function diaspora_message($importer, $xml, $msg) { $a = get_app(); $msg_guid = notags(unxmlify($xml['guid'])); $msg_parent_guid = notags(unxmlify($xml['parent_guid'])); $msg_parent_author_signature = notags(unxmlify($xml['parent_author_signature'])); $msg_author_signature = notags(unxmlify($xml['author_signature'])); $msg_text = unxmlify($xml['text']); $msg_created_at = datetime_convert('UTC', 'UTC', notags(unxmlify($xml['created_at']))); $msg_diaspora_handle = notags(diaspora_get_author($xml)); $msg_conversation_guid = notags(unxmlify($xml['conversation_guid'])); $parent_uri = $msg_parent_guid; $contact = diaspora_get_contact_by_handle($importer['channel_id'], $msg_diaspora_handle); if (!$contact) { logger('diaspora_message: cannot find contact: ' . $msg_diaspora_handle); return; } if (!perm_is_allowed($importer['channel_id'], $contact['xchan_hash'], 'post_mail')) { logger('Ignoring this author.'); return 202; } $conversation = null; $c = q("select * from conv where uid = %d and guid = '%s' limit 1", intval($importer['channel_id']), dbesc($msg_conversation_guid)); if ($c) { $conversation = $c[0]; } else { logger('diaspora_message: conversation not available.'); return; } $reply = 0; $subject = $conversation['subject']; //this is already encoded $body = diaspora2bb($msg_text); $maxlen = get_max_import_size(); if ($maxlen && mb_strlen($body) > $maxlen) { $body = mb_substr($body, 0, $maxlen, 'UTF-8'); logger('message length exceeds max_import_size: truncated'); } $parent_ptr = $msg_parent_guid; if ($parent_ptr === $conversation['guid']) { // this should always be the case $x = q("select mid from mail where conv_guid = '%s' and channel_id = %d order by id asc limit 1", dbesc($conversation['guid']), intval($importer['channel_id'])); if ($x) { $parent_ptr = $x[0]['mid']; } } $author_signed_data = $msg_guid . ';' . $msg_parent_guid . ';' . $msg_text . ';' . unxmlify($xml['created_at']) . ';' . $msg_diaspora_handle . ';' . $msg_conversation_guid; $author_signature = base64_decode($msg_author_signature); $person = find_diaspora_person_by_handle($msg_diaspora_handle); if (is_array($person) && x($person, 'xchan_pubkey')) { $key = $person['xchan_pubkey']; } else { logger('diaspora_message: unable to find author details'); return; } if (!rsa_verify($author_signed_data, $author_signature, $key, 'sha256')) { logger('diaspora_message: verification failed.'); return; } $r = q("select id from mail where mid = '%s' and channel_id = %d limit 1", dbesc($msg_guid), intval($importer['channel_id'])); if ($r) { logger('diaspora_message: duplicate message already delivered.', LOGGER_DEBUG); return; } $key = get_config('system', 'pubkey'); // $subject is a copy of the already obscured subject from the conversation structure if ($body) { $body = str_rot47(base64url_encode($body)); } q("insert into mail ( `account_id`, `channel_id`, `convid`, `conv_guid`, `from_xchan`,`to_xchan`,`title`,`body`,`mail_obscured`,`mid`,`parent_mid`,`created`, mail_isreply) values ( %d, %d, %d, '%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', %d)", intval($importer['channel_account_id']), intval($importer['channel_id']), intval($conversation['id']), dbesc($conversation['guid']), dbesc($person['xchan_hash']), dbesc($importer['xchan_hash']), dbesc($subject), dbesc($body), intval(1), dbesc($msg_guid), dbesc($parent_ptr), dbesc($msg_created_at), intval(1)); q("update conv set updated = '%s' where id = %d", dbesc(datetime_convert()), intval($conversation['id'])); $z = q("select * from mail where mid = '%s' and channel_id = %d limit 1", dbesc($msg_guid), intval($importer['channel_id'])); \Zotlabs\Lib\Enotify::submit(array('from_xchan' => $person['xchan_hash'], 'to_xchan' => $importer['channel_hash'], 'type' => NOTIFY_MAIL, 'item' => $z[0], 'verb' => ACTIVITY_POST, 'otype' => 'mail')); return; }