diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/RedDAV/RedBrowser.php | 2 | ||||
-rw-r--r-- | include/account.php | 4 | ||||
-rw-r--r-- | include/api.php | 25 | ||||
-rw-r--r-- | include/api_auth.php | 80 | ||||
-rw-r--r-- | include/comanche.php | 9 | ||||
-rw-r--r-- | include/externals.php | 21 | ||||
-rw-r--r-- | include/follow.php | 25 | ||||
-rw-r--r-- | include/identity.php | 6 | ||||
-rwxr-xr-x | include/items.php | 9 | ||||
-rw-r--r-- | include/message.php | 12 | ||||
-rw-r--r-- | include/network.php | 187 | ||||
-rw-r--r-- | include/notifier.php | 2 | ||||
-rw-r--r-- | include/oauth.php | 149 | ||||
-rwxr-xr-x | include/plugin.php | 20 | ||||
-rw-r--r-- | include/security.php | 18 | ||||
-rw-r--r-- | include/widgets.php | 20 | ||||
-rw-r--r-- | include/zot.php | 464 |
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 |