Merge branch '2.8RC'

This commit is contained in:
Mario
2017-10-25 13:29:19 +02:00
310 changed files with 20161 additions and 7135 deletions

View File

@@ -7,6 +7,8 @@
function api_login(&$a){
$record = null;
$remote_auth = false;
$sigblock = null;
require_once('include/oauth.php');
@@ -33,21 +35,66 @@ function api_login(&$a){
// workarounds for HTTP-auth in CGI mode
if(x($_SERVER,'REDIRECT_REMOTE_USER')) {
$userpass = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"],6)) ;
if(strlen($userpass)) {
list($name, $password) = explode(':', $userpass);
$_SERVER['PHP_AUTH_USER'] = $name;
$_SERVER['PHP_AUTH_PW'] = $password;
}
}
foreach([ 'REDIRECT_REMOTE_USER', 'HTTP_AUTHORIZATION' ] as $head) {
if(x($_SERVER,'HTTP_AUTHORIZATION')) {
$userpass = base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"],6)) ;
if(strlen($userpass)) {
list($name, $password) = explode(':', $userpass);
$_SERVER['PHP_AUTH_USER'] = $name;
$_SERVER['PHP_AUTH_PW'] = $password;
/* Basic authentication */
if(array_key_exists($head,$_SERVER) && substr(trim($_SERVER[$head]),0,5) === 'Basic') {
$userpass = @base64_decode(substr(trim($_SERVER[$head]),6)) ;
if(strlen($userpass)) {
list($name, $password) = explode(':', $userpass);
$_SERVER['PHP_AUTH_USER'] = $name;
$_SERVER['PHP_AUTH_PW'] = $password;
}
break;
}
/* Signature authentication */
if(array_key_exists($head,$_SERVER) && substr(trim($_SERVER[$head]),0,9) === 'Signature') {
if($head !== 'HTTP_AUTHORIZATION') {
$_SERVER['HTTP_AUTHORIZATION'] = $_SERVER[$head];
continue;
}
$sigblock = \Zotlabs\Web\HTTPSig::parse_sigheader($_SERVER[$head]);
if($sigblock) {
$keyId = $sigblock['keyId'];
if($keyId) {
$r = q("select * from hubloc where hubloc_addr = '%s' limit 1",
dbesc($keyId)
);
if($r) {
$c = channelx_by_hash($r[0]['hubloc_hash']);
if($c) {
$a = q("select * from account where account_id = %d limit 1",
intval($c['channel_account_id'])
);
if($a) {
$record = [ 'channel' => $c, 'account' => $a[0] ];
$channel_login = $c['channel_id'];
}
else {
continue;
}
}
else {
continue;
}
}
else {
continue;
}
if($record) {
$verified = \Zotlabs\Web\HTTPSig::verify('',$record['channel']['channel_pubkey']);
if(! ($verified && $verified['header_signed'] && $verified['header_valid'])) {
$record = null;
}
break;
}
}
}
}
}
@@ -64,8 +111,6 @@ function api_login(&$a){
}
}
if($record['account']) {
authenticate_success($record['account']);
@@ -85,8 +130,8 @@ function api_login(&$a){
}
function retry_basic_auth() {
header('WWW-Authenticate: Basic realm="Hubzilla"');
function retry_basic_auth($method = 'Basic') {
header('WWW-Authenticate: ' . $method . ' realm="Hubzilla"');
header('HTTP/1.0 401 Unauthorized');
echo('This api requires login');
killme();

View File

@@ -423,6 +423,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
$hash = (($arr && $arr['hash']) ? $arr['hash'] : null);
$upload_path = (($arr && $arr['directory']) ? $arr['directory'] : '');
$visible = (($arr && $arr['visible']) ? $arr['visible'] : '');
$notify = (($arr && $arr['notify']) ? $arr['notify'] : '');
$observer = array();
@@ -459,6 +460,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
// By default remove $src when finished
$remove_when_processed = true;
$import_replace = false;
if($options === 'import') {
$src = $arr['src'];
@@ -475,6 +477,9 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
if($arr['preserve_original'])
$remove_when_processed = false;
if($arr['replace'])
$import_replace = true;
// if importing a directory, just do it now and go home - we're done.
if(array_key_exists('is_dir',$arr) && intval($arr['is_dir'])) {
@@ -616,8 +621,10 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
dbesc($folder_hash)
);
if($r) {
$overwrite = get_pconfig($channel_id,'system','overwrite_dup_files');
$overwrite = (($import_replace || get_pconfig($channel_id,'system','overwrite_dup_files')) ? true : false);
if(($overwrite) || ($options === 'import')) {
if(! array_key_exists('edited',$arr))
$arr['edited'] = datetime_convert();
$options = 'replace';
$existing_id = $x[0]['id'];
$existing_size = intval($x[0]['filesize']);
@@ -709,7 +716,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
$os_relpath = ltrim($os_relpath,'/');
$os_path = $os_relpath;
$display_path = $pathname . '/' . $filename;
$display_path = ltrim($pathname . '/' . $filename,'/');
if($src)
@file_put_contents($os_basepath . $os_relpath,@file_get_contents($src));
@@ -886,6 +893,12 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
build_sync_packet($channel['channel_id'],array('file' => array($sync)));
}
if($notify) {
//$cloudPath = z_root() . '/cloud/' . $channel['channel_address'] . '/' . $r['0']['display_path'];
//$object = get_file_activity_object($channel['channel_id'], $r['0']['hash'], $cloudPath);
//file_activity($channel['channel_id'], $object, $r['0']['allow_cid'], $r['0']['allow_gid'], $r['0']['deny_cid'], $r['0']['deny_gid'], 'post', $notify);
}
return $ret;
}
@@ -1299,8 +1312,8 @@ function attach_delete($channel_id, $resource, $is_photo = 0) {
return;
}
$cloudpath = get_parent_cloudpath($channel_id, $channel_address, $resource);
$object = get_file_activity_object($channel_id, $resource, $cloudpath);
$url = get_cloudpath($channel_id, $channel_address, $resource);
$object = get_file_activity_object($channel_id, $resource, $url);
// If resource is a directory delete everything in the directory recursive
if(intval($r[0]['is_dir'])) {
@@ -1441,7 +1454,7 @@ function get_cloudpath($arr) {
* @param string $attachHash
* @return string with the full folder path
*/
function get_parent_cloudpath($channel_id, $channel_name, $attachHash) {
function get_cloud_url($channel_id, $channel_name, $attachHash) {
$parentFullPath = '';
// build directory tree
$parentHash = $attachHash;
@@ -1453,9 +1466,9 @@ function get_parent_cloudpath($channel_id, $channel_name, $attachHash) {
}
} while ($parentHash);
$parentFullPath = z_root() . '/cloud/' . $channel_name . '/' . $parentFullPath;
$url = z_root() . '/cloud/' . $channel_name . '/' . $parentFullPath . find_filename_by_hash($channel_id, $attachHash);
return $parentFullPath;
return $url;
}
/**
@@ -1719,14 +1732,14 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid,
* @param string $hash
* @param string $cloudpath
*/
function get_file_activity_object($channel_id, $hash, $cloudpath) {
function get_file_activity_object($channel_id, $hash, $url) {
$x = q("SELECT creator, filename, filetype, filesize, revision, folder, os_storage, is_photo, is_dir, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid FROM attach WHERE uid = %d AND hash = '%s' LIMIT 1",
intval($channel_id),
dbesc($hash)
);
$url = rawurlencode($cloudpath . $x[0]['filename']);
$url = rawurlencode($url);
$links = array();
$links[] = array(

View File

@@ -83,7 +83,7 @@ function account_verify_password($login, $pass) {
if(($email_verify) && ($register_policy == REGISTER_OPEN) && ($account['account_flags'] & ACCOUNT_UNVERIFIED)) {
logger('email verification required for ' . $login);
return null;
return ( [ 'reason' => 'unvalidated' ] );
}
if(($account['account_flags'] == ACCOUNT_OK)
@@ -259,7 +259,10 @@ else {
}
else {
$verify = account_verify_password($_POST['username'], $_POST['password']);
if($verify) {
if($verify && array_key_exists('reason',$verify) && $verify['reason'] === 'unvalidated') {
notice( t('Email validation is incomplete. Please check your email.'));
}
elseif($verify) {
$atoken = $verify['xchan'];
$channel = $verify['channel'];
$account = App::$account = $verify['account'];

View File

@@ -327,11 +327,16 @@ function bb_ShareAttributes($match) {
if ($avatar != "")
$headline .= '<a href="' . zid($profile) . '" ><img src="' . $avatar . '" alt="' . $author . '" height="32" width="32" /></a>';
if(strpos($link,'/cards/'))
$type = t('card');
else
$type = t('post');
// Bob Smith wrote the following post 2 hours ago
$fmt = sprintf( t('%1$s wrote the following %2$s %3$s'),
'<a href="' . zid($profile) . '" >' . $author . '</a>',
'<a href="' . zid($link) . '" >' . t('post') . '</a>',
'<a href="' . zid($link) . '" >' . $type . '</a>',
$reldate
);
@@ -1255,6 +1260,9 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false)
$Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_unspacefy_and_trim', $Text);
}
// replace escaped links in code= blocks
$Text = str_replace('%eY9-!','http', $Text);
$Text = preg_replace('/\[\&amp\;([#a-z0-9]+)\;\]/', '&$1;', $Text);
// fix any escaped ampersands that may have been converted into links

View File

@@ -7,6 +7,7 @@ require_once('include/zot.php');
require_once('include/crypto.php');
require_once('include/menu.php');
require_once('include/perm_upgrade.php');
require_once('include/photo/photo_driver.php');
/**
* @brief Called when creating a new channel.
@@ -52,7 +53,7 @@ function identity_check_service_class($account_id) {
*
* This action is pluggable.
* We're currently only checking for an empty name or one that exceeds our
* storage limit (255 chars). 255 chars is probably going to create a mess on
* storage limit (191 chars). 191 chars is probably going to create a mess on
* some pages.
* Plugins can set additional policies such as full name requirements, character
* sets, multi-byte length, etc.
@@ -67,7 +68,7 @@ function validate_channelname($name) {
if (! $name)
return t('Empty name');
if (strlen($name) > 255)
if (mb_strlen($name) > 191)
return t('Name too long');
$arr = ['name' => $name];
@@ -273,6 +274,19 @@ function create_identity($arr) {
return $ret;
}
$a = q("select * from account where account_id = %d",
intval($arr['account_id'])
);
$photo_type = null;
$z = [ 'account' => $a[0], 'channel' => $r[0], 'photo_url' => '' ];
call_hooks('create_channel_photo',$z);
if($z['photo_url']) {
$photo_type = import_channel_photo_from_url($z['photo_url'],$arr['account_id'],$r[0]['channel_id']);
}
if($role_permissions && array_key_exists('limits',$role_permissions))
$perm_limits = $role_permissions['limits'];
else
@@ -318,6 +332,7 @@ function create_identity($arr) {
'xchan_guid' => $guid,
'xchan_guid_sig' => $sig,
'xchan_pubkey' => $key['pubkey'],
'xchan_photo_mimetype' => (($photo_type) ? $photo_type : 'image/png'),
'xchan_photo_l' => z_root() . "/photo/profile/l/{$newuid}",
'xchan_photo_m' => z_root() . "/photo/profile/m/{$newuid}",
'xchan_photo_s' => z_root() . "/photo/profile/s/{$newuid}",
@@ -454,6 +469,194 @@ function create_identity($arr) {
return $ret;
}
function change_channel_keys($channel) {
$ret = array('success' => false);
$stored = [];
$key = new_keypair(4096);
$sig = base64url_encode(rsa_sign($channel['channel_guid'],$key['prvkey']));
$hash = make_xchan_hash($channel['channel_guid'],$sig);
$stored['old_guid'] = $channel['channel_guid'];
$stored['old_guid_sig'] = $channel['channel_guid_sig'];
$stored['old_key'] = $channel['channel_pubkey'];
$stored['old_hash'] = $channel['channel_hash'];
$stored['new_key'] = $key['pubkey'];
$stored['new_sig'] = base64url_encode(rsa_sign($key['pubkey'],$channel['channel_prvkey']));
// Save this info for the notifier to collect
set_pconfig($channel['channel_id'],'system','keychange',$stored);
$r = q("update channel set channel_prvkey = '%s', channel_pubkey = '%s', channel_guid_sig = '%s', channel_hash = '%s' where channel_id = %d",
dbesc($key['prvkey']),
dbesc($key['pubkey']),
dbesc($sig),
dbesc($hash),
intval($channel['channel_id'])
);
if(! $r) {
return $ret;
}
$r = q("select * from channel where channel_id = %d",
intval($channel['channel_id'])
);
if(! $r) {
$ret['message'] = t('Unable to retrieve modified identity');
return $ret;
}
$modified = $r[0];
$h = q("select * from hubloc where hubloc_hash = '%s' and hubloc_url = '%s' ",
dbesc($stored['old_hash']),
dbesc(z_root())
);
if($h) {
foreach($h as $hv) {
$hv['hubloc_guid_sig'] = $sig;
$hv['hubloc_hash'] = $hash;
$hv['hubloc_url_sig'] = base64url_encode(rsa_sign(z_root(),$modifed['channel_prvkey']));
hubloc_store_lowlevel($hv);
}
}
$x = q("select * from xchan where xchan_hash = '%s' ",
dbesc($stored['old_hash'])
);
$check = q("select * from xchan where xchan_hash = '%s'",
dbesc($hash)
);
if(($x) && (! $check)) {
$oldxchan = $x[0];
foreach($x as $xv) {
$xv['xchan_guid_sig'] = $sig;
$xv['xchan_hash'] = $hash;
$xv['xchan_pubkey'] = $key['pubkey'];
xchan_store_lowlevel($xv);
$newxchan = $xv;
}
}
build_sync_packet($channel['channel_id'], [ 'keychange' => $stored ]);
$a = q("select * from abook where abook_xchan = '%s' and abook_self = 1",
dbesc($stored['old_hash'])
);
if($a) {
q("update abook set abook_xchan = '%s' where abook_id = %d",
dbesc($hash),
intval($a[0]['abook_id'])
);
}
xchan_change_key($oldxchan,$newxchan,$stored);
Zotlabs\Daemon\Master::Summon(array('Notifier', 'keychange', $channel['channel_id']));
$ret['success'] = true;
return $ret;
}
function channel_change_address($channel,$new_address) {
$ret = array('success' => false);
$old_address = $channel['channel_address'];
if($new_address === 'sys') {
$ret['message'] = t('Reserved nickname. Please choose another.');
return $ret;
}
if(check_webbie(array($new_address)) !== $new_address) {
$ret['message'] = t('Nickname has unsupported characters or is already being used on this site.');
return $ret;
}
$r = q("update channel set channel_address = '%s' where channel_id = %d",
dbesc($new_address),
intval($channel['channel_id'])
);
if(! $r) {
return $ret;
}
$r = q("select * from channel where channel_id = %d",
intval($channel['channel_id'])
);
if(! $r) {
$ret['message'] = t('Unable to retrieve modified identity');
return $ret;
}
$r = q("update xchan set xchan_addr = '%s' where xchan_hash = '%s'",
dbesc($new_address . '@' . App::get_hostname()),
dbesc($channel['channel_hash'])
);
$h = q("select * from hubloc where hubloc_hash = '%s' and hubloc_url = '%s' ",
dbesc($channel['channel_hash']),
dbesc(z_root())
);
if($h) {
foreach($h as $hv) {
if($hv['hubloc_primary']) {
q("update hubloc set hubloc_primary = 0 where hubloc_id = %d",
intval($hv['hubloc_id'])
);
}
q("update hubloc set hubloc_deleted = 1 where hubloc_id = %d",
intval($hv['hubloc_id'])
);
unset($hv['hubloc_id']);
$hv['hubloc_addr'] = $new_address . '@' . App::get_hostname();
hubloc_store_lowlevel($hv);
}
}
// fix apps which were stored with the actual name rather than a macro
$r = q("select * from app where app_channel = %d and app_system = 1",
intval($channel['channel_id'])
);
if($r) {
foreach($r as $rv) {
$replace = preg_replace('/([\=\/])(' . $old_address . ')($|[\%\/])/ism','$1' . $new_address . '$3',$rv['app_url']);
if($replace != $rv['app_url']) {
q("update app set app_url = '%s' where id = %d",
dbesc($replace),
intval($rv['id'])
);
}
}
}
Zotlabs\Daemon\Master::Summon(array('Notifier', 'refresh_all', $channel['channel_id']));
$ret['success'] = true;
return $ret;
}
/**
* @brief Set default channel to be used on login.
*
@@ -1170,7 +1373,6 @@ function profile_sidebar($profile, $block = 0, $show_connect = true, $zcard = fa
? trim(substr($profile['channel_name'],0,strpos($profile['channel_name'],' '))) : $profile['channel_name']);
$lastname = (($firstname === $profile['channel_name']) ? '' : trim(substr($profile['channel_name'],strlen($firstname))));
// @fixme move this to the diaspora plugin itself
$contact_block = contact_block();
@@ -1428,13 +1630,15 @@ function get_my_address() {
function zid_init() {
$tmp_str = get_my_address();
if(validate_email($tmp_str)) {
Zotlabs\Daemon\Master::Summon(array('Gprobe',bin2hex($tmp_str)));
$arr = array('zid' => $tmp_str, 'url' => App::$cmd);
call_hooks('zid_init',$arr);
if(! local_channel()) {
$r = q("select * from hubloc where hubloc_addr = '%s' order by hubloc_connected desc limit 1",
dbesc($tmp_str)
);
if(! $r) {
Zotlabs\Daemon\Master::Summon(array('Gprobe',bin2hex($tmp_str)));
}
if($r && remote_channel() && remote_channel() === $r[0]['hubloc_hash'])
return;
logger('zid_init: not authenticated. Invoking reverse magic-auth for ' . $tmp_str);
@@ -1443,7 +1647,7 @@ function zid_init() {
$query = str_replace(array('?zid=','&zid='),array('?rzid=','&rzid='),$query);
$dest = '/' . urlencode($query);
if($r && ($r[0]['hubloc_url'] != z_root()) && (! strstr($dest,'/magic')) && (! strstr($dest,'/rmagic'))) {
goaway($r[0]['hubloc_url'] . '/magic' . '?f=&rev=1&dest=' . z_root() . $dest);
goaway($r[0]['hubloc_url'] . '/magic' . '?f=&rev=1&owa=1&dest=' . z_root() . $dest);
}
else
logger('zid_init: no hubloc found.');
@@ -2104,7 +2308,7 @@ function profile_store_lowlevel($arr) {
// It is the caller's responsibility to confirm the requestor's intent and
// authorisation to do this.
function account_remove($account_id,$local = true,$unset_session=true) {
function account_remove($account_id,$local = true,$unset_session = true) {
logger('account_remove: ' . $account_id);
@@ -2149,13 +2353,12 @@ function account_remove($account_id,$local = true,$unset_session=true) {
if ($unset_session) {
unset($_SESSION['authenticated']);
unset($_SESSION['uid']);
notice( sprintf(t("User '%s' deleted"),$account_email) . EOL);
App::$session->nuke();
notice( sprintf(t('Account \'%s\' deleted'),$account_email) . EOL);
goaway(z_root());
}
return $r;
return $r;
}
/**

View File

@@ -126,3 +126,19 @@ function set_iconfig(&$item, $family, $key, $value, $sharing = false) {
function del_iconfig(&$item, $family, $key) {
return Zlib\IConfig::Delete($item, $family, $key);
}
function load_sconfig($server_id) {
Zlib\SConfig::Load($server_id);
}
function get_sconfig($server_id, $family, $key, $default = false) {
return Zlib\SConfig::Get($server_id, $family, $key, $default);
}
function set_sconfig($server_id, $family, $key, $value) {
return Zlib\SConfig::Set($server_id, $family, $key, $value);
}
function del_sconfig($server_id, $family, $key) {
return Zlib\SConfig::Delete($server_id, $family, $key);
}

View File

@@ -115,7 +115,7 @@ function vcard_from_xchan($xchan, $observer = null, $mode = '') {
App::$profile_uid = $xchan['channel_id'];
$url = (($observer)
? z_root() . '/magic?f=&dest=' . $xchan['xchan_url'] . '&addr=' . $xchan['xchan_addr']
? z_root() . '/magic?f=&owa=1&dest=' . $xchan['xchan_url'] . '&addr=' . $xchan['xchan_addr']
: $xchan['xchan_url']
);
@@ -369,7 +369,8 @@ function contact_remove($channel_id, $abook_id) {
return false;
$r = q("select * from item where author_xchan = '%s' and uid = %d",
$r = q("select * from item where (owner_xchan = '%s' or author_xchan = '%s') and uid = %d",
dbesc($abook['abook_xchan']),
dbesc($abook['abook_xchan']),
intval($channel_id)
);
@@ -629,13 +630,20 @@ function get_vcard_array($vc,$id) {
if($vc->ADR) {
foreach($vc->ADR as $adr) {
$type = (($adr['TYPE']) ? vcard_translate_type((string)$adr['TYPE']) : '');
$adrs[] = [
$entry = [
'type' => $type,
'address' => $adr->getParts()
];
$last_entry = end($adrs);
if($last_entry && is_array($adrs[$last_entry]['address']))
array_walk($adrs[$last_entry]['address'],'array_escape_tags');
if(is_array($entry['address'])) {
array_walk($entry['address'],'array_escape_tags');
}
else {
$entry['address'] = (string) escape_tags($entry['address']);
}
$adrs[] = $entry;
}
}

View File

@@ -65,6 +65,10 @@ function categories_widget($baseurl,$selected = '') {
if(! feature_enabled(App::$profile['profile_uid'],'categories'))
return '';
require_once('include/security.php');
$sql_extra = item_permissions_sql(App::$profile['profile_uid']);
$item_normal = item_normal();
$terms = array();
@@ -77,6 +81,7 @@ function categories_widget($baseurl,$selected = '') {
and item.owner_xchan = '%s'
and item.item_wall = 1
$item_normal
$sql_extra
order by term.term asc",
intval(App::$profile['profile_uid']),
intval(TERM_CATEGORY),
@@ -100,7 +105,53 @@ function categories_widget($baseurl,$selected = '') {
return '';
}
function common_friends_visitor_widget($profile_uid) {
function cardcategories_widget($baseurl,$selected = '') {
if(! feature_enabled(App::$profile['profile_uid'],'categories'))
return '';
$sql_extra = item_permissions_sql(App::$profile['profile_uid']);
$item_normal = "and item.item_hidden = 0 and item.item_type = 6 and item.item_deleted = 0
and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0
and item.item_blocked = 0 ";
$terms = array();
$r = q("select distinct(term.term)
from term join item on term.oid = item.id
where item.uid = %d
and term.uid = item.uid
and term.ttype = %d
and term.otype = %d
and item.owner_xchan = '%s'
$item_normal
$sql_extra
order by term.term asc",
intval(App::$profile['profile_uid']),
intval(TERM_CATEGORY),
intval(TERM_OBJ_POST),
dbesc(App::$profile['channel_hash'])
);
if($r && count($r)) {
foreach($r as $rr)
$terms[] = array('name' => $rr['term'], 'selected' => (($selected == $rr['term']) ? 'selected' : ''));
return replace_macros(get_markup_template('categories_widget.tpl'),array(
'$title' => t('Categories'),
'$desc' => '',
'$sel_all' => (($selected == '') ? 'selected' : ''),
'$all' => t('Everything'),
'$terms' => $terms,
'$base' => $baseurl,
));
}
return '';
}
function common_friends_visitor_widget($profile_uid,$cnt = 25) {
if(local_channel() == $profile_uid)
return;
@@ -113,19 +164,20 @@ function common_friends_visitor_widget($profile_uid) {
require_once('include/socgraph.php');
$t = count_common_friends($profile_uid,$observer_hash);
if(! $t)
return;
$r = common_friends($profile_uid,$observer_hash,0,5,true);
$r = common_friends($profile_uid,$observer_hash,0,$cnt,true);
return replace_macros(get_markup_template('remote_friends_common.tpl'), array(
'$desc' => sprintf( tt("%d connection in common", "%d connections in common", $t), $t),
'$base' => z_root(),
'$uid' => $profile_uid,
'$cid' => $observer,
'$linkmore' => (($t > 5) ? 'true' : ''),
'$more' => t('show more'),
'$items' => $r
'$desc' => t('Common Connections'),
'$base' => z_root(),
'$uid' => $profile_uid,
'$cid' => $observer,
'$linkmore' => (($t > $cnt) ? 'true' : ''),
'$more' => sprintf( t('View all %d common connections'), $t),
'$items' => $r
));
};

View File

@@ -295,7 +295,7 @@ function localize_item(&$item){
}
$plink = '[zrl=' . $obj['plink'] . ']' . $post_type . '[/zrl]';
$parsedobj = parse_xml_string($xmlhead.$item['obj']);
// $parsedobj = parse_xml_string($xmlhead.$item['obj']);
$tag = sprintf('#[zrl=%s]%s[/zrl]', $parsedobj->id, $parsedobj->content);
$item['body'] = sprintf( t('%1$s tagged %2$s\'s %3$s with %4$s'), $author, $objauthor, $plink, $tag );
@@ -312,7 +312,7 @@ function localize_item(&$item){
$xmlhead="<"."?xml version='1.0' encoding='UTF-8' ?".">";
$obj = parse_xml_string($xmlhead.$item['obj']);
// $obj = parse_xml_string($xmlhead.$item['obj']);
if(strlen($obj->id)) {
$r = q("select * from item where mid = '%s' and uid = %d limit 1",
dbesc($obj->id),
@@ -464,9 +464,11 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
$profile_owner = 0;
$page_writeable = false;
$live_update_div = '';
$jsreload = '';
$preview = (($page_mode === 'preview') ? true : false);
$previewing = (($preview) ? ' preview ' : '');
$preview_lbl = t('This is an unsaved preview');
if ($mode === 'network') {
@@ -516,6 +518,16 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
}
}
elseif ($mode === 'cards') {
$profile_owner = App::$profile['profile_uid'];
$page_writeable = ($profile_owner == local_channel());
$live_update_div = '<div id="live-cards"></div>' . "\r\n"
. "<script> var profile_uid = " . App::$profile['profile_uid']
. "; var netargs = '?f='; var profile_page = " . App::$pager['page'] . "; </script>\r\n";
$jsreload = $_SESSION['return_url'];
}
elseif ($mode === 'display') {
$profile_owner = local_channel();
$page_writeable = false;
@@ -549,6 +561,19 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
if (! feature_enabled($profile_owner,'multi_delete'))
$page_dropping = false;
$uploading = true;
if($profile_owner > 0) {
$owner_channel = channelx_by_n($profile_owner);
if($owner_channel['channel_allow_cid'] || $owner_channel['channel_allow_gid']
|| $owner_channel['channel_deny_cid'] || $owner_channel['channel_deny_gid']) {
$uploading = false;
}
}
else {
$uploading = false;
}
$channel = App::get_channel();
$observer = App::get_observer();
@@ -678,12 +703,19 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0)
$is_new = true;
$conv_link_mid = (($mode == 'moderate') ? $item['parent_mid'] : $item['mid']);
$conv_link = (($item['item_type'] == ITEM_TYPE_CARD) ? $item['plink'] : z_root() . '/display/' . gen_link_id($conv_link_mid));
$tmp_item = array(
'template' => $tpl,
'toplevel' => 'toplevel_item',
'item_type' => intval($item['item_type']),
'mode' => $mode,
'approve' => t('Approve'),
'delete' => t('Delete'),
'preview_lbl' => $preview_lbl,
'id' => (($preview) ? 'P0' : $item['item_id']),
'linktitle' => sprintf( t('View %s\'s profile @ %s'), $profile_name, $profile_url),
'profile_url' => $profile_link,
@@ -731,7 +763,7 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
'like' => '',
'dislike' => '',
'comment' => '',
'conv' => (($preview) ? '' : array('href'=> z_root() . '/display/' . gen_link_id($item['mid']), 'title'=> t('View in context'))),
'conv' => (($preview) ? '' : array('href'=> $conv_link, 'title'=> t('View in context'))),
'previewing' => $previewing,
'wait' => t('Please wait'),
'thread_level' => 1,
@@ -751,7 +783,7 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
// Normal View
// logger('conv: items: ' . print_r($items,true));
$conv = new Zotlabs\Lib\ThreadStream($mode, $preview, $prepared_item);
$conv = new Zotlabs\Lib\ThreadStream($mode, $preview, $uploading, $prepared_item);
// In the display mode we don't have a profile owner.
@@ -793,6 +825,10 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
$item_object->set_template('conv_list.tpl');
$item_object->set_display_mode('list');
}
if($page_mode === 'cards') {
$item_object->set_reload($jsreload);
}
}
}
@@ -1290,6 +1326,11 @@ function status_editor($a, $x, $popup = false) {
if(! $cipher)
$cipher = 'aes256';
if(array_key_exists('catsenabled',$x))
$catsenabled = $x['catsenabled'];
else
$catsenabled = ((feature_enabled($x['profile_uid'], 'categories') && (! $webpage)) ? 'categories' : '');
// avoid illegal offset errors
if(! array_key_exists('permissions',$x))
$x['permissions'] = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ];
@@ -1302,10 +1343,14 @@ function status_editor($a, $x, $popup = false) {
call_hooks('jot_networks', $jotnets);
}
$sharebutton = (x($x,'button') ? $x['button'] : t('Share'));
$placeholdtext = (x($x,'content_label') ? $x['content_label'] : $sharebutton);
$o .= replace_macros($tpl, array(
'$return_path' => ((x($x, 'return_path')) ? $x['return_path'] : App::$query_string),
'$action' => z_root() . '/item',
'$share' => (x($x,'button') ? $x['button'] : t('Share')),
'$share' => $sharebutton,
'$placeholdtext' => $placeholdtext,
'$webpage' => $webpage,
'$placeholdpagetitle' => ((x($x,'ptlabel')) ? $x['ptlabel'] : t('Page link name')),
'$pagetitle' => (x($x,'pagetitle') ? $x['pagetitle'] : ''),
@@ -1334,7 +1379,7 @@ function status_editor($a, $x, $popup = false) {
'$clearloc' => $clearloc,
'$title' => ((x($x, 'title')) ? htmlspecialchars($x['title'], ENT_COMPAT,'UTF-8') : ''),
'$placeholdertitle' => ((x($x, 'placeholdertitle')) ? $x['placeholdertitle'] : t('Title (optional)')),
'$catsenabled' => ((feature_enabled($x['profile_uid'], 'categories') && (! $webpage)) ? 'categories' : ''),
'$catsenabled' => $catsenabled,
'$category' => ((x($x, 'category')) ? $x['category'] : ''),
'$placeholdercategory' => t('Categories (optional, comma-separated list)'),
'$permset' => t('Permission settings'),
@@ -1441,6 +1486,8 @@ function conv_sort($arr, $order) {
usort($parents,'sort_thr_created');
elseif(stristr($order,'commented'))
usort($parents,'sort_thr_commented');
elseif(stristr($order,'updated'))
usort($parents,'sort_thr_updated');
elseif(stristr($order,'ascending'))
usort($parents,'sort_thr_created_rev');
@@ -1482,6 +1529,12 @@ function sort_thr_commented($a,$b) {
return strcmp($b['commented'],$a['commented']);
}
function sort_thr_updated($a,$b) {
$indexa = (($a['changed'] > $a['edited']) ? $a['changed'] : $a['edited']);
$indexb = (($b['changed'] > $b['edited']) ? $b['changed'] : $b['edited']);
return strcmp($indexb,$indexa);
}
function find_thread_parent_index($arr,$x) {
foreach($arr as $k => $v)
if($v['id'] == $x['parent'])
@@ -1573,7 +1626,6 @@ function network_tabs() {
$conv_active = '';
$spam_active = '';
$postord_active = '';
$public_active = '';
if(x($_GET,'new')) {
$new_active = 'active';
@@ -1595,16 +1647,11 @@ function network_tabs() {
$spam_active = 'active';
}
if(x($_GET,'fh')) {
$public_active = 'active';
}
if (($new_active == '')
&& ($starred_active == '')
&& ($conv_active == '')
&& ($search_active == '')
&& ($spam_active == '')
&& ($public_active == '')) {
&& ($spam_active == '')) {
$no_active = 'active';
}
@@ -1622,17 +1669,6 @@ function network_tabs() {
// tabs
$tabs = array();
$disable_discover_tab = get_config('system','disable_discover_tab') || get_config('system','disable_discover_tab') === false;
if(! $disable_discover_tab) {
$tabs[] = array(
'label' => t('Discover'),
'url' => z_root() . '/' . $cmd . '?f=&fh=1' ,
'sel' => $public_active,
'title' => t('Imported public streams'),
);
}
$tabs[] = array(
'label' => t('Commented Order'),
'url'=>z_root() . '/' . $cmd . '?f=&order=comment' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : '') . ((x($_GET,'gid')) ? '&gid=' . $_GET['gid'] : ''),
@@ -1820,7 +1856,8 @@ function profile_tabs($a, $is_owner = false, $nickname = null){
require_once('include/menu.php');
$has_bookmarks = menu_list_count(local_channel(),'',MENU_BOOKMARK) + menu_list_count(local_channel(),'',MENU_SYSTEM|MENU_BOOKMARK);
if ($is_owner && $has_bookmarks) {
if($is_owner && $has_bookmarks) {
$tabs[] = array(
'label' => t('Bookmarks'),
'url' => z_root() . '/bookmarks',
@@ -1831,6 +1868,17 @@ function profile_tabs($a, $is_owner = false, $nickname = null){
);
}
if(feature_enabled($uid,'cards')) {
$tabs[] = array(
'label' => t('Cards'),
'url' => z_root() . '/cards/' . $nickname,
'sel' => ((argv(0) == 'cards') ? 'active' : ''),
'title' => t('View Cards'),
'id' => 'cards-tab',
'icon' => 'list'
);
}
if($has_webpages && feature_enabled($uid,'webpages')) {
$tabs[] = array(
'label' => t('Webpages'),
@@ -1841,7 +1889,7 @@ function profile_tabs($a, $is_owner = false, $nickname = null){
'icon' => 'newspaper-o'
);
}
if ($p['view_wiki']) {
if(feature_enabled($uid,'wiki') && (get_account_techlevel($account_id) > 3)) {

View File

@@ -148,6 +148,7 @@ function other_encapsulate($data,$pubkey,$alg) {
// compromised by state actors and evidence is mounting that this has
// already happened.
$result = [ 'encrypted' => true ];
$key = openssl_random_pseudo_bytes(256);
$iv = openssl_random_pseudo_bytes(256);
$result['data'] = base64url_encode($fn($data,$key,$iv),true);
@@ -185,11 +186,24 @@ function crypto_methods() {
}
function signing_methods() {
$r = [ 'sha256' ];
call_hooks('signing_methods',$r);
return $r;
}
function aes_encapsulate($data,$pubkey) {
if(! $pubkey)
logger('aes_encapsulate: no key. data: ' . $data);
$key = openssl_random_pseudo_bytes(32);
$iv = openssl_random_pseudo_bytes(16);
$result = [ 'encrypted' => true ];
$result['data'] = base64url_encode(AES256CBC_encrypt($data,$key,$iv),true);
// log the offending call so we can track it down
if(! openssl_public_encrypt($key,$k,$pubkey)) {

View File

@@ -117,6 +117,15 @@ function get_features($filtered = true) {
feature_level('private_notes',1),
],
[
'cards',
t('Cards'),
t('Create personal planning cards'),
false,
get_config('feature_lock','cards'),
feature_level('cards',1),
],
[
'nav_channel_select',
t('Navigation Channel Select'),

View File

@@ -255,7 +255,7 @@ function get_atom_elements($feed, $item, &$author) {
$author['author_is_feed'] = false;
$rawauthor = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author');
logger('rawauthor: ' . print_r($rawauthor, true));
//logger('rawauthor: ' . print_r($rawauthor, true));
}
else {
@@ -519,7 +519,7 @@ function get_atom_elements($feed, $item, &$author) {
// turn Mastodon content warning into a #nsfw hashtag
if($mastodon && $summary) {
$res['body'] .= "\n\n#ContentWarning\n";
$res['body'] = $summary . "\n\n" . $res['body'] . "\n\n#ContentWarning\n";
}
@@ -811,6 +811,7 @@ function feed_get_reshare(&$res,$item) {
}
$attach = $share['links'];
if($attach) {
foreach($attach as $att) {
if($att['rel'] === 'alternate') {
@@ -845,6 +846,10 @@ function feed_get_reshare(&$res,$item) {
}
}
if((! $body) && ($share['alternate'])) {
$body = $share['alternate'];
}
$res['body'] = "[share author='" . urlencode($share['author']) .
"' profile='" . $share['profile'] .
"' avatar='" . $share['avatar'] .
@@ -895,6 +900,41 @@ function encode_rel_links($links) {
return $o;
}
function process_feed_tombstones($feed,$importer,$contact,$pass) {
$arr_deleted = [];
$del_entries = $feed->get_feed_tags(NAMESPACE_TOMB, 'deleted-entry');
if(is_array($del_entries) && count($del_entries) && $pass != 2) {
foreach($del_entries as $dentry) {
if(isset($dentry['attribs']['']['ref'])) {
$arr_deleted[] = normalise_id($dentry['attribs']['']['ref']);
}
}
}
if($arr_deleted && is_array($contact)) {
foreach($arr_deleted as $mid) {
$r = q("SELECT * from item where mid = '%s' and author_xchan = '%s' and uid = %d limit 1",
dbesc($mid),
dbesc($contact['xchan_hash']),
intval($importer['channel_id'])
);
if($r) {
$item = $r[0];
if(! intval($item['item_deleted'])) {
logger('deleting item ' . $item['id'] . ' mid=' . $item['mid'], LOGGER_DEBUG);
drop_item($item['id'],false);
}
}
}
}
}
/**
* @brief Process atom feed and update anything/everything we might need to update.
*
@@ -950,43 +990,11 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) {
$permalink = $feed->get_permalink();
// Check at the feed level for updated contact name and/or photo
// process any deleted entries
// Check at the feed level for tombstones
$del_entries = $feed->get_feed_tags(NAMESPACE_TOMB, 'deleted-entry');
if(is_array($del_entries) && count($del_entries) && $pass != 2) {
foreach($del_entries as $dentry) {
$deleted = false;
if(isset($dentry['attribs']['']['ref'])) {
$mid = normalise_id($dentry['attribs']['']['ref']);
$deleted = true;
if(isset($dentry['attribs']['']['when'])) {
$when = $dentry['attribs']['']['when'];
$when = datetime_convert('UTC','UTC', $when, 'Y-m-d H:i:s');
}
else
$when = datetime_convert('UTC','UTC','now','Y-m-d H:i:s');
}
process_feed_tombstones($feed,$importer,$contact,$pass);
if($deleted && is_array($contact)) {
$r = q("SELECT * from item where mid = '%s' and author_xchan = '%s' and uid = %d limit 1",
dbesc($mid),
dbesc($contact['xchan_hash']),
intval($importer['channel_id'])
);
if($r) {
$item = $r[0];
if(! intval($item['item_deleted'])) {
logger('deleting item ' . $item['id'] . ' mid=' . $item['mid'], LOGGER_DEBUG);
drop_item($item['id'],false);
}
}
}
}
}
// Now process the feed
@@ -1028,6 +1036,13 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) {
if(! $datarray['mid'])
continue;
$item_parent_mid = q("select parent_mid from item where mid = '%s' and uid = %d limit 1",
dbesc($parent_mid),
intval($importer['channel_id'])
);
// This probably isn't an appropriate default but we're about to change it
// if it's wrong.
@@ -1079,7 +1094,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) {
$datarray['owner_xchan'] = $contact['xchan_hash'];
$r = q("SELECT id, edited FROM item WHERE mid = '%s' AND uid = %d LIMIT 1",
$r = q("SELECT id, edited, author_xchan, item_deleted FROM item WHERE mid = '%s' AND uid = %d LIMIT 1",
dbesc($datarray['mid']),
intval($importer['channel_id'])
);
@@ -1088,6 +1103,15 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) {
// Update content if 'updated' changes
if($r) {
if(activity_match($datarray['verb'],ACTIVITY_DELETE)
&& $datarray['author_xchan'] === $r[0]['author_xchan']) {
if(! intval($r[0]['item_deleted'])) {
logger('deleting item ' . $r[0]['id'] . ' mid=' . $datarray['mid'], LOGGER_DEBUG);
drop_item($r[0]['id'],false);
}
continue;
}
if((x($datarray,'edited') !== false)
&& (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) {
@@ -1123,18 +1147,12 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) {
$datarray['parent_mid'] = $pmid;
}
}
if(! $pmid) {
$x = q("select parent_mid from item where mid = '%s' and uid = %d limit 1",
dbesc($parent_mid),
intval($importer['channel_id'])
);
if($x) {
logger('find_parent: matched in-reply-to: ' . $parent_mid, LOGGER_DEBUG);
$pmid = $x[0]['parent_mid'];
$datarray['parent_mid'] = $pmid;
}
if(($item_parent_mid) && (! $pmid)) {
logger('find_parent: matched in-reply-to: ' . $parent_mid, LOGGER_DEBUG);
$pmid = $item_parent_mid[0]['parent_mid'];
$datarray['parent_mid'] = $pmid;
}
if((! $pmid) && $parent_link !== '') {
$f = feed_conversation_fetch($importer,$contact,$parent_link);
if($f) {
@@ -1156,6 +1174,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) {
);
if($x) {
$item_parent_mid = $x;
$pmid = $x[0]['parent_mid'];
$datarray['parent_mid'] = $pmid;
}
@@ -1237,6 +1256,13 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) {
set_iconfig($datarray,'system','parent_mid',$parent_mid,true);
}
// allow likes of comments
if($item_parent_mid && activity_match($datarray['verb'],ACTVITY_LIKE)) {
$datarray['thr_parent'] = $item_parent_mid[0]['parent_mid'];
}
$datarray['aid'] = $importer['channel_account_id'];
$datarray['uid'] = $importer['channel_id'];
@@ -1330,8 +1356,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) {
}
}
$r = q("SELECT id, edited FROM item WHERE mid = '%s' AND uid = %d LIMIT 1",
$r = q("SELECT id, edited, author_xchan, item_deleted FROM item WHERE mid = '%s' AND uid = %d LIMIT 1",
dbesc($datarray['mid']),
intval($importer['channel_id'])
);
@@ -1339,6 +1364,15 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) {
// Update content if 'updated' changes
if($r) {
if(activity_match($datarray['verb'],ACTIVITY_DELETE)
&& $datarray['author_xchan'] === $r[0]['author_xchan']) {
if(! intval($r[0]['item_deleted'])) {
logger('deleting item ' . $r[0]['id'] . ' mid=' . $datarray['mid'], LOGGER_DEBUG);
drop_item($r[0]['id'],false);
}
continue;
}
if((x($datarray,'edited') !== false)
&& (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) {
@@ -1706,7 +1740,7 @@ function compat_photos_list($s) {
$found = preg_match_all('/\[[zi]mg(.*?)\](.*?)\[/ism',$s,$matches,PREG_SET_ORDER);
if($found) {
foreach($matches as $match) {
foreach($matches as $match) {
$ret[] = [
'href' => $match[2],
'length' => 0,

View File

@@ -17,6 +17,17 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
$my_perms = false;
$is_zot = false;
$protocol = '';
if(substr($url,0,1) === '[') {
$x = strpos($url,']');
if($x) {
$protocol = substr($url,1,$x-1);
$url = substr($url,$x+1);
}
}
$is_http = ((strpos($url,'://') !== false) ? true : false);
if($is_http && substr($url,-1,1) === '/')
@@ -47,13 +58,13 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
}
$arr = array('url' => $url, 'channel' => array());
$arr = array('url' => $url, 'protocol', 'channel' => array());
call_hooks('follow_init', $arr);
if($arr['channel']['success'])
$ret = $arr['channel'];
elseif(! $is_http)
elseif((! $is_http) && ((! $protocol) || (strtolower($protocol) === 'zot')))
$ret = Zotlabs\Zot\Finger::run($url,$channel);
if($ret && is_array($ret) && $ret['success']) {
@@ -118,26 +129,31 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
else {
$xchan_hash = '';
$sql_options = (($protocol) ? " and xchan_network = '" . dbesc($protocol) . "' " : '');
$r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' limit 1",
$r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' $sql_options limit 1",
dbesc($url),
dbesc($url)
);
if(! $r) {
// attempt network auto-discovery
$d = discover_by_webbie($url);
$d = discover_by_webbie($url,$protocol);
if((! $d) && ($is_http)) {
// try RSS discovery
if(get_config('system','feed_contacts')) {
$feeds = get_config('system','feed_contacts');
if(($feeds) && ($protocol === '' || $protocol === 'feed')) {
$d = discover_by_url($url);
}
else {
$result['message'] = t('Protocol disabled.');
$result['message'] = t('Remote channel or protocol unavailable.');
return $result;
}
}

View File

@@ -28,10 +28,23 @@ function get_help_content($tocpath = false) {
}
if($path) {
$title = basename($path);
if(! $tocpath)
\App::$page['title'] = t('Help:') . ' ' . ucwords(str_replace('-',' ',notags($title)));
// Check that there is a "toc" or "sitetoc" located at the specified path.
// If there is not, then there was not a translation of the table of contents
// available and so default back to the English TOC at /doc/toc.{html,bb,md}
// TODO: This is incompatible with the hierarchical TOC construction
// defined in /Zotlabs/Widget/Helpindex.php.
if($tocpath !== false &&
load_doc_file('doc/' . $path . '.md') === '' &&
load_doc_file('doc/' . $path . '.bb') === '' &&
load_doc_file('doc/' . $path . '.html') === ''
) {
$path = $title;
}
$text = load_doc_file('doc/' . $path . '.md');
if(! $text) {
@@ -107,20 +120,55 @@ function preg_callback_help_include($matches) {
}
function determine_help_language() {
require_once('Text/LanguageDetect.php');
$lang_detect = new Text_LanguageDetect();
// Set this mode to recognize language by the short code like "en", "ru", etc.
$lang_detect->setNameMode(2);
// If the language was specified in the URL, override the language preference
// of the browser. Default to English if both of these are absent.
if($lang_detect->languageExists(argv(1))) {
$lang = argv(1);
$from_url = true;
} else {
$lang = \App::$language;
if(! isset($lang))
$lang = 'en';
$from_url = false;
}
return array('language' => $lang, 'from_url' => $from_url);
}
function load_doc_file($s) {
$lang = \App::$language;
if(! isset($lang))
$lang = 'en';
$b = basename($s);
$d = dirname($s);
if($dirname !== '-') {
$c = find_doc_file("$d/$lang/$b");
if($c)
return $c;
$path = 'doc';
// Determine the language and modify the path accordingly
$x = determine_help_language();
$lang = $x['language'];
$url_idx = ($x['from_url'] ? 1 : 0);
// The English translation is at the root of /doc/. Other languages are in
// subfolders named by the language code such as "de", "es", etc.
if($lang !== 'en') {
$path .= '/' . $lang;
}
$b = basename($s);
for($i=1+$url_idx; $i<argc()-1; $i++) {
$path .= '/' . argv($i);
}
$c = find_doc_file($path . '/' . $b);
if($c)
return $c;
// Possibly a translation was requested that has not been translated, so fall
// back to the English version
$path = 'doc';
for($i=1+$url_idx; $i<argc()-1; $i++) {
$path .= '/' . argv($i);
}
$c = find_doc_file($path . '/' . $b);
if($c)
return $c;
// Try one last time to find the file at the explicit path input to the function
$c = find_doc_file($s);
if($c)
return $c;

View File

@@ -87,9 +87,6 @@ function deletenode(&$doc, $node)
function html2bbcode($message)
{
//$file = tempnam("/tmp/", "html");
//file_put_contents($file, $message);
$message = str_replace("\r", "", $message);
$message = str_replace(array(

View File

@@ -603,6 +603,11 @@ function import_items($channel, $items, $sync = false, $relocate = null) {
if(! $item)
continue;
// deprecated
if(array_key_exists('diaspora_meta',$item))
unset($item['diaspora_meta']);
if($relocate && $item['mid'] === $item['parent_mid']) {
item_url_replace($channel,$item,$relocate['url'],z_root(),$relocate['channel_address']);
}
@@ -640,6 +645,12 @@ function import_items($channel, $items, $sync = false, $relocate = null) {
fix_attached_file_permissions($channel,$item['author_xchan'],$item['body'],$item['allow_cid'],$item['allow_gid'],$item['deny_cid'],$item['deny_gid']);
if($sync && $item['item_wall']) {
// deliver singletons if we have any
if($item_result && $item_result['success']) {
Zotlabs\Daemon\Master::Summon( [ 'Notifier','single_activity',$item_result['item_id'] ]);
}
}
}
}
}
@@ -1013,6 +1024,9 @@ function import_mail($channel, $mails, $sync = false) {
$m['aid'] = $channel['channel_account_id'];
$m['uid'] = $channel['channel_id'];
$mail_id = mail_store($m);
if($sync && $mail_id) {
Zotlabs\Daemon\Master::Summon(array('Notifier','single_mail',$mail_id));
}
}
}
}

View File

@@ -177,7 +177,13 @@ function item_normal() {
}
function item_normal_search() {
return " and item.item_hidden = 0 and item.item_type in (0,3) and item.item_deleted = 0
return " and item.item_hidden = 0 and item.item_type in (0,3,6) and item.item_deleted = 0
and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0
and item.item_blocked = 0 ";
}
function item_normal_update() {
return " and item.item_hidden = 0 and item.item_type = 0
and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0
and item.item_blocked = 0 ";
}
@@ -1124,7 +1130,7 @@ function encode_item_xchan($xchan) {
function encode_item_terms($terms,$mirror = false) {
$ret = array();
$allowed_export_terms = array( TERM_UNKNOWN, TERM_HASHTAG, TERM_MENTION, TERM_CATEGORY, TERM_BOOKMARK, TERM_COMMUNITYTAG );
$allowed_export_terms = array( TERM_UNKNOWN, TERM_HASHTAG, TERM_MENTION, TERM_CATEGORY, TERM_BOOKMARK, TERM_COMMUNITYTAG, TERM_FORUM );
if($mirror) {
$allowed_export_terms[] = TERM_PCATEGORY;
@@ -1172,7 +1178,7 @@ function decode_item_meta($meta) {
* @return string
*/
function termtype($t) {
$types = array('unknown','hashtag','mention','category','private_category','file','search','thing','bookmark', 'hierarchy', 'communitytag');
$types = array('unknown','hashtag','mention','category','private_category','file','search','thing','bookmark', 'hierarchy', 'communitytag', 'forum');
return(($types[$t]) ? $types[$t] : 'unknown');
}
@@ -1221,6 +1227,9 @@ function decode_tags($t) {
case 'communitytag':
$tag['ttype'] = TERM_COMMUNITYTAG;
break;
case 'forum':
$tag['ttype'] = TERM_FORUM;
break;
default:
case 'unknown':
$tag['ttype'] = TERM_UNKNOWN;
@@ -1600,7 +1609,6 @@ function item_store($arr, $allow_exec = false, $deliver = true) {
$arr['aid'] = ((x($arr,'aid')) ? intval($arr['aid']) : 0);
$arr['mid'] = ((x($arr,'mid')) ? notags(trim($arr['mid'])) : random_string());
$arr['revision'] = ((x($arr,'revision') && intval($arr['revision']) > 0) ? intval($arr['revision']) : 0);
logger('revision: ' . $arr['revision']);
$arr['author_xchan'] = ((x($arr,'author_xchan')) ? notags(trim($arr['author_xchan'])) : '');
$arr['owner_xchan'] = ((x($arr,'owner_xchan')) ? notags(trim($arr['owner_xchan'])) : '');
@@ -1999,17 +2007,17 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) {
$arr = $translate['item'];
}
if((x($arr,'obj')) && is_array($arr['obj'])) {
if((array_key_exists('obj',$arr)) && is_array($arr['obj'])) {
activity_sanitise($arr['obj']);
$arr['obj'] = json_encode($arr['obj']);
}
if((x($arr,'target')) && is_array($arr['target'])) {
if((array_key_exists('target',$arr)) && is_array($arr['target'])) {
activity_sanitise($arr['target']);
$arr['target'] = json_encode($arr['target']);
}
if((x($arr,'attach')) && is_array($arr['attach'])) {
if((array_key_exists('attach',$arr)) && is_array($arr['attach'])) {
activity_sanitise($arr['attach']);
$arr['attach'] = json_encode($arr['attach']);
}
@@ -2452,7 +2460,7 @@ function tag_deliver($uid, $item_id) {
* Now we've got those out of the way. Let's see if this is a post that's tagged for re-delivery
*/
$terms = get_terms_oftype($item['term'],TERM_MENTION);
$terms = array_merge(get_terms_oftype($item['term'],TERM_MENTION),get_terms_oftype($item['term'],TERM_FORUM));
if($terms)
logger('tag_deliver: post mentions: ' . print_r($terms,true), LOGGER_DATA);
@@ -2487,22 +2495,46 @@ function tag_deliver($uid, $item_id) {
$plustagged = false;
$matches = array();
$pattern = '/@\!?\[zrl\=' . preg_quote($term['url'],'/') . '\]' . preg_quote($term['term'],'/') . '\[\/zrl\]/';
$pattern = '/[\!@]\!?\[zrl\=' . preg_quote($term['url'],'/') . '\]' . preg_quote($term['term'],'/') . '\[\/zrl\]/';
if(preg_match($pattern,$body,$matches))
$tagged = true;
$pattern = '/@\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\+\[\/zrl\]/';
// original red forum tagging sequence @forumname+
// standard forum tagging sequence !forumname
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;
$pluspattern = '/@\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\+\[\/zrl\]/';
$forumpattern = '/\!\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\[\/zrl\]/';
$found = false;
$max_forums = get_config('system','max_tagged_forums');
if(! $max_forums)
$max_forums = 2;
$matched_forums = 0;
$matches = array();
if(preg_match_all($pluspattern,$body,$matches,PREG_SET_ORDER)) {
foreach($matches as $match) {
$matched_forums ++;
if($term['url'] === $match[1] && $term['term'] === $match[2]) {
if($matched_forums <= $max_forums) {
$plustagged = true;
$found = true;
break;
}
logger('forum ' . $term['term'] . ' exceeded max_tagged_forums - ignoring');
}
}
}
if(preg_match_all($forumpattern,$body,$matches,PREG_SET_ORDER)) {
foreach($matches as $match) {
$matched_forums ++;
if($term['url'] === $match[1] && $term['term'] === $match[2]) {
if($matched_forums <= $max_forums) {
$plustagged = true;
$found = true;
break;
}
logger('forum ' . $term['term'] . ' exceeded max_tagged_forums - ignoring');
@@ -2601,7 +2633,8 @@ function tgroup_check($uid,$item) {
if(! $u)
return false;
$terms = get_terms_oftype($item['term'],TERM_MENTION);
$terms = array_merge(get_terms_oftype($item['term'],TERM_MENTION),get_terms_oftype($item['term'],TERM_FORUM));
if($terms)
logger('tgroup_check: post mentions: ' . print_r($terms,true), LOGGER_DATA);
@@ -2632,18 +2665,34 @@ function tgroup_check($uid,$item) {
$body = preg_replace('/\[share(.*?)\[\/share\]/','',$body);
// $pattern = '/@\!?\[zrl\=' . preg_quote($term['url'],'/') . '\]' . preg_quote($term['term'] . '+','/') . '\[\/zrl\]/';
$pattern = '/@\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\+\[\/zrl\]/';
$pluspattern = '/@\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\+\[\/zrl\]/';
$forumpattern = '/\!\!?\[zrl\=([^\]]*?)\]((?:.(?!\[zrl\=))*?)\[\/zrl\]/';
$found = false;
$max_forums = get_config('system','max_tagged_forums');
if(! $max_forums)
$max_forums = 2;
$matched_forums = 0;
$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;
if(preg_match_all($pluspattern,$body,$matches,PREG_SET_ORDER)) {
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(preg_match_all($forumpattern,$body,$matches,PREG_SET_ORDER)) {
foreach($matches as $match) {
$matched_forums ++;
if($term['url'] === $match[1] && $term['term'] === $match[2]) {
@@ -4084,7 +4133,7 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C
//$third = dba_timer();
$items = fetch_post_tags($items,true);
$items = fetch_post_tags($items,false);
//$fourth = dba_timer();
@@ -4112,6 +4161,8 @@ function webpage_to_namespace($webpage) {
$page_type = 'BUILDBLOCK';
elseif($webpage == ITEM_TYPE_PDL)
$page_type = 'PDL';
elseif($webpage == ITEM_TYPE_CARD)
$page_type = 'CARD';
elseif($webpage == ITEM_TYPE_DOC)
$page_type = 'docfile';
else

View File

@@ -6,7 +6,7 @@ require_once('include/security.php');
require_once('include/menu.php');
function nav() {
function nav($template = 'default') {
/**
*
@@ -62,7 +62,6 @@ EOT;
if($banner === false)
$banner = get_config('system','sitename');
//the notifications template is in hdr.tpl
App::$page['header'] .= replace_macros(get_markup_template('hdr.tpl'), array(
//we could additionally use this to display important system notifications e.g. for updates
));
@@ -92,34 +91,77 @@ EOT;
$nav['loginmenu'][] = Array('rmagic',t('Remote authentication'),'',t('Click to authenticate to your home hub'),'rmagic_nav_btn');
}
if(local_channel()) {
$nav['network'] = array('network', t('Activity'), "", t('Network Activity'),'network_nav_btn');
$nav['network']['all'] = [ 'network', t('View your network activity'), '','' ];
$nav['network']['mark'] = array('', t('Mark all activity notifications seen'), '','');
$nav['home'] = array('channel/' . $channel['channel_address'], t('Channel Home'), "", t('Channel home'),'home_nav_btn');
$nav['home']['all'] = [ 'channel/' . $channel['channel_address'], t('View your channel home'), '' , '' ];
$nav['home']['mark'] = array('', t('Mark all channel notifications seen'), '','');
$nav['intros'] = array('connections/ifpending', t('Connections'), "", t('Connections'),'connections_nav_btn');
if(is_site_admin())
$nav['registrations'] = array('admin/accounts', t('Registrations'), "", t('Registrations'),'registrations_nav_btn');
$nav['notifications'] = array('notifications/system', t('Notices'), "", t('Notifications'),'notifications_nav_btn');
$nav['notifications']['all']=array('notifications/system', t('View all notifications'), "", "");
$nav['notifications']['mark'] = array('', t('Mark all system notifications seen'), '','');
$nav['messages'] = array('mail/combined', t('Mail'), "", t('Private mail'),'mail_nav_btn');
$nav['messages']['all']=array('mail/combined', t('View your private messages'), "", "");
$nav['messages']['mark'] = array('', t('Mark all private messages seen'), '','');
$nav['messages']['inbox'] = array('mail/inbox', t('Inbox'), "", t('Inbox'));
$nav['messages']['outbox']= array('mail/outbox', t('Outbox'), "", t('Outbox'));
$nav['messages']['new'] = array('mail/new', t('New Message'), "", t('New Message'));
$nav['all_events'] = array('events', t('Events'), "", t('Event Calendar'),'events_nav_btn');
$nav['all_events']['all']=array('events', t('View events'), "", "");
$nav['all_events']['mark'] = array('', t('Mark all events seen'), '','');
if(! $_SESSION['delegate']) {
$nav['manage'] = array('manage', t('Channel Manager'), "", t('Manage Your Channels'),'manage_nav_btn');
}
$nav['settings'] = array('settings', t('Settings'),"", t('Account/Channel Settings'),'settings_nav_btn');
if($chans && count($chans) > 1 && feature_enabled(local_channel(),'nav_channel_select'))
$nav['channels'] = $chans;
$nav['logout'] = ['logout',t('Logout'), "", t('End this session'),'logout_nav_btn'];
// user menu
$nav['usermenu'][] = ['profile/' . $channel['channel_address'], t('View Profile'), ((\App::$nav_sel['active'] == 'Profile') ? 'active' : ''), t('Your profile page'),'profile_nav_btn'];
$nav['usermenu'][] = ['profile/' . $channel['channel_address'], t('View Profile'), ((\App::$nav_sel['name'] == 'Profile') ? 'active' : ''), t('Your profile page'),'profile_nav_btn'];
if(feature_enabled(local_channel(),'multi_profiles'))
$nav['usermenu'][] = ['profiles', t('Edit Profiles'), ((\App::$nav_sel['active'] == 'Profiles') ? 'active' : '') , t('Manage/Edit profiles'),'profiles_nav_btn'];
$nav['usermenu'][] = ['profiles', t('Edit Profiles'), ((\App::$nav_sel['name'] == 'Profiles') ? 'active' : '') , t('Manage/Edit profiles'),'profiles_nav_btn'];
else
$nav['usermenu'][] = ['profiles/' . $prof[0]['id'], t('Edit Profile'), ((\App::$nav_sel['active'] == 'Profiles') ? 'active' : ''), t('Edit your profile'),'profiles_nav_btn'];
$nav['usermenu'][] = ['profiles/' . $prof[0]['id'], t('Edit Profile'), ((\App::$nav_sel['name'] == 'Profiles') ? 'active' : ''), t('Edit your profile'),'profiles_nav_btn'];
}
else {
if(! get_account_id()) {
$nav['login'] = login(true,'main-login',false,false);
$nav['loginmenu'][] = ['login',t('Login'),'',t('Sign in'),'login_nav_btn'];
App::$page['content'] .= replace_macros(get_markup_template('nav_login.tpl'),
[
'$nav' => $nav,
'userinfo' => $userinfo
]
);
if(App::$module === 'channel') {
$nav['login'] = login(true,'main-login',false,false);
$nav['loginmenu'][] = ['login',t('Login'),'',t('Sign in'),''];
}
else {
$nav['login'] = login(true,'main-login',false,false);
$nav['loginmenu'][] = ['login',t('Login'),'',t('Sign in'),'login_nav_btn'];
App::$page['content'] .= replace_macros(get_markup_template('nav_login.tpl'),
[
'$nav' => $nav,
'userinfo' => $userinfo
]
);
}
}
else
$nav['alogout'] = ['logout',t('Logout'), "", t('End this session'),'logout_nav_btn'];
@@ -169,47 +211,16 @@ EOT;
*/
if(local_channel()) {
$nav['network'] = array('network', t('Grid'), "", t('Your grid'),'network_nav_btn');
$nav['network']['all'] = [ 'network', t('View your network/grid'), '','' ];
$nav['network']['mark'] = array('', t('Mark all grid notifications seen'), '','');
$nav['home'] = array('channel/' . $channel['channel_address'], t('Channel Home'), "", t('Channel home'),'home_nav_btn');
$nav['home']['all'] = [ 'channel/' . $channel['channel_address'], t('View your channel home'), '' , '' ];
$nav['home']['mark'] = array('', t('Mark all channel notifications seen'), '','');
$nav['intros'] = array('connections/ifpending', t('Connections'), "", t('Connections'),'connections_nav_btn');
$nav['notifications'] = array('notifications/system', t('Notices'), "", t('Notifications'),'notifications_nav_btn');
$nav['notifications']['all']=array('notifications/system', t('View all notifications'), "", "");
$nav['notifications']['mark'] = array('', t('Mark all system notifications seen'), '','');
$nav['messages'] = array('mail/combined', t('Mail'), "", t('Private mail'),'mail_nav_btn');
$nav['messages']['all']=array('mail/combined', t('View your private messages'), "", "");
$nav['messages']['mark'] = array('', t('Mark all private messages seen'), '','');
$nav['messages']['inbox'] = array('mail/inbox', t('Inbox'), "", t('Inbox'));
$nav['messages']['outbox']= array('mail/outbox', t('Outbox'), "", t('Outbox'));
$nav['messages']['new'] = array('mail/new', t('New Message'), "", t('New Message'));
$nav['all_events'] = array('events', t('Events'), "", t('Event Calendar'),'events_nav_btn');
$nav['all_events']['all']=array('events', t('View events'), "", "");
$nav['all_events']['mark'] = array('', t('Mark all events seen'), '','');
if(! $_SESSION['delegate']) {
$nav['manage'] = array('manage', t('Channel Manager'), "", t('Manage Your Channels'),'manage_nav_btn');
}
$nav['settings'] = array('settings', t('Settings'),"", t('Account/Channel Settings'),'settings_nav_btn');
}
/**
* Admin page
*/
if (is_site_admin()){
if (is_site_admin()) {
$nav['admin'] = array('admin/', t('Admin'), "", t('Site Setup and Configuration'),'admin_nav_btn');
}
@@ -221,6 +232,17 @@ EOT;
// turned off until somebody discovers this and figures out a good location for it.
$powered_by = '';
if(App::$profile_uid && App::$nav_sel['raw_name']) {
$active_app = q("SELECT app_url FROM app WHERE app_channel = %d AND app_name = '%s' LIMIT 1",
intval(App::$profile_uid),
dbesc(App::$nav_sel['raw_name'])
);
if($active_app) {
$url = $active_app[0]['app_url'];
}
}
//app bin
if($is_owner) {
if(get_pconfig(local_channel(), 'system','initial_import_system_apps') === false) {
@@ -246,16 +268,33 @@ EOT;
$syslist = Zlib\Apps::app_order(local_channel(),$syslist);
foreach($syslist as $app) {
if(\App::$nav_sel['active'] == $app['name'])
if(\App::$nav_sel['name'] == $app['name'])
$app['active'] = true;
if($is_owner)
if($is_owner) {
$nav_apps[] = Zlib\Apps::app_render($app,'nav');
elseif(! $is_owner && strpos($app['requires'], 'local_channel') === false)
if(strpos($app['categories'],'navbar_' . $template)) {
$navbar_apps[] = Zlib\Apps::app_render($app,'navbar');
}
}
elseif(! $is_owner && strpos($app['requires'], 'local_channel') === false) {
$nav_apps[] = Zlib\Apps::app_render($app,'nav');
if(strpos($app['categories'],'navbar_' . $template)) {
$navbar_apps[] = Zlib\Apps::app_render($app,'navbar');
}
}
}
$tpl = get_markup_template('nav.tpl');
$c = theme_include('navbar_' . purify_filename($template) . '.css');
$tpl = get_markup_template('navbar_' . purify_filename($template) . '.tpl');
if($c && $tpl) {
head_add_css('navbar_' . $template . '.css');
}
if(! $tpl) {
$tpl = get_markup_template('navbar_default.tpl');
}
App::$page['nav'] .= replace_macros($tpl, array(
'$baseurl' => z_root(),
@@ -267,15 +306,19 @@ EOT;
'$userinfo' => $x['usermenu'],
'$localuser' => local_channel(),
'$is_owner' => $is_owner,
'$sel' => App::$nav_sel,
'$sel' => App::$nav_sel,
'$powered_by' => $powered_by,
'$help' => t('@name, #tag, ?doc, content'),
'$pleasewait' => t('Please wait...'),
'$nav_apps' => $nav_apps,
'$navbar_apps' => $navbar_apps,
'$channel_menu' => get_config('system','channel_menu'),
'$channel_thumb' => ((App::$profile) ? App::$profile['thumb'] : ''),
'$channel_apps' => $channel_apps,
'$addapps' => t('Add Apps'),
'$orderapps' => t('Arrange Apps'),
'$sysapps_toggle' => t('Toggle System Apps')
'$sysapps_toggle' => t('Toggle System Apps'),
'$url' => (($url) ? $url : App::$cmd)
));
if(x($_SESSION, 'reload_avatar') && $observer) {
@@ -298,7 +341,10 @@ EOT;
*
*/
function nav_set_selected($item){
App::$nav_sel['active'] = $item;
App::$nav_sel['raw_name'] = $item;
$item = ['name' => $item];
Zlib\Apps::translate_system_apps($item);
App::$nav_sel['name'] = $item['name'];
}
@@ -428,6 +474,18 @@ function channel_apps($is_owner = false, $nickname = null) {
];
}
if($p['view_pages'] && feature_enabled($uid,'cards')) {
$tabs[] = [
'label' => t('Cards'),
'url' => z_root() . '/cards/' . $nickname ,
'sel' => ((argv(0) == 'cards') ? 'active' : ''),
'title' => t('View Cards'),
'id' => 'cards-tab',
'icon' => 'list'
];
}
if($has_webpages && feature_enabled($uid,'webpages')) {
$tabs[] = [
'label' => t('Webpages'),
@@ -461,7 +519,7 @@ function channel_apps($is_owner = false, $nickname = null) {
[
'$tabs' => $arr['tabs'],
'$name' => App::$profile['channel_name'],
'$thumb' => App::$profile['thumb']
'$thumb' => App::$profile['thumb'],
]
);
}

View File

@@ -411,7 +411,7 @@ function http_status($val, $msg = '') {
if ($val >= 200 && $val < 300)
$msg = (($msg) ? $msg : 'OK');
logger('' . $val . ' ' . $msg);
logger(\App::$query_string . ':' . $val . ' ' . $msg);
header($_SERVER['SERVER_PROTOCOL'] . ' ' . $val . ' ' . $msg);
}
@@ -1137,13 +1137,13 @@ function discover_by_url($url, $arr = null) {
return true;
}
function discover_by_webbie($webbie) {
function discover_by_webbie($webbie,$protocol = '') {
$result = [];
$network = null;
$webbie = strtolower($webbie);
// $webbie = strtolower($webbie);
$x = webfinger_rfc7033($webbie,true);
if($x && array_key_exists('links',$x) && $x['links']) {
@@ -1153,7 +1153,7 @@ function discover_by_webbie($webbie) {
// If we discover zot - don't search further; grab the info and get out of
// here.
if($link['rel'] === PROTOCOL_ZOT) {
if($link['rel'] === PROTOCOL_ZOT && ((! $protocol) || (strtolower($protocol) === 'zot'))) {
logger('discover_by_webbie: zot found for ' . $webbie, LOGGER_DEBUG);
if(array_key_exists('zot',$x) && $x['zot']['success']) {
$i = import_xchan($x['zot']);
@@ -1174,7 +1174,7 @@ function discover_by_webbie($webbie) {
logger('webfinger: ' . print_r($x,true), LOGGER_DATA, LOG_INFO);
$arr = array('address' => $webbie, 'success' => false, 'webfinger' => $x);
$arr = array('address' => $webbie, 'protocol' => $protocol, 'success' => false, 'webfinger' => $x);
call_hooks('discover_channel_webfinger', $arr);
if($arr['success'])
return true;
@@ -1291,7 +1291,7 @@ function fetch_xrd_links($url) {
return array();
$h = parse_xml_string($xml);
if(! $h)
if($h === false)
return array();
$arr = convert_xml_element_to_array($h);
@@ -1652,9 +1652,17 @@ function check_channelallowed($hash) {
}
function deliverable_singleton($channel_id,$xchan) {
if(array_key_exists('xchan_hash',$xchan))
$xchan_hash = $xchan['xchan_hash'];
elseif(array_key_exists('hubloc_hash',$xchan))
$xchan_hash = $xchan['hubloc_hash'];
else
return true;
$r = q("select abook_instance from abook where abook_channel = %d and abook_xchan = '%s' limit 1",
intval($channel_id),
dbesc($xchan['xchan_hash'])
dbesc($xchan_hash)
);
if($r) {
if(! $r[0]['abook_instance'])
@@ -1689,18 +1697,19 @@ function get_repository_version($branch = 'master') {
function network_to_name($s) {
$nets = array(
NETWORK_DFRN => t('Friendica'),
NETWORK_FRND => t('Friendica'),
NETWORK_OSTATUS => t('OStatus'),
NETWORK_GNUSOCIAL => t('GNU-Social'),
NETWORK_FEED => t('RSS/Atom'),
NETWORK_MAIL => t('Email'),
NETWORK_DIASPORA => t('Diaspora'),
NETWORK_FACEBOOK => t('Facebook'),
NETWORK_ZOT => t('Zot'),
NETWORK_LINKEDIN => t('LinkedIn'),
NETWORK_XMPP => t('XMPP/IM'),
NETWORK_MYSPACE => t('MySpace'),
NETWORK_DFRN => t('Friendica'),
NETWORK_FRND => t('Friendica'),
NETWORK_OSTATUS => t('OStatus'),
NETWORK_GNUSOCIAL => t('GNU-Social'),
NETWORK_FEED => t('RSS/Atom'),
NETWORK_ACTIVITYPUB => t('ActivityPub'),
NETWORK_MAIL => t('Email'),
NETWORK_DIASPORA => t('Diaspora'),
NETWORK_FACEBOOK => t('Facebook'),
NETWORK_ZOT => t('Zot'),
NETWORK_LINKEDIN => t('LinkedIn'),
NETWORK_XMPP => t('XMPP/IM'),
NETWORK_MYSPACE => t('MySpace'),
);
call_hooks('network_to_name', $nets);
@@ -1934,4 +1943,35 @@ function getBestSupportedMimeType($mimeTypes = null, $acceptedTypes = false) {
}
// no mime-type found
return null;
}
function jsonld_document_loader($url) {
// perform caching for jsonld normaliser
require_once('library/jsonld/jsonld.php');
$cachepath = 'store/[data]/ldcache';
if(! is_dir($cachepath))
os_mkdir($cachepath,STORAGE_DEFAULT_PERMISSIONS,true);
$filename = $cachepath . '/' . urlencode($url);
if(file_exists($filename) && filemtime($filename) > time() - (12 * 60 * 60)) {
return json_decode(file_get_contents($filename));
}
$r = jsonld_default_document_loader($url);
if($r) {
file_put_contents($filename,json_encode($r));
return $r;
}
logger('not found');
if(file_exists($filename)) {
return json_decode(file_get_contents($filename));
}
return [];
}

View File

@@ -225,6 +225,17 @@ function oembed_fetch_url($embedurl){
if($j['html']) {
$orig = $j['html'];
$allow_position = (($is_matrix) ? true : false);
// some sites wrap their entire embed in an iframe
// which we will purify away and which we provide anyway.
// So if we see this, grab the frame src url and use that
// as the embed content - which will still need to be purified.
if(preg_match('#\<iframe(.*?)src\=[\'\"](.*?)[\'\"]#',$j['html'],$matches)) {
$x = z_fetch_url($matches[2]);
$j['html'] = $x['body'];
}
$j['html'] = purify_html($j['html'],$allow_position);
if($j['html'] != $orig) {
logger('oembed html was purified. original: ' . $orig . ' purified: ' . $j['html'], LOGGER_DEBUG, LOG_INFO);

View File

@@ -135,6 +135,9 @@ function translate_abook_perms_outbound(&$abook) {
$my_perms = 0;
$their_perms = 0;
if(! $abook)
return;
if(array_key_exists('abconfig',$abook) && is_array($abook['abconfig']) && $abook['abconfig']) {
foreach($abook['abconfig'] as $p) {
if($p['cat'] === 'their_perms') {

View File

@@ -446,7 +446,7 @@ abstract class photo_driver {
*/
function guess_image_type($filename, $headers = '') {
logger('Photo: guess_image_type: '.$filename . ($headers?' from curl headers':''), LOGGER_DEBUG);
// logger('Photo: guess_image_type: '.$filename . ($headers?' from curl headers':''), LOGGER_DEBUG);
$type = null;
if ($headers) {
@@ -513,11 +513,32 @@ function guess_image_type($filename, $headers = '') {
}
}
logger('Photo: guess_image_type: type = ' . $type, LOGGER_DEBUG);
logger('Photo: guess_image_type: filename = ' . $filename . ' type = ' . $type, LOGGER_DEBUG);
return $type;
}
function delete_thing_photo($url,$ob_hash) {
$hash = basename($url);
$hash = substr($hash,0,strpos($hash,'-'));
// hashes should be 32 bytes.
if((! $ob_hash) || (strlen($hash) < 16))
return;
$r = q("delete from photo where xchan = '%s' and photo_usage = %d and resource_id = '%s'",
dbesc($ob_hash),
intval(PHOTO_THING),
dbesc($hash)
);
}
function import_xchan_photo($photo,$xchan,$thing = false) {
$flags = (($thing) ? PHOTO_THING : PHOTO_XCHAN);
@@ -637,6 +658,37 @@ function import_xchan_photo($photo,$xchan,$thing = false) {
}
function import_channel_photo_from_url($photo,$aid,$uid) {
if($photo) {
$filename = basename($photo);
$result = z_fetch_url($photo,true);
if($result['success']) {
$img_str = $result['body'];
$type = guess_image_type($photo, $result['header']);
$h = explode("\n",$result['header']);
if($h) {
foreach($h as $hl) {
if(stristr($hl,'content-type:')) {
if(! stristr($hl,'image/')) {
$photo_failure = true;
}
}
}
}
}
}
else {
$photo_failure = true;
}
import_channel_photo($img_str,$type,$aid,$uid);
return $type;
}
function import_channel_photo($photo,$type,$aid,$uid) {

View File

@@ -66,7 +66,19 @@ function photo_upload($channel, $observer, $args) {
$os_storage = 0;
if($args['os_syspath'] && $args['getimagesize']) {
$imagedata = @file_get_contents($args['os_syspath']);
if($args['getimagesize'][0] > 1600 || $args['getimagesize'][1] > 1600) {
$imagick_path = get_config('system','imagick_convert_path');
if($imagick_path && @file_exists($imagick_path)) {
$tmp_name = $args['os_syspath'] . '-001';
$newsize = photo_calculate_1600_scale($args['getimagesize']);
exec($imagick_path . ' ' . $args['os_syspath'] . ' -resize ' . $newsize . '^ ' . $tmp_name);
$imagedata = @file_get_contents($tmp_name);
@unlink($tmp_name);
}
}
else {
$imagedata = @file_get_contents($args['os_syspath']);
}
$filename = $args['filename'];
$filesize = strlen($imagedata);
// this is going to be deleted if it exists
@@ -122,7 +134,6 @@ function photo_upload($channel, $observer, $args) {
}
logger('photo_upload: loading the contents of ' . $src , LOGGER_DEBUG);
$imagedata = @file_get_contents($src);
}
@@ -428,6 +439,70 @@ function photo_upload($channel, $observer, $args) {
return $ret;
}
function photo_calculate_1600_scale($arr) {
$max = 1600;
$width = $arr[0];
$height = $arr[1];
$dest_width = $dest_height = 0;
if((! $width)|| (! $height))
return FALSE;
if($width > $max && $height > $max) {
// very tall image (greater than 16:9)
// constrain the width - let the height float.
if((($height * 9) / 16) > $width) {
$dest_width = $max;
$dest_height = intval(( $height * $max ) / $width);
}
// else constrain both dimensions
elseif($width > $height) {
$dest_width = $max;
$dest_height = intval(( $height * $max ) / $width);
}
else {
$dest_width = intval(( $width * $max ) / $height);
$dest_height = $max;
}
}
else {
if( $width > $max ) {
$dest_width = $max;
$dest_height = intval(( $height * $max ) / $width);
}
else {
if( $height > $max ) {
// very tall image (greater than 16:9)
// but width is OK - don't do anything
if((($height * 9) / 16) > $width) {
$dest_width = $width;
$dest_height = $height;
}
else {
$dest_width = intval(( $width * $max ) / $height);
$dest_height = $max;
}
}
else {
$dest_width = $width;
$dest_height = $height;
}
}
}
return $dest_width . 'x' . $dest_height;
}
/**
* @brief Returns a list with all photo albums observer is allowed to see.
*
@@ -595,7 +670,7 @@ function photos_album_exists($channel_id, $observer_hash, $album) {
// partial backward compatibility with Hubzilla < 2.4 when we used the filename only
// (ambiguous which would get chosen if you had two albums of the same name in different directories)
if(!$r) {
if(!$r && ctype_xdigit($album)) {
$r = q("SELECT folder, hash, is_dir, filename, os_path, display_path FROM attach WHERE filename = '%s' AND is_dir = 1 AND uid = %d $sql_extra limit 1",
dbesc(hex2bin($album)),
intval($channel_id)

View File

@@ -114,9 +114,9 @@ function atoken_xchan($atoken) {
'atoken_id' => $atoken['atoken_id'],
'xchan_hash' => substr($c['channel_hash'],0,16) . '.' . $atoken['atoken_name'],
'xchan_name' => $atoken['atoken_name'],
'xchan_addr' => t('guest:') . $atoken['atoken_name'] . '@' . \App::get_hostname(),
'xchan_addr' => 'guest:' . $atoken['atoken_name'] . '@' . \App::get_hostname(),
'xchan_network' => 'unknown',
'xchan_url' => z_root(),
'xchan_url' => z_root() . '/guest/' . substr($c['channel_hash'],0,16) . '.' . $atoken['atoken_name'],
'xchan_hidden' => 1,
'xchan_photo_mimetype' => 'image/jpeg',
'xchan_photo_l' => get_default_profile_photo(300),
@@ -640,7 +640,7 @@ function stream_perms_xchans($perms = NULL ) {
if(local_channel())
$ret[] = get_observer_hash();
$x = q("select uid from pconfig where cat = 'perm_limits' and k = 'view_stream' ");
$x = q("select uid, v from pconfig where cat = 'perm_limits' and k = 'view_stream' ");
if($x) {
$y = [];
foreach($x as $xv) {
@@ -650,6 +650,7 @@ function stream_perms_xchans($perms = NULL ) {
}
if($y) {
$ids = ids_to_querystr($y,'uid');
$r = q("select channel_hash from channel where channel_id in ( $ids ) and ( channel_pageflags & %d ) = 0 and channel_system = 0 and channel_removed = 0 ",
intval(PAGE_ADULT|PAGE_CENSORED)
);

View File

@@ -134,6 +134,8 @@ function format_term_for_display($term) {
$s = '';
if(($term['ttype'] == TERM_HASHTAG) || ($term['ttype'] == TERM_COMMUNITYTAG))
$s .= '#';
elseif($term['ttype'] == TERM_FORUM)
$s .= '!';
elseif($term['ttype'] == TERM_MENTION)
$s .= '@';
else
@@ -199,6 +201,62 @@ function tagadelic($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $re
}
function card_tagadelic($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $restrict = 0, $type = TERM_CATEGORY) {
require_once('include/security.php');
if(! perm_is_allowed($uid,get_observer_hash(),'view_pages'))
return array();
$item_normal = item_normal();
$sql_options = item_permissions_sql($uid);
$count = intval($count);
if($flags) {
if($flags === 'wall')
$sql_options .= " and item_wall = 1 ";
}
if($authors) {
if(! is_array($authors))
$authors = array($authors);
stringify_array_elms($authors,true);
$sql_options .= " and author_xchan in (" . implode(',',$authors) . ") ";
}
if($owner) {
$sql_options .= " and owner_xchan = '" . dbesc($owner) . "' ";
}
// Fetch tags
$r = q("select term, count(term) as total from term left join item on term.oid = item.id
where term.uid = %d and term.ttype = %d
and otype = %d and item_type = %d and item_private = 0
$sql_options $item_normal
group by term order by total desc %s",
intval($uid),
intval($type),
intval(TERM_OBJ_POST),
intval($restrict),
((intval($count)) ? "limit $count" : '')
);
if(! $r)
return array();
return Zotlabs\Text\Tagadelic::calc($r);
}
function dir_tagadelic($count = 0) {
$count = intval($count);
@@ -316,6 +374,26 @@ function catblock($uid,$count = 0,$authors = '',$owner = '', $flags = 0,$restric
return $o;
}
function card_catblock($uid,$count = 0,$authors = '',$owner = '', $flags = 0,$restrict = 0,$type = TERM_CATEGORY) {
$o = '';
$r = card_tagadelic($uid,$count,$authors,$owner,$flags,$restrict,$type);
if($r) {
$c = q("select channel_address from channel where channel_id = %d limit 1",
intval($uid)
);
$o = '<div class="tagblock widget"><h3>' . t('Categories') . '</h3><div class="tags" align="center">';
foreach($r as $rr) {
$o .= '<a href="cards/' . $c[0]['channel_address']. '?f=&cat=' . urlencode($rr[0]).'" class="tag'.$rr[2].'">'.$rr[0].'</a> ' . "\r\n";
}
$o .= '</div></div>';
}
return $o;
}
function dir_tagblock($link,$r) {
$o = '';

View File

@@ -651,7 +651,7 @@ function logger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) {
$stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
$where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': ';
$s = datetime_convert() . ':' . log_priority_str($priority) . ':' . session_id() . ':' . $where . $msg . PHP_EOL;
$s = datetime_convert('UTC','UTC', 'now', ATOM_TIME) . ':' . log_priority_str($priority) . ':' . session_id() . ':' . $where . $msg . PHP_EOL;
$pluginfo = array('filename' => $logfile, 'loglevel' => $level, 'message' => $s,'priority' => $priority, 'logged' => false);
if(! (App::$module == 'setup'))
@@ -679,7 +679,7 @@ function btlogger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) {
if(file_exists(BTLOGGER_DEBUG_FILE) && is_writable(BTLOGGER_DEBUG_FILE)) {
$stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
$where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': ';
$s = datetime_convert() . ':' . log_priority_str($priority) . ':' . session_id() . ':' . $where . $msg . PHP_EOL;
$s = datetime_convert('UTC','UTC', 'now', ATOM_TIME) . ':' . log_priority_str($priority) . ':' . session_id() . ':' . $where . $msg . PHP_EOL;
@file_put_contents(BTLOGGER_DEBUG_FILE, $s, FILE_APPEND);
}
@@ -750,7 +750,7 @@ function dlogger($msg, $level = 0) {
$where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': ';
@file_put_contents($logfile, datetime_convert() . ':' . session_id() . ' ' . $where . $msg . PHP_EOL, FILE_APPEND);
@file_put_contents($logfile, datetime_convert('UTC','UTC', 'now', ATOM_TIME) . ':' . session_id() . ' ' . $where . $msg . PHP_EOL, FILE_APPEND);
}
@@ -761,9 +761,17 @@ function profiler($t1,$t2,$label) {
function activity_match($haystack,$needle) {
if(($haystack === $needle) || ((basename($needle) === $haystack) && strstr($needle,NAMESPACE_ACTIVITY_SCHEMA)))
return true;
if(! is_array($needle))
$needle = [ $needle ];
if($needle) {
foreach($needle as $n) {
if(($haystack === $n) || (strtolower(basename($n)) === strtolower(basename($haystack)))) {
return true;
}
}
}
return false;
}
@@ -794,7 +802,7 @@ function get_tags($s) {
// match any double quoted tags
if(preg_match_all('/([@#]\&quot\;.*?\&quot\;)/',$s,$match)) {
if(preg_match_all('/([@#!]\&quot\;.*?\&quot\;)/',$s,$match)) {
foreach($match[1] as $mtch) {
$ret[] = $mtch;
}
@@ -823,7 +831,7 @@ function get_tags($s) {
// Otherwise pull out single word tags. These can be @nickname, @first_last
// and #hash tags.
if(preg_match_all('/(?<![a-zA-Z0-9=\/\?\;])([@#][^ \x0D\x0A,;:?\[]+)/',$s,$match)) {
if(preg_match_all('/(?<![a-zA-Z0-9=\/\?\;])([@#\!][^ \x0D\x0A,;:?\[]+)/',$s,$match)) {
foreach($match[1] as $mtch) {
if(substr($mtch,-1,1) === '.')
$mtch = substr($mtch,0,-1);
@@ -987,7 +995,7 @@ function chanlink_cid($d) {
function magiclink_url($observer,$myaddr,$url) {
return (($observer)
? z_root() . '/magic?f=&dest=' . $url . '&addr=' . $myaddr
? z_root() . '/magic?f=&owa=1&dest=' . $url . '&addr=' . $myaddr
: $url
);
}
@@ -1389,7 +1397,7 @@ function theme_attachments(&$item) {
if(is_foreigner($item['author_xchan']))
$url = $r['href'];
else
$url = z_root() . '/magic?f=&hash=' . $item['author_xchan'] . '&dest=' . $r['href'] . '/' . $r['revision'];
$url = z_root() . '/magic?f=&owa=1&hash=' . $item['author_xchan'] . '&dest=' . $r['href'] . '/' . $r['revision'];
//$s .= '<a href="' . $url . '" title="' . $title . '" class="attachlink" >' . $icon . '</a>';
$attaches[] = array('label' => $label, 'url' => $url, 'icon' => $icon, 'title' => $title);
@@ -1786,28 +1794,28 @@ function layout_select($channel_id, $current = '') {
}
function mimetype_select($channel_id, $current = 'text/bbcode') {
function mimetype_select($channel_id, $current = 'text/bbcode', $choices = null, $element = 'mimetype') {
$x = array(
'text/bbcode',
'text/html',
'text/markdown',
'text/plain',
'application/x-pdl'
);
$x = (($choices) ? $choices : [
'text/bbcode' => t('BBcode'),
'text/html' => t('HTML'),
'text/markdown' => t('Markdown'),
'text/plain' => t('Text'),
'application/x-pdl' => t('Comanche Layout')
]);
if((App::$is_sys) || (channel_codeallowed($channel_id) && $channel_id == local_channel())){
$x[] = 'application/x-php';
$x['application/x-php'] = t('PHP');
}
foreach($x as $y) {
foreach($x as $y => $z) {
$selected = (($y == $current) ? ' selected="selected" ' : '');
$options .= '<option name="' . $y . '"' . $selected . '>' . $y . '</option>';
$options .= '<option value="' . $y . '"' . $selected . '>' . $z . '</option>';
}
$o = replace_macros(get_markup_template('field_select_raw.tpl'), array(
'$field' => array('mimetype', t('Page content type'), $selected, '', $options)
'$field' => array( $element, t('Page content type'), $selected, '', $options)
));
return $o;
@@ -1984,14 +1992,14 @@ function is_a_date_arg($s) {
}
function legal_webbie($s) {
if(! strlen($s))
if(! $s)
return '';
// WARNING: This regex will not work in a federated environment.
// WARNING: This regex may not work in a federated environment.
// You will probably want something like
// preg_replace('/([^a-z0-9\_])/','',strtolower($s));
$r = preg_replace('/([^a-z0-9\-\_\.])/','',strtolower($s));
$r = preg_replace('/([^a-z0-9\-\_])/','',strtolower($s));
$x = [ 'input' => $s, 'output' => $r ];
call_hooks('legal_webbie',$x);
@@ -2003,7 +2011,7 @@ function legal_webbie_text() {
// WARNING: This will not work in a federated environment.
$s = t('a-z, 0-9, -, _, and . only');
$s = t('a-z, 0-9, -, and _ only');
$x = [ 'text' => $s ];
call_hooks('legal_webbie_text',$x);
@@ -2383,8 +2391,9 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d
$r = null;
$match = array();
$termtype = ((strpos($tag,'#') === 0) ? TERM_HASHTAG : TERM_UNKNOWN);
$termtype = ((strpos($tag,'@') === 0) ? TERM_MENTION : $termtype);
$termtype = ((strpos($tag,'#') === 0) ? TERM_HASHTAG : TERM_UNKNOWN);
$termtype = ((strpos($tag,'@') === 0) ? TERM_MENTION : $termtype);
$termtype = ((strpos($tag,'!') === 0) ? TERM_FORUM : $termtype);
$termtype = ((strpos($tag,'#^[') === 0) ? TERM_BOOKMARK : $termtype);
//is it a hash tag?
@@ -2401,16 +2410,9 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d
//...do nothing
return $replaced;
}
if($tag == '#getzot') {
$basetag = 'getzot';
$url = 'http://hubzilla.org';
$newtag = '#[zrl=' . $url . ']' . $basetag . '[/zrl]';
$body = str_replace($tag,$newtag,$body);
$replaced = true;
}
if(! $replaced) {
//base tag has the tags name only
// base tag has the tags name only
if((substr($tag,0,7) === '#&quot;') && (substr($tag,-6,6) === '&quot;')) {
$basetag = substr($tag,7);
@@ -2448,10 +2450,16 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d
//is it a person tag?
if(strpos($tag,'@') === 0) {
$grouptag = false;
if(strpos($tag,'!') === 0) {
$grouptag = true;
}
if(strpos($tag,'@') === 0 || $grouptag) {
// The @! tag will alter permissions
$exclusive = ((strpos($tag,'!') === 1 && (! $diaspora)) ? true : false);
$exclusive = (((! $grouptag) && (strpos($tag,'!') === 1) && (! $diaspora)) ? true : false);
//is it already replaced?
if(strpos($tag,'[zrl='))
@@ -2492,7 +2500,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d
if(($t2) && (! $diaspora)) {
//get the id
$tagcid = substr($newname,$t2 + 1);
$tagcid = urldecode(substr($newname,$t2 + 1));
if(strrpos($tagcid,' '))
$tagcid = substr($tagcid,0,strrpos($tagcid,' '));
@@ -2623,8 +2631,15 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d
//create profile link
$profile = str_replace(',','%2c',$profile);
$url = $profile;
$newtag = '@' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . (($forum && ! $trailing_plus_name) ? '+' : '') . '[/zrl]';
$body = str_replace('@' . (($exclusive) ? '!' : '') . $name, $newtag, $body);
if($grouptag) {
$newtag = '!' . '[zrl=' . $profile . ']' . $newname . '[/zrl]';
$body = str_replace('!' . $name, $newtag, $body);
}
else {
$newtag = '@' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . (($forum && ! $trailing_plus_name) ? '+' : '') . '[/zrl]';
$body = str_replace('@' . (($exclusive) ? '!' : '') . $name, $newtag, $body);
}
//append tag to str_tags
if(! stristr($str_tags,$newtag)) {
if(strlen($str_tags))
@@ -2831,7 +2846,7 @@ function item_url_replace($channel,&$item,$old,$new,$oldnick = '') {
*/
function sanitise_acl(&$item) {
if (strlen($item))
$item = '<' . notags(trim($item)) . '>';
$item = '<' . notags(trim(urldecode($item))) . '>';
else
unset($item);
}
@@ -2973,6 +2988,9 @@ function flatten_array_recursive($arr) {
* @param string $s Text to highlight
* @param string $lang Which language should be highlighted
* @return string
* Important: The returned text has the text pattern 'http' translated to '%eY9-!' which should be converted back
* after further processing. This was done to prevent oembed links from occurring inside code blocks.
* See include/bbcode.php
*/
function text_highlight($s, $lang) {
@@ -2993,6 +3011,8 @@ function text_highlight($s, $lang) {
else
$o = $s;
$o = str_replace('http','%eY9-!',$o);
return('<code>' . $o . '</code>');
}
@@ -3136,3 +3156,9 @@ function ellipsify($s,$maxlen) {
return mb_substr($s,0,$maxlen / 2) . '...' . mb_substr($s,mb_strlen($s) - ($maxlen / 2));
}
function purify_filename($s) {
if(($s[0] === '.') || strpos($s,'/') !== false)
return '';
return $s;
}

View File

@@ -137,3 +137,83 @@ function xchan_fetch($arr) {
}
function xchan_keychange_table($table,$column,$oldxchan,$newxchan) {
$r = q("update $table set $column = '%s' where $column = '%s'",
dbesc($newxchan['xchan_hash']),
dbesc($oldxchan['xchan_hash'])
);
return $r;
}
function xchan_keychange_acl($table,$column,$oldxchan,$newxchan) {
$allow = (($table === 'channel') ? 'channel_allow_cid' : 'allow_cid');
$deny = (($table === 'channel') ? 'channel_deny_cid' : 'deny_cid');
$r = q("select $column, $allow, $deny from $table where ($allow like '%s' or $deny like '%s') ",
dbesc('<' . $oldxchan['xchan_hash'] . '>'),
dbesc('<' . $oldxchan['xchan_hash'] . '>')
);
if($r) {
foreach($r as $rv) {
$z = q("update $table set $allow = '%s', $deny = '%s' where $column = %d",
dbesc(str_replace('<' . $oldxchan['xchan_hash'] . '>', '<' . $newxchan['xchan_hash'] . '>',
$rv[$allow])),
dbesc(str_replace('<' . $oldxchan['xchan_hash'] . '>', '<' . $newxchan['xchan_hash'] . '>',
$rv[$deny])),
intval($rv[$column])
);
}
}
return $z;
}
function xchan_change_key($oldx,$newx,$data) {
$tables = [
'abook' => 'abook_xchan',
'abconfig' => 'xchan',
'group_member' => 'xchan',
'chat' => 'chat_xchan',
'chatpresence' => 'cp_xchan',
'event' => 'event_xchan',
'item' => 'owner_xchan',
'item' => 'author_xchan',
'item' => 'source_xchan',
'mail' => 'from_xchan',
'mail' => 'to_xchan',
'shares' => 'share_xchan',
'source' => 'src_channel_xchan',
'source' => 'src_xchan',
'xchat' => 'xchat_xchan',
'xconfig' => 'xchan',
'xign' => 'xchan',
'xlink' => 'xlink_xchan',
'xprof' => 'xprof_hash',
'xtag' => 'xtag_hash'
];
$acls = [
'channel' => 'channel_id',
'attach' => 'id',
'chatroom' => 'cr_id',
'event' => 'id',
'item' => 'id',
'menu_item' => 'mitem_id',
'obj' => 'obj_id',
'photo' => 'id'
];
foreach($tables as $k => $v) {
xchan_keychange_table($k,$v,$oldx,$newx);
}
foreach($acls as $k => $v) {
xchan_keychange_acl($k,$v,$oldx,$newx);
}
}

View File

@@ -81,6 +81,10 @@ function zid($s,$address = '') {
}
function strip_query_param($s,$param) {
return preg_replace('/[\?&]' . $param . '=(.*?)(&|$)/ism','$2',$s);
}
function strip_zids($s) {
return preg_replace('/[\?&]zid=(.*?)(&|$)/ism','$2',$s);
}
@@ -230,3 +234,76 @@ function red_zrlify_img_callback($matches) {
return $matches[0];
}
function owt_init($token) {
\Zotlabs\Zot\Verify::purge('owt','3 MINUTE');
$ob_hash = \Zotlabs\Zot\Verify::get_meta('owt',0,$token);
if($ob_hash === false) {
return;
}
$r = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash
where hubloc_addr = '%s' order by hubloc_id desc",
dbesc($ob_hash)
);
if(! $r) {
// finger them if they can't be found.
$j = \Zotlabs\Zot\Finger::run($ob_hash, null);
if ($j['success']) {
import_xchan($j);
$r = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash
where hubloc_addr = '%s' order by hubloc_id desc",
dbesc($ob_hash)
);
}
}
if(! $r) {
logger('owt: unable to finger ' . $ob_hash);
return;
}
$hubloc = $r[0];
$_SESSION['authenticated'] = 1;
$delegate_success = false;
if($_REQUEST['delegate']) {
$r = q("select * from channel left join xchan on channel_hash = xchan_hash where xchan_addr = '%s' limit 1",
dbesc($_REQUEST['delegate'])
);
if ($r && intval($r[0]['channel_id'])) {
$allowed = perm_is_allowed($r[0]['channel_id'],$hubloc['xchan_hash'],'delegate');
if($allowed) {
$_SESSION['delegate_channel'] = $r[0]['channel_id'];
$_SESSION['delegate'] = $hubloc['xchan_hash'];
$_SESSION['account_id'] = intval($r[0]['channel_account_id']);
require_once('include/security.php');
// this will set the local_channel authentication in the session
change_channel($r[0]['channel_id']);
$delegate_success = true;
}
}
}
if (! $delegate_success) {
// normal visitor (remote_channel) login session credentials
$_SESSION['visitor_id'] = $hubloc['xchan_hash'];
$_SESSION['my_url'] = $hubloc['xchan_url'];
$_SESSION['my_address'] = $hubloc['hubloc_addr'];
$_SESSION['remote_hub'] = $hubloc['hubloc_url'];
$_SESSION['DNT'] = 1;
}
$arr = array('xchan' => $hubloc, 'url' => \App::$query_string, 'session' => $_SESSION);
call_hooks('magic_auth_success',$arr);
\App::set_observer($hubloc);
require_once('include/security.php');
\App::set_groups(init_groups_visitor($_SESSION['visitor_id']));
if(! get_config('system','hide_owa_greeting'))
info(sprintf( t('OpenWebAuth: %1$s welcomes %2$s'),\App::get_hostname(), $hubloc['xchan_name']));
logger('OpenWebAuth: auth success from ' . $hubloc['xchan_addr']);
}

View File

@@ -31,9 +31,9 @@ require_once('include/perm_upgrade.php');
* @param string $channel_nick a unique nickname of controlling entity
* @returns string
*/
function zot_new_uid($channel_nick) {
$rawstr = z_root() . '/' . $channel_nick . '.' . mt_rand();
return(base64url_encode(hash('whirlpool', $rawstr, true), true));
}
@@ -49,6 +49,7 @@ function zot_new_uid($channel_nick) {
* @param string $guid
* @param string $guid_sig
*/
function make_xchan_hash($guid, $guid_sig) {
return base64url_encode(hash('whirlpool', $guid . $guid_sig, true));
}
@@ -62,17 +63,17 @@ function make_xchan_hash($guid, $guid_sig) {
* @param string $hash - xchan_hash
* @returns array of hubloc (hub location structures)
* * \b hubloc_id int
* * \b hubloc_guid char(255)
* * \b hubloc_guid char(191)
* * \b hubloc_guid_sig text
* * \b hubloc_hash char(255)
* * \b hubloc_addr char(255)
* * \b hubloc_hash char(191)
* * \b hubloc_addr char(191)
* * \b hubloc_flags int
* * \b hubloc_status int
* * \b hubloc_url char(255)
* * \b hubloc_url char(191)
* * \b hubloc_url_sig text
* * \b hubloc_host char(255)
* * \b hubloc_callback char(255)
* * \b hubloc_connect char(255)
* * \b hubloc_host char(191)
* * \b hubloc_callback char(191)
* * \b hubloc_connect char(191)
* * \b hubloc_sitekey text
* * \b hubloc_updated datetime
* * \b hubloc_connected datetime
@@ -97,7 +98,7 @@ function zot_get_hublocs($hash) {
* @param array $channel
* sender channel structure
* @param string $type
* packet type: one of 'ping', 'pickup', 'purge', 'refresh', 'force_refresh', 'notify', 'auth_check'
* packet type: one of 'ping', 'pickup', 'purge', 'refresh', 'keychange', 'force_refresh', 'notify', 'auth_check'
* @param array $recipients
* envelope information, array ( 'guid' => string, 'guid_sig' => string ); empty for public posts
* @param string $remote_key
@@ -111,18 +112,21 @@ function zot_get_hublocs($hash) {
*/
function zot_build_packet($channel, $type = 'notify', $recipients = null, $remote_key = null, $methods = '', $secret = null, $extra = null) {
$sig_method = get_config('system','signature_algorithm','sha256');
$data = [
'type' => $type,
'sender' => [
'guid' => $channel['channel_guid'],
'guid_sig' => base64url_encode(rsa_sign($channel['channel_guid'],$channel['channel_prvkey'])),
'guid_sig' => base64url_encode(rsa_sign($channel['channel_guid'],$channel['channel_prvkey'],$sig_method)),
'url' => z_root(),
'url_sig' => base64url_encode(rsa_sign(z_root(),$channel['channel_prvkey'])),
'url_sig' => base64url_encode(rsa_sign(z_root(),$channel['channel_prvkey'],$sig_method)),
'sitekey' => get_config('system','pubkey')
],
'callback' => '/post',
'version' => ZOT_REVISION,
'encryption' => crypto_methods()
'version' => Zotlabs\Lib\System::get_zot_revision(),
'encryption' => crypto_methods(),
'signing' => signing_methods()
];
if ($recipients) {
@@ -134,7 +138,7 @@ function zot_build_packet($channel, $type = 'notify', $recipients = null, $remot
if ($secret) {
$data['secret'] = preg_replace('/[^0-9a-fA-F]/','',$secret);
$data['secret_sig'] = base64url_encode(rsa_sign($secret,$channel['channel_prvkey']));
$data['secret_sig'] = base64url_encode(rsa_sign($secret,$channel['channel_prvkey'],$sig_method));
}
if ($extra) {
@@ -308,6 +312,7 @@ function zot_refresh($them, $channel = null, $force = false) {
logger('zot_refresh: ' . $url, LOGGER_DATA, LOG_INFO);
$result = z_post_url($url . $rhs,$postvars);
if ($result['success']) {
@@ -356,8 +361,6 @@ function zot_refresh($them, $channel = null, $force = false) {
else
$permissions = $j['permissions'];
$connected_set = false;
if($permissions && is_array($permissions)) {
$old_read_stream_perm = get_abconfig($channel['channel_id'],$x['hash'],'their_perms','view_stream');
@@ -529,7 +532,7 @@ function zot_gethub($arr, $multiple = false) {
}
$limit = (($multiple) ? '' : ' limit 1 ');
$sitekey = ((array_key_exists('sitekey',$arr) && $arr['sitekey']) ? " and hubloc_sitekey = '" . protect_sprintf($arr['sitekey']) . "' " : '');
$sitekey = ((array_key_exists('sitekey',$arr) && $arr['sitekey']) ? " and hubloc_sitekey = '" . dbesc(protect_sprintf($arr['sitekey'])) . "' " : '');
$r = q("select hubloc.*, site.site_crypto from hubloc left join site on hubloc_url = site_url
where hubloc_guid = '%s' and hubloc_guid_sig = '%s'
@@ -575,6 +578,8 @@ function zot_register_hub($arr) {
if($arr['url'] && $arr['url_sig'] && $arr['guid'] && $arr['guid_sig']) {
$sig_methods = ((array_key_exists('signing',$arr) && is_array($arr['signing'])) ? $arr['signing'] : [ 'sha256' ]);
$guid_hash = make_xchan_hash($arr['guid'],$arr['guid_sig']);
$url = $arr['url'] . '/.well-known/zot-info/?f=&guid_hash=' . $guid_hash;
@@ -594,17 +599,18 @@ function zot_register_hub($arr) {
* our current communication.
*/
if((rsa_verify($arr['guid'],base64url_decode($arr['guid_sig']),$record['key']))
&& (rsa_verify($arr['url'],base64url_decode($arr['url_sig']),$record['key']))
foreach($sig_methods as $method) {
if((rsa_verify($arr['guid'],base64url_decode($arr['guid_sig']),$record['key'],$method))
&& (rsa_verify($arr['url'],base64url_decode($arr['url_sig']),$record['key'],$method))
&& ($arr['guid'] === $record['guid'])
&& ($arr['guid_sig'] === $record['guid_sig'])) {
$c = import_xchan($record);
if($c['success'])
$result['success'] = true;
}
else {
logger('zot_register_hub: failure to verify returned packet.');
$c = import_xchan($record);
if($c['success'])
$result['success'] = true;
}
else {
logger('zot_register_hub: failure to verify returned packet using ' . $method);
}
}
}
}
@@ -657,8 +663,19 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
$import_photos = false;
if(! rsa_verify($arr['guid'],base64url_decode($arr['guid_sig']),$arr['key'])) {
logger('import_xchan: Unable to verify channel signature for ' . $arr['address']);
$sig_methods = ((array_key_exists('signing',$arr) && is_array($arr['signing'])) ? $arr['signing'] : [ 'sha256' ]);
$verified = false;
foreach($sig_methods as $method) {
if(! rsa_verify($arr['guid'],base64url_decode($arr['guid_sig']),$arr['key'],$method)) {
logger('import_xchan: Unable to verify channel signature for ' . $arr['address'] . ' using ' . $method);
continue;
}
else {
$verified = true;
}
}
if(! $verified) {
$ret['message'] = t('Unable to verify channel signature');
return $ret;
}
@@ -700,6 +717,16 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
if(intval($r[0]['xchan_pubforum']) != intval($arr['public_forum']))
$pubforum_changed = 1;
if($arr['protocols']) {
$protocols = implode(',',$arr['protocols']);
if($protocols !== 'zot') {
set_xconfig($xchan_hash,'system','protocols',$protocols);
}
else {
del_xconfig($xchan_hash,'system','protocols');
}
}
if(($r[0]['xchan_name_date'] != $arr['name_updated'])
|| ($r[0]['xchan_connurl'] != $arr['connections_url'])
|| ($r[0]['xchan_addr'] != $arr['address'])
@@ -917,7 +944,7 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
}
elseif(! $ud_flags) {
// nothing changed but we still need to update the updates record
q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d)>0 ",
q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d) > 0 ",
intval(UPDATE_FLAGS_UPDATED),
dbesc($address),
intval(UPDATE_FLAGS_UPDATED)
@@ -959,6 +986,18 @@ function zot_process_response($hub, $arr, $outq) {
}
if(is_array($x) && array_key_exists('delivery_report',$x) && is_array($x['delivery_report'])) {
if(array_key_exists('iv',$x['delivery_report'])) {
$j = crypto_unencapsulate($x['delivery_report'],get_config('system','prvkey'));
if($j) {
$x['delivery_report'] = json_decode($j,true);
}
if(! (is_array($x['delivery_report']) && count($x['delivery_report']))) {
logger('encrypted delivery report could not be decrypted');
return;
}
}
foreach($x['delivery_report'] as $xx) {
if(is_array($xx) && array_key_exists('message_id',$xx) && delivery_report_is_storable($xx)) {
q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_result, dreport_time, dreport_xchan ) values ( '%s', '%s','%s','%s','%s','%s' ) ",
@@ -1030,13 +1069,15 @@ function zot_fetch($arr) {
foreach($ret_hubs as $ret_hub) {
$secret = substr(preg_replace('/[^0-9a-fA-F]/','',$arr['secret']),0,64);
$data = [
'type' => 'pickup',
'url' => z_root(),
'callback_sig' => base64url_encode(rsa_sign(z_root() . '/post', get_config('system','prvkey'))),
'callback' => z_root() . '/post',
'secret' => $arr['secret'],
'secret_sig' => base64url_encode(rsa_sign($arr['secret'], get_config('system','prvkey')))
'secret' => $secret,
'secret_sig' => base64url_encode(rsa_sign($secret, get_config('system','prvkey')))
];
$algorithm = zot_best_algorithm($ret_hub['site_crypto']);
@@ -1046,8 +1087,11 @@ function zot_fetch($arr) {
$result = zot_import($fetch, $arr['sender']['url']);
if($result)
if($result) {
$result = crypto_encapsulate(json_encode($result),$ret_hub['hubloc_sitekey'], $algorithm);
return $result;
}
}
return;
@@ -1397,7 +1441,7 @@ function public_recips($msg) {
if($msg['message']['tags']) {
if(is_array($msg['message']['tags']) && $msg['message']['tags']) {
foreach($msg['message']['tags'] as $tag) {
if(($tag['type'] === 'mention') && (strpos($tag['url'],z_root()) !== false)) {
if(($tag['type'] === 'mention' || $tag['type'] === 'forum') && (strpos($tag['url'],z_root()) !== false)) {
$address = basename($tag['url']);
if($address) {
$z = q("select channel_hash as hash from channel where channel_address = '%s'
@@ -2857,8 +2901,14 @@ function import_site($arr, $pubkey) {
$site_directory = DIRECTORY_MODE_NORMAL;
}
$site_flags = $site_directory;
if(array_key_exists('zot',$arr)) {
set_sconfig($arr['url'],'system','zot_version',$arr['zot']);
}
if($exists) {
if(($siterecord['site_flags'] != $site_directory)
if(($siterecord['site_flags'] != $site_flags)
|| ($siterecord['site_access'] != $access_policy)
|| ($siterecord['site_directory'] != $directory_url)
|| ($siterecord['site_sellpage'] != $sellpage)
@@ -2878,7 +2928,7 @@ function import_site($arr, $pubkey) {
$r = q("update site set site_dead = 0, site_location = '%s', site_flags = %d, site_access = %d, site_directory = '%s', site_register = %d, site_update = '%s', site_sellpage = '%s', site_realm = '%s', site_type = %d, site_project = '%s', site_version = '%s', site_crypto = '%s'
where site_url = '%s'",
dbesc($site_location),
intval($site_directory),
intval($site_flags),
intval($access_policy),
dbesc($directory_url),
intval($register_policy),
@@ -2911,7 +2961,7 @@ function import_site($arr, $pubkey) {
'site_location' => $site_location,
'site_url' => $url,
'site_access' => intval($access_policy),
'site_flags' => intval($site_directory),
'site_flags' => intval($site_flags),
'site_update' => datetime_convert(),
'site_directory' => $directory_url,
'site_register' => intval($register_policy),
@@ -2947,8 +2997,11 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) {
logger('build_sync_packet');
if($packet)
logger('packet: ' . print_r($packet, true),LOGGER_DATA, LOG_DEBUG);
$keychange = (($packet && array_key_exists('keychange',$packet)) ? true : false);
if($keychange) {
logger('keychange sync');
}
if(! $uid)
$uid = local_channel();
@@ -2963,6 +3016,7 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) {
return;
$channel = $r[0];
unset($channel['channel_password']);
unset($channel['channel_salt']);
@@ -2973,12 +3027,11 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) {
}
}
if(intval($channel['channel_removed']))
return;
$h = q("select hubloc.*, site.site_crypto from hubloc left join site on site_url = hubloc_url where hubloc_hash = '%s' and hubloc_deleted = 0",
dbesc($channel['channel_hash'])
dbesc(($keychange) ? $packet['keychange']['old_hash'] : $channel['channel_hash'])
);
if(! $h)
@@ -3010,6 +3063,9 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) {
$env_recips = array();
$env_recips[] = array('guid' => $r[0]['xchan_guid'],'guid_sig' => $r[0]['xchan_guid_sig']);
if($packet)
logger('packet: ' . print_r($packet, true),LOGGER_DATA, LOG_DEBUG);
$info = (($packet) ? $packet : array());
$info['type'] = 'channel_sync';
$info['encoding'] = 'red'; // note: not zot, this packet is very platform specific
@@ -3033,7 +3089,15 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) {
// don't pass these elements, they should not be synchronised
$disallowed = array('channel_id','channel_account_id','channel_primary','channel_prvkey','channel_address','channel_deleted','channel_removed','channel_system');
$disallowed = [
'channel_id','channel_account_id','channel_primary','channel_address',
'channel_deleted','channel_removed','channel_system'
];
if(! $keychange) {
$disallowed[] = 'channel_prvkey';
}
if(in_array($k,$disallowed))
continue;
@@ -3093,17 +3157,18 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) {
function process_channel_sync_delivery($sender, $arr, $deliveries) {
require_once('include/import.php');
/** @FIXME this will sync red structures (channel, pconfig and abook).
Eventually we need to make this application agnostic. */
$result = array();
$result = [];
$keychange = ((array_key_exists('keychange',$arr)) ? true : false);
foreach ($deliveries as $d) {
$r = q("select * from channel where channel_hash = '%s' limit 1",
dbesc($d['hash'])
dbesc(($keychange) ? $arr['keychange']['old_hash'] : $d['hash'])
);
if (! $r) {
@@ -3122,6 +3187,94 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) {
continue;
}
if($keychange) {
// verify the keychange operation
if(! rsa_verify($arr['channel']['channel_pubkey'],base64url_decode($arr['keychange']['new_sig']),$channel['channel_prvkey'])) {
logger('sync keychange: verification failed');
continue;
}
$sig = base64url_encode(rsa_sign($channel['channel_guid'],$arr['channel']['channel_prvkey']));
$hash = make_xchan_hash($channel['channel_guid'],$sig);
$r = q("update channel set channel_prvkey = '%s', channel_pubkey = '%s', channel_guid_sig = '%s',
channel_hash = '%s' where channel_id = %d",
dbesc($arr['channel']['channel_prvkey']),
dbesc($arr['channel']['channel_pubkey']),
dbesc($sig),
dbesc($hash),
intval($channel['channel_id'])
);
if(! $r) {
logger('keychange sync: channel update failed');
continue;
}
$r = q("select * from channel where channel_id = %d",
intval($channel['channel_id'])
);
if(! $r) {
logger('keychange sync: channel retrieve failed');
continue;
}
$channel = $r[0];
$h = q("select * from hubloc where hubloc_hash = '%s' and hubloc_url = '%s' ",
dbesc($arr['keychange']['old_hash']),
dbesc(z_root())
);
if($h) {
foreach($h as $hv) {
$hv['hubloc_guid_sig'] = $sig;
$hv['hubloc_hash'] = $hash;
$hv['hubloc_url_sig'] = base64url_encode(rsa_sign(z_root(),$channel['channel_prvkey']));
hubloc_store_lowlevel($hv);
}
}
$x = q("select * from xchan where xchan_hash = '%s' ",
dbesc($arr['keychange']['old_hash'])
);
$check = q("select * from xchan where xchan_hash = '%s'",
dbesc($hash)
);
if(($x) && (! $check)) {
$oldxchan = $x[0];
foreach($x as $xv) {
$xv['xchan_guid_sig'] = $sig;
$xv['xchan_hash'] = $hash;
$xv['xchan_pubkey'] = $channel['channel_pubkey'];
xchan_store_lowlevel($xv);
$newxchan = $xv;
}
}
$a = q("select * from abook where abook_xchan = '%s' and abook_self = 1",
dbesc($arr['keychange']['old_hash'])
);
if($a) {
q("update abook set abook_xchan = '%s' where abook_id = %d",
dbesc($hash),
intval($a[0]['abook_id'])
);
}
xchan_change_key($oldxchan,$newxchan,$arr['keychange']);
// keychange operations can end up in a confused state if you try and sync anything else
// besides the channel keys, so ignore any other packets.
continue;
}
if(array_key_exists('config',$arr) && is_array($arr['config']) && count($arr['config'])) {
foreach($arr['config'] as $cat => $k) {
foreach($arr['config'][$cat] as $k => $v)
@@ -3759,11 +3912,57 @@ function zot_reply_message_request($data) {
json_return_and_die($ret);
}
function zot_rekey_request($sender,$data) {
$ret = array('success' => false);
// newsig is newkey signed with oldkey
// The original xchan will remain. In Zot/Receiver we will have imported the new xchan and hubloc to verify
// the packet authenticity. What we will do now is verify that the keychange operation was signed by the
// oldkey, and if so change all the abook, abconfig, group, and permission elements which reference the
// old xchan_hash.
if((! $data['old_key']) && (! $data['new_key']) && (! $data['new_sig']))
json_return_and_die($ret);
$oldhash = make_xchan_hash($data['old_guid'],$data['old_guid_sig']);
$r = q("select * from xchan where xchan_hash = '%s' limit 1",
dbesc($oldhash)
);
if(! $r) {
json_return_and_die($ret);
}
$xchan = $r[0];
if(! rsa_verify($data['new_key'],base64url_decode($data['new_sig']),$xchan['xchan_pubkey'])) {
json_return_and_die($ret);
}
$newhash = make_xchan_hash($sender['guid'],$sender['guid_sig']);
$r = q("select * from xchan where xchan_hash = '%s' limit 1",
dbesc($newhash)
);
$newxchan = $r[0];
xchan_change_key($xchan,$newxchan,$data);
$ret['success'] = true;
json_return_and_die($ret);
}
function zotinfo($arr) {
$ret = array('success' => false);
$sig_method = get_config('system','signature_algorithm','sha256');
$zhash = ((x($arr,'guid_hash')) ? $arr['guid_hash'] : '');
$zguid = ((x($arr,'guid')) ? $arr['guid'] : '');
$zguid_sig = ((x($arr,'guid_sig')) ? $arr['guid_sig'] : '');
@@ -3845,6 +4044,11 @@ function zotinfo($arr) {
$id = $e['channel_id'];
$x = [ 'channel_id' => $id, 'protocols' => ['zot'] ];
call_hooks('channel_protocols',$x);
$protocols = $x['protocols'];
$sys_channel = (intval($e['channel_system']) ? true : false);
$special_channel = (($e['channel_pageflags'] & PAGE_PREMIUM) ? true : false);
$adult_channel = (($e['channel_pageflags'] & PAGE_ADULT) ? true : false);
@@ -3927,7 +4131,7 @@ function zotinfo($arr) {
// Communication details
if($token)
$ret['signed_token'] = base64url_encode(rsa_sign('token.' . $token,$e['channel_prvkey']));
$ret['signed_token'] = base64url_encode(rsa_sign('token.' . $token,$e['channel_prvkey'],$sig_method));
$ret['guid'] = $e['xchan_guid'];
@@ -3945,6 +4149,7 @@ function zotinfo($arr) {
$ret['target'] = $ztarget;
$ret['target_sig'] = $zsig;
$ret['searchable'] = $searchable;
$ret['protocols'] = $protocols;
$ret['adult_content'] = $adult_channel;
$ret['public_forum'] = $public_forum;
if($deleted)
@@ -3970,7 +4175,7 @@ function zotinfo($arr) {
if($ztarget_hash) {
$permissions['connected'] = false;
$b = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1",
$b = q("select * from abook where abook_xchan = '%s' and abook_channel = %d and abook_pending = 0 limit 1",
dbesc($ztarget_hash),
intval($e['channel_id'])
);
@@ -3994,10 +4199,33 @@ function zotinfo($arr) {
if($x)
$ret['locations'] = $x;
$ret['site'] = array();
$ret['site'] = zot_site_info($e['channel_prvkey']);
check_zotinfo($e,$x,$ret);
call_hooks('zot_finger',$ret);
return($ret);
}
function zot_site_info($channel_key = '') {
$signing_key = get_config('system','prvkey');
$sig_method = get_config('system','signature_algorithm','sha256');
$ret = [];
$ret['site'] = [];
$ret['site']['url'] = z_root();
$ret['site']['url_sig'] = base64url_encode(rsa_sign(z_root(),$e['channel_prvkey']));
$ret['site']['zot_auth'] = z_root() . '/magic';
if($channel_key) {
$ret['site']['url_sig'] = base64url_encode(rsa_sign(z_root(),$channel_key,$sig_method));
}
$ret['site']['url_site_sig'] = base64url_encode(rsa_sign(z_root(),$signing_key,$sig_method));
$ret['site']['post'] = z_root() . '/post';
$ret['site']['openWebAuth'] = z_root() . '/owa';
$ret['site']['authRedirect'] = z_root() . '/magic';
$ret['site']['key'] = get_config('system','pubkey');
$dirmode = get_config('system','directory_mode');
if(($dirmode === false) || ($dirmode == DIRECTORY_MODE_NORMAL))
@@ -4014,6 +4242,8 @@ function zotinfo($arr) {
$ret['site']['encryption'] = crypto_methods();
$ret['site']['signing'] = signing_methods();
$ret['site']['zot'] = Zotlabs\Lib\System::get_zot_revision();
// hide detailed site information if you're off the grid
@@ -4066,15 +4296,10 @@ function zotinfo($arr) {
}
check_zotinfo($e,$x,$ret);
call_hooks('zot_finger',$ret);
return($ret);
return $ret['site'];
}
function check_zotinfo($channel,$locations,&$ret) {