aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/RedDAV/RedBrowser.php2
-rw-r--r--include/account.php4
-rw-r--r--include/api.php25
-rw-r--r--include/api_auth.php80
-rw-r--r--include/comanche.php9
-rw-r--r--include/externals.php21
-rw-r--r--include/follow.php25
-rw-r--r--include/identity.php6
-rwxr-xr-xinclude/items.php9
-rw-r--r--include/message.php12
-rw-r--r--include/network.php187
-rw-r--r--include/notifier.php2
-rw-r--r--include/oauth.php149
-rwxr-xr-xinclude/plugin.php20
-rw-r--r--include/security.php18
-rw-r--r--include/widgets.php20
-rw-r--r--include/zot.php464
17 files changed, 854 insertions, 199 deletions
diff --git a/include/RedDAV/RedBrowser.php b/include/RedDAV/RedBrowser.php
index efea5d92f..1aa5f435e 100644
--- a/include/RedDAV/RedBrowser.php
+++ b/include/RedDAV/RedBrowser.php
@@ -188,7 +188,7 @@ class RedBrowser extends DAV\Browser\Plugin {
$parentHash = '';
$owner = $this->auth->owner_id;
- $splitPath = split('/', $fullPath);
+ $splitPath = explode('/', $fullPath);
if (count($splitPath) > 3) {
for ($i = 3; $i < count($splitPath); $i++) {
$attachName = urldecode($splitPath[$i]);
diff --git a/include/account.php b/include/account.php
index b3a520fd4..e448bdcc6 100644
--- a/include/account.php
+++ b/include/account.php
@@ -67,7 +67,7 @@ function check_account_invite($invite_code) {
$result['message'] .= t('An invitation is required.') . EOL;
}
$r = q("select * from register where `hash` = '%s' limit 1", dbesc($invite_code));
- if(! results($r)) {
+ if(! $r) {
$result['message'] .= t('Invitation could not be verified.') . EOL;
}
}
@@ -718,4 +718,4 @@ function upgrade_message($bbcode = false) {
function upgrade_bool_message($bbcode = false) {
$x = upgrade_link($bbcode);
return t('This action is not available under your subscription plan.') . (($x) ? ' ' . $x : '') ;
-} \ No newline at end of file
+}
diff --git a/include/api.php b/include/api.php
index f279b2aa3..4ad915c03 100644
--- a/include/api.php
+++ b/include/api.php
@@ -1,10 +1,10 @@
<?php /** @file */
-require_once("bbcode.php");
-require_once("datetime.php");
-require_once("conversation.php");
-require_once("oauth.php");
-require_once("html2plain.php");
+require_once("include/bbcode.php");
+require_once("include/datetime.php");
+require_once("include/conversation.php");
+require_once("include/oauth.php");
+require_once("include/html2plain.php");
require_once('include/security.php');
require_once('include/photos.php');
require_once('include/items.php');
@@ -382,7 +382,6 @@ require_once('include/api_auth.php');
function api_item_get_user(&$a, $item) {
- global $usercache;
// The author is our direct contact, in a conversation with us.
@@ -396,11 +395,11 @@ require_once('include/api_auth.php');
$name = $item['author']['xchan_name'];
// Generating a random ID
- if (is_null($usercache[$nick]) or !array_key_exists($nick, $usercache))
- $usercache[$nick] = mt_rand(2000000, 2100000);
+ if (! $nick)
+ $nick = mt_rand(2000000, 2100000);
$ret = array(
- 'id' => $usercache[$nick],
+ 'id' => $nick,
'name' => $name,
'screen_name' => $nick,
'location' => '', //$uinfo[0]['default-location'],
@@ -2299,12 +2298,11 @@ require_once('include/api_auth.php');
api_register_func('api/direct_messages','api_direct_messages_inbox',true);
-
function api_oauth_request_token(&$a, $type){
try{
- $oauth = new FKOAuth1();
+ $oauth = new ZotOAuth1();
$req = OAuthRequest::from_request();
-logger('Req: ' . var_export($req,true));
+ logger('Req: ' . var_export($req,true),LOGGER_DATA);
$r = $oauth->fetch_request_token($req);
}catch(Exception $e){
logger('oauth_exception: ' . print_r($e->getMessage(),true));
@@ -2314,9 +2312,10 @@ logger('Req: ' . var_export($req,true));
echo $r;
killme();
}
+
function api_oauth_access_token(&$a, $type){
try{
- $oauth = new FKOAuth1();
+ $oauth = new ZotOAuth1();
$req = OAuthRequest::from_request();
$r = $oauth->fetch_access_token($req);
}catch(Exception $e){
diff --git a/include/api_auth.php b/include/api_auth.php
index ee9db3f55..c9978c99d 100644
--- a/include/api_auth.php
+++ b/include/api_auth.php
@@ -1,16 +1,18 @@
<?php /** @file */
-require_once("oauth.php");
-
-
/**
- * Simple HTTP Login
+ * API Login via basic-auth or OAuth
*/
function api_login(&$a){
+
+ $record = null;
+
+ require_once('include/oauth.php');
+
// login with oauth
try {
- $oauth = new FKOAuth1();
+ $oauth = new ZotOAuth1();
$req = OAuthRequest::from_request();
list($consumer,$token) = $oauth->verify_request($req);
@@ -23,16 +25,14 @@ function api_login(&$a){
call_hooks('logged_in', $a->user);
return;
}
- echo __file__.__line__.__function__."<pre>";
-// var_dump($consumer, $token);
- die();
+ killme();
}
catch(Exception $e) {
logger(__file__.__line__.__function__."\n".$e);
}
-
- // workaround for HTTP-auth in CGI mode
+ // 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)) {
@@ -51,45 +51,49 @@ function api_login(&$a){
}
}
+ require_once('include/auth.php');
+ require_once('include/security.php');
- if (!isset($_SERVER['PHP_AUTH_USER'])) {
- logger('API_login: ' . print_r($_SERVER,true), LOGGER_DEBUG);
- header('WWW-Authenticate: Basic realm="Red"');
- header('HTTP/1.0 401 Unauthorized');
- die('This api requires login');
- }
-
// process normal login request
- require_once('include/auth.php');
- $channel_login = 0;
- $record = account_verify_password($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']);
- if(! $record) {
- $r = q("select * from channel where channel_address = '%s' limit 1",
+
+ if(isset($_SERVER['PHP_AUTH_USER'])) {
+ $channel_login = 0;
+ $record = account_verify_password($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']);
+ if(! $record) {
+ $r = q("select * from channel left join account on account.account_id = channel.channel_account_id
+ where channel.channel_address = '%s' limit 1",
dbesc($_SERVER['PHP_AUTH_USER'])
);
if ($r) {
- $x = q("select * from account where account_id = %d limit 1",
- intval($r[0]['channel_account_id'])
- );
- if ($x) {
- $record = account_verify_password($x[0]['account_email'],$_SERVER['PHP_AUTH_PW']);
+ $record = account_verify_password($r[0]['account_email'],$_SERVER['PHP_AUTH_PW']);
if($record)
$channel_login = $r[0]['channel_id'];
}
}
- if(! $record) {
- logger('API_login failure: ' . print_r($_SERVER,true), LOGGER_DEBUG);
- header('WWW-Authenticate: Basic realm="Red"');
- header('HTTP/1.0 401 Unauthorized');
- die('This api requires login');
- }
}
- require_once('include/security.php');
- authenticate_success($record);
+ if($record) {
+ authenticate_success($record);
- if($channel_login)
- change_channel($channel_login);
+ if($channel_login)
+ change_channel($channel_login);
+
+ $_SESSION['allow_api'] = true;
+ return true;
+ }
+ else {
+ $_SERVER['PHP_AUTH_PW'] = '*****';
+ logger('API_login failure: ' . print_r($_SERVER,true), LOGGER_DEBUG);
+ log_failed_login('API login failure');
+ retry_basic_auth();
+ }
- $_SESSION['allow_api'] = true;
}
+
+
+function retry_basic_auth() {
+ header('WWW-Authenticate: Basic realm="Hubzilla"');
+ header('HTTP/1.0 401 Unauthorized');
+ echo('This api requires login');
+ killme();
+} \ No newline at end of file
diff --git a/include/comanche.php b/include/comanche.php
index 9585a6578..ef71886f2 100644
--- a/include/comanche.php
+++ b/include/comanche.php
@@ -282,15 +282,16 @@ function comanche_widget($name, $text) {
}
}
- if(file_exists('widget/' . trim($name) . '.php'))
+ $func = 'widget_' . trim($name);
+
+ if((! function_exists($func)) && file_exists('widget/' . trim($name) . '.php'))
require_once('widget/' . trim($name) . '.php');
else {
- $theme_widget = 'widget_' . trim($name) . '.php';
- if(theme_include($theme_widget))
+ $theme_widget = $func . '.php';
+ if((! function_exists($func)) && theme_include($theme_widget))
require_once(theme_include($theme_widget));
}
- $func = 'widget_' . trim($name);
if (function_exists($func))
return $func($vars);
}
diff --git a/include/externals.php b/include/externals.php
index 18c034bb2..3a3a32420 100644
--- a/include/externals.php
+++ b/include/externals.php
@@ -28,7 +28,10 @@ function externals_run($argv, $argc){
}
else {
$randfunc = db_getfunc('RAND');
- $r = q("select site_url, site_pull from site where site_url != '%s' and site_flags != %d and site_type = %d order by $randfunc limit 1",
+
+ // fixme this query does not deal with directory realms.
+
+ $r = q("select site_url, site_pull from site where site_url != '%s' and site_flags != %d and site_type = %d and site_dead = 0 order by $randfunc limit 1",
dbesc(z_root()),
intval(DIRECTORY_MODE_STANDALONE),
intval(SITE_TYPE_ZOT)
@@ -37,19 +40,11 @@ function externals_run($argv, $argc){
$url = $r[0]['site_url'];
}
- // Note: blacklisted sites must be stored in the config as an array.
- // No simple way to turn this into a personal config because we have no identity here.
- // For that we probably need a variant of superblock.
-
$blacklisted = false;
- $bl1 = get_config('system','blacklisted_sites');
- if(is_array($bl1) && $bl1) {
- foreach($bl1 as $bl) {
- if($bl && strpos($url,$bl) !== false) {
- $blacklisted = true;
- break;
- }
- }
+
+ if(! check_siteallowed($url)) {
+ logger('blacklisted site: ' . $url);
+ $blacklisted = true;
}
$attempts ++;
diff --git a/include/follow.php b/include/follow.php
index 40ad2c299..97be82da7 100644
--- a/include/follow.php
+++ b/include/follow.php
@@ -161,6 +161,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
}
}
if($r) {
+ $xchan = $r[0];
$xchan_hash = $r[0]['xchan_hash'];
$their_perms = 0;
}
@@ -172,7 +173,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
return $result;
}
- $x = array('channel_id' => $uid, 'follow_address' => $url, 'xchan' => $r[0], 'allowed' => 1);
+ $x = array('channel_id' => $uid, 'follow_address' => $url, 'xchan' => $r[0], 'allowed' => 1, 'singleton' => 0);
call_hooks('follow_allow',$x);
@@ -180,7 +181,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
$result['message'] = t('Protocol disabled.');
return $result;
}
-
+ $singleton = intval($x['singleton']);
if((local_channel()) && $uid == local_channel()) {
$aid = get_account_id();
@@ -221,13 +222,22 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
return $result;
}
- $r = q("select abook_xchan from abook where abook_xchan = '%s' and abook_channel = %d limit 1",
+ $r = q("select abook_xchan, abook_instance from abook where abook_xchan = '%s' and abook_channel = %d limit 1",
dbesc($xchan_hash),
intval($uid)
);
if($r) {
- $x = q("update abook set abook_their_perms = %d where abook_id = %d",
+ $abook_instance = $r[0]['abook_instance'];
+
+ if(($singleton) && strpos($abook_instance,z_root()) === false) {
+ if($abook_instance)
+ $abook_instance .= ',';
+ $abook_instance .= z_root();
+ }
+
+ $x = q("update abook set abook_their_perms = %d, abook_instance = '%s' where abook_id = %d",
intval($their_perms),
+ dbesc($abook_instance),
intval($r[0]['abook_id'])
);
}
@@ -237,8 +247,8 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
if($closeness === false)
$closeness = 80;
- $r = q("insert into abook ( abook_account, abook_channel, abook_closeness, abook_xchan, abook_feed, abook_their_perms, abook_my_perms, abook_created, abook_updated )
- values( %d, %d, %d, '%s', %d, %d, %d, '%s', '%s' ) ",
+ $r = q("insert into abook ( abook_account, abook_channel, abook_closeness, abook_xchan, abook_feed, abook_their_perms, abook_my_perms, abook_created, abook_updated, abook_instance )
+ values( %d, %d, %d, '%s', %d, %d, %d, '%s', '%s', '%s' ) ",
intval($aid),
intval($uid),
intval($closeness),
@@ -247,7 +257,8 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
intval(($is_http) ? $their_perms|PERMS_R_STREAM|PERMS_A_REPUBLISH : $their_perms),
intval($my_perms),
dbesc(datetime_convert()),
- dbesc(datetime_convert())
+ dbesc(datetime_convert()),
+ dbesc(($singleton) ? z_root() : '')
);
}
diff --git a/include/identity.php b/include/identity.php
index 95ade3b28..98ba26bd8 100644
--- a/include/identity.php
+++ b/include/identity.php
@@ -896,12 +896,6 @@ function profile_load(&$a, $nickname, $profile = '') {
$_SESSION['theme'] = $p[0]['channel_theme'];
-// $a->set_template_engine(); // reset the template engine to the default in case the user's theme doesn't specify one
-
-// $theme_info_file = "view/theme/".current_theme()."/php/theme.php";
-// if (file_exists($theme_info_file)){
-// require_once($theme_info_file);
-// }
}
/**
diff --git a/include/items.php b/include/items.php
index 7d349c631..ef1867c14 100755
--- a/include/items.php
+++ b/include/items.php
@@ -550,6 +550,7 @@ function get_public_feed($channel, $params) {
$params['direction'] = ((x($params,'direction')) ? $params['direction'] : 'desc');
$params['pages'] = ((x($params,'pages')) ? intval($params['pages']) : 0);
$params['top'] = ((x($params,'top')) ? intval($params['top']) : 0);
+ $params['cat'] = ((x($params,'cat')) ? $params['cat'] : '');
switch($params['type']) {
case 'json':
@@ -593,7 +594,8 @@ function get_feed_for($channel, $observer_hash, $params) {
'direction' => $params['direction'], // FIXME
'pages' => $params['pages'],
'order' => 'post',
- 'top' => $params['top']
+ 'top' => $params['top'],
+ 'cat' => $params['cat']
), $channel, $observer_hash, CLIENT_MODE_NORMAL, get_app()->module);
@@ -3472,7 +3474,7 @@ function post_is_importable($item,$abook) {
unobscure($item);
$text = prepare_text($item['body'],$item['mimetype']);
- $text = html2plain($text);
+ $text = html2plain(($item['title']) ? $item['title'] . ' ' . $text : $text);
$lang = null;
@@ -4817,6 +4819,9 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C
if($arr['since_id'])
$sql_extra .= " and item.id > " . $since_id . " ";
+ if($arr['cat'])
+ $sql_extra .= protect_sprintf(term_query('item', $arr['cat'], TERM_CATEGORY));
+
if($arr['gid'] && $uid) {
$r = q("SELECT * FROM `groups` WHERE id = %d AND uid = %d LIMIT 1",
intval($arr['group']),
diff --git a/include/message.php b/include/message.php
index 820d814b6..940fcc275 100644
--- a/include/message.php
+++ b/include/message.php
@@ -13,6 +13,7 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto='
$ret = array('success' => false);
$a = get_app();
+ $observer_hash = get_observer_hash();
if(! $recipient) {
$ret['message'] = t('No recipient provided.');
@@ -148,8 +149,8 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto='
$match = null;
$images = null;
- if(preg_match_all("/\[zmg\](.*?)\[\/zmg\]/",((strpos($body,'[/crypt]')) ? $_POST['media_str'] : $body),$match))
- $images = $match[1];
+ if(preg_match_all("/\[zmg\=([0-9]*)x([0-9]*)\](.*?)\[\/zmg\]/",((strpos($body,'[/crypt]')) ? $_POST['media_str'] : $body),$match))
+ $images = $match[3];
$match = false;
@@ -173,7 +174,7 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto='
'revision' => $r['data']['revision']
);
}
- $body = str_replace($match[1],'',$body);
+ $body = trim(str_replace($match[1],'',$body));
}
}
@@ -230,7 +231,7 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto='
dbesc($image_uri),
intval($channel['channel_id']),
dbesc('<' . $channel['channel_hash'] . '>')
- );
+ );
$r = q("UPDATE attach SET allow_cid = '%s' WHERE hash = '%s' AND is_photo = 1 and uid = %d and allow_cid = '%s'",
dbesc('<' . $recipient . '>'),
dbesc($image_uri),
@@ -239,7 +240,7 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto='
);
}
}
-
+
if($attaches) {
foreach($attaches as $attach) {
$hash = substr($attach,0,strpos($attach,','));
@@ -505,3 +506,4 @@ function private_messages_fetch_conversation($channel_id, $messageitem_id, $upda
return $messages;
}
+
diff --git a/include/network.php b/include/network.php
index 026f5ee0a..68452c3d1 100644
--- a/include/network.php
+++ b/include/network.php
@@ -1709,3 +1709,190 @@ function do_delivery($deliveries) {
}
+
+
+function get_site_info() {
+
+ global $db;
+ global $a;
+
+ $register_policy = Array('REGISTER_CLOSED', 'REGISTER_APPROVE', 'REGISTER_OPEN');
+ $directory_mode = Array('DIRECTORY_MODE_NORMAL', 'DIRECTORY_MODE_SECONDARY','DIRECTORY_MODE_PRIMARY', 256 => 'DIRECTORY_MODE_STANDALONE');
+
+ $sql_extra = '';
+
+ $r = q("select * from channel left join account on account_id = channel_account_id where ( account_roles & 4096 )>0 and account_default_channel = channel_id");
+
+
+ if($r) {
+ $admin = array();
+ foreach($r as $rr) {
+ if($rr['channel_pageflags'] & PAGE_HUBADMIN)
+ $admin[] = array( 'name' => $rr['channel_name'], 'address' => $rr['channel_address'] . '@' . get_app()->get_hostname(), 'channel' => z_root() . '/channel/' . $rr['channel_address']);
+ }
+ if(! $admin) {
+ foreach($r as $rr) {
+ $admin[] = array( 'name' => $rr['channel_name'], 'address' => $rr['channel_address'] . '@' . get_app()->get_hostname(), 'channel' => z_root() . '/channel/' . $rr['channel_address']);
+ }
+ }
+ }
+ else {
+ $admin = false;
+ }
+
+ $def_service_class = get_config('system','default_service_class');
+ if($def_service_class)
+ $service_class = get_config('service_class',$def_service_class);
+ else
+ $service_class = false;
+
+ $visible_plugins = array();
+ if(is_array($a->plugins) && count($a->plugins)) {
+ $r = q("select * from addon where hidden = 0");
+ if(count($r))
+ foreach($r as $rr)
+ $visible_plugins[] = $rr['name'];
+ }
+ sort($visible_plugins);
+
+ if(@is_dir('.git') && function_exists('shell_exec'))
+ $commit = trim(@shell_exec('git log -1 --format="%h"'));
+ if(! isset($commit) || strlen($commit) > 16)
+ $commit = '';
+
+ $site_info = get_config('system','info');
+ $site_name = get_config('system','sitename');
+ if(! get_config('system','hidden_version_siteinfo')) {
+ $version = RED_VERSION;
+ $tag = get_std_version();
+
+ if(@is_dir('.git') && function_exists('shell_exec')) {
+ $commit = trim( @shell_exec('git log -1 --format="%h"'));
+// if(! get_config('system','hidden_tag_siteinfo'))
+// $tag = trim( @shell_exec('git describe --tags --abbrev=0'));
+// else
+// $tag = '';
+ }
+ if(! isset($commit) || strlen($commit) > 16)
+ $commit = '';
+ }
+ else {
+ $version = $commit = '';
+ }
+
+ //Statistics
+ $channels_total_stat = intval(get_config('system','channels_total_stat'));
+ $channels_active_halfyear_stat = intval(get_config('system','channels_active_halfyear_stat'));
+ $channels_active_monthly_stat = intval(get_config('system','channels_active_monthly_stat'));
+ $local_posts_stat = intval(get_config('system','local_posts_stat'));
+ $hide_in_statistics = intval(get_config('system','hide_in_statistics'));
+ $site_expire = intval(get_config('system', 'default_expire_days'));
+
+
+ $data = Array(
+ 'version' => $version,
+ 'version_tag' => $tag,
+ 'commit' => $commit,
+ 'url' => z_root(),
+ 'plugins' => $visible_plugins,
+ 'register_policy' => $register_policy[get_config('system','register_policy')],
+ 'directory_mode' => $directory_mode[get_config('system','directory_mode')],
+ 'language' => get_config('system','language'),
+ 'rss_connections' => get_config('system','feed_contacts'),
+ 'expiration' => $site_expire,
+ 'default_service_restrictions' => $service_class,
+ 'admin' => $admin,
+ 'site_name' => (($site_name) ? $site_name : ''),
+ 'platform' => PLATFORM_NAME,
+ 'dbdriver' => $db->getdriver(),
+ 'lastpoll' => get_config('system','lastpoll'),
+ 'info' => (($site_info) ? $site_info : ''),
+ 'channels_total' => $channels_total_stat,
+ 'channels_active_halfyear' => $channels_active_halfyear_stat,
+ 'channels_active_monthly' => $channels_active_monthly_stat,
+ 'local_posts' => $local_posts_stat,
+ 'hide_in_statistics' => $hide_in_statistics
+ );
+ return $data;
+}
+
+
+
+function check_siteallowed($url) {
+
+ $retvalue = true;
+
+
+ $arr = array('url' => $url);
+ call_hooks('check_siteallowed',$arr);
+
+ if(array_key_exists('allowed',$arr))
+ return $arr['allowed'];
+
+ $bl1 = get_config('system','whitelisted_sites');
+ if(is_array($bl1) && $bl1) {
+ foreach($bl1 as $bl) {
+ if($bl1 === '*')
+ $retvalue = true;
+ if($bl && strpos($url,$bl) !== false)
+ return true;
+ }
+ }
+ $bl1 = get_config('system','blacklisted_sites');
+ if(is_array($bl1) && $bl1) {
+ foreach($bl1 as $bl) {
+ if($bl1 === '*')
+ $retvalue = false;
+ if($bl && strpos($url,$bl) !== false) {
+ return false;
+ }
+ }
+ }
+ return $retvalue;
+}
+
+function check_channelallowed($hash) {
+
+ $retvalue = true;
+
+ $arr = array('hash' => $hash);
+ call_hooks('check_channelallowed',$arr);
+
+ if(array_key_exists('allowed',$arr))
+ return $arr['allowed'];
+
+ $bl1 = get_config('system','whitelisted_channels');
+ if(is_array($bl1) && $bl1) {
+ foreach($bl1 as $bl) {
+ if($bl1 === '*')
+ $retvalue = true;
+ if($bl && strpos($hash,$bl) !== false)
+ return true;
+ }
+ }
+ $bl1 = get_config('system','blacklisted_channels');
+ if(is_array($bl1) && $bl1) {
+ foreach($bl1 as $bl) {
+ if($bl1 === '*')
+ $retvalue = false;
+ if($bl && strpos($hash,$bl) !== false) {
+ return false;
+ }
+ }
+ }
+ return $retvalue;
+}
+
+function deliverable_singleton($xchan) {
+ $r = q("select abook_instance from abook where abook_xchan = '%s' limit 1",
+ dbesc($xchan['xchan_hash'])
+ );
+ if($r) {
+ if(! $r[0]['abook_instance'])
+ return true;
+ if(strpos($r[0]['abook_instance'],z_root()) !== false)
+ return true;
+ }
+ return false;
+}
+
diff --git a/include/notifier.php b/include/notifier.php
index b7830285a..66b6160e4 100644
--- a/include/notifier.php
+++ b/include/notifier.php
@@ -57,6 +57,8 @@ require_once('include/html2plain.php');
* purge_all channel_id
* expire channel_id
* relay item_id (item was relayed to owner, we will deliver it as owner)
+ * single_activity item_id (deliver to a singleton network from the appropriate clone)
+ * single_mail mail_id (deliver to a singleton network from the appropriate clone)
* location channel_id
* request channel_id xchan_hash message_id
* rating xlink_id
diff --git a/include/oauth.php b/include/oauth.php
index 80336f906..4e5e4ecf8 100644
--- a/include/oauth.php
+++ b/include/oauth.php
@@ -1,4 +1,5 @@
<?php /** @file */
+
/**
* OAuth server
* Based on oauth2-php <http://code.google.com/p/oauth2-php/>
@@ -9,16 +10,17 @@ define('REQUEST_TOKEN_DURATION', 300);
define('ACCESS_TOKEN_DURATION', 31536000);
require_once("library/OAuth1.php");
-require_once("library/oauth2-php/lib/OAuth2.inc");
-class FKOAuthDataStore extends OAuthDataStore {
- function gen_token(){
+//require_once("library/oauth2-php/lib/OAuth2.inc");
+
+class ZotOAuthDataStore extends OAuthDataStore {
+
+ function gen_token(){
return md5(base64_encode(pack('N6', mt_rand(), mt_rand(), mt_rand(), mt_rand(), mt_rand(), uniqid())));
- }
+ }
- function lookup_consumer($consumer_key) {
- logger(__function__.":".$consumer_key);
-// echo "<pre>"; var_dump($consumer_key); killme();
+ function lookup_consumer($consumer_key) {
+ logger('consumer_key: ' . $consumer_key, LOGGER_DEBUG);
$r = q("SELECT client_id, pw, redirect_uri FROM clients WHERE client_id = '%s'",
dbesc($consumer_key)
@@ -29,10 +31,11 @@ class FKOAuthDataStore extends OAuthDataStore {
return new OAuthConsumer($r[0]['client_id'],$r[0]['pw'],$r[0]['redirect_uri']);
}
return null;
- }
+ }
- function lookup_token($consumer, $token_type, $token) {
- logger(__function__.":".$consumer.", ". $token_type.", ".$token);
+ function lookup_token($consumer, $token_type, $token) {
+
+ logger(__function__.":".$consumer.", ". $token_type.", ".$token, LOGGER_DEBUG);
$r = q("SELECT id, secret, scope, expires, uid FROM tokens WHERE client_id = '%s' AND scope = '%s' AND id = '%s'",
dbesc($consumer->key),
@@ -48,10 +51,9 @@ class FKOAuthDataStore extends OAuthDataStore {
return $ot;
}
return null;
- }
+ }
- function lookup_nonce($consumer, $token, $nonce, $timestamp) {
-// echo __file__.":".__line__."<pre>"; var_dump($consumer,$key); killme();
+ function lookup_nonce($consumer, $token, $nonce, $timestamp) {
$r = q("SELECT id, secret FROM tokens WHERE client_id = '%s' AND id = '%s' AND expires = %d",
dbesc($consumer->key),
@@ -62,10 +64,12 @@ class FKOAuthDataStore extends OAuthDataStore {
if (count($r))
return new OAuthToken($r[0]['id'],$r[0]['secret']);
return null;
- }
+ }
+
+ function new_request_token($consumer, $callback = null) {
+
+ logger(__function__.":".$consumer.", ". $callback, LOGGER_DEBUG);
- function new_request_token($consumer, $callback = null) {
- logger(__function__.":".$consumer.", ". $callback);
$key = $this->gen_token();
$sec = $this->gen_token();
@@ -82,29 +86,31 @@ class FKOAuthDataStore extends OAuthDataStore {
'request',
time()+intval(REQUEST_TOKEN_DURATION));
- if (!$r) return null;
+ if(! $r)
+ return null;
return new OAuthToken($key,$sec);
- }
+ }
- function new_access_token($token, $consumer, $verifier = null) {
- logger(__function__.":".$token.", ". $consumer.", ". $verifier);
-
- // return a new access token attached to this consumer
- // for the user associated with this token if the request token
- // is authorized
- // should also invalidate the request token
-
- $ret=Null;
+ function new_access_token($token, $consumer, $verifier = null) {
+
+ logger(__function__.":".$token.", ". $consumer.", ". $verifier, LOGGER_DEBUG);
- // get user for this verifier
- $uverifier = get_config("oauth", $verifier);
- logger(__function__.":".$verifier.",".$uverifier);
- if (is_null($verifier) || ($uverifier!==false)){
+ // return a new access token attached to this consumer
+ // for the user associated with this token if the request token
+ // is authorized
+ // should also invalidate the request token
+
+ $ret=Null;
+
+ // get user for this verifier
+ $uverifier = get_config("oauth", $verifier);
+ logger(__function__.":".$verifier.",".$uverifier, LOGGER_DEBUG);
+ if (is_null($verifier) || ($uverifier!==false)) {
- $key = $this->gen_token();
- $sec = $this->gen_token();
+ $key = $this->gen_token();
+ $sec = $this->gen_token();
- $r = q("INSERT INTO tokens (id, secret, client_id, scope, expires, uid) VALUES ('%s','%s','%s','%s', %d, %d)",
+ $r = q("INSERT INTO tokens (id, secret, client_id, scope, expires, uid) VALUES ('%s','%s','%s','%s', %d, %d)",
dbesc($key),
dbesc($sec),
dbesc($consumer->key),
@@ -112,81 +118,70 @@ class FKOAuthDataStore extends OAuthDataStore {
time()+intval(ACCESS_TOKEN_DURATION),
intval($uverifier));
- if ($r)
- $ret = new OAuthToken($key,$sec);
- }
+ if ($r)
+ $ret = new OAuthToken($key,$sec);
+ }
- q("DELETE FROM tokens WHERE id='%s'", $token->key);
+ q("DELETE FROM tokens WHERE id='%s'", $token->key);
- if (!is_null($ret) && $uverifier!==false){
- del_config("oauth", $verifier);
- /* $apps = get_pconfig($uverifier, "oauth", "apps");
- if ($apps===false) $apps=array();
- $apps[] = $consumer->key;
- set_pconfig($uverifier, "oauth", "apps", $apps);*/
+ if (!is_null($ret) && $uverifier!==false) {
+ del_config("oauth", $verifier);
+
+ // $apps = get_pconfig($uverifier, "oauth", "apps");
+ // if ($apps===false) $apps=array();
+ // $apps[] = $consumer->key;
+ // set_pconfig($uverifier, "oauth", "apps", $apps);
+ }
+ return $ret;
}
-
- return $ret;
-
- }
}
-class FKOAuth1 extends OAuthServer {
+class ZotOAuth1 extends OAuthServer {
function __construct() {
- parent::__construct(new FKOAuthDataStore());
+ parent::__construct(new ZotOAuthDataStore());
$this->add_signature_method(new OAuthSignatureMethod_PLAINTEXT());
$this->add_signature_method(new OAuthSignatureMethod_HMAC_SHA1());
}
function loginUser($uid){
- logger("RedOAuth1::loginUser $uid");
- $a = get_app();
+
+ logger("ZotOAuth1::loginUser $uid");
+
$r = q("SELECT * FROM channel WHERE channel_id = %d LIMIT 1",
intval($uid)
);
if(count($r)){
$record = $r[0];
} else {
- logger('FKOAuth1::loginUser failure: ' . print_r($_SERVER,true), LOGGER_DEBUG);
- header('HTTP/1.0 401 Unauthorized');
- die('This api requires login');
+ logger('ZotOAuth1::loginUser failure: ' . print_r($_SERVER,true), LOGGER_DEBUG);
+ header('HTTP/1.0 401 Unauthorized');
+ echo('This api requires login');
+ killme();
}
$_SESSION['uid'] = $record['channel_id'];
- $_SESSION['theme'] = $record['channel_theme'];
- $_SESSION['account_id'] = $record['channel_account_id'];
- $_SESSION['mobile_theme'] = get_pconfig($record['channel_id'], 'system', 'mobile_theme');
- $_SESSION['authenticated'] = 1;
- $_SESSION['my_url'] = $a->get_baseurl() . '/channel/' . $record['channel_address'];
$_SESSION['addr'] = $_SERVER['REMOTE_ADDR'];
- $_SESSION['allow_api'] = true;
+
$x = q("select * from account where account_id = %d limit 1",
intval($record['channel_account_id'])
);
- if($x)
- $a->account = $x[0];
-
- change_channel($record['channel_id']);
-
- $a->channel = $record;
-
- if(strlen($a->channel['channel_timezone'])) {
- date_default_timezone_set($a->channel['channel_timezone']);
+ if($x) {
+ require_once('include/security.php');
+ authenticate_success($x[0],true,false,true,true);
+ $_SESSION['allow_api'] = true;
}
-
-// q("UPDATE `user` SET `login_date` = '%s' WHERE `uid` = %d LIMIT 1",
-// dbesc(datetime_convert()),
-// intval($_SESSION['uid'])
-// );
-//
-// call_hooks('logged_in', $a->user);
}
}
+
/*
+ *
+
+ not yet used
+
class FKOAuth2 extends OAuth2 {
private function db_secret($client_secret){
diff --git a/include/plugin.php b/include/plugin.php
index 8749f3fbf..1f4d60736 100755
--- a/include/plugin.php
+++ b/include/plugin.php
@@ -495,6 +495,15 @@ function format_css_if_exists($source) {
return '<link rel="stylesheet" href="' . script_path() . '/' . $path . '" type="text/css" media="' . $source[1] . '">' . "\r\n";
}
+/*
+ * This basically calculates the baseurl. We have other functions to do that, but
+ * there was an issue with script paths and mixed-content whose details are arcane
+ * and perhaps lost in the message archives. The short answer is that we're ignoring
+ * the URL which we are "supposed" to use, and generating script paths relative to
+ * the URL which we are currently using; in order to ensure they are found and aren't
+ * blocked due to mixed content issues.
+ */
+
function script_path() {
if(x($_SERVER,'HTTPS') && $_SERVER['HTTPS'])
$scheme = 'https';
@@ -616,3 +625,14 @@ function get_markup_template($s, $root = '') {
$template = $t->get_markup_template($s, $root);
return $template;
}
+
+// return the standardised version. Since we can't easily compare
+// before the STD_VERSION definition was applied, we have to treat
+// all prior release versions the same. You can dig through them
+// with other means (such as RED_VERSION) if necessary.
+
+function get_std_version() {
+ if(defined('STD_VERSION'))
+ return STD_VERSION;
+ return '0.0.0';
+}
diff --git a/include/security.php b/include/security.php
index 9a25d9e0e..d4ebe0024 100644
--- a/include/security.php
+++ b/include/security.php
@@ -93,6 +93,7 @@ function change_channel($change_channel) {
$ret = false;
if($change_channel) {
+
$r = q("select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel_id = %d and channel_account_id = %d and channel_removed = 0 limit 1",
intval($change_channel),
intval(get_account_id())
@@ -136,14 +137,14 @@ function change_channel($change_channel) {
}
/**
- * @brief Creates an addiontal SQL where statement to check permissions.
+ * @brief Creates an additional SQL where statement to check permissions.
*
* @param int $owner_id
- * @param bool $remote_verified default false, not used at all
- * @param string $groups this param is not used at all
+ * @param bool $remote_observer - if unset use current observer
*
* @return string additional SQL where statement
*/
+
function permissions_sql($owner_id, $remote_observer = null) {
$local_channel = local_channel();
@@ -208,8 +209,7 @@ function permissions_sql($owner_id, $remote_observer = null) {
* @brief Creates an addiontal SQL where statement to check permissions for an item.
*
* @param int $owner_id
- * @param bool $remote_verified default false, not used at all
- * @param string $groups this param is not used at all
+ * @param bool $remote_observer, use current observer if unset
*
* @return string additional SQL where statement
*/
@@ -400,11 +400,9 @@ function check_form_security_token_ForbiddenOnErr($typename = '', $formname = 'f
}
-// Returns an array of group id's this contact is a member of.
-// This array will only contain group id's related to the uid of this
-// DFRN contact. They are *not* neccessarily unique across the entire site.
+// Returns an array of group hash id's on this entire site (across all channels) that this connection is a member of.
+// var $contact_id = xchan_hash of connection
-if(! function_exists('init_groups_visitor')) {
function init_groups_visitor($contact_id) {
$groups = array();
$r = q("SELECT hash FROM `groups` left join group_member on groups.id = group_member.gid WHERE xchan = '%s' ",
@@ -415,7 +413,7 @@ function init_groups_visitor($contact_id) {
$groups[] = $rr['hash'];
}
return $groups;
-}}
+}
diff --git a/include/widgets.php b/include/widgets.php
index a3f7444ec..4b14d6c94 100644
--- a/include/widgets.php
+++ b/include/widgets.php
@@ -666,7 +666,7 @@ function widget_eventsmenu($arr) {
if (! local_channel())
return;
- return replace_macros(get_markup_template('events_side.tpl'), array(
+ return replace_macros(get_markup_template('events_menu_side.tpl'), array(
'$title' => t('Events Menu'),
'$day' => t('Day View'),
'$week' => t('Week View'),
@@ -677,6 +677,18 @@ function widget_eventsmenu($arr) {
));
}
+function widget_eventstools($arr) {
+ if (! local_channel())
+ return;
+
+ return replace_macros(get_markup_template('events_tools_side.tpl'), array(
+ '$title' => t('Events Tools'),
+ '$export' => t('Export Calendar'),
+ '$import' => t('Import Calendar'),
+ '$submit' => t('Submit')
+ ));
+}
+
function widget_design_tools($arr) {
$a = get_app();
@@ -1136,7 +1148,7 @@ function widget_forums($arr) {
foreach($r1 as $rr) {
if($unseen && (! intval($rr['unseen'])))
continue;
- $o .= '<li><span class="pull-right">' . ((intval($rr['unseen'])) ? intval($rr['unseen']) : '') . '</span><a href="network?f=&pf=1&cid=' . $rr['abook_id'] . '" ><img src="' . $rr['xchan_photo_s'] . '" style="width: 16px; height: 16px;" /> ' . $rr['xchan_name'] . '</a></li>';
+ $o .= '<li><a href="network?f=&pf=1&cid=' . $rr['abook_id'] . '" ><span class="badge pull-right">' . ((intval($rr['unseen'])) ? intval($rr['unseen']) : '') . '</span><img src="' . $rr['xchan_photo_s'] . '" style="width: 16px; height: 16px;" /> ' . $rr['xchan_name'] . '</a></li>';
}
$o .= '</ul></div>';
}
@@ -1147,6 +1159,8 @@ function widget_forums($arr) {
function widget_tasklist($arr) {
+ if (! local_channel())
+ return;
require_once('include/event.php');
$o .= '<script>var tasksShowAll = 0; $(document).ready(function() { tasksFetch(); $("#tasklist-new-form").submit(function(event) { event.preventDefault(); $.post( "tasks/new", $("#tasklist-new-form").serialize(), function(data) { tasksFetch(); $("#tasklist-new-summary").val(""); } ); return false; } )});</script>';
@@ -1285,7 +1299,6 @@ function widget_album($args) {
//edit album name
$album_edit = null;
-
$photos = array();
if($r) {
$twist = 'rotright';
@@ -1324,6 +1337,7 @@ function widget_album($args) {
$o .= replace_macros($tpl, array(
'$photos' => $photos,
'$album' => (($title) ? $title : $album),
+ '$album_id' => rand(),
'$album_edit' => array(t('Edit Album'), $album_edit),
'$can_post' => false,
'$upload' => array(t('Upload'), z_root() . '/photos/' . get_app()->profile['channel_address'] . '/upload/' . bin2hex($album)),
diff --git a/include/zot.php b/include/zot.php
index c0d537eb9..390407e4e 100644
--- a/include/zot.php
+++ b/include/zot.php
@@ -554,18 +554,8 @@ function zot_gethub($arr,$multiple = false) {
if($arr['guid'] && $arr['guid_sig'] && $arr['url'] && $arr['url_sig']) {
- $blacklisted = false;
- $bl1 = get_config('system','blacklisted_sites');
- if(is_array($bl1) && $bl1) {
- foreach($bl1 as $bl) {
- if($bl && strpos($arr['url'],$bl) !== false) {
- $blacklisted = true;
- break;
- }
- }
- }
- if($blacklisted) {
- logger('zot_gethub: blacklisted site: ' . $arr['url']);
+ if(! check_siteallowed($arr['url'])) {
+ logger('blacklisted site: ' . $arr['url']);
return null;
}
@@ -1246,6 +1236,10 @@ function zot_import($arr, $sender_url) {
$no_dups = array();
if($deliveries) {
foreach($deliveries as $d) {
+ if(! is_array($d)) {
+ logger('Delivery hash array is not an array: ' . print_r($d,true));
+ continue;
+ }
if(! in_array($d['hash'],$no_dups))
$no_dups[] = $d['hash'];
}
@@ -1617,6 +1611,14 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $
$channel = $r[0];
$DR->addto_recipient($channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>');
+ /* blacklisted channels get a permission denied, no special message to tip them off */
+
+ if(! check_channelallowed($sender['hash'])) {
+ $DR->update('permission denied');
+ $result[] = $DR->get();
+ continue;
+ }
+
/**
* @FIXME: Somehow we need to block normal message delivery from our clones, as the delivered
* message doesn't have ACL information in it as the cloned copy does. That copy
@@ -2088,6 +2090,14 @@ function process_mail_delivery($sender, $arr, $deliveries) {
$channel = $r[0];
$DR->addto_recipient($channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>');
+ /* blacklisted channels get a permission denied, no special message to tip them off */
+
+ if(! check_channelallowed($sender['hash'])) {
+ $DR->update('permission denied');
+ $result[] = $DR->get();
+ continue;
+ }
+
if(! perm_is_allowed($channel['channel_id'],$sender['hash'],'post_mail')) {
logger("permission denied for mail delivery {$channel['channel_id']}");
$DR->update('permission denied');
@@ -3484,13 +3494,13 @@ function import_author_zot($x) {
* @param array $data
* @return array
*/
-function zot_process_message_request($data) {
+function zot_reply_message_request($data) {
$ret = array('success' => false);
if (! $data['message_id']) {
$ret['message'] = 'no message_id';
logger('no message_id');
- return $ret;
+ json_return_and_die($ret);
}
$sender = $data['sender'];
@@ -3508,7 +3518,7 @@ function zot_process_message_request($data) {
if (! $c) {
logger('recipient channel not found.');
$ret['message'] .= 'recipient not found.' . EOL;
- return $ret;
+ json_return_and_die($ret);
}
/*
@@ -3526,7 +3536,7 @@ function zot_process_message_request($data) {
);
if (! $r) {
logger('no hubs');
- return $ret;
+ json_return_and_die($ret);
}
$hubs = $r;
@@ -3567,8 +3577,7 @@ function zot_process_message_request($data) {
}
}
$ret['success'] = true;
-
- return $ret;
+ json_return_and_die($ret);
}
@@ -3789,6 +3798,7 @@ function zotinfo($arr) {
$ret['site'] = array();
$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';
$dirmode = get_config('system','directory_mode');
if(($dirmode === false) || ($dirmode == DIRECTORY_MODE_NORMAL))
@@ -3979,3 +3989,421 @@ function delivery_report_is_storable($dr) {
}
+
+function update_hub_connected($hub,$sitekey = '') {
+
+ if($sitekey) {
+
+ /*
+ * This hub has now been proven to be valid.
+ * Any hub with the same URL and a different sitekey cannot be valid.
+ * Get rid of them (mark them deleted). There's a good chance they were re-installs.
+ */
+
+ q("update hubloc set hubloc_deleted = 1, hubloc_error = 1 where hubloc_url = '%s' and hubloc_sitekey != '%s' ",
+ dbesc($hub['hubloc_url']),
+ dbesc($sitekey)
+ );
+
+ }
+ else {
+ $sitekey = $hub['sitekey'];
+ }
+
+ // $sender['sitekey'] is a new addition to the protcol to distinguish
+ // hublocs coming from re-installed sites. Older sites will not provide
+ // this field and we have to still mark them valid, since we can't tell
+ // if this hubloc has the same sitekey as the packet we received.
+
+
+ // Update our DB to show when we last communicated successfully with this hub
+ // This will allow us to prune dead hubs from using up resources
+
+ $r = q("update hubloc set hubloc_connected = '%s' where hubloc_id = %d and hubloc_sitekey = '%s' ",
+ dbesc(datetime_convert()),
+ intval($hub['hubloc_id']),
+ dbesc($sitekey)
+ );
+
+ // a dead hub came back to life - reset any tombstones we might have
+
+ if(intval($hub['hubloc_error'])) {
+ q("update hubloc set hubloc_error = 0 where hubloc_id = %d and hubloc_sitekey = '%s' ",
+ intval($hub['hubloc_id']),
+ dbesc($sitekey)
+ );
+ if(intval($r[0]['hubloc_orphancheck'])) {
+ q("update hubloc set hubloc_orhpancheck = 0 where hubloc_id = %d and hubloc_sitekey = '%s' ",
+ intval($hub['hubloc_id']),
+ dbesc($sitekey)
+ );
+ }
+ q("update xchan set xchan_orphan = 0 where xchan_orphan = 1 and xchan_hash = '%s'",
+ dbesc($hub['hubloc_hash'])
+ );
+ }
+
+ return $hub['hubloc_url'];
+}
+
+
+function zot_reply_ping() {
+
+ $ret = array('success'=> false);
+
+ // Useful to get a health check on a remote site.
+ // This will let us know if any important communication details
+ // that we may have stored are no longer valid, regardless of xchan details.
+ logger('POST: got ping send pong now back: ' . z_root() , LOGGER_DEBUG );
+
+ $ret['success'] = true;
+ $ret['site'] = array();
+ $ret['site']['url'] = z_root();
+ $ret['site']['url_sig'] = base64url_encode(rsa_sign(z_root(),get_config('system','prvkey')));
+ $ret['site']['sitekey'] = get_config('system','pubkey');
+ json_return_and_die($ret);
+}
+
+function zot_reply_pickup($data) {
+
+ $ret = array('success'=> false);
+
+ /*
+ * The 'pickup' message arrives with a tracking ID which is associated with a particular outq_hash
+ * First verify that that the returned signatures verify, then check that we have an outbound queue item
+ * with the correct hash.
+ * If everything verifies, find any/all outbound messages in the queue for this hubloc and send them back
+ */
+
+ if((! $data['secret']) || (! $data['secret_sig'])) {
+ $ret['message'] = 'no verification signature';
+ logger('mod_zot: pickup: ' . $ret['message'], LOGGER_DEBUG);
+ json_return_and_die($ret);
+ }
+
+ $r = q("select distinct hubloc_sitekey from hubloc where hubloc_url = '%s' and hubloc_callback = '%s' and hubloc_sitekey != '' group by hubloc_sitekey ",
+ dbesc($data['url']),
+ dbesc($data['callback'])
+ );
+ if(! $r) {
+ $ret['message'] = 'site not found';
+ logger('mod_zot: pickup: ' . $ret['message']);
+ json_return_and_die($ret);
+ }
+
+ foreach ($r as $hubsite) {
+
+ // verify the url_sig
+ // If the server was re-installed at some point, there could be multiple hubs with the same url and callback.
+ // Only one will have a valid key.
+
+ $forgery = true;
+ $secret_fail = true;
+
+ $sitekey = $hubsite['hubloc_sitekey'];
+
+ logger('mod_zot: Checking sitekey: ' . $sitekey, LOGGER_DATA);
+
+ if(rsa_verify($data['callback'],base64url_decode($data['callback_sig']),$sitekey)) {
+ $forgery = false;
+ }
+ if(rsa_verify($data['secret'],base64url_decode($data['secret_sig']),$sitekey)) {
+ $secret_fail = false;
+ }
+ if((! $forgery) && (! $secret_fail))
+ break;
+ }
+
+ if($forgery) {
+ $ret['message'] = 'possible site forgery';
+ logger('mod_zot: pickup: ' . $ret['message']);
+ json_return_and_die($ret);
+ }
+
+ if($secret_fail) {
+ $ret['message'] = 'secret validation failed';
+ logger('mod_zot: pickup: ' . $ret['message']);
+ json_return_and_die($ret);
+ }
+
+ /*
+ * If we made it to here, the signatures verify, but we still don't know if the tracking ID is valid.
+ * It wouldn't be an error if the tracking ID isn't found, because we may have sent this particular
+ * queue item with another pickup (after the tracking ID for the other pickup was verified).
+ */
+
+ $r = q("select outq_posturl from outq where outq_hash = '%s' and outq_posturl = '%s' limit 1",
+ dbesc($data['secret']),
+ dbesc($data['callback'])
+ );
+ if(! $r) {
+ $ret['message'] = 'nothing to pick up';
+ logger('mod_zot: pickup: ' . $ret['message']);
+ json_return_and_die($ret);
+ }
+
+ /*
+ * Everything is good if we made it here, so find all messages that are going to this location
+ * and send them all.
+ */
+
+ $r = q("select * from outq where outq_posturl = '%s'",
+ dbesc($data['callback'])
+ );
+ if($r) {
+ logger('mod_zot: successful pickup message received from ' . $data['callback'] . ' ' . count($r) . ' message(s) picked up', LOGGER_DEBUG);
+
+ $ret['success'] = true;
+ $ret['pickup'] = array();
+ foreach($r as $rr) {
+ if($rr['outq_msg']) {
+ $x = json_decode($rr['outq_msg'],true);
+
+ if(! $x)
+ continue;
+
+ if(array_key_exists('message_list',$x)) {
+ foreach($x['message_list'] as $xx) {
+ $ret['pickup'][] = array('notify' => json_decode($rr['outq_notify'],true),'message' => $xx);
+ }
+ }
+ else
+ $ret['pickup'][] = array('notify' => json_decode($rr['outq_notify'],true),'message' => $x);
+ $x = q("delete from outq where outq_hash = '%s'",
+ dbesc($rr['outq_hash'])
+ );
+ }
+ }
+ }
+
+ $encrypted = crypto_encapsulate(json_encode($ret),$sitekey);
+ json_return_and_die($encrypted);
+
+ /* pickup: end */
+}
+
+
+
+function zot_reply_auth_check($data,$encrypted_packet) {
+
+ $ret = array('success' => false);
+
+ /*
+ * Requestor visits /magic/?dest=somewhere on their own site with a browser
+ * magic redirects them to $destsite/post [with auth args....]
+ * $destsite sends an auth_check packet to originator site
+ * The auth_check packet is handled here by the originator's site
+ * - the browser session is still waiting
+ * inside $destsite/post for everything to verify
+ * If everything checks out we'll return a token to $destsite
+ * and then $destsite will verify the token, authenticate the browser
+ * session and then redirect to the original destination.
+ * If authentication fails, the redirection to the original destination
+ * will still take place but without authentication.
+ */
+ logger('mod_zot: auth_check', LOGGER_DEBUG);
+
+ if (! $encrypted_packet) {
+ logger('mod_zot: auth_check packet was not encrypted.');
+ $ret['message'] .= 'no packet encryption' . EOL;
+ json_return_and_die($ret);
+ }
+
+ $arr = $data['sender'];
+ $sender_hash = make_xchan_hash($arr['guid'],$arr['guid_sig']);
+
+ // garbage collect any old unused notifications
+
+ // This was and should be 10 minutes but my hosting provider has time lag between the DB and
+ // the web server. We should probably convert this to webserver time rather than DB time so
+ // that the different clocks won't affect it and allow us to keep the time short.
+
+ q("delete from verify where type = 'auth' and created < %s - INTERVAL %s",
+ db_utcnow(), db_quoteinterval('30 MINUTE')
+ );
+
+ $y = q("select xchan_pubkey from xchan where xchan_hash = '%s' limit 1",
+ dbesc($sender_hash)
+ );
+
+ // We created a unique hash in mod/magic.php when we invoked remote auth, and stored it in
+ // the verify table. It is now coming back to us as 'secret' and is signed by a channel at the other end.
+ // First verify their signature. We will have obtained a zot-info packet from them as part of the sender
+ // verification.
+
+ if ((! $y) || (! rsa_verify($data['secret'], base64url_decode($data['secret_sig']),$y[0]['xchan_pubkey']))) {
+ logger('mod_zot: auth_check: sender not found or secret_sig invalid.');
+ $ret['message'] .= 'sender not found or sig invalid ' . print_r($y,true) . EOL;
+ json_return_and_die($ret);
+ }
+
+ // There should be exactly one recipient, the original auth requestor
+
+ $ret['message'] .= 'recipients ' . print_r($recipients,true) . EOL;
+
+ if ($data['recipients']) {
+
+ $arr = $data['recipients'][0];
+ $recip_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']);
+ $c = q("select channel_id, channel_account_id, channel_prvkey from channel where channel_hash = '%s' limit 1",
+ dbesc($recip_hash)
+ );
+ if (! $c) {
+ logger('mod_zot: auth_check: recipient channel not found.');
+ $ret['message'] .= 'recipient not found.' . EOL;
+ json_return_and_die($ret);
+ }
+
+ $confirm = base64url_encode(rsa_sign($data['secret'] . $recip_hash,$c[0]['channel_prvkey']));
+
+ // This additionally checks for forged sites since we already stored the expected result in meta
+ // and we've already verified that this is them via zot_gethub() and that their key signed our token
+
+ $z = q("select id from verify where channel = %d and type = 'auth' and token = '%s' and meta = '%s' limit 1",
+ intval($c[0]['channel_id']),
+ dbesc($data['secret']),
+ dbesc($data['sender']['url'])
+ );
+ if (! $z) {
+ logger('mod_zot: auth_check: verification key not found.');
+ $ret['message'] .= 'verification key not found' . EOL;
+ json_return_and_die($ret);
+ }
+ $r = q("delete from verify where id = %d",
+ intval($z[0]['id'])
+ );
+
+ $u = q("select account_service_class from account where account_id = %d limit 1",
+ intval($c[0]['channel_account_id'])
+ );
+
+ logger('mod_zot: auth_check: success', LOGGER_DEBUG);
+ $ret['success'] = true;
+ $ret['confirm'] = $confirm;
+ if ($u && $u[0]['account_service_class'])
+ $ret['service_class'] = $u[0]['account_service_class'];
+
+ // Set "do not track" flag if this site or this channel's profile is restricted
+ // in some way
+
+ if (intval(get_config('system','block_public')))
+ $ret['DNT'] = true;
+ if (! perm_is_allowed($c[0]['channel_id'],'','view_profile'))
+ $ret['DNT'] = true;
+ if (get_pconfig($c[0]['channel_id'],'system','do_not_track'))
+ $ret['DNT'] = true;
+ if (get_pconfig($c[0]['channel_id'],'system','hide_online_status'))
+ $ret['DNT'] = true;
+
+ json_return_and_die($ret);
+ }
+ json_return_and_die($ret);
+}
+
+
+function zot_reply_purge($sender,$recipients) {
+
+ $ret = array('success' => false);
+
+ if ($recipients) {
+ // basically this means "unfriend"
+ foreach ($recipients as $recip) {
+ $r = q("select channel.*,xchan.* from channel
+ left join xchan on channel_hash = xchan_hash
+ where channel_guid = '%s' and channel_guid_sig = '%s' limit 1",
+ dbesc($recip['guid']),
+ dbesc($recip['guid_sig'])
+ );
+ if ($r) {
+ $r = q("select abook_id from abook where uid = %d and abook_xchan = '%s' limit 1",
+ intval($r[0]['channel_id']),
+ dbesc(make_xchan_hash($sender['guid'],$sender['guid_sig']))
+ );
+ if ($r) {
+ contact_remove($r[0]['channel_id'],$r[0]['abook_id']);
+ }
+ }
+ }
+ $ret['success'] = true;
+ }
+ else {
+ // Unfriend everybody - basically this means the channel has committed suicide
+ $arr = $sender;
+ $sender_hash = make_xchan_hash($arr['guid'],$arr['guid_sig']);
+
+ require_once('include/Contact.php');
+ remove_all_xchan_resources($sender_hash);
+
+ $ret['success'] = true;
+ }
+
+ json_return_and_die($ret);
+}
+
+
+function zot_reply_refresh($sender,$recipients) {
+
+ $ret = array('success' => false);
+
+ // remote channel info (such as permissions or photo or something)
+ // has been updated. Grab a fresh copy and sync it.
+ // The difference between refresh and force_refresh is that
+ // force_refresh unconditionally creates a directory update record,
+ // even if no changes were detected upon processing.
+
+ if($recipients) {
+
+ // This would be a permissions update, typically for one connection
+
+ foreach ($recipients as $recip) {
+ $r = q("select channel.*,xchan.* from channel
+ left join xchan on channel_hash = xchan_hash
+ where channel_guid = '%s' and channel_guid_sig = '%s' limit 1",
+ dbesc($recip['guid']),
+ dbesc($recip['guid_sig'])
+ );
+
+ $x = zot_refresh(array(
+ 'xchan_guid' => $sender['guid'],
+ 'xchan_guid_sig' => $sender['guid_sig'],
+ 'hubloc_url' => $sender['url']
+ ), $r[0], (($msgtype === 'force_refresh') ? true : false));
+ }
+ }
+ else {
+ // system wide refresh
+
+ $x = zot_refresh(array(
+ 'xchan_guid' => $sender['guid'],
+ 'xchan_guid_sig' => $sender['guid_sig'],
+ 'hubloc_url' => $sender['url']
+ ), null, (($msgtype === 'force_refresh') ? true : false));
+ }
+
+ $ret['success'] = true;
+ json_return_and_die($ret);
+
+}
+
+
+function zot_reply_notify($data) {
+
+ $ret = array('success' => false);
+
+ logger('notify received from ' . $data['sender']['url']);
+
+ $async = get_config('system','queued_fetch');
+
+ if($async) {
+ // add to receive queue
+ // qreceive_add($data);
+ }
+ else {
+ $x = zot_fetch($data);
+ $ret['delivery_report'] = $x;
+ }
+
+ $ret['success'] = true;
+ json_return_and_die($ret);
+
+} \ No newline at end of file