diff options
author | Mario Vavti <mario@mariovavti.com> | 2016-12-23 10:09:46 +0100 |
---|---|---|
committer | Mario Vavti <mario@mariovavti.com> | 2016-12-23 10:09:46 +0100 |
commit | 3b9b03cf86979b28e7fa249133176bed84b0105c (patch) | |
tree | 336dc8b8b9627e7f4a93e5c35fe3e98555274616 /include | |
parent | 2e5a993f880d619aedf3693927e7b3e164fbfcc0 (diff) | |
parent | ef39c1e94b5149a3019d417d08dc7c16c8aef9c1 (diff) | |
download | volse-hubzilla-3b9b03cf86979b28e7fa249133176bed84b0105c.tar.gz volse-hubzilla-3b9b03cf86979b28e7fa249133176bed84b0105c.tar.bz2 volse-hubzilla-3b9b03cf86979b28e7fa249133176bed84b0105c.zip |
Merge branch '2.0RC'
Diffstat (limited to 'include')
48 files changed, 3167 insertions, 4277 deletions
diff --git a/include/Import/Importer.php b/include/Import/Importer.php index cddfac7b5..1fa677db0 100644 --- a/include/Import/Importer.php +++ b/include/Import/Importer.php @@ -5,7 +5,6 @@ namespace Hubzilla\Import; /** * @brief Class Import * - * @package Hubzilla\Import */ class Import { diff --git a/include/Import/import_diaspora.php b/include/Import/import_diaspora.php index b664badf1..c6dae0117 100644 --- a/include/Import/import_diaspora.php +++ b/include/Import/import_diaspora.php @@ -89,7 +89,7 @@ function import_diaspora($data) { } $gender = escape_tags($data['user']['profile']['gender']); - $about = diaspora2bb($data['user']['profile']['bio']); + $about = markdown_to_bb($data['user']['profile']['bio']); $publish = intval($data['user']['profile']['searchable']); if($data['user']['profile']['birthday']) $dob = datetime_convert('UTC','UTC',$data['user']['profile']['birthday'],'Y-m-d'); diff --git a/include/account.php b/include/account.php index b78c3e56d..5e57d53a8 100644 --- a/include/account.php +++ b/include/account.php @@ -74,7 +74,7 @@ function check_account_invite($invite_code) { if(! $invite_code) { $result['message'] .= t('An invitation is required.') . EOL; } - $r = q("select * from register where `hash` = '%s' limit 1", dbesc($invite_code)); + $r = q("select * from register where hash = '%s' limit 1", dbesc($invite_code)); if(! $r) { $result['message'] .= t('Invitation could not be verified.') . EOL; } @@ -373,7 +373,7 @@ function account_allow($hash) { $ret = array('success' => false); - $register = q("SELECT * FROM `register` WHERE `hash` = '%s' LIMIT 1", + $register = q("SELECT * FROM register WHERE hash = '%s' LIMIT 1", dbesc($hash) ); @@ -465,7 +465,7 @@ function account_deny($hash) { intval($register[0]['uid']) ); - $r = q("DELETE FROM `register` WHERE id = %d", + $r = q("DELETE FROM register WHERE id = %d", dbesc($register[0]['id']) ); notice( sprintf(t('Registration revoked for %s'), $account[0]['account_email']) . EOL); @@ -482,7 +482,7 @@ function account_approve($hash) { // Note: when the password in the register table is 'verify', the uid actually contains the account_id - $register = q("SELECT * FROM `register` WHERE `hash` = '%s' and password = 'verify' LIMIT 1", + $register = q("SELECT * FROM register WHERE hash = '%s' and password = 'verify' LIMIT 1", dbesc($hash) ); diff --git a/include/acl_selectors.php b/include/acl_selectors.php index 63867823f..dcf0fe9a2 100644 --- a/include/acl_selectors.php +++ b/include/acl_selectors.php @@ -1,17 +1,24 @@ -<?php /** @file */ +<?php /** - * + * @file include/acl_selectors.php + * + * @package acl_selectors */ /** - * @package acl_selectors + * @brief + * + * @param string $selname + * @param string $selclass + * @param mixed $preselected + * @param number $size + * @return string */ - -function group_select($selname,$selclass,$preselected = false,$size = 4) { +function group_select($selname, $selclass, $preselected = false, $size = 4) { $o = ''; - $o .= "<select name=\"{$selname}[]\" id=\"$selclass\" class=\"$selclass\" multiple=\"multiple\" size=\"$size\" >\r\n"; + $o .= "<select name=\"{$selname}[]\" id=\"$selclass\" class=\"$selclass\" multiple=\"multiple\" size=\"$size\">\r\n"; $r = q("SELECT * FROM groups WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", intval(local_channel()) @@ -34,19 +41,17 @@ function group_select($selname,$selclass,$preselected = false,$size = 4) { $o .= "<option value=\"{$rr['id']}\" $selected title=\"{$rr['name']}\" >$trimmed</option>\r\n"; } - + } $o .= "</select>\r\n"; call_hooks(App::$module . '_post_' . $selname, $o); - return $o; } function contact_select($selname, $selclass, $preselected = false, $size = 4, $privmail = false, $celeb = false, $privatenet = false, $tabindex = null) { - $o = ''; // When used for private messages, we limit correspondence to mutual DFRN/Friendica friends and the selector @@ -54,17 +59,17 @@ function contact_select($selname, $selclass, $preselected = false, $size = 4, $p $sql_extra = ''; - $tabindex = ($tabindex > 0 ? "tabindex=\"$tabindex\"" : ""); + $tabindex = ($tabindex > 0 ? 'tabindex="$tabindex"' : ''); if($privmail) $o .= "<select name=\"$selname\" id=\"$selclass\" class=\"$selclass\" size=\"$size\" $tabindex >\r\n"; - else - $o .= "<select name=\"{$selname}[]\" id=\"$selclass\" class=\"$selclass\" multiple=\"multiple\" size=\"$size\" $tabindex >\r\n"; + else + $o .= "<select name=\"{$selname}[]\" id=\"$selclass\" class=\"$selclass\" multiple=\"multiple\" size=\"$size\" $tabindex>\r\n"; $r = q("SELECT abook_id, xchan_name, xchan_url, xchan_photo_s from abook left join xchan on abook_xchan = xchan_hash where abook_self = 0 and abook_channel = %d $sql_extra - ORDER BY xchan_name ASC ", + ORDER BY xchan_name ASC", intval(local_channel()) ); @@ -78,15 +83,14 @@ function contact_select($selname, $selclass, $preselected = false, $size = 4, $p if($r) { foreach($r as $rr) { if((is_array($preselected)) && in_array($rr['id'], $preselected)) - $selected = " selected=\"selected\" "; + $selected = ' selected="selected" '; else $selected = ''; - $trimmed = mb_substr($rr['xchan_name'],0,20); + $trimmed = mb_substr($rr['xchan_name'], 0, 20); $o .= "<option value=\"{$rr['abook_id']}\" $selected title=\"{$rr['xchan_name']}|{$rr['xchan_url']}\" >$trimmed</option>\r\n"; } - } $o .= "</select>\r\n"; @@ -98,13 +102,13 @@ function contact_select($selname, $selclass, $preselected = false, $size = 4, $p function fixacl(&$item) { - $item = str_replace(array('<','>'),array('',''),$item); + $item = str_replace(array('<', '>'), array('', ''), $item); } /** * Builds a modal dialog for editing permissions, using acl_selector.tpl as the template. * -* @param array $default Optional access control list for the initial state of the dialog. +* @param array $defaults Optional access control list for the initial state of the dialog. * @param boolean $show_jotnets Whether plugins for federated networks should be included in the permissions dialog * @param PermissionDescription $emptyACL_description - An optional description for the permission implied by selecting an empty ACL. Preferably an instance of PermissionDescription. * @param string $dialog_description Optional message to include at the top of the dialog. E.g. "Warning: Post permissions cannot be changed once sent". @@ -118,16 +122,15 @@ function populate_acl($defaults = null,$show_jotnets = true, $emptyACL_descripti $allow_cid = $allow_gid = $deny_cid = $deny_gid = false; $showall_origin = ''; $showall_icon = 'fa-globe'; - $role = get_pconfig(local_channel(),'system','permissions_role'); + $role = get_pconfig(local_channel(), 'system', 'permissions_role'); if(! $emptyACL_description) { $showall_caption = t('Visible to your default audience'); - } else if (is_a($emptyACL_description, '\\Zotlabs\\Lib\\PermissionDescription')) { + } else if(is_a($emptyACL_description, '\\Zotlabs\\Lib\\PermissionDescription')) { $showall_caption = $emptyACL_description->get_permission_description(); $showall_origin = (($role === 'custom') ? $emptyACL_description->get_permission_origin_description() : ''); $showall_icon = $emptyACL_description->get_permission_icon(); - } else { // For backwards compatibility we still accept a string... for now! $showall_caption = $emptyACL_description; @@ -135,7 +138,7 @@ function populate_acl($defaults = null,$show_jotnets = true, $emptyACL_descripti if(is_array($defaults)) { - $allow_cid = ((strlen($defaults['allow_cid'])) + $allow_cid = ((strlen($defaults['allow_cid'])) ? explode('><', $defaults['allow_cid']) : array() ); $allow_gid = ((strlen($defaults['allow_gid'])) ? explode('><', $defaults['allow_gid']) : array() ); @@ -195,9 +198,9 @@ function populate_acl($defaults = null,$show_jotnets = true, $emptyACL_descripti '$select_label' => t('Who can see this?'), '$custom' => t('Custom selection'), '$showlimitedDesc' => t('Select "Show" to allow viewing. "Don\'t show" lets you override and limit the scope of "Show".'), - '$show' => t("Show"), + '$show' => t('Show'), '$hide' => t("Don't show"), - '$search' => t("Search"), + '$search' => t('Search'), '$allowcid' => json_encode($allow_cid), '$allowgid' => json_encode($allow_gid), '$denycid' => json_encode($deny_cid), @@ -209,20 +212,19 @@ function populate_acl($defaults = null,$show_jotnets = true, $emptyACL_descripti )); return $o; - } /** -* Returns a string that's suitable for passing as the $dialog_description argument to a -* populate_acl() call for wall posts or network posts. -* -* This string is needed in 3 different files, and our .po translation system currently -* cannot be used as a string table (because the value is always the key in english) so -* I've centralized the value here (making this function name the "key") until we have a -* better way. -* -* @return string Description to present to user in modal permissions dialog -*/ + * Returns a string that's suitable for passing as the $dialog_description argument to a + * populate_acl() call for wall posts or network posts. + * + * This string is needed in 3 different files, and our .po translation system currently + * cannot be used as a string table (because the value is always the key in english) so + * I've centralized the value here (making this function name the "key") until we have a + * better way. + * + * @return string Description to present to user in modal permissions dialog + */ function get_post_aclDialogDescription() { // I'm trying to make two points in this description text - warn about finality of wall @@ -238,4 +240,3 @@ function get_post_aclDialogDescription() { return sprintf($description, $emphasisOpen, $emphasisClose); } - diff --git a/include/api.php b/include/api.php index 692baf563..693967696 100644 --- a/include/api.php +++ b/include/api.php @@ -10,25 +10,22 @@ require_once('include/photos.php'); require_once('include/items.php'); require_once('include/attach.php'); require_once('include/api_auth.php'); +require_once('include/api_zot.php'); /* * - * Hubzilla API. Loosely based on and possibly compatible with Twitter-Like (v1.0) API but all similarities end there. + * Hubzilla API. * */ - /** - ** TWITTER API - */ - $API = array(); $called_api = Null; // All commands which require authentication accept a "channel" parameter // which is the left hand side of the channel address/nickname. - // If provided, the desired channel is selected before caarying out the command. + // If provided, the desired channel is selected before carrying out the command. // If not provided, the default channel associated with the account is used. // If channel selection fails, the API command requiring login will fail. @@ -49,7 +46,7 @@ require_once('include/api_auth.php'); return false; } } - if ($_SESSION["allow_api"]) + if ($_SESSION['allow_api']) return local_channel(); return false; } @@ -57,11 +54,11 @@ require_once('include/api_auth.php'); function api_date($str){ //Wed May 23 06:01:13 +0000 2007 - return datetime_convert('UTC', 'UTC', $str, "D M d H:i:s +0000 Y" ); + return datetime_convert('UTC', 'UTC', $str, 'D M d H:i:s +0000 Y' ); } - function api_register_func($path, $func, $auth=false) { + function api_register_func($path, $func, $auth = false) { \Zotlabs\Lib\Api_router::register($path,$func,$auth); } @@ -90,7 +87,12 @@ require_once('include/api_auth.php'); $info = \Zotlabs\Lib\Api_router::find($p); - logger('info: ' . $p . ' type: ' . $type . ' ' . print_r($info,true)); + if(in_array($type, [ 'rss', 'atom', 'as' ])) { + // These types no longer supported. + $info = false; + } + + logger('API info: ' . $p . ' type: ' . $type . ' ' . print_r($info,true), LOGGER_DEBUG,LOG_INFO); if($info) { @@ -111,247 +113,73 @@ require_once('include/api_auth.php'); return; switch($type) { - case "xml": - $r = mb_convert_encoding($r, "UTF-8",mb_detect_encoding($r)); - header ("Content-Type: text/xml"); - return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r; + case 'xml': + header ('Content-Type: text/xml'); + return $r; break; - case "json": - header ("Content-Type: application/json"); - if($r) { - foreach($r as $rv) { - $json = json_encode($rv); - } - } + case 'json': + header ('Content-Type: application/json'); // Lookup JSONP to understand these lines. They provide cross-domain AJAX ability. if ($_GET['callback']) - $json = $_GET['callback'] . '(' . $json . ')' ; - return $json; + $r = $_GET['callback'] . '(' . $r . ')' ; + return $r; break; - case "rss": - header ("Content-Type: application/rss+xml"); - return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r; - break; - case "atom": - header ("Content-Type: application/atom+xml"); - return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r; - break; - case "as": - if($r) { - foreach($r as $rv) { - $json = json_encode($rv); - } - } - return $json; - break; - } } - header("HTTP/1.1 404 Not Found"); + header('HTTP/1.1 404 Not Found'); logger('API call not implemented: ' . App::$query_string . ' - ' . print_r($_REQUEST,true)); $r = '<status><error>not implemented</error></status>'; switch($type){ - case "xml": - header ("Content-Type: text/xml"); - return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r; + case 'xml': + header ('Content-Type: text/xml'); + return '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $r; break; case "json": - header ("Content-Type: application/json"); + header ('Content-Type: application/json'); return json_encode(array('error' => 'not implemented')); break; case "rss": - header ("Content-Type: application/rss+xml"); - return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r; + header ('Content-Type: application/rss+xml'); + return '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $r; break; case "atom": - header ("Content-Type: application/atom+xml"); - return '<?xml version="1.0" encoding="UTF-8"?>'."\n".$r; + header ('Content-Type: application/atom+xml'); + return '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $r; break; } } /** - * RSS extra info - */ - - function api_rss_extra( $arr, $user_info){ - if (is_null($user_info)) $user_info = api_get_user($a); - $arr['$user'] = $user_info; - $arr['$rss'] = array( - 'alternate' => $user_info['url'], - 'self' => z_root(). "/". App::$query_string, - 'base' => z_root(), - 'updated' => api_date(null), - 'atom_updated' => datetime_convert('UTC','UTC','now',ATOM_TIME), - 'language' => $user_info['language'], - 'logo' => z_root()."/images/rm-64.png", - ); - - return $arr; - } - - /** - * Returns user info array. + * load api $templatename for $type and replace $data array */ - function api_get_user( $contact_id = null, $contact_xchan = null){ - - $user = null; - $extra_query = ""; - - - if(! is_null($contact_xchan)) { - $user = local_channel(); - $extra_query = " and abook_xchan = '" . dbesc($contact_xchan) . "' "; - } - else { - if(!is_null($contact_id)){ - $user=$contact_id; - $extra_query = " AND abook_id = %d "; - } - - if(is_null($user) && x($_GET, 'user_id')) { - $user = intval($_GET['user_id']); - $extra_query = " AND abook_id = %d "; - } - if(is_null($user) && x($_GET, 'screen_name')) { - $user = dbesc($_GET['screen_name']); - $extra_query = " AND xchan_addr like '%s@%%' "; - if (api_user()!==false) - $extra_query .= " AND abook_channel = ".intval(api_user()); - } - } - - if (! $user) { - if (api_user() === false) { - api_login($a); - return false; - } else { - $user = local_channel(); - $extra_query = " AND abook_channel = %d AND abook_self = 1 "; - } - - } - - logger('api_user: ' . $extra_query . ', user: ' . $user, LOGGER_DATA, LOG_INFO); - - // user info - - $uinfo = q("SELECT * from abook left join xchan on abook_xchan = xchan_hash - WHERE 1 - $extra_query", - $user - ); - - if (count($uinfo)==0) { - return False; - } - $following = false; - - if(intval($uinfo[0]['abook_self'])) { - $usr = q("select * from channel where channel_id = %d limit 1", - intval(api_user()) - ); - $profile = q("select * from profile where uid = %d and `is_default` = 1 limit 1", - intval(api_user()) - ); - - $item_normal = item_normal(); - - // count public wall messages - $r = q("SELECT COUNT(`id`) as `count` FROM `item` - WHERE `uid` = %d - AND item_wall = 1 $item_normal - AND `allow_cid`='' AND `allow_gid`='' AND `deny_cid`='' AND `deny_gid`='' - AND item_private = 0 ", - intval($usr[0]['channel_id']) - ); - $countitms = $r[0]['count']; - $following = true; - } - else { - $r = q("SELECT COUNT(`id`) as `count` FROM `item` - WHERE author_xchan = '%s' - AND `allow_cid`='' AND `allow_gid`='' AND `deny_cid`='' AND `deny_gid`='' - AND item_private = 0 ", - intval($uinfo[0]['xchan_hash']) - ); - $countitms = $r[0]['count']; - - $following = ((get_abconfig($uinfo[0]['abook_channel'],$uinfo[0]['abook_xchan'],'my_perms','view_stream')) ? true : false ); - } - + function api_apply_template($templatename, $type, $data){ - // count friends - if($usr) { - $r = q("SELECT COUNT(abook_id) as `count` FROM abook - WHERE abook_channel = %d AND abook_self = 0 ", - intval($usr[0]['channel_id']) - ); - $countfriends = $r[0]['count']; - $countfollowers = $r[0]['count']; + switch($type){ + case 'xml': + if($data) { + foreach($data as $k => $v) + $ret = arrtoxml(str_replace('$','',$k),$v); + } + break; + case 'json': + default: + if($data) { + foreach($data as $rv) { + $ret = json_encode($rv); + } + } + break; } - $r = q("SELECT count(`id`) as `count` FROM item where item_starred = 1 and uid = %d " . item_normal(), - intval($uinfo[0]['channel_id']) - ); - $starred = $r[0]['count']; + return $ret; + } - if(! intval($uinfo[0]['abook_self'])) { - $countfriends = 0; - $countfollowers = 0; - $starred = 0; - } - - $ret = Array( - 'id' => intval($uinfo[0]['abook_id']), - 'self' => (intval($uinfo[0]['abook_self']) ? 1 : 0), - 'uid' => intval($uinfo[0]['abook_channel']), - 'guid' => $uinfo[0]['xchan_hash'], - 'name' => (($uinfo[0]['xchan_name']) ? $uinfo[0]['xchan_name'] : substr($uinfo[0]['xchan_addr'],0,strpos($uinfo[0]['xchan_addr'],'@'))), - 'screen_name' => substr($uinfo[0]['xchan_addr'],0,strpos($uinfo[0]['xchan_addr'],'@')), - 'location' => ($usr) ? $usr[0]['channel_location'] : '', - 'profile_image_url' => $uinfo[0]['xchan_photo_l'], - 'url' => $uinfo[0]['xchan_url'], - 'contact_url' => z_root() . "/connections/".$uinfo[0]['abook_id'], - 'protected' => false, - 'friends_count' => intval($countfriends), - 'created_at' => api_date($uinfo[0]['abook_created']), - 'utc_offset' => "+00:00", - 'time_zone' => 'UTC', //$uinfo[0]['timezone'], - 'geo_enabled' => false, - 'statuses_count' => intval($countitms), //#XXX: fix me - 'lang' => App::$language, - 'description' => (($profile) ? $profile[0]['pdesc'] : ''), - 'followers_count' => intval($countfollowers), - 'favourites_count' => intval($starred), - 'contributors_enabled' => false, - 'follow_request_sent' => true, - 'profile_background_color' => 'cfe8f6', - 'profile_text_color' => '000000', - 'profile_link_color' => 'FF8500', - 'profile_sidebar_fill_color' =>'AD0066', - 'profile_sidebar_border_color' => 'AD0066', - 'profile_background_image_url' => '', - 'profile_background_tile' => false, - 'profile_use_background_image' => false, - 'notifications' => false, - 'following' => $following, - 'verified' => true // #XXX: fix me - ); - $x = api_get_status($uinfo[0]['xchan_hash']); - if($x) - $ret['status'] = $x; -// logger('api_get_user: ' . print_r($ret,true)); - - return $ret; - - } function api_client_register($type) { @@ -382,1969 +210,6 @@ require_once('include/api_auth.php'); json_return_and_die($ret); } - api_register_func('api/client/register','api_client_register', false); - - - - function api_item_get_user( $item) { - - // The author is our direct contact, in a conversation with us. - - if($item['author']['abook_id']) { - return api_get_user($item['author']['abook_id']); - } - - // We don't know this person directly. - - $nick = substr($item['author']['xchan_addr'],0,strpos($item['author']['xchan_addr'],'@')); - $name = $item['author']['xchan_name']; - - // Generating a random ID - if (! $nick) - $nick = mt_rand(2000000, 2100000); - - $ret = array( - 'id' => $nick, - 'name' => $name, - 'screen_name' => $nick, - 'location' => '', //$uinfo[0]['default-location'], - 'description' => '', - 'profile_image_url' => $item['author']['xchan_photo_m'], - 'url' => $item['author']['xchan_url'], - 'protected' => false, - 'followers_count' => 0, - 'friends_count' => 0, - 'created_at' => '', - 'favourites_count' => 0, - 'utc_offset' => 0, // #XXX: fix me - 'time_zone' => '', //$uinfo[0]['timezone'], - 'statuses_count' => 0, - 'following' => false, - 'statusnet_blocking' => false, - 'notifications' => false, - 'uid' => 0, - 'contact_url' => 0, - 'geo_enabled' => false, - 'lang' => 'en', // #XXX: fix me - 'contributors_enabled' => false, - 'follow_request_sent' => false, - 'profile_background_color' => 'cfe8f6', - 'profile_text_color' => '000000', - 'profile_link_color' => 'FF8500', - 'profile_sidebar_fill_color' =>'AD0066', - 'profile_sidebar_border_color' => 'AD0066', - 'profile_background_image_url' => '', - 'profile_background_tile' => false, - 'profile_use_background_image' => false, - 'verified' => true, // #XXX: fix me - 'followers' => '' // #XXX: fix me - ); - - return $ret; - } - - - /** - * load api $templatename for $type and replace $data array - */ - function api_apply_template($templatename, $type, $data){ - - switch($type){ - case "atom": - case "rss": - case "xml": - $data = array_xmlify($data); - $tpl = get_markup_template("api_".$templatename."_".$type.".tpl"); - $ret = replace_macros($tpl, $data); - break; - case "json": - default: - $ret = $data; - break; - } - - return $ret; - } - - - /** - * Returns an HTTP 200 OK response code and a representation of the requesting user if authentication was successful; - * returns a 401 status code and an error message if not. - * http://developer.twitter.com/doc/get/account/verify_credentials - */ - function api_account_verify_credentials( $type){ - if (api_user()===false) return false; - $user_info = api_get_user($a); - return api_apply_template("user", $type, array('$user' => $user_info)); - - } - api_register_func('api/account/verify_credentials','api_account_verify_credentials', true); - - - function api_account_logout( $type){ - require_once('include/auth.php'); - App::$session->nuke(); - return api_apply_template("user", $type, array('$user' => null)); - - } - api_register_func('api/account/logout','api_account_logout', false); - - - - /** - * get data from $_REQUEST ( e.g. $_POST or $_GET ) - */ - function requestdata($k) { - if(array_key_exists($k,$_REQUEST)) - return $_REQUEST[$k]; - return null; - } - - - /* - * Red basic channel export - */ - - function api_export_basic( $type) { - if(api_user() === false) { - logger('api_export_basic: no user'); - return false; - } - - require_once('include/channel.php'); - - json_return_and_die(identity_basic_export(api_user(),(($_REQUEST['posts']) ? intval($_REQUEST['posts']) : 0 ))); - } - api_register_func('api/export/basic','api_export_basic', true); - api_register_func('api/red/channel/export/basic','api_export_basic', true); - api_register_func('api/hz/1.0/channel/export/basic','api_export_basic', true); - - - function api_channel_stream( $type) { - if(api_user() === false) { - logger('api_channel_stream: no user'); - return false; - } - - if($_SERVER['REQUEST_METHOD'] == 'POST') { - json_return_and_die(post_activity_item($_REQUEST)); - } - else { - // fetch stream - - } - } - api_register_func('api/red/channel/stream','api_channel_stream', true); - api_register_func('api/hz/1.0/channel/stream','api_channel_stream', true); - - function api_attach_list($type) { - logger('api_user: ' . api_user()); - json_return_and_die(attach_list_files(api_user(),get_observer_hash(),'','','','created asc')); - } - api_register_func('api/red/files','api_attach_list', true); - api_register_func('api/hz/1.0/files','api_attach_list', true); - - - - - - function api_file_meta($type) { - if (api_user()===false) return false; - if(! $_REQUEST['file_id']) return false; - $r = q("select * from attach where uid = %d and hash = '%s' limit 1", - intval(api_user()), - dbesc($_REQUEST['file_id']) - ); - if($r) { - unset($r[0]['content']); - $ret = array('attach' => $r[0]); - json_return_and_die($ret); - } - killme(); - } - - api_register_func('api/red/filemeta', 'api_file_meta', true); - api_register_func('api/hz/1.0/filemeta', 'api_file_meta', true); - - - function api_file_data($type) { - if (api_user()===false) return false; - if(! $_REQUEST['file_id']) return false; - $start = (($_REQUEST['start']) ? intval($_REQUEST['start']) : 0); - $length = (($_REQUEST['length']) ? intval($_REQUEST['length']) : 0); - - $r = q("select * from attach where uid = %d and hash = '%s' limit 1", - intval(api_user()), - dbesc($_REQUEST['file_id']) - ); - if($r) { - $ptr = $r[0]; - if($length === 0) - $length = intval($ptr['filesize']); - - if($ptr['is_dir']) - $ptr['content'] = ''; - elseif(! intval($r[0]['os_storage'])) { - $ptr['start'] = $start; - $x = substr(dbunescbin($ptr['content'],$start,$length)); - $ptr['length'] = strlen($x); - $ptr['content'] = base64_encode($x); - } - else { - $fp = fopen(dbunescbin($ptr['content']),'r'); - if($fp) { - $seek = fseek($fp,$start,SEEK_SET); - $x = fread($fp,$length); - $ptr['start'] = $start; - $ptr['length'] = strlen($x); - $ptr['content'] = base64_encode($x); - } - } - - $ret = array('attach' => $ptr); - json_return_and_die($ret); - } - killme(); - } - - api_register_func('api/red/filedata', 'api_file_data', true); - api_register_func('api/hz/1.0/filedata', 'api_file_data', true); - - - - function api_file_detail($type) { - if (api_user()===false) return false; - if(! $_REQUEST['file_id']) return false; - $r = q("select * from attach where uid = %d and hash = '%s' limit 1", - intval(api_user()), - dbesc($_REQUEST['file_id']) - ); - if($r) { - if($r[0]['is_dir']) - $r[0]['content'] = ''; - elseif(intval($r[0]['os_storage'])) - $r[0]['content'] = base64_encode(file_get_contents(dbunescbin($r[0]['content']))); - else - $r[0]['content'] = base64_encode(dbunescbin($r[0]['content'])); - - $ret = array('attach' => $r[0]); - json_return_and_die($ret); - } - killme(); - } - - api_register_func('api/red/file', 'api_file_detail', true); - api_register_func('api/hz/1.0/file', 'api_file_detail', true); - - - function api_albums($type) { - json_return_and_die(photos_albums_list(App::get_channel(),App::get_observer())); - } - api_register_func('api/red/albums','api_albums', true); - api_register_func('api/hz/1.0/albums','api_albums', true); - - function api_photos($type) { - $album = $_REQUEST['album']; - json_return_and_die(photos_list_photos(App::get_channel(),App::get_observer(),$album)); - } - api_register_func('api/red/photos','api_photos', true); - api_register_func('api/hz/1.0/photos','api_photos', true); - - function api_photo_detail($type) { - if (api_user()===false) return false; - if(! $_REQUEST['photo_id']) return false; - $scale = ((array_key_exists('scale',$_REQUEST)) ? intval($_REQUEST['scale']) : 0); - $r = q("select * from photo where uid = %d and resource_id = '%s' and imgscale = %d limit 1", - intval(local_channel()), - dbesc($_REQUEST['photo_id']), - intval($scale) - ); - if($r) { - $data = dbunescbin($r[0]['content']); - if(array_key_exists('os_storage',$r[0]) && intval($r[0]['os_storage'])) - $data = file_get_contents($data); - $r[0]['content'] = base64_encode($data); - $ret = array('photo' => $r[0]); - $i = q("select id from item where uid = %d and resource_type = 'photo' and resource_id = '%s' limit 1", - intval(local_channel()), - dbesc($_REQUEST['photo_id']) - ); - if($i) { - $ii = q("select * from item where parent = %d order by id", - intval($i[0]['id']) - ); - if($ii) { - xchan_query($ii,true,0); - $ii = fetch_post_tags($ii,true); - if($ii) { - $ret['item'] = array(); - foreach($ii as $iii) - $ret['item'][] = encode_item($iii,true); - } - } - } - - json_return_and_die($ret); - } - killme(); - } - - api_register_func('api/red/photo', 'api_photo_detail', true); - api_register_func('api/hz/1.0/photo', 'api_photo_detail', true); - - - function api_group_members($type) { - if(api_user() === false) - return false; - - if($_REQUEST['group_id']) { - $r = q("select * from groups where uid = %d and id = %d limit 1", - intval(api_user()), - intval($_REQUEST['group_id']) - ); - if($r) { - $x = q("select * from group_member left join xchan on group_member.xchan = xchan.xchan_hash - left join abook on abook_xchan = xchan_hash where gid = %d", - intval($_REQUEST['group_id']) - ); - json_return_and_die($x); - } - } - } - - api_register_func('api/red/group_members','api_group_members', true); - api_register_func('api/hz/1.0/group_members','api_group_members', true); - - - - - function api_group($type) { - if(api_user() === false) - return false; - - $r = q("select * from groups where uid = %d", - intval(api_user()) - ); - json_return_and_die($r); - } - api_register_func('api/red/group','api_group', true); - api_register_func('api/hz/1.0/group','api_group', true); - - - function api_red_xchan($type) { - logger('api_xchan'); - - if(api_user() === false) - return false; - logger('api_xchan'); - require_once('include/hubloc.php'); - - if($_SERVER['REQUEST_METHOD'] === 'POST') { - $r = xchan_store($_REQUEST); - } - $r = xchan_fetch($_REQUEST); - json_return_and_die($r); - }; - - api_register_func('api/red/xchan','api_red_xchan',true); - api_register_func('api/hz/1.0/xchan','api_red_xchan',true); - - - function api_statuses_mediap( $type) { - if (api_user() === false) { - logger('api_statuses_update: no user'); - return false; - } - $user_info = api_get_user($a); - -// logger('status_with_media: ' . print_r($_REQUEST,true), LOGGER_DEBUG); - - $_REQUEST['type'] = 'wall'; - $_REQUEST['profile_uid'] = api_user(); - $_REQUEST['api_source'] = true; - - $txt = requestdata('status'); - - require_once('library/HTMLPurifier.auto.php'); - require_once('include/html2bbcode.php'); - - if((strpos($txt,'<') !== false) || (strpos($txt,'>') !== false)) { - $txt = html2bb_video($txt); - $config = HTMLPurifier_Config::createDefault(); - $config->set('Cache.DefinitionImpl', null); - $purifier = new HTMLPurifier($config); - $txt = $purifier->purify($txt); - } - $txt = html2bbcode($txt); - - App::$argv[1] = $user_info['screen_name']; - - $_REQUEST['silent']='1'; //tell wall_upload function to return img info instead of echo - $_FILES['userfile'] = $_FILES['media']; - - $mod = new Zotlabs\Module\Wall_attach(); - $mod->post(); - - - $_REQUEST['body']=$txt."\n\n".$posted; - - $mod = new Zotlabs\Module\Item(); - $mod->post(); - - // this should output the last post (the one we just posted). - return api_status_show($type); - } - api_register_func('api/statuses/mediap','api_statuses_mediap', true); - - function api_statuses_update( $type) { - if (api_user() === false) { - logger('api_statuses_update: no user'); - return false; - } - - logger('api_statuses_update: REQUEST ' . print_r($_REQUEST,true)); - logger('api_statuses_update: FILES ' . print_r($_FILES,true)); - - - // set this so that the item_post() function is quiet and doesn't redirect or emit json - - $_REQUEST['api_source'] = true; - - - $user_info = api_get_user($a); - - // convert $_POST array items to the form we use for web posts. - - // logger('api_post: ' . print_r($_POST,true)); - - if(requestdata('htmlstatus')) { - require_once('library/HTMLPurifier.auto.php'); - require_once('include/html2bbcode.php'); - - $txt = requestdata('htmlstatus'); - - if((strpos($txt,'<') !== false) || (strpos($txt,'>') !== false)) { - - $txt = html2bb_video($txt); - - $config = HTMLPurifier_Config::createDefault(); - $config->set('Cache.DefinitionImpl', null); - - - $purifier = new HTMLPurifier($config); - $txt = $purifier->purify($txt); - - } - - $_REQUEST['body'] = html2bbcode($txt); - - } - else - $_REQUEST['body'] = requestdata('status'); - - $parent = requestdata('in_reply_to_status_id'); - - if(ctype_digit($parent)) - $_REQUEST['parent'] = $parent; - else - $_REQUEST['parent_mid'] = $parent; - - if($_REQUEST['namespace'] && $parent) { - $x = q("select iid from iconfig where cat = 'system' and k = '%s' and v = '%s' limit 1", - dbesc($_REQUEST['namespace']), - dbesc($parent) - ); - if($x) { - $_REQUEST['parent'] = $x[0]['iid']; - } - } - - if(requestdata('lat') && requestdata('long')) - $_REQUEST['coord'] = sprintf("%s %s",requestdata('lat'),requestdata('long')); - - $_REQUEST['profile_uid'] = api_user(); - - if($parent) - $_REQUEST['type'] = 'net-comment'; - else { - $_REQUEST['type'] = 'wall'; - - if(x($_FILES,'media')) { - if(is_array($_FILES['media']['name'])) { - $num_uploads = count($_FILES['media']['name']); - for($x = 0; $x < $num_uploads; $x ++) { - $_FILES['userfile'] = array(); - $_FILES['userfile']['name'] = $_FILES['media']['name'][$x]; - $_FILES['userfile']['type'] = $_FILES['media']['type'][$x]; - $_FILES['userfile']['tmp_name'] = $_FILES['media']['tmp_name'][$x]; - $_FILES['userfile']['error'] = $_FILES['media']['error'][$x]; - $_FILES['userfile']['size'] = $_FILES['media']['size'][$x]; - - // upload each image if we have any - $_REQUEST['silent']='1'; //tell wall_upload function to return img info instead of echo - $mod = new Zotlabs\Module\Wall_attach(); - App::$data['api_info'] = $user_info; - $media = $mod->post(); - - if(strlen($media)>0) - $_REQUEST['body'] .= "\n\n" . $media; - } - } - else { - // AndStatus doesn't present media as an array - $_FILES['userfile'] = $_FILES['media']; - // upload each image if we have any - $_REQUEST['silent']='1'; //tell wall_upload function to return img info instead of echo - $mod = new Zotlabs\Module\Wall_attach(); - App::$data['api_info'] = $user_info; - $media = $mod->post(); - - if(strlen($media)>0) - $_REQUEST['body'] .= "\n\n" . $media; - } - } - } - - // call out normal post function - - $mod = new Zotlabs\Module\Item(); - $mod->post(); - - // this should output the last post (the one we just posted). - return api_status_show($type); - } - api_register_func('api/statuses/update_with_media','api_statuses_update', true); - api_register_func('api/statuses/update','api_statuses_update', true); - - - function red_item_new( $type) { - - if (api_user() === false) { - logger('api_red_item_new: no user'); - return false; - } - - logger('api_red_item_new: REQUEST ' . print_r($_REQUEST,true)); - logger('api_red_item_new: FILES ' . print_r($_FILES,true)); - - - // set this so that the item_post() function is quiet and doesn't redirect or emit json - - $_REQUEST['api_source'] = true; - $_REQUEST['profile_uid'] = api_user(); - - if(x($_FILES,'media')) { - $_FILES['userfile'] = $_FILES['media']; - // upload the image if we have one - $_REQUEST['silent']='1'; //tell wall_upload function to return img info instead of echo - $mod = new Zotlabs\Module\Wall_upload(); - $media = $mod->post(); - if(strlen($media)>0) - $_REQUEST['body'] .= "\n\n".$media; - } - - $mod = new Zotlabs\Module\Item(); - $x = $mod->post(); - json_return_and_die($x); - } - - api_register_func('api/red/item/new','red_item_new', true); - api_register_func('api/hz/1.0/item/new','red_item_new', true); - - - function red_item( $type) { - - if (api_user() === false) { - logger('api_red_item_full: no user'); - return false; - } - - if($_REQUEST['mid']) { - $arr = array('mid' => $_REQUEST['mid']); - } - elseif($_REQUEST['item_id']) { - $arr = array('item_id' => $_REQUEST['item_id']); - } - else - json_return_and_die(array()); - - $arr['start'] = 0; - $arr['records'] = 999999; - $arr['item_type'] = '*'; - - $i = items_fetch($arr,App::get_channel(),get_observer_hash()); - - if(! $i) - json_return_and_die(array()); - - $ret = array(); - $tmp = array(); - foreach($i as $ii) { - $tmp[] = encode_item($ii,true); - } - $ret['item'] = $tmp; - - json_return_and_die($ret); - } - - api_register_func('api/red/item/full','red_item', true); - api_register_func('api/hz/1.0/item/full','red_item', true); - - - - function api_get_status($xchan_hash) { - require_once('include/security.php'); - - $item_normal = item_normal(); - - $lastwall = q("SELECT * from item where - item_private = 0 $item_normal - and author_xchan = '%s' - and allow_cid = '' and allow_gid = '' and deny_cid = '' and deny_gid = '' - and verb = '%s' - order by created desc limit 1", - dbesc($xchan_hash), - dbesc(ACTIVITY_POST) - ); - - if($lastwall){ - $lastwall = $lastwall[0]; - - $in_reply_to_status_id = ''; - $in_reply_to_user_id = ''; - $in_reply_to_screen_name = ''; - - if($lastwall['author_xchan'] != $lastwall['owner_xchan']) { - $w = q("select * from abook left join xchan on abook_xchan = xchan_hash where - xchan_hash = '%s' limit 1", - dbesc($lastwall['owner_xchan']) - ); - if($w) { - $in_reply_to_user_id = $w[0]['abook_id']; - $in_reply_to_screen_name = substr($w[0]['xchan_addr'],0,strpos($w[0]['xchan_addr'],'@')); - } - } - - if ($lastwall['parent']!=$lastwall['id']) { - $in_reply_to_status_id=$lastwall['thr_parent']; - if(! $in_reply_to_user_id) { - $in_reply_to_user_id = $user_info['id']; - $in_reply_to_screen_name = $user_info['screen_name']; - } - } - unobscure($lastwall); - $status_info = array( - 'text' => html2plain(prepare_text($lastwall['body'],$lastwall['mimetype']), 0), - 'truncated' => false, - 'created_at' => api_date($lastwall['created']), - 'in_reply_to_status_id' => $in_reply_to_status_id, - 'source' => (($lastwall['app']) ? $lastwall['app'] : 'web'), - 'id' => ($lastwall['id']), - 'in_reply_to_user_id' => $in_reply_to_user_id, - 'in_reply_to_screen_name' => $in_reply_to_screen_name, - 'geo' => '', - 'favorited' => false, - 'coordinates' => $lastwall['coord'], - 'place' => $lastwall['location'], - 'contributors' => '' - ); - - } - - return $status_info; - } - - function api_status_show( $type){ - $user_info = api_get_user($a); - - // get last public message - - require_once('include/security.php'); - $item_normal = item_normal(); - - $lastwall = q("SELECT * from item where - item_private = 0 $item_normal - and author_xchan = '%s' - and allow_cid = '' and allow_gid = '' and deny_cid = '' and deny_gid = '' - and verb = '%s' - order by created desc limit 1", - dbesc($user_info['guid']), - dbesc(ACTIVITY_POST) - ); - - if($lastwall){ - $lastwall = $lastwall[0]; - - $in_reply_to_status_id = ''; - $in_reply_to_user_id = ''; - $in_reply_to_screen_name = ''; - - if($lastwall['author_xchan'] != $lastwall['owner_xchan']) { - $w = q("select * from abook left join xchan on abook_xchan = xchan_hash where - xchan_hash = '%s' limit 1", - dbesc($lastwall['owner_xchan']) - ); - if($w) { - $in_reply_to_user_id = $w[0]['abook_id']; - $in_reply_to_screen_name = substr($w[0]['xchan_addr'],0,strpos($w[0]['xchan_addr'],'@')); - } - } - - if ($lastwall['parent']!=$lastwall['id']) { - $in_reply_to_status_id=$lastwall['thr_parent']; - if(! $in_reply_to_user_id) { - $in_reply_to_user_id = $user_info['id']; - $in_reply_to_screen_name = $user_info['screen_name']; - } - } - unobscure($lastwall); - $status_info = array( - 'text' => html2plain(prepare_text($lastwall['body'],$lastwall['mimetype']), 0), - 'truncated' => false, - 'created_at' => api_date($lastwall['created']), - 'in_reply_to_status_id' => $in_reply_to_status_id, - 'source' => (($lastwall['app']) ? $lastwall['app'] : 'web'), - 'id' => ($lastwall['id']), - 'in_reply_to_user_id' => $in_reply_to_user_id, - 'in_reply_to_screen_name' => $in_reply_to_screen_name, - 'geo' => '', - 'favorited' => false, - 'coordinates' => $lastwall['coord'], - 'place' => $lastwall['location'], - 'contributors' => '' - ); - $status_info['user'] = $user_info; - if(array_key_exists('status',$status_info['user'])) - unset($status_info['user']['status']); - } - - return api_apply_template("status", $type, array('$status' => $status_info)); - - } - - - /** - * Returns extended information of a given user, specified by ID or screen name as per the required id parameter. - * The author's most recent status will be returned inline. - * http://developer.twitter.com/doc/get/users/show - */ - -// FIXME - this is essentially the same as api_status_show except for the template formatting at the end. Consolidate. - - - function api_users_show( $type){ - $user_info = api_get_user($a); - - require_once('include/security.php'); - $item_normal = item_normal(); - - $lastwall = q("SELECT * from item where 1 - and item_private != 0 $item_normal - and author_xchan = '%s' - and allow_cid = '' and allow_gid = '' and deny_cid = '' and deny_gid = '' - and verb = '%s' - order by created desc limit 1", - dbesc($user_info['guid']), - dbesc(ACTIVITY_POST) - ); - - if($lastwall){ - $lastwall = $lastwall[0]; - - $in_reply_to_status_id = ''; - $in_reply_to_user_id = ''; - $in_reply_to_screen_name = ''; - - if($lastwall['author_xchan'] != $lastwall['owner_xchan']) { - $w = q("select * from abook left join xchan on abook_xchan = xchan_hash where - xchan_hash = '%s' limit 1", - dbesc($lastwall['owner_xchan']) - ); - if($w) { - $in_reply_to_user_id = $w[0]['abook_id']; - $in_reply_to_screen_name = substr($w[0]['xchan_addr'],0,strpos($w[0]['xchan_addr'],'@')); - } - } - - if ($lastwall['parent']!=$lastwall['id']) { - $in_reply_to_status_id=$lastwall['thr_parent']; - if(! $in_reply_to_user_id) { - $in_reply_to_user_id = $user_info['id']; - $in_reply_to_screen_name = $user_info['screen_name']; - } - } - unobscure($lastwall); - $user_info['status'] = array( - 'text' => html2plain(prepare_text($lastwall['body'],$lastwall['mimetype']), 0), - 'truncated' => false, - 'created_at' => api_date($lastwall['created']), - 'in_reply_to_status_id' => $in_reply_to_status_id, - 'source' => (($lastwall['app']) ? $lastwall['app'] : 'web'), - 'id' => (($w) ? $w[0]['abook_id'] : $user_info['id']), - 'in_reply_to_user_id' => $in_reply_to_user_id, - 'in_reply_to_screen_name' => $in_reply_to_screen_name, - 'geo' => '', - 'favorited' => false, - 'coordinates' => $lastwall['coord'], - 'place' => $lastwall['location'], - 'contributors' => '' - ); - - } - return api_apply_template("user", $type, array('$user' => $user_info)); - - } - api_register_func('api/users/show','api_users_show'); - - /** - * - * http://developer.twitter.com/doc/get/statuses/home_timeline - * - * TODO: Optional parameters - * TODO: Add reply info - */ - - function api_statuses_home_timeline( $type){ - if (api_user() === false) - return false; - - $user_info = api_get_user($a); - // get last network messages - - - // params - $count = (x($_REQUEST,'count')?$_REQUEST['count']:20); - $page = (x($_REQUEST,'page')?$_REQUEST['page']-1:0); - if($page < 0) - $page = 0; - $since_id = (x($_REQUEST,'since_id')?$_REQUEST['since_id']:0); - $max_id = (x($_REQUEST,'max_id')?$_REQUEST['max_id']:0); - $exclude_replies = (x($_REQUEST,'exclude_replies')?1:0); - //$since_id = 0;//$since_id = (x($_REQUEST,'since_id')?$_REQUEST['since_id']:0); - - $start = $page*$count; - - //$include_entities = (x($_REQUEST,'include_entities')?$_REQUEST['include_entities']:false); - - $sql_extra = ''; - if ($max_id > 0) - $sql_extra .= ' AND `item`.`id` <= '.intval($max_id); - if ($exclude_replies > 0) - $sql_extra .= ' AND `item`.`parent` = `item`.`id`'; - - if (api_user() != $user_info['uid']) { - $observer = App::get_observer(); - require_once('include/permissions.php'); - if(! perm_is_allowed($user_info['uid'],(($observer) ? $observer['xchan_hash'] : ''),'view_stream')) - return ''; - $sql_extra .= " and item_private = 0 "; - } - - $item_normal = item_normal(); - - $r = q("SELECT * from item WHERE uid = %d $item_normal - $sql_extra - AND id > %d - ORDER BY received DESC LIMIT %d ,%d ", - intval($user_info['uid']), - intval($since_id), - intval($start), - intval($count) - ); - - xchan_query($r,true); - - $ret = api_format_items($r,$user_info); - - // We aren't going to try to figure out at the item, group, and page - // level which items you've seen and which you haven't. If you're looking - // at the network timeline just mark everything seen. - - if (api_user() == $user_info['uid']) { - $r = q("UPDATE item SET item_unseen = 0 WHERE item_unseen = 1 and uid = %d", - intval($user_info['uid']) - ); - } - - - $data = array('$statuses' => $ret); - switch($type){ - case "atom": - case "rss": - $data = api_rss_extra( $data, $user_info); - break; - case "as": - $as = api_format_as( $ret, $user_info); - $as['title'] = App::$config['sitename']." Home Timeline"; - $as['link']['url'] = z_root()."/".$user_info["screen_name"]."/all"; - return($as); - break; - } - - return api_apply_template("timeline", $type, $data); - } - api_register_func('api/statuses/home_timeline','api_statuses_home_timeline', true); - api_register_func('api/statuses/friends_timeline','api_statuses_home_timeline', true); - - function api_statuses_public_timeline( $type){ - if (api_user()===false) return false; - - $user_info = api_get_user($a); - - $sys = get_sys_channel(); - - // params - $count = (x($_REQUEST,'count')?$_REQUEST['count']:20); - $page = (x($_REQUEST,'page')?$_REQUEST['page']-1:0); - if ($page<0) $page=0; - $since_id = (x($_REQUEST,'since_id')?$_REQUEST['since_id']:0); - $max_id = (x($_REQUEST,'max_id')?$_REQUEST['max_id']:0); - //$since_id = 0;//$since_id = (x($_REQUEST,'since_id')?$_REQUEST['since_id']:0); - - $start = $page*$count; - - //$include_entities = (x($_REQUEST,'include_entities')?$_REQUEST['include_entities']:false); - - if ($max_id > 0) - $sql_extra = 'AND `item`.`id` <= '.intval($max_id); - require_once('include/security.php'); - $item_normal = item_normal(); - - $r = q("select * from item where allow_cid = '' and allow_gid = '' - and deny_cid = '' and deny_gid = '' - and item_private = 0 - $item_normal - and uid = " . $sys['channel_id'] . " - $sql_extra - AND id > %d group by mid - order by received desc LIMIT %d OFFSET %d ", - intval($since_id), - intval($count), - intval($start) - ); - - xchan_query($r,true); - - $ret = api_format_items($r,$user_info); - - - $data = array('$statuses' => $ret); - switch($type){ - case "atom": - case "rss": - $data = api_rss_extra( $data, $user_info); - break; - case "as": - $as = api_format_as( $ret, $user_info); - $as['title'] = App::$config['sitename']. " " . t('Public Timeline'); - $as['link']['url'] = z_root()."/"; - return($as); - break; - } - - return api_apply_template("timeline", $type, $data); - } - api_register_func('api/statuses/public_timeline','api_statuses_public_timeline', true); - - /** - * - - */ - function api_statuses_show( $type){ - if (api_user()===false) return false; - - $user_info = api_get_user($a); - - // params - $id = intval(argv(3)); - if(! $id) - $id = $_REQUEST['id']; - - logger('API: api_statuses_show: '.$id); - - //$include_entities = (x($_REQUEST,'include_entities')?$_REQUEST['include_entities']:false); - $conversation = (x($_REQUEST,'conversation')?1:0); - - $sql_extra = ''; - if ($conversation) - $sql_extra .= " AND `item`.`parent` = %d ORDER BY `received` ASC "; - else - $sql_extra .= " AND `item`.`id` = %d"; - - $item_normal = item_normal(); - $r = q("select * from item where true $item_normal $sql_extra", - intval($id) - ); - - xchan_query($r,true); - - $ret = api_format_items($r,$user_info); - - - if ($conversation) { - $data = array('$statuses' => $ret); - return api_apply_template("timeline", $type, $data); - } else { - $data = array('$status' => $ret[0]); - /*switch($type){ - case "atom": - case "rss": - $data = api_rss_extra( $data, $user_info); - }*/ - return api_apply_template("status", $type, $data); - } - } - api_register_func('api/statuses/show','api_statuses_show', true); - - - /** - * - */ - function api_statuses_repeat( $type){ - if (api_user()===false) return false; - - $user_info = api_get_user($a); - - // params - $id = intval(argv(3)); - - logger('API: api_statuses_repeat: '.$id); - - //$include_entities = (x($_REQUEST,'include_entities')?$_REQUEST['include_entities']:false); - - $observer = App::get_observer(); - - $item_normal = item_normal(); - - $r = q("SELECT * from item where and id = %d $item_normal limit 1", - intval($id) - ); - - if(perm_is_allowed($r[0]['uid'],$observer['xchan_hash'],'view_stream')) { - if ($r[0]['body'] != "") { - $_REQUEST['body'] = html_entity_decode("♲ ", ENT_QUOTES, 'UTF-8')."[zrl=".$r[0]['reply_url']."]".$r[0]['reply_author']."[/zrl] \n".$r[0]['body']; - $_REQUEST['profile_uid'] = api_user(); - $_REQUEST['type'] = 'wall'; - $_REQUEST['api_source'] = true; - $mod = new Zotlabs\Module\Item(); - $mod->post(); - } - } - else - return false; - - if ($type == 'xml') - $ok = "true"; - else - $ok = "ok"; - - return api_apply_template('test', $type, array('$ok' => $ok)); - } - api_register_func('api/statuses/retweet','api_statuses_repeat', true); - - /** - * - */ - - function api_statuses_destroy( $type){ - if (api_user()===false) return false; - - $user_info = api_get_user($a); - - // params - $id = intval(argv(3)); - if($id) { - // first prove that we own the item - - $r = q("select * from item where id = %d and uid = %d limit 1", - intval($id), - intval($user_info['uid']) - ); - if(! $r) - return false; - } - else { - if($_REQUEST['namespace'] && $_REQUEST['remote_id']) { - $r = q("select * from iconfig left join item on iconfig.iid = item.id - where cat = 'system' and k = '%s' and v = '%s' and item.uid = %d limit 1", - dbesc($_REQUEST['namespace']), - dbesc($_REQUEST['remote_id']), - intval($user_info['uid']) - ); - if(! $r) - return false; - $id = $r[0]['iid']; - } - if($_REQUEST['namespace'] && $_REQUEST['comment_id']) { - $r = q("select * from iconfig left join item on item.id = iconfig.iid where cat = 'system' and k = '%s' and v = '%s' and uid = %d and item.id != item.parent limit 1", - dbesc($_REQUEST['namespace']), - dbesc($_REQUEST['comment_id']), - intval($user_info['uid']) - ); - if(! $r) - return false; - $id = $r[0]['iid']; - } - } - if(! $id) - return false; - - logger('API: api_statuses_destroy: '.$id); - require_once('include/items.php'); - drop_item($id, false); - - - if ($type == 'xml') - $ok = "true"; - else - $ok = "ok"; - - return api_apply_template('test', $type, array('$ok' => $ok)); - } - api_register_func('api/statuses/destroy','api_statuses_destroy', true); - - /** - * - * http://developer.twitter.com/doc/get/statuses/mentions - * - */ - - - function api_statuses_mentions( $type){ - if (api_user()===false) return false; - - $user_info = api_get_user($a); - // get last network messages - - - // params - $count = (x($_REQUEST,'count')?$_REQUEST['count']:20); - $page = (x($_REQUEST,'page')?$_REQUEST['page']-1:0); - if ($page<0) $page=0; - $since_id = (x($_REQUEST,'since_id')?$_REQUEST['since_id']:0); - $max_id = (x($_REQUEST,'max_id')?$_REQUEST['max_id']:0); - //$since_id = 0;//$since_id = (x($_REQUEST,'since_id')?$_REQUEST['since_id']:0); - - $start = $page*$count; - - //$include_entities = (x($_REQUEST,'include_entities')?$_REQUEST['include_entities']:false); - - $myurl = z_root() . '/channel/'. App::$user['nickname']; - $myurl = substr($myurl,strpos($myurl,'://')+3); - $myurl = str_replace(array('www.','.'),array('','\\.'),$myurl); - $diasp_url = str_replace('/channel/','/u/',$myurl); - - $sql_extra .= " AND item_mentionsme = 1 "; - if ($max_id > 0) - $sql_extra .= " AND item.id <= " . intval($max_id) . " "; - - require_once('include/security.php'); - $item_normal = item_normal(); - - $r = q("select * from item where uid = " . intval(api_user()) . " - $item_normal $sql_extra - AND id > %d group by mid - order by received desc LIMIT %d OFFSET %d ", - intval($since_id), - intval($count), - intval($start) - ); - - xchan_query($r,true); - - - $ret = api_format_items($r,$user_info); - - - $data = array('$statuses' => $ret); - switch($type){ - case "atom": - case "rss": - $data = api_rss_extra( $data, $user_info); - break; - case "as": - $as = api_format_as( $ret, $user_info); - $as["title"] = App::$config['sitename']." Mentions"; - $as['link']['url'] = z_root()."/"; - return($as); - break; - } - - return api_apply_template("timeline", $type, $data); - } - api_register_func('api/statuses/mentions','api_statuses_mentions', true); - // FIXME?? I don't think mentions and replies are congruent in this case - api_register_func('api/statuses/replies','api_statuses_mentions', true); - - - function api_statuses_user_timeline( $type){ - if (api_user()===false) return false; - - $user_info = api_get_user($a); - // get last network messages - - - logger("api_statuses_user_timeline: api_user: ". api_user() . - "\nuser_info: ".print_r($user_info, true) . - "\n_REQUEST: ".print_r($_REQUEST, true), - LOGGER_DEBUG); - - // params - $count = (x($_REQUEST,'count')?$_REQUEST['count']:20); - $page = (x($_REQUEST,'page')?$_REQUEST['page']-1:0); - if ($page<0) $page=0; - $since_id = (x($_REQUEST,'since_id')?$_REQUEST['since_id']:0); - $exclude_replies = (x($_REQUEST,'exclude_replies')?1:0); - //$since_id = 0;//$since_id = (x($_REQUEST,'since_id')?$_REQUEST['since_id']:0); - - $start = $page*$count; - - $sql_extra = ''; - if ($user_info['self']==1) $sql_extra .= " AND `item`.`wall` = 1 "; - -//FIXME - this isn't yet implemented - if ($exclude_replies > 0) $sql_extra .= ' AND `item`.`parent` = `item`.`id`'; - -// $r = q("SELECT `item`.*, `item`.`id` AS `item_id`, -// `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, -// `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn_id`, `contact`.`self`, -// `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid` -// FROM `item`, `contact` -// WHERE `item`.`uid` = %d -// AND `item`.`contact-id` = %d -// AND `item`.`visible` = 1 and `item`.`moderated` = 0 AND `item`.`deleted` = 0 -// AND `contact`.`id` = `item`.`contact-id` -// AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 -// $sql_extra -// AND `item`.`id`>%d -// ORDER BY `item`.`received` DESC LIMIT %d ,%d ", -// intval(api_user()), -// intval($user_info['id']), -// intval($since_id), -// intval($start), intval($count) -// ); - - $arr = array( - 'uid' => api_user(), - 'since_id' => $since_id, - 'start' => $start, - 'records' => $count); - - if ($user_info['self']==1) - $arr['wall'] = 1; - else - $arr['cid'] = $user_info['id']; - - - $r = items_fetch($arr,App::get_channel(),get_observer_hash()); - - $ret = api_format_items($r,$user_info); - - - $data = array('$statuses' => $ret); - switch($type){ - case "atom": - case "rss": - $data = api_rss_extra( $data, $user_info); - } - - return api_apply_template("timeline", $type, $data); - } - - api_register_func('api/statuses/user_timeline','api_statuses_user_timeline', true); - - - - /** - * Star/unstar an item - * param: id : id of the item - * - * api v1 : https://web.archive.org/web/20131019055350/https://dev.twitter.com/docs/api/1/post/favorites/create/%3Aid - */ - function api_favorites_create_destroy( $type){ - - logger('favorites_create_destroy'); - - if (api_user()===false) - return false; - - $action = str_replace(".".$type,"",argv(2)); - if (argc() > 3) { - $itemid = intval(argv(3)); - } else { - $itemid = intval($_REQUEST['id']); - } - - $item = q("SELECT * FROM item WHERE id = %d AND uid = %d", - intval($itemid), - intval(api_user()) - ); - - if (! $item) - return false; - - switch($action){ - case "create": - $flags = $item[0]['item_starred'] = 1; - break; - case "destroy": - $flags = $item[0]['item_starred'] = 0; - break; - default: - return false; - } - - $r = q("UPDATE item SET item_starred = %d where id = %d and uid = %d", - intval($flags), - intval($itemid), - intval(api_user()) - ); - if(! $r) - return false; - - $item = q("SELECT * FROM item WHERE id = %d AND uid = %d", - intval($itemid), - intval(api_user()) - ); - - xchan_query($item,true); - - - $user_info = api_get_user($a); - $rets = api_format_items($item,$user_info); - $ret = $rets[0]; - - $data = array('$status' => $ret); - switch($type){ - case "atom": - case "rss": - $data = api_rss_extra( $data, $user_info); - } - - return api_apply_template("status", $type, $data); - } - - api_register_func('api/favorites/create', 'api_favorites_create_destroy', true); - api_register_func('api/favorites/destroy', 'api_favorites_create_destroy', true); - - - - function api_favorites( $type){ - if (api_user()===false) - return false; - - $user_info = api_get_user($a); - - // params - $count = (x($_REQUEST,'count')?$_REQUEST['count']:20); - $page = (x($_REQUEST,'page')?$_REQUEST['page']-1:0); - if($page < 0) - $page = 0; - $since_id = (x($_REQUEST,'since_id')?$_REQUEST['since_id']:0); - $max_id = (x($_REQUEST,'max_id')?$_REQUEST['max_id']:0); - $exclude_replies = (x($_REQUEST,'exclude_replies')?1:0); - - $start = $page*$count; - - //$include_entities = (x($_REQUEST,'include_entities')?$_REQUEST['include_entities']:false); - - $sql_extra = ''; - if ($max_id > 0) - $sql_extra .= ' AND `item`.`id` <= '.intval($max_id); - if ($exclude_replies > 0) - $sql_extra .= ' AND `item`.`parent` = `item`.`id`'; - - if (api_user() != $user_info['uid']) { - $observer = App::get_observer(); - require_once('include/permissions.php'); - if(! perm_is_allowed($user_info['uid'],(($observer) ? $observer['xchan_hash'] : ''),'view_stream')) - return ''; - $sql_extra .= " and item_private = 0 "; - } - - $item_normal = item_normal(); - - $r = q("SELECT * from item WHERE uid = %d $item_normal - and item_starred = 1 $sql_extra - AND id > %d - ORDER BY received DESC LIMIT %d ,%d ", - intval($user_info['uid']), - intval($since_id), - intval($start), - intval($count) - ); - - xchan_query($r,true); - - $ret = api_format_items($r,$user_info); - - $data = array('$statuses' => $ret); - switch($type){ - case "atom": - case "rss": - $data = api_rss_extra( $data, $user_info); - break; - case "as": - $as = api_format_as( $ret, $user_info); - $as['title'] = App::$config['sitename']." Home Timeline"; - $as['link']['url'] = z_root()."/".$user_info["screen_name"]."/all"; - return($as); - break; - } - - return api_apply_template("timeline", $type, $data); - - } - - api_register_func('api/favorites','api_favorites', true); - - - - - function api_format_as( $ret, $user_info) { - - $as = array(); - $as['title'] = App::$config['sitename']." Public Timeline"; - $items = array(); - foreach ($ret as $item) { - $singleitem["actor"]["displayName"] = $item["user"]["name"]; - $singleitem["actor"]["id"] = $item["user"]["contact_url"]; - $avatar[0]["url"] = $item["user"]["profile_image_url"]; - $avatar[0]["rel"] = "avatar"; - $avatar[0]["type"] = ""; - $avatar[0]["width"] = 96; - $avatar[0]["height"] = 96; - $avatar[1]["url"] = $item["user"]["profile_image_url"]; - $avatar[1]["rel"] = "avatar"; - $avatar[1]["type"] = ""; - $avatar[1]["width"] = 48; - $avatar[1]["height"] = 48; - $avatar[2]["url"] = $item["user"]["profile_image_url"]; - $avatar[2]["rel"] = "avatar"; - $avatar[2]["type"] = ""; - $avatar[2]["width"] = 24; - $avatar[2]["height"] = 24; - $singleitem["actor"]["avatarLinks"] = $avatar; - - $singleitem["actor"]["image"]["url"] = $item["user"]["profile_image_url"]; - $singleitem["actor"]["image"]["rel"] = "avatar"; - $singleitem["actor"]["image"]["type"] = ""; - $singleitem["actor"]["image"]["width"] = 96; - $singleitem["actor"]["image"]["height"] = 96; - $singleitem["actor"]["type"] = "person"; - $singleitem["actor"]["url"] = $item["person"]["contact_url"]; - $singleitem["actor"]["statusnet:profile_info"]["local_id"] = $item["user"]["id"]; - $singleitem["actor"]["statusnet:profile_info"]["following"] = $item["user"]["following"] ? "true" : "false"; - $singleitem["actor"]["statusnet:profile_info"]["blocking"] = "false"; - $singleitem["actor"]["contact"]["preferredUsername"] = $item["user"]["screen_name"]; - $singleitem["actor"]["contact"]["displayName"] = $item["user"]["name"]; - $singleitem["actor"]["contact"]["addresses"] = ""; - - $singleitem["body"] = $item["text"]; - $singleitem["object"]["displayName"] = $item["text"]; - $singleitem["object"]["id"] = $item["url"]; - $singleitem["object"]["type"] = "note"; - $singleitem["object"]["url"] = $item["url"]; - //$singleitem["context"] =; - $singleitem["postedTime"] = date("c", strtotime($item["published"])); - $singleitem["provider"]["objectType"] = "service"; - $singleitem["provider"]["displayName"] = "Test"; - $singleitem["provider"]["url"] = "http://test.tld"; - $singleitem["title"] = $item["text"]; - $singleitem["verb"] = "post"; - $singleitem["statusnet:notice_info"]["local_id"] = $item["id"]; - $singleitem["statusnet:notice_info"]["source"] = $item["source"]; - $singleitem["statusnet:notice_info"]["favorite"] = "false"; - $singleitem["statusnet:notice_info"]["repeated"] = "false"; - //$singleitem["original"] = $item; - $items[] = $singleitem; - } - $as['items'] = $items; - $as['link']['url'] = z_root()."/".$user_info["screen_name"]."/all"; - $as['link']['rel'] = "alternate"; - $as['link']['type'] = "text/html"; - return($as); - } - - function api_format_message($item, $recipient, $sender) { - // standard meta information - $ret = array( - 'id' => $item['id'], - 'created_at' => api_date($item['created']), - 'sender_id' => $sender['id'] , - 'sender_screen_name' => $sender['screen_name'], - 'sender' => $sender, - 'recipient_id' => $recipient['id'], - 'recipient_screen_name' => $recipient['screen_name'], - 'recipient' => $recipient, - ); - unobscure_mail($item); - //don't send title to regular StatusNET requests to avoid confusing these apps - if (x($_GET, 'getText')) { - $ret['title'] = $item['title'] ; - if ($_GET["getText"] == "html") { - $ret['text'] = prepare_text($item['body'],$item['mimetype']); - } - elseif ($_GET["getText"] == "plain") { - $ret['text'] = html2plain(prepare_text($item['body'],$item['mimetype']), 0); - } - } - else { - $ret['text'] = $item['title']."\n".html2plain(prepare_text($item['body'],$item['mimetype']), 0); - } - if (isset($_GET["getUserObjects"]) && $_GET["getUserObjects"] == "false") { - unset($ret['sender']); - unset($ret['recipient']); - } - - return $ret; - } - - function api_format_items($r,$user_info) { - - //logger('api_format_items: ' . print_r($r,true)); - - //logger('api_format_items: ' . print_r($user_info,true)); - - $ret = array(); - - $x = array('items' => $r,'api_user' => api_user(),'user_info' => $user_info); - call_hooks('api_format_items',$x); - $r = $x['items']; - - if(! $r) - return $ret; - - foreach($r as $item) { - - localize_item($item); - - $status_user = (($item['author_xchan']==$user_info['guid'])?$user_info: api_item_get_user($item)); - if(array_key_exists('status',$status_user)) - unset($status_user['status']); - - if($item['parent'] != $item['id']) { - $r = q("select id from item where parent= %d and id < %d order by id desc limit 1", - intval($item['parent']), - intval($item['id']) - ); - if ($r) - $in_reply_to_status_id = $r[0]['id']; - else - $in_reply_to_status_id = $item['parent']; - - xchan_query($r,true); - - $in_reply_to_screen_name = $r[0]['author']['xchan_name']; - $in_reply_to_user_id = $r[0]['author']['abook_id']; - - } else { - $in_reply_to_screen_name = ''; - $in_reply_to_user_id = 0; - $in_reply_to_status_id = 0; - } - unobscure($item); - // Workaround for ostatus messages where the title is identically to the body - $statusbody = trim(html2plain(prepare_text($item['body'],$item['mimetype']), 0)); - $statustitle = trim($item['title']); - - if (($statustitle != '') and (strpos($statusbody, $statustitle) !== false)) - $statustext = trim($statusbody); - else - $statustext = trim($statustitle."\n\n".$statusbody); - - - $status = array( - 'text' => $statustext, - 'truncated' => False, - 'created_at' => api_date($item['created']), - 'in_reply_to_status_id' => $in_reply_to_status_id, - 'source' => (($item['app']) ? $item['app'] : 'web'), - 'id' => intval($item['id']), - 'in_reply_to_user_id' => $in_reply_to_user_id, - 'in_reply_to_screen_name' => $in_reply_to_screen_name, - 'geo' => '', - 'favorited' => (intval($item['item_starred']) ? true : false), - 'user' => $status_user , - 'statusnet_html' => trim(prepare_text($item['body'],$item['mimetype'])), - - 'statusnet_conversation_id' => $item['parent'], - ); - - // Seesmic doesn't like the following content - if ($_SERVER['HTTP_USER_AGENT'] != 'Seesmic') { - $status2 = array( - 'updated' => api_date($item['edited']), - 'published' => api_date($item['created']), - 'message_id' => $item['mid'], - 'url' => $item['plink'], - 'coordinates' => $item['coord'], - 'place' => $item['location'], - 'contributors' => '', - 'annotations' => '', - 'entities' => '', - 'objecttype' => (($item['obj_type']) ? $item['obj_type'] : ACTIVITY_OBJ_NOTE), - 'verb' => (($item['verb']) ? $item['verb'] : ACTIVITY_POST), - 'self' => z_root()."/api/statuses/show/".$item['id'].".".$type, - 'edit' => z_root()."/api/statuses/show/".$item['id'].".".$type, - ); - - $status = array_merge($status, $status2); - } - - $ret[]=$status; - }; - return $ret; - } - - - function api_account_rate_limit_status($type) { - - $hash = array( - 'reset_time_in_seconds' => strtotime('now + 1 hour'), - 'remaining_hits' => (string) 150, - 'hourly_limit' => (string) 150, - 'reset_time' => datetime_convert('UTC','UTC','now + 1 hour',ATOM_TIME), - ); - if ($type == "xml") - $hash['resettime_in_seconds'] = $hash['reset_time_in_seconds']; - - return api_apply_template('ratelimit', $type, array('$hash' => $hash)); - - } - api_register_func('api/account/rate_limit_status','api_account_rate_limit_status',true); - - function api_help_test($type) { - - if ($type == 'xml') - $ok = "true"; - else - $ok = "ok"; - - return api_apply_template('test', $type, array('$ok' => $ok)); - - } - api_register_func('api/help/test','api_help_test',false); - - /** - * https://dev.twitter.com/docs/api/1/get/statuses/friends - * This function is deprecated by Twitter - * returns: json, xml - **/ - function api_statuses_f( $type, $qtype) { - if (api_user()===false) return false; - $user_info = api_get_user($a); - - - // friends and followers only for self - if ($user_info['self']==0){ - return false; - } - - if (x($_GET,'cursor') && $_GET['cursor']=='undefined'){ - /* this is to stop Hotot to load friends multiple times - * I'm not sure if I'm missing return something or - * is a bug in hotot. Workaround, meantime - */ - - /*$ret=Array(); - return array('$users' => $ret);*/ - return false; - } - -// @fixme - update for hubzilla extensible perms using abconfig or find a better way to do it - // For Red, the closest thing we can do to figure out if you're friends is if both of you are sending each other your streams. - // This won't work if either of you send your stream to everybody on the network - if($qtype == 'friends') - $sql_extra = sprintf(" AND ( abook_their_perms & %d )>0 and ( abook_my_perms & %d )>0 ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM)); - if($qtype == 'followers') - $sql_extra = sprintf(" AND ( abook_my_perms & %d )>0 and not ( abook_their_perms & %d )>0 ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM)); - - $r = q("SELECT abook_id FROM abook where abook_self = 0 and abook_channel = %d $sql_extra", - intval(api_user()) - ); - - $ret = array(); - foreach($r as $cid){ - $ret[] = api_get_user( $cid['abook_id']); - } - - - return array('$users' => $ret); - - } - function api_statuses_friends( $type){ - $data = api_statuses_f($type,"friends"); - if ($data===false) return false; - return api_apply_template("friends", $type, $data); - } - function api_statuses_followers( $type){ - $data = api_statuses_f($type,"followers"); - if ($data===false) return false; - return api_apply_template("friends", $type, $data); - } - api_register_func('api/statuses/friends','api_statuses_friends',true); - api_register_func('api/statuses/followers','api_statuses_followers',true); - - - - - - - function api_statusnet_config($type) { - - load_config('system'); - - $name = get_config('system','sitename'); - $server = App::get_hostname(); - $logo = z_root() . '/images/rm-64.png'; - $email = get_config('system','admin_email'); - $closed = ((get_config('system','register_policy') == REGISTER_CLOSED) ? 'true' : 'false'); - $private = ((get_config('system','block_public')) ? 'true' : 'false'); - $textlimit = (string) ((get_config('system','max_import_size')) ? get_config('system','max_import_size') : 200000); - if(get_config('system','api_import_size')) - $texlimit = string(get_config('system','api_import_size')); - $ssl = ((get_config('system','have_ssl')) ? 'true' : 'false'); - $sslserver = (($ssl === 'true') ? str_replace('http:','https:',z_root()) : ''); - - $config = array( - 'site' => array('name' => $name,'server' => $server, 'theme' => 'default', 'path' => '', - 'logo' => $logo, 'fancy' => 'true', 'language' => 'en', 'email' => $email, 'broughtby' => '', - 'broughtbyurl' => '', 'timezone' => 'UTC', 'closed' => $closed, 'inviteonly' => 'false', - 'private' => $private, 'textlimit' => $textlimit, 'sslserver' => $sslserver, 'ssl' => $ssl, - 'shorturllength' => '30', - 'hubzilla' => array( - 'PLATFORM_NAME' => Zotlabs\Lib\System::get_platform_name(), - 'STD_VERSION' => Zotlabs\Lib\System::get_project_version(), - 'ZOT_REVISION' => ZOT_REVISION, - 'DB_UPDATE_VERSION' => Zotlabs\Lib\System::get_update_version() - ) - )); - - return api_apply_template('config', $type, array('$config' => $config)); - - } - api_register_func('api/statusnet/config','api_statusnet_config',false); - api_register_func('api/friendica/config','api_statusnet_config',false); - api_register_func('api/red/config','api_statusnet_config',false); - api_register_func('api/hz/1.0/config','api_statusnet_config',false); - - function api_statusnet_version($type) { - - // liar - - if($type === 'xml') { - header("Content-type: application/xml"); - echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n" . '<version>0.9.7</version>' . "\r\n"; - killme(); - } - elseif($type === 'json') { - header("Content-type: application/json"); - echo '"0.9.7"'; - killme(); - } - } - api_register_func('api/statusnet/version','api_statusnet_version',false); - - - function api_friendica_version($type) { - - if($type === 'xml') { - header("Content-type: application/xml"); - echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n" . '<version>' . Zotlabs\Lib\System::get_project_version() . '</version>' . "\r\n"; - killme(); - } - elseif($type === 'json') { - header("Content-type: application/json"); - echo '"' . Zotlabs\Lib\System::get_project_version() . '"'; - killme(); - } - } - api_register_func('api/friendica/version','api_friendica_version',false); - api_register_func('api/red/version','api_friendica_version',false); - api_register_func('api/hz/1.0/version','api_friendica_version',false); - - - function api_ff_ids($type,$qtype) { - if(! api_user()) - return false; - - - // For Red, the closest thing we can do to figure out if you're friends is if both of you are sending each other your streams. - // This won't work if either of you send your stream to everybody on the network - - if($qtype == 'friends') - $sql_extra = sprintf(" AND ( abook_their_perms & %d )>0 and ( abook_my_perms & %d )>0 ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM)); - if($qtype == 'followers') - $sql_extra = sprintf(" AND ( abook_my_perms & %d )>0 and not ( abook_their_perms & %d )>0 ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM)); - - $r = q("SELECT abook_id FROM abook where abook_self = 0 and abook_channel = %d $sql_extra", - intval(api_user()) - ); - - if(is_array($r)) { - if($type === 'xml') { - header("Content-type: application/xml"); - echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n" . '<ids>' . "\r\n"; - foreach($r as $rr) - echo '<id>' . $rr['abook_id'] . '</id>' . "\r\n"; - echo '</ids>' . "\r\n"; - killme(); - } - elseif($type === 'json') { - $ret = array(); - header("Content-type: application/json"); - foreach($r as $rr) $ret[] = $rr['abook_id']; - echo json_encode($ret); - killme(); - } - } - } - - function api_friends_ids($type) { - api_ff_ids($type,'friends'); - } - function api_followers_ids($type) { - api_ff_ids($type,'followers'); - } - api_register_func('api/friends/ids','api_friends_ids',true); - api_register_func('api/followers/ids','api_followers_ids',true); - - - function api_direct_messages_new( $type) { - if (api_user()===false) return false; - - if (!x($_POST, "text") || !x($_POST,"screen_name")) return; - - $sender = api_get_user($a); - - require_once("include/message.php"); - - // in a decentralised world the screen name is ambiguous - - $r = q("SELECT `abook_id` FROM `abook` left join xchan on abook_xchan = xchan_hash WHERE `abook_channel`=%d and xchan_addr like '%s'", - intval(api_user()), - dbesc($_POST['screen_name'] . '@%') - ); - - $recipient = api_get_user( $r[0]['abook_id']); - $replyto = ''; - $sub = ''; - if (x($_REQUEST,'replyto')) { - $r = q('SELECT `parent_mid`, `title` FROM `mail` WHERE `uid`=%d AND `id`=%d', - intval(api_user()), - intval($_REQUEST['replyto'])); - $replyto = $r[0]['parent_mid']; - $sub = $r[0]['title']; - } - else { - if (x($_REQUEST,'title')) { - $sub = $_REQUEST['title']; - } - else { - $sub = ((strlen($_POST['text'])>10)?substr($_POST['text'],0,10)."...":$_POST['text']); - } - } - - $id = send_message(api_user(),$recipient['guid'], $_POST['text'], $sub, $replyto); - - if ($id>-1) { - $r = q("SELECT * FROM `mail` WHERE id=%d", intval($id)); - $ret = api_format_message($r[0], $recipient, $sender); - - } else { - $ret = array("error"=>$id); - } - - $data = Array('$messages'=>$ret); - - switch($type){ - case "atom": - case "rss": - $data = api_rss_extra( $data, $user_info); - } - - return api_apply_template("direct_messages", $type, $data); - - } - api_register_func('api/direct_messages/new','api_direct_messages_new',true); - - function api_direct_messages_box( $type, $box) { - if (api_user()===false) return false; - - $user_info = api_get_user($a); - - // params - $count = (x($_GET,'count')?$_GET['count']:20); - $page = (x($_REQUEST,'page')?$_REQUEST['page']-1:0); - if ($page<0) $page=0; - - $start = $page*$count; - $channel = App::get_channel(); - - $profile_url = z_root() . '/channel/' . $channel['channel_address']; - if ($box=="sentbox") { - $sql_extra = "`from_xchan`='".dbesc( $channel['channel_hash'] )."'"; - } - elseif ($box=="conversation") { - $sql_extra = "`parent_mid`='".dbesc( $_GET["uri"] ) ."'"; - } - elseif ($box=="all") { - $sql_extra = "true"; - } - elseif ($box=="inbox") { - $sql_extra = "`from_xchan`!='".dbesc( $channel['channel_hash'] )."'"; - } - - $r = q("SELECT * FROM `mail` WHERE channel_id = %d AND $sql_extra ORDER BY created DESC LIMIT %d OFFSET %d", - intval(api_user()), - intval($count), intval($start) - ); - - $ret = Array(); - if($r) { - foreach($r as $item) { - if ($item['from_xchan'] == $channel['channel_hash']) { - $sender = $user_info; - $recipient = api_get_user( null, $item['to_xchan']); - } - else { - $sender = api_get_user( null, $item['from_xchan']); - $recipient = $user_info; - } - - $ret[]=api_format_message($item, $recipient, $sender); - } - } - - - $data = array('$messages' => $ret); - switch($type){ - case "atom": - case "rss": - $data = api_rss_extra( $data, $user_info); - } - - return api_apply_template("direct_messages", $type, $data); - - } - - function api_direct_messages_sentbox( $type){ - return api_direct_messages_box( $type, "sentbox"); - } - function api_direct_messages_inbox( $type){ - return api_direct_messages_box( $type, "inbox"); - } - function api_direct_messages_all( $type){ - return api_direct_messages_box( $type, "all"); - } - function api_direct_messages_conversation( $type){ - return api_direct_messages_box( $type, "conversation"); - } - api_register_func('api/direct_messages/conversation','api_direct_messages_conversation',true); - api_register_func('api/direct_messages/all','api_direct_messages_all',true); - api_register_func('api/direct_messages/sent','api_direct_messages_sentbox',true); - api_register_func('api/direct_messages','api_direct_messages_inbox',true); function api_oauth_request_token( $type){ @@ -2355,7 +220,7 @@ require_once('include/api_auth.php'); $r = $oauth->fetch_request_token($req); }catch(Exception $e){ logger('oauth_exception: ' . print_r($e->getMessage(),true)); - echo "error=". OAuth1Util::urlencode_rfc3986($e->getMessage()); + echo 'error=' . OAuth1Util::urlencode_rfc3986($e->getMessage()); killme(); } echo $r; @@ -2365,42 +230,15 @@ require_once('include/api_auth.php'); function api_oauth_access_token( $type){ try{ $oauth = new ZotOAuth1(); - $req = OAuth1Request::from_request(); - $r = $oauth->fetch_access_token($req); - }catch(Exception $e){ - echo "error=". OAuth1Util::urlencode_rfc3986($e->getMessage()); killme(); + $req = OAuth1Request::from_request(); + $r = $oauth->fetch_access_token($req); + } + catch(Exception $e) { + echo 'error=' . OAuth1Util::urlencode_rfc3986($e->getMessage()); + killme(); } echo $r; killme(); } - api_register_func('api/oauth/request_token', 'api_oauth_request_token', false); - api_register_func('api/oauth/access_token', 'api_oauth_access_token', false); - - -/* -Not implemented by now: -statuses/retweets_of_me -friendships/create -friendships/destroy -friendships/exists -friendships/show -account/update_location -account/update_profile_background_image -account/update_profile_image -blocks/create -blocks/destroy - -Not implemented in status.net: -statuses/retweeted_to_me -statuses/retweeted_by_me -direct_messages/destroy -account/end_session -account/update_delivery_device -notifications/follow -notifications/leave -blocks/exists -blocks/blocking -lists -*/ diff --git a/include/api_zot.php b/include/api_zot.php new file mode 100644 index 000000000..d1979c3ae --- /dev/null +++ b/include/api_zot.php @@ -0,0 +1,481 @@ +<?php + + function zot_api_init() { + api_register_func('api/export/basic','api_export_basic', true); + api_register_func('api/red/channel/export/basic','api_export_basic', true); + api_register_func('api/z/1.0/channel/export/basic','api_export_basic', true); + api_register_func('api/red/channel/stream','api_channel_stream', true); + api_register_func('api/z/1.0/channel/stream','api_channel_stream', true); + api_register_func('api/red/files','api_attach_list', true); + api_register_func('api/z/1.0/files','api_attach_list', true); + api_register_func('api/red/filemeta', 'api_file_meta', true); + api_register_func('api/z/1.0/filemeta', 'api_file_meta', true); + api_register_func('api/red/filedata', 'api_file_data', true); + api_register_func('api/z/1.0/filedata', 'api_file_data', true); + api_register_func('api/red/file/export', 'api_file_export', true); + api_register_func('api/z/1.0/file/export', 'api_file_export', true); + api_register_func('api/red/file', 'api_file_detail', true); + api_register_func('api/z/1.0/file', 'api_file_detail', true); + api_register_func('api/red/albums','api_albums', true); + api_register_func('api/z/1.0/albums','api_albums', true); + api_register_func('api/red/photos','api_photos', true); + api_register_func('api/z/1.0/photos','api_photos', true); + api_register_func('api/red/photo', 'api_photo_detail', true); + api_register_func('api/z/1.0/photo', 'api_photo_detail', true); + api_register_func('api/red/group_members','api_group_members', true); + api_register_func('api/z/1.0/group_members','api_group_members', true); + api_register_func('api/red/group','api_group', true); + api_register_func('api/z/1.0/group','api_group', true); + api_register_func('api/red/xchan','api_red_xchan',true); + api_register_func('api/z/1.0/xchan','api_red_xchan',true); + api_register_func('api/red/item/update','zot_item_update', true); + api_register_func('api/z/1.0/item/update','zot_item_update', true); + api_register_func('api/red/item/full','red_item', true); + api_register_func('api/z/1.0/item/full','red_item', true); + + api_register_func('api/z/1.0/network/stream','api_network_stream', true); + api_register_func('api/z/1.0/abook','api_zot_abook_xchan',true); + api_register_func('api/z/1.0/abconfig','api_zot_abconfig',true); + api_register_func('api/z/1.0/perm_allowed','api_zot_perm_allowed',true); + + return; + } + + + /* + * Red basic channel export + */ + + function api_export_basic($type) { + if(api_user() === false) { + logger('api_export_basic: no user'); + return false; + } + + json_return_and_die(identity_basic_export(api_user(),(($_REQUEST['posts']) ? intval($_REQUEST['posts']) : 0 ))); + } + + + function api_network_stream($type) { + if(api_user() === false) { + logger('api_channel_stream: no user'); + return false; + } + + $channel = channelx_by_n(api_user()); + if(! $channel) + return false; + + + if($_SERVER['REQUEST_METHOD'] == 'POST') { + // json_return_and_die(post_activity_item($_REQUEST)); + } + else { + $mindate = (($_REQUEST['mindate']) ? datetime_convert('UTC','UTC',$_REQUEST['mindate']) : ''); + if(! $mindate) + $mindate = datetime_convert('UTC','UTC', 'now - 14 days'); + + $arr = $_REQUEST; + $ret = []; + $i = items_fetch($arr,App::get_channel(),get_observer_hash()); + if($i) { + foreach($i as $iv) { + $ret[] = encode_item($iv); + } + } + + json_return_and_die($ret); + } + } + + + + + + + function api_channel_stream($type) { + if(api_user() === false) { + logger('api_channel_stream: no user'); + return false; + } + + $channel = channelx_by_n(api_user()); + if(! $channel) + return false; + + + if($_SERVER['REQUEST_METHOD'] == 'POST') { + json_return_and_die(post_activity_item($_REQUEST)); + } + else { + $mindate = (($_REQUEST['mindate']) ? datetime_convert('UTC','UTC',$_REQUEST['mindate']) : ''); + if(! $mindate) + $mindate = datetime_convert('UTC','UTC', 'now - 14 days'); + + json_return_and_die(zot_feed($channel['channel_id'],$channel['channel_hash'],[ 'mindate' => $mindate ])); + } + } + + function api_attach_list($type) { + if(api_user() === false) + return false; + + logger('api_user: ' . api_user()); + $hash = ((array_key_exists('filehash',$_REQUEST)) ? $_REQUEST['filehash'] : ''); + $filename = ((array_key_exists('filename',$_REQUEST)) ? $_REQUEST['filename'] : ''); + $filetype = ((array_key_exists('filetype',$_REQUEST)) ? $_REQUEST['filetype'] : ''); + $start = ((array_key_exists('start',$_REQUEST)) ? intval($_REQUEST['start']) : 0); + $records = ((array_key_exists('records',$_REQUEST)) ? intval($_REQUEST['records']) : 0); + + $x = attach_list_files(api_user(),get_observer_hash(),$hash,$filename,$filetype,'created asc',$start,$records); + if($start || $records) { + $x['start'] = $start; + $x['records'] = count($x['results']); + } + + json_return_and_die($x); + } + + + function api_file_meta($type) { + if(api_user() === false) + return false; + if(! $_REQUEST['file_id']) return false; + $r = q("select * from attach where uid = %d and hash = '%s' limit 1", + intval(api_user()), + dbesc($_REQUEST['file_id']) + ); + if($r) { + unset($r[0]['content']); + $ret = array('attach' => $r[0]); + json_return_and_die($ret); + } + killme(); + } + + + + function api_file_data($type) { + if(api_user() === false) + return false; + if(! $_REQUEST['file_id']) return false; + $start = (($_REQUEST['start']) ? intval($_REQUEST['start']) : 0); + $length = (($_REQUEST['length']) ? intval($_REQUEST['length']) : 0); + + $r = q("select * from attach where uid = %d and hash like '%s' limit 1", + intval(api_user()), + dbesc($_REQUEST['file_id'] . '%') + ); + if($r) { + $ptr = $r[0]; + if($length === 0) + $length = intval($ptr['filesize']); + + if($ptr['is_dir']) + $ptr['content'] = ''; + elseif(! intval($r[0]['os_storage'])) { + $ptr['start'] = $start; + $x = substr(dbunescbin($ptr['content']),$start,$length); + $ptr['length'] = strlen($x); + $ptr['content'] = base64_encode($x); + } + else { + $fp = fopen(dbunescbin($ptr['content']),'r'); + if($fp) { + $seek = fseek($fp,$start,SEEK_SET); + $x = fread($fp,$length); + $ptr['start'] = $start; + $ptr['length'] = strlen($x); + $ptr['content'] = base64_encode($x); + } + } + + $ret = array('attach' => $ptr); + json_return_and_die($ret); + } + killme(); + } + + + function api_file_export($type) { + if(api_user() === false) + return false; + if(! $_REQUEST['file_id']) + return false; + + $ret = attach_export_data(api_user(),$_REQUEST['file_id']); + if($ret) { + json_return_and_die($ret); + } + killme(); + } + + + function api_file_detail($type) { + if(api_user() === false) + return false; + if(! $_REQUEST['file_id']) return false; + $r = q("select * from attach where uid = %d and hash = '%s' limit 1", + intval(api_user()), + dbesc($_REQUEST['file_id']) + ); + if($r) { + if($r[0]['is_dir']) + $r[0]['content'] = ''; + elseif(intval($r[0]['os_storage'])) + $r[0]['content'] = base64_encode(file_get_contents(dbunescbin($r[0]['content']))); + else + $r[0]['content'] = base64_encode(dbunescbin($r[0]['content'])); + + $ret = array('attach' => $r[0]); + json_return_and_die($ret); + } + killme(); + } + + + + function api_albums($type) { + if(api_user() === false) + return false; + json_return_and_die(photos_albums_list(App::get_channel(),App::get_observer())); + } + + function api_photos($type) { + if(api_user() === false) + return false; + $album = $_REQUEST['album']; + json_return_and_die(photos_list_photos(App::get_channel(),App::get_observer(),$album)); + } + + function api_photo_detail($type) { + if(api_user() === false) + return false; + if(! $_REQUEST['photo_id']) return false; + $scale = ((array_key_exists('scale',$_REQUEST)) ? intval($_REQUEST['scale']) : 0); + $r = q("select * from photo where uid = %d and resource_id = '%s' and imgscale = %d limit 1", + intval(local_channel()), + dbesc($_REQUEST['photo_id']), + intval($scale) + ); + if($r) { + $data = dbunescbin($r[0]['content']); + if(array_key_exists('os_storage',$r[0]) && intval($r[0]['os_storage'])) + $data = file_get_contents($data); + $r[0]['content'] = base64_encode($data); + $ret = array('photo' => $r[0]); + $i = q("select id from item where uid = %d and resource_type = 'photo' and resource_id = '%s' limit 1", + intval(local_channel()), + dbesc($_REQUEST['photo_id']) + ); + if($i) { + $ii = q("select * from item where parent = %d order by id", + intval($i[0]['id']) + ); + if($ii) { + xchan_query($ii,true,0); + $ii = fetch_post_tags($ii,true); + if($ii) { + $ret['item'] = array(); + foreach($ii as $iii) + $ret['item'][] = encode_item($iii,true); + } + } + } + + json_return_and_die($ret); + } + killme(); + } + + function api_group_members($type) { + if(api_user() === false) + return false; + + $r = null; + + if($_REQUEST['group_id']) { + $r = q("select * from groups where uid = %d and id = %d limit 1", + intval(api_user()), + intval($_REQUEST['group_id']) + ); + } + elseif($_REQUEST['group_name']) { + $r = q("select * from groups where uid = %d and gname = '%s' limit 1", + intval(api_user()), + dbesc($_REQUEST['group_name']) + ); + } + + if($r) { + $x = q("select * from group_member left join abook on abook_xchan = xchan and abook_channel = group_member.uid left join xchan on group_member.xchan = xchan.xchan_hash + where gid = %d", + intval($r[0]['id']) + ); + json_return_and_die($x); + } + + } + + function api_group($type) { + if(api_user() === false) + return false; + + $r = q("select * from groups where uid = %d", + intval(api_user()) + ); + json_return_and_die($r); + } + + + function api_red_xchan($type) { + if(api_user() === false) + return false; + logger('api_xchan'); + require_once('include/hubloc.php'); + + if($_SERVER['REQUEST_METHOD'] === 'POST') { + // $r = xchan_store($_REQUEST); + } + $r = xchan_fetch($_REQUEST); + json_return_and_die($r); + }; + + function api_zot_abook_xchan($type) { + logger('api_abook_xchan'); + + if(api_user() === false) + return false; + + $sql_extra = ((array_key_exists('abook_id',$_REQUEST) && intval($_REQUEST['abook_id'])) ? ' and abook_id = ' . intval($_REQUEST['abook_id']) . ' ' : ''); + if($_SERVER['REQUEST_METHOD'] === 'POST') { + // update + } + $r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d $sql_extra ", + intval(api_user()) + ); + + json_return_and_die($r); + }; + + function api_zot_abconfig($type) { + + if(api_user() === false) + return false; + + $sql_extra = ((array_key_exists('abook_id',$_REQUEST) && intval($_REQUEST['abook_id'])) ? ' and abook_id = ' . intval($_REQUEST['abook_id']) . ' ' : ''); + if($_SERVER['REQUEST_METHOD'] === 'POST') { + // update + } + $r = q("select abconfig.* from abconfig left join abook on abook_xchan = abconfig.xchan and abook_channel = abconfig.chan where abconfig.chan = %d $sql_extra ", + intval(api_user()) + ); + + json_return_and_die($r); + + } + + function api_zot_perm_allowed($type) { + if(api_user() === false) + return false; + + $perm = ((array_key_exists('perm',$_REQUEST)) ? $_REQUEST['perm'] : ''); + + if(array_key_exists('abook_id',$_REQUEST) && intval($_REQUEST['abook_id'])) { + $r = q("select abook_xchan as hash from abook where abook_id = %d and abook_channel = %d limit 1", + intval($_REQUEST['abook_id']), + intval(api_user()) + ); + } + else { + $r = xchan_fetch($_REQUEST); + } + + $x = false; + + if($r) { + if($perm) + $x = [ [ 'perm' => $perm, 'allowed' => perm_is_allowed(api_user(), $r[0]['hash'], $perm)] ]; + else { + $x = []; + $p = get_all_perms(api_user(),$r[0]['hash']); + if($p) { + foreach($p as $k => $v) + $x[] = [ 'perm' => $k, 'allowed' => $v ]; + } + } + } + + json_return_and_die($x); + + } + + function zot_item_update($type) { + + if (api_user() === false) { + logger('api_red_item_store: no user'); + return false; + } + + logger('api_red_item_store: REQUEST ' . print_r($_REQUEST,true)); + logger('api_red_item_store: FILES ' . print_r($_FILES,true)); + + + // set this so that the item_post() function is quiet and doesn't redirect or emit json + + $_REQUEST['api_source'] = true; + $_REQUEST['profile_uid'] = api_user(); + + if(x($_FILES,'media')) { + $_FILES['userfile'] = $_FILES['media']; + // upload the image if we have one + $mod = new Zotlabs\Module\Wall_attach(); + $media = $mod->post(); + if($media) + $_REQUEST['body'] .= "\n\n" . $media; + } + + $mod = new Zotlabs\Module\Item(); + $x = $mod->post(); + json_return_and_die($x); + } + + + + function red_item($type) { + + if (api_user() === false) { + logger('api_red_item_full: no user'); + return false; + } + + if($_REQUEST['mid']) { + $arr = array('mid' => $_REQUEST['mid']); + } + elseif($_REQUEST['item_id']) { + $arr = array('item_id' => $_REQUEST['item_id']); + } + else + json_return_and_die(array()); + + $arr['start'] = 0; + $arr['records'] = 999999; + $arr['item_type'] = '*'; + + $i = items_fetch($arr,App::get_channel(),get_observer_hash()); + + if(! $i) + json_return_and_die(array()); + + $ret = array(); + $tmp = array(); + foreach($i as $ii) { + $tmp[] = encode_item($ii,true); + } + $ret['item'] = $tmp; + + json_return_and_die($ret); + } + + + diff --git a/include/attach.php b/include/attach.php index b5c334d3e..ba2f60a90 100644 --- a/include/attach.php +++ b/include/attach.php @@ -159,7 +159,6 @@ function attach_count_files($channel_id, $observer, $hash = '', $filename = '', intval($channel_id) ); - $ret['success'] = ((is_array($r)) ? true : false); $ret['results'] = ((is_array($r)) ? count($r) : false); @@ -198,13 +197,13 @@ function attach_list_files($channel_id, $observer, $hash = '', $filename = '', $ $sql_extra .= protect_sprintf(" and hash = '" . dbesc($hash) . "' "); if($filename) - $sql_extra .= protect_sprintf(" and filename like '@" . dbesc($filename) . "@' "); + $sql_extra .= protect_sprintf(" and filename like '%" . dbesc($filename) . "%' "); if($filetype) - $sql_extra .= protect_sprintf(" and filetype like '@" . dbesc($filetype) . "@' "); + $sql_extra .= protect_sprintf(" and filetype like '%" . dbesc($filetype) . "%' "); if($entries) - $limit = " limit " . intval($start) . ", " . intval(entries) . " "; + $limit = " limit " . intval($start) . ", " . intval($entries) . " "; // Retrieve all columns except 'data' @@ -226,7 +225,8 @@ function attach_list_files($channel_id, $observer, $hash = '', $filename = '', $ * This could exhaust memory so most useful only when immediately sending the data. * * @param string $hash - * @param int $rev Revision + * @param string $observer_hash + * @param int $rev (optional) Revision default 0 * @return array */ function attach_by_hash($hash, $observer_hash, $rev = 0) { @@ -285,7 +285,7 @@ function attach_by_hash($hash, $observer_hash, $rev = 0) { function attach_can_view_folder($uid,$ob_hash,$folder_hash) { $sql_extra = permissions_sql($uid,$ob_hash); - $hash = $folder_hash; + $hash = $folder_hash; $result = false; do { @@ -295,9 +295,10 @@ function attach_can_view_folder($uid,$ob_hash,$folder_hash) { ); if(! $r) return false; + $hash = $r[0]['folder']; - } - while($hash); + } while($hash); + return true; } @@ -308,8 +309,9 @@ function attach_can_view_folder($uid,$ob_hash,$folder_hash) { * Returns the entire attach structure excluding data. * * @see attach_by_hash() - * @param $hash - * @param $rev revision default 0 + * @param string $hash + * @param string $observer_hash + * @param int $rev (optional) revision default 0 * @return associative array with everything except data * * \e boolean \b success boolean true or false * * \e string \b message (optional) only when success is false @@ -335,12 +337,12 @@ function attach_by_hash_nodata($hash, $observer_hash, $rev = 0) { return $ret; } - if(! perm_is_allowed($r[0]['uid'],$observer_hash,'view_storage')) { + if(! perm_is_allowed($r[0]['uid'], $observer_hash, 'view_storage')) { $ret['message'] = t('Permission denied.'); return $ret; } - $sql_extra = permissions_sql($r[0]['uid'],$observer_hash); + $sql_extra = permissions_sql($r[0]['uid'], $observer_hash); // Now we'll see if we can access the attachment @@ -355,14 +357,13 @@ function attach_by_hash_nodata($hash, $observer_hash, $rev = 0) { } if($r[0]['folder']) { - $x = attach_can_view_folder($r[0]['uid'],$observer_hash,$r[0]['folder']); + $x = attach_can_view_folder($r[0]['uid'], $observer_hash, $r[0]['folder']); if(! $x) { $ret['message'] = t('Permission denied.'); return $ret; } } - $ret['success'] = true; $ret['data'] = $r[0]; @@ -378,25 +379,18 @@ function attach_by_hash_nodata($hash, $observer_hash, $rev = 0) { * @note Requires an input field \e userfile and does not accept multiple files * in one request. * - * @param array $channel channel array of owner - * @param string $observer_hash hash of current observer - * @param string $options (optional) one of update, replace, revision - * @param array $arr (optional) associative array - */ - -/** - * A lot going on in this function, and some of it is old cruft and some is new cruft + * @note A lot going on in this function, and some of it is old cruft and some is new cruft * and the entire thing probably needs to be refactored. It started out just storing - * files, before we had DAV. It was made extensible to do extra stuff like edit an + * files, before we had DAV. It was made extensible to do extra stuff like edit an * existing file or optionally store a separate revision using $options to choose between different * storage models. Along the way we moved from - * DB data storage to file system storage. - * Then DAV came along and used different upload methods depending on whether the - * file was stored as a DAV directory object or updated as a file object. One of these + * DB data storage to file system storage. + * Then DAV came along and used different upload methods depending on whether the + * file was stored as a DAV directory object or updated as a file object. One of these * is essentially an update and the other is basically an upload, but doesn't use the traditional PHP - * upload workflow. + * upload workflow. * Then came hubzilla and we tried to merge photo functionality with the file storage. Most of - * that integration occurs within this function. + * that integration occurs within this function. * This required overlap with the old photo_upload stuff and photo albums were * completely different concepts from directories which needed to be reconciled somehow. * The old revision stuff is kind of orphaned currently. There's new revision stuff for photos @@ -404,13 +398,18 @@ function attach_by_hash_nodata($hash, $observer_hash, $rev = 0) { * That's where it sits currently. I repeat it needs to be refactored, and this note is here * for future explorers and those who may be doing that work to understand where it came * from and got to be the monstrosity of tangled unrelated code that it currently is. + * + * @param array $channel channel array of owner + * @param string $observer_hash hash of current observer + * @param string $options (optional) one of update, replace, revision + * @param array $arr (optional) associative array + * @return void|array */ - function attach_store($channel, $observer_hash, $options = '', $arr = null) { require_once('include/photos.php'); - call_hooks('photo_upload_begin',$arr); + call_hooks('photo_upload_begin', $arr); $ret = array('success' => false); $channel_id = $channel['channel_id']; @@ -441,7 +440,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { return $ret; } - $str_group_allow = perms2str($arr['group_allow']); + $str_group_allow = perms2str($arr['group_allow']); $str_contact_allow = perms2str($arr['contact_allow']); $str_group_deny = perms2str($arr['group_deny']); $str_contact_deny = perms2str($arr['contact_deny']); @@ -458,7 +457,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { $remove_when_processed = true; - if($options === 'import') { + if($options === 'import') { $src = $arr['src']; $filename = $arr['filename']; $filesize = @filesize($src); @@ -485,17 +484,15 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { elseif($options !== 'update') { $f = array('src' => '', 'filename' => '', 'filesize' => 0, 'type' => ''); - call_hooks('photo_upload_file',$f); + call_hooks('photo_upload_file',$f); call_hooks('attach_upload_file',$f); - if (x($f,'src') && x($f,'filesize')) { - $src = $f['src']; - $filename = $f['filename']; - $filesize = $f['filesize']; - $type = $f['type']; - - } else { - + if (x($f,'src') && x($f,'filesize')) { + $src = $f['src']; + $filename = $f['filename']; + $filesize = $f['filesize']; + $type = $f['type']; + } else { if(! x($_FILES,'userfile')) { $ret['message'] = t('No source file.'); return $ret; @@ -543,12 +540,10 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { $hash = $x[0]['hash']; } - - $def_extension = ''; $is_photo = 0; $gis = @getimagesize($src); - logger('getimagesize: ' . print_r($gis,true), LOGGER_DATA); + logger('getimagesize: ' . print_r($gis,true), LOGGER_DATA); if(($gis) && ($gis[2] === IMAGETYPE_GIF || $gis[2] === IMAGETYPE_JPEG || $gis[2] === IMAGETYPE_PNG)) { $is_photo = 1; if($gis[2] === IMAGETYPE_GIF) @@ -557,7 +552,6 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { $def_extension = '.jpg'; if($gis[2] === IMAGETYPE_PNG) $def_extension = '.png'; - } $pathname = ''; @@ -607,7 +601,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { } else { $folder_hash = ((($arr) && array_key_exists('folder',$arr)) ? $arr['folder'] : ''); - } + } if((! $options) || ($options === 'import')) { @@ -654,8 +648,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { } if($found) $x++; - } - while($found); + } while($found); $filename = $basename . '(' . $x . ')' . $ext; } else @@ -702,7 +695,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { if($folder_hash) { $curr = find_folder_hash_by_attach_hash($channel_id,$folder_hash,true); - if($curr) + if($curr) $os_relpath .= $curr . '/'; $os_relpath .= $folder_hash . '/'; } @@ -711,6 +704,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { // not yet used $os_path = ''; + $display_path = ''; if($src) @file_put_contents($os_basepath . $os_relpath,@file_get_contents($src)); @@ -726,23 +720,24 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { $edited = $created; if($options === 'replace') { - $r = q("update attach set filename = '%s', filetype = '%s', folder = '%s', filesize = %d, os_storage = %d, is_photo = %d, content = '%s', edited = '%s', os_path = '%s' where id = %d and uid = %d", + $r = q("update attach set filename = '%s', filetype = '%s', folder = '%s', filesize = %d, os_storage = %d, is_photo = %d, content = '%s', edited = '%s', os_path = '%s', display_path = '%s' where id = %d and uid = %d", dbesc($filename), dbesc($mimetype), dbesc($folder_hash), intval($filesize), intval(1), intval($is_photo), - dbesc($os_basepath . $os_relpath), + dbescbin($os_basepath . $os_relpath), dbesc($created), dbesc($os_path), + dbesc($display_path), intval($existing_id), intval($channel_id) ); } elseif($options === 'revise') { - $r = q("insert into attach ( aid, uid, hash, creator, filename, filetype, folder, filesize, revision, os_storage, is_photo, content, created, edited, os_path, allow_cid, allow_gid, deny_cid, deny_gid ) - VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", + $r = q("insert into attach ( aid, uid, hash, creator, filename, filetype, folder, filesize, revision, os_storage, is_photo, content, created, edited, os_path, display_path, allow_cid, allow_gid, deny_cid, deny_gid ) + VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", intval($x[0]['aid']), intval($channel_id), dbesc($x[0]['hash']), @@ -754,10 +749,11 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { intval($x[0]['revision'] + 1), intval(1), intval($is_photo), - dbesc($os_basepath . $os_relpath), + dbescbin($os_basepath . $os_relpath), dbesc($created), dbesc($created), dbesc($os_path), + dbesc($display_path), dbesc($x[0]['allow_cid']), dbesc($x[0]['allow_gid']), dbesc($x[0]['deny_cid']), @@ -766,7 +762,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { } elseif($options === 'update') { $r = q("update attach set filename = '%s', filetype = '%s', folder = '%s', edited = '%s', os_storage = %d, is_photo = %d, os_path = '%s', - allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where id = %d and uid = %d", + display_path = '%s', allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where id = %d and uid = %d", dbesc((array_key_exists('filename',$arr)) ? $arr['filename'] : $x[0]['filename']), dbesc((array_key_exists('filetype',$arr)) ? $arr['filetype'] : $x[0]['filetype']), dbesc(($folder_hash) ? $folder_hash : $x[0]['folder']), @@ -774,6 +770,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { dbesc((array_key_exists('os_storage',$arr)) ? $arr['os_storage'] : $x[0]['os_storage']), dbesc((array_key_exists('is_photo',$arr)) ? $arr['is_photo'] : $x[0]['is_photo']), dbesc((array_key_exists('os_path',$arr)) ? $arr['os_path'] : $x[0]['os_path']), + dbesc((array_key_exists('display_path',$arr)) ? $arr['display_path'] : $x[0]['display_path']), dbesc((array_key_exists('allow_cid',$arr)) ? $arr['allow_cid'] : $x[0]['allow_cid']), dbesc((array_key_exists('allow_gid',$arr)) ? $arr['allow_gid'] : $x[0]['allow_gid']), dbesc((array_key_exists('deny_cid',$arr)) ? $arr['deny_cid'] : $x[0]['deny_cid']), @@ -784,8 +781,8 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { } else { - $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, folder, filesize, revision, os_storage, is_photo, content, created, edited, os_path, allow_cid, allow_gid,deny_cid, deny_gid ) - VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", + $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, folder, filesize, revision, os_storage, is_photo, content, created, edited, os_path, display_path, allow_cid, allow_gid,deny_cid, deny_gid ) + VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", intval($channel['channel_account_id']), intval($channel_id), dbesc($hash), @@ -797,10 +794,11 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { intval(0), intval(1), intval($is_photo), - dbesc($os_basepath . $os_relpath), + dbescbin($os_basepath . $os_relpath), dbesc($created), dbesc($created), dbesc($os_path), + dbesc($display_path), dbesc(($arr && array_key_exists('allow_cid',$arr)) ? $arr['allow_cid'] : $str_contact_allow), dbesc(($arr && array_key_exists('allow_gid',$arr)) ? $arr['allow_gid'] : $str_group_allow), dbesc(($arr && array_key_exists('deny_cid',$arr)) ? $arr['deny_cid'] : $str_contact_deny), @@ -869,7 +867,6 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { return $ret; } - $ret['success'] = true; $ret['data'] = $r[0]; if(! $is_photo) { @@ -880,7 +877,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { if($dosync) { $sync = attach_export_data($channel,$hash); - if($sync) + if($sync) build_sync_packet($channel['channel_id'],array('file' => array($sync))); } @@ -998,7 +995,7 @@ function attach_mkdir($channel, $observer_hash, $arr = null) { intval($channel['channel_id']) ); if($r) { - if(array_key_exists('force',$arr) && intval($arr['force']) + if(array_key_exists('force',$arr) && intval($arr['force']) && (intval($r[0]['is_dir']))) { $ret['success'] = true; $r = q("select * from attach where id = %d limit 1", @@ -1046,8 +1043,12 @@ function attach_mkdir($channel, $observer_hash, $arr = null) { $created = datetime_convert(); - $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, filesize, revision, folder, os_storage, is_dir, content, created, edited, allow_cid, allow_gid, deny_cid, deny_gid ) - VALUES ( %d, %d, '%s', '%s', '%s', '%s', %d, %d, '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", + // not yet used + $os_path = ''; + $display_path = ''; + + $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, filesize, revision, folder, os_storage, is_dir, content, created, edited, os_path, display_path, allow_cid, allow_gid, deny_cid, deny_gid ) + VALUES ( %d, %d, '%s', '%s', '%s', '%s', %d, %d, '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", intval($channel['channel_account_id']), intval($channel_id), dbesc($arr['hash']), @@ -1059,9 +1060,11 @@ function attach_mkdir($channel, $observer_hash, $arr = null) { dbesc($arr['folder']), intval(1), intval(1), - dbesc($path), + dbescbin($path), dbesc($created), dbesc($created), + dbesc($os_path), + dbesc($display_path), dbesc(($arr && array_key_exists('allow_cid',$arr)) ? $arr['allow_cid'] : $channel['channel_allow_cid']), dbesc(($arr && array_key_exists('allow_gid',$arr)) ? $arr['allow_gid'] : $channel['channel_allow_gid']), dbesc(($arr && array_key_exists('deny_cid',$arr)) ? $arr['deny_cid'] : $channel['channel_deny_cid']), @@ -1150,7 +1153,7 @@ function attach_mkdirp($channel, $observer_hash, $arr = null) { if(! $p) continue; $arx = array( - 'filename' => $p, + 'filename' => $p, 'folder' => $current_parent, 'force' => 1 ); @@ -1163,7 +1166,7 @@ function attach_mkdirp($channel, $observer_hash, $arr = null) { if(array_key_exists('deny_gid',$arr)) $arx['deny_gid'] = $arr['deny_gid']; - $x = attach_mkdir($channel, $observer_hash, $arx); + $x = attach_mkdir($channel, $observer_hash, $arx); if($x['success']) { $current_parent = $x['data']['hash']; } @@ -1177,26 +1180,22 @@ function attach_mkdirp($channel, $observer_hash, $arr = null) { $ret['data'] = $x['data']; } - return $ret; - + return $ret; } - - - - /** * @brief Changes permissions of a file. * - * @param int $channel_id + * @param int $channel_id The id of the channel * @param array $resource * @param string $allow_cid * @param string $allow_gid * @param string $deny_cid * @param string $deny_gid * @param boolean $recurse (optional) default false + * @param boolean $sync (optional) default false */ function attach_change_permissions($channel_id, $resource, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $recurse = false, $sync = false) { @@ -1248,7 +1247,7 @@ function attach_change_permissions($channel_id, $resource, $allow_cid, $allow_gi if($sync) { $data = attach_export_data($channel,$resource); - if($data) + if($data) build_sync_packet($channel['channel_id'],array('file' => array($data))); } } @@ -1263,6 +1262,7 @@ function attach_change_permissions($channel_id, $resource, $allow_cid, $allow_gi * The id of the channel * @param string $resource * The hash to delete + * @param int $is_photo (optional) default 0 * @return void */ function attach_delete($channel_id, $resource, $is_photo = 0) { @@ -1306,6 +1306,7 @@ function attach_delete($channel_id, $resource, $is_photo = 0) { ); if($y) { + $y[0]['content'] = dbunescbin($y[0]['content']); if(strpos($y[0]['content'],'store') === false) $f = 'store/' . $channel_address . '/' . $y[0]['content']; else @@ -1337,7 +1338,7 @@ function attach_delete($channel_id, $resource, $is_photo = 0) { dbesc($resource) ); } - + // update the parent folder's lastmodified timestamp $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", dbesc(datetime_convert()), @@ -1425,6 +1426,7 @@ function get_parent_cloudpath($channel_id, $channel_name, $attachHash) { $parentFullPath = $parentName . '/' . $parentFullPath; } } while ($parentHash); + $parentFullPath = z_root() . '/cloud/' . $channel_name . '/' . $parentFullPath; return $parentFullPath; @@ -1437,11 +1439,14 @@ function get_parent_cloudpath($channel_id, $channel_name, $attachHash) { * The id of the channel * @param string $attachHash * The hash of the attachment + * @param boolean $recurse + * (optional) default false * @return string */ function find_folder_hash_by_attach_hash($channel_id, $attachHash, $recurse = false) { -logger('attach_hash: ' . $attachHash); + logger('attach_hash: ' . $attachHash); + $r = q("SELECT folder FROM attach WHERE uid = %d AND hash = '%s' LIMIT 1", intval($channel_id), dbesc($attachHash) @@ -1449,10 +1454,11 @@ logger('attach_hash: ' . $attachHash); $hash = ''; if($r && $r[0]['folder']) { if($recurse) - $hash = find_folder_hash_by_attach_hash($channel_id,$r[0]['folder'],true) . '/' . $r[0]['folder']; + $hash = find_folder_hash_by_attach_hash($channel_id,$r[0]['folder'],true) . '/' . $r[0]['folder']; else $hash = $r[0]['folder']; } + return $hash; } @@ -1569,13 +1575,13 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, $arr = array(); $arr['aid'] = get_account_id(); $arr['uid'] = $channel_id; - $arr['item_wall'] = 1; + $arr['item_wall'] = 1; $arr['item_origin'] = 1; $arr['item_unseen'] = 1; $arr['author_xchan'] = $poster['xchan_hash']; $arr['owner_xchan'] = $poster['xchan_hash']; $arr['title'] = ''; - $arr['item_hidden'] = 1; + $arr['item_notshown'] = 1; $arr['obj_type'] = $objtype; $arr['resource_id'] = $object['hash']; $arr['resource_type'] = 'attach'; @@ -1732,11 +1738,11 @@ function get_file_activity_object($channel_id, $hash, $cloudpath) { /** * @brief Returns array of channels which have recursive permission for a file * - * @param $arr_allow_cid - * @param $arr_allow_gid - * @param $arr_deny_cid - * @param $arr_deny_gid - * @param $folder_hash + * @param array $arr_allow_cid + * @param array $arr_allow_gid + * @param array $arr_deny_cid + * @param array $arr_deny_gid + * @param string $folder_hash */ function recursive_activity_recipients($arr_allow_cid, $arr_allow_gid, $arr_deny_cid, $arr_deny_gid, $folder_hash) { @@ -1907,7 +1913,7 @@ function attach_export_data($channel, $resource_id, $deleted = false) { ); if($r) { for($x = 0; $x < count($r); $x ++) { - $r[$x]['content'] = base64_encode($r[$x]['content']); + $r[$x]['content'] = base64_encode(dbunescbin($r[$x]['content'])); } $ret['photo'] = $r; } @@ -1931,50 +1937,53 @@ function attach_export_data($channel, $resource_id, $deleted = false) { } return $ret; - } -/* strip off 'store/nickname/' from the provided path */ - +/** + * @brief Strip off 'store/nickname/' from the provided path + * + * @param string $s + * @return string + */ function get_attach_binname($s) { $p = $s; - if(strpos($s,'store/') === 0) { - $p = substr($s,6); - $p = substr($p,strpos($p,'/')+1); + if(strpos($s, 'store/') === 0) { + $p = substr($s, 6); + $p = substr($p, strpos($p, '/')+1); } + return $p; } function get_dirpath_by_cloudpath($channel, $path) { - - // Warning: Do not edit the following line. The first symbol is UTF-8 @ - $path = str_replace('@','@',notags(trim($path))); + + $path = notags(trim($path)); $h = @parse_url($path); if(! $h || !x($h, 'path')) { return null; } - if(substr($h['path'],-1,1) === '/') { - $h['path'] = substr($h['path'],0,-1); + if(substr($h['path'], -1, 1) === '/') { + $h['path'] = substr($h['path'], 0, -1); } if(substr($h['path'],0,1) === '/') { - $h['path'] = substr($h['path'],1); + $h['path'] = substr($h['path'], 1); } $folders = explode('/', $h['path']); $f = array_shift($folders); - + $nick = $channel['channel_address']; //check to see if the absolute path was provided (/cloud/channelname/path/to/folder) - if($f === 'cloud' ) { + if($f === 'cloud' ) { $g = array_shift($folders); if( $g !== $nick) { // if nick does not follow "cloud", then the top level folder must be called "cloud" - // and the given path must be relative to "/cloud/channelname/". + // and the given path must be relative to "/cloud/channelname/". $folders = array_unshift(array_unshift($folders, $g), $f); - } + } } else { array_unshift($folders, $f); } @@ -1998,8 +2007,6 @@ function get_dirpath_by_cloudpath($channel, $path) { } else { return $clouddir . $subdir; } - - } function get_filename_by_cloudname($cloudname, $channel, $storepath) { @@ -2013,66 +2020,74 @@ function get_filename_by_cloudname($cloudname, $channel, $storepath) { return null; } - -// recursively copy a directory into cloud files -function copy_folder_to_cloudfiles($channel, $observer_hash, $srcpath, $cloudpath) -{ - if (!is_dir($srcpath) || !is_readable($srcpath)) { - logger('Error reading source path: ' . $srcpath, LOGGER_NORMAL); +/** + * @brief recursively copy a directory into cloud files + * + * @param array $channel + * @param string $observer_hash + * @param string $srcpath + * @param string $cloudpath + * @return boolean + */ +function copy_folder_to_cloudfiles($channel, $observer_hash, $srcpath, $cloudpath) { + if (!is_dir($srcpath) || !is_readable($srcpath)) { + logger('Error reading source path: ' . $srcpath, LOGGER_NORMAL); + return false; + } + $nodes = array_diff(scandir($srcpath), array('.', '..')); + foreach ($nodes as $node) { + $clouddir = $cloudpath . '/' . $node; // Sub-folder in cloud files destination + $nodepath = $srcpath . '/' . $node; // Sub-folder in source path + if(is_dir($nodepath)) { + $x = attach_mkdirp($channel, $observer_hash, array('pathname' => $clouddir)); + if(!$x['success']) { + logger('Error creating cloud path: ' . $clouddir, LOGGER_NORMAL); return false; + } + // Recursively call this function where the source and destination are the subfolders + $success = copy_folder_to_cloudfiles($channel, $observer_hash, $nodepath, $clouddir); + if(!$success) { + logger('Error copying contents of folder: ' . $nodepath, LOGGER_NORMAL); + return false; + } + } elseif(is_file($nodepath) && is_readable($nodepath)) { + $x = attach_store($channel, $observer_hash, 'import', array( + 'directory' => $cloudpath, + 'src' => $nodepath, + 'filename' => $node, + 'filesize' => @filesize($nodepath), + 'preserve_original' => true + )); + if(!$x['success']) { + logger('Error copying file: ' . $nodepath, LOGGER_NORMAL); + logger('Return value: ' . json_encode($x), LOGGER_NORMAL); + return false; + } + } else { + logger('Error scanning source path', LOGGER_NORMAL); + return false; } - $nodes = array_diff(scandir($srcpath), array('.', '..')); - foreach ($nodes as $node) { - $clouddir = $cloudpath . '/' . $node; // Sub-folder in cloud files destination - $nodepath = $srcpath . '/' . $node; // Sub-folder in source path - if(is_dir($nodepath)) { - $x = attach_mkdirp($channel, $observer_hash, array('pathname' => $clouddir)); - if(!$x['success']) { - logger('Error creating cloud path: ' . $clouddir, LOGGER_NORMAL); - return false; - } - // Recursively call this function where the source and destination are the subfolders - $success = copy_folder_to_cloudfiles($channel, $observer_hash, $nodepath, $clouddir); - if(!$success) { - logger('Error copying contents of folder: ' . $nodepath, LOGGER_NORMAL); - return false; - } - } elseif (is_file($nodepath) && is_readable($nodepath)) { - $x = attach_store($channel, $observer_hash, 'import', - array( - 'directory' => $cloudpath, - 'src' => $nodepath, - 'filename' => $node, - 'filesize' => @filesize($nodepath), - 'preserve_original' => true) - ); - if(!$x['success']) { - logger('Error copying file: ' . $nodepath , LOGGER_NORMAL); - logger('Return value: ' . json_encode($x), LOGGER_NORMAL); - return false; - } - } else { - logger('Error scanning source path', LOGGER_NORMAL); - return false; - } - } + } - return true; + return true; } /** - * attach_move() * This function performs an in place directory-to-directory move of a stored attachment or photo. * The data is physically moved in the store/nickname storage location and the paths adjusted * in the attach structure (and if applicable the photo table). The new 'album name' is recorded * for photos and will show up immediately there. * This takes a channel_id, attach.hash of the file to move (this is the same as a photo resource_id), and * the attach.hash of the new parent folder, which must already exist. If $new_folder_hash is blank or empty, - * the file is relocated to the root of the channel's storage area. + * the file is relocated to the root of the channel's storage area. * * @fixme: this operation is currently not synced to clones !! + * + * @param int $channel_id + * @param int $resource_id + * @param string $new_folder_hash + * @return void|boolean */ - -function attach_move($channel_id,$resource_id,$new_folder_hash) { +function attach_move($channel_id, $resource_id, $new_folder_hash) { $c = channelx_by_n($channel_id); if(! $c) @@ -2085,8 +2100,8 @@ function attach_move($channel_id,$resource_id,$new_folder_hash) { if(! $r) return false; - $oldstorepath = $r[0]['content']; - + $oldstorepath = dbunescbin($r[0]['content']); + if($new_folder_hash) { $n = q("select * from attach where hash = '%s' and uid = %d limit 1", dbesc($new_folder_hash), @@ -2094,8 +2109,9 @@ function attach_move($channel_id,$resource_id,$new_folder_hash) { ); if(! $n) return; + $newdirname = $n[0]['filename']; - $newstorepath = $n[0]['content'] . '/' . $resource_id; + $newstorepath = dbunescbin($n[0]['content']) . '/' . $resource_id; } else { $newstorepath = 'store/' . $c['channel_address'] . '/' . $resource_id; @@ -2115,7 +2131,7 @@ function attach_move($channel_id,$resource_id,$new_folder_hash) { if($s) { $overwrite = get_pconfig($channel_id,'system','overwrite_dup_files'); if($overwrite) { - // @fixme + /// @fixme return; } else { @@ -2128,6 +2144,10 @@ function attach_move($channel_id,$resource_id,$new_folder_hash) { $ext = ''; } + $matches = false; + if(preg_match('/(.*?)\([0-9]{1,}\)$/',$basename,$matches)) + $basename = $matches[1]; + $v = q("select filename from attach where ( filename = '%s' OR filename like '%s' ) and folder = '%s' ", dbesc($basename . $ext), dbesc($basename . '(%)' . $ext), @@ -2147,7 +2167,7 @@ function attach_move($channel_id,$resource_id,$new_folder_hash) { } if($found) $x++; - } + } while($found); $filename = $basename . '(' . $x . ')' . $ext; } @@ -2157,7 +2177,7 @@ function attach_move($channel_id,$resource_id,$new_folder_hash) { } $t = q("update attach set content = '%s', folder = '%s', filename = '%s' where id = %d", - dbesc($newstorepath), + dbescbin($newstorepath), dbesc($new_folder_hash), dbesc($filename), intval($r[0]['id']) @@ -2172,14 +2192,13 @@ function attach_move($channel_id,$resource_id,$new_folder_hash) { ); $t = q("update photo set content = '%s' where resource_id = '%s' and uid = %d and imgscale = 0", - dbesc($newstorepath), + dbescbin($newstorepath), dbesc($resource_id), intval($channel_id) ); } return true; - } @@ -2191,7 +2210,7 @@ function attach_folder_select_list($channel_id) { $out = []; $out[''] = '/'; - + if($r) { foreach($r as $rv) { $x = attach_folder_rpaths($r,$rv); @@ -2199,6 +2218,7 @@ function attach_folder_select_list($channel_id) { $out[$x[0]] = $x[1]; } } + return $out; } @@ -2223,11 +2243,11 @@ function attach_folder_rpaths($all_folders,$that_folder) { break; } } - if(! $found) + if(! $found) $error = true; } while((! $found) && (! $error) && ($parent_hash != '')); } - return (($error) ? false : [ $current_hash , $path ]); -}
\ No newline at end of file + return (($error) ? false : [ $current_hash , $path ]); +} diff --git a/include/bb2diaspora.php b/include/bb2diaspora.php index d3f88e17c..87a8551ae 100644 --- a/include/bb2diaspora.php +++ b/include/bb2diaspora.php @@ -123,13 +123,16 @@ function diaspora_mention_callback($matches) { * @param boolean $use_zrl default false * @return string */ -function diaspora2bb($s, $use_zrl = false) { +function markdown_to_bb($s, $use_zrl = false) { $s = str_replace("
","\r",$s); $s = str_replace("
\n>","",$s); $s = html_entity_decode($s,ENT_COMPAT,'UTF-8'); + // if empty link text replace with the url + $s = preg_replace("/\[\]\((.*?)\)/ism",'[$1]($1)',$s); + // first try plustags $s = preg_replace_callback('/\@\{(.+?)\; (.+?)\@(.+?)\}\+/','diaspora_mention_callback',$s); @@ -155,23 +158,20 @@ function diaspora2bb($s, $use_zrl = false) { // Convert everything that looks like a link to a link if($use_zrl) { $s = str_replace(array('[img','/img]'),array('[zmg','/zmg]'),$s); - $s = preg_replace("/([^\]\=]|^)(https?\:\/\/)([a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism", '$1[zrl=$2$3]$2$3[/zrl]',$s); + $s = preg_replace("/([^\]\=]|^)(https?\:\/\/)([a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,\(\)]+)/ism", '$1[zrl=$2$3]$2$3[/zrl]',$s); } else { - $s = preg_replace("/([^\]\=]|^)(https?\:\/\/)([a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism", '$1[url=$2$3]$2$3[/url]',$s); + $s = preg_replace("/([^\]\=]|^)(https?\:\/\/)([a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,\(\)]+)/ism", '$1[url=$2$3]$2$3[/url]',$s); } - //$s = preg_replace("/([^\]\=]|^)(https?\:\/\/)(vimeo|youtu|www\.youtube|soundcloud)([a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism", '$1[url=$2$3$4]$2$3$4[/url]',$s); - $s = bb_tag_preg_replace("/\[url\=?(.*?)\]https?:\/\/www.youtube.com\/watch\?v\=(.*?)\[\/url\]/ism",'[embed]https://www.youtube.com/watch?v=$2[/embed]','url',$s); - $s = bb_tag_preg_replace("/\[url\=https?:\/\/www.youtube.com\/watch\?v\=(.*?)\].*?\[\/url\]/ism",'[embed]https://www.youtube.com/watch?v=$1[/embed]','url',$s); - $s = bb_tag_preg_replace("/\[url\=?(.*?)\]https?:\/ \/vimeo.com\/([0-9]+)(.*?)\[\/url\]/ism",'[embed]https://vimeo.com/$2[/embed]','url',$s); - $s = bb_tag_preg_replace("/\[url\=https?:\/\/vimeo.com\/([0-9]+)\](.*?)\[\/url\]/ism",'[embed]https://vimeo.com/$1[/embed]','url',$s); // remove duplicate adjacent code tags $s = preg_replace("/(\[code\])+(.*?)(\[\/code\])+/ism","[code]$2[/code]", $s); // Don't show link to full picture (until it is fixed) $s = scale_external_images($s, false); + call_hooks('markdown_to_bb',$s); + return $s; } @@ -324,7 +324,7 @@ function bb2diaspora_itemwallwall(&$item,$uplink = false) { } -function bb2diaspora_itembody($item, $force_update = false, $have_channel = false, $uplink) { +function bb2diaspora_itembody($item, $force_update = false, $have_channel = false, $uplink = false) { if(! get_iconfig($item,'diaspora','fields')) { @@ -430,6 +430,12 @@ function bb2diaspora($Text,$preserve_nl = false, $fordiaspora = true) { $Text = preg_replace_callback('/\@\!?\[([zu])rl\=(\w+.*?)\](\w+.*?)\[\/([zu])rl\]/i', 'bb2dmention_callback', $Text); + // strip map tags, as the rendering is performed in bbcode() and the resulting output + // is not compatible with Diaspora (at least in the case of openstreetmap and probably + // due to the inclusion of an html iframe) + + $Text = preg_replace("/\[map\=(.*?)\]/ism", '$1', $Text); + $Text = preg_replace("/\[map\](.*?)\[\/map\]/ism", '$1', $Text); // Converting images with size parameters to simple images. Markdown doesn't know it. $Text = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $Text); @@ -452,6 +458,8 @@ function bb2diaspora($Text,$preserve_nl = false, $fordiaspora = true) { $md = new Markdownify(false, false, false); $Text = $md->parseString($Text); + + // It also adds backslashes to our attempt at getting around the html entity preservation for some weird reason. $Text = str_replace(array('&\\_lt\\_;','&\\_gt\\_;','&\\_amp\\_;'),array('<','>','&'),$Text); diff --git a/include/bbcode.php b/include/bbcode.php index a82b658b1..32354aeda 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -403,6 +403,15 @@ function bb_definitionList_unescapeBraces($match) { return '<dt>' . str_replace('\]', ']', $match[1]) . '</dt>'; } + +function bb_checklist($match) { + $str = $match[1]; + $str = str_replace("[]", "<li><input type=\"checkbox\" disabled=\"disabled\">", $str); + $str = str_replace("[x]", "<li><input type=\"checkbox\" checked=\"checked\" disabled=\"disabled\">", $str); + return '<ul class="checklist" style="list-style-type: none;">' . $str . '</ul>'; +} + + /** * @brief Sanitize style properties from BBCode to HTML. * @@ -503,10 +512,10 @@ function bb_code($match) { } function bb_highlight($match) { - if(in_array(strtolower($match[1]),['php','css','mysql','sql','abap','diff','html','perl','ruby', + $lang = ((in_array(strtolower($match[1]),['php','css','mysql','sql','abap','diff','html','perl','ruby', 'vbscript','avrc','dtd','java','xml','cpp','python','javascript','js','json','sh'])) - return text_highlight($match[2],strtolower($match[1])); - return $match[0]; + ? strtolower($match[1]) : 'php' ); + return text_highlight($match[2],$lang); } function bb_fixtable_lf($match) { @@ -520,7 +529,49 @@ function bb_fixtable_lf($match) { } +function parseIdentityAwareHTML($Text) { + + // process [observer] tags before we do anything else because we might + // be stripping away stuff that then doesn't need to be worked on anymore + + $observer = App::get_observer(); + if ((strpos($Text,'[/observer]') !== false) || (strpos($Text,'[/rpost]') !== false)) { + if ($observer) { + $Text = preg_replace("/\[observer\=1\](.*?)\[\/observer\]/ism", '$1', $Text); + $Text = preg_replace("/\[observer\=0\].*?\[\/observer\]/ism", '', $Text); + $Text = preg_replace_callback("/\[rpost(=(.*?))?\](.*?)\[\/rpost\]/ism", 'rpost_callback', $Text); + } else { + $Text = preg_replace("/\[observer\=1\].*?\[\/observer\]/ism", '', $Text); + $Text = preg_replace("/\[observer\=0\](.*?)\[\/observer\]/ism", '$1', $Text); + $Text = preg_replace("/\[rpost(=.*?)?\](.*?)\[\/rpost\]/ism", '', $Text); + } + } + // replace [observer.baseurl] + if ($observer) { + $s1 = '<span class="bb_observer" title="' . t('Different viewers will see this text differently') . '">'; + $s2 = '</span>'; + $obsBaseURL = $observer['xchan_connurl']; + $obsBaseURL = preg_replace("/\/poco\/.*$/", '', $obsBaseURL); + $Text = str_replace('[observer.baseurl]', $obsBaseURL, $Text); + $Text = str_replace('[observer.url]',$observer['xchan_url'], $Text); + $Text = str_replace('[observer.name]',$s1 . $observer['xchan_name'] . $s2, $Text); + $Text = str_replace('[observer.address]',$s1 . $observer['xchan_addr'] . $s2, $Text); + $Text = str_replace('[observer.webname]', substr($observer['xchan_addr'],0,strpos($observer['xchan_addr'],'@')), $Text); + $Text = str_replace('[observer.photo]',$s1 . '[zmg]'.$observer['xchan_photo_l'].'[/zmg]' . $s2, $Text); + } else { + $Text = str_replace('[observer.baseurl]', '', $Text); + $Text = str_replace('[observer.url]','', $Text); + $Text = str_replace('[observer.name]','', $Text); + $Text = str_replace('[observer.address]','', $Text); + $Text = str_replace('[observer.webname]','',$Text); + $Text = str_replace('[observer.photo]','', $Text); + } + + $Text = str_replace(array('[baseurl]','[sitename]'),array(z_root(),get_config('system','sitename')),$Text); + + return $Text; +} // BBcode 2 HTML was written by WAY2WEB.net // extended to work with Mistpark/Friendica/Redmatrix/Hubzilla - Mike Macgirvin @@ -550,7 +601,6 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) // process [observer] tags before we do anything else because we might // be stripping away stuff that then doesn't need to be worked on anymore - if($cache) $observer = false; else @@ -653,7 +703,7 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) // Perform URL Search - $urlchars = '[a-zA-Z0-9\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\+\,\@]'; + $urlchars = '[a-zA-Z0-9\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\+\,\@\(\)]'; if (strpos($Text,'http') !== false) { if($tryoembed) { @@ -737,6 +787,12 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) if (strpos($Text,'[/color]') !== false) { $Text = preg_replace("(\[color=(.*?)\](.*?)\[\/color\])ism", "<span style=\"color: $1;\">$2</span>", $Text); } + // Check for colored text + if (strpos($Text,'[/hl]') !== false) { + $Text = preg_replace("(\[hl\](.*?)\[\/hl\])ism", "<span style=\"background-color: yellow;\">$1</span>", $Text); + $Text = preg_replace("(\[hl=(.*?)\](.*?)\[\/hl\])ism", "<span style=\"background-color: $1;\">$2</span>", $Text); + } + // Check for sized text // [size=50] --> font-size: 50px (with the unit). if (strpos($Text,'[/size]') !== false) { @@ -768,12 +824,14 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $Text = preg_replace("(\[h6\](.*?)\[\/h6\])ism",'<h6>$1</h6>',$Text); } // Check for table of content without params - if (strpos($Text,'[toc]') !== false) { - $Text = preg_replace("/\[toc\]/ism",'<ul id="toc"></ul>',$Text); + while(strpos($Text,'[toc]') !== false) { + $toc_id = 'toc-' . random_string(10); + $Text = preg_replace("/\[toc\]/ism", '<ul id="' . $toc_id . '" class="toc" data-toc=".section-content-wrapper"></ul><script>$("#' . $toc_id . '").toc();</script>', $Text, 1); } // Check for table of content with params - if (strpos($Text,'[toc') !== false) { - $Text = preg_replace("/\[toc([^\]]+?)\]/ism",'<ul$1></ul>',$Text); + while(strpos($Text,'[toc') !== false) { + $toc_id = 'toc-' . random_string(10); + $Text = preg_replace("/\[toc([^\]]+?)\]/ism", '<ul id="' . $toc_id . '" class="toc"$1></ul><script>$("#' . $toc_id . '").toc();</script>', $Text, 1); } // Check for centered text if (strpos($Text,'[/center]') !== false) { @@ -784,15 +842,15 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $Text = preg_replace("(\[footer\](.*?)\[\/footer\])ism", "<div class=\"wall-item-footer\">$1</div>", $Text); } // Check for list text + + $Text = preg_replace("/<br \/>\[\*\]/ism",'[*]',$Text); + $Text = str_replace("[*]", "<li>", $Text); - $Text = str_replace("[]", "<li><input type=\"checkbox\" disabled=\"disabled\">", $Text); - $Text = str_replace("[x]", "<li><input type=\"checkbox\" checked=\"checked\" disabled=\"disabled\">", $Text); // handle nested lists $endlessloop = 0; while ((((strpos($Text, "[/list]") !== false) && (strpos($Text, "[list") !== false)) || - ((strpos($Text, "[/checklist]") !== false) && (strpos($Text, "[checklist]") !== false)) || ((strpos($Text, "[/ol]") !== false) && (strpos($Text, "[ol]") !== false)) || ((strpos($Text, "[/ul]") !== false) && (strpos($Text, "[ul]") !== false)) || ((strpos($Text, "[/dl]") !== false) && (strpos($Text, "[dl") !== false)) || @@ -804,9 +862,9 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $Text = preg_replace("/\[list=((?-i)I)\](.*?)\[\/list\]/ism", '<ul class="listupperroman" style="list-style-type: upper-roman;">$2</ul>', $Text); $Text = preg_replace("/\[list=((?-i)a)\](.*?)\[\/list\]/ism", '<ul class="listloweralpha" style="list-style-type: lower-alpha;">$2</ul>', $Text); $Text = preg_replace("/\[list=((?-i)A)\](.*?)\[\/list\]/ism", '<ul class="listupperalpha" style="list-style-type: upper-alpha;">$2</ul>', $Text); - $Text = preg_replace("/\[checklist\](.*?)\[\/checklist\]/ism", '<ul class="checklist" style="list-style-type: none;">$1</ul>', $Text); $Text = preg_replace("/\[ul\](.*?)\[\/ul\]/ism", '<ul class="listbullet" style="list-style-type: circle;">$1</ul>', $Text); $Text = preg_replace("/\[ol\](.*?)\[\/ol\]/ism", '<ul class="listdecimal" style="list-style-type: decimal;">$1</ul>', $Text); + $Text = preg_replace("/\[\/li\]<br \/>\[li\]/ism",'[/li][li]',$Text); $Text = preg_replace("/\[li\](.*?)\[\/li\]/ism", '<li>$1</li>', $Text); // [dl] tags have an optional [dl terms="bi"] form where bold/italic/underline/mono/large @@ -815,7 +873,13 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) // "[dl" <optional-whitespace> <optional-termStyles> "]" <matchGroup2> "[/dl]" // where optional-termStyles are: "terms=" <optional-quote> <matchGroup1> <optional-quote> $Text = preg_replace_callback('/\[dl[[:space:]]*(?:terms=(?:"|")?([a-zA-Z]+)(?:"|")?)?\](.*?)\[\/dl\]/ism', 'bb_definitionList', $Text); + } + + if (strpos($Text,'[checklist]') !== false) { + $Text = preg_replace_callback("/\[checklist\](.*?)\[\/checklist\]/ism", 'bb_checklist', $Text); + } + if (strpos($Text,'[th]') !== false) { $Text = preg_replace("/\[th\](.*?)\[\/th\]/sm", '<th>$1</th>', $Text); } @@ -1003,15 +1067,15 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $Text = preg_replace("/\[zaudio\](.*?)\[\/zaudio\]/", '<a class="zid" href="$1" target="_blank" >$1</a>', $Text); } - if ($tryoembed){ - if (strpos($Text,'[/iframe]') !== false) { - $Text = preg_replace_callback("/\[iframe\](.*?)\[\/iframe\]/ism", 'bb_iframe', $Text); - } - } else { - if (strpos($Text,'[/iframe]') !== false) { - $Text = preg_replace("/\[iframe\](.*?)\[\/iframe\]/ism", '<a href="$1" target="_blank" >$1</a>', $Text); - } - } +// if ($tryoembed){ +// if (strpos($Text,'[/iframe]') !== false) { +// $Text = preg_replace_callback("/\[iframe\](.*?)\[\/iframe\]/ism", 'bb_iframe', $Text); +// } +// } else { +// if (strpos($Text,'[/iframe]') !== false) { +// $Text = preg_replace("/\[iframe\](.*?)\[\/iframe\]/ism", '<a href="$1" target="_blank" >$1</a>', $Text); +// } +// } // oembed tag $Text = oembed_bbcode2html($Text); @@ -1024,7 +1088,7 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) // Summary (e.g. title) is required, earlier revisions only required description (in addition to // start which is always required). Allow desc with a missing summary for compatibility. - if ((x($ev,'desc') || x($ev,'summary')) && x($ev,'start')) { + if ((x($ev,'desc') || x($ev,'summary')) && x($ev,'dtstart')) { $sub = format_event_html($ev); diff --git a/include/channel.php b/include/channel.php index 88e59830f..4fc873402 100644 --- a/include/channel.php +++ b/include/channel.php @@ -162,7 +162,7 @@ function channel_total() { * Also creates the related xchan, hubloc, profile, and "self" abook records, * and an empty "Friends" group/collection for the new channel. * - * @param array $arr assoziative array with: + * @param array $arr associative array with: * * \e string \b name full name of channel * * \e string \b nickname "email/url-compliant" nickname * * \e int \b account_id to attach with this channel @@ -182,7 +182,7 @@ function create_identity($arr) { return $ret; } $ret = identity_check_service_class($arr['account_id']); - if (!$ret['success']) { + if (!$ret['success']) { return $ret; } // save this for auto_friending @@ -236,13 +236,13 @@ function create_identity($arr) { $publish = intval($role_permissions['directory_publish']); $primary = true; - + if(array_key_exists('primary', $arr)) $primary = intval($arr['primary']); $expire = 0; - $r = q("insert into channel ( channel_account_id, channel_primary, + $r = q("insert into channel ( channel_account_id, channel_primary, channel_name, channel_address, channel_guid, channel_guid_sig, channel_hash, channel_prvkey, channel_pubkey, channel_pageflags, channel_system, channel_expire_days, channel_timezone ) values ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s' ) ", @@ -262,7 +262,7 @@ function create_identity($arr) { dbesc(App::$timezone) ); - $r = q("select * from channel where channel_account_id = %d + $r = q("select * from channel where channel_account_id = %d and channel_guid = '%s' limit 1", intval($arr['account_id']), dbesc($guid) @@ -291,7 +291,7 @@ function create_identity($arr) { // Create a verified hub location pointing to this site. - $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_primary, + $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_primary, hubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey, hubloc_network ) values ( '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s' )", dbesc($guid), @@ -392,11 +392,11 @@ function create_identity($arr) { intval($newuid) ); } - } + } } - // Create a group with yourself as a member. This allows somebody to use it - // right away as a default group for new contacts. + // Create a group with yourself as a member. This allows somebody to use it + // right away as a default group for new contacts. require_once('include/group.php'); group_add($newuid, t('Friends')); @@ -422,7 +422,7 @@ function create_identity($arr) { set_pconfig($ret['channel']['channel_id'],'system','photo_path', '%Y-%m'); set_pconfig($ret['channel']['channel_id'],'system','attach_path','%Y-%m'); } - + // auto-follow any of the hub's pre-configured channel choices. // Only do this if it's the first channel for this account; // otherwise it could get annoying. Don't make this list too big @@ -494,7 +494,7 @@ function identity_basic_export($channel_id, $items = false) { $ret = array(); - // use constants here as otherwise we will have no idea if we can import from a site + // use constants here as otherwise we will have no idea if we can import from a site // with a non-standard platform and version. $ret['compatibility'] = array('project' => PLATFORM_NAME, 'version' => STD_VERSION, 'database' => DB_UPDATE_VERSION, 'server_role' => Zotlabs\Lib\System::get_server_role()); @@ -503,7 +503,7 @@ function identity_basic_export($channel_id, $items = false) { ); if($r) { translate_channel_perms_outbound($r[0]); - $ret['channel'] = $r[0]; + $ret['channel'] = $r[0]; $ret['relocate'] = [ 'channel_address' => $r[0]['channel_address'], 'url' => z_root()]; } @@ -526,7 +526,7 @@ function identity_basic_export($channel_id, $items = false) { if($abconfig) $ret['abook'][$x]['abconfig'] = $abconfig; translate_abook_perms_outbound($ret['abook'][$x]); - } + } stringify_array_elms($xchans); } @@ -534,13 +534,13 @@ function identity_basic_export($channel_id, $items = false) { $r = q("select * from xchan where xchan_hash in ( " . implode(',',$xchans) . " ) "); if($r) $ret['xchan'] = $r; - + $r = q("select * from hubloc where hubloc_hash in ( " . implode(',',$xchans) . " ) "); if($r) $ret['hubloc'] = $r; } - $r = q("select * from `groups` where uid = %d ", + $r = q("select * from groups where uid = %d ", intval($channel_id) ); @@ -578,7 +578,6 @@ function identity_basic_export($channel_id, $items = false) { if($r) $ret['term'] = $r; - // add psuedo-column obj_baseurl to aid in relocations $r = q("select obj.*, '%s' as obj_baseurl from obj where obj_channel = %d", @@ -608,7 +607,6 @@ function identity_basic_export($channel_id, $items = false) { if($r) $ret['chatroom'] = $r; - $r = q("select * from event where uid = %d", intval($channel_id) ); @@ -625,7 +623,7 @@ function identity_basic_export($channel_id, $items = false) { foreach($r as $rr) $ret['event_item'][] = encode_item($rr,true); } - + $x = menu_list($channel_id); if($x) { $ret['menu'] = array(); @@ -636,12 +634,10 @@ function identity_basic_export($channel_id, $items = false) { } } - $addon = array('channel_id' => $channel_id,'data' => $ret); call_hooks('identity_basic_export',$addon); $ret = $addon['data']; - if(! $items) return $ret; @@ -659,11 +655,10 @@ function identity_basic_export($channel_id, $items = false) { if($r) { for($x = 0; $x < count($r); $x ++) { $r[$x]['subject'] = base64url_decode(str_rot47($r[$x]['subject'])); - } + } $ret['conv'] = $r; } - $r = q("select * from mail where mail.uid = %d", intval($channel_id) ); @@ -680,15 +675,15 @@ function identity_basic_export($channel_id, $items = false) { /** @warning this may run into memory limits on smaller systems */ - /** export three months of posts. If you want to export and import all posts you have to start with - * the first year and export/import them in ascending order. + /** export three months of posts. If you want to export and import all posts you have to start with + * the first year and export/import them in ascending order. * * Don't export linked resource items. we'll have to pull those out separately. */ $r = q("select * from item where item_wall = 1 and item_deleted = 0 and uid = %d and created > %s - INTERVAL %s and resource_type = '' order by created", intval($channel_id), - db_utcnow(), + db_utcnow(), db_quoteinterval('3 MONTH') ); if($r) { @@ -730,7 +725,7 @@ function identity_export_year($channel_id,$year,$month = 0) { $r = q("select * from item where ( item_wall = 1 or item_type != %d ) and item_deleted = 0 and uid = %d and created >= '%s' and created < '%s' and resource_type = '' order by created", intval(ITEM_TYPE_POST), intval($channel_id), - dbesc($mindate), + dbesc($mindate), dbesc($maxdate) ); @@ -745,16 +740,24 @@ function identity_export_year($channel_id,$year,$month = 0) { return $ret; } -// export items within an arbitrary date range. Date/time is in UTC. - -function channel_export_items($channel_id,$start,$finish) { +/** + * @brief Export items within an arbitrary date range. + * + * Date/time is in UTC. + * + * @param int $channel_id The channel ID + * @param string $start + * @param string $finish + * @return array + */ +function channel_export_items($channel_id, $start, $finish) { if(! $start) return array(); else - $start = datetime_convert('UTC','UTC',$start); + $start = datetime_convert('UTC', 'UTC', $start); - $finish = datetime_convert('UTC','UTC',(($finish) ? $finish : 'now')); + $finish = datetime_convert('UTC', 'UTC', (($finish) ? $finish : 'now')); if($finish < $start) return array(); @@ -768,16 +771,16 @@ function channel_export_items($channel_id,$start,$finish) { $r = q("select * from item where ( item_wall = 1 or item_type != %d ) and item_deleted = 0 and uid = %d and created >= '%s' and created < '%s' and resource_type = '' order by created", intval(ITEM_TYPE_POST), intval($channel_id), - dbesc($start), + dbesc($start), dbesc($finish) ); if($r) { $ret['item'] = array(); xchan_query($r); - $r = fetch_post_tags($r,true); + $r = fetch_post_tags($r, true); foreach($r as $rr) - $ret['item'][] = encode_item($rr,true); + $ret['item'][] = encode_item($rr, true); } return $ret; @@ -792,7 +795,7 @@ function channel_export_items($channel_id,$start,$finish) { * * Permissions of the current observer are checked. If a restricted profile is available * to the current observer, that will be loaded instead of the channel default profile. - * + * * The channel owner can set $profile to a valid profile_guid to preview that profile. * * The channel default theme is also selected for use, unless over-riden elsewhere. @@ -866,7 +869,6 @@ function profile_load($nickname, $profile = '') { intval($p[0]['profile_uid']) ); if($q) { - $extra_fields = array(); require_once('include/channel.php'); @@ -903,7 +905,7 @@ function profile_load($nickname, $profile = '') { // fetch user tags if this isn't the default profile if(! $p[0]['is_default']) { - $x = q("select `keywords` from `profile` where uid = %d and `is_default` = 1 limit 1", + $x = q("select keywords from profile where uid = %d and is_default = 1 limit 1", intval($p[0]['profile_uid']) ); if($x && $can_view_profile) @@ -985,7 +987,6 @@ function profile_edit_menu($uid) { } return $ret; - } /** @@ -997,6 +998,7 @@ function profile_edit_menu($uid) { * @param array $profile * @param int $block * @param boolean $show_connect + * @param mixed $zcard * * @return HTML string suitable for sidebar inclusion * Exceptions: Returns empty string if passed $profile is wrong type or not populated @@ -1032,7 +1034,7 @@ function profile_sidebar($profile, $block = 0, $show_connect = true, $zcard = fa $connect_url = rconnect_url($profile['uid'],get_observer_hash()); $connect = (($connect_url) ? t('Connect') : ''); - if($connect_url) + if($connect_url) $connect_url = sprintf($connect_url,urlencode(channel_reddress($profile))); // premium channel - over-ride @@ -1066,19 +1068,26 @@ function profile_sidebar($profile, $block = 0, $show_connect = true, $zcard = fa ? trim(substr($profile['channel_name'],0,strpos($profile['channel_name'],' '))) : $profile['channel_name']); $lastname = (($firstname === $profile['channel_name']) ? '' : trim(substr($profile['channel_name'],strlen($firstname)))); - $diaspora = array( - 'podloc' => z_root(), - 'guid' => $profile['channel_guid'] . str_replace('.','',App::get_hostname()), - 'pubkey' => pemtorsa($profile['channel_pubkey']), - 'searchable' => (($block) ? 'false' : 'true'), - 'nickname' => $profile['channel_address'], - 'fullname' => $profile['channel_name'], - 'firstname' => $firstname, - 'lastname' => $lastname, - 'photo300' => z_root() . '/photo/profile/300/' . $profile['uid'] . '.jpg', - 'photo100' => z_root() . '/photo/profile/100/' . $profile['uid'] . '.jpg', - 'photo50' => z_root() . '/photo/profile/50/' . $profile['uid'] . '.jpg', - ); + // @fixme move this to the diaspora plugin itself + + if(plugin_is_installed('diaspora')) { + $diaspora = array( + 'podloc' => z_root(), + 'guid' => $profile['channel_guid'] . str_replace('.','',App::get_hostname()), + 'pubkey' => pemtorsa($profile['channel_pubkey']), + 'searchable' => (($block) ? 'false' : 'true'), + 'nickname' => $profile['channel_address'], + 'fullname' => $profile['channel_name'], + 'firstname' => $firstname, + 'lastname' => $lastname, + 'photo300' => z_root() . '/photo/profile/300/' . $profile['uid'] . '.jpg', + 'photo100' => z_root() . '/photo/profile/100/' . $profile['uid'] . '.jpg', + 'photo50' => z_root() . '/photo/profile/50/' . $profile['uid'] . '.jpg', + ); + } + else + $diaspora = ''; + $contact_block = contact_block(); @@ -1124,7 +1133,7 @@ function profile_sidebar($profile, $block = 0, $show_connect = true, $zcard = fa '$editmenu' => profile_edit_menu($profile['uid']) )); - $arr = array('profile' => &$profile, 'entry' => &$o); + $arr = array('profile' => $profile, 'entry' => $o); call_hooks('profile_sidebar', $arr); @@ -1156,7 +1165,6 @@ function advanced_profile(&$a) { } - $tpl = get_markup_template('profile_advanced.tpl'); $profile = array(); @@ -1191,12 +1199,12 @@ function advanced_profile(&$a) { if((substr(App::$profile['dob'],5,2) === '00') || (substr(App::$profile['dob'],8,2) === '00')) $val = substr(App::$profile['dob'],0,4); - + $year_bd_format = t('j F, Y'); $short_bd_format = t('j F'); if(! $val) { - $val = ((intval(App::$profile['dob'])) + $val = ((intval(App::$profile['dob'])) ? day_translate(datetime_convert('UTC','UTC',App::$profile['dob'] . ' 00:00 +00:00',$year_bd_format)) : day_translate(datetime_convert('UTC','UTC','2001-' . substr(App::$profile['dob'],5) . ' 00:00 +00:00',$short_bd_format))); } @@ -1241,7 +1249,7 @@ function advanced_profile(&$a) { if($txt = prepare_text(App::$profile['channels'])) $profile['channels'] = array( t('My other channels:'), $txt); if($txt = prepare_text(App::$profile['music'])) $profile['music'] = array( t('Musical interests:'), $txt); - + if($txt = prepare_text(App::$profile['book'])) $profile['book'] = array( t('Books, literature:'), $txt); if($txt = prepare_text(App::$profile['tv'])) $profile['tv'] = array( t('Television:'), $txt); @@ -1249,7 +1257,7 @@ function advanced_profile(&$a) { if($txt = prepare_text(App::$profile['film'])) $profile['film'] = array( t('Film/dance/culture/entertainment:'), $txt); if($txt = prepare_text(App::$profile['romance'])) $profile['romance'] = array( t('Love/Romance:'), $txt); - + if($txt = prepare_text(App::$profile['employment'])) $profile['employment'] = array( t('Work/employment:'), $txt); if($txt = prepare_text(App::$profile['education'])) $profile['education'] = array( t('School/education:'), $txt ); @@ -1268,7 +1276,7 @@ function advanced_profile(&$a) { $things = get_things(App::$profile['profile_guid'],App::$profile['profile_uid']); -// logger('mod_profile: things: ' . print_r($things,true), LOGGER_DATA); +// logger('mod_profile: things: ' . print_r($things,true), LOGGER_DATA); return replace_macros($tpl, array( '$title' => t('Profile'), @@ -1363,64 +1371,11 @@ function zat_init() { -/** - * @brief Adds a zid parameter to a url. - * - * @param string $s - * The url to accept the zid - * @param boolean $address - * $address to use instead of session environment - * @return string - * - * @hooks 'zid' - * string url - url to accept zid - * string zid - urlencoded zid - * string result - the return string we calculated, change it if you want to return something else - */ -function zid($s,$address = '') { - if (! strlen($s) || strpos($s,'zid=')) - return $s; - - $m = parse_url($s); - $fragment = ((array_key_exists('fragment',$m) && $m['fragment']) ? $m['fragment'] : false); - if($fragment !== false) - $s = str_replace('#' . $fragment,'',$s); - - $has_params = ((strpos($s,'?')) ? true : false); - $num_slashes = substr_count($s, '/'); - if (! $has_params) - $has_params = ((strpos($s, '&')) ? true : false); - - $achar = strpos($s,'?') ? '&' : '?'; - - $mine = get_my_url(); - $myaddr = (($address) ? $address : get_my_address()); - - /** @FIXME checking against our own channel url is no longer reliable. We may have a lot - * of urls attached to out channel. Should probably match against our site, since we - * will not need to remote authenticate on our own site anyway. - */ - - if ($mine && $myaddr && (! link_compare($mine,$s))) - $zurl = $s . (($num_slashes >= 3) ? '' : '/') . $achar . 'zid=' . urlencode($myaddr); - else - $zurl = $s; - - // put fragment at the end - - if($fragment) - $zurl .= '#' . $fragment; - - $arr = array('url' => $s, 'zid' => urlencode($myaddr), 'result' => $zurl); - call_hooks('zid', $arr); - - return $arr['result']; -} // Used from within PCSS themes to set theme parameters. If there's a // puid request variable, that is the "page owner" and normally their theme -// settings take precedence; unless a local user sets the "always_my_theme" -// system pconfig, which means they don't want to see anybody else's theme +// settings take precedence; unless a local user sets the "always_my_theme" +// system pconfig, which means they don't want to see anybody else's theme // settings except their own while on this site. function get_theme_uid() { @@ -1523,25 +1478,18 @@ function remote_online_status($webbie) { } -function get_channel_by_nick($nick) { - $r = q("select * from channel where channel_address = '%s' limit 1", - dbesc($nick) - ); - return(($r) ? $r[0] : false); - -} - /** * @brief * * @return string */ + function identity_selector() { - if (local_channel()) { + if(local_channel()) { $r = q("select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel.channel_account_id = %d and channel_removed = 0 order by channel_name ", intval(get_account_id()) ); - if (count($r) > 1) { + if($r && count($r) > 1) { //$account = App::get_account(); $o = replace_macros(get_markup_template('channel_id_select.tpl'), array( '$channels' => $r, @@ -1612,7 +1560,7 @@ function get_profile_fields_advanced($filter = 0) { * The channel to disable notifications for * @returns int * Current notification flag value. Send this to notifications_on() to restore the channel settings when finished - * with the activity requiring notifications_off(); + * with the activity requiring notifications_off(); */ function notifications_off($channel_id) { $r = q("select channel_notifyflags from channel where channel_id = %d limit 1", @@ -1626,18 +1574,18 @@ function notifications_off($channel_id) { } -function notifications_on($channel_id,$value) { +function notifications_on($channel_id, $value) { $x = q("update channel set channel_notifyflags = %d where channel_id = %d", intval($value), intval($channel_id) ); + return $x; } function get_channel_default_perms($uid) { - $ret = []; $r = q("select abook_xchan from abook where abook_channel = %d and abook_self = 1 limit 1", @@ -1659,7 +1607,6 @@ function get_channel_default_perms($uid) { function profiles_build_sync($channel_id) { - $r = q("select * from profile where uid = %d", intval($channel_id) ); @@ -1706,7 +1653,6 @@ function auto_channel_create($account_id) { $arr['nickname'] = check_webbie(array($arr['nickname'], $arr['nickname'] . mt_rand(1000,9999))); return create_identity($arr); - } function get_cover_photo($channel_id,$format = 'bbcode', $res = PHOTO_RES_COVER_1200) { @@ -1741,18 +1687,24 @@ function get_cover_photo($channel_id,$format = 'bbcode', $res = PHOTO_RES_COVER_ break; } - return $output; - + return $output; } -function get_zcard($channel,$observer_hash = '',$args = array()) { +/** + * @brief + * + * @param array $channel + * @param string $observer_hash + * @param array $args + * @return string + */ +function get_zcard($channel, $observer_hash = '', $args = array()) { logger('get_zcard'); $maxwidth = (($args['width']) ? intval($args['width']) : 0); $maxheight = (($args['height']) ? intval($args['height']) : 0); - if(($maxwidth > 1200) || ($maxwidth < 1)) $maxwidth = 1200; @@ -1760,25 +1712,22 @@ function get_zcard($channel,$observer_hash = '',$args = array()) { $width = 425; $size = 'hz_small'; $cover_size = PHOTO_RES_COVER_425; - $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 80 , 'height' => 80, 'href' => $channel['xchan_photo_m']); - } - elseif($maxwidth <= 900) { + $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 80 , 'height' => 80, 'href' => $channel['xchan_photo_m']); + } elseif($maxwidth <= 900) { $width = 900; $size = 'hz_medium'; $cover_size = PHOTO_RES_COVER_850; - $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 160 , 'height' => 160, 'href' => $channel['xchan_photo_l']); - } - elseif($maxwidth <= 1200) { + $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 160 , 'height' => 160, 'href' => $channel['xchan_photo_l']); + } elseif($maxwidth <= 1200) { $width = 1200; $size = 'hz_large'; $cover_size = PHOTO_RES_COVER_1200; - $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 300 , 'height' => 300, 'href' => $channel['xchan_photo_l']); + $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 300 , 'height' => 300, 'href' => $channel['xchan_photo_l']); } // $scale = (float) $maxwidth / $width; // $translate = intval(($scale / 1.0) * 100); - $channel['channel_addr'] = channel_reddress($channel); $zcard = array('chan' => $channel); @@ -1791,12 +1740,11 @@ function get_zcard($channel,$observer_hash = '',$args = array()) { if($r) { $cover = $r[0]; $cover['href'] = z_root() . '/photo/' . $r[0]['resource_id'] . '-' . $r[0]['imgscale']; - } - else { + } else { $cover = $pphoto; } - - $o .= replace_macros(get_markup_template('zcard.tpl'),array( + + $o .= replace_macros(get_markup_template('zcard.tpl'), array( '$maxwidth' => $maxwidth, '$scale' => $scale, '$translate' => $translate, @@ -1804,21 +1752,19 @@ function get_zcard($channel,$observer_hash = '',$args = array()) { '$cover' => $cover, '$pphoto' => $pphoto, '$zcard' => $zcard - )); - + )); + return $o; - } -function get_zcard_embed($channel,$observer_hash = '',$args = array()) { +function get_zcard_embed($channel, $observer_hash = '', $args = array()) { logger('get_zcard_embed'); $maxwidth = (($args['width']) ? intval($args['width']) : 0); $maxheight = (($args['height']) ? intval($args['height']) : 0); - if(($maxwidth > 1200) || ($maxwidth < 1)) $maxwidth = 1200; @@ -1853,11 +1799,10 @@ function get_zcard_embed($channel,$observer_hash = '',$args = array()) { if($r) { $cover = $r[0]; $cover['href'] = z_root() . '/photo/' . $r[0]['resource_id'] . '-' . $r[0]['imgscale']; - } - else { + } else { $cover = $pphoto; } - + $o .= replace_macros(get_markup_template('zcard_embed.tpl'),array( '$maxwidth' => $maxwidth, '$scale' => $scale, @@ -1866,36 +1811,73 @@ function get_zcard_embed($channel,$observer_hash = '',$args = array()) { '$cover' => $cover, '$pphoto' => $pphoto, '$zcard' => $zcard - )); - + )); + return $o; - } - +/** + * @brief + * + * @param string $nick + * @return mixed + */ function channelx_by_nick($nick) { $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_address = '%s' and channel_removed = 0 LIMIT 1", dbesc($nick) ); + return(($r) ? $r[0] : false); } +/** + * @brief + * + * @param string $hash + * @return mixed + */ function channelx_by_hash($hash) { - $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_hash = '%s' and channel_removed = 0 LIMIT 1", + $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_hash = '%s' and channel_removed = 0 LIMIT 1", dbesc($hash) ); + return(($r) ? $r[0] : false); } +/** + * @brief + * + * @param int $id + * @return mixed + */ function channelx_by_n($id) { - $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_id = %d and channel_removed = 0 LIMIT 1", + $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_id = %d and channel_removed = 0 LIMIT 1", dbesc($id) ); + return(($r) ? $r[0] : false); } +/** + * @brief + * + * @param string $channel + * @return string + */ function channel_reddress($channel) { - if(! ($channel && array_key_exists('channel_address',$channel))) + if(! ($channel && array_key_exists('channel_address', $channel))) return ''; + return strtolower($channel['channel_address'] . '@' . App::get_hostname()); +} + + +function channel_manual_conv_update($channel_id) { + + $x = get_pconfig($channel_id, 'system','manual_conversation_update'); + if($x === false) + $x = get_config('system','manual_conversation_update'); + + return intval($x); + }
\ No newline at end of file diff --git a/include/config.php b/include/config.php index 8c0469392..0b0e639ab 100644 --- a/include/config.php +++ b/include/config.php @@ -1,5 +1,4 @@ <?php - /** * @file include/config.php * @brief Arbitrary configuration storage. @@ -7,7 +6,6 @@ * Arrays get stored as serialized strings. * Booleans are stored as integer 0/1. * - * - <b>config</b> is used for hub specific configurations. It overrides the * configurations from .htconfig file. The storage is of size TEXT. * - <b>pconfig</b> is used for channel specific configurations and takes a @@ -26,7 +24,7 @@ * - get_config() and set_config() can also be done through the command line tool * @ref util/config.md "util/config" * - get_pconfig() and set_pconfig() can also be done through the command line tool - * @ref util/pconfig.md "util/pconfig" and takes a channel_id as first argument. + * @ref util/pconfig.md "util/pconfig" and takes a channel_id as first argument. * */ @@ -37,8 +35,8 @@ function load_config($family) { Zlib\Config::Load($family); } -function get_config($family, $key) { - return Zlib\Config::Get($family,$key); +function get_config($family, $key, $default = false) { + return Zlib\Config::Get($family,$key,$default); } function set_config($family, $key, $value) { @@ -53,8 +51,8 @@ function load_pconfig($uid) { Zlib\PConfig::Load($uid); } -function get_pconfig($uid, $family, $key, $instore = false) { - return Zlib\PConfig::Get($uid,$family,$key,$instore = false); +function get_pconfig($uid, $family, $key, $default = false) { + return Zlib\PConfig::Get($uid,$family,$key,$default); } function set_pconfig($uid, $family, $key, $value) { @@ -69,8 +67,8 @@ function load_xconfig($xchan) { Zlib\XConfig::Load($xchan); } -function get_xconfig($xchan, $family, $key) { - return Zlib\XConfig::Get($xchan,$family,$key); +function get_xconfig($xchan, $family, $key, $default = false) { + return Zlib\XConfig::Get($xchan,$family,$key, $default); } function set_xconfig($xchan, $family, $key, $value) { @@ -85,8 +83,8 @@ function load_aconfig($account_id) { Zlib\AConfig::Load($account_id); } -function get_aconfig($account_id, $family, $key) { - return Zlib\AConfig::Get($account_id, $family, $key); +function get_aconfig($account_id, $family, $key, $default = false) { + return Zlib\AConfig::Get($account_id, $family, $key, $default); } function set_aconfig($account_id, $family, $key, $value) { @@ -101,8 +99,8 @@ function load_abconfig($chan, $xhash, $family = '') { return Zlib\AbConfig::Load($chan,$xhash,$family); } -function get_abconfig($chan,$xhash,$family,$key) { - return Zlib\AbConfig::Get($chan,$xhash,$family,$key); +function get_abconfig($chan,$xhash,$family,$key, $default = false) { + return Zlib\AbConfig::Get($chan,$xhash,$family,$key, $default); } function set_abconfig($chan,$xhash,$family,$key,$value) { @@ -117,8 +115,8 @@ function load_iconfig(&$item) { Zlib\IConfig::Load($item); } -function get_iconfig(&$item, $family, $key) { - return Zlib\IConfig::Get($item, $family, $key); +function get_iconfig(&$item, $family, $key, $default = false) { + return Zlib\IConfig::Get($item, $family, $key, $default); } function set_iconfig(&$item, $family, $key, $value, $sharing = false) { diff --git a/include/connections.php b/include/connections.php index 017117dda..b08d046b3 100644 --- a/include/connections.php +++ b/include/connections.php @@ -297,16 +297,16 @@ function channel_remove($channel_id, $local = true, $unset_session=false) { } - q("DELETE FROM `groups` WHERE `uid` = %d", intval($channel_id)); - q("DELETE FROM `group_member` WHERE `uid` = %d", intval($channel_id)); - q("DELETE FROM `event` WHERE `uid` = %d", intval($channel_id)); - q("DELETE FROM `item` WHERE `uid` = %d", intval($channel_id)); - q("DELETE FROM `mail` WHERE `channel_id` = %d", intval($channel_id)); - q("DELETE FROM `notify` WHERE `uid` = %d", intval($channel_id)); - q("DELETE FROM `photo` WHERE `uid` = %d", intval($channel_id)); - q("DELETE FROM `attach` WHERE `uid` = %d", intval($channel_id)); - q("DELETE FROM `profile` WHERE `uid` = %d", intval($channel_id)); - q("DELETE FROM `pconfig` WHERE `uid` = %d", intval($channel_id)); + q("DELETE FROM groups WHERE uid = %d", intval($channel_id)); + q("DELETE FROM group_member WHERE uid = %d", intval($channel_id)); + q("DELETE FROM event WHERE uid = %d", intval($channel_id)); + q("DELETE FROM item WHERE uid = %d", intval($channel_id)); + q("DELETE FROM mail WHERE channel_id = %d", intval($channel_id)); + q("DELETE FROM notify WHERE uid = %d", intval($channel_id)); + q("DELETE FROM photo WHERE uid = %d", intval($channel_id)); + q("DELETE FROM attach WHERE uid = %d", intval($channel_id)); + q("DELETE FROM profile WHERE uid = %d", intval($channel_id)); + q("DELETE FROM pconfig WHERE uid = %d", intval($channel_id)); // @FIXME At this stage we need to remove the file resources located under /store/$nickname diff --git a/include/conversation.php b/include/conversation.php index 287dd4983..d367c27a6 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -146,7 +146,7 @@ function localize_item(&$item){ case ACTIVITY_OBJ_NOTE: default: $post_type = t('status'); - if($obj['mid'] != $obj['parent_mid']) + if($obj['id'] != $obj['parent']) $post_type = t('comment'); break; } @@ -269,8 +269,8 @@ function localize_item(&$item){ // (and update to json storage) if (activity_match($item['verb'],ACTIVITY_TAG)) { - $r = q("SELECT * from `item`,`contact` WHERE - `item`.`contact-id`=`contact`.`id` AND `item`.`mid`='%s';", + $r = q("SELECT * from item,contact WHERE + item.contact-id=contact.id AND item.mid='%s';", dbesc($item['parent_mid'])); if(count($r)==0) return; $obj=$r[0]; @@ -363,7 +363,7 @@ function localize_item(&$item){ if(intval($item['item_obscured']) && strlen($item['body']) && (! strpos($item['body'],'data'))) { - $item['body'] = json_encode(crypto_encapsulate($item['body'],get_config('system','pubkey'))); + $item['body'] = z_obscure($item['body']); } } @@ -405,8 +405,6 @@ function count_descendants($item) { function visible_activity($item) { $hidden_activities = [ ACTIVITY_LIKE, ACTIVITY_DISLIKE, ACTIVITY_AGREE, ACTIVITY_DISAGREE, ACTIVITY_ABSTAIN, ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE ]; - $post_types = [ ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_COMMENT, basename(ACTIVITY_OBJ_NOTE), basename(ACTIVITY_OBJ_COMMENT)]; - if(intval($item['item_notshown'])) return false; @@ -416,14 +414,32 @@ function visible_activity($item) { } } + if(is_edit_activity($item)) + return false; + + return true; +} + +/** + * @brief Check if a given activity is an edit activity + * + * + * @param array $item + * @return boolean + */ + +function is_edit_activity($item) { + + $post_types = [ ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_COMMENT, basename(ACTIVITY_OBJ_NOTE), basename(ACTIVITY_OBJ_COMMENT)]; + // In order to share edits with networks which have no concept of editing, we'll create // separate activities to indicate the edit. Our network will not require them, since our // edits are automatically applied and the activity indicated. if(($item['verb'] === ACTIVITY_UPDATE) && (in_array($item['obj_type'],$post_types))) - return false; + return true; - return true; + return false; } /** @@ -457,22 +473,6 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $ if (local_channel()) load_pconfig(local_channel(),''); - $arr_blocked = null; - - if (local_channel()) - $str_blocked = get_pconfig(local_channel(),'system','blocked'); - if (! local_channel() && ($mode == 'network')) { - $sys = get_sys_channel(); - $id = $sys['channel_id']; - $str_blocked = get_pconfig($id,'system','blocked'); - } - - if ($str_blocked) { - $arr_blocked = explode(',',$str_blocked); - for ($x = 0; $x < count($arr_blocked); $x ++) - $arr_blocked[$x] = trim($arr_blocked[$x]); - } - $profile_owner = 0; $page_writeable = false; $live_update_div = ''; @@ -599,17 +599,13 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $ foreach($items as $item) { - if($arr_blocked) { - $blocked = false; - foreach($arr_blocked as $b) { - if(($b) && (($item['author_xchan'] == $b) || ($item['owner_xchan'] == $b))) { - $blocked = true; - break; - } - } - if($blocked) - continue; - } + $x = [ 'mode' => $mode, 'item' => $item ]; + call_hooks('stream_item',$x); + + if($x['item']['blocked']) + continue; + + $item = $x['item']; $threadsid++; @@ -771,28 +767,14 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $ // Check for any blocked authors - if($arr_blocked) { - $blocked = false; - foreach($arr_blocked as $b) { - if(($b) && ($item['author_xchan'] == $b)) { - $blocked = true; - break; - } - } - if($blocked) - continue; - } - // Check all the kids too + $x = [ 'mode' => $mode, 'item' => $item ]; + call_hooks('stream_item',$x); + + if($x['item']['blocked']) + continue; - if($arr_blocked && $item['children']) { - for($d = 0; $d < count($item['children']); $d ++) { - foreach($arr_blocked as $b) { - if(($b) && ($item['children'][$d]['author_xchan'] == $b)) - $item['children'][$d]['author_blocked'] = true; - } - } - } + $item = $x['item']; builtin_activity_puller($item, $conv_responses); @@ -1296,7 +1278,8 @@ function status_editor($a, $x, $popup = false) { '$expiryModalOK' => t('OK'), '$expiryModalCANCEL' => t('Cancel'), '$expanded' => ((x($x, 'expanded')) ? $x['expanded'] : false), - '$bbcode' => ((x($x, 'bbcode')) ? $x['bbcode'] : false) + '$bbcode' => ((x($x, 'bbcode')) ? $x['bbcode'] : false), + '$parent' => ((array_key_exists('parent',$x) && $x['parent']) ? $x['parent'] : 0) )); if ($popup === true) { @@ -1536,7 +1519,11 @@ function network_tabs() { // tabs $tabs = array(); - if(! get_config('system','disable_discover_tab')) { + $d = get_config('system','disable_discover_tab'); + if($d === false) + $d = 1; + + if(! $d) { $tabs[] = array( 'label' => t('Discover'), 'url' => z_root() . '/' . $cmd . '?f=&fh=1' , @@ -1628,7 +1615,6 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ $uid = ((App::$profile['profile_uid']) ? App::$profile['profile_uid'] : local_channel()); $account_id = ((App::$profile['profile_uid']) ? App::$profile['channel_account_id'] : App::$channel['channel_account_id']); - if($uid == local_channel()) { $cal_link = ''; } @@ -1732,7 +1718,7 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ if(feature_enabled($uid,'wiki') && (get_account_techlevel($account_id) > 3)) { $tabs[] = array( - 'label' => t('Wiki'), + 'label' => t('Wikis'), 'url' => z_root() . '/wiki/' . $nickname, 'sel' => ((argv(0) == 'wiki') ? 'active' : ''), 'title' => t('Wiki'), diff --git a/include/crypto.php b/include/crypto.php index bc798d919..f75390985 100644 --- a/include/crypto.php +++ b/include/crypto.php @@ -48,27 +48,122 @@ function pkcs5_unpad($text) function AES256CBC_encrypt($data,$key,$iv) { return openssl_encrypt($data,'aes-256-cbc',str_pad($key,32,"\0"),OPENSSL_RAW_DATA,str_pad($iv,16,"\0")); - } function AES256CBC_decrypt($data,$key,$iv) { return openssl_decrypt($data,'aes-256-cbc',str_pad($key,32,"\0"),OPENSSL_RAW_DATA,str_pad($iv,16,"\0")); +} + +function AES128CBC_encrypt($data,$key,$iv) { + $key = substr($key,0,16); + $iv = substr($iv,0,16); + return openssl_encrypt($data,'aes-128-cbc',str_pad($key,16,"\0"),OPENSSL_RAW_DATA,str_pad($iv,16,"\0")); +} + +function AES128CBC_decrypt($data,$key,$iv) { + $key = substr($key,0,16); + $iv = substr($iv,0,16); + return openssl_decrypt($data,'aes-128-cbc',str_pad($key,16,"\0"),OPENSSL_RAW_DATA,str_pad($iv,16,"\0")); +} + +function STD_encrypt($data,$key,$iv) { + $key = substr($key,0,32); + $iv = substr($iv,0,16); + return openssl_encrypt($data,'aes-256-cbc',str_pad($key,32,"\0"),OPENSSL_RAW_DATA,str_pad($iv,16,"\0")); +} + +function STD_decrypt($data,$key,$iv) { + $key = substr($key,0,32); + $iv = substr($iv,0,16); + return openssl_decrypt($data,'aes-256-cbc',str_pad($key,32,"\0"),OPENSSL_RAW_DATA,str_pad($iv,16,"\0")); +} +function CAST5CBC_encrypt($data,$key,$iv) { + $key = substr($key,0,16); + $iv = substr($iv,0,8); + return openssl_encrypt($data,'cast5-cbc',str_pad($key,16,"\0"),OPENSSL_RAW_DATA,str_pad($iv,8,"\0")); +} + +function CAST5CBC_decrypt($data,$key,$iv) { + $key = substr($key,0,16); + $iv = substr($iv,0,8); + return openssl_decrypt($data,'cast5-cbc',str_pad($key,16,"\0"),OPENSSL_RAW_DATA,str_pad($iv,8,"\0")); } function crypto_encapsulate($data,$pubkey,$alg='aes256cbc') { + $fn = strtoupper($alg) . '_encrypt'; + if($alg === 'aes256cbc') return aes_encapsulate($data,$pubkey); + return other_encapsulate($data,$pubkey,$alg); + +} + +function other_encapsulate($data,$pubkey,$alg) { + if(! $pubkey) + logger('no key. data: ' . $data); + + $fn = strtoupper($alg) . '_encrypt'; + if(function_exists($fn)) { + + // A bit hesitant to use openssl_random_pseudo_bytes() as we know + // it has been historically targeted by US agencies for 'weakening'. + // It is still arguably better than trying to come up with an + // alternative cryptographically secure random generator. + // There is little point in using the optional second arg to flag the + // assurance of security since it is meaningless if the source algorithms + // have been compromised. Also none of this matters if RSA has been + // compromised by state actors and evidence is mounting that this has + // already happened. + + $key = openssl_random_pseudo_bytes(256); + $iv = openssl_random_pseudo_bytes(256); + $result['data'] = base64url_encode($fn($data,$key,$iv),true); + // log the offending call so we can track it down + if(! openssl_public_encrypt($key,$k,$pubkey)) { + $x = debug_backtrace(); + logger('RSA failed. ' . print_r($x[0],true)); + } + + $result['alg'] = $alg; + $result['key'] = base64url_encode($k,true); + openssl_public_encrypt($iv,$i,$pubkey); + $result['iv'] = base64url_encode($i,true); + return $result; + } + else { + $x = [ 'data' => $data, 'pubkey' => $pubkey, 'alg' => $alg, 'result' => $data ]; + call_hooks('other_encapsulate', $x); + return $x['result']; + } +} + +function crypto_methods() { + + if(\Zotlabs\Lib\System::get_server_role() !== 'pro') + return [ 'aes256cbc' ]; + + // 'std' is the new project standard which is aes256cbc but transmits/receives 256-byte key and iv. + // aes256cbc is provided for compatibility with earlier zot implementations which assume 32-byte key and 16-byte iv. + // other_encapsulate() now produces these longer keys/ivs by default so that it is difficult to guess a + // particular implementation or choice of underlying implementations based on the key/iv length. + // The actual methods are responsible for deriving the actual key/iv from the provided parameters; + // possibly by truncation or segmentation - though many other methods could be used. + + $r = [ 'std', 'aes256cbc', 'aes128cbc', 'cast5cbc' ]; + call_hooks('crypto_methods',$r); + return $r; + } function aes_encapsulate($data,$pubkey) { if(! $pubkey) logger('aes_encapsulate: no key. data: ' . $data); - $key = random_string(32,RANDOM_STRING_TEXT); - $iv = random_string(16,RANDOM_STRING_TEXT); + $key = openssl_random_pseudo_bytes(32); + $iv = openssl_random_pseudo_bytes(16); $result['data'] = base64url_encode(AES256CBC_encrypt($data,$key,$iv),true); // log the offending call so we can track it down if(! openssl_public_encrypt($key,$k,$pubkey)) { @@ -89,6 +184,22 @@ function crypto_unencapsulate($data,$prvkey) { if($alg === 'aes256cbc') return aes_unencapsulate($data,$prvkey); + return other_unencapsulate($data,$prvkey,$alg); + +} + +function other_unencapsulate($data,$prvkey,$alg) { + $fn = strtoupper($alg) . '_decrypt'; + if(function_exists($fn)) { + openssl_private_decrypt(base64url_decode($data['key']),$k,$prvkey); + openssl_private_decrypt(base64url_decode($data['iv']),$i,$prvkey); + return $fn(base64url_decode($data['data']),$k,$i); + } + else { + $x = [ 'data' => $data, 'prvkey' => $prvkey, 'alg' => $alg, 'result' => $data ]; + call_hooks('other_unencapsulate',$x); + return $x['result']; + } } diff --git a/include/datetime.php b/include/datetime.php index cd08ab367..85e87848b 100644 --- a/include/datetime.php +++ b/include/datetime.php @@ -92,8 +92,8 @@ function datetime_convert($from = 'UTC', $to = 'UTC', $s = 'now', $fmt = "Y-m-d // Slight hackish adjustment so that 'zero' datetime actually returns what is intended // otherwise we end up with -0001-11-30 ... - // add 32 days so that we at least get year 00, and then hack around the fact that - // months and days always start with 1. + // add 32 days so that we at least get year 00, and then hack around the fact that + // months and days always start with 1. // if(substr($s,0,10) == '0000-00-00') { // $d = new DateTime($s . ' + 32 days', new DateTimeZone('UTC')); @@ -195,7 +195,7 @@ function timesel($format, $h, $m, $id='timepicker') { /** * @brief Returns a datetime selector. * - * @param $format + * @param string $format * format string, e.g. 'ymd' or 'mdy'. Not currently supported * @param $min * unix timestamp of minimum date @@ -203,6 +203,7 @@ function timesel($format, $h, $m, $id='timepicker') { * unix timestap of maximum date * @param $default * unix timestamp of default date + * @param string $label * @param string $id * id and name of datetimepicker (defaults to "datetimepicker") * @param boolean $pickdate @@ -214,8 +215,9 @@ function timesel($format, $h, $m, $id='timepicker') { * @param $maxfrom * set maximum date from picker with id $maxfrom (none by default) * @param boolean $required default false + * @param int $first_day (optional) default 0 * @return string Parsed HTML output. - * + * * @todo Once browser support is better this could probably be replaced with * native HTML5 date picker. */ @@ -239,10 +241,10 @@ function datetimesel($format, $min, $max, $default, $label, $id = 'datetimepicke if(!$picktime) $pickers .= ',timepicker: false, closeOnDateSelect:true'; $extra_js = ''; - if($minfrom != '') + if($minfrom != '') $extra_js .= "\$('#id_$minfrom').data('xdsoft_datetimepicker').setOptions({onChangeDateTime: function (currentDateTime) { \$('#id_$id').data('xdsoft_datetimepicker').setOptions({minDate: currentDateTime})}})"; - if($maxfrom != '') + if($maxfrom != '') $extra_js .= "\$('#id_$maxfrom').data('xdsoft_datetimepicker').setOptions({onChangeDateTime: function (currentDateTime) { \$('#id_$id').data('xdsoft_datetimepicker').setOptions({maxDate: currentDateTime})}})"; $readable_format = $dateformat; @@ -276,7 +278,7 @@ function datetimesel($format, $min, $max, $default, $label, $id = 'datetimepicke */ function relative_date($posted_date, $format = null) { - $localtime = datetime_convert('UTC', date_default_timezone_get(), $posted_date); + $localtime = datetime_convert('UTC', date_default_timezone_get(), $posted_date); $abs = strtotime($localtime); @@ -340,7 +342,7 @@ function plural_dates($k,$n) { return; } } - + @@ -512,7 +514,7 @@ function cal($y = 0, $m = 0, $links = false, $class='') { /** * @brief Return the next birthday, converted from the owner's timezone to UTC. - * + * * This makes it globally portable. * If the provided birthday lacks a month and or day, return an empty string. * A missing year is acceptable. @@ -554,7 +556,7 @@ function update_birthdays() { require_once('include/event.php'); require_once('include/permissions.php'); - $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash + $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_dob > %s + interval %s and abook_dob < %s + interval %s", db_utcnow(), db_quoteinterval('7 day'), db_utcnow(), db_quoteinterval('14 day') @@ -572,7 +574,7 @@ function update_birthdays() { $ev['dtend'] = datetime_convert('UTC', 'UTC', $rr['abook_dob'] . ' + 1 day '); $ev['adjust'] = intval(feature_enabled($rr['abook_channel'],'smart_birthdays')); $ev['summary'] = sprintf( t('%1$s\'s birthday'), $rr['xchan_name']); - $ev['description'] = sprintf( t('Happy Birthday %1$s'), + $ev['description'] = sprintf( t('Happy Birthday %1$s'), '[zrl=' . $rr['xchan_url'] . ']' . $rr['xchan_name'] . '[/zrl]') ; $ev['etype'] = 'birthday'; diff --git a/include/dba/dba_driver.php b/include/dba/dba_driver.php index 852dc16af..81a3bd590 100755 --- a/include/dba/dba_driver.php +++ b/include/dba/dba_driver.php @@ -12,66 +12,78 @@ class DBA { static public $dba = null; static public $dbtype = null; + static public $scheme = 'mysql'; static public $logging = false; + static public $install_script = 'install/schema_mysql.sql'; + static public $null_date = '0001-01-01 00:00:00'; + static public $utc_now = 'UTC_TIMESTAMP()'; + static public $tquot = "`"; + + + /** * @brief Returns the database driver object. * - * If available it will use PHP's mysqli otherwise mysql driver. - * - * @param string $server DB server name + * @param string $server DB server name (or PDO dsn - e.g. mysqli:foobar.com;) * @param string $port DB port * @param string $user DB username * @param string $pass DB password * @param string $db database name * @param string $dbtype 0 for mysql, 1 for postgres * @param bool $install Defaults to false - * @return null|dba_driver A database driver object (dba_mysql|dba_mysqli) or null if no driver found. + * @return null|dba_driver A database driver object (dba_pdo) or null if no driver found. */ - static public function dba_factory($server, $port,$user,$pass,$db,$dbtype,$install = false) { + static public function dba_factory($server,$port,$user,$pass,$db,$dbtype,$install = false) { self::$dba = null; self::$dbtype = intval($dbtype); - $set_port = $port; + if(self::$dbtype == DBTYPE_POSTGRES) { - require_once('include/dba/dba_postgres.php'); - if(is_null($port)) $set_port = 5432; - self::$dba = new dba_postgres($server, $set_port, $user, $pass, $db, $install); + if(!($port)) + $port = 5432; + + self::$install_script = 'install/schema_postgres.sql'; + self::$utc_now = "now() at time zone 'UTC'"; + self::$tquot = '"'; + self::$scheme = 'pgsql'; + } else { -// Highly experimental at the present time. -// require_once('include/dba/dba_pdo.php'); -// self::$dba = new dba_pdo($server, $set_port,$user,$pass,$db,$install); -// } + // attempt to use the pdo driver compiled-in mysqli socket + // if using 'localhost' with no port configured. + // If this is wrong you'll need to set the socket path specifically + // using a server name of 'mysql:unix_socket=/socket/path', setting /socket/path + // as needed for your platform - if(class_exists('mysqli')) { - if (is_null($port)) $set_port = ini_get("mysqli.default_port"); - require_once('include/dba/dba_mysqli.php'); - self::$dba = new dba_mysqli($server, $set_port,$user,$pass,$db,$install); - } + if((!($port)) && ($server !== 'localhost')) + $port = 3306; } - // Until we have a proper PDO driver, store the DB connection parameters for - // plugins/addons which use PDO natively (such as cdav). This is wasteful as - // it opens a separate connection to the DB, but saves a lot of effort re-writing - // third-party interfaces that are working and well tested. - - + require_once('include/dba/dba_pdo.php'); + self::$dba = new dba_pdo($server,self::$scheme,$port,$user,$pass,$db,$install); + if(is_object(self::$dba) && self::$dba->connected) { - if($server === 'localhost') - $port = $set_port; - $dns = ((self::$dbtype == DBTYPE_POSTGRES) ? 'postgres' : 'mysql') - . ':host=' . $server . (is_null($port) ? '' : ';port=' . $port) - . ';dbname=' . $db; - self::$dba->pdo_set(array($dns,$user,$pass)); + + if(strpbrk($server,':;')) { + $dsn = $server; + } + else { + $dsn = self::$scheme . ':host=' . $server . (intval($port) ? '' : ';port=' . $port); + } + $dsn .= ';dbname=' . $db; + + + self::$dba->pdo_set(array($dsn,$user,$pass)); } - define('NULL_DATE', self::$dba->get_null_date()); + define('NULL_DATE', self::$null_date); define('ACTIVE_DBTYPE', self::$dbtype); + define('TQUOT', self::$tquot); return self::$dba; } @@ -85,11 +97,8 @@ class DBA { */ abstract class dba_driver { // legacy behavior - const INSTALL_SCRIPT='install/schema_mysql.sql'; - const NULL_DATE = '0001-01-01 00:00:00'; - const UTC_NOW = 'UTC_TIMESTAMP()'; - protected $db; + public $db; protected $pdo = array(); public $debug = 0; @@ -108,7 +117,7 @@ abstract class dba_driver { * @param string $db database name * @return bool */ - abstract function connect($server, $port, $user, $pass, $db); + abstract function connect($server, $scheme, $port, $user, $pass, $db); /** * @brief Perform a DB query with the SQL statement $sql. @@ -142,34 +151,39 @@ abstract class dba_driver { */ abstract function getdriver(); - function __construct($server, $port, $user,$pass,$db,$install = false) { - if(($install) && (! $this->install($server, $port, $user, $pass, $db))) { + function __construct($server, $scheme, $port, $user,$pass,$db,$install = false) { + if(($install) && (! $this->install($server, $scheme, $port, $user, $pass, $db))) { return; } - $this->connect($server, $port, $user, $pass, $db); + $this->connect($server, $scheme, $port, $user, $pass, $db); } function get_null_date() { - return static::NULL_DATE; + return \DBA::$null_date; } function get_install_script() { - return static::INSTALL_SCRIPT; + return \DBA::$install_script; + } + + function get_table_quote() { + return \DBA::$tquot; } + function utcnow() { - return static::UTC_NOW; + return \DBA::$utc_now; } - function install($server,$user,$pass,$db) { + function install($server,$scheme,$port,$user,$pass,$db) { if (!(strlen($server) && strlen($user))){ $this->connected = false; $this->db = null; return false; } - if(strlen($server) && ($server !== 'localhost') && ($server !== '127.0.0.1')) { - if((! dns_get_record($server, DNS_A + DNS_CNAME + DNS_PTR)) && (! filter_var($server, FILTER_VALIDATE_IP))) { + if(strlen($server) && ($server !== 'localhost') && ($server !== '127.0.0.1') && (! strpbrk($server,':;'))) { + if(! z_dns_check($server)) { $this->error = sprintf( t('Cannot locate DNS info for database server \'%s\''), $server); $this->connected = false; $this->db = null; @@ -313,7 +327,7 @@ function db_concat($fld, $sep) { * queries return true if the command was successful or false if it wasn't. * * Example: - * $r = q("SELECT * FROM `%s` WHERE `uid` = %d", + * $r = q("SELECT * FROM %s WHERE `uid` = %d", * 'user', 1); * * @param string $sql The SQL query to execute @@ -384,9 +398,22 @@ function dbesc_array_cb(&$item, $key) { function dbesc_array(&$arr) { + $bogus_key = false; if(is_array($arr) && count($arr)) { + $matches = false; + foreach($arr as $k => $v) { + if(preg_match('/([^a-zA-Z0-9\-\_\.])/',$k,$matches)) { + logger('bogus key: ' . $k); + $bogus_key = true; + } + } array_walk($arr,'dbesc_array_cb'); + if($bogus_key) { + $arr['BOGUS.KEY'] = 1; + return false; + } } + return true; } function db_getfunc($f) { diff --git a/include/dba/dba_mysql.php b/include/dba/dba_mysql.php index 3cadad6dc..8b51cf578 100755 --- a/include/dba/dba_mysql.php +++ b/include/dba/dba_mysql.php @@ -5,7 +5,7 @@ require_once('include/dba/dba_driver.php'); class dba_mysql extends dba_driver { - function connect($server, $port, $user,$pass,$db) { + function connect($server, $scheme, $port, $user,$pass,$db) { $this->db = mysql_connect($server.":".$port,$user,$pass); if($this->db && mysql_select_db($db,$this->db)) { $this->connected = true; diff --git a/include/dba/dba_mysqli.php b/include/dba/dba_mysqli.php index afd2aa642..165c8e969 100755 --- a/include/dba/dba_mysqli.php +++ b/include/dba/dba_mysqli.php @@ -4,7 +4,7 @@ require_once('include/dba/dba_driver.php'); class dba_mysqli extends dba_driver { - function connect($server,$port,$user,$pass,$db) { + function connect($server,$scheme,$port,$user,$pass,$db) { if($port) $this->db = new mysqli($server,$user,$pass,$db, $port); else diff --git a/include/dba/dba_pdo.php b/include/dba/dba_pdo.php index 7255a2b66..f76e6cdd7 100755 --- a/include/dba/dba_pdo.php +++ b/include/dba/dba_pdo.php @@ -7,16 +7,21 @@ class dba_pdo extends dba_driver { public $driver_dbtype = null; - function connect($server,$port,$user,$pass,$db) { + function connect($server,$scheme,$port,$user,$pass,$db) { - $this->driver_dbtype = 'mysql'; // (($dbtype == DBTYPE_POSTGRES) ? 'postgres' : 'mysql'); - $dns = $this->driver_dbtype - . ':host=' . $server . (is_null($port) ? '' : ';port=' . $port) - . ';dbname=' . $db; + $this->driver_dbtype = $scheme; + if(strpbrk($server,':;')) { + $dsn = $server; + } + else { + $dsn = $this->driver_dbtype . ':host=' . $server . (intval($port) ? '' : ';port=' . $port); + } + + $dsn .= ';dbname=' . $db; try { - $this->db = new PDO($dns,$user,$pass); + $this->db = new PDO($dsn,$user,$pass); $this->db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); } catch(PDOException $e) { @@ -27,6 +32,9 @@ class dba_pdo extends dba_driver { return false; } + if($this->driver_dbtype === 'pgsql') + $this->q("SET standard_conforming_strings = 'off'; SET backslash_quote = 'on';"); + $this->connected = true; return true; @@ -36,17 +44,23 @@ class dba_pdo extends dba_driver { if((! $this->db) || (! $this->connected)) return false; + if($this->driver_dbtype === 'pgsql') { + if(substr(rtrim($sql),-1,1) !== ';') { + $sql .= ';'; + } + } + $this->error = ''; $select = ((stripos($sql,'select') === 0) ? true : false); try { - $result = $this->db->query($sql); + $result = $this->db->query($sql, PDO::FETCH_ASSOC); } catch(PDOException $e) { $this->error = $e->getMessage(); if($this->error) { - db_logger('dba_mysqli: ERROR: ' . printable($sql) . "\n" . $this->error, LOGGER_NORMAL, LOG_ERR); + db_logger('dba_pdo: ERROR: ' . printable($sql) . "\n" . $this->error, LOGGER_NORMAL, LOG_ERR); if(file_exists('dbfail.out')) { file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . "\n" . $this->error . "\n", FILE_APPEND); } @@ -55,13 +69,13 @@ class dba_pdo extends dba_driver { if(!($select)) { if($this->debug) { - db_logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returns ' . (($result) ? 'true' : 'false'), LOGGER_NORMAL,(($result) ? LOG_INFO : LOG_ERR)); + db_logger('dba_pdo: DEBUG: ' . printable($sql) . ' returns ' . (($result) ? 'true' : 'false'), LOGGER_NORMAL,(($result) ? LOG_INFO : LOG_ERR)); } return $result; } if($this->debug) { - db_logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returned ' . count($result) . ' results.', LOGGER_NORMAL, LOG_INFO); + db_logger('dba_pdo: DEBUG: ' . printable($sql) . ' returned ' . count($result) . ' results.', LOGGER_NORMAL, LOG_INFO); } $r = array(); @@ -73,7 +87,7 @@ class dba_pdo extends dba_driver { db_logger('dba_pdo: ' . printable(print_r($r,true)), LOGGER_NORMAL, LOG_INFO); } } - return $r; + return (($this->error) ? false : $r); } function escape($str) { @@ -88,6 +102,54 @@ class dba_pdo extends dba_driver { $this->connected = false; } + function concat($fld,$sep) { + if($this->driver_dbtype === 'pgsql') { + return 'string_agg(' . $fld . ',\'' . $sep . '\')'; + } + else { + return 'GROUP_CONCAT(DISTINCT ' . $fld . ' SEPARATOR \'' . $sep . '\')'; + } + } + + function quote_interval($txt) { + if($this->driver_dbtype === 'pgsql') { + return "'$txt'"; + } + else { + return $txt; + } + } + + // These two functions assume that postgres standard_conforming_strings is set to off; + // which we perform during DB open. + + function escapebin($str) { + if($this->driver_dbtype === 'pgsql') { + return "\\\\x" . bin2hex($str); + } + else { + return $this->escape($str); + } + } + + function unescapebin($str) { + if($this->driver_dbtype === 'pgsql' && (! is_null($str))) { + $x = ''; + while(! feof($str)) { + $x .= fread($str,8192); + } + if(substr($x,0,2) === '\\x') { + $x = hex2bin(substr($x,2)); + } + return $x; + + } + else { + return $str; + } + } + + function getdriver() { return 'pdo'; } diff --git a/include/dba/dba_postgres.php b/include/dba/dba_postgres.php index 03b29d703..560d8da60 100644 --- a/include/dba/dba_postgres.php +++ b/include/dba/dba_postgres.php @@ -7,8 +7,9 @@ class dba_postgres extends dba_driver { const INSTALL_SCRIPT='install/schema_postgres.sql'; const NULL_DATE = '0001-01-01 00:00:00'; const UTC_NOW = "now() at time zone 'UTC'"; + const TQUOT = '"'; - function connect($server,$port,$user,$pass,$db) { + function connect($server,$scheme,$port,$user,$pass,$db) { if(!$port) $port = 5432; $connstr = 'host=' . $server . ' port='.$port . ' user=' . $user . ' password=' . $pass . ' dbname='. $db; $this->db = pg_connect($connstr); diff --git a/include/dir_fns.php b/include/dir_fns.php index 03cc2706a..3922730fc 100644 --- a/include/dir_fns.php +++ b/include/dir_fns.php @@ -192,17 +192,19 @@ function sync_directories($dirmode) { 'site_update' => NULL_DATE, 'site_directory' => DIRECTORY_FALLBACK_MASTER . '/dirsearch', 'site_realm' => DIRECTORY_REALM, - 'site_valid' => 1 + 'site_valid' => 1, + 'site_crypto' => 'aes256cbc' ); - $x = q("insert into site ( site_url, site_flags, site_update, site_directory, site_realm, site_valid ) - values ( '%s', %d, '%s', '%s', '%s', %d ) ", + $x = q("insert into site ( site_url, site_flags, site_update, site_directory, site_realm, site_valid, site_crypto ) + values ( '%s', %d, '%s', '%s', '%s', %d, '%s' ) ", dbesc($r[0]['site_url']), intval($r[0]['site_flags']), dbesc($r[0]['site_update']), dbesc($r[0]['site_directory']), dbesc($r[0]['site_realm']), - intval($r[0]['site_valid']) + intval($r[0]['site_valid']), + dbesc($r[0]['site_crypto']) ); $r = q("select * from site where site_flags in (%d, %d) and site_url != '%s' and site_type = %d ", diff --git a/include/event.php b/include/event.php index 153654120..cbee2b759 100644 --- a/include/event.php +++ b/include/event.php @@ -362,28 +362,28 @@ function event_store_event($arr) { // The event changed. Update it. - $r = q("UPDATE `event` SET - `edited` = '%s', - `dtstart` = '%s', - `dtend` = '%s', - `summary` = '%s', - `description` = '%s', - `location` = '%s', - `etype` = '%s', - `adjust` = %d, - `nofinish` = %d, - `event_status` = '%s', - `event_status_date` = '%s', - `event_percent` = %d, - `event_repeat` = '%s', - `event_sequence` = %d, - `event_priority` = %d, - `event_vdata` = '%s', - `allow_cid` = '%s', - `allow_gid` = '%s', - `deny_cid` = '%s', - `deny_gid` = '%s' - WHERE `id` = %d AND `uid` = %d", + $r = q("UPDATE event SET + edited = '%s', + dtstart = '%s', + dtend = '%s', + summary = '%s', + description = '%s', + location = '%s', + etype = '%s', + adjust = %d, + nofinish = %d, + event_status = '%s', + event_status_date = '%s', + event_percent = %d, + event_repeat = '%s', + event_sequence = %d, + event_priority = %d, + event_vdata = '%s', + allow_cid = '%s', + allow_gid = '%s', + deny_cid = '%s', + deny_gid = '%s' + WHERE id = %d AND uid = %d", dbesc($arr['edited']), dbesc($arr['dtstart']), diff --git a/include/follow.php b/include/follow.php index 5f63687f8..fa198e402 100644 --- a/include/follow.php +++ b/include/follow.php @@ -211,7 +211,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) return $result; } - $r = q("select abook_xchan, abook_instance from abook where abook_xchan = '%s' and abook_channel = %d limit 1", + $r = q("select abook_id, abook_xchan, abook_pending, abook_instance from abook where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($xchan_hash), intval($uid) ); @@ -237,7 +237,28 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) $x = q("update abook set abook_instance = '%s' where abook_id = %d", dbesc($abook_instance), intval($r[0]['abook_id']) - ); + ); + + if(intval($r[0]['abook_pending'])) { + + $abook_my_perms = get_channel_default_perms($uid); + $role = get_pconfig($uid,'system','permissions_role'); + if($role) { + $x = \Zotlabs\Access\PermissionRoles::role_perms($role); + if($x['perms_connect']) { + $abook_my_perms = $x['perms_connect']; + } + } + + $filled_perms = \Zotlabs\Access\Permissions::FilledPerms($abook_my_perms); + foreach($filled_perms as $k => $v) { + set_abconfig($uid,$r[0]['abook_xchan'],'my_perms',$k,$v); + } + + $x = q("update abook set abook_pending = 0 where abook_id = %d", + intval($r[0]['abook_id']) + ); + } } else { $closeness = get_pconfig($uid,'system','new_abook_closeness'); diff --git a/include/group.php b/include/group.php index 10853ff6b..38d9d190f 100644 --- a/include/group.php +++ b/include/group.php @@ -14,11 +14,11 @@ function group_add($uid,$name,$public = 0) { // access lists. What we're doing here is reviving the dead group, but old content which // was restricted to this group may now be seen by the new group members. - $z = q("SELECT * FROM `groups` WHERE `id` = %d LIMIT 1", + $z = q("SELECT * FROM groups WHERE id = %d LIMIT 1", intval($r) ); if(($z) && $z[0]['deleted']) { - /*$r = q("UPDATE `groups` SET `deleted` = 0 WHERE `uid` = %d AND `gname` = '%s' LIMIT 1", + /*$r = q("UPDATE groups SET deleted = 0 WHERE uid = %d AND gname = '%s'", intval($uid), dbesc($name) );*/ @@ -32,13 +32,13 @@ function group_add($uid,$name,$public = 0) { $dups = false; $hash = random_string() . $name; - $r = q("SELECT id FROM `groups` WHERE hash = '%s' LIMIT 1", dbesc($hash)); + $r = q("SELECT id FROM groups WHERE hash = '%s' LIMIT 1", dbesc($hash)); if($r) $dups = true; } while($dups == true); - $r = q("INSERT INTO `groups` ( hash, uid, visible, gname ) + $r = q("INSERT INTO groups ( hash, uid, visible, gname ) VALUES( '%s', %d, %d, '%s' ) ", dbesc($hash), intval($uid), @@ -57,7 +57,7 @@ function group_add($uid,$name,$public = 0) { function group_rmv($uid,$name) { $ret = false; if(x($uid) && x($name)) { - $r = q("SELECT id, hash FROM `groups` WHERE `uid` = %d AND `gname` = '%s' LIMIT 1", + $r = q("SELECT id, hash FROM groups WHERE uid = %d AND gname = '%s' LIMIT 1", intval($uid), dbesc($name) ); @@ -102,13 +102,13 @@ function group_rmv($uid,$name) { } // remove all members - $r = q("DELETE FROM `group_member` WHERE `uid` = %d AND `gid` = %d ", + $r = q("DELETE FROM group_member WHERE uid = %d AND gid = %d ", intval($uid), intval($group_id) ); // remove group - $r = q("UPDATE `groups` SET `deleted` = 1 WHERE `uid` = %d AND `gname` = '%s'", + $r = q("UPDATE groups SET deleted = 1 WHERE uid = %d AND gname = '%s'", intval($uid), dbesc($name) ); @@ -125,7 +125,7 @@ function group_rmv($uid,$name) { function group_byname($uid,$name) { if((! $uid) || (! strlen($name))) return false; - $r = q("SELECT * FROM `groups` WHERE `uid` = %d AND `gname` = '%s' LIMIT 1", + $r = q("SELECT * FROM groups WHERE uid = %d AND gname = '%s' LIMIT 1", intval($uid), dbesc($name) ); @@ -138,7 +138,7 @@ function group_byname($uid,$name) { function group_rec_byhash($uid,$hash) { if((! $uid) || (! strlen($hash))) return false; - $r = q("SELECT * FROM `groups` WHERE `uid` = %d AND `hash` = '%s' LIMIT 1", + $r = q("SELECT * FROM groups WHERE uid = %d AND hash = '%s' LIMIT 1", intval($uid), dbesc($hash) ); @@ -153,7 +153,7 @@ function group_rmv_member($uid,$name,$member) { return false; if(! ( $uid && $gid && $member)) return false; - $r = q("DELETE FROM `group_member` WHERE `uid` = %d AND `gid` = %d AND xchan = '%s' ", + $r = q("DELETE FROM group_member WHERE uid = %d AND gid = %d AND xchan = '%s' ", intval($uid), intval($gid), dbesc($member) @@ -173,7 +173,7 @@ function group_add_member($uid,$name,$member,$gid = 0) { if((! $gid) || (! $uid) || (! $member)) return false; - $r = q("SELECT * FROM `group_member` WHERE `uid` = %d AND `gid` = %d AND `xchan` = '%s' LIMIT 1", + $r = q("SELECT * FROM group_member WHERE uid = %d AND gid = %d AND xchan = '%s' LIMIT 1", intval($uid), intval($gid), dbesc($member) @@ -183,7 +183,7 @@ function group_add_member($uid,$name,$member,$gid = 0) { // we indicate success because the group member was in fact created // -- It was just created at another time if(! $r) - $r = q("INSERT INTO `group_member` (`uid`, `gid`, `xchan`) + $r = q("INSERT INTO group_member (uid, gid, xchan) VALUES( %d, %d, '%s' ) ", intval($uid), intval($gid), @@ -198,9 +198,9 @@ function group_add_member($uid,$name,$member,$gid = 0) { function group_get_members($gid) { $ret = array(); if(intval($gid)) { - $r = q("SELECT * FROM `group_member` - LEFT JOIN abook ON abook_xchan = `group_member`.`xchan` left join xchan on xchan_hash = abook_xchan - WHERE `gid` = %d AND abook_channel = %d and `group_member`.`uid` = %d and xchan_deleted = 0 and abook_self = 0 and abook_blocked = 0 and abook_pending = 0 ORDER BY xchan_name ASC ", + $r = q("SELECT * FROM group_member + LEFT JOIN abook ON abook_xchan = group_member.xchan left join xchan on xchan_hash = abook_xchan + WHERE gid = %d AND abook_channel = %d and group_member.uid = %d and xchan_deleted = 0 and abook_self = 0 and abook_blocked = 0 and abook_pending = 0 ORDER BY xchan_name ASC ", intval($gid), intval(local_channel()), intval(local_channel()) @@ -232,7 +232,7 @@ function mini_group_select($uid,$group = '') { $grps = array(); $o = ''; - $r = q("SELECT * FROM `groups` WHERE `deleted` = 0 AND `uid` = %d ORDER BY `gname` ASC", + $r = q("SELECT * FROM groups WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", intval($uid) ); $grps[] = array('name' => '', 'hash' => '0', 'selected' => ''); @@ -271,7 +271,7 @@ function group_side($every="connections",$each="group",$edit = false, $group_id ); - $r = q("SELECT * FROM `groups` WHERE `deleted` = 0 AND `uid` = %d ORDER BY `gname` ASC", + $r = q("SELECT * FROM groups WHERE deleted = 0 AND uid = %d ORDER BY gname ASC", intval($_SESSION['uid']) ); $member_of = array(); @@ -328,7 +328,7 @@ function expand_groups($a) { $groups = implode(',', $x); if($groups) - $r = q("SELECT xchan FROM group_member WHERE gid IN ( select id from `groups` where hash in ( $groups ))"); + $r = q("SELECT xchan FROM group_member WHERE gid IN ( select id from groups where hash in ( $groups ))"); $ret = array(); if($r) @@ -340,7 +340,7 @@ function expand_groups($a) { function member_of($c) { - $r = q("SELECT `groups`.`gname`, `groups`.`id` FROM `groups` LEFT JOIN `group_member` ON `group_member`.`gid` = `groups`.`id` WHERE `group_member`.`xchan` = '%s' AND `groups`.`deleted` = 0 ORDER BY `groups`.`gname` ASC ", + $r = q("SELECT groups.gname, groups.id FROM groups LEFT JOIN group_member ON group_member.gid = groups.id WHERE group_member.xchan = '%s' AND groups.deleted = 0 ORDER BY groups.gname ASC ", dbesc($c) ); @@ -350,7 +350,7 @@ function member_of($c) { function groups_containing($uid,$c) { - $r = q("SELECT `gid` FROM `group_member` WHERE `uid` = %d AND `group_member`.`xchan` = '%s' ", + $r = q("SELECT gid FROM group_member WHERE uid = %d AND group_member.xchan = '%s' ", intval($uid), dbesc($c) ); diff --git a/include/help.php b/include/help.php index 3081ae41f..03d01d1a1 100644 --- a/include/help.php +++ b/include/help.php @@ -1,15 +1,21 @@ <?php +/** + * @brief + * + * @param string $tocpath + * @return string|unknown + */ function get_help_content($tocpath = false) { - + global $lang; - + $doctype = 'markdown'; - + $text = ''; $path = (($tocpath !== false) ? $tocpath : ''); - + if($tocpath === false && argc() > 1) { $path = ''; for($x = 1; $x < argc(); $x ++) { @@ -25,7 +31,7 @@ function get_help_content($tocpath = false) { \App::$page['title'] = t('Help:') . ' ' . ucwords(str_replace('-',' ',notags($title))); $text = load_doc_file('doc/' . $path . '.md'); - + if(! $text) { $text = load_doc_file('doc/' . $path . '.bb'); if($text) @@ -49,20 +55,21 @@ function get_help_content($tocpath = false) { if(! $text) { $doctype = 'bbcode'; $text = load_doc_file('doc/main.bb'); + goaway('/help/about/about_hubzilla'); \App::$page['title'] = t('Help'); } - + if(! $text) { header($_SERVER["SERVER_PROTOCOL"] . ' 404 ' . t('Not Found')); $tpl = get_markup_template("404.tpl"); return replace_macros($tpl, array( - '$message' => t('Page not found.' ) + '$message' => t('Page not found.') )); } } - + if($doctype === 'html') - $content = $text; + $content = parseIdentityAwareHTML($text); if($doctype === 'markdown') { require_once('library/markdown.php'); # escape #include tags @@ -74,30 +81,30 @@ function get_help_content($tocpath = false) { require_once('include/bbcode.php'); $content = bbcode($text); // bbcode retargets external content to new windows. This content is internal. - $content = str_replace(' target="_blank"','',$content); - } - + $content = str_replace(' target="_blank"', '', $content); + } + $content = preg_replace_callback("/#include (.*?)\;/ism", 'preg_callback_help_include', $content); + return translate_projectname($content); - } function preg_callback_help_include($matches) { - + if($matches[1]) { $include = str_replace($matches[0],load_doc_file($matches[1]),$matches[0]); if(preg_match('/\.bb$/', $matches[1]) || preg_match('/\.txt$/', $matches[1])) { require_once('include/bbcode.php'); $include = bbcode($include); - $include = str_replace(' target="_blank"','',$include); - } + $include = str_replace(' target="_blank"','',$include); + } elseif(preg_match('/\.md$/', $matches[1])) { require_once('library/markdown.php'); $include = Markdown($include); } return $include; } - + } @@ -110,10 +117,10 @@ function load_doc_file($s) { $d = dirname($s); $c = find_doc_file("$d/$lang/$b"); - if($c) + if($c) return $c; $c = find_doc_file($s); - if($c) + if($c) return $c; return ''; } @@ -125,6 +132,12 @@ function find_doc_file($s) { return ''; } +/** + * @brief + * + * @param string $s + * @return number|mixed|unknown|boolean + */ function search_doc_files($s) { $itemspage = get_pconfig(local_channel(),'system','itemspage'); @@ -133,21 +146,21 @@ function search_doc_files($s) { $regexop = db_getfunc('REGEXP'); - $r = q("select iconfig.v, item.* from item left join iconfig on item.id = iconfig.iid + $r = q("select iconfig.v, item.* from item left join iconfig on item.id = iconfig.iid where iconfig.cat = 'system' and iconfig.k = 'docfile' and body $regexop '%s' and item_type = %d $pager_sql", dbesc($s), intval(ITEM_TYPE_DOC) ); - - $r = fetch_post_tags($r,true); + + $r = fetch_post_tags($r, true); for($x = 0; $x < count($r); $x ++) { $position = stripos($r[$x]['body'], $s); $dislen = 300; $start = $position-floor($dislen/2); if ( $start < 0) { - $start = 0; + $start = 0; } $r[$x]['text'] = substr($r[$x]['body'], $start, $dislen); @@ -159,43 +172,50 @@ function search_doc_files($s) { } } } - if(stristr($r[$x]['v'],$s)) + if(stristr($r[$x]['v'], $s)) $r[$x]['rank'] ++; - $r[$x]['rank'] += substr_count(strtolower($r[$x]['text']),strtolower($s)); + $r[$x]['rank'] += substr_count(strtolower($r[$x]['text']), strtolower($s)); // bias the results to the observer's native language if($r[$x]['lang'] === \App::$language) $r[$x]['rank'] = $r[$x]['rank'] + 10; } usort($r,'doc_rank_sort'); + return $r; } -function doc_rank_sort($s1,$s2) { +function doc_rank_sort($s1, $s2) { if($s1['rank'] == $s2['rank']) return 0; + return (($s1['rank'] < $s2['rank']) ? 1 : (-1)); } - +/** + * @brief + * + * @return string + */ function load_context_help() { - + $path = App::$cmd; $args = App::$argv; $lang = App::$language; - + if(! isset($lang) || !is_dir('doc/context/' . $lang . '/')) { - $lang = 'en'; - } + $lang = 'en'; + } while($path) { $context_help = load_doc_file('doc/context/' . $lang . '/' . $path . '/help.html'); - if(!$context_help) { - // Fallback to English if the translation is absent - $context_help = load_doc_file('doc/context/en/' . $path . '/help.html'); - } + if(!$context_help) { + // Fallback to English if the translation is absent + $context_help = load_doc_file('doc/context/en/' . $path . '/help.html'); + } if($context_help) break; + array_pop($args); $path = implode($args,'/'); } @@ -203,7 +223,12 @@ function load_context_help() { return $context_help; } - +/** + * @brief + * + * @param string $s + * @return void|boolean[]|number[]|string[]|unknown[] + */ function store_doc_file($s) { if(is_dir($s)) @@ -215,10 +240,9 @@ function store_doc_file($s) { $item['aid'] = 0; $item['uid'] = $sys['channel_id']; - - if(strpos($s,'.md')) + if(strpos($s, '.md')) $mimetype = 'text/markdown'; - elseif(strpos($s,'.html')) + elseif(strpos($s, '.html')) $mimetype = 'text/html'; else $mimetype = 'text/bbcode'; @@ -227,12 +251,12 @@ function store_doc_file($s) { $item['body'] = html2plain(prepare_text(file_get_contents($s),$mimetype, true)); $item['mimetype'] = 'text/plain'; - + $item['plink'] = z_root() . '/' . str_replace('doc','help',$s); $item['owner_xchan'] = $item['author_xchan'] = $sys['channel_hash']; $item['item_type'] = ITEM_TYPE_DOC; - $r = q("select item.* from item left join iconfig on item.id = iconfig.iid + $r = q("select item.* from item left join iconfig on item.id = iconfig.iid where iconfig.cat = 'system' and iconfig.k = 'docfile' and iconfig.v = '%s' and item_type = %d limit 1", dbesc($s), @@ -252,6 +276,4 @@ function store_doc_file($s) { } return $x; - } - diff --git a/include/hubloc.php b/include/hubloc.php index 397646449..17f921f67 100644 --- a/include/hubloc.php +++ b/include/hubloc.php @@ -1,19 +1,5 @@ <?php /** @file */ -function is_matrix_url($url) { - $m = @parse_url($url); - if($m['host']) { - $r = q("select hubloc_url from hubloc where hubloc_host = '%s' limit 1", - dbesc($m['host']) - ); - if($r) - return true; - } - return false; -} - - - function prune_hub_reinstalls() { $r = q("select site_url from site where site_type = %d", diff --git a/include/import.php b/include/import.php index 479e45cc2..0d8398acb 100644 --- a/include/import.php +++ b/include/import.php @@ -82,14 +82,7 @@ function import_channel($channel, $account_id, $seize) { } if($clean) { - dbesc_array($clean); - - $r = dbq("INSERT INTO channel (`" - . implode("`, `", array_keys($clean)) - . "`) VALUES ('" - . implode("', '", array_values($clean)) - . "')" - ); + create_table_from_array('channel',$clean); } if(! $r) { @@ -131,12 +124,7 @@ function import_config($channel,$configs) { foreach($configs as $config) { unset($config['id']); $config['uid'] = $channel['channel_id']; - dbesc_array($config); - $r = dbq("INSERT INTO pconfig (`" - . implode("`, `", array_keys($config)) - . "`) VALUES ('" - . implode("', '", array_values($config)) - . "')" ); + create_table_from_array('pconfig',$config); } load_pconfig($channel['channel_id']); } @@ -161,14 +149,7 @@ function import_profiles($channel,$profiles) { $profile['photo'] = z_root() . '/photo/profile/l/' . $channel['channel_id']; $profile['thumb'] = z_root() . '/photo/profile/m/' . $channel['channel_id']; - - dbesc_array($profile); - $r = dbq("INSERT INTO profile (`" - . implode("`, `", array_keys($profile)) - . "`) VALUES ('" - . implode("', '", array_values($profile)) - . "')" - ); + create_table_from_array('profile',$profile); } } } @@ -203,14 +184,7 @@ function import_hublocs($channel,$hublocs,$seize) { if(! zot_gethub($arr)) { unset($hubloc['hubloc_id']); - dbesc_array($hubloc); - - $r = dbq("INSERT INTO hubloc (`" - . implode("`, `", array_keys($hubloc)) - . "`) VALUES ('" - . implode("', '", array_values($hubloc)) - . "')" - ); + create_table_from_array('hubloc',$hubloc); } } } @@ -242,14 +216,7 @@ function import_objs($channel,$objs) { $obj['obj_imgurl'] = $x[0]; } - dbesc_array($obj); - - $r = dbq("INSERT INTO obj (`" - . implode("`, `", array_keys($obj)) - . "`) VALUES ('" - . implode("', '", array_values($obj)) - . "')" - ); + create_table_from_array('obj',$obj); } } } @@ -260,7 +227,7 @@ function sync_objs($channel,$objs) { foreach($objs as $obj) { if(array_key_exists('obj_deleted',$obj) && $obj['obj_deleted'] && $obj['obj_obj']) { - q("delete from obj where obj_obj = '%s' and obj_channel = %d limit 1", + q("delete from obj where obj_obj = '%s' and obj_channel = %d", dbesc($obj['obj_obj']), intval($channel['channel_id']) ); @@ -304,7 +271,7 @@ function sync_objs($channel,$objs) { if($exists) { unset($obj['obj_obj']); foreach($obj as $k => $v) { - $r = q("UPDATE obj SET `%s` = '%s' WHERE obj_obj = '%s' AND obj_channel = %d", + $r = q("UPDATE obj SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE obj_obj = '%s' AND obj_channel = %d", dbesc($k), dbesc($v), dbesc($hash), @@ -313,15 +280,7 @@ function sync_objs($channel,$objs) { } } else { - - dbesc_array($obj); - - $r = dbq("INSERT INTO obj (`" - . implode("`, `", array_keys($obj)) - . "`) VALUES ('" - . implode("', '", array_values($obj)) - . "')" - ); + create_table_from_array('obj',$obj); } } } @@ -351,13 +310,7 @@ function import_apps($channel,$apps) { $hash = $app['app_id']; - dbesc_array($app); - $r = dbq("INSERT INTO app (`" - . implode("`, `", array_keys($app)) - . "`) VALUES ('" - . implode("', '", array_values($app)) - . "')" - ); + create_table_from_array('app',$app); if($term) { $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", @@ -398,7 +351,7 @@ function sync_apps($channel,$apps) { } if(array_key_exists('app_deleted',$app) && $app['app_deleted'] && $app['app_id']) { - q("delete from app where app_id = '%s' and app_channel = %d limit 1", + q("delete from app where app_id = '%s' and app_channel = %d", dbesc($app['app_id']), intval($channel['channel_id']) ); @@ -451,7 +404,7 @@ function sync_apps($channel,$apps) { if($exists) { unset($app['app_id']); foreach($app as $k => $v) { - $r = q("UPDATE app SET `%s` = '%s' WHERE app_id = '%s' AND app_channel = %d", + $r = q("UPDATE app SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE app_id = '%s' AND app_channel = %d", dbesc($k), dbesc($v), dbesc($hash), @@ -460,15 +413,10 @@ function sync_apps($channel,$apps) { } } else { - dbesc_array($app); - $r = dbq("INSERT INTO app (`" - . implode("`, `", array_keys($app)) - . "`) VALUES ('" - . implode("', '", array_values($app)) - . "')" - ); + create_table_from_array('app',$app); + if($term) { - $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", + $x = q("select * from app where app_id = '%s' and app_channel = %d", dbesc($hash), intval($channel['channel_id']) ); @@ -502,13 +450,7 @@ function import_chatrooms($channel,$chatrooms) { $chatroom['cr_aid'] = $channel['channel_account_id']; $chatroom['cr_uid'] = $channel['channel_id']; - dbesc_array($chatroom); - $r = dbq("INSERT INTO chatroom (`" - . implode("`, `", array_keys($chatroom)) - . "`) VALUES ('" - . implode("', '", array_values($chatroom)) - . "')" - ); + create_table_from_array('chatroom',$chatroom); } } } @@ -524,7 +466,7 @@ function sync_chatrooms($channel,$chatrooms) { continue; if(array_key_exists('cr_deleted',$chatroom) && $chatroom['cr_deleted']) { - q("delete from chatroom where cr_name = '%s' and cr_uid = %d limit 1", + q("delete from chatroom where cr_name = '%s' and cr_uid = %d", dbesc($chatroom['cr_name']), intval($channel['channel_id']) ); @@ -559,7 +501,7 @@ function sync_chatrooms($channel,$chatrooms) { if($exists) { foreach($chatroom as $k => $v) { - $r = q("UPDATE chatroom SET `%s` = '%s' WHERE cr_name = '%s' AND cr_uid = %d", + $r = q("UPDATE chatroom SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE cr_name = '%s' AND cr_uid = %d", dbesc($k), dbesc($v), dbesc($name), @@ -568,13 +510,7 @@ function sync_chatrooms($channel,$chatrooms) { } } else { - dbesc_array($chatroom); - $r = dbq("INSERT INTO chatroom (`" - . implode("`, `", array_keys($chatroom)) - . "`) VALUES ('" - . implode("', '", array_values($chatroom)) - . "')" - ); + create_table_from_array('chatroom',$chatroom); } } } @@ -684,13 +620,7 @@ function import_events($channel,$events) { convert_oldfields($event,'type','etype'); convert_oldfields($event,'ignore','dismissed'); - dbesc_array($event); - $r = dbq("INSERT INTO event (`" - . implode("`, `", array_keys($event)) - . "`) VALUES ('" - . implode("', '", array_values($event)) - . "')" - ); + create_table_from_array('event',$event); } } } @@ -705,7 +635,7 @@ function sync_events($channel,$events) { continue; if($event['event_deleted']) { - $r = q("delete from event where event_hash = '%s' and uid = %d limit 1", + $r = q("delete from event where event_hash = '%s' and uid = %d", dbesc($event['event_hash']), intval($channel['channel_id']) ); @@ -736,7 +666,7 @@ function sync_events($channel,$events) { if($exists) { foreach($event as $k => $v) { - $r = q("UPDATE event SET `%s` = '%s' WHERE event_hash = '%s' AND uid = %d", + $r = q("UPDATE event SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE event_hash = '%s' AND uid = %d", dbesc($k), dbesc($v), dbesc($event['event_hash']), @@ -745,13 +675,7 @@ function sync_events($channel,$events) { } } else { - dbesc_array($event); - $r = dbq("INSERT INTO event (`" - . implode("`, `", array_keys($event)) - . "`) VALUES ('" - . implode("', '", array_values($event)) - . "')" - ); + create_table_from_array('event',$event); } } } @@ -927,12 +851,7 @@ function import_likes($channel,$likes) { if($r) continue; - dbesc_array($like); - $r = dbq("INSERT INTO likes (`" - . implode("`, `", array_keys($like)) - . "`) VALUES ('" - . implode("', '", array_values($like)) - . "')" ); + create_table_from_array('likes',$like); } } } @@ -941,7 +860,7 @@ function import_conv($channel,$convs) { if($channel && $convs) { foreach($convs as $conv) { if($conv['deleted']) { - q("delete from conv where guid = '%s' and uid = %d limit 1", + q("delete from conv where guid = '%s' and uid = %d", dbesc($conv['guid']), intval($channel['channel_id']) ); @@ -959,13 +878,7 @@ function import_conv($channel,$convs) { ); if($r) continue; - - dbesc_array($conv); - $r = dbq("INSERT INTO conv (`" - . implode("`, `", array_keys($conv)) - . "`) VALUES ('" - . implode("', '", array_values($conv)) - . "')" ); + create_table_from_array('conv',$conv); } } } @@ -976,14 +889,14 @@ function import_mail($channel,$mails,$sync = false) { if($channel && $mails) { foreach($mails as $mail) { if(array_key_exists('flags',$mail) && in_array('deleted',$mail['flags'])) { - q("delete from mail where mid = '%s' and uid = %d limit 1", + q("delete from mail where mid = '%s' and uid = %d", dbesc($mail['message_id']), intval($channel['channel_id']) ); continue; } if(array_key_exists('flags',$mail) && in_array('recalled',$mail['flags'])) { - q("update mail set mail_recalled = 1 where mid = '%s' and uid = %d limit 1", + q("update mail set mail_recalled = 1 where mid = '%s' and uid = %d", dbesc($mail['message_id']), intval($channel['channel_id']) ); @@ -1110,25 +1023,22 @@ function sync_files($channel,$files) { if(!isset($att['os_path'])) $att['os_path'] = ''; - dbesc_array($att); if($attach_exists) { logger('sync_files attach exists: ' . print_r($att,true), LOGGER_DEBUG); + if(! dbesc_array($att)) + continue; $str = ''; - foreach($att as $k => $v) { - if($str) - $str .= ","; - $str .= " `" . $k . "` = '" . $v . "' "; - } - $r = dbq("update `attach` set " . $str . " where id = " . intval($attach_id) ); + foreach($att as $k => $v) { + if($str) + $str .= ","; + $str .= " " . TQUOT . $k . TQUOT . " = '" . $v . "' "; + } + $r = dbq("update attach set " . $str . " where id = " . intval($attach_id) ); } else { logger('sync_files attach does not exists: ' . print_r($att,true), LOGGER_DEBUG); - $r = dbq("INSERT INTO attach (`" - . implode("`, `", array_keys($att)) - . "`) VALUES ('" - . implode("', '", array_values($att)) - . "')" ); + create_table_from_array('attach',$att); } @@ -1229,23 +1139,20 @@ function sync_files($channel,$files) { intval($channel['channel_id']) ); - dbesc_array($p); if($exists) { + if(! dbesc_array($p)) + continue; $str = ''; foreach($p as $k => $v) { if($str) $str .= ","; - $str .= " `" . $k . "` = '" . $v . "' "; + $str .= " " . TQUOT . $k . TQUOT . " = '" . $v . "' "; } - $r = dbq("update `photo` set " . $str . " where id = " . intval($exists[0]['id']) ); + $r = dbq("update photo set " . $str . " where id = " . intval($exists[0]['id']) ); } else { - $r = dbq("INSERT INTO photo (`" - . implode("`, `", array_keys($p)) - . "`) VALUES ('" - . implode("', '", array_values($p)) - . "')" ); + create_attach_from_array('photo',$p); } } } diff --git a/include/items.php b/include/items.php index c62d53c3e..9bd256d58 100755 --- a/include/items.php +++ b/include/items.php @@ -3,9 +3,6 @@ * @file include/items.php */ -// uncertain if this line is needed and why -use Sabre\HTTP\URLUtil; - use Zotlabs\Lib as Zlib; require_once('include/bbcode.php'); @@ -66,6 +63,7 @@ function collect_recipients($item, &$private_envelope) { if($recipients && $deny) $recipients = array_diff($recipients,$deny); + $private_envelope = true; } else { @@ -116,7 +114,7 @@ function collect_recipients($item, &$private_envelope) { // Add the authors of any posts in this thread, if they are known to us. // This is specifically designed to forward wall-to-wall posts to the original author, - // in case they aren't a connection but have permission to write on our wall. + // in case they aren't a connection but have permission to write on our wall. // This is important for issue tracker channels. It should be a no-op for most channels. // Whether or not they will accept the delivery is not determined here, but should // be taken into account by zot:process_delivery() @@ -131,7 +129,6 @@ function collect_recipients($item, &$private_envelope) { } } } - } @@ -173,19 +170,19 @@ function comments_are_now_closed($item) { } function item_normal() { - return " and item.item_hidden = 0 and item.item_type = 0 and item.item_deleted = 0 - and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 + return " and item.item_hidden = 0 and item.item_type = 0 and item.item_deleted = 0 + and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 and item.item_blocked = 0 "; } /** * @brief - * - * This is a compatibility function primarily for plugins, because + * + * This is a compatibility function primarily for plugins, because * in earlier DB schemas this was a much simpler single integer compare * + * @param array $item */ - function is_item_normal($item) { if(intval($item['item_hidden']) || intval($item['item_type']) || intval($item['item_deleted']) @@ -193,8 +190,7 @@ function is_item_normal($item) { || intval($item['item_blocked'])) return false; - return true; - + return true; } /** @@ -236,7 +232,7 @@ function can_comment_on_post($observer_xchan, $item) { case 'public': // We don't really allow or support public comments yet, but anonymous // folks won't ever reach this point (as $observer_xchan will be empty). - // This means the viewer has an xchan and we can identify them. + // This means the viewer has an xchan and we can identify them. return true; break; case 'any connections': @@ -295,78 +291,6 @@ function add_source_route($iid, $hash) { /** - * @brief preg_match function when fixing 'naked' links in mod item.php. - * - * Check if we've got a hubloc for the site and use a zrl if we do, a url if we don't. - * Remove any existing zid= param which may have been pasted by mistake - and will have - * the author's credentials. zid's are dynamic and can't really be passed around like - * that. - * - * @param array $matches - * @return string - */ -function red_zrl_callback($matches) { - require_once('include/hubloc.php'); - $zrl = is_matrix_url($matches[2]); - - $t = strip_zids($matches[2]); - if($t !== $matches[2]) { - $zrl = true; - $matches[2] = $t; - } - - if($matches[1] === '#^') - $matches[1] = ''; - if($zrl) - return $matches[1] . '#^[zrl=' . $matches[2] . ']' . $matches[2] . '[/zrl]'; - - return $matches[1] . '#^[url=' . $matches[2] . ']' . $matches[2] . '[/url]'; -} - -/** - * If we've got a url or zrl tag with a naked url somewhere in the link text, - * escape it with quotes unless the naked url is a linked photo. - * - * @param array $matches - * @return string - */ -function red_escape_zrl_callback($matches) { - - // Uncertain why the url/zrl forms weren't picked up by the non-greedy regex. - - if((strpos($matches[3], 'zmg') !== false) || (strpos($matches[3], 'img') !== false) || (strpos($matches[3],'zrl') !== false) || (strpos($matches[3],'url') !== false)) - return $matches[0]; - - return '[' . $matches[1] . 'rl' . $matches[2] . ']' . $matches[3] . '"' . $matches[4] . '"' . $matches[5] . '[/' . $matches[6] . 'rl]'; -} - -function red_escape_codeblock($m) { - return '[$b64' . $m[2] . base64_encode($m[1]) . '[/' . $m[2] . ']'; -} - -function red_unescape_codeblock($m) { - return '[' . $m[2] . base64_decode($m[1]) . '[/' . $m[2] . ']'; -} - - -function red_zrlify_img_callback($matches) { - require_once('include/hubloc.php'); - $zrl = is_matrix_url($matches[2]); - - $t = strip_zids($matches[2]); - if($t !== $matches[2]) { - $zrl = true; - $matches[2] = $t; - } - - if($zrl) - return '[zmg' . $matches[1] . ']' . $matches[2] . '[/zmg]'; - - return $matches[0]; -} - - -/** * @brief Post an activity. * * In its simplest form one needs only to set $arr['body'] to post a note to the logged in channel's wall. @@ -481,7 +405,7 @@ function validate_item_elements($message,$arr) { if(! array_key_exists('created',$arr)) $result['message'] = 'missing created, possible author/owner lookup failure'; - if((! $arr['mid']) || (! $arr['parent_mid'])) + if((! $arr['mid']) || (! $arr['parent_mid'])) $result['message'] = 'missing message-id or parent message-id'; if(array_key_exists('flags',$message) && in_array('relay',$message['flags']) && $arr['mid'] === $arr['parent_mid']) @@ -495,10 +419,6 @@ function validate_item_elements($message,$arr) { } - - - - /** * @brief Limit lenght on imported system messages. * @@ -652,7 +572,6 @@ function get_item_elements($x,$allow_code = false) { if(mb_strlen($arr['title']) > 255) $arr['title'] = mb_substr($arr['title'],0,255); - $arr['app'] = (($x['app']) ? htmlspecialchars($x['app'], ENT_COMPAT,'UTF-8',false) : ''); $arr['route'] = (($x['route']) ? htmlspecialchars($x['route'], ENT_COMPAT,'UTF-8',false) : ''); $arr['mid'] = (($x['message_id']) ? htmlspecialchars($x['message_id'], ENT_COMPAT,'UTF-8',false) : ''); @@ -714,7 +633,7 @@ function get_item_elements($x,$allow_code = false) { // hub and verify that they are legit - or else we're going to toss the post. We only need to do this // once, and after that your hub knows them. Sure some info is in the post, but it's only a transit identifier // and not enough info to be able to look you up from your hash - which is the only thing stored with the post. - + $xchan_hash = import_author_xchan($x['author']); if($xchan_hash) $arr['author_xchan'] = $xchan_hash; @@ -1057,7 +976,6 @@ function encode_item($item,$mirror = false) { $x['item_blocked'] = $item['item_blocked']; } - $x['message_id'] = $item['mid']; $x['message_top'] = $item['parent_mid']; $x['message_parent'] = $item['thr_parent']; @@ -1078,9 +996,9 @@ function encode_item($item,$mirror = false) { $x['longlat'] = $item['coord']; $x['signature'] = $item['sig']; $x['route'] = $item['route']; - $x['owner'] = encode_item_xchan($item['owner']); $x['author'] = encode_item_xchan($item['author']); + if($item['obj']) $x['object'] = json_decode($item['obj'],true); if($item['target']) @@ -1237,7 +1155,7 @@ function decode_item_meta($meta) { $ret[] = array('cat' => escape_tags($m['family']),'k' => escape_tags($m['key']),'v' => $m['value'],'sharing' => $m['sharing']); } } - return $ret; + return $ret; } /** @@ -1530,6 +1448,8 @@ function get_profile_elements($x) { * * @param array $arr * @param boolean $allow_exec (optional) default false + * @param boolean $deliver (optional) default true + * * @return array * * \e boolean \b success * * \e int \b item_id @@ -1543,6 +1463,11 @@ function item_store($arr, $allow_exec = false, $deliver = true) { $ret = array('success' => false, 'item_id' => 0); + if(array_key_exists('cancel',$arr) && $arr['cancel']) { + logger('cancelled by plugin'); + return $ret; + } + if(! $arr['uid']) { logger('item_store: no uid'); $ret['message'] = 'No uid.'; @@ -1662,7 +1587,7 @@ function item_store($arr, $allow_exec = false, $deliver = true) { // otherwise, just preserve the original timestamp. $arr['received'] = ((x($arr,'received') !== false) ? datetime_convert('UTC','UTC',$arr['received']) : datetime_convert()); - $arr['changed'] = ((x($arr,'changed') !== false) ? datetime_convert('UTC','UTC',$arr['changed']) : datetime_convert()); + $arr['changed'] = ((x($arr,'changed') !== false) ? datetime_convert('UTC','UTC',$arr['changed']) : datetime_convert()); } $arr['location'] = ((x($arr,'location')) ? notags(trim($arr['location'])) : ''); @@ -1681,7 +1606,7 @@ function item_store($arr, $allow_exec = false, $deliver = true) { $arr['public_policy'] = ((x($arr,'public_policy')) ? notags(trim($arr['public_policy'])) : '' ); $arr['comment_policy'] = ((x($arr,'comment_policy')) ? notags(trim($arr['comment_policy'])) : 'contacts' ); - + if(! array_key_exists('item_unseen',$arr)) $arr['item_unseen'] = 1; @@ -1717,7 +1642,7 @@ function item_store($arr, $allow_exec = false, $deliver = true) { // find the parent and snarf the item id and ACL's // and anything else we need to inherit - $r = q("SELECT * FROM `item` WHERE `mid` = '%s' AND `uid` = %d ORDER BY `id` ASC LIMIT 1", + $r = q("SELECT * FROM item WHERE mid = '%s' AND uid = %d ORDER BY id ASC LIMIT 1", dbesc($arr['parent_mid']), intval($arr['uid']) ); @@ -1749,8 +1674,8 @@ function item_store($arr, $allow_exec = false, $deliver = true) { if($r[0]['mid'] != $r[0]['parent_mid']) { $arr['parent_mid'] = $r[0]['parent_mid']; - $z = q("SELECT * FROM `item` WHERE `mid` = '%s' AND `parent_mid` = '%s' AND `uid` = %d - ORDER BY `id` ASC LIMIT 1", + $z = q("SELECT * FROM item WHERE mid = '%s' AND parent_mid = '%s' AND uid = %d + ORDER BY id ASC LIMIT 1", dbesc($r[0]['parent_mid']), dbesc($r[0]['parent_mid']), intval($arr['uid']) @@ -1801,7 +1726,7 @@ function item_store($arr, $allow_exec = false, $deliver = true) { if($parent_deleted) $arr['item_deleted'] = 1; - $r = q("SELECT `id` FROM `item` WHERE `mid` = '%s' AND `uid` = %d LIMIT 1", + $r = q("SELECT id FROM item WHERE mid = '%s' AND uid = %d LIMIT 1", dbesc($arr['mid']), intval($arr['uid']) ); @@ -1853,17 +1778,12 @@ function item_store($arr, $allow_exec = false, $deliver = true) { logger('item_store: ' . print_r($arr,true), LOGGER_DATA); - dbesc_array($arr); - $r = dbq("INSERT INTO `item` (`" - . implode("`, `", array_keys($arr)) - . "`) VALUES ('" - . implode("', '", array_values($arr)) - . "')" ); + create_table_from_array('item',$arr); // find the item we just created - $r = q("SELECT * FROM `item` WHERE `mid` = '%s' AND `uid` = %d ORDER BY `id` ASC ", + $r = q("SELECT * FROM item WHERE mid = '%s' AND uid = %d ORDER BY id ASC ", $arr['mid'], // already dbesc'd intval($arr['uid']) ); @@ -1880,7 +1800,7 @@ function item_store($arr, $allow_exec = false, $deliver = true) { } if(count($r) > 1) { logger('item_store: duplicated post occurred. Removing duplicates.'); - q("DELETE FROM `item` WHERE `mid` = '%s' AND `uid` = %d AND `id` != %d ", + q("DELETE FROM item WHERE mid = '%s' AND uid = %d AND id != %d ", $arr['mid'], intval($arr['uid']), intval($current_post) @@ -1893,6 +1813,7 @@ function item_store($arr, $allow_exec = false, $deliver = true) { $x = q("update item set parent = id where id = %d", intval($r[0]['id']) ); + $arr['parent'] = $r[0]['id']; } @@ -1926,18 +1847,22 @@ function item_store($arr, $allow_exec = false, $deliver = true) { call_hooks('post_remote_end',$arr); - // update the commented timestamp on the parent + // update the commented timestamp on the parent - unless this is potentially a clone of an older item + // which we don't wish to bring to the surface. As the queue only holds deliveries for 3 days, it's + // suspected of being an older cloned item if the creation time is older than that. - $z = q("select max(created) as commented from item where parent_mid = '%s' and uid = %d and item_delayed = 0 ", - dbesc($arr['parent_mid']), - intval($arr['uid']) - ); + if($arr['created'] > datetime_convert('','','now - 4 days')) { + $z = q("select max(created) as commented from item where parent_mid = '%s' and uid = %d and item_delayed = 0 ", + dbesc($arr['parent_mid']), + intval($arr['uid']) + ); - q("UPDATE item set commented = '%s', changed = '%s' WHERE id = %d", - dbesc(($z) ? $z[0]['commented'] : (datetime_convert())), - dbesc(datetime_convert()), - intval($parent_id) - ); + q("UPDATE item set commented = '%s', changed = '%s' WHERE id = %d", + dbesc(($z) ? $z[0]['commented'] : (datetime_convert())), + dbesc(datetime_convert()), + intval($parent_id) + ); + } // If _creating_ a deleted item, don't propagate it further or send out notifications. @@ -1966,6 +1891,12 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) { $allow_exec = $d['allow_exec']; $ret = array('success' => false, 'item_id' => 0); + + if(array_key_exists('cancel',$arr) && $arr['cancel']) { + logger('cancelled by plugin'); + return $ret; + } + if(! intval($arr['uid'])) { logger('item_store_update: no uid'); $ret['message'] = 'no uid.'; @@ -2072,20 +2003,8 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) { $arr['commented'] = $orig[0]['commented']; - if($deliver) { - $arr['received'] = datetime_convert(); - $arr['changed'] = datetime_convert(); - } - else { - - // When deliver flag is false, we are *probably* performing an import or bulk migration. - // If one updates the changed timestamp it will be made available to zotfeed and delivery - // will still take place through backdoor methods. Since these fields are rarely used - // otherwise, just preserve the original timestamp. - - $arr['received'] = $orig[0]['received']; - $arr['changed'] = $orig[0]['changed']; - } + $arr['received'] = $orig[0]['received']; + $arr['changed'] = $orig[0]['changed']; $arr['route'] = ((array_key_exists('route',$arr)) ? trim($arr['route']) : $orig[0]['route']); $arr['diaspora_meta'] = ((x($arr,'diaspora_meta')) ? $arr['diaspora_meta'] : $orig[0]['diaspora_meta']); @@ -2166,7 +2085,10 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) { } - dbesc_array($arr); + if(! dbesc_array($arr)) { + $ret['message'] = 'DB array malformed'; + return $ret; + } logger('item_store_update: ' . print_r($arr,true), LOGGER_DATA); @@ -2174,10 +2096,10 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) { foreach($arr as $k => $v) { if($str) $str .= ","; - $str .= " `" . $k . "` = '" . $v . "' "; + $str .= " " . TQUOT . $k . TQUOT . " = '" . $v . "' "; } - $r = dbq("update `item` set " . $str . " where id = " . $orig_post_id ); + $r = dbq("update item set " . $str . " where id = " . $orig_post_id ); if($r) logger('item_store_update: updated item ' . $orig_post_id, LOGGER_DEBUG); @@ -2336,10 +2258,10 @@ function send_status_notifications($post_id,$item) { // check for an unfollow thread activity - we should probably decode the obj and check the id // but it will be extremely rare for this to be wrong. - if(($xx['verb'] === ACTIVITY_UNFOLLOW) - && ($xx['obj_type'] === ACTIVITY_OBJ_NOTE || $xx['obj_type'] === ACTIVITY_OBJ_PHOTO) + if(($xx['verb'] === ACTIVITY_UNFOLLOW) + && ($xx['obj_type'] === ACTIVITY_OBJ_NOTE || $xx['obj_type'] === ACTIVITY_OBJ_PHOTO) && ($xx['parent'] != $xx['id'])) - $unfollowed = true; + $unfollowed = true; } if($xx['id'] == $xx['parent']) { $parent = $xx['parent']; @@ -2562,7 +2484,7 @@ function tag_deliver($uid, $item_id) { if($mention) { logger('tag_deliver: mention found for ' . $u[0]['channel_name']); - + $r = q("update item set item_mentionsme = 1 where id = %d", intval($item_id) ); @@ -2851,7 +2773,7 @@ function start_delivery_chain($channel, $item, $item_id, $parent) { $title = $item['title']; $body = $item['body']; - $r = q("update item set item_uplink = %d, item_nocomment = %d, item_obscured = %d, item_flags = %d, owner_xchan = '%s', allow_cid = '%s', allow_gid = '%s', + $r = q("update item set item_uplink = %d, item_nocomment = %d, item_obscured = %d, item_flags = %d, owner_xchan = '%s', allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', item_private = %d, public_policy = '%s', comment_policy = '%s', title = '%s', body = '%s', item_wall = %d, item_origin = %d where id = %d", intval($item_uplink), intval($item_nocomment), @@ -3066,7 +2988,7 @@ function mail_store($arr) { $arr['parent_mid'] = $arr['mid']; } - $r = q("SELECT `id` FROM mail WHERE `mid` = '%s' AND channel_id = %d LIMIT 1", + $r = q("SELECT id FROM mail WHERE mid = '%s' AND channel_id = %d LIMIT 1", dbesc($arr['mid']), intval($arr['channel_id']) ); @@ -3088,19 +3010,13 @@ function mail_store($arr) { return 0; } - dbesc_array($arr); - logger('mail_store: ' . print_r($arr,true), LOGGER_DATA); - $r = dbq("INSERT INTO mail (`" - . implode("`, `", array_keys($arr)) - . "`) VALUES ('" - . implode("', '", array_values($arr)) - . "')" ); + create_table_from_array('mail', $arr); // find the item we just created - $r = q("SELECT `id` FROM mail WHERE `mid` = '%s' AND `channel_id` = %d ORDER BY `id` ASC ", + $r = q("SELECT id FROM mail WHERE mid = '%s' AND channel_id = %d ORDER BY id ASC ", $arr['mid'], // already dbesc'd intval($arr['channel_id']) ); @@ -3116,7 +3032,7 @@ function mail_store($arr) { } if(count($r) > 1) { logger('mail_store: duplicated post occurred. Removing duplicates.'); - q("DELETE FROM mail WHERE `mid` = '%s' AND `channel_id` = %d AND `id` != %d ", + q("DELETE FROM mail WHERE mid = '%s' AND channel_id = %d AND id != %d ", $arr['mid'], intval($arr['channel_id']), intval($current_post) @@ -3168,7 +3084,7 @@ function fix_private_photos($s, $uid, $item = null, $cid = 0) { if($x) { $res = substr($i,$x+1); $i = substr($i,0,$x); - $r = q("SELECT * FROM `photo` WHERE `resource_id` = '%s' AND `imgscale` = %d AND `uid` = %d", + $r = q("SELECT * FROM photo WHERE resource_id = '%s' AND imgscale = %d AND uid = %d", dbesc($i), intval($res), intval($uid) @@ -3352,7 +3268,7 @@ function item_expire($uid,$days) { AND item_starred = 0 $sql_extra $item_normal LIMIT $expire_limit ", intval($uid), - db_utcnow(), + db_utcnow(), db_quoteinterval(intval($days).' DAY') ); @@ -3745,7 +3661,7 @@ function fetch_post_tags($items,$link = false) { ); $imeta = q("select * from iconfig where iid in ( %s )", dbesc($tag_finder_str) - ); + ); } @@ -3878,13 +3794,13 @@ function zot_feed($uid,$observer_hash,$arr) { unset($r[$x]); } } - + $parents_str = ids_to_querystr($r,'parent'); $sys_query = ((is_sys_channel($uid)) ? $sql_extra : ''); $item_normal = item_normal(); - $items = q("SELECT `item`.*, `item`.`id` AS `item_id` FROM `item` - WHERE `item`.`parent` IN ( %s ) $item_normal $sys_query ", + $items = q("SELECT item.*, item.id AS item_id FROM item + WHERE item.parent IN ( %s ) $item_normal $sys_query ", dbesc($parents_str) ); } @@ -3942,9 +3858,9 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C if($arr['mid']) $sql_options .= " and parent_mid = '" . dbesc($arr['mid']) . "' "; - + $sql_extra = " AND item.parent IN ( SELECT parent FROM item WHERE item_thread_top = 1 $sql_options $item_normal ) "; - + if($arr['since_id']) $sql_extra .= " and item.id > " . $since_id . " "; @@ -3952,7 +3868,7 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C $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", + $r = q("SELECT * FROM groups WHERE id = %d AND uid = %d LIMIT 1", intval($arr['group']), intval($uid) ); @@ -4056,8 +3972,8 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C $sql_nets .= "( abook.abook_closeness >= " . intval($arr['cmin']) . " "; $sql_nets .= " AND abook.abook_closeness <= " . intval($arr['cmax']) . " ) "; - /** @fixme dead code, $cmax is undefined */ - if ($cmax == 99) + + if ($arr['cmax'] == 99) $sql_nets .= " OR abook.abook_closeness IS NULL ) "; } } @@ -4192,25 +4108,21 @@ function webpage_to_namespace($webpage) { function update_remote_id($channel,$post_id,$webpage,$pagetitle,$namespace,$remote_id,$mid) { - $page_type = ''; - if(! $post_id) return; - - if($webpage == ITEM_TYPE_WEBPAGE) - $page_type = 'WEBPAGE'; - elseif($webpage == ITEM_TYPE_BLOCK) - $page_type = 'BUILDBLOCK'; - elseif($webpage == ITEM_TYPE_PDL) - $page_type = 'PDL'; - elseif($webpage == ITEM_TYPE_DOC) - $page_type = 'docfile'; - elseif($namespace && $remote_id) { + + $page_type = webpage_to_namespace($webpage); + + if($page_type == 'unknown' && $namespace && $remote_id) { $page_type = $namespace; $pagetitle = $remote_id; } + else { + $page_type = ''; + } if($page_type) { + // store page info as an alternate message_id so we can access it via // https://sitename/page/$channelname/$pagetitle // if no pagetitle was given or it couldn't be transliterated into a url, use the first @@ -4347,7 +4259,7 @@ function send_profile_photo_activity($channel,$photo,$profile) { $ptext = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo['resource_id'] . ']' . t('profile photo') . '[/zrl]'; - $ltext = '[zrl=' . z_root() . '/profile/' . $channel['channel_address'] . ']' . '[zmg=150x150]' . z_root() . '/photo/' . $photo['resource_id'] . '-4[/zmg][/zrl]'; + $ltext = '[zrl=' . z_root() . '/profile/' . $channel['channel_address'] . ']' . '[zmg=150x150]' . z_root() . '/photo/' . $photo['resource_id'] . '-4[/zmg][/zrl]'; $arr['body'] = sprintf($t,$channel['channel_name'],$ptext) . "\n\n" . $ltext; @@ -4389,11 +4301,11 @@ function sync_an_item($channel_id,$item_id) { function fix_attached_photo_permissions($uid,$xchan_hash,$body, $str_contact_allow,$str_group_allow,$str_contact_deny,$str_group_deny) { - + if(get_pconfig($uid,'system','force_public_uploads')) { $str_contact_allow = $str_group_allow = $str_contact_deny = $str_group_deny = ''; } - + $match = null; // match img and zmg image links if(preg_match_all("/\[[zi]mg(.*?)\](.*?)\[\/[zi]mg\]/",$body,$match)) { @@ -4410,7 +4322,7 @@ function fix_attached_photo_permissions($uid,$xchan_hash,$body, if(! strlen($image_uri)) continue; $srch = '<' . $xchan_hash . '>'; - + $r = q("select folder from attach where hash = '%s' and uid = %d limit 1", dbesc($image_uri), intval($uid) @@ -4427,15 +4339,15 @@ function fix_attached_photo_permissions($uid,$xchan_hash,$body, $str_group_deny = $f[0]['deny_gid']; } } - - $r = q("SELECT id FROM photo + + $r = q("SELECT id FROM photo WHERE allow_cid = '%s' AND allow_gid = '' AND deny_cid = '' AND deny_gid = '' AND resource_id = '%s' AND uid = %d LIMIT 1", dbesc($srch), dbesc($image_uri), intval($uid) ); - + if($r) { $r = q("UPDATE photo SET allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' WHERE resource_id = '%s' AND uid = %d ", @@ -4446,9 +4358,9 @@ function fix_attached_photo_permissions($uid,$xchan_hash,$body, dbesc($image_uri), intval($uid) ); - + // also update the linked item (which is probably invisible) - + $r = q("select id from item WHERE allow_cid = '%s' AND allow_gid = '' AND deny_cid = '' AND deny_gid = '' AND resource_id = '%s' and resource_type = 'photo' AND uid = %d LIMIT 1", @@ -4458,7 +4370,7 @@ function fix_attached_photo_permissions($uid,$xchan_hash,$body, ); if($r) { $private = (($str_contact_allow || $str_group_allow || $str_contact_deny || $str_group_deny) ? true : false); - + $r = q("UPDATE item SET allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', item_private = %d WHERE id = %d AND uid = %d", dbesc($str_contact_allow), @@ -4484,23 +4396,23 @@ function fix_attached_photo_permissions($uid,$xchan_hash,$body, intval($r[0]['id']), intval($uid) ); - } + } } } } } } - - + + function fix_attached_file_permissions($channel,$observer_hash,$body, $str_contact_allow,$str_group_allow,$str_contact_deny,$str_group_deny) { - + if(get_pconfig($channel['channel_id'],'system','force_public_uploads')) { $str_contact_allow = $str_group_allow = $str_contact_deny = $str_group_deny = ''; } - + $match = false; - + if(preg_match_all("/\[attachment\](.*?)\[\/attachment\]/",$body,$match)) { $attaches = $match[1]; if($attaches) { @@ -4519,3 +4431,75 @@ function fix_attached_file_permissions($channel,$observer_hash,$body, } } } + + +function item_create_edit_activity($post) { + + if((! $post) || (! $post['item']) || ($post['item']['item_type'] != ITEM_TYPE_POST)) + return; + + $update_item = $post['item']; + + $new_item = $update_item; + + $author = q("select * from xchan where xchan_hash = '%s' limit 1", + dbesc($new_item['author_xchan']) + ); + if($author) + $item_author = $author[0]; + + + $new_item['id'] = 0; + $new_item['parent'] = 0; + $new_item['mid'] = item_message_id(); + + $new_item['body'] = sprintf( t('[Edited %s]'), (($update_item['item_thread_top']) ? t('Post','edit_activity') : t('Comment','edit_activity'))); + + $new_item['body'] .= "\n\n"; + $new_item['body'] .= $update_item['body']; + + $new_item['sig'] = ''; + + $new_item['verb'] = ACTIVITY_UPDATE; + $new_item['item_thread_top'] = 0; + $new_item['created'] = $new_item['edited'] = datetime_convert(); + + $new_item['obj'] = json_encode(array( + 'type' => (($update_item['item_thread_top']) ? ACTIVITY_OBJ_NOTE : ACTIVITY_OBJ_COMMENT), + 'id' => $update_item['mid'], + 'parent' => $update_item['parent_mid'], + 'link' => array(array('rel' => 'alternate','type' => 'text/html', 'href' => $update_item['plink'])), + 'title' => $update_item['title'], + 'content' => $update_item['body'], + 'created' => $update_item['created'], + 'edited' => $update_item['edited'], + 'author' => array( + 'name' => $item_author['xchan_name'], + 'address' => $item_author['xchan_addr'], + 'guid' => $item_author['xchan_guid'], + 'guid_sig' => $item_author['xchan_guid_sig'], + 'link' => array( + array('rel' => 'alternate', 'type' => 'text/html', 'href' => $item_author['xchan_url']), + array('rel' => 'photo', 'type' => $item_author['xchan_photo_mimetype'], 'href' => $item_author['xchan_photo_m'])), + ), + )); + + + + $x = post_activity_item($new_item); + + $post_id = $x['id']; + if($post_id) { + $r = q("select * from item where id = %d", + intval($post_id) + ); + if($r) { + xchan_query($r); + $sync_item = fetch_post_tags($r); + build_sync_packet($new_item['uid'],array('item' => array(encode_item($sync_item[0],true)))); + } + } + + \Zotlabs\Daemon\Master::Summon(array('Notifier', 'edit_activity', $post_id)); + +} diff --git a/include/language.php b/include/language.php index 96d3e48a9..efe9397fb 100644 --- a/include/language.php +++ b/include/language.php @@ -327,3 +327,61 @@ function get_language_name($s, $l = null) { return $language->getName(); } + + + +function language_list() { + + $langs = glob('view/*/hstrings.php'); + + $lang_options = array(); + $selected = ""; + + if(is_array($langs) && count($langs)) { + if(! in_array('view/en/hstrings.php',$langs)) + $langs[] = 'view/en/'; + asort($langs); + foreach($langs as $l) { + $ll = substr($l,5); + $ll = substr($ll,0,strrpos($ll,'/')); + $lang_options[$ll] = get_language_name($ll, $ll) . " ($ll)"; + } + } + return $lang_options; +} + +function lang_selector() { + + $langs = glob('view/*/hstrings.php'); + + $lang_options = array(); + $selected = ""; + + if(is_array($langs) && count($langs)) { + $langs[] = ''; + if(! in_array('view/en/hstrings.php',$langs)) + $langs[] = 'view/en/'; + asort($langs); + foreach($langs as $l) { + if($l == '') { + $lang_options[""] = t('default'); + continue; + } + $ll = substr($l,5); + $ll = substr($ll,0,strrpos($ll,'/')); + $selected = (($ll === App::$language && (x($_SESSION, 'language'))) ? $ll : $selected); + $lang_options[$ll] = get_language_name($ll, $ll) . " ($ll)"; + } + } + + $tpl = get_markup_template('lang_selector.tpl'); + + $o = replace_macros($tpl, array( + '$title' => t('Select an alternate language'), + '$langs' => array($lang_options, $selected), + + )); + + return $o; +} + diff --git a/include/menu.php b/include/menu.php index 3b0180d37..b54ff7f9e 100644 --- a/include/menu.php +++ b/include/menu.php @@ -284,7 +284,7 @@ function menu_delete_id($menu_id, $uid) { intval($menu_id), intval($uid) ); - return q("delete from menu where menu_id = %d and menu_channel_id = %d limit 1", + return q("delete from menu where menu_id = %d and menu_channel_id = %d", intval($menu_id), intval($uid) ); diff --git a/include/message.php b/include/message.php index 748689206..7cbea3c6b 100644 --- a/include/message.php +++ b/include/message.php @@ -187,10 +187,10 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto=' if($body) $body = str_rot47(base64url_encode($body)); + $sig = ''; // placeholder - - $r = q("INSERT INTO mail ( account_id, conv_guid, mail_obscured, channel_id, from_xchan, to_xchan, title, body, attach, mid, parent_mid, created, expires, mail_isreply ) - VALUES ( %d, '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d )", + $r = q("INSERT INTO mail ( account_id, conv_guid, mail_obscured, channel_id, from_xchan, to_xchan, title, body, sig, attach, mid, parent_mid, created, expires, mail_isreply ) + VALUES ( %d, '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d )", intval($channel['channel_account_id']), dbesc($conv_guid), intval(1), @@ -199,6 +199,7 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto=' dbesc($recipient), dbesc($subject), dbesc($body), + dbesc($sig), dbesc($jattach), dbesc($mid), dbesc($replyto), @@ -392,7 +393,7 @@ function private_messages_fetch_message($channel_id, $messageitem_id, $updatesee if($updateseen) { - $r = q("UPDATE `mail` SET mail_seen = 1 where mail_seen = 0 and id = %d AND channel_id = %d", + $r = q("UPDATE mail SET mail_seen = 1 where mail_seen = 0 and id = %d AND channel_id = %d", dbesc($messageitem_id), intval($channel_id) ); @@ -436,7 +437,7 @@ function private_messages_drop($channel_id, $messageitem_id, $drop_conversation intval($channel_id) ); if($z) { - q("delete from conv where guid = '%s' and uid = %d limit 1", + q("delete from conv where guid = '%s' and uid = %d", dbesc($x[0]['conv_guid']), intval($channel_id) ); @@ -516,7 +517,7 @@ function private_messages_fetch_conversation($channel_id, $messageitem_id, $upda if($updateseen) { - $r = q("UPDATE `mail` SET mail_seen = 1 where mail_seen = 0 and parent_mid = '%s' AND channel_id = %d", + $r = q("UPDATE mail SET mail_seen = 1 where mail_seen = 0 and parent_mid = '%s' AND channel_id = %d", dbesc($r[0]['parent_mid']), intval($channel_id) ); diff --git a/include/nav.php b/include/nav.php index c2a058457..2762e2a14 100644 --- a/include/nav.php +++ b/include/nav.php @@ -108,7 +108,7 @@ EOT; if(feature_enabled($channel['channel_id'],'webpages') && (! $basic)) $nav['usermenu'][] = Array('webpages/' . $channel['channel_address'],t('Webpages'),"",t('Your webpages'),'webpages_nav_btn'); if(feature_enabled($channel['channel_id'],'wiki') && (! $basic)) - $nav['usermenu'][] = Array('wiki/' . $channel['channel_address'],t('Wiki'),"",t('Your wiki'),'wiki_nav_btn'); + $nav['usermenu'][] = Array('wiki/' . $channel['channel_address'],t('Wikis'),"",t('Your wikis'),'wiki_nav_btn'); } else { if(! get_account_id()) { @@ -127,26 +127,24 @@ EOT; ); } - if($observer) { - $nav['lock'] = array('logout','','lock', - sprintf( t('%s - click to logout'), $observer['xchan_addr'])); - } elseif(! $_SESSION['authenticated']) { $nav['loginmenu'][] = Array('rmagic',t('Remote authentication'),'',t('Click to authenticate to your home hub'),'rmagic_nav_btn'); } - /** - * "Home" should also take you home from an authenticated remote profile connection - */ - $homelink = get_my_url(); if(! $homelink) { $observer = App::get_observer(); $homelink = (($observer) ? $observer['xchan_url'] : ''); } - if(! local_channel()) - $nav['home'] = array($homelink, t('Home'), "", t('Home Page'),'home_nav_btn'); + if(! local_channel()) { + $nav['rusermenu'] = array( + $homelink, + t('Get me home'), + 'logout', + t('Log me out of this site') + ); + } if(((get_config('system','register_policy') == REGISTER_OPEN) || (get_config('system','register_policy') == REGISTER_APPROVE)) && (! $_SESSION['authenticated'])) $nav['register'] = array('register',t('Register'), "", t('Create an account'),'register_nav_btn'); diff --git a/include/network.php b/include/network.php index 7851f8976..451ce12a1 100644 --- a/include/network.php +++ b/include/network.php @@ -30,12 +30,12 @@ function get_capath() { * * \b filep => stream resource to write body to. header and body are not returned when using this option. * * \b custom => custom request method: e.g. 'PUT', 'DELETE' * * \b cookiejar => cookie file (write) - * * \B cookiefile => cookie file (read) + * * \b cookiefile => cookie file (read) * * @return array an associative array with: * * \e int \b return_code => HTTP return code or 0 if timeout or failure * * \e boolean \b success => boolean true (if HTTP 2xx result) or false - * * \e string \b header => HTTP headers + * * \e string \b header => HTTP headers * * \e string \b body => fetched content */ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { @@ -43,7 +43,7 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { $ret = array('return_code' => 0, 'success' => false, 'header' => "", 'body' => ""); $ch = @curl_init($url); - if(($redirects > 8) || (! $ch)) + if(($redirects > 8) || (! $ch)) return $ret; @curl_setopt($ch, CURLOPT_HEADER, true); @@ -59,12 +59,12 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { if(x($opts,'filep')) { @curl_setopt($ch, CURLOPT_FILE, $opts['filep']); - @curl_setopt($ch, CURLOPT_HEADER, $false); + @curl_setopt($ch, CURLOPT_HEADER, false); } if(x($opts,'upload')) @curl_setopt($ch, CURLOPT_UPLOAD, $opts['upload']); - + if(x($opts,'infile')) @curl_setopt($ch, CURLOPT_INFILE, $opts['infile']); @@ -87,7 +87,7 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { @curl_setopt($ch, CURLOPT_TIMEOUT, $opts['timeout']); } else { - $curl_time = intval(get_config('system','curl_timeout')); + $curl_time = intval(@get_config('system','curl_timeout')); @curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60)); } @@ -104,10 +104,10 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { if(x($opts,'cookie')) @curl_setopt($ch, CURLOPT_COOKIE, $opts['cookie']); - @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, + @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, ((x($opts,'novalidate') && intval($opts['novalidate'])) ? false : true)); - $prx = get_config('system','proxy'); + $prx = @get_config('system','proxy'); if(strlen($prx)) { @curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); @curl_setopt($ch, CURLOPT_PROXY, $prx); @@ -179,10 +179,10 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { * @param string $url * URL to post * @param mixed $params - * The full data to post in a HTTP "POST" operation. This parameter can - * either be passed as a urlencoded string like 'para1=val1¶2=val2&...' - * or as an array with the field name as key and field data as value. If value - * is an array, the Content-Type header will be set to multipart/form-data. + * The full data to post in a HTTP "POST" operation. This parameter can + * either be passed as a urlencoded string like 'para1=val1¶2=val2&...' + * or as an array with the field name as key and field data as value. If value + * is an array, the Content-Type header will be set to multipart/form-data. * @param int $redirects = 0 * internal use, recursion counter * @param array $opts (optional parameters) @@ -209,7 +209,7 @@ function z_post_url($url,$params, $redirects = 0, $opts = array()) { $ret = array('return_code' => 0, 'success' => false, 'header' => "", 'body' => ""); $ch = curl_init($url); - if(($redirects > 8) || (! $ch)) + if(($redirects > 8) || (! $ch)) return $ret; @curl_setopt($ch, CURLOPT_HEADER, true); @@ -226,13 +226,13 @@ function z_post_url($url,$params, $redirects = 0, $opts = array()) { if(x($opts,'filep')) { @curl_setopt($ch, CURLOPT_FILE, $opts['filep']); - @curl_setopt($ch, CURLOPT_HEADER, $false); + @curl_setopt($ch, CURLOPT_HEADER, false); } if(x($opts,'headers')) { @curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['headers']); } - + if(x($opts,'nobody')) @curl_setopt($ch, CURLOPT_NOBODY, $opts['nobody']); @@ -246,7 +246,7 @@ function z_post_url($url,$params, $redirects = 0, $opts = array()) { @curl_setopt($ch, CURLOPT_TIMEOUT, $opts['timeout']); } else { - $curl_time = intval(get_config('system','curl_timeout')); + $curl_time = intval(@get_config('system','curl_timeout')); @curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60)); } @@ -265,7 +265,7 @@ function z_post_url($url,$params, $redirects = 0, $opts = array()) { if(x($opts,'cookie')) @curl_setopt($ch, CURLOPT_COOKIE, $opts['cookie']); - @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, + @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, ((x($opts,'novalidate') && intval($opts['novalidate'])) ? false : true)); $prx = get_config('system','proxy'); @@ -379,8 +379,8 @@ function json_return_and_die($x, $content_type = 'application/json') { // Generic XML return -// Outputs a basic dfrn XML status structure to STDOUT, with a <status> variable -// of $st and an optional text <message> of $message and terminates the current process. +// Outputs a basic dfrn XML status structure to STDOUT, with a <status> variable +// of $st and an optional text <message> of $message and terminates the current process. function xml_status($st, $message = '') { @@ -399,7 +399,7 @@ function xml_status($st, $message = '') { /** - * @brief Send HTTP status header + * @brief Send HTTP status header * * @param int $val * integer HTTP status result value @@ -413,7 +413,7 @@ function http_status($val, $msg = '') { if ($val >= 200 && $val < 300) $msg = (($msg) ? $msg : 'OK'); - logger('http_status_exit ' . $val . ' ' . $msg); + logger('http_status_exit ' . $val . ' ' . $msg); header($_SERVER['SERVER_PROTOCOL'] . ' ' . $val . ' ' . $msg); } @@ -480,21 +480,46 @@ function convert_xml_element_to_array($xml_element, &$recursion_depth=0) { } } + + +function z_dns_check($h,$check_mx = 0) { + + // dns_get_record() has issues on some platforms + // so allow somebody to ignore it completely + // Use config values from memory as this can be called during setup + // before a database or even any config structure exists. + + if(is_array(\App::$config) && array_key_exists('system',\App::$config) + && is_array(\App::$config['system']) + && array_key_exists('do_not_check_dns',\App::$config['system']) + && \App::$config['system']['do_not_check_dns']) + return true; + + + //$opts = DNS_A + DNS_CNAME + DNS_PTR; + //if($check_mx) + // $opts += DNS_MX; + // Specific record type flags are unreliable on FreeBSD and Mac, + // so now we'll ignore these and just check for the existence of any DNS record. + return((@dns_get_record($h) || filter_var($h, FILTER_VALIDATE_IP)) ? true : false); + +} + // Take a URL from the wild, prepend http:// if necessary // and check DNS to see if it's real (or check if is a valid IP address) // return true if it's OK, false if something is wrong with it function validate_url(&$url) { - + // no naked subdomains (allow localhost for tests) if(strpos($url,'.') === false && strpos($url,'/localhost/') === false) return false; if(substr($url,0,4) != 'http') $url = 'http://' . $url; $h = @parse_url($url); - - if(($h) && (@dns_get_record($h['host'], DNS_A + DNS_CNAME + DNS_PTR) || filter_var($h['host'], FILTER_VALIDATE_IP) )) { + + if(($h) && z_dns_check($h['host'])) { return true; } return false; @@ -512,7 +537,7 @@ function validate_email($addr) { return false; $h = substr($addr,strpos($addr,'@') + 1); - if(($h) && (@dns_get_record($h, DNS_A + DNS_CNAME + DNS_PTR + DNS_MX) || filter_var($h, FILTER_VALIDATE_IP) )) { + if(($h) && z_dns_check($h,true)) { return true; } return false; @@ -552,7 +577,7 @@ function allowed_url($url) { foreach($allowed as $a) { $pat = strtolower(trim($a)); if(($fnmatch && fnmatch($pat,$host)) || ($pat == $host)) { - $found = true; + $found = true; break; } } @@ -575,14 +600,14 @@ function allowed_email($email) { $str_allowed = get_config('system','allowed_email'); $str_not_allowed = get_config('system','not_allowed_email'); - + if(! $str_allowed && ! $str_not_allowed) return true; $return = false; - $found_allowed = false; + $found_allowed = false; $found_not_allowed = false; - + $fnmatch = function_exists('fnmatch'); $allowed = explode(',',$str_allowed); @@ -591,7 +616,7 @@ function allowed_email($email) { foreach($allowed as $a) { $pat = strtolower(trim($a)); if(($fnmatch && fnmatch($pat,$email)) || ($pat == $domain)) { - $found_allowed = true; + $found_allowed = true; break; } } @@ -603,16 +628,16 @@ function allowed_email($email) { foreach($not_allowed as $na) { $pat = strtolower(trim($na)); if(($fnmatch && fnmatch($pat,$email)) || ($pat == $domain)) { - $found_not_allowed = true; + $found_not_allowed = true; break; } } - } - + } + if ($found_allowed) { - $return = true; + $return = true; } elseif (!$str_allowed && !$found_not_allowed) { - $return = true; + $return = true; } return $return; } @@ -652,7 +677,7 @@ function scale_external_images($s, $include_link = true, $scale_replace = false) foreach($matches as $mtch) { logger('scale_external_image: ' . $mtch[2] . ' ' . $mtch[3]); - + if(substr($mtch[1],0,1) == '=') { $owidth = intval(substr($mtch[2],1)); if(intval($owidth) > 0 && intval($owidth) < 1024) @@ -686,7 +711,7 @@ function scale_external_images($s, $include_link = true, $scale_replace = false) $type = guess_image_type($mtch[3],$i['header']); if(strpos($type,'image') === false) continue; - + if($i['success']) { $ph = photo_factory($i['body'], $type); if($ph->is_valid()) { @@ -700,7 +725,7 @@ function scale_external_images($s, $include_link = true, $scale_replace = false) $new_height = $ph->getHeight(); logger('scale_external_images: ' . $orig_width . '->' . $new_width . 'w ' . $orig_height . '->' . $new_height . 'h' . ' match: ' . $mtch[0], LOGGER_DEBUG); $s = str_replace($mtch[0],'[' . $tag . '=' . $new_width . 'x' . $new_height. ']' . $scaled . '[/' . $tag . ']' - . "\n" . (($include_link) + . "\n" . (($include_link) ? '[zrl=' . $mtch[2] . ']' . t('view full size') . '[/zrl]' . "\n" : ''),$s); logger('scale_external_images: new string: ' . $s, LOGGER_DEBUG); @@ -728,7 +753,7 @@ function scale_external_images($s, $include_link = true, $scale_replace = false) * Return: The parsed XML in an array form. Use print_r() to see the resulting array structure. * Examples: $array = xml2array(file_get_contents('feed.xml')); * $array = xml2array(file_get_contents('feed.xml', true, 1, 'attribute')); - */ + */ function xml2array($contents, $namespaces = true, $get_attributes=1, $priority = 'attribute') { if(!$contents) return array(); @@ -752,7 +777,7 @@ function xml2array($contents, $namespaces = true, $get_attributes=1, $priority = return array(); } - xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8"); + xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8"); // http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); @@ -786,7 +811,7 @@ function xml2array($contents, $namespaces = true, $get_attributes=1, $priority = $result = array(); $attributes_data = array(); - + if(isset($value)) { if($priority == 'tag') $result = $value; else $result['value'] = $value; // Put the value in a assoc array if we are in the 'Attribute' mode @@ -802,7 +827,7 @@ function xml2array($contents, $namespaces = true, $get_attributes=1, $priority = // See tag status and do the needed. if($namespaces && strpos($tag,':')) { - $namespc = substr($tag,0,strrpos($tag,':')); + $namespc = substr($tag,0,strrpos($tag,':')); $tag = strtolower(substr($tag,strlen($namespc)+1)); $result['@namespace'] = $namespc; } @@ -825,7 +850,7 @@ function xml2array($contents, $namespaces = true, $get_attributes=1, $priority = } else { // This section will make the value an array if multiple tags with the same name appear together $current[$tag] = array($current[$tag],$result); // This will combine the existing item and the new item together to make an array $repeated_tag_index[$tag.'_'.$level] = 2; - + if(isset($current[$tag.'_attr'])) { // The attribute of the last(0th) tag must be moved as well $current[$tag]['0_attr'] = $current[$tag.'_attr']; unset($current[$tag.'_attr']); @@ -848,7 +873,7 @@ function xml2array($contents, $namespaces = true, $get_attributes=1, $priority = // ...push the new element into that array. $current[$tag][$repeated_tag_index[$tag.'_'.$level]] = $result; - + if($priority == 'tag' and $get_attributes and $attributes_data) { $current[$tag][$repeated_tag_index[$tag.'_'.$level] . '_attr'] = $attributes_data; } @@ -859,11 +884,11 @@ function xml2array($contents, $namespaces = true, $get_attributes=1, $priority = $repeated_tag_index[$tag.'_'.$level] = 1; if($priority == 'tag' and $get_attributes) { if(isset($current[$tag.'_attr'])) { // The attribute of the last(0th) tag must be moved as well - + $current[$tag]['0_attr'] = $current[$tag.'_attr']; unset($current[$tag.'_attr']); } - + if($attributes_data) { $current[$tag][$repeated_tag_index[$tag.'_'.$level] . '_attr'] = $attributes_data; } @@ -876,9 +901,9 @@ function xml2array($contents, $namespaces = true, $get_attributes=1, $priority = $current = &$parent[$level-1]; } } - + return($xml_array); -} +} function email_header_encode($in_str, $charset = 'UTF-8') { @@ -1128,7 +1153,7 @@ function discover_by_webbie($webbie) { $diaspora = false; $gnusoc = false; $dfrn = false; - + $has_salmon = false; $salmon_key = false; $atom_feed = false; @@ -1144,7 +1169,7 @@ function discover_by_webbie($webbie) { if(array_key_exists('rel',$link)) { // If we discover zot - don't search further; grab the info and get out of - // here. + // here. if($link['rel'] === PROTOCOL_ZOT) { logger('discover_by_webbie: zot found for ' . $webbie, LOGGER_DEBUG); @@ -1204,11 +1229,11 @@ function discover_by_webbie($webbie) { $pubkey = ''; if(is_array($x)) { - if(array_key_exists('address',$x)) + if(array_key_exists('address',$x)) $address = $x['address']; - if(array_key_exists('location',$x)) + if(array_key_exists('location',$x)) $location = $x['location']; - if(array_key_exists('nickname',$x)) + if(array_key_exists('nickname',$x)) $nickname = $x['nickname']; } @@ -1216,16 +1241,16 @@ function discover_by_webbie($webbie) { $probe_old = true; - if((! $dfrn) && (! $has_salmon)) + if((! $dfrn) && (! $has_salmon)) $probe_old = true; if($probe_old) { - $y = old_webfinger($webbie); + $y = old_webfinger($webbie); if($y) { logger('old_webfinger: ' . print_r($x,true)); foreach($y as $link) { if($link['@attributes']['rel'] === NAMESPACE_DFRN) - $dfrn = unamp($link['@attributes']['href']); + $dfrn = unamp($link['@attributes']['href']); if($link['@attributes']['rel'] === 'salmon') $notify = unamp($link['@attributes']['href']); if($link['@attributes']['rel'] === NAMESPACE_FEED) @@ -1344,7 +1369,7 @@ function discover_by_webbie($webbie) { if($vcard['fn']) $fullname = $vcard['fn']; if($vcard['photo'] && (strpos($vcard['photo'],'http') !== 0)) - $vcard['photo'] = $diaspora_base . '/' . $vcard['photo']; + $vcard['photo'] = $diaspora_base . '/' . $vcard['photo']; if(($vcard['public_key']) && (! $pubkey)) { $diaspora_key = $vcard['public_key']; if(strstr($diaspora_key,'RSA ')) @@ -1358,7 +1383,7 @@ function discover_by_webbie($webbie) { if(($vcard['uid']) && (! $diaspora_guid)) $diaspora_guid = $vcard['uid']; if(($vcard['url']) && (! $diaspora_base)) - $diaspora_base = $vcard['url']; + $diaspora_base = $vcard['url']; @@ -1372,7 +1397,7 @@ function discover_by_webbie($webbie) { if(($profile) && (! $location)) $location = $profile; - if($location) { + if($location) { $m = parse_url($location); $base = $m['scheme'] . '://' . $m['host']; $host = $m['host']; @@ -1407,12 +1432,12 @@ function discover_by_webbie($webbie) { // if we have everything we need, let's create the records - if($network && $address && $fullname && $pubkey && $location) { + if($network && $address && $fullname && $pubkey && $location) { $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($address) ); if($r) { - $r = q("update xchan set xchan_name = '%s', xchan_network = '%s', xchan_name_date = '%s' where xchan_hash = '%s' limit 1", + $r = q("update xchan set xchan_name = '%s', xchan_network = '%s', xchan_name_date = '%s' where xchan_hash = '%s'", dbesc($fullname), dbesc($network), dbesc(datetime_convert()), @@ -1493,7 +1518,7 @@ function webfinger_rfc7033($webbie,$zot = false) { // We could have a number of URL aliases and webbies // make an executive decision about the most likely "best" of each // by comparing against some examples from known networks we're likely to encounter. - // Otherwise we have to store every alias that we may ever encounter and + // Otherwise we have to store every alias that we may ever encounter and // validate every URL we ever find against every possible alias // @fixme pump.io is going to be a real bugger since it doesn't return subject or aliases @@ -1564,7 +1589,7 @@ function match_webfinger_location($s,$h) { return $s; return ''; } - + @@ -1935,7 +1960,7 @@ function format_and_send_email($sender,$xchan,$item) { )); $sender_name = t('Administrator'); - + $hostname = App::get_hostname(); if(strpos($hostname,':')) $hostname = substr($hostname,0,strpos($hostname,':')); @@ -1964,7 +1989,7 @@ function do_delivery($deliveries) { if(! (is_array($deliveries) && count($deliveries))) return; - $interval = ((get_config('system','delivery_interval') !== false) + $interval = ((get_config('system','delivery_interval') !== false) ? intval(get_config('system','delivery_interval')) : 2 ); $deliveries_per_process = intval(get_config('system','delivery_batch_count')); @@ -1993,7 +2018,7 @@ function do_delivery($deliveries) { if($deliver) Zotlabs\Daemon\Master::Summon(array('Deliver',$deliver)); - + } @@ -2002,7 +2027,7 @@ function get_site_info() { $register_policy = Array('REGISTER_CLOSED', 'REGISTER_APPROVE', 'REGISTER_OPEN'); $directory_mode = Array('DIRECTORY_MODE_NORMAL', 'DIRECTORY_MODE_PRIMARY', 'DIRECTORY_MODE_SECONDARY', 256 => 'DIRECTORY_MODE_STANDALONE'); - + $sql_extra = ''; $r = q("select * from channel left join account on account_id = channel_account_id where ( account_roles & 4096 )>0 and account_default_channel = channel_id"); @@ -2053,7 +2078,7 @@ function get_site_info() { 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')); @@ -2073,7 +2098,7 @@ function get_site_info() { } - + $data = Array( 'version' => $version, 'version_tag' => $tag, @@ -2190,7 +2215,7 @@ function deliverable_singleton($channel_id,$xchan) { function get_repository_version($branch = 'master') { $path = "https://raw.githubusercontent.com/redmatrix/hubzilla/$branch/boot.php"; - + $x = z_fetch_url($path); if($x['success']) { $y = preg_match('/define(.*?)STD_VERSION(.*?)([0-9.].*)\'/',$x['body'],$matches); @@ -2199,7 +2224,7 @@ function get_repository_version($branch = 'master') { } return '?.?'; -} +} function network_to_name($s) { @@ -2266,11 +2291,11 @@ function z_mail($params) { call_hooks('email_send', $params); if($params['sent']) { - logger('notification: z_mail returns ' . $params['result'], LOGGER_DEBUG); + logger('notification: z_mail returns ' . (($params['result']) ? 'success' : 'failure'), LOGGER_DEBUG); return $params['result']; } - $fromName = email_header_encode(html_entity_decode($params['fromName'],ENT_QUOTES,'UTF-8'),'UTF-8'); + $fromName = email_header_encode(html_entity_decode($params['fromName'],ENT_QUOTES,'UTF-8'),'UTF-8'); $messageSubject = email_header_encode(html_entity_decode($params['messageSubject'],ENT_QUOTES,'UTF-8'),'UTF-8'); $messageHeader = @@ -2285,6 +2310,25 @@ function z_mail($params) { $params['textVersion'], $messageHeader // message headers ); - logger('notification: z_mail returns ' . $res, LOGGER_DEBUG); + logger('notification: z_mail returns ' . (($res) ? 'success' : 'failure'), LOGGER_DEBUG); return $res; } + +// discover the best API path available for redmatrix/hubzilla servers + +function probe_api_path($host) { + + $schemes = ['https', 'http' ]; + $paths = ['/api/z/1.0/version', '/api/red/version' ]; + + foreach($schemes as $scheme) { + foreach($paths as $path) { + $curpath = $scheme . '://' . $host . $path; + $x = z_fetch_url($curpath); + if($x['success'] && ! strlen($x['body'],'not implemented')) + return str_replace('version','',$curpath); + } + } + + return ''; +} diff --git a/include/oembed.php b/include/oembed.php index 1780e7046..eb7b76437 100755 --- a/include/oembed.php +++ b/include/oembed.php @@ -137,10 +137,6 @@ function oembed_fetch_url($embedurl){ if($action !== 'block') { $txt = Zlib\Cache::get('[' . App::$videowidth . '] ' . $embedurl); - - if(strstr($txt,'youtu') && strstr(z_root(),'https:')) { - $txt = str_replace('http:','https:',$txt); - } } if(is_null($txt)) { @@ -272,13 +268,7 @@ function oembed_format_object($j){ $th=120; $tw = $th*$tr; $tpl=get_markup_template('oembed_video.tpl'); - if(strstr($embedurl,'youtu') && strstr(z_root(),'https:')) { - $embedurl = str_replace('http:','https:',$embedurl); - $j['thumbnail_url'] = str_replace('http:','https:', $j['thumbnail_url']); - $jhtml = str_replace('http:','https:', $jhtml); - $j['html'] = str_replace('http:','https:', $j['html']); - - } + $ret.=replace_macros($tpl, array( '$baseurl' => z_root(), '$embedurl'=>$embedurl, @@ -348,7 +338,7 @@ function oembed_iframe($src,$width,$height) { // Make sure any children are sandboxed within their own iframe. - return '<iframe ' . $scroll . 'height="' . $height . '" width="' . $width . '" src="' . $s . '" frameborder="no" >' + return '<iframe ' . $scroll . 'height="' . $height . '" width="' . $width . '" src="' . $s . '" allowfullscreen frameborder="no" >' . t('Embedded content') . '</iframe>'; } diff --git a/include/photo/photo_driver.php b/include/photo/photo_driver.php index 9b6d38cc1..af4fd0a30 100644 --- a/include/photo/photo_driver.php +++ b/include/photo/photo_driver.php @@ -340,31 +340,31 @@ abstract class photo_driver { intval($p['imgscale']) ); if($x) { - $r = q("UPDATE `photo` set - `aid` = %d, - `uid` = %d, - `xchan` = '%s', - `resource_id` = '%s', - `created` = '%s', - `edited` = '%s', - `filename` = '%s', - `mimetype` = '%s', - `album` = '%s', - `height` = %d, - `width` = %d, - `content` = '%s', - `os_storage` = %d, - `filesize` = %d, - `imgscale` = %d, - `photo_usage` = %d, - `title` = '%s', - `description` = '%s', - `os_path` = '%s', - `display_path` = '%s', - `allow_cid` = '%s', - `allow_gid` = '%s', - `deny_cid` = '%s', - `deny_gid` = '%s' + $r = q("UPDATE photo set + aid = %d, + uid = %d, + xchan = '%s', + resource_id = '%s', + created = '%s', + edited = '%s', + filename = '%s', + mimetype = '%s', + album = '%s', + height = %d, + width = %d, + content = '%s', + os_storage = %d, + filesize = %d, + imgscale = %d, + photo_usage = %d, + title = '%s', + description = '%s', + os_path = '%s', + display_path = '%s', + allow_cid = '%s', + allow_gid = '%s', + deny_cid = '%s', + deny_gid = '%s' where id = %d", intval($p['aid']), @@ -378,7 +378,7 @@ abstract class photo_driver { dbesc($p['album']), intval($this->getHeight()), intval($this->getWidth()), - (intval($p['os_storage']) ? dbesc($p['os_path']) : dbescbin($this->imageString())), + (intval($p['os_storage']) ? dbescbin($p['os_path']) : dbescbin($this->imageString())), intval($p['os_storage']), intval(strlen($this->imageString())), intval($p['imgscale']), @@ -395,8 +395,8 @@ abstract class photo_driver { ); } else { - $r = q("INSERT INTO `photo` - ( `aid`, `uid`, `xchan`, `resource_id`, `created`, `edited`, `filename`, mimetype, `album`, `height`, `width`, `content`, `os_storage`, `filesize`, `imgscale`, `photo_usage`, `title`, `description`, `os_path`, `display_path`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid` ) + $r = q("INSERT INTO photo + ( aid, uid, xchan, resource_id, created, edited, filename, mimetype, album, height, width, content, os_storage, filesize, imgscale, photo_usage, title, description, os_path, display_path, allow_cid, allow_gid, deny_cid, deny_gid ) VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )", intval($p['aid']), intval($p['uid']), @@ -409,7 +409,7 @@ abstract class photo_driver { dbesc($p['album']), intval($this->getHeight()), intval($this->getWidth()), - (intval($p['os_storage']) ? dbesc($p['os_path']) : dbescbin($this->imageString())), + (intval($p['os_storage']) ? dbescbin($p['os_path']) : dbescbin($this->imageString())), intval($p['os_storage']), intval(strlen($this->imageString())), intval($p['imgscale']), @@ -432,33 +432,33 @@ abstract class photo_driver { public function store($aid, $uid, $xchan, $rid, $filename, $album, $scale, $usage = PHOTO_NORMAL, $allow_cid = '', $allow_gid = '', $deny_cid = '', $deny_gid = '') { - $x = q("select id from photo where `resource_id` = '%s' and uid = %d and `xchan` = '%s' and `imgscale` = %d limit 1", + $x = q("select id from photo where resource_id = '%s' and uid = %d and xchan = '%s' and imgscale = %d limit 1", dbesc($rid), intval($uid), dbesc($xchan), intval($scale) ); if(count($x)) { - $r = q("UPDATE `photo` - set `aid` = %d, - `uid` = %d, - `xchan` = '%s', - `resource_id` = '%s', - `created` = '%s', - `edited` = '%s', - `filename` = '%s', - `mimetype` = '%s', - `album` = '%s', - `height` = %d, - `width` = %d, - `content` = '%s', - `filesize` = %d, - `imgscale` = %d, - `photo_usage` = %d, - `allow_cid` = '%s', - `allow_gid` = '%s', - `deny_cid` = '%s', - `deny_gid` = '%s' + $r = q("UPDATE photo + set aid = %d, + uid = %d, + xchan = '%s', + resource_id = '%s', + created = '%s', + edited = '%s', + filename = '%s', + mimetype = '%s', + album = '%s', + height = %d, + width = %d, + content = '%s', + filesize = %d, + imgscale = %d, + photo_usage = %d, + allow_cid = '%s', + allow_gid = '%s', + deny_cid = '%s', + deny_gid = '%s' where id = %d", intval($aid), @@ -484,8 +484,8 @@ abstract class photo_driver { ); } else { - $r = q("INSERT INTO `photo` - ( `aid`, `uid`, `xchan`, `resource_id`, `created`, `edited`, `filename`, mimetype, `album`, `height`, `width`, `content`, `filesize`, `imgscale`, `photo_usage`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid` ) + $r = q("INSERT INTO photo + ( aid, uid, xchan, resource_id, created, edited, filename, mimetype, album, height, width, content, filesize, imgscale, photo_usage, allow_cid, allow_gid, deny_cid, deny_gid ) VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, '%s', '%s', '%s', '%s' )", intval($aid), intval($uid), diff --git a/include/photos.php b/include/photos.php index a3018816c..5e4d755e3 100644 --- a/include/photos.php +++ b/include/photos.php @@ -28,7 +28,8 @@ function photo_upload($channel, $observer, $args) { return $ret; } -// call_hooks('photo_upload_begin', $args); + +//call_hooks('photo_upload_begin', $args); /* * Determine the album to use @@ -45,7 +46,7 @@ function photo_upload($channel, $observer, $args) { if(array_key_exists('deliver',$args)) $deliver = intval($args['deliver']); - // Set to default channel permissions. If the parent directory (album) has permissions set, + // Set to default channel permissions. If the parent directory (album) has permissions set, // use those instead. If we have specific permissions supplied, they take precedence over // all other settings. 'allow_cid' being passed from an external source takes priority over channel settings. // ...messy... needs re-factoring once the photos/files integration stabilises @@ -55,9 +56,9 @@ function photo_upload($channel, $observer, $args) { $acl->set($args['directory']); if(array_key_exists('allow_cid',$args)) $acl->set($args); - if( (array_key_exists('group_allow',$args)) - || (array_key_exists('contact_allow',$args)) - || (array_key_exists('group_deny',$args)) + if( (array_key_exists('group_allow',$args)) + || (array_key_exists('contact_allow',$args)) + || (array_key_exists('group_deny',$args)) || (array_key_exists('contact_deny',$args))) { $acl->set_from_array($args); } @@ -103,7 +104,7 @@ function photo_upload($channel, $observer, $args) { $type = $_FILES['userfile']['type']; } - if (! $type) + if (! $type) $type=guess_image_type($filename); logger('photo_upload: received file: ' . $filename . ' as ' . $src . ' ('. $type . ') ' . $filesize . ' bytes', LOGGER_DEBUG); @@ -176,7 +177,7 @@ function photo_upload($channel, $observer, $args) { $errors = false; $p = array('aid' => $account_id, 'uid' => $channel_id, 'xchan' => $visitor, 'resource_id' => $photo_hash, - 'filename' => $filename, 'album' => $album, 'imgscale' => 0, 'photo_usage' => PHOTO_NORMAL, + 'filename' => $filename, 'album' => $album, 'imgscale' => 0, 'photo_usage' => PHOTO_NORMAL, 'allow_cid' => $ac['allow_cid'], 'allow_gid' => $ac['allow_gid'], 'deny_cid' => $ac['deny_cid'], 'deny_gid' => $ac['deny_gid'], 'os_storage' => $os_storage, 'os_path' => $args['os_path'] @@ -220,8 +221,8 @@ function photo_upload($channel, $observer, $args) { ); if(! $r1) $errors = true; - - if(($width > 640 || $height > 640) && (! $errors)) + + if(($width > 640 || $height > 640) && (! $errors)) $ph->scaleImage(640); $p['imgscale'] = 2; @@ -236,7 +237,7 @@ function photo_upload($channel, $observer, $args) { if(! $r2) $errors = true; - if(($width > 320 || $height > 320) && (! $errors)) + if(($width > 320 || $height > 320) && (! $errors)) $ph->scaleImage(320); $p['imgscale'] = 3; @@ -304,8 +305,8 @@ function photo_upload($channel, $observer, $args) { $summary = (($args['body']) ? $args['body'] : '') . '[footer]' . $activity_format . '[/footer]'; - $obj_body = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo_hash . ']' - . $tag . z_root() . "/photo/{$photo_hash}-{$scale}." . $ph->getExt() . '[/zmg]' + $obj_body = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo_hash . ']' + . $tag . z_root() . "/photo/{$photo_hash}-{$scale}." . $ph->getExt() . '[/zmg]' . '[/zrl]'; // Create item object @@ -361,7 +362,7 @@ function photo_upload($channel, $observer, $args) { $item['uid'] = $channel['channel_id']; item_store_update($item,false,$deliver); continue; - } + } } else { $item['aid'] = $channel['channel_account_id']; @@ -381,7 +382,7 @@ function photo_upload($channel, $observer, $args) { $arr['aid'] = $account_id; $arr['uid'] = $channel_id; $arr['mid'] = $mid; - $arr['parent_mid'] = $mid; + $arr['parent_mid'] = $mid; $arr['item_hidden'] = $item_hidden; $arr['resource_type'] = 'photo'; $arr['resource_id'] = $photo_hash; @@ -407,10 +408,10 @@ function photo_upload($channel, $observer, $args) { // this one is tricky because the item and the photo have the same permissions, those of the photo. // Use the channel read_stream permissions to get the correct public_policy for the item and recalculate the - // private flag accordingly. This may cause subtle bugs due to custom permissions roles. We want to use + // private flag accordingly. This may cause subtle bugs due to custom permissions roles. We want to use // public policy when federating items to other sites, but should probably ignore them when accessing the item // in the photos pages - using the photos permissions instead. We need the public policy to keep the photo - // linked item from leaking into the feed when somebody has a channel with read_stream restrictions. + // linked item from leaking into the feed when somebody has a channel with read_stream restrictions. $arr['public_policy'] = map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_stream'),true); if($arr['public_policy']) @@ -421,7 +422,7 @@ function photo_upload($channel, $observer, $args) { $result = item_store($arr,false,$deliver); $item_id = $result['item_id']; - if($visible && $deliver) + if($visible && $deliver) Zotlabs\Daemon\Master::Summon(array('Notifier', 'wall-new', $item_id)); } @@ -443,9 +444,12 @@ function photo_upload($channel, $observer, $args) { * * @param array $channel * @param array $observer + * @param array $sort_key (optional) default album + * @param array $direction (optional) default asc + * * @return bool|array false if no view_storage permission or an array - * * success (bool) - * * albums (array) + * * \e boolean \b success + * * \e array \b albums */ function photos_albums_list($channel, $observer, $sort_key = 'album', $direction = 'asc') { @@ -455,14 +459,11 @@ function photos_albums_list($channel, $observer, $sort_key = 'album', $direction if(! perm_is_allowed($channel_id, $observer_xchan, 'view_storage')) return false; - $sql_extra = permissions_sql($channel_id,$observer_xchan); $sort_key = dbesc($sort_key); $direction = dbesc($direction); - - $albums = q("SELECT count( distinct resource_id ) as total, album from photo where uid = %d and photo_usage IN ( %d, %d ) $sql_extra group by album order by $sort_key $direction", intval($channel_id), intval(PHOTO_NORMAL), @@ -479,8 +480,8 @@ function photos_albums_list($channel, $observer, $sort_key = 'album', $direction foreach($albums as $k => $album) { $entry = array( 'text' => (($album['album']) ? $album['album'] : '/'), - 'total' => $album['total'], - 'url' => z_root() . '/photos/' . $channel['channel_address'] . '/album/' . bin2hex($album['album']), + 'total' => $album['total'], + 'url' => z_root() . '/photos/' . $channel['channel_address'] . '/album/' . bin2hex($album['album']), 'urlencode' => urlencode($album['album']), 'bin2hex' => bin2hex($album['album']) ); @@ -506,7 +507,7 @@ function photos_album_widget($channelx,$observer,$sortkey = 'album',$direction = '$title' => t('Photo Albums'), '$albums' => $albums['albums'], '$baseurl' => z_root(), - '$upload' => ((perm_is_allowed($channelx['channel_id'],(($observer) ? $observer['xchan_hash'] : ''),'write_storage')) + '$upload' => ((perm_is_allowed($channelx['channel_id'],(($observer) ? $observer['xchan_hash'] : ''),'write_storage')) ? t('Upload New Photos') : '') )); } @@ -533,7 +534,7 @@ function photos_list_photos($channel, $observer, $album = '') { $sql_extra = permissions_sql($channel_id); if($album) - $sql_extra .= " and album = '" . protect_sprintf(dbesc($album)) . "' "; + $sql_extra .= " and album = '" . protect_sprintf(dbesc($album)) . "' "; $ret = array('success' => false); @@ -647,7 +648,7 @@ function photos_create_item($channel, $creator_hash, $photo, $visible = false) { $arr['aid'] = $channel['channel_account_id']; $arr['uid'] = $channel['channel_id']; $arr['mid'] = $mid; - $arr['parent_mid'] = $mid; + $arr['parent_mid'] = $mid; $arr['item_wall'] = 1; $arr['item_origin'] = 1; $arr['item_thread_top'] = 1; @@ -663,9 +664,9 @@ function photos_create_item($channel, $creator_hash, $photo, $visible = false) { $arr['deny_gid'] = $photo['deny_gid']; $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . $arr['mid']; - - $arr['body'] = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo['resource_id'] . ']' - . '[zmg]' . z_root() . '/photo/' . $photo['resource_id'] . '-' . $photo['imgscale'] . '[/zmg]' + + $arr['body'] = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo['resource_id'] . ']' + . '[zmg]' . z_root() . '/photo/' . $photo['resource_id'] . '-' . $photo['imgscale'] . '[/zmg]' . '[/zrl]'; $result = item_store($arr); @@ -710,39 +711,39 @@ function gps2Num($coordPart) { } function profile_photo_set_profile_perms($uid, $profileid = 0) { - + $allowcid = ''; if($profileid) { $r = q("SELECT photo, profile_guid, id, is_default, uid - FROM profile WHERE uid = %d and ( profile.id = %d OR profile.profile_guid = '%s') LIMIT 1", + FROM profile WHERE uid = %d and ( profile.id = %d OR profile.profile_guid = '%s') LIMIT 1", intval($uid), - intval($profileid), + intval($profileid), dbesc($profileid) ); - } + } else { logger('Resetting permissions on default-profile-photo for user'.local_channel()); - $r = q("SELECT photo, profile_guid, id, is_default, uid FROM profile - WHERE profile.uid = %d AND is_default = 1 LIMIT 1", - intval($uid) + $r = q("SELECT photo, profile_guid, id, is_default, uid FROM profile + WHERE profile.uid = %d AND is_default = 1 LIMIT 1", + intval($uid) ); //If no profile is given, we update the default profile } if(! $r) return; - + $profile = $r[0]; - if($profile['id'] && $profile['photo']) { + if($profile['id'] && $profile['photo']) { preg_match("@\w*(?=-\d*$)@i", $profile['photo'], $resource_id); $resource_id = $resource_id[0]; - + if (! intval($profile['is_default'])) { - $r0 = q("SELECT channel_hash FROM channel WHERE channel_id = %d LIMIT 1", - intval($uid) + $r0 = q("SELECT channel_hash FROM channel WHERE channel_id = %d LIMIT 1", + intval($uid) ); //Should not be needed in future. Catches old int-profile-ids. - $r1 = q("SELECT abook.abook_xchan FROM abook WHERE abook_profile = '%d' ", + $r1 = q("SELECT abook.abook_xchan FROM abook WHERE abook_profile = '%d' ", intval($profile['id']) ); $r2 = q("SELECT abook.abook_xchan FROM abook WHERE abook_profile = '%s'", @@ -750,27 +751,27 @@ function profile_photo_set_profile_perms($uid, $profileid = 0) { ); $allowcid = "<" . $r0[0]['channel_hash'] . ">"; foreach ($r1 as $entry) { - $allowcid .= "<" . $entry['abook_xchan'] . ">"; + $allowcid .= "<" . $entry['abook_xchan'] . ">"; } foreach ($r2 as $entry) { $allowcid .= "<" . $entry['abook_xchan'] . ">"; } - + q("UPDATE photo SET allow_cid = '%s' WHERE resource_id = '%s' AND uid = %d", dbesc($allowcid), dbesc($resource_id), intval($uid) ); - - } + + } else { //Reset permissions on default profile picture to public q("UPDATE photo SET allow_cid = '' WHERE photo_usage = %d AND uid = %d", intval(PHOTO_PROFILE), intval($uid) - ); + ); } } - + return; } diff --git a/include/plugin.php b/include/plugin.php index 663d17959..80c303b42 100755 --- a/include/plugin.php +++ b/include/plugin.php @@ -41,7 +41,7 @@ function uninstall_plugin($plugin) { $func(); } - q("DELETE FROM `addon` WHERE `aname` = '%s' ", + q("DELETE FROM addon WHERE aname = '%s' ", dbesc($plugin) ); } @@ -66,7 +66,7 @@ function install_plugin($plugin) { $plugin_admin = (function_exists($plugin . '_plugin_admin') ? 1 : 0); - q("INSERT INTO `addon` (`aname`, `installed`, `tstamp`, `plugin_admin`) VALUES ( '%s', 1, %d , %d ) ", + q("INSERT INTO addon (aname, installed, tstamp, plugin_admin) VALUES ( '%s', 1, %d , %d ) ", dbesc($plugin), intval($t), $plugin_admin @@ -126,7 +126,7 @@ function plugin_is_installed($name) { function reload_plugins() { $plugins = get_config('system', 'addon'); if(strlen($plugins)) { - $r = q("SELECT * FROM `addon` WHERE `installed` = 1"); + $r = q("SELECT * FROM addon WHERE installed = 1"); if(count($r)) $installed = $r; else @@ -143,7 +143,7 @@ function reload_plugins() { if(file_exists($fname)) { $t = @filemtime($fname); foreach($installed as $i) { - if(($i['aname'] == $pl) && ($i['tstamp'] != $t)) { + if(($i['aname'] == $pl) && ($i['tstamp'] != $t)) { logger('Reloading plugin: ' . $i['aname']); @include_once($fname); @@ -155,7 +155,7 @@ function reload_plugins() { $func = $pl . '_load'; $func(); } - q("UPDATE `addon` SET `tstamp` = %d WHERE `id` = %d", + q("UPDATE addon SET tstamp = %d WHERE id = %d", intval($t), intval($i['id']) ); @@ -184,7 +184,7 @@ function visible_plugin_list() { * @return mixed|bool */ function register_hook($hook, $file, $function, $priority = 0) { - $r = q("SELECT * FROM `hook` WHERE `hook` = '%s' AND `file` = '%s' AND `fn` = '%s' LIMIT 1", + $r = q("SELECT * FROM hook WHERE hook = '%s' AND file = '%s' AND fn = '%s' LIMIT 1", dbesc($hook), dbesc($file), dbesc($function) @@ -192,7 +192,7 @@ function register_hook($hook, $file, $function, $priority = 0) { if($r) return true; - $r = q("INSERT INTO `hook` (`hook`, `file`, `fn`, `priority`) VALUES ( '%s', '%s', '%s', '%s' )", + $r = q("INSERT INTO hook (hook, file, fn, priority) VALUES ( '%s', '%s', '%s', '%s' )", dbesc($hook), dbesc($file), dbesc($function), @@ -205,14 +205,14 @@ function register_hook($hook, $file, $function, $priority = 0) { /** * @brief unregisters a hook. - * + * * @param string $hook the name of the hook * @param string $file the name of the file that hooks into * @param string $function the name of the function that the hook called * @return array */ function unregister_hook($hook, $file, $function) { - $r = q("DELETE FROM hook WHERE hook = '%s' AND `file` = '%s' AND `fn` = '%s'", + $r = q("DELETE FROM hook WHERE hook = '%s' AND file = '%s' AND fn = '%s'", dbesc($hook), dbesc($file), dbesc($function) @@ -224,7 +224,7 @@ function unregister_hook($hook, $file, $function) { // // It might not be obvious but themes can manually add hooks to the App::$hooks -// array in their theme_init() and use this to customise the app behaviour. +// array in their theme_init() and use this to customise the app behaviour. // UPDATE: use insert_hook($hookname,$function_name) to do this // @@ -248,20 +248,22 @@ function load_hooks() { /** * @brief Inserts a hook into a page request. * - * Insert a short-lived hook into the running page request. - * Hooks are normally persistent so that they can be called + * Insert a short-lived hook into the running page request. + * Hooks are normally persistent so that they can be called * across asynchronous processes such as delivery and poll * processes. * * insert_hook lets you attach a hook callback immediately * which will not persist beyond the life of this page request - * or the current process. + * or the current process. * * @param string $hook * name of hook to attach callback * @param string $fn * function name of callback handler - */ + * @param int $version (optional) default 0 + * @param int $priority (optional) default 0 + */ function insert_hook($hook, $fn, $version = 0, $priority = 0) { if(! is_array(App::$hooks)) @@ -293,7 +295,7 @@ function call_hooks($name, &$data = null) { $hook[1] = unserialize($hook[1]); } elseif(strpos($hook[1],'::')) { - // We shouldn't need to do this, but it appears that PHP + // We shouldn't need to do this, but it appears that PHP // isn't able to directly execute a string variable with a class // method in the manner we are attempting it, so we'll // turn it into an array. @@ -306,10 +308,10 @@ function call_hooks($name, &$data = null) { $func($data); else $func($a, $data); - } + } else { - // Don't do any DB write calls if we're currently logging a possibly failed DB call. + // Don't do any DB write calls if we're currently logging a possibly failed DB call. if(! DBA::$logging) { // The hook should be removed so we don't process it. q("DELETE FROM hook WHERE hook = '%s' AND file = '%s' AND fn = '%s'", @@ -370,7 +372,7 @@ function get_plugin_info($plugin){ } else { $info[$k][] = array('name' => $v); } - } + } else { $info[$k] = $v; } @@ -407,8 +409,8 @@ function check_plugin_versions($info) { if(array_key_exists('serverroles',$info)) { $role = \Zotlabs\Lib\System::get_server_role(); if(! ( - stristr($info['serverroles'],'*') - || stristr($info['serverroles'],'any') + stristr($info['serverroles'],'*') + || stristr($info['serverroles'],'any') || stristr($info['serverroles'],$role))) { logger('serverrole limit: ' . $info['name'],LOGGER_NORMAL,LOG_WARNING); return false; @@ -425,8 +427,15 @@ function check_plugin_versions($info) { $test = trim($test); if(! $test) continue; + if(strpos($test,'.')) { + $conf = explode('.',$test); + if(get_config(trim($conf[0]),trim($conf[1]))) + return true; + else + return false; + } if(! in_array($test,App::$plugins)) - $found = false; + $found = false; } } if(! $found) @@ -583,11 +592,11 @@ function format_css_if_exists($source) { /* * 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 + * 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. + * blocked due to mixed content issues. */ function script_path() { @@ -599,7 +608,7 @@ function script_path() { $scheme = 'https'; else $scheme = 'http'; - + // Some proxy setups may require using http_host if(intval(App::$config['system']['script_path_use_http_host'])) @@ -631,7 +640,7 @@ function head_remove_js($src, $priority = 0) { } // We should probably try to register main.js with a high priority, but currently we handle it -// separately and put it at the end of the html head block in case any other javascript is +// separately and put it at the end of the html head block in case any other javascript is // added outside the head_add_js construct. function head_get_js() { @@ -640,7 +649,7 @@ function head_get_js() { if(App::$js_sources) { ksort(App::$js_sources,SORT_NUMERIC); foreach(App::$js_sources as $sources) { - if(count($sources)) { + if(count($sources)) { foreach($sources as $source) { if($src === 'main.js') continue; @@ -655,7 +664,7 @@ function head_get_js() { function head_get_main_js() { $str = ''; $sources = array('main.js'); - if(count($sources)) + if(count($sources)) foreach($sources as $source) $str .= format_js_if_exists($source,true); return $str; diff --git a/include/queue_fn.php b/include/queue_fn.php index 676620e2f..0950faf85 100644 --- a/include/queue_fn.php +++ b/include/queue_fn.php @@ -93,7 +93,7 @@ function queue_deliver($outq, $immediate = false) { // your site has existed. Since we don't know for sure what these sites are, // call them unknown - q("insert into site (site_url, site_update, site_dead, site_type) values ('%s','%s',0,%d) ", + q("insert into site (site_url, site_update, site_dead, site_type, site_crypto) values ('%s','%s',0,%d,'') ", dbesc($base), dbesc(datetime_convert()), intval(($outq['outq_driver'] === 'post') ? SITE_TYPE_NOTZOT : SITE_TYPE_UNKNOWN) @@ -118,7 +118,7 @@ function queue_deliver($outq, $immediate = false) { dbesc($base) ); } - q("update dreport set dreport_result = '%s', dreport_time = '%s' where dreport_queue = '%s' limit 1", + q("update dreport set dreport_result = '%s', dreport_time = '%s' where dreport_queue = '%s'", dbesc('accepted for delivery'), dbesc(datetime_convert()), dbesc($outq['outq_hash']) diff --git a/include/security.php b/include/security.php index 9b508d339..2f5de54c7 100644 --- a/include/security.php +++ b/include/security.php @@ -2,11 +2,12 @@ /** * @file include/security.php * - * Some security related functions. + * @brief Some security related functions. */ /** * @param int $user_record The account_id + * @param array $channel * @param bool $login_initial default false * @param bool $interactive default false * @param bool $return @@ -27,8 +28,8 @@ function authenticate_success($user_record, $channel = null, $login_initial = fa $uid_to_load = $channel['channel_id']; if(! $uid_to_load) { - $uid_to_load = (((x($_SESSION,'uid')) && (intval($_SESSION['uid']))) - ? intval($_SESSION['uid']) + $uid_to_load = (((x($_SESSION,'uid')) && (intval($_SESSION['uid']))) + ? intval($_SESSION['uid']) : intval(App::$account['account_default_channel']) ); } @@ -89,21 +90,28 @@ function authenticate_success($user_record, $channel = null, $login_initial = fa function atoken_login($atoken) { if(! $atoken) return false; + $_SESSION['authenticated'] = 1; $_SESSION['visitor_id'] = $atoken['xchan_hash']; $_SESSION['atoken'] = $atoken['atoken_id']; \App::set_observer($atoken); + return true; } - +/** + * @brief + * + * @param array $atoken + * @return array|null + */ function atoken_xchan($atoken) { $c = channelx_by_n($atoken['atoken_uid']); if($c) { return [ - 'atoken_id' => $atoken['atoken_id'], + 'atoken_id' => $atoken['atoken_id'], 'xchan_hash' => substr($c['channel_hash'],0,16) . '.' . $atoken['atoken_name'], 'xchan_name' => $atoken['atoken_name'], 'xchan_addr' => t('guest:') . $atoken['atoken_name'] . '@' . \App::get_hostname(), @@ -114,9 +122,9 @@ function atoken_xchan($atoken) { 'xchan_photo_l' => get_default_profile_photo(300), 'xchan_photo_m' => get_default_profile_photo(80), 'xchan_photo_s' => get_default_profile_photo(48) - ]; } + return null; } @@ -133,7 +141,7 @@ function atoken_delete($atoken_id) { ); if(! $c) return; - + $atoken_xchan = substr($c[0]['channel_hash'],0,16) . '.' . $r[0]['atoken_name']; q("delete from atoken where atoken_id = %d", @@ -145,12 +153,16 @@ function atoken_delete($atoken_id) { ); } - - -// in order for atoken logins to create content (such as posts) they need a stored xchan. -// we'll create one on the first atoken_login; it can't really ever go away but perhaps -// @fixme we should set xchan_deleted if it's expired or removed - +/** + * @brief + * + * In order for atoken logins to create content (such as posts) they need a stored xchan. + * we'll create one on the first atoken_login; it can't really ever go away but perhaps + * @fixme we should set xchan_deleted if it's expired or removed + * + * @param array $xchan + * @return void|boolean + */ function atoken_create_xchan($xchan) { $r = q("select xchan_hash from xchan where xchan_hash = '%s'", @@ -159,7 +171,7 @@ function atoken_create_xchan($xchan) { if($r) return; - $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_addr, xchan_url, xchan_name, xchan_network, xchan_photo_mimetype, xchan_photo_l, xchan_photo_m, xchan_photo_s ) + $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_addr, xchan_url, xchan_name, xchan_network, xchan_photo_mimetype, xchan_photo_l, xchan_photo_m, xchan_photo_s ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", dbesc($xchan['xchan_hash']), dbesc($xchan['xchan_hash']), @@ -202,12 +214,11 @@ function atoken_abook($uid,$xchan_hash) { } return false; - } function pseudo_abook($xchan) { - if(! $xchan) + if(! $xchan) return false; // set abook_pseudo to flag that we aren't really connected. @@ -216,8 +227,8 @@ function pseudo_abook($xchan) { $xchan['abook_blocked'] = 0; $xchan['abook_ignored'] = 0; $xchan['abook_pending'] = 0; + return $xchan; - } @@ -228,7 +239,6 @@ function pseudo_abook($xchan) { * * @return bool|array false or channel record of the new channel */ - function change_channel($change_channel) { $ret = false; @@ -260,7 +270,7 @@ function change_channel($change_channel) { date_default_timezone_set($r[0]['channel_timezone']); $ret = $r[0]; } - $x = q("select * from xchan where xchan_hash = '%s' limit 1", + $x = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($hash) ); if($x) { @@ -275,7 +285,6 @@ function change_channel($change_channel) { $arr = [ 'channel_id' => $change_channel, 'chanx' => $ret ]; call_hooks('change_channel', $arr); - } return $ret; @@ -285,11 +294,11 @@ function change_channel($change_channel) { * @brief Creates an additional SQL where statement to check permissions. * * @param int $owner_id - * @param bool $remote_observer - if unset use current observer + * @param bool $remote_observer (optional) use current observer if unset + * @param $table (optional) * * @return string additional SQL where statement */ - function permissions_sql($owner_id, $remote_observer = null, $table = '') { $local_channel = local_channel(); @@ -303,11 +312,10 @@ function permissions_sql($owner_id, $remote_observer = null, $table = '') { if($table) $table .= '.'; - - $sql = " AND {$table}allow_cid = '' - AND {$table}allow_gid = '' - AND {$table}deny_cid = '' - AND {$table}deny_gid = '' + $sql = " AND {$table}allow_cid = '' + AND {$table}allow_gid = '' + AND {$table}deny_cid = '' + AND {$table}deny_gid = '' "; /** @@ -319,7 +327,7 @@ function permissions_sql($owner_id, $remote_observer = null, $table = '') { } /** - * Authenticated visitor. Unless pre-verified, + * Authenticated visitor. Unless pre-verified, * check that the contact belongs to this $owner_id * and load the groups the visitor belongs to. * If pre-verified, the caller is expected to have already @@ -358,7 +366,7 @@ function permissions_sql($owner_id, $remote_observer = null, $table = '') { * @brief Creates an addiontal SQL where statement to check permissions for an item. * * @param int $owner_id - * @param bool $remote_observer, use current observer if unset + * @param bool $remote_observer (optional) use current observer if unset * * @return string additional SQL where statement */ @@ -379,7 +387,7 @@ function item_permissions_sql($owner_id, $remote_observer = null) { */ if(($local_channel) && ($local_channel == $owner_id)) { - $sql = ''; + $sql = ''; } /** @@ -425,7 +433,7 @@ function item_permissions_sql($owner_id, $remote_observer = null) { /** * Remote visitors also need to be checked against the public_scope parameter if item_private is set. * This function checks the various permutations of that field for any which apply to this observer. - * + * */ @@ -448,9 +456,9 @@ function scopes_sql($uid,$observer) { $str .= " or public_policy = 'contacts' ) "; return $str; } - - - + + + @@ -500,7 +508,7 @@ function public_permissions_sql($observer_hash) { * If the new page contains by any chance external elements, then the used security token is exposed by the referrer. * Actually, important actions should not be triggered by Links / GET-Requests at all, but somethimes they still are, * so this mechanism brings in some damage control (the attacker would be able to forge a request to a form of this type, but not to forms of other types). - */ + */ function get_form_security_token($typename = '') { $timestamp = time(); @@ -549,7 +557,7 @@ function check_form_security_token_ForbiddenOnErr($typename = '', $formname = 'f 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' ", + $r = q("SELECT hash FROM groups left join group_member on groups.id = group_member.gid WHERE xchan = '%s' ", dbesc($contact_id) ); if($r) { @@ -561,13 +569,13 @@ function init_groups_visitor($contact_id) { -// This is used to determine which uid have posts which are visible to the logged in user (from the API) for the +// This is used to determine which uid have posts which are visible to the logged in user (from the API) for the // public_timeline, and we can use this in a community page by making -// $perms = (PERMS_NETWORK|PERMS_PUBLIC) unless logged in. +// $perms = (PERMS_NETWORK|PERMS_PUBLIC) unless logged in. // Collect uids of everybody on this site who has opened their posts to everybody on this site (or greater visibility) // We always include yourself if logged in because you can always see your own posts // resolving granular permissions for the observer against every person and every post on the site -// will likely be too expensive. +// will likely be too expensive. // Returns a string list of comma separated channel_ids suitable for direct inclusion in a SQL query function stream_perms_api_uids($perms = NULL, $limit = 0, $rand = 0 ) { diff --git a/include/selectors.php b/include/selectors.php index d7d070d31..ab049fff6 100644 --- a/include/selectors.php +++ b/include/selectors.php @@ -7,7 +7,7 @@ function contact_profile_assign($current) { $o .= "<select id=\"contact-profile-selector\" name=\"profile_assign\" class=\"form-control\"/>\r\n"; - $r = q("SELECT profile_guid, profile_name FROM `profile` WHERE `uid` = %d", + $r = q("SELECT profile_guid, profile_name FROM profile WHERE uid = %d", intval($_SESSION['uid'])); if($r) { diff --git a/include/socgraph.php b/include/socgraph.php index 4cb5600ec..69365f80f 100644 --- a/include/socgraph.php +++ b/include/socgraph.php @@ -245,10 +245,10 @@ function common_friends($uid,$xchan,$start = 0,$limit=100000000,$shuffle = false function count_common_friends_zcid($uid,$zcid) { - $r = q("SELECT count(*) as `total` - FROM `glink` left join `gcontact` on `glink`.`gcid` = `gcontact`.`id` - where `glink`.`zcid` = %d - and `gcontact`.`nurl` in (select nurl from contact where uid = %d and self = 0 and blocked = 0 and hidden = 0 ) ", + $r = q("SELECT count(*) as total + FROM glink left join gcontact on glink.gcid = gcontact.id + where glink.zcid = %d + and gcontact.nurl in (select nurl from contact where uid = %d and self = 0 and blocked = 0 and hidden = 0 ) ", intval($zcid), intval($uid) ); @@ -264,12 +264,12 @@ function common_friends_zcid($uid,$zcid,$start = 0, $limit = 9999,$shuffle = fal if($shuffle) $sql_extra = " order by rand() "; else - $sql_extra = " order by `gcontact`.`name` asc "; + $sql_extra = " order by gcontact.name asc "; - $r = q("SELECT `gcontact`.* - FROM `glink` left join `gcontact` on `glink`.`gcid` = `gcontact`.`id` - where `glink`.`zcid` = %d - and `gcontact`.`nurl` in (select nurl from contact where uid = %d and self = 0 and blocked = 0 and hidden = 0 ) + $r = q("SELECT gcontact.* + FROM glink left join gcontact on glink.gcid = gcontact.id + where glink.zcid = %d + and gcontact.nurl in (select nurl from contact where uid = %d and self = 0 and blocked = 0 and hidden = 0 ) $sql_extra limit %d offset %d", intval($zcid), intval($uid), @@ -283,9 +283,9 @@ function common_friends_zcid($uid,$zcid,$start = 0, $limit = 9999,$shuffle = fal function count_all_friends($uid,$cid) { - $r = q("SELECT count(*) as `total` - FROM `glink` left join `gcontact` on `glink`.`gcid` = `gcontact`.`id` - where `glink`.`cid` = %d and `glink`.`uid` = %d ", + $r = q("SELECT count(*) as total + FROM glink left join gcontact on glink.gcid = gcontact.id + where glink.cid = %d and glink.uid = %d ", intval($cid), intval($uid) ); @@ -299,10 +299,10 @@ function count_all_friends($uid,$cid) { function all_friends($uid,$cid,$start = 0, $limit = 80) { - $r = q("SELECT `gcontact`.* - FROM `glink` left join `gcontact` on `glink`.`gcid` = `gcontact`.`id` - where `glink`.`cid` = %d and `glink`.`uid` = %d - order by `gcontact`.`name` asc LIMIT %d OFFSET %d ", + $r = q("SELECT gcontact.* + FROM glink left join gcontact on glink.gcid = gcontact.id + where glink.cid = %d and glink.uid = %d + order by gcontact.name asc LIMIT %d OFFSET %d ", intval($cid), intval($uid), intval($limit), @@ -319,7 +319,7 @@ function suggestion_query($uid, $myxchan, $start = 0, $limit = 80) { if((! $uid) || (! $myxchan)) return array(); - $r = q("SELECT count(xlink_xchan) as `total`, xchan.* from xchan + $r = q("SELECT count(xlink_xchan) as total, xchan.* from xchan left join xlink on xlink_link = xchan_hash where xlink_xchan in ( select abook_xchan from abook where abook_channel = %d ) and not xlink_link in ( select abook_xchan from abook where abook_channel = %d ) @@ -339,7 +339,7 @@ function suggestion_query($uid, $myxchan, $start = 0, $limit = 80) { if($r && count($r) >= ($limit -1)) return $r; - $r2 = q("SELECT count(xlink_link) as `total`, xchan.* from xchan + $r2 = q("SELECT count(xlink_link) as total, xchan.* from xchan left join xlink on xlink_link = xchan_hash where xlink_xchan = '' and not xlink_link in ( select abook_xchan from abook where abook_channel = %d ) @@ -465,11 +465,11 @@ function poco($a,$extended = false) { $sql_extra = sprintf(" and abook_id = %d and abook_hidden = 0 ",intval($cid)); if($system_mode) { - $r = q("SELECT count(*) as `total` from abook where abook_self = 1 + $r = q("SELECT count(*) as total from abook where abook_self = 1 and abook_channel in (select uid from pconfig where cat = 'system' and k = 'suggestme' and v = '1') "); } else { - $r = q("SELECT count(*) as `total` from abook where abook_channel = %d + $r = q("SELECT count(*) as total from abook where abook_channel = %d $sql_extra ", intval($channel_id) ); diff --git a/include/statistics_fns.php b/include/statistics_fns.php index ce2eee5e7..cbff3b0b7 100644 --- a/include/statistics_fns.php +++ b/include/statistics_fns.php @@ -64,7 +64,7 @@ function update_channels_active_monthly_stat() { } function update_local_posts_stat() { - $posts = q("SELECT COUNT(*) AS local_posts FROM `item` WHERE item_wall = 1 "); + $posts = q("SELECT COUNT(*) AS local_posts FROM item WHERE item_wall = 1 "); if (is_array($posts)) { $local_posts_stat = intval($posts[0]["local_posts"]); set_config('system','local_posts_stat',$local_posts_stat); diff --git a/include/taxonomy.php b/include/taxonomy.php index 067bd3246..0b4b2aa9a 100644 --- a/include/taxonomy.php +++ b/include/taxonomy.php @@ -386,7 +386,7 @@ function get_things($profile_hash,$uid) { $things[$k] = null; foreach($r as $rr) { - $l = q("select xchan_name, xchan_photo_s, xchan_url from likes left join xchan on likee = xchan_hash where + $l = q("select xchan_name, xchan_photo_s, xchan_url from likes left join xchan on liker = xchan_hash where target_type = '%s' and target_id = '%s' and channel_id = %d", dbesc(ACTIVITY_OBJ_THING), dbesc($rr['obj_obj']), diff --git a/include/text.php b/include/text.php index 9c4a4a5c5..b25eb8e46 100644 --- a/include/text.php +++ b/include/text.php @@ -4,6 +4,7 @@ */ require_once("include/bbcode.php"); +require_once('include/hubloc.php'); // random string, there are 86 characters max in text mode, 128 for hex // output is urlsafe @@ -17,6 +18,7 @@ define('RANDOM_STRING_TEXT', 0x01 ); * @param string|SmartyEngine $s the string requiring macro substitution, * or an instance of SmartyEngine * @param array $r key value pairs (search => replace) + * * @return string substituted string */ function replace_macros($s, $r) { @@ -35,6 +37,7 @@ function replace_macros($s, $r) { * * @param number $size * @param int $type + * * @return string */ function random_string($size = 64, $type = RANDOM_STRING_HEX) { @@ -52,14 +55,15 @@ function random_string($size = 64, $type = RANDOM_STRING_HEX) { * that had an XSS attack vector due to stripping the high-bit on an 8-bit character * after cleansing, and angle chars with the high bit set could get through as markup. * - * This is now disabled because it was interfering with some legitimate unicode sequences - * and hopefully there aren't a lot of those browsers left. + * This is now disabled because it was interfering with some legitimate unicode sequences + * and hopefully there aren't a lot of those browsers left. * * Use this on any text input where angle chars are not valid or permitted * They will be replaced with safer brackets. This may be filtered further * if these are not allowed either. * * @param string $string Input string + * * @return string Filtered string */ function notags($string) { @@ -74,13 +78,13 @@ function notags($string) { /** * use this on "body" or "content" input where angle chars shouldn't be removed, * and allow them to be safely displayed. + * * @param string $string + * * @return string */ function escape_tags($string) { - return(htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false)); - } @@ -182,7 +186,7 @@ function purify_html($s, $allow_position = false) { // f6 plugins - //abide - the use is pointless since we can't do anything with forms + //abide - the use is pointless since we can't do anything with forms //equalizer $def->info_global_attr['data-equalizer'] = new HTMLPurifier_AttrDef_Text; @@ -272,14 +276,12 @@ function purify_html($s, $allow_position = false) { } - $purifier = new HTMLPurifier($config); return $purifier->purify($s); } - /** * @brief generate a string that's random, but usually pronounceable. * @@ -293,7 +295,7 @@ function autoname($len) { if ($len <= 0) return ''; - $vowels = array('a','a','ai','au','e','e','e','ee','ea','i','ie','o','ou','u'); + $vowels = array('a','a','ai','au','e','e','e','ee','ea','i','ie','o','ou','u'); if (mt_rand(0, 5) == 4) $vowels[] = 'y'; @@ -370,13 +372,12 @@ function xmlify($str) { if(is_array($str)) { - // allow to fall through so we ge a PHP error, as the log statement will - // probably get lost in the noise unless we're specifically looking for it. + // allow to fall through so we ge a PHP error, as the log statement will + // probably get lost in the noise unless we're specifically looking for it. btlogger('xmlify called with array: ' . print_r($str,true), LOGGER_NORMAL, LOG_WARNING); } - $len = mb_strlen($str); for($x = 0; $x < $len; $x ++) { $char = mb_substr($str,$x,1); @@ -412,27 +413,36 @@ function xmlify($str) { return($buffer); } -// undo an xmlify -// pass xml escaped text ($s), returns unescaped text - - +/** + * @brief Undo an xmlify. + * + * Pass xml escaped text ($s), returns unescaped text. + * + * @param string $s + * + * @return string + */ function unxmlify($s) { - $ret = str_replace('&','&', $s); - $ret = str_replace(array('<','>','"','''),array('<','>','"',"'"),$ret); + $ret = str_replace('&', '&', $s); + $ret = str_replace(array('<', '>', '"', '''), array('<', '>', '"', "'"), $ret); + return $ret; } -// Automatic pagination. -// To use, get the count of total items. -// Then call App::set_pager_total($number_items); -// Optionally call App::set_pager_itemspage($n) to the number of items to display on each page -// Then call paginate($a) after the end of the display loop to insert the pager block on the page -// (assuming there are enough items to paginate). -// When using with SQL, the setting LIMIT %d, %d => App::$pager['start'],App::$pager['itemspage'] -// will limit the results to the correct items for the current page. -// The actual page handling is then accomplished at the application layer. - - +/** + * @brief Automatic pagination. + * + * To use, get the count of total items. + * Then call App::set_pager_total($number_items); + * Optionally call App::set_pager_itemspage($n) to the number of items to display on each page + * Then call paginate($a) after the end of the display loop to insert the pager block on the page + * (assuming there are enough items to paginate). + * When using with SQL, the setting LIMIT %d, %d => App::$pager['start'],App::$pager['itemspage'] + * will limit the results to the correct items for the current page. + * The actual page handling is then accomplished at the application layer. + * + * @param App &$a + */ function paginate(&$a) { $o = ''; $stripped = preg_replace('/(&page=[0-9]*)/','',App::$query_string); @@ -451,15 +461,15 @@ function paginate(&$a) { $o .= "<span class=\"pager_first\"><a href=\"$url"."&page=1\">" . t('first') . "</a></span> "; - $numpages = App::$pager['total'] / App::$pager['itemspage']; + $numpages = App::$pager['total'] / App::$pager['itemspage']; - $numstart = 1; - $numstop = $numpages; + $numstart = 1; + $numstop = $numpages; - if($numpages > 14) { - $numstart = (($pagenum > 7) ? ($pagenum - 7) : 1); - $numstop = (($pagenum > ($numpages - 7)) ? $numpages : ($numstart + 14)); - } + if($numpages > 14) { + $numstart = (($pagenum > 7) ? ($pagenum - 7) : 1); + $numstop = (($pagenum > ($numpages - 7)) ? $numpages : ($numstart + 14)); + } for($i = $numstart; $i <= $numstop; $i++){ if($i == App::$pager['page']) @@ -484,6 +494,7 @@ function paginate(&$a) { $o .= '<span class="pager_next">'."<a href=\"$url"."&page=".(App::$pager['page'] + 1).'">' . t('next') . '</a></span>'; $o .= '</div>'."\r\n"; } + return $o; } @@ -514,7 +525,6 @@ function alt_pager(&$a, $i, $more = '', $less = '') { } - /** * @brief Generate a guaranteed unique (for this domain) item ID for ATOM. * @@ -558,17 +568,23 @@ function photo_new_resource() { return $resource; } - - -// for html,xml parsing - let's say you've got -// an attribute foobar="class1 class2 class3" -// and you want to find out if it contains 'class3'. -// you can't use a normal sub string search because you -// might match 'notclass3' and a regex to do the job is -// possible but a bit complicated. -// pass the attribute string as $attr and the attribute you -// are looking for as $s - returns true if found, otherwise false - +/** + * @brief + * + * for html,xml parsing - let's say you've got + * an attribute foobar="class1 class2 class3" + * and you want to find out if it contains 'class3'. + * you can't use a normal sub string search because you + * might match 'notclass3' and a regex to do the job is + * possible but a bit complicated. + * + * pass the attribute string as $attr and the attribute you + * are looking for as $s - returns true if found, otherwise false + * + * @param string $attr attribute string + * @param string $s attribute you are looking for + * @return boolean true if found + */ function attribute_contains($attr, $s) { $a = explode(' ', $attr); if(count($a) && in_array($s, $a)) @@ -591,10 +607,9 @@ function attribute_contains($attr, $s) { * was called, so no need to add it to the message anymore. * * @param string $msg Message to log - * @param int $level A log level. + * @param int $level A log level * @param int $priority - compatible with syslog */ - function logger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) { if(App::$module == 'setup' && is_writable('install.log')) { @@ -612,7 +627,7 @@ function logger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) { return; $where = ''; - + // We require > 5.4 but leave the version check so that install issues (including version) can be logged if(version_compare(PHP_VERSION, '5.4.0') >= 0) { @@ -630,19 +645,23 @@ function logger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) { @file_put_contents($pluginfo['filename'], $pluginfo['message'], FILE_APPEND); } -// like logger() but with a function backtrace to pinpoint certain classes -// of problems which show up deep in the calling stack - - +/** + * @brief like logger() but with a function backtrace to pinpoint certain classes + * of problems which show up deep in the calling stack. + * + * @param string $msg Message to log + * @param int $level A log level + * @param int $priority - compatible with syslog + */ function btlogger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) { logger($msg, $level, $priority); if(version_compare(PHP_VERSION, '5.4.0') >= 0) { - $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); if($stack) { for($x = 1; $x < count($stack); $x ++) { logger('stack: ' . basename($stack[$x]['file']) . ':' . $stack[$x]['line'] . ':' . $stack[$x]['function'] . '()',$level, $priority); - } + } } } } @@ -718,15 +737,17 @@ function activity_match($haystack,$needle) { return false; } - -// Pull out all #hashtags and @person tags from $s; -// We also get @person@domain.com - which would make -// the regex quite complicated as tags can also -// end a sentence. So we'll run through our results -// and strip the period from any tags which end with one. -// Returns array of tags found, or empty array. - - +/** + * @brief Pull out all #hashtags and @person tags from $s. + * + * We also get @person@domain.com - which would make + * the regex quite complicated as tags can also + * end a sentence. So we'll run through our results + * and strip the period from any tags which end with one. + * + * @param string $s + * @return Returns array of tags found, or empty array. + */ function get_tags($s) { $ret = array(); $match = array(); @@ -797,9 +818,9 @@ function get_tags($s) { // make sure the longer tags are returned first so that if two or more have common substrings // we'll replace the longest ones first. Otherwise the common substring would be found in - // both strings and the string replacement would link both to the shorter strings and + // both strings and the string replacement would link both to the shorter strings and // fail to link the longer string. Hubzilla github issue #378 - + usort($ret,'tag_sort_length'); // logger('get_tags: ' . print_r($ret,true)); @@ -816,21 +837,15 @@ function tag_sort_length($a,$b) { -function strip_zids($s) { - return preg_replace('/[\?&]zid=(.*?)(&|$)/ism','$2',$s); -} - -function strip_zats($s) { - return preg_replace('/[\?&]zat=(.*?)(&|$)/ism','$2',$s); -} - - -// quick and dirty quoted_printable encoding - - +/** + * @brief Quick and dirty quoted_printable encoding. + * + * @param string $s + * @return string + */ function qp($s) { - return str_replace ("%","=",rawurlencode($s)); -} + return str_replace ("%", "=", rawurlencode($s)); +} function get_mentions($item,$tags) { @@ -889,9 +904,9 @@ function contact_block() { $contacts = t('No connections'); $micropro = null; } else { - + $randfunc = db_getfunc('RAND'); - + $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash WHERE abook_channel = %d $abook_flags and abook_archived = 0 and xchan_orphan = 0 and xchan_deleted = 0 $sql_extra ORDER BY $randfunc LIMIT %d", intval(App::$profile['uid']), intval($shown) @@ -936,8 +951,8 @@ function chanlink_cid($d) { } function magiclink_url($observer,$myaddr,$url) { - return (($observer) - ? z_root() . '/magic?f=&dest=' . $url . '&addr=' . $myaddr + return (($observer) + ? z_root() . '/magic?f=&dest=' . $url . '&addr=' . $myaddr : $url ); } @@ -1034,8 +1049,8 @@ function sslify($s) { // Complain to your browser maker $allow = get_config('system','sslify_everything'); - - $pattern = (($allow) ? "/\<(.*?)src=\"(http\:.*?)\"(.*?)\>/" : "/\<img(.*?)src=\"(http\:.*?)\"(.*?)\>/" ); + + $pattern = (($allow) ? "/\<(.*?)src=\"(http\:.*?)\"(.*?)\>/" : "/\<img(.*?)src=\"(http\:.*?)\"(.*?)\>/" ); $matches = null; $cnt = preg_match_all($pattern,$s,$matches,PREG_SET_ORDER); @@ -1105,40 +1120,43 @@ function get_mood_verbs() { return $arr; } -// Function to list all smilies, both internal and from addons -// Returns array with keys 'texts' and 'icons' +/** + * @brief Function to list all smilies, both internal and from addons. + * + * @return Returns array with keys 'texts' and 'icons' + */ function list_smilies() { - $texts = array( - '<3', - '</3', - '<\\3', - ':-)', - ';-)', - ':-(', - ':-P', - ':-p', - ':-"', - ':-"', - ':-x', - ':-X', - ':-D', - '8-|', - '8-O', - ':-O', - '\\o/', - 'o.O', - 'O.o', - 'o_O', - 'O_o', - ":'(", - ":-!", - ":-/", - ":-[", + $texts = array( + '<3', + '</3', + '<\\3', + ':-)', + ';-)', + ':-(', + ':-P', + ':-p', + ':-"', + ':-"', + ':-x', + ':-X', + ':-D', + '8-|', + '8-O', + ':-O', + '\\o/', + 'o.O', + 'O.o', + 'o_O', + 'O_o', + ":'(", + ":-!", + ":-/", + ":-[", "8-)", - ':beer', - ':homebrew', - ':coffee', + ':beer', + ':homebrew', + ':coffee', ':facepalm', ':like', ':dislike', @@ -1146,38 +1164,38 @@ function list_smilies() { ); $icons = array( - '<img class="smiley" src="' . z_root() . '/images/smiley-heart.gif" alt="<3" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-brokenheart.gif" alt="</3" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-brokenheart.gif" alt="<\\3" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-smile.gif" alt=":-)" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-wink.gif" alt=";-)" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-frown.gif" alt=":-(" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-tongue-out.gif" alt=":-P" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-tongue-out.gif" alt=":-p" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-\"" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-\"" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-x" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-X" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-laughing.gif" alt=":-D" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-surprised.gif" alt="8-|" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-surprised.gif" alt="8-O" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-surprised.gif" alt=":-O" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-thumbsup.gif" alt="\\o/" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="o.O" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="O.o" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="o_O" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="O_o" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-cry.gif" alt=":\'(" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-foot-in-mouth.gif" alt=":-!" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-undecided.gif" alt=":-/" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-embarassed.gif" alt=":-[" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-cool.gif" alt="8-)" />', - '<img class="smiley" src="' . z_root() . '/images/beer_mug.gif" alt=":beer" />', - '<img class="smiley" src="' . z_root() . '/images/beer_mug.gif" alt=":homebrew" />', - '<img class="smiley" src="' . z_root() . '/images/coffee.gif" alt=":coffee" />', - '<img class="smiley" src="' . z_root() . '/images/smiley-facepalm.gif" alt=":facepalm" />', - '<img class="smiley" src="' . z_root() . '/images/like.gif" alt=":like" />', - '<img class="smiley" src="' . z_root() . '/images/dislike.gif" alt=":dislike" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-heart.gif" alt="<3" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-brokenheart.gif" alt="</3" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-brokenheart.gif" alt="<\\3" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-smile.gif" alt=":-)" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-wink.gif" alt=";-)" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-frown.gif" alt=":-(" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-tongue-out.gif" alt=":-P" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-tongue-out.gif" alt=":-p" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-kiss.gif" alt=":-\"" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-kiss.gif" alt=":-\"" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-kiss.gif" alt=":-x" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-kiss.gif" alt=":-X" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-laughing.gif" alt=":-D" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-surprised.gif" alt="8-|" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-surprised.gif" alt="8-O" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-surprised.gif" alt=":-O" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-thumbsup.gif" alt="\\o/" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-Oo.gif" alt="o.O" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-Oo.gif" alt="O.o" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-Oo.gif" alt="o_O" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-Oo.gif" alt="O_o" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-cry.gif" alt=":\'(" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-foot-in-mouth.gif" alt=":-!" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-undecided.gif" alt=":-/" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-embarassed.gif" alt=":-[" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-cool.gif" alt="8-)" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/beer_mug.gif" alt=":beer" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/beer_mug.gif" alt=":homebrew" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/coffee.gif" alt=":coffee" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-facepalm.gif" alt=":facepalm" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/like.gif" alt=":like" />', + '<img class="smiley" src="' . z_root() . '/images/emoticons/dislike.gif" alt=":dislike" />', '<img class="smiley" src="' . z_root() . '/images/hz-16.png" alt=":hubzilla" />', ); @@ -1208,7 +1226,7 @@ function list_smilies() { * We will escape text between HTML pre and code blocks, and HTML attributes * (such as urls) from being processed. * - * At a higher level, the bbcode [nosmile] tag can be used to prevent this + * At a higher level, the bbcode [nosmile] tag can be used to prevent this * function from being executed by the prepare_text() routine when preparing * bbcode source for HTML display. * @@ -1218,7 +1236,7 @@ function list_smilies() { */ function smilies($s, $sample = false) { - if(intval(get_config('system', 'no_smilies')) + if(intval(get_config('system', 'no_smilies')) || (local_channel() && intval(get_pconfig(local_channel(), 'system', 'no_smilies')))) return $s; @@ -1255,8 +1273,8 @@ function smile_shield($m) { return '<!--base64:' . base64special_encode($m[0]) . '-->'; } -function smile_unshield($m) { - return base64special_decode($m[1]); +function smile_unshield($m) { + return base64special_decode($m[1]); } /** @@ -1271,7 +1289,7 @@ function preg_heart($x) { $t = ''; for($cnt = 0; $cnt < strlen($x[1]); $cnt ++) - $t .= '<img class="smiley" src="' . z_root() . '/images/smiley-heart.gif" alt="<3" />'; + $t .= '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-heart.gif" alt="<3" />'; $r = str_replace($x[0],$t,$x[0]); @@ -1364,11 +1382,11 @@ function theme_attachments(&$item) { $icon = getIconFromType($r['type']); $label = (($r['title']) ? urldecode(htmlspecialchars($r['title'], ENT_COMPAT, 'UTF-8')) : t('Unknown Attachment')); - + //some feeds provide an attachment where title an empty space if($label == ' ') $label = t('Unknown Attachment'); - + $title = t('Size') . ' ' . (($r['length']) ? userReadableSize($r['length']) : t('unknown')); require_once('include/channel.php'); @@ -1508,7 +1526,7 @@ function generate_named_map($location) { function prepare_body(&$item,$attach = false) { - call_hooks('prepare_body_init', $item); + call_hooks('prepare_body_init', $item); $s = ''; $photo = ''; @@ -1602,7 +1620,9 @@ function prepare_body(&$item,$attach = false) { * @brief Given a text string, convert from bbcode to html and add smilie icons. * * @param string $text - * @param sting $content_type + * @param sting $content_type (optional) default text/bbcode + * @param boolean $cache (optional) default false + * * @return string */ function prepare_text($text, $content_type = 'text/bbcode', $cache = false) { @@ -1621,19 +1641,18 @@ function prepare_text($text, $content_type = 'text/bbcode', $cache = false) { $s = Markdown($text); break; - case 'application/x-pdl'; $s = escape_tags($text); break; - - // No security checking is done here at display time - so we need to verify - // that the author is allowed to use PHP before storing. We also cannot allow - // importation of PHP text bodies from other sites. Therefore this content + + // No security checking is done here at display time - so we need to verify + // that the author is allowed to use PHP before storing. We also cannot allow + // importation of PHP text bodies from other sites. Therefore this content // type is only valid for web pages (and profile details). - // It may be possible to provide a PHP message body which is evaluated on the - // sender's site before sending it elsewhere. In that case we will have a - // different content-type here. + // It may be possible to provide a PHP message body which is evaluated on the + // sender's site before sending it elsewhere. In that case we will have a + // different content-type here. case 'application/x-php': ob_start(); @@ -1674,43 +1693,6 @@ function create_export_photo_body(&$item) { } /** - * zidify_callback() and zidify_links() work together to turn any HTML a tags with class="zrl" into zid links - * These will typically be generated by a bbcode '[zrl]' tag. This is done inside prepare_text() rather than bbcode() - * because the latter is used for general purpose conversions and the former is used only when preparing text for - * immediate display. - * - * Issues: Currently the order of HTML parameters in the text is somewhat rigid and inflexible. - * We assume it looks like \<a class="zrl" href="xxxxxxxxxx"\> and will not work if zrl and href appear in a different order. - * - * @param array $match - * @return string - */ -function zidify_callback($match) { - $is_zid = ((feature_enabled(local_channel(),'sendzid')) || (strpos($match[1],'zrl')) ? true : false); - $replace = '<a' . $match[1] . ' href="' . (($is_zid) ? zid($match[2]) : $match[2]) . '"'; - $x = str_replace($match[0],$replace,$match[0]); - - return $x; -} - -function zidify_img_callback($match) { - $is_zid = ((feature_enabled(local_channel(),'sendzid')) || (strpos($match[1],'zrl')) ? true : false); - $replace = '<img' . $match[1] . ' src="' . (($is_zid) ? zid($match[2]) : $match[2]) . '"'; - - $x = str_replace($match[0],$replace,$match[0]); - - return $x; -} - - -function zidify_links($s) { - $s = preg_replace_callback('/\<a(.*?)href\=\"(.*?)\"/ism','zidify_callback',$s); - $s = preg_replace_callback('/\<img(.*?)src\=\"(.*?)\"/ism','zidify_img_callback',$s); - - return $s; -} - -/** * @brief Return atom link elements for all of our hubs. * * @return string @@ -1735,16 +1717,20 @@ function feed_hublinks() { return $hubxml; } -/* return atom link elements for salmon endpoints */ - +/** + * @brief Return atom link elements for salmon endpoints + * + * @param string $nick + * @return string + */ function feed_salmonlinks($nick) { $salmon = '<link rel="salmon" href="' . xmlify(z_root() . '/salmon/' . $nick) . '" />' . "\n" ; - // old style links that status.net still needed as of 12/2010 + // old style links that status.net still needed as of 12/2010 - $salmon .= ' <link rel="http://salmon-protocol.org/ns/salmon-replies" href="' . xmlify(z_root() . '/salmon/' . $nick) . '" />' . "\n" ; - $salmon .= ' <link rel="http://salmon-protocol.org/ns/salmon-mention" href="' . xmlify(z_root() . '/salmon/' . $nick) . '" />' . "\n" ; + $salmon .= ' <link rel="http://salmon-protocol.org/ns/salmon-replies" href="' . xmlify(z_root() . '/salmon/' . $nick) . '" />' . "\n" ; + $salmon .= ' <link rel="http://salmon-protocol.org/ns/salmon-mention" href="' . xmlify(z_root() . '/salmon/' . $nick) . '" />' . "\n" ; return $salmon; } @@ -1773,7 +1759,7 @@ function unamp($s) { } function layout_select($channel_id, $current = '') { - $r = q("select mid, v from item left join iconfig on iconfig.iid = item.id + $r = q("select mid, v from item left join iconfig on iconfig.iid = item.id where iconfig.cat = 'system' and iconfig.k = 'PDL' and item.uid = %d and item_type = %d ", intval($channel_id), intval(ITEM_TYPE_PDL) @@ -1817,12 +1803,12 @@ function mimetype_select($channel_id, $current = 'text/bbcode') { ); if($r) { - if(($r[0]['account_roles'] & ACCOUNT_ROLE_ALLOWCODE) || ($r[0]['channel_pageflags'] & PAGE_ALLOWCODE)) { + if(($r[0]['account_roles'] & ACCOUNT_ROLE_ALLOWCODE) || ($r[0]['channel_pageflags'] & PAGE_ALLOWCODE)) { if(local_channel() && get_account_id() == $r[0]['account_id']) { $x[] = 'application/x-php'; } } - } + } } foreach($x as $y) { @@ -1837,42 +1823,6 @@ function mimetype_select($channel_id, $current = 'text/bbcode') { return $o; } - -function lang_selector() { - - $langs = glob('view/*/hstrings.php'); - - $lang_options = array(); - $selected = ""; - - if(is_array($langs) && count($langs)) { - $langs[] = ''; - if(! in_array('view/en/hstrings.php',$langs)) - $langs[] = 'view/en/'; - asort($langs); - foreach($langs as $l) { - if($l == '') { - $lang_options[""] = t('default'); - continue; - } - $ll = substr($l,5); - $ll = substr($ll,0,strrpos($ll,'/')); - $selected = (($ll === App::$language && (x($_SESSION, 'language'))) ? $ll : $selected); - $lang_options[$ll] = get_language_name($ll, $ll) . " ($ll)"; - } - } - - $tpl = get_markup_template("lang_selector.tpl"); - $o = replace_macros($tpl, array( - '$title' => t('Select an alternate language'), - '$langs' => array($lang_options, $selected), - - )); - - return $o; -} - - function engr_units_to_bytes ($size_str) { if(! $size_str) return $size_str; @@ -1922,9 +1872,8 @@ function base64special_decode($s) { return base64_decode(strtr($s,',.','+/')); } - /** - * @ Return a div to clear floats. + * @brief Return a div to clear floats. * * @return string */ @@ -2100,7 +2049,7 @@ function ids_to_array($arr,$idx = 'id') { $t = array(); if($arr) { foreach($arr as $x) { - if(array_key_exists($idx,$x) && strlen($x[$idx]) && (! in_array($x[$idx],$t))) { + if(array_key_exists($idx,$x) && strlen($x[$idx]) && (! in_array($x[$idx],$t))) { $t[] = $x[$idx]; } } @@ -2111,23 +2060,33 @@ function ids_to_array($arr,$idx = 'id') { -function ids_to_querystr($arr,$idx = 'id') { +function ids_to_querystr($arr,$idx = 'id',$quote = false) { $t = array(); if($arr) { foreach($arr as $x) { if(! in_array($x[$idx],$t)) { - $t[] = $x[$idx]; + if($quote) + $t[] = "'" . dbesc($x[$idx]) . "'"; + else + $t[] = $x[$idx]; } } } return(implode(',', $t)); } -// Fetches xchan and hubloc data for an array of items with only an -// author_xchan and owner_xchan. If $abook is true also include the abook info. -// This is needed in the API to save extra per item lookups there. - -function xchan_query(&$items,$abook = true,$effective_uid = 0) { +/** + * @brief Fetches xchan and hubloc data for an array of items with only an + * author_xchan and owner_xchan. + * + * If $abook is true also include the abook info. This is needed in the API to + * save extra per item lookups there. + * + * @param array[in,out] &$items + * @param boolean $abook If true also include the abook info + * @param number $effective_uid + */ +function xchan_query(&$items, $abook = true, $effective_uid = 0) { $arr = array(); if($items && count($items)) { @@ -2160,7 +2119,7 @@ function xchan_query(&$items,$abook = true,$effective_uid = 0) { if(! $chans) $chans = $xchans; else - $chans = array_merge($xchans,$chans); + $chans = array_merge($xchans,$chans); } if($items && count($items) && $chans && count($chans)) { for($x = 0; $x < count($items); $x ++) { @@ -2218,15 +2177,19 @@ function magic_link($s) { return $s; } -// if $escape is true, dbesc() each element before adding quotes - -function stringify_array_elms(&$arr,$escape = false) { +/** + * if $escape is true, dbesc() each element before adding quotes + * + * @param array[in,out] &$arr + * @param boolean $escape default false + */ +function stringify_array_elms(&$arr, $escape = false) { for($x = 0; $x < count($arr); $x ++) $arr[$x] = "'" . (($escape) ? dbesc($arr[$x]) : $arr[$x]) . "'"; } /** - * Indents a flat JSON string to make it more human-readable. + * @brief Indents a flat JSON string to make it more human-readable. * * @param string $json The original JSON string to process. * @@ -2250,7 +2213,7 @@ function jindent($json) { if ($char == '"' && $prevChar != '\\') { $outOfQuotes = !$outOfQuotes; - // If this character is the end of an element, + // If this character is the end of an element, // output a new line and indent the next line. } else if(($char == '}' || $char == ']') && $outOfQuotes) { $result .= $newLine; @@ -2263,7 +2226,7 @@ function jindent($json) { // Add the character to the result string. $result .= $char; - // If the last character was the beginning of an element, + // If the last character was the beginning of an element, // output a new line and indent the next line. if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) { $result .= $newLine; @@ -2275,7 +2238,7 @@ function jindent($json) { $result .= $indentStr; } } - + $prevChar = $char; } @@ -2285,9 +2248,8 @@ function jindent($json) { /** * @brief Creates navigation menu for webpage, layout, blocks, menu sites. * - * @return string + * @return string with parsed HTML */ - function design_tools() { $channel = App::get_channel(); @@ -2336,7 +2298,7 @@ function website_portation_tools() { '$file_import_text' => t('Import from cloud files:'), '$desc' => t('/cloud/channel/path/to/folder'), '$hint' => t('Enter path to website files'), - '$select' => t('Select folder'), + '$select' => t('Select folder'), '$export_label' => t('Export website...'), '$file_download_text' => t('Export to a zip file'), '$filename_desc' => t('website.zip'), @@ -2344,12 +2306,17 @@ function website_portation_tools() { '$cloud_export_text' => t('Export to cloud files'), '$cloud_export_desc' => t('/path/to/export/folder'), '$cloud_export_hint' => t('Enter a path to a cloud files destination.'), - '$cloud_export_select' => t('Specify folder'), + '$cloud_export_select' => t('Specify folder'), )); } -/* case insensitive in_array() */ - +/** + * @brief case insensitive in_array() + * + * @param string $needle + * @param array $haystack + * @return boolean + */ function in_arrayi($needle, $haystack) { return in_array(strtolower($needle), array_map('strtolower', $haystack)); } @@ -2358,10 +2325,13 @@ function normalise_openid($s) { return trim(str_replace(array('http://','https://'),array('',''),$s),'/'); } -// used in ajax endless scroll request to find out all the args that the master page was viewing. -// This was using $_REQUEST, but $_REQUEST also contains all your cookies. So we're restricting it -// to $_GET and $_POST. - +/** + * Used in ajax endless scroll request to find out all the args that the master page was viewing. + * This was using $_REQUEST, but $_REQUEST also contains all your cookies. So we're restricting it + * to $_GET and $_POST. + * + * @return string with additional URL parameters + */ function extra_query_args() { $s = ''; if(count($_GET)) { @@ -2380,6 +2350,7 @@ function extra_query_args() { } } } + return $s; } @@ -2406,7 +2377,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d $termtype = ((strpos($tag,'@') === 0) ? TERM_MENTION : $termtype); $termtype = ((strpos($tag,'#^[') === 0) ? TERM_BOOKMARK : $termtype); - //is it a hash tag? + //is it a hash tag? if(strpos($tag,'#') === 0) { if(strpos($tag,'#^[') === 0) { if(preg_match('/#\^\[(url|zrl)(.*?)\](.*?)\[\/(url|zrl)\]/',$tag,$match)) { @@ -2421,7 +2392,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d return $replaced; } if($tag == '#getzot') { - $basetag = 'getzot'; + $basetag = 'getzot'; $url = 'http://hubzilla.org'; $newtag = '#[zrl=' . $url . ']' . $basetag . '[/zrl]'; $body = str_replace($tag,$newtag,$body); @@ -2457,15 +2428,15 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d $str_tags .= $newtag; } return [ - 'replaced' => $replaced, - 'termtype' => $termtype, - 'term' => $basetag, - 'url' => $url, + 'replaced' => $replaced, + 'termtype' => $termtype, + 'term' => $basetag, + 'url' => $url, 'contact' => $r[0] - ]; + ]; } - //is it a person tag? + //is it a person tag? if(strpos($tag,'@') === 0) { @@ -2479,7 +2450,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d //get the person's name $name = substr($tag,(($exclusive) ? 2 : 1)); // The name or name fragment we are going to replace - $newname = $name; // a copy that we can mess with + $newname = $name; // a copy that we can mess with $tagcid = 0; $r = null; @@ -2525,14 +2496,14 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d if($abook_id) { // if there was an id // select channel with that id from the logged in user's address book - $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash + $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_id = %d AND abook_channel = %d LIMIT 1", intval($abook_id), intval($profile_uid) ); } else { - $r = q("SELECT * FROM xchan + $r = q("SELECT * FROM xchan WHERE xchan_hash like '%s%%' LIMIT 1", dbesc($tagcid) ); @@ -2561,7 +2532,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d } //select someone from this user's contacts by name - $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash + $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash WHERE xchan_name = '%s' AND abook_channel = %d LIMIT 1", dbesc($newname), intval($profile_uid) @@ -2569,7 +2540,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d if(! $r) { //select someone by attag or nick and the name passed in - $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash + $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash WHERE xchan_addr like ('%s') AND abook_channel = %d LIMIT 1", dbesc(((strpos($newname,'@')) ? $newname : $newname . '@%')), intval($profile_uid) @@ -2580,7 +2551,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d // it's possible somebody has a name ending with '+', which we stripped off as a forum indicator // This is very rare but we want to get it right. - $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash + $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash WHERE xchan_name = '%s' AND abook_channel = %d LIMIT 1", dbesc($newname . '+'), intval($profile_uid) @@ -2604,12 +2575,12 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d } else { - // check for a group/collection exclusion tag + // check for a group/collection exclusion tag // note that we aren't setting $replaced even though we're replacing text. // This tag isn't going to get a term attached to it. It's only used for // access control. The link points to out own channel just so it doesn't look - // weird - as all the other tags are linked to something. + // weird - as all the other tags are linked to something. if(local_channel() && local_channel() == $profile_uid) { require_once('include/group.php'); @@ -2654,10 +2625,10 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d } return [ - 'replaced' => $replaced, - 'termtype' => $termtype, - 'term' => $newname, - 'url' => $url, + 'replaced' => $replaced, + 'termtype' => $termtype, + 'term' => $newname, + 'url' => $url, 'contact' => $r[0] ]; } @@ -2686,7 +2657,7 @@ function linkify_tags($a, &$body, $uid, $diaspora = false) { if($fullnametagged) continue; - $success = handle_tag($a, $body, $access_tag, $str_tags, ($uid) ? $uid : App::$profile_uid , $tag, $diaspora); + $success = handle_tag($a, $body, $access_tag, $str_tags, ($uid) ? $uid : App::$profile_uid , $tag, $diaspora); $results[] = array('success' => $success, 'access_tag' => $access_tag); if($success['replaced']) $tagged[] = $tag; } @@ -2807,10 +2778,10 @@ function json_url_replace($old,$new,&$s) { $s = $x; return $replaced; } - + function item_url_replace($channel,&$item,$old,$new,$oldnick = '') { - + if($item['attach']) { json_url_replace($old,$new,$item['attach']); if($oldnick) @@ -2831,7 +2802,7 @@ function item_url_replace($channel,&$item,$old,$new,$oldnick = '') { $item['sig'] = base64url_encode(rsa_sign($item['body'],$channel['channel_prvkey'])); $item['item_verified'] = 1; } - + $item['plink'] = str_replace($old,$new,$item['plink']); if($oldnick) $item['plink'] = str_replace('/' . $oldnick . '/' ,'/' . $channel['channel_address'] . '/' ,$item['plink']); @@ -2839,7 +2810,7 @@ function item_url_replace($channel,&$item,$old,$new,$oldnick = '') { $item['llink'] = str_replace($old,$new,$item['llink']); if($oldnick) $item['llink'] = str_replace('/' . $oldnick . '/' ,'/' . $channel['channel_address'] . '/' ,$item['llink']); - + } @@ -2877,7 +2848,6 @@ function perms2str($p) { return $ret; } - /** * @brief Turn user/group ACLs stored as angle bracketed text into arrays. * @@ -2905,35 +2875,41 @@ function expand_acl($s) { function acl2json($s) { $s = expand_acl($s); $s = json_encode($s); + return $s; } - -// When editing a webpage - a dropdown is needed to select a page layout -// On submit, the pdl_select value (which is the mid of an item with item_type = ITEM_TYPE_PDL) is stored in -// the webpage's resource_id, with resource_type 'pdl'. - -// Then when displaying a webpage, we can see if it has a pdl attached. If not we'll -// use the default site/page layout. - -// If it has a pdl we'll load it as we know the mid and pass the body through comanche_parser() which will generate the -// page layout from the given description - -// @FIXME - there is apparently a very similar function called layout_select; this one should probably take precedence -// and the other should be checked for compatibility and removed - -function pdl_selector($uid, $current="") { +/** + * @brief When editing a webpage - a dropdown is needed to select a page layout + * + * On submit, the pdl_select value (which is the mid of an item with item_type = ITEM_TYPE_PDL) + * is stored in the webpage's resource_id, with resource_type 'pdl'. + * + * Then when displaying a webpage, we can see if it has a pdl attached. If not we'll + * use the default site/page layout. + * + * If it has a pdl we'll load it as we know the mid and pass the body through comanche_parser() which will generate the + * page layout from the given description + * + * @FIXME - there is apparently a very similar function called layout_select; this one should probably take precedence + * and the other should be checked for compatibility and removed + * + * @param int $uid + * @param string $current + * @return string HTML code for dropdown + */ +function pdl_selector($uid, $current='') { $o = ''; $sql_extra = item_permissions_sql($uid); - $r = q("select iconfig.*, mid from item_id left join item on iconfig.iid = item.id + $r = q("select iconfig.*, mid from item_id left join item on iconfig.iid = item.id where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'PDL' $sql_extra order by v asc", intval($uid) ); $arr = array('channel_id' => $uid, 'current' => $current, 'entries' => $r); - call_hooks('pdl_selector',$arr); + call_hooks('pdl_selector', $arr); $entries = $arr['entries']; $current = $arr['current']; @@ -2949,16 +2925,17 @@ function pdl_selector($uid, $current="") { return $o; } -/* - * array flatten_array_recursive(array); - * returns a one-dimensional array from a multi-dimensional array +/** + * @brief returns a one-dimensional array from a multi-dimensional array * empty values are discarded + * * example: print_r(flatten_array_recursive(array('foo','bar',array('baz','blip',array('zob','glob')),'','grip'))); * - * Array ( [0] => foo [1] => bar [2] => baz [3] => blip [4] => zob [5] => glob [6] => grip ) + * Array ( [0] => foo [1] => bar [2] => baz [3] => blip [4] => zob [5] => glob [6] => grip ) * + * @param array $arr multi-dimensional array + * @return one-dimensional array */ - function flatten_array_recursive($arr) { $ret = array(); @@ -2969,64 +2946,71 @@ function flatten_array_recursive($arr) { if(is_array($a)) { $tmp = flatten_array_recursive($a); if($tmp) { - $ret = array_merge($ret,$tmp); + $ret = array_merge($ret, $tmp); } } elseif($a) { $ret[] = $a; } } + return($ret); -} +} -function text_highlight($s,$lang) { +/** + * @brief Highlight Text. + * + * @param string $s Text to highlight + * @param string $lang Which language should be highlighted + * @return string + */ +function text_highlight($s, $lang) { if($lang === 'js') $lang = 'javascript'; if($lang === 'json') { $lang = 'javascript'; - if(! strpos(trim($s),"\n")) + if(! strpos(trim($s), "\n")) $s = jindent($s); } - if(! strpos('Text_Highlighter',get_include_path())) { + if(! strpos('Text_Highlighter', get_include_path())) { set_include_path(get_include_path() . PATH_SEPARATOR . 'library/Text_Highlighter'); } require_once('library/Text_Highlighter/Text/Highlighter.php'); - require_once('library/Text_Highlighter/Text/Highlighter/Renderer/Html.php'); - $options = array( - 'numbers' => HL_NUMBERS_LI, - 'tabsize' => 4, - ); + require_once('library/Text_Highlighter/Text/Highlighter/Renderer/Html.php'); + $options = array( + 'numbers' => HL_NUMBERS_LI, + 'tabsize' => 4, + ); $tag_added = false; - $s = trim(html_entity_decode($s,ENT_COMPAT)); - $s = str_replace(" ","\t",$s); + $s = trim(html_entity_decode($s, ENT_COMPAT)); + $s = str_replace(" ", "\t", $s); - // The highlighter library insists on an opening php tag for php code blocks. If + // The highlighter library insists on an opening php tag for php code blocks. If // it isn't present, nothing is highlighted. So we're going to see if it's present. - // If not, we'll add it, and then quietly remove it after we get the processed output back. + // If not, we'll add it, and then quietly remove it after we get the processed output back. if($lang === 'php') { - if(strpos('<?php',$s) !== 0) { + if(strpos('<?php', $s) !== 0) { $s = '<?php' . "\n" . $s; - $tag_added = true; + $tag_added = true; } - - } - $renderer = new Text_Highlighter_Renderer_HTML($options); - $hl = Text_Highlighter::factory($lang); - $hl->setRenderer($renderer); + } + $renderer = new Text_Highlighter_Renderer_HTML($options); + $hl = Text_Highlighter::factory($lang); + $hl->setRenderer($renderer); $o = $hl->highlight($s); - $o = str_replace([" ","\n"],[" ",''],$o); + $o = str_replace([" ", "\n"], [" ", ''], $o); if($tag_added) { - $b = substr($o,0,strpos($o,'<li>')); - $e = substr($o,strpos($o,'</li>')); + $b = substr($o, 0, strpos($o, '<li>')); + $e = substr($o, strpos($o, '</li>')); $o = $b . $e; } - return('<code>' . $o . '</code>'); + return('<code>' . $o . '</code>'); } // function to convert multi-dimensional array to xml @@ -3041,26 +3025,40 @@ function text_highlight($s,$lang) { // echo (($xml->asXML('data.xml')) ? 'Your XML file has been generated successfully!' : 'Error generating XML file!'); function arrtoxml($root_elem,$arr) { - $xml = new SimpleXMLElement('<' . $root_elem . '/>'); + $xml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><' . $root_elem . '></' . $root_elem . '>', null, false); array2XML($xml,$arr); + return $xml->asXML(); } -function array2XML($obj, $array) -{ - foreach ($array as $key => $value) - { - if(is_numeric($key)) - $key = 'item' . $key; - - if (is_array($value)) - { - $node = $obj->addChild($key); - array2XML($node, $value); - } - else - { - $obj->addChild($key, htmlspecialchars($value)); - } - } +function array2XML($obj, $array) { + foreach ($array as $key => $value) { + if(is_numeric($key)) + $key = 'item' . $key; + + if(is_array($value)) { + $node = $obj->addChild($key); + array2XML($node, $value); + } else { + $obj->addChild($key, htmlspecialchars($value)); + } + } } + + +function create_table_from_array($table, $arr) { + + if(! ($arr && $table)) + return false; + + if(dbesc_array($arr)) { + $r = dbq("INSERT INTO " . TQUOT . $table . TQUOT . " (" . TQUOT + . implode(TQUOT . ', ' . TQUOT, array_keys($arr)) + . TQUOT . ") VALUES ('" + . implode("', '", array_values($arr)) + . "')" + ); + } + + return $r; +}
\ No newline at end of file diff --git a/include/widgets.php b/include/widgets.php index 2e1e58717..dde8e7cd0 100644 --- a/include/widgets.php +++ b/include/widgets.php @@ -126,7 +126,7 @@ function widget_suggestions($arr) { // Get two random entries from the top 20 returned. // We'll grab the first one and the one immediately following. - // This will throw some entropy intot he situation so you won't + // This will throw some entropy intot he situation so you won't // be looking at the same two mug shots every time the widget runs $index = ((count($r) > 2) ? mt_rand(0,count($r) - 2) : 0); @@ -169,7 +169,7 @@ function widget_follow($args) { intval($uid) ); if($r) - $total_channels = $r[0]['total']; + $total_channels = $r[0]['total']; $limit = service_class_fetch($uid,'total_channels'); if($limit !== false) { $abook_usage_message = sprintf( t("You have %1$.0f of %2$.0f allowed connections."), $total_channels, $limit); @@ -212,15 +212,15 @@ function widget_savedsearch($arr) { $search = ((x($_GET,'netsearch')) ? $_GET['netsearch'] : ''); if(! $search) $search = ((x($_GET,'search')) ? $_GET['search'] : ''); - + if(x($_GET,'searchsave') && $search) { - $r = q("select * from `term` where `uid` = %d and `ttype` = %d and `term` = '%s' limit 1", + $r = q("select * from term where uid = %d and ttype = %d and term = '%s' limit 1", intval(local_channel()), intval(TERM_SAVEDSEARCH), dbesc($search) ); if(! $r) { - q("insert into `term` ( `uid`,`ttype`,`term` ) values ( %d, %d, '%s') ", + q("insert into term ( uid,ttype,term ) values ( %d, %d, '%s') ", intval(local_channel()), intval(TERM_SAVEDSEARCH), dbesc($search) @@ -229,7 +229,7 @@ function widget_savedsearch($arr) { } if(x($_GET,'searchremove') && $search) { - q("delete from `term` where `uid` = %d and `ttype` = %d and `term` = '%s'", + q("delete from term where uid = %d and ttype = %d and term = '%s'", intval(local_channel()), intval(TERM_SAVEDSEARCH), dbesc($search) @@ -252,11 +252,11 @@ function widget_savedsearch($arr) { $hasamp = ((strpos($srchurl,'&') !== false) ? true : false); if(($hasamp) && (! $hasq)) - $srchurl = substr($srchurl,0,strpos($srchurl,'&')) . '?f=&' . substr($srchurl,strpos($srchurl,'&')+1); + $srchurl = substr($srchurl,0,strpos($srchurl,'&')) . '?f=&' . substr($srchurl,strpos($srchurl,'&')+1); $o = ''; - $r = q("select `tid`,`term` from `term` WHERE `uid` = %d and `ttype` = %d ", + $r = q("select tid,term from term WHERE uid = %d and ttype = %d ", intval(local_channel()), intval(TERM_SAVEDSEARCH) ); @@ -292,7 +292,7 @@ function widget_savedsearch($arr) { function widget_sitesearch($arr) { $search = ((x($_GET,'search')) ? $_GET['search'] : ''); - + $srchurl = App::$query_string; $srchurl = rtrim(preg_replace('/search\=[^\&].*?(\&|$)/is','',$srchurl),'&'); @@ -304,7 +304,7 @@ function widget_sitesearch($arr) { $hasamp = ((strpos($srchurl,'&') !== false) ? true : false); if(($hasamp) && (! $hasq)) - $srchurl = substr($srchurl,0,strpos($srchurl,'&')) . '?f=&' . substr($srchurl,strpos($srchurl,'&')+1); + $srchurl = substr($srchurl,0,strpos($srchurl,'&')) . '?f=&' . substr($srchurl,strpos($srchurl,'&')+1); $o = ''; @@ -554,7 +554,7 @@ function widget_affinity($arr) { )); $arr = array('html' => $x); call_hooks('main_slider',$arr); - return $arr['html']; + return $arr['html']; } return ''; @@ -581,11 +581,11 @@ function widget_settings_menu($arr) { if($abk) $abook_self_id = $abk[0]['abook_id']; - $hublocs = q("select count(*) as total from hubloc where hubloc_hash = '%s'", + $x = q("select count(*) as total from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0 ", dbesc($channel['channel_hash']) ); - $hublocs = (($hublocs[0]['total'] > 1) ? true : false); + $hublocs = (($x && $x[0]['total'] > 1) ? true : false); $tabs = array( array( @@ -778,7 +778,7 @@ function widget_conversations($arr) { '$messages' => $messages )); - $o .= alt_pager($a,count($r)); + //$o .= alt_pager($a,count($r)); } @@ -802,10 +802,10 @@ function widget_design_tools($arr) { // mod menu doesn't load a profile. For any modules which load a profile, check it. // otherwise local_channel() is sufficient for permissions. - if(App::$profile['profile_uid']) + if(App::$profile['profile_uid']) if((App::$profile['profile_uid'] != local_channel()) && (! App::$is_sys)) return ''; - + if(! local_channel()) return ''; @@ -817,10 +817,10 @@ function widget_website_portation_tools($arr) { // mod menu doesn't load a profile. For any modules which load a profile, check it. // otherwise local_channel() is sufficient for permissions. - if(App::$profile['profile_uid']) + if(App::$profile['profile_uid']) if((App::$profile['profile_uid'] != local_channel()) && (! App::$is_sys)) return ''; - + if(! local_channel()) return ''; @@ -841,7 +841,7 @@ function widget_photo_albums($arr) { return ''; require_once('include/photos.php'); $sortkey = ((array_key_exists('sortkey',$arr)) ? $arr['sortkey'] : 'album'); - $direction = ((array_key_exists('direction',$arr)) ? $arr['direction'] : 'asc'); + $direction = ((array_key_exists('direction',$arr)) ? $arr['direction'] : 'asc'); return photos_album_widget($channelx, App::get_observer(),$sortkey,$direction); } @@ -898,26 +898,14 @@ function widget_chatroom_members() { } function widget_wiki_list($arr) { - require_once("include/wiki.php"); - $channel = null; - if (argc() < 2 && local_channel()) { - // This should not occur because /wiki should redirect to /wiki/channel ... - $channel = \App::get_channel(); - } else { - $channel = get_channel_by_nick(argv(1)); // Channel being viewed by observer - } - if (!$channel) { - return ''; - } + $channel = channelx_by_n(App::$profile_uid); $wikis = wiki_list($channel, get_observer_hash()); - if ($wikis) { - return replace_macros(get_markup_template('wikilist.tpl'), array( + if($wikis) { + return replace_macros(get_markup_template('wikilist_widget.tpl'), array( '$header' => t('Wiki List'), '$channel' => $channel['channel_address'], - '$wikis' => $wikis['wikis'], - // If the observer is the local channel owner, show the wiki controls - '$showControls' => ((local_channel() === intval($channel['channel_id'])) ? true : false) + '$wikis' => $wikis['wikis'] )); } return ''; @@ -948,13 +936,18 @@ function widget_wiki_pages($arr) { } } } + $can_create = perm_is_allowed(\App::$profile['uid'],get_observer_hash(),'write_pages'); + return replace_macros(get_markup_template('wiki_page_list.tpl'), array( '$hide' => $hide, '$not_refresh' => $not_refresh, '$header' => t('Wiki Pages'), '$channel' => $channelname, '$wikiname' => $wikiname, - '$pages' => $pages + '$pages' => $pages, + '$canadd' => $can_create, + '$addnew' => t('Add new page'), + '$pageName' => array('pageName', t('Page name')), )); } @@ -965,7 +958,8 @@ function widget_wiki_page_history($arr) { $pageHistory = wiki_page_history(array('resource_id' => $resource_id, 'pageUrlName' => $pageUrlName)); return replace_macros(get_markup_template('wiki_page_history.tpl'), array( - '$pageHistory' => $pageHistory['history'] + '$pageHistory' => $pageHistory['history'], + '$permsWrite' => $arr['permsWrite'] )); } @@ -996,9 +990,9 @@ function widget_suggestedchats($arr) { if(! feature_enabled(App::$profile['profile_uid'],'ajaxchat')) return ''; - // There are reports that this tool does not ever remove chatrooms on dead sites, + // There are reports that this tool does not ever remove chatrooms on dead sites, // and also will happily link to private chats which you cannot enter. - // For those reasons, it will be disabled until somebody decides it's worth + // For those reasons, it will be disabled until somebody decides it's worth // fixing and comes up with a plan for doing so. return ''; @@ -1044,7 +1038,7 @@ function widget_item($arr) { if($arr['title']) { $r = q("select item.* from item left join iconfig on item.id = iconfig.iid - where item.uid = %d and iconfig.cat = 'system' and iconfig.v = '%s' + where item.uid = %d and iconfig.cat = 'system' and iconfig.v = '%s' and iconfig.k = 'WEBPAGE' and item_type = %d $sql_options $revision limit 1", intval($channel_id), dbesc($arr['title']), @@ -1108,7 +1102,7 @@ function showtime(){ // timeValue += ((seconds < 10) ? ":0" : ":") + seconds if(! military) timeValue += (hours >= 12) ? " P.M." : " A.M." - $('.clockface').html(timeValue) + $('.clockface').html(timeValue) timerID = setTimeout("showtime()",1000) timerRunning = true } @@ -1124,17 +1118,16 @@ return $o; } - /** - * @function widget_photo($arr) - * widget to display a single photo. - * @param array $arr; - * 'src' => URL of photo - * 'zrl' => true or false, use zid in url - * 'style' => CSS string - * URL must be an http or https URL + * @brief Widget to display a single photo. + * + * @param array $arr associative array with + * * \e string \b src URL of photo; URL must be an http or https URL + * * \e boolean \b zrl use zid in URL + * * \e string \b style CSS string + * + * @return string with parsed HTML */ - function widget_photo($arr) { $style = $zrl = false; @@ -1142,7 +1135,7 @@ function widget_photo($arr) { if(array_key_exists('src', $arr) && isset($arr['src'])) $url = $arr['src']; - if(strpos($url,'http') !== 0) + if(strpos($url, 'http') !== 0) return ''; if(array_key_exists('style', $arr) && isset($arr['style'])) @@ -1150,7 +1143,7 @@ function widget_photo($arr) { // ensure they can't sneak in an eval(js) function - if(strpbrk($style,'(\'"<>') !== false) + if(strpbrk($style, '(\'"<>') !== false) $style = ''; if(array_key_exists('zrl', $arr) && isset($arr['zrl'])) @@ -1161,8 +1154,8 @@ function widget_photo($arr) { $o = '<div class="widget">'; - $o .= '<img ' . (($zrl) ? ' class="zrl" ' : '') - . (($style) ? ' style="' . $style . '"' : '') + $o .= '<img ' . (($zrl) ? ' class="zrl" ' : '') + . (($style) ? ' style="' . $style . '"' : '') . ' src="' . $url . '" alt="' . t('photo/image') . '">'; $o .= '</div>'; @@ -1175,7 +1168,7 @@ function widget_cover_photo($arr) { require_once('include/channel.php'); $o = ''; - + if(App::$module == 'channel' && $_REQUEST['mid']) return ''; @@ -1191,7 +1184,7 @@ function widget_cover_photo($arr) { if(array_key_exists('style', $arr) && isset($arr['style'])) $style = $arr['style']; - else + else $style = 'width:100%; height: auto;'; // ensure they can't sneak in an eval(js) function @@ -1273,8 +1266,8 @@ function widget_photo_rand($arr) { $o = '<div class="widget">'; - $o .= '<img class="zrl" ' - . (($style) ? ' style="' . $style . '"' : '') + $o .= '<img class="zrl" ' + . (($style) ? ' style="' . $style . '"' : '') . ' src="' . $url . '" alt="' . t('photo/image') . '">'; $o .= '</div>'; @@ -1304,7 +1297,7 @@ function widget_random_block($arr) { $randfunc = db_getfunc('RAND'); $r = q("select item.* from item left join iconfig on item.id = iconfig.iid - where item.uid = %d and iconfig.cat = 'system' and iconfig.v like '%s' and iconfig.k = 'BUILDBLOCK' and + where item.uid = %d and iconfig.cat = 'system' and iconfig.v like '%s' and iconfig.k = 'BUILDBLOCK' and item_type = %d $sql_options order by $randfunc limit 1", intval($channel_id), dbesc('%' . $contains . '%'), @@ -1411,14 +1404,23 @@ function widget_forums($arr) { $perms_sql = item_permissions_sql(local_channel()) . item_normal(); - /** - * We used to try and find public forums with custom permissions by checking to see if - * send_stream was false and tag_deliver was true. However with the newer extensible - * permissions infrastructure this makes for a very complicated query. Now we're only - * checking channels that report themselves specifically as pubforums - */ + $xf = false; - $r1 = q("select abook_id, xchan_hash, xchan_name, xchan_url, xchan_photo_s from abook left join xchan on abook_xchan = xchan_hash where xchan_pubforum = 1 and xchan_deleted = 0 and abook_channel = %d order by xchan_name $limit ", + $x1 = q("select xchan from abconfig where chan = %d and cat = 'their_perms' and k = 'send_stream' and v = '0'", + intval(local_channel()) + ); + if($x1) { + $xc = ids_to_querystr($x1,'xchan',true); + $x2 = q("select xchan from abconfig where chan = %d and cat = 'their_perms' and k = 'tag_deliver' and v = '1' and xchan in (" . $xc . ") ", + intval(local_channel()) + ); + if($x2) + $xf = ids_to_querystr($x2,'xchan',true); + } + + $sql_extra = (($xf) ? " and ( xchan_hash in (" . $xf . ") or xchan_pubforum = 1 ) " : " and xchan_pubforum = 1 "); + + $r1 = q("select abook_id, xchan_hash, xchan_name, xchan_url, xchan_photo_s from abook left join xchan on abook_xchan = xchan_hash where xchan_deleted = 0 and abook_channel = %d $sql_extra order by xchan_name $limit ", intval(local_channel()) ); if(! $r1) @@ -1440,11 +1442,11 @@ function widget_forums($arr) { /** * @FIXME * This SQL makes the counts correct when you get forum posts arriving from different routes/sources - * (like personal channels). However the network query for these posts doesn't yet include this - * correction and it makes the SQL for that query pretty hairy so this is left as a future exercise. + * (like personal channels). However the network query for these posts doesn't yet include this + * correction and it makes the SQL for that query pretty hairy so this is left as a future exercise. * It may make more sense in that query to look for the mention in the body rather than another join, * but that makes it very inefficient. - * + * $r = q("select sum(item_unseen) as unseen from item left join term on oid = id where otype = %d and owner_xchan != '%s' and item.uid = %d and url = '%s' and ttype = %d $perms_sql ", intval(TERM_OBJ_POST), dbesc($r1[$x]['xchan_hash']), @@ -1457,7 +1459,7 @@ function widget_forums($arr) { * * end @FIXME */ - + } if($r1) { @@ -1471,7 +1473,7 @@ function widget_forums($arr) { } $o .= '</ul></div>'; } - return $o; + return $o; } @@ -1489,7 +1491,7 @@ function widget_tasklist($arr) { $(".tasklist-tasks").html(data.html); }); } - </script>'; + </script>'; $o .= '<div class="widget">' . '<h3>' . t('Tasks') . '</h3><div class="tasklist-tasks">'; $o .= '</div><form id="tasklist-new-form" action="" ><input id="tasklist-new-summary" type="text" name="summary" value="" /></form>'; @@ -1502,7 +1504,6 @@ function widget_tasklist($arr) { function widget_helpindex($arr) { $o .= '<div class="widget">'; - $o .= '<h3>' . t('Documentation') . '</h3>'; $level_0 = get_help_content('sitetoc'); if(! $level_0) @@ -1516,7 +1517,7 @@ function widget_helpindex($arr) { if(argc() > 2) { $path = ''; for($x = 1; $x < argc(); $x ++) { - $path .= argv($x) . '/'; + $path .= argv($x) . '/'; $y = get_help_content($path . 'sitetoc'); if(! $y) $y = get_help_content($path . 'toc'); @@ -1590,7 +1591,7 @@ function widget_admin($arr) { call_hooks('admin_aside',$arr); $o .= replace_macros(get_markup_template('admin_aside.tpl'), array( - '$admin' => $aside, + '$admin' => $aside, '$admtxt' => t('Admin'), '$plugadmtxt' => t('Plugin Features'), '$plugins' => $plugins, @@ -1620,7 +1621,7 @@ function widget_album($args) { if($args['title']) $title = $args['title']; - /** + /** * This may return incorrect permissions if you have multiple directories of the same name. * It is a limitation of the photo table using a name for a photo album instead of a folder hash */ @@ -1640,7 +1641,7 @@ function widget_album($args) { $order = 'DESC'; $r = q("SELECT p.resource_id, p.id, p.filename, p.mimetype, p.imgscale, p.description, p.created FROM photo p INNER JOIN - (SELECT resource_id, max(imgscale) imgscale FROM photo WHERE uid = %d AND album = '%s' AND imgscale <= 4 AND photo_usage IN ( %d, %d ) $sql_extra GROUP BY resource_id) ph + (SELECT resource_id, max(imgscale) imgscale FROM photo WHERE uid = %d AND album = '%s' AND imgscale <= 4 AND photo_usage IN ( %d, %d ) $sql_extra GROUP BY resource_id) ph ON (p.resource_id = ph.resource_id AND p.imgscale = ph.imgscale) ORDER BY created $order ", intval($owner_uid), @@ -1648,7 +1649,7 @@ function widget_album($args) { intval(PHOTO_NORMAL), intval(PHOTO_PROFILE) ); - + //edit album name $album_edit = null; @@ -1661,7 +1662,7 @@ function widget_album($args) { $twist = 'rotleft'; else $twist = 'rotright'; - + $ext = $phototypes[$rr['mimetype']]; $imgalt_e = $rr['filename']; diff --git a/include/wiki.php b/include/wiki.php index d52308b08..332d4efe0 100644 --- a/include/wiki.php +++ b/include/wiki.php @@ -13,11 +13,15 @@ function wiki_list($channel, $observer_hash) { dbesc(WIKI_ITEM_RESOURCE_TYPE), intval($channel['channel_id']) ); - foreach($wikis as &$w) { - $w['rawName'] = get_iconfig($w, 'wiki', 'rawName'); - $w['htmlName'] = get_iconfig($w, 'wiki', 'htmlName'); - $w['urlName'] = get_iconfig($w, 'wiki', 'urlName'); - $w['path'] = get_iconfig($w, 'wiki', 'path'); + if($wikis) { + foreach($wikis as &$w) { + $w['rawName'] = get_iconfig($w, 'wiki', 'rawName'); + $w['htmlName'] = get_iconfig($w, 'wiki', 'htmlName'); + $w['urlName'] = get_iconfig($w, 'wiki', 'urlName'); + $w['path'] = get_iconfig($w, 'wiki', 'path'); + $w['mimeType'] = get_iconfig($w, 'wiki', 'mimeType'); + $w['lock'] = (($w['allow_cid'] || $w['allow_gid'] || $w['deny_cid'] || $w['deny_gid']) ? true : false); + } } // TODO: query db for wikis the observer can access. Return with two lists, for read and write access return array('wikis' => $wikis); @@ -29,14 +33,30 @@ function wiki_page_list($resource_id) { if (!$w['path']) { return array('pages' => null, 'wiki' => null); } - $pages = array(); + + $pages[] = [ + 'resource_id' => '', + 'title' => 'Home', + 'url' => 'Home', + 'link_id' => 'id_wiki_home_0' + ]; + if (is_dir($w['path']) === true) { $files = array_diff(scandir($w['path']), array('.', '..', '.git')); // TODO: Check that the files are all text files - + $i = 1; foreach($files as $file) { - // strip the .md file extension and unwrap URL encoding to leave HTML encoded name - $pages[] = array('title' => urldecode(substr($file, 0, -3)), 'url' => urlencode(substr($file, 0, -3))); + // strip the file extension and unwrap URL encoding to leave HTML encoded name + $title = substr($file, 0, strrpos($file,'.')); + if(urldecode($title) !== 'Home') { + $pages[] = [ + 'resource_id' => $resource_id, + 'title' => urldecode($title), + 'url' => $title, + 'link_id' => 'id_' . substr($resource_id, 0, 10) . '_' . $i + ]; + $i++; + } } } @@ -73,7 +93,7 @@ function wiki_create_wiki($channel, $observer_hash, $wiki, $acl) { $resource_id = random_string(); $r = q("SELECT mid FROM item WHERE resource_id = '%s' AND resource_type = '%s' AND uid = %d LIMIT 1", dbesc($resource_id), - dbesc(WIKI_ITEM_RESOURCE_TYPE), + dbesc(WIKI_ITEM_RESOURCE_TYPE), intval($channel['channel_id']) ); if (count($r)) @@ -121,12 +141,15 @@ function wiki_create_wiki($channel, $observer_hash, $wiki, $acl) { if (!set_iconfig($arr, 'wiki', 'urlName', $wiki['urlName'], true)) { return array('item' => null, 'success' => false); } + if (!set_iconfig($arr, 'wiki', 'mimeType', $wiki['mimeType'], true)) { + return array('item' => null, 'success' => false); + } $post = item_store($arr); $item_id = $post['item_id']; if ($item_id) { - proc_run('php', "include/notifier.php", "activity", $item_id); - return array('item' => $arr, 'success' => true); + \Zotlabs\Daemon\Master::Summon(array('Notifier', 'activity', $item_id)); + return array('item' => $post['item'], 'success' => true); } else { return array('item' => null, 'success' => false); } @@ -150,8 +173,8 @@ function wiki_delete_wiki($resource_id) { function wiki_get_wiki($resource_id) { $item = q("SELECT * FROM item WHERE resource_type = '%s' AND resource_id = '%s' AND item_deleted = 0 limit 1", - dbesc(WIKI_ITEM_RESOURCE_TYPE), - dbesc($resource_id) + dbesc(WIKI_ITEM_RESOURCE_TYPE), + dbesc($resource_id) ); if (!$item) { return array('wiki' => null, 'path' => null); @@ -161,17 +184,21 @@ function wiki_get_wiki($resource_id) { $rawName = get_iconfig($w, 'wiki', 'rawName'); $htmlName = get_iconfig($w, 'wiki', 'htmlName'); $urlName = get_iconfig($w, 'wiki', 'urlName'); + $mimeType = get_iconfig($w, 'wiki', 'mimeType'); + $path = get_iconfig($w, 'wiki', 'path'); if (!realpath(__DIR__ . '/../' . $path)) { return array('wiki' => null, 'path' => null); } // Path to wiki exists $abs_path = realpath(__DIR__ . '/../' . $path); - return array( 'wiki' => $w, - 'path' => $abs_path, - 'rawName' => $rawName, - 'htmlName' => $htmlName, - 'urlName' => $urlName + return array( + 'wiki' => $w, + 'path' => $abs_path, + 'rawName' => $rawName, + 'htmlName' => $htmlName, + 'urlName' => $urlName, + 'mimeType' => $mimeType ); } } @@ -192,22 +219,23 @@ function wiki_exists_by_name($uid, $urlName) { function wiki_get_permissions($resource_id, $owner_id, $observer_hash) { // TODO: For now, only the owner can edit $sql_extra = item_permissions_sql($owner_id, $observer_hash); - $r = q("SELECT * FROM item WHERE resource_type = '%s' AND resource_id = '%s' $sql_extra LIMIT 1", - dbesc(WIKI_ITEM_RESOURCE_TYPE), - dbesc($resource_id) - ); - + + if(local_channel() && local_channel == $owner_id) { + return [ 'read' => true, 'write' => true, 'success' => true ]; + } + + $r = q("SELECT * FROM item WHERE uid = %d and resource_type = '%s' AND resource_id = '%s' $sql_extra LIMIT 1", + intval($owner_id), + dbesc(WIKI_ITEM_RESOURCE_TYPE), + dbesc($resource_id) + ); + if (!$r) { return array('read' => false, 'write' => false, 'success' => true); } else { - $perms = get_all_perms($owner_id, $observer_hash); // TODO: Create a new permission setting for wiki analogous to webpages. Until // then, use webpage permissions - if (!$perms['write_pages']) { - $write = false; - } else { - $write = true; - } + $write = perm_is_allowed($owner_id, $observer_hash,'write_pages'); return array('read' => true, 'write' => $write, 'success' => true); } } @@ -217,7 +245,8 @@ function wiki_create_page($name, $resource_id) { if (!$w['path']) { return array('page' => null, 'wiki' => null, 'message' => 'Wiki not found.', 'success' => false); } - $page = array('rawName' => $name, 'htmlName' => escape_tags($name), 'urlName' => urlencode(escape_tags($name)), 'fileName' => urlencode(escape_tags($name)).'.md'); + + $page = array('rawName' => $name, 'htmlName' => escape_tags($name), 'urlName' => urlencode(escape_tags($name)), 'fileName' => urlencode(escape_tags($name)) . wiki_get_file_ext($w)); $page_path = $w['path'] . '/' . $page['fileName']; if (is_file($page_path)) { return array('page' => null, 'wiki' => null, 'message' => 'Page already exists.', 'success' => false); @@ -239,11 +268,11 @@ function wiki_rename_page($arr) { if (!$w['path']) { return array('message' => 'Wiki not found.', 'success' => false); } - $page_path_old = $w['path'].'/'.$pageUrlName.'.md'; + $page_path_old = $w['path'] . '/' . $pageUrlName . wiki_get_file_ext($w); if (!is_readable($page_path_old) === true) { return array('message' => 'Cannot read wiki page: ' . $page_path_old, 'success' => false); } - $page = array('rawName' => $pageNewName, 'htmlName' => escape_tags($pageNewName), 'urlName' => urlencode(escape_tags($pageNewName)), 'fileName' => urlencode(escape_tags($pageNewName)).'.md'); + $page = array('rawName' => $pageNewName, 'htmlName' => escape_tags($pageNewName), 'urlName' => urlencode(escape_tags($pageNewName)), 'fileName' => urlencode(escape_tags($pageNewName)) . wiki_get_file_ext($w)); $page_path_new = $w['path'] . '/' . $page['fileName'] ; if (is_file($page_path_new)) { return array('message' => 'Page already exists.', 'success' => false); @@ -264,7 +293,7 @@ function wiki_get_page_content($arr) { if (!$w['path']) { return array('content' => null, 'message' => 'Error reading wiki', 'success' => false); } - $page_path = $w['path'].'/'.$pageUrlName.'.md'; + $page_path = $w['path'] . '/' . $pageUrlName . wiki_get_file_ext($w); if (is_readable($page_path) === true) { if(filesize($page_path) === 0) { $content = ''; @@ -275,7 +304,7 @@ function wiki_get_page_content($arr) { } } // TODO: Check that the files are all text files - return array('content' => json_encode($content), 'message' => '', 'success' => true); + return array('content' => json_encode($content), 'mimeType' => $w['mimeType'], 'message' => '', 'success' => true); } } @@ -286,7 +315,7 @@ function wiki_page_history($arr) { if (!$w['path']) { return array('history' => null, 'message' => 'Error reading wiki', 'success' => false); } - $page_path = $w['path'].'/'.$pageUrlName.'.md'; + $page_path = $w['path'] . '/' . $pageUrlName . wiki_get_file_ext($w); if (!is_readable($page_path) === true) { return array('history' => null, 'message' => 'Cannot read wiki page: ' . $page_path, 'success' => false); } @@ -311,12 +340,14 @@ function wiki_save_page($arr) { if (!$w['path']) { return array('message' => 'Error reading wiki', 'success' => false); } - $page_path = $w['path'].'/'.$pageUrlName.'.md'; + + $fileName = $pageUrlName . wiki_get_file_ext($w); + $page_path = $w['path'] . '/' . $fileName; if (is_writable($page_path) === true) { if(!file_put_contents($page_path, $content)) { return array('message' => 'Error writing to page file', 'success' => false); } - return array('message' => '', 'success' => true); + return array('message' => '', 'filename' => $filename, 'success' => true); } else { return array('message' => 'Page file not writable', 'success' => false); } @@ -329,7 +360,7 @@ function wiki_delete_page($arr) { if (!$w['path']) { return array('message' => 'Error reading wiki', 'success' => false); } - $page_path = $w['path'].'/'.$pageUrlName.'.md'; + $page_path = $w['path'] . '/' . $pageUrlName . wiki_get_file_ext($w); if (is_writable($page_path) === true) { if(!unlink($page_path)) { return array('message' => 'Error deleting page file', 'success' => false); @@ -351,7 +382,7 @@ function wiki_revert_page($arr) { if (!$w['path']) { return array('content' => $content, 'message' => 'Error reading wiki', 'success' => false); } - $page_path = $w['path'].'/'.$pageUrlName.'.md'; + $page_path = $w['path'] . '/' . $pageUrlName . wiki_get_file_ext($w); if (is_writable($page_path) === true) { $reponame = ((array_key_exists('title', $w['wiki'])) ? urlencode($w['wiki']['title']) : 'repo'); @@ -363,7 +394,7 @@ function wiki_revert_page($arr) { try { $git->setIdentity($observer['xchan_name'], $observer['xchan_addr']); foreach ($git->git->tree($commitHash) as $object) { - if ($object['type'] == 'blob' && $object['file'] === $pageUrlName.'.md' ) { + if ($object['type'] == 'blob' && $object['file'] === $pageUrlName . wiki_get_file_ext($w)) { $content = $git->git->cat->blob($object['hash']); } } @@ -388,7 +419,7 @@ function wiki_compare_page($arr) { if (!$w['path']) { return array('message' => 'Error reading wiki', 'success' => false); } - $page_path = $w['path'].'/'.$pageUrlName.'.md'; + $page_path = $w['path'] . '/' . $pageUrlName . wiki_get_file_ext($w); if (is_readable($page_path) === true) { $reponame = ((array_key_exists('title', $w['wiki'])) ? urlencode($w['wiki']['title']) : 'repo'); if($reponame === '') { @@ -398,12 +429,12 @@ function wiki_compare_page($arr) { $compareContent = $currentContent = ''; try { foreach ($git->git->tree($currentCommit) as $object) { - if ($object['type'] == 'blob' && $object['file'] === $pageUrlName.'.md' ) { + if ($object['type'] == 'blob' && $object['file'] === $pageUrlName . wiki_get_file_ext($w)) { $currentContent = $git->git->cat->blob($object['hash']); } } foreach ($git->git->tree($compareCommit) as $object) { - if ($object['type'] == 'blob' && $object['file'] === $pageUrlName.'.md' ) { + if ($object['type'] == 'blob' && $object['file'] === $pageUrlName . wiki_get_file_ext($w)) { $compareContent = $git->git->cat->blob($object['hash']); } } @@ -466,15 +497,6 @@ function wiki_git_commit($arr) { } } -function wiki_generate_page_filename($name) { - $file = urlencode(escape_tags($name)); - if( $file === '') { - return null; - } else { - return $file . '.md'; - } -} - function wiki_convert_links($s, $wikiURL) { if (strpos($s,'[[') !== false) { @@ -502,7 +524,6 @@ function wiki_convert_links($s, $wikiURL) { * @return string */ function wiki_generate_toc($s) { - if (strpos($s,'[toc]') !== false) { //$toc_md = wiki_toc($s); // Generate Markdown-formatted list prior to HTML render $toc_md = '<ul id="wiki-toc"></ul>'; // use the available jQuery plugin http://ndabas.github.io/toc/ @@ -544,6 +565,13 @@ function wiki_bbcode($s) { return $s; } +function wiki_get_file_ext($arr) { + if($arr['mimeType'] == 'text/bbcode') + return '.bb'; + else + return '.md'; +} + // This function is derived from // http://stackoverflow.com/questions/32068537/generate-table-of-contents-from-markdown-in-php function wiki_toc($content) { @@ -552,36 +580,36 @@ function wiki_toc($content) { // look for markdown TOC items preg_match_all( - '/^(?:=|-|#).*$/m', - $source, - $matches, - PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE + '/^(?:=|-|#).*$/m', + $source, + $matches, + PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE ); // preprocess: iterate matched lines to create an array of items // where each item is an array(level, text) $file_size = strlen($source); foreach ($matches[0] as $item) { - $found_mark = substr($item[0], 0, 1); - if ($found_mark == '#') { - // text is the found item - $item_text = $item[0]; - $item_level = strrpos($item_text, '#') + 1; - $item_text = substr($item_text, $item_level); - } else { - // text is the previous line (empty if <hr>) - $item_offset = $item[1]; - $prev_line_offset = strrpos($source, "\n", -($file_size - $item_offset + 2)); - $item_text = - substr($source, $prev_line_offset, $item_offset - $prev_line_offset - 1); - $item_text = trim($item_text); - $item_level = $found_mark == '=' ? 1 : 2; - } - if (!trim($item_text) OR strpos($item_text, '|') !== FALSE) { - // item is an horizontal separator or a table header, don't mind - continue; - } - $raw_toc[] = ['level' => $item_level, 'text' => trim($item_text)]; + $found_mark = substr($item[0], 0, 1); + if ($found_mark == '#') { + // text is the found item + $item_text = $item[0]; + $item_level = strrpos($item_text, '#') + 1; + $item_text = substr($item_text, $item_level); + } else { + // text is the previous line (empty if <hr>) + $item_offset = $item[1]; + $prev_line_offset = strrpos($source, "\n", -($file_size - $item_offset + 2)); + $item_text = + substr($source, $prev_line_offset, $item_offset - $prev_line_offset - 1); + $item_text = trim($item_text); + $item_level = $found_mark == '=' ? 1 : 2; + } + if (!trim($item_text) OR strpos($item_text, '|') !== FALSE) { + // item is an horizontal separator or a table header, don't mind + continue; + } + $raw_toc[] = ['level' => $item_level, 'text' => trim($item_text)]; } $o = ''; foreach($raw_toc as $t) { diff --git a/include/zid.php b/include/zid.php new file mode 100644 index 000000000..ac6433e4c --- /dev/null +++ b/include/zid.php @@ -0,0 +1,232 @@ +<?php + + +function is_matrix_url($url) { + + // in-memory cache to avoid repeated queries for the same host + static $remembered = []; + + $m = @parse_url($url); + if($m['host']) { + + if(array_key_exists($m['host'],$remembered)) + return $remembered[$m['host']]; + + $r = q("select hubloc_url from hubloc where hubloc_host = '%s' and hubloc_network = 'zot' limit 1", + dbesc($m['host']) + ); + if($r) { + $remembered[$m['host']] = true; + return true; + } + $remembered[$m['host']] = false; + } + return false; +} + +/** + * @brief Adds a zid parameter to a url. + * + * @param string $s + * The url to accept the zid + * @param boolean $address + * $address to use instead of session environment + * @return string + * + * @hooks 'zid' + * string url - url to accept zid + * string zid - urlencoded zid + * string result - the return string we calculated, change it if you want to return something else + */ + +function zid($s,$address = '') { + if (! strlen($s) || strpos($s,'zid=')) + return $s; + + $m = parse_url($s); + $fragment = ((array_key_exists('fragment',$m) && $m['fragment']) ? $m['fragment'] : false); + if($fragment !== false) + $s = str_replace('#' . $fragment,'',$s); + + $has_params = ((strpos($s,'?')) ? true : false); + $num_slashes = substr_count($s, '/'); + if (! $has_params) + $has_params = ((strpos($s, '&')) ? true : false); + + $achar = strpos($s,'?') ? '&' : '?'; + + $mine = get_my_url(); + $myaddr = (($address) ? $address : get_my_address()); + + /** + * @FIXME checking against our own channel url is no longer reliable. We may have a lot + * of urls attached to out channel. Should probably match against our site, since we + * will not need to remote authenticate on our own site anyway. + */ + + if ($mine && $myaddr && (! link_compare($mine,$s))) + $zurl = $s . (($num_slashes >= 3) ? '' : '/') . $achar . 'zid=' . urlencode($myaddr); + else + $zurl = $s; + + // put fragment at the end + + if($fragment) + $zurl .= '#' . $fragment; + + $arr = array('url' => $s, 'zid' => urlencode($myaddr), 'result' => $zurl); + call_hooks('zid', $arr); + + return $arr['result']; +} + + +function strip_zids($s) { + return preg_replace('/[\?&]zid=(.*?)(&|$)/ism','$2',$s); +} + +function strip_zats($s) { + return preg_replace('/[\?&]zat=(.*?)(&|$)/ism','$2',$s); +} + + +/** + * zidify_callback() and zidify_links() work together to turn any HTML a tags with class="zrl" into zid links + * These will typically be generated by a bbcode '[zrl]' tag. This is done inside prepare_text() rather than bbcode() + * because the latter is used for general purpose conversions and the former is used only when preparing text for + * immediate display. + * + * Issues: Currently the order of HTML parameters in the text is somewhat rigid and inflexible. + * We assume it looks like \<a class="zrl" href="xxxxxxxxxx"\> and will not work if zrl and href appear in a different order. + * + * @param array $match + * @return string + */ +function zidify_callback($match) { + $is_zid = ((feature_enabled(local_channel(),'sendzid')) || (strpos($match[1],'zrl')) ? true : false); + $replace = '<a' . $match[1] . ' href="' . (($is_zid) ? zid($match[2]) : $match[2]) . '"'; + $x = str_replace($match[0],$replace,$match[0]); + + return $x; +} + +function zidify_img_callback($match) { + $is_zid = ((feature_enabled(local_channel(),'sendzid')) || (strpos($match[1],'zrl')) ? true : false); + $replace = '<img' . $match[1] . ' src="' . (($is_zid) ? zid($match[2]) : $match[2]) . '"'; + + $x = str_replace($match[0],$replace,$match[0]); + + return $x; +} + + +function zidify_links($s) { + $s = preg_replace_callback('/\<a(.*?)href\=\"(.*?)\"/ism','zidify_callback',$s); + $s = preg_replace_callback('/\<img(.*?)src\=\"(.*?)\"/ism','zidify_img_callback',$s); + + return $s; +} + + + + +function zidify_text_callback($match) { + $is_zid = is_matrix_url($match[2]); + $replace = '<a' . $match[1] . ' href="' . (($is_zid) ? zid($match[2]) : $match[2]) . '"'; + $x = str_replace($match[0],$replace,$match[0]); + + return $x; +} + +function zidify_text_img_callback($match) { + $is_zid = is_matrix_url($match[2]); + $replace = '<img' . $match[1] . ' src="' . (($is_zid) ? zid($match[2]) : $match[2]) . '"'; + + $x = str_replace($match[0],$replace,$match[0]); + + return $x; +} + +function zidify_text($s) { + + $s = preg_replace_callback('/\<a(.*?)href\=\"(.*?)\"/ism','zidify_text_callback',$s); + $s = preg_replace_callback('/\<img(.*?)src\=\"(.*?)\"/ism','zidify_text_img_callback',$s); + + return $s; + + +} + + +/** + * @brief preg_match function when fixing 'naked' links in mod item.php. + * + * Check if we've got a hubloc for the site and use a zrl if we do, a url if we don't. + * Remove any existing zid= param which may have been pasted by mistake - and will have + * the author's credentials. zid's are dynamic and can't really be passed around like + * that. + * + * @param array $matches + * @return string + */ +function red_zrl_callback($matches) { + require_once('include/hubloc.php'); + $zrl = is_matrix_url($matches[2]); + + $t = strip_zids($matches[2]); + if($t !== $matches[2]) { + $zrl = true; + $matches[2] = $t; + } + + if($matches[1] === '#^') + $matches[1] = ''; + if($zrl) + return $matches[1] . '#^[zrl=' . $matches[2] . ']' . $matches[2] . '[/zrl]'; + + return $matches[1] . '#^[url=' . $matches[2] . ']' . $matches[2] . '[/url]'; +} + +/** + * If we've got a url or zrl tag with a naked url somewhere in the link text, + * escape it with quotes unless the naked url is a linked photo. + * + * @param array $matches + * @return string + */ + +function red_escape_zrl_callback($matches) { + + // Uncertain why the url/zrl forms weren't picked up by the non-greedy regex. + + if((strpos($matches[3], 'zmg') !== false) || (strpos($matches[3], 'img') !== false) || (strpos($matches[3],'zrl') !== false) || (strpos($matches[3],'url') !== false)) + return $matches[0]; + + return '[' . $matches[1] . 'rl' . $matches[2] . ']' . $matches[3] . '"' . $matches[4] . '"' . $matches[5] . '[/' . $matches[6] . 'rl]'; +} + +function red_escape_codeblock($m) { + return '[$b64' . $m[2] . base64_encode($m[1]) . '[/' . $m[2] . ']'; +} + +function red_unescape_codeblock($m) { + return '[' . $m[2] . base64_decode($m[1]) . '[/' . $m[2] . ']'; +} + + +function red_zrlify_img_callback($matches) { + require_once('include/hubloc.php'); + $zrl = is_matrix_url($matches[2]); + + $t = strip_zids($matches[2]); + if($t !== $matches[2]) { + $zrl = true; + $matches[2] = $t; + } + + if($zrl) + return '[zmg' . $matches[1] . ']' . $matches[2] . '[/zmg]'; + + return $matches[0]; +} + diff --git a/include/zot.php b/include/zot.php index 572cbaef8..77843c35b 100644 --- a/include/zot.php +++ b/include/zot.php @@ -110,20 +110,21 @@ function zot_get_hublocs($hash) { * @param string $extra * @returns string json encoded zot packet */ -function zot_build_packet($channel, $type = 'notify', $recipients = null, $remote_key = null, $secret = null, $extra = null) { +function zot_build_packet($channel, $type = 'notify', $recipients = null, $remote_key = null, $methods = '', $secret = null, $extra = null) { - $data = array( + $data = [ 'type' => $type, - 'sender' => array( + 'sender' => [ 'guid' => $channel['channel_guid'], 'guid_sig' => base64url_encode(rsa_sign($channel['channel_guid'],$channel['channel_prvkey'])), 'url' => z_root(), 'url_sig' => base64url_encode(rsa_sign(z_root(),$channel['channel_prvkey'])), 'sitekey' => get_config('system','pubkey') - ), + ], 'callback' => '/post', - 'version' => ZOT_REVISION - ); + 'version' => ZOT_REVISION, + 'encryption' => crypto_methods() + ]; if ($recipients) { for ($x = 0; $x < count($recipients); $x ++) @@ -146,120 +147,67 @@ function zot_build_packet($channel, $type = 'notify', $recipients = null, $remot // Hush-hush ultra top-secret mode - if ($remote_key) { - $data = crypto_encapsulate(json_encode($data),$remote_key); + if($remote_key) { + $algorithm = zot_best_algorithm($methods); + $data = crypto_encapsulate(json_encode($data),$remote_key, $algorithm); } return json_encode($data); } /** - * @brief - * - * @see z_post_url() - * - * @param string $url - * @param array $data - * @return array see z_post_url() for returned data format - */ -function zot_zot($url, $data) { - return z_post_url($url, array('data' => $data)); -} - -/** - * @brief Look up information about channel. - * - * @param string $webbie - * does not have to be host qualified e.g. 'foo' is treated as 'foo\@thishub' - * @param array $channel - * (optional), if supplied permissions will be enumerated specifically for $channel - * @param boolean $autofallback - * fallback/failover to http if https connection cannot be established. Default is true. - * - * @return array see z_post_url() and \ref mod/zfinger.php + * @brief choose best encryption function from those available on both sites + * + * @param string $methods + * comma separated list of encryption methods + * @return string first match from our site method preferences crypto_methods() array + * of a method which is common to both sites; or 'aes256cbc' if no matches are found. */ -function zot_finger($webbie, $channel = null, $autofallback = true) { - - if (strpos($webbie,'@') === false) { - $address = $webbie; - $host = App::get_hostname(); - } else { - $address = substr($webbie,0,strpos($webbie,'@')); - $host = substr($webbie,strpos($webbie,'@')+1); - } - - $xchan_addr = $address . '@' . $host; - - if ((! $address) || (! $xchan_addr)) { - logger('zot_finger: no address :' . $webbie); - return array('success' => false); - } - logger('using xchan_addr: ' . $xchan_addr, LOGGER_DATA, LOG_DEBUG); - // potential issue here; the xchan_addr points to the primary hub. - // The webbie we were called with may not, so it might not be found - // unless we query for hubloc_addr instead of xchan_addr +function zot_best_algorithm($methods) { - $r = q("select xchan.*, hubloc.* from xchan - left join hubloc on xchan_hash = hubloc_hash - where xchan_addr = '%s' and hubloc_primary = 1 limit 1", - dbesc($xchan_addr) - ); - - if ($r) { - $url = $r[0]['hubloc_url']; + if(\Zotlabs\Lib\System::get_server_role() !== 'pro') + return 'aes256cbc'; - if ($r[0]['hubloc_network'] && $r[0]['hubloc_network'] !== 'zot') { - logger('zot_finger: alternate network: ' . $webbie); - logger('url: '.$url.', net: '.var_export($r[0]['hubloc_network'],true), LOGGER_DATA, LOG_DEBUG); - return array('success' => false); + if($methods) { + $x = explode(',',$methods); + if($x) { + $y = crypto_methods(); + if($y) { + foreach($y as $yv) { + $yv = trim($yv); + if(in_array($yv,$x)) { + return($yv); + } + } + } } - } else { - $url = 'https://' . $host; } - $rhs = '/.well-known/zot-info'; - $https = ((strpos($url,'https://') === 0) ? true : false); - - logger('zot_finger: ' . $address . ' at ' . $url, LOGGER_DEBUG); - - if ($channel) { - $postvars = array( - 'address' => $address, - 'target' => $channel['channel_guid'], - 'target_sig' => $channel['channel_guid_sig'], - 'key' => $channel['channel_pubkey'] - ); - - $result = z_post_url($url . $rhs,$postvars); - - if ((! $result['success']) && ($autofallback)) { - if ($https) { - logger('zot_finger: https failed. falling back to http'); - $result = z_post_url('http://' . $host . $rhs,$postvars); - } - } - } else { - $rhs .= '?f=&address=' . urlencode($address); + return 'aes256cbc'; +} - $result = z_fetch_url($url . $rhs); - if ((! $result['success']) && ($autofallback)) { - if ($https) { - logger('zot_finger: https failed. falling back to http'); - $result = z_fetch_url('http://' . $host . $rhs); - } - } - } - if (! $result['success']) - logger('zot_finger: no results'); - return $result; +/** + * @brief + * + * @see z_post_url() + * + * @param string $url + * @param array $data + * @return array see z_post_url() for returned data format + */ +function zot_zot($url, $data) { + return z_post_url($url, array('data' => $data)); } /** * @brief Refreshes after permission changed or friending, etc. * + * The top half of this function is similar to \Zotlabs\Zot\Finger::run() and could potentially be + * consolidated. + * * zot_refresh is typically invoked when somebody has changed permissions of a channel and they are notified * to fetch new permissions via a finger/discovery operation. This may result in a new connection * (abook entry) being added to a local channel and it may result in auto-permissions being granted. @@ -281,6 +229,7 @@ function zot_finger($webbie, $channel = null, $autofallback = true) { * * @returns boolean true if successful, else false */ + function zot_refresh($them, $channel = null, $force = false) { if (array_key_exists('xchan_network', $them) && ($them['xchan_network'] !== 'zot')) { @@ -296,12 +245,13 @@ function zot_refresh($them, $channel = null, $force = false) { if ($them['hubloc_url']) { $url = $them['hubloc_url']; - } else { + } + else { $r = null; // if they re-installed the server we could end up with the wrong record - pointing to the old install. // We'll order by reverse id to try and pick off the newest one first and hopefully end up with the - // correct hubloc. If this doesn't work we may have to re-write this section to try them all. + // correct hubloc. If this doesn't work we may have to re-write this section to try them all. if(array_key_exists('xchan_addr',$them) && $them['xchan_addr']) { $r = q("select hubloc_url, hubloc_primary from hubloc where hubloc_addr = '%s' order by hubloc_id desc", @@ -332,7 +282,7 @@ function zot_refresh($them, $channel = null, $force = false) { $token = random_string(); - $postvars = array(); + $postvars = []; $postvars['token'] = $token; @@ -355,6 +305,8 @@ function zot_refresh($them, $channel = null, $force = false) { $rhs = '/.well-known/zot-info'; + logger('zot_refresh: ' . $url, LOGGER_DATA, LOG_INFO); + $result = z_post_url($url . $rhs,$postvars); logger('zot_refresh: zot-info: ' . print_r($result,true), LOGGER_DATA, LOG_DEBUG); @@ -391,10 +343,13 @@ function zot_refresh($them, $channel = null, $force = false) { if($channel) { if($j['permissions']['data']) { - $permissions = crypto_unencapsulate(array( + $permissions = crypto_unencapsulate( + [ 'data' => $j['permissions']['data'], 'key' => $j['permissions']['key'], - 'iv' => $j['permissions']['iv']), + 'iv' => $j['permissions']['iv'], + 'alg' => $j['permissions']['alg'] + ], $channel['channel_prvkey']); if($permissions) $permissions = json_decode($permissions,true); @@ -420,6 +375,10 @@ function zot_refresh($them, $channel = null, $force = false) { $next_birthday = NULL_DATE; } + + // Keep original perms to check if we need to notify them + $previous_perms = get_all_perms($channel['channel_id'],$x['hash']); + $r = q("select * from abook where abook_xchan = '%s' and abook_channel = %d and abook_self = 0 limit 1", dbesc($x['hash']), intval($channel['channel_id']) @@ -483,10 +442,6 @@ function zot_refresh($them, $channel = null, $force = false) { } } - // Keep original perms to check if we need to notify them - $previous_perms = get_all_perms($channel['channel_id'],$x['hash']); - - $closeness = get_pconfig($channel['channel_id'],'system','new_abook_closeness'); if($closeness === false) $closeness = 80; @@ -516,13 +471,15 @@ function zot_refresh($them, $channel = null, $force = false) { if($new_connection) { if(! \Zotlabs\Access\Permissions::PermsCompare($new_perms,$previous_perms)) Zotlabs\Daemon\Master::Summon(array('Notifier','permission_create',$new_connection[0]['abook_id'])); - Zotlabs\Lib\Enotify::submit(array( + Zotlabs\Lib\Enotify::submit( + [ 'type' => NOTIFY_INTRO, 'from_xchan' => $x['hash'], 'to_xchan' => $channel['channel_hash'], - 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id'], - )); - + 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id'] + ] + ); + if(intval($permissions['view_stream'])) { if(intval(get_pconfig($channel['channel_id'],'perm_limits','send_stream') & PERMS_PENDING) || (! intval($new_connection[0]['abook_pending']))) @@ -531,11 +488,12 @@ function zot_refresh($them, $channel = null, $force = false) { /** If there is a default group for this channel, add this connection to it */ + $default_group = $channel['channel_default_group']; if($default_group) { require_once('include/group.php'); $g = group_rec_byhash($channel['channel_id'],$default_group); - if($g) + if($g) group_add_member($channel['channel_id'],'',$x['hash'],$g['id']); } @@ -571,11 +529,13 @@ function zot_refresh($them, $channel = null, $force = false) { * * \e string \b guid_sig => guid signed with conversant's private key * * \e string \b url => URL of the origination hub of this communication * * \e string \b url_sig => URL signed with conversant's private key + * @param boolean $multiple (optional) default false * * @returns array|null null if site is blacklisted or not found, otherwise an * array with an hubloc record */ -function zot_gethub($arr,$multiple = false) { + +function zot_gethub($arr, $multiple = false) { if($arr['guid'] && $arr['guid_sig'] && $arr['url'] && $arr['url_sig']) { @@ -586,8 +546,8 @@ function zot_gethub($arr,$multiple = false) { $limit = (($multiple) ? '' : ' limit 1 '); $sitekey = ((array_key_exists('sitekey',$arr) && $arr['sitekey']) ? " and hubloc_sitekey = '" . protect_sprintf($arr['sitekey']) . "' " : ''); - - $r = q("select * from hubloc + + $r = q("select hubloc.*, site.site_crypto from hubloc left join site on hubloc_url = site_url where hubloc_guid = '%s' and hubloc_guid_sig = '%s' and hubloc_url = '%s' and hubloc_url_sig = '%s' $sitekey $limit", @@ -624,9 +584,10 @@ function zot_gethub($arr,$multiple = false) { * * \b success boolean true or false * * \b message (optional) error string only if success is false */ + function zot_register_hub($arr) { - $result = array('success' => false); + $result = [ 'success' => false ]; if($arr['url'] && $arr['url_sig'] && $arr['guid'] && $arr['guid_sig']) { @@ -685,6 +646,7 @@ function zot_register_hub($arr) { * * \e boolean \b success boolean true or false * * \e string \b message (optional) error string only if success is false */ + function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { call_hooks('import_xchan', $arr); @@ -754,15 +716,15 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { if(intval($r[0]['xchan_pubforum']) != intval($arr['public_forum'])) $pubforum_changed = 1; - if(($r[0]['xchan_name_date'] != $arr['name_updated']) - || ($r[0]['xchan_connurl'] != $arr['connections_url']) + if(($r[0]['xchan_name_date'] != $arr['name_updated']) + || ($r[0]['xchan_connurl'] != $arr['connections_url']) || ($r[0]['xchan_addr'] != $arr['address']) || ($r[0]['xchan_follow'] != $arr['follow_url']) - || ($r[0]['xchan_connpage'] != $arr['connect_url']) + || ($r[0]['xchan_connpage'] != $arr['connect_url']) || ($r[0]['xchan_url'] != $arr['url']) || $hidden_changed || $adult_changed || $deleted_changed || $pubforum_changed ) { - $rup = q("update xchan set xchan_name = '%s', xchan_name_date = '%s', xchan_connurl = '%s', xchan_follow = '%s', - xchan_connpage = '%s', xchan_hidden = %d, xchan_selfcensored = %d, xchan_deleted = %d, xchan_pubforum = %d, + $rup = q("update xchan set xchan_name = '%s', xchan_name_date = '%s', xchan_connurl = '%s', xchan_follow = '%s', + xchan_connpage = '%s', xchan_hidden = %d, xchan_selfcensored = %d, xchan_deleted = %d, xchan_pubforum = %d, xchan_addr = '%s', xchan_url = '%s' where xchan_hash = '%s'", dbesc(($arr['name']) ? $arr['name'] : '-'), dbesc($arr['name_updated']), @@ -783,7 +745,8 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { $what .= 'xchan '; $changed = true; } - } else { + } + else { $import_photos = true; if((($arr['site']['directory_mode'] === 'standalone') @@ -819,7 +782,7 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { $changed = true; } - if ($import_photos) { + if($import_photos) { require_once('include/photo/photo_driver.php'); @@ -828,9 +791,9 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { $local = q("select channel_account_id, channel_id from channel where channel_hash = '%s' limit 1", dbesc($xchan_hash) ); - if ($local) { + if($local) { $ph = z_fetch_url($arr['photo'], true); - if ($ph['success']) { + if($ph['success']) { $hash = import_channel_photo($ph['body'], $arr['photo_mimetype'], $local[0]['channel_account_id'], $local[0]['channel_id']); @@ -868,11 +831,12 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { false ); } - } else { + } + else { $photos = import_xchan_photo($arr['photo'], $xchan_hash); } - if ($photos) { - if ($photos[4]) { + if($photos) { + if($photos[4]) { // importing the photo failed somehow. Leave the photo_date alone so we can try again at a later date. // This often happens when somebody joins the matrix with a bad cert. $r = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' @@ -883,7 +847,8 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { dbesc($photos[3]), dbesc($xchan_hash) ); - } else { + } + else { $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", dbescdate(datetime_convert('UTC','UTC',$arr['photo_updated'])), @@ -940,7 +905,8 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { $what .= 'profile '; $changed = true; } - } else { + } + else { logger('import_xchan: profile not available - hiding'); // they may have made it private $r = q("delete from xprof where xprof_hash = '%s'", @@ -993,16 +959,17 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { * @param array $arr - output of z_post_url() * @param array $outq - The queue structure attached to this request */ + function zot_process_response($hub, $arr, $outq) { - if (! $arr['success']) { + if(! $arr['success']) { logger('zot_process_response: failed: ' . $hub); return; } $x = json_decode($arr['body'], true); - if (! $x) { + if(! $x) { logger('zot_process_response: No json from ' . $hub); logger('zot_process_response: headers: ' . print_r($arr['header'],true), LOGGER_DATA, LOG_DEBUG); } @@ -1022,12 +989,12 @@ function zot_process_response($hub, $arr, $outq) { } } - // we have a more descriptive delivery report, so discard the per hub 'queued' report. + // we have a more descriptive delivery report, so discard the per hub 'queued' report. q("delete from dreport where dreport_queue = '%s' ", dbesc($outq['outq_hash']) ); - + // update the timestamp for this site q("update site set site_dead = 0, site_update = '%s' where site_url = '%s'", @@ -1060,6 +1027,7 @@ function zot_process_response($hub, $arr, $outq) { * decrypted and json decoded notify packet from remote site * @return array from zot_import() */ + function zot_fetch($arr) { logger('zot_fetch: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); @@ -1068,7 +1036,7 @@ function zot_fetch($arr) { // set $multiple param on zot_gethub() to return all matching hubs // This allows us to recover from re-installs when a redundant (but invalid) hubloc for - // this identity is widely dispersed throughout the network. + // this identity is widely dispersed throughout the network. $ret_hubs = zot_gethub($arr['sender'],true); if(! $ret_hubs) { @@ -1077,21 +1045,23 @@ function zot_fetch($arr) { } foreach($ret_hubs as $ret_hub) { - $data = array( - 'type' => 'pickup', - 'url' => z_root(), - 'callback_sig' => base64url_encode(rsa_sign(z_root() . '/post',get_config('system','prvkey'))), - 'callback' => z_root() . '/post', - 'secret' => $arr['secret'], - 'secret_sig' => base64url_encode(rsa_sign($arr['secret'],get_config('system','prvkey'))) - ); - $datatosend = json_encode(crypto_encapsulate(json_encode($data),$ret_hub['hubloc_sitekey'])); + $data = [ + 'type' => 'pickup', + 'url' => z_root(), + 'callback_sig' => base64url_encode(rsa_sign(z_root() . '/post', get_config('system','prvkey'))), + 'callback' => z_root() . '/post', + 'secret' => $arr['secret'], + 'secret_sig' => base64url_encode(rsa_sign($arr['secret'], get_config('system','prvkey'))) + ]; + + $algorithm = zot_best_algorithm($ret_hub['site_crypto']); + $datatosend = json_encode(crypto_encapsulate(json_encode($data),$ret_hub['hubloc_sitekey'], $algorithm)); $fetch = zot_zot($url,$datatosend); $result = zot_import($fetch, $arr['sender']['url']); - + if($result) return $result; } @@ -1137,6 +1107,11 @@ function zot_import($arr, $sender_url) { $data = json_decode(crypto_unencapsulate($data,get_config('system','prvkey')),true); } + if(! is_array($data)) { + logger('decode error'); + return array(); + } + if(! $data['success']) { if($data['message']) logger('remote pickup failed: ' . $data['message']); @@ -1162,6 +1137,12 @@ function zot_import($arr, $sender_url) { logger('zot_import: notify: ' . print_r($i['notify'],true), LOGGER_DATA, LOG_DEBUG); + if(! is_array($i['notify'])) { + logger('decode error'); + continue; + } + + $hub = zot_gethub($i['notify']['sender']); if((! $hub) || ($hub['hubloc_url'] != $sender_url)) { logger('zot_import: potential forgery: wrong site for sender: ' . $sender_url . ' != ' . print_r($i['notify'],true)); @@ -1195,7 +1176,7 @@ function zot_import($arr, $sender_url) { if($recip_arr) { stringify_array_elms($recip_arr); $recips = implode(',',$recip_arr); - $r = q("select channel_hash as hash from channel where channel_hash in ( " . $recips . " ) + $r = q("select channel_hash as hash from channel where channel_hash in ( " . $recips . " ) and channel_removed = 0 "); } @@ -1352,6 +1333,7 @@ function zot_import($arr, $sender_url) { * @param array $msg * @return NULL|array */ + function public_recips($msg) { require_once('include/channel.php'); @@ -1403,7 +1385,7 @@ function public_recips($msg) { $perm = 'post_mail'; $r = array(); - + $c = q("select channel_id, channel_hash from channel where channel_removed = 0"); if($c) { foreach($c as $cc) { @@ -1432,7 +1414,7 @@ function public_recips($msg) { if(($tag['type'] === 'mention') && (strpos($tag['url'],z_root()) !== false)) { $address = basename($tag['url']); if($address) { - $z = q("select channel_hash as hash from channel where channel_address = '%s' + $z = q("select channel_hash as hash from channel where channel_address = '%s' and channel_removed = 0 limit 1", dbesc($address) ); @@ -1561,6 +1543,7 @@ function allowed_public_recips($msg) { * @param boolean $request (optional) default false * @return array */ + function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $request = false) { $result = array(); @@ -1605,12 +1588,12 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ /** * @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 - * will normally arrive first via sync delivery, but this isn't guaranteed. + * message doesn't have ACL information in it as the cloned copy does. That copy + * will normally arrive first via sync delivery, but this isn't guaranteed. * There's a chance the current delivery could take place before the cloned copy arrives * hence the item could have the wrong ACL and *could* be used in subsequent deliveries or * access checks. So far all attempts at identifying this situation precisely - * have caused issues with delivery of relayed comments. + * have caused issues with delivery of relayed comments. */ // if(($d['hash'] === $sender['hash']) && ($sender['url'] !== z_root()) && (! $relay)) { @@ -1743,7 +1726,7 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ intval($channel['channel_id']), dbesc($arr['owner_xchan']) ); - $abook = (($ab) ? $ab[0] : null); + $abook = (($ab) ? $ab[0] : null); if(intval($arr['item_deleted'])) { @@ -1757,7 +1740,7 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ $arr['uid'] = $channel['channel_id']; $item_id = delete_imported_item($sender,$arr,$channel['channel_id'],$relay); - $DR->update(($item_id) ? 'deleted' : 'delete_failed'); + $DR->update(($item_id) ? 'deleted' : 'delete_failed'); $result[] = $DR->get(); if($relay && $item_id) { @@ -1779,7 +1762,7 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ $item_id = $r[0]['id']; if(intval($r[0]['item_deleted'])) { - // It was deleted locally. + // It was deleted locally. $DR->update('update ignored'); $result[] = $DR->get(); @@ -1806,8 +1789,8 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ $result[] = $DR->get(); - // We need this line to ensure wall-to-wall comments are relayed (by falling through to the relay bit), - // and at the same time not relay any other relayable posts more than once, because to do so is very wasteful. + // We need this line to ensure wall-to-wall comments are relayed (by falling through to the relay bit), + // and at the same time not relay any other relayable posts more than once, because to do so is very wasteful. if(! intval($r[0]['item_origin'])) continue; } @@ -1821,7 +1804,7 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ if(check_item_source($arr['uid'], $arr)) call_hooks('post_local', $arr); - + $item_id = 0; if(($arr['mid'] == $arr['parent_mid']) && (! post_is_importable($arr,$abook))) { @@ -1870,6 +1853,7 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ * * \e int \b mid * @param int $uid */ + function remove_community_tag($sender, $arr, $uid) { if(! (activity_match($arr['verb'], ACTIVITY_TAG) && ($arr['obj_type'] == ACTIVITY_OBJ_TAGTERM))) @@ -1934,10 +1918,13 @@ function remove_community_tag($sender, $arr, $uid) { * @brief Just calls item_store_update() and logs result. * * @see item_store_update() + * * @param array $sender (unused) * @param array $item - * @param int $uid (unused) + * @param array $orig + * @param int $uid */ + function update_imported_item($sender, $item, $orig, $uid) { // If this is a comment being updated, remove any privacy information @@ -1957,7 +1944,7 @@ function update_imported_item($sender, $item, $orig, $uid) { // because event_addtocal will parse the body to get the 'new' event details if($orig['resource_type'] === 'event') { - $res = event_addtocal($orig['id'],$uid); + $res = event_addtocal($orig['id'], $uid); if(! $res) logger('update event: failed'); } @@ -1978,6 +1965,7 @@ function update_imported_item($sender, $item, $orig, $uid) { * @param boolean $relay * @return boolean|int post_id */ + function delete_imported_item($sender, $item, $uid, $relay) { logger('delete_imported_item invoked', LOGGER_DEBUG); @@ -1995,13 +1983,14 @@ function delete_imported_item($sender, $item, $uid, $relay) { intval($uid) ); - if ($r) { - if ($r[0]['author_xchan'] === $sender['hash'] || $r[0]['owner_xchan'] === $sender['hash'] || $r[0]['source_xchan'] === $sender['hash']) + if($r) { + if($r[0]['author_xchan'] === $sender['hash'] || $r[0]['owner_xchan'] === $sender['hash'] || $r[0]['source_xchan'] === $sender['hash']) $ownership_valid = true; $post_id = $r[0]['id']; $item_found = true; - } else { + } + else { // perhaps the item is still in transit and the delete notification got here before the actual item did. Store it with the deleted flag set. // item_store() won't try to deliver any notifications or start delivery chains if this flag is set. @@ -2010,25 +1999,24 @@ function delete_imported_item($sender, $item, $uid, $relay) { logger('delete received for non-existent item - storing item data.'); - /** @BUG $arr is undefined here, so this is dead code */ - if ($arr['author_xchan'] === $sender['hash'] || $arr['owner_xchan'] === $sender['hash'] || $arr['source_xchan'] === $sender['hash']) { + if($item['author_xchan'] === $sender['hash'] || $item['owner_xchan'] === $sender['hash'] || $item['source_xchan'] === $sender['hash']) { $ownership_valid = true; - $item_result = item_store($arr); + $item_result = item_store($item); $post_id = $item_result['item_id']; } } - if ($ownership_valid === false) { + if($ownership_valid === false) { logger('delete_imported_item: failed: ownership issue'); return false; } require_once('include/items.php'); - if ($item_found) { - if (intval($r[0]['item_deleted'])) { + if($item_found) { + if(intval($r[0]['item_deleted'])) { logger('delete_imported_item: item was already deleted'); - if (! $relay) + if(! $relay) return false; // This is a bit hackish, but may have to suffice until the notification/delivery loop is optimised @@ -2139,6 +2127,7 @@ function process_mail_delivery($sender, $arr, $deliveries) { * * \e string \b hash a xchan_hash * @param array $arr */ + function process_rating_delivery($sender, $arr) { logger('process_rating_delivery: ' . print_r($arr,true)); @@ -2198,6 +2187,7 @@ function process_rating_delivery($sender, $arr) { * @param array $arr * @param array $deliveries (unused) */ + function process_profile_delivery($sender, $arr, $deliveries) { logger('process_profile_delivery', LOGGER_DEBUG); @@ -2232,15 +2222,15 @@ function process_location_delivery($sender,$arr,$deliveries) { /** * @brief checks for a moved UNO channel and sets the channel_moved flag - * + * * Currently the effect of this flag is to turn the channel into 'read-only' mode. - * New content will not be processed (there was still an issue with blocking the + * New content will not be processed (there was still an issue with blocking the * ability to post comments as of 10-Mar-2016). - * We do not physically remove the channel at this time. The hub admin may choose + * We do not physically remove the channel at this time. The hub admin may choose * to do so, but is encouraged to allow a grace period of several days in case there * are any issues migrating content. This packet will generally be received by the * original site when the basic channel import has been processed. - * + * * This will only be executed on the UNO system which is the old location * if a new location is reported and there is only one location record. * The rest of the hubloc syncronisation will be handled within @@ -2252,7 +2242,7 @@ function process_location_delivery($sender,$arr,$deliveries) { function check_location_move($sender_hash,$locations) { if(! $locations) - return; + return; if(get_config('system','server_role') !== 'basic') return; @@ -2275,13 +2265,13 @@ function check_location_move($sender_hash,$locations) { dbesc($sender_hash) ); - // federation plugins may wish to notify connections + // federation plugins may wish to notify connections // of the move on singleton networks $arr = array('channel' => $r[0],'locations' => $locations); call_hooks('location_move',$arr); - } + } } @@ -2294,6 +2284,7 @@ function check_location_move($sender_hash,$locations) { * @param boolean $absolute (optional) default false * @return array */ + function sync_locations($sender, $arr, $absolute = false) { $ret = array(); @@ -2536,12 +2527,12 @@ function zot_encode_locations($channel) { foreach($x as $hub) { // if this is a local channel that has been deleted, the hubloc is no good - make sure it is marked deleted - // so that nobody tries to use it. + // so that nobody tries to use it. if(intval($channel['channel_removed']) && $hub['hubloc_url'] === z_root()) $hub['hubloc_deleted'] = 1; - $ret[] = array( + $ret[] = [ 'host' => $hub['hubloc_host'], 'address' => $hub['hubloc_addr'], 'primary' => (intval($hub['hubloc_primary']) ? true : false), @@ -2550,7 +2541,7 @@ function zot_encode_locations($channel) { 'callback' => $hub['hubloc_callback'], 'sitekey' => $hub['hubloc_sitekey'], 'deleted' => (intval($hub['hubloc_deleted']) ? true : false) - ); + ]; } } @@ -2567,6 +2558,7 @@ function zot_encode_locations($channel) { * @param number $suppress_update default 0 * @return boolean $updated if something changed */ + function import_directory_profile($hash, $profile, $addr, $ud_flags = UPDATE_FLAGS_UPDATED, $suppress_update = 0) { logger('import_directory_profile', LOGGER_DEBUG); @@ -2701,6 +2693,7 @@ function import_directory_profile($hash, $profile, $addr, $ud_flags = UPDATE_FLA * @param string $hash * @param array $keywords */ + function import_directory_keywords($hash, $keywords) { $existing = array(); @@ -2745,6 +2738,7 @@ function import_directory_keywords($hash, $keywords) { * @param string $addr * @param int $flags (optional) default 0 */ + function update_modtime($hash, $guid, $addr, $flags = 0) { $dirmode = intval(get_config('system', 'directory_mode')); @@ -2777,6 +2771,7 @@ function update_modtime($hash, $guid, $addr, $flags = 0) { * @param string $pubkey * @return boolean true if updated or inserted */ + function import_site($arr, $pubkey) { if( (! is_array($arr)) || (! $arr['url']) || (! $arr['url_sig'])) return false; @@ -2844,6 +2839,7 @@ function import_site($arr, $pubkey) { $site_location = htmlspecialchars($arr['location'],ENT_COMPAT,'UTF-8',false); $site_realm = htmlspecialchars($arr['realm'],ENT_COMPAT,'UTF-8',false); $site_project = htmlspecialchars($arr['project'],ENT_COMPAT,'UTF-8',false); + $site_crypto = ((array_key_exists('encryption',$arr) && is_array($arr['encryption'])) ? htmlspecialchars(implode(',',$arr['encryption']),ENT_COMPAT,'UTF-8',false) : ''); $site_version = ((array_key_exists('version',$arr)) ? htmlspecialchars($arr['version'],ENT_COMPAT,'UTF-8',false) : ''); // You can have one and only one primary directory per realm. @@ -2865,6 +2861,7 @@ function import_site($arr, $pubkey) { || ($siterecord['site_register'] != $register_policy) || ($siterecord['site_project'] != $site_project) || ($siterecord['site_realm'] != $site_realm) + || ($siterecord['site_crypto'] != $site_crypto) || ($siterecord['site_version'] != $site_version) ) { $update = true; @@ -2873,7 +2870,7 @@ function import_site($arr, $pubkey) { // logger('import_site: stored: ' . print_r($siterecord,true)); - $r = q("update site set site_dead = 0, site_location = '%s', site_flags = %d, site_access = %d, site_directory = '%s', site_register = %d, site_update = '%s', site_sellpage = '%s', site_realm = '%s', site_type = %d, site_project = '%s', site_version = '%s' + $r = q("update site set site_dead = 0, site_location = '%s', site_flags = %d, site_access = %d, site_directory = '%s', site_register = %d, site_update = '%s', site_sellpage = '%s', site_realm = '%s', site_type = %d, site_project = '%s', site_version = '%s', site_crypto = '%s' where site_url = '%s'", dbesc($site_location), intval($site_directory), @@ -2886,6 +2883,7 @@ function import_site($arr, $pubkey) { intval(SITE_TYPE_ZOT), dbesc($site_project), dbesc($site_version), + dbesc($site_crypto), dbesc($url) ); if(! $r) { @@ -2903,8 +2901,8 @@ function import_site($arr, $pubkey) { else { $update = true; - $r = q("insert into site ( site_location, site_url, site_access, site_flags, site_update, site_directory, site_register, site_sellpage, site_realm, site_type, site_project, site_version ) - values ( '%s', '%s', %d, %d, '%s', '%s', %d, '%s', '%s', %d, '%s', '%s' )", + $r = q("insert into site ( site_location, site_url, site_access, site_flags, site_update, site_directory, site_register, site_sellpage, site_realm, site_type, site_project, site_version, site_crypto ) + values ( '%s', '%s', %d, %d, '%s', '%s', %d, '%s', '%s', %d, '%s', '%s', '%s' )", dbesc($site_location), dbesc($url), intval($access_policy), @@ -2916,7 +2914,8 @@ function import_site($arr, $pubkey) { dbesc($site_realm), intval(SITE_TYPE_ZOT), dbesc($site_project), - dbesc($site_version) + dbesc($site_version), + dbesc($site_crypto) ); if(! $r) { logger('import_site: record create failed. ' . print_r($arr,true)); @@ -2935,6 +2934,7 @@ function import_site($arr, $pubkey) { * @param array $packet (optional) default null * @param boolean $groups_changed (optional) default false */ + function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { if(get_config('system','server_role') === 'basic') @@ -2970,7 +2970,7 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { if(intval($channel['channel_removed'])) return; - $h = q("select * from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0", + $h = q("select hubloc.*, site.site_crypto from hubloc left join site on site_url = hubloc_url where hubloc_hash = '%s' and hubloc_deleted = 0", dbesc($channel['channel_hash']) ); @@ -3057,7 +3057,7 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { foreach($synchubs as $hub) { $hash = random_string(); - $n = zot_build_packet($channel,'notify',$env_recips,$hub['hubloc_sitekey'],$hash); + $n = zot_build_packet($channel,'notify',$env_recips,$hub['hubloc_sitekey'],$hub['site_crypto'],$hash); queue_insert(array( 'hash' => $hash, 'account_id' => $channel['channel_account_id'], @@ -3083,6 +3083,7 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { * @param array $deliveries * @return array */ + function process_channel_sync_delivery($sender, $arr, $deliveries) { if(get_config('system','server_role') === 'basic') @@ -3090,7 +3091,7 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { require_once('include/import.php'); - /** @FIXME this will sync red structures (channel, pconfig and abook). + /** @FIXME this will sync red structures (channel, pconfig and abook). Eventually we need to make this application agnostic. */ $result = array(); @@ -3181,15 +3182,15 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { $arr['channel']['channel_pageflags'] = $arr['channel']['channel_pageflags'] - 0x1000; } - - $disallowed = [ - 'channel_id', 'channel_account_id', 'channel_primary', 'channel_prvkey', - 'channel_address', 'channel_notifyflags', 'channel_removed', 'channel_deleted', - 'channel_system', 'channel_r_stream', 'channel_r_profile', 'channel_r_abook', - 'channel_r_storage', 'channel_r_pages', 'channel_w_stream', 'channel_w_wall', - 'channel_w_comment', 'channel_w_mail', 'channel_w_like', 'channel_w_tagwall', - 'channel_w_chat', 'channel_w_storage', 'channel_w_pages', 'channel_a_republish', - 'channel_a_delegate' + + $disallowed = [ + 'channel_id', 'channel_account_id', 'channel_primary', 'channel_prvkey', + 'channel_address', 'channel_notifyflags', 'channel_removed', 'channel_deleted', + 'channel_system', 'channel_r_stream', 'channel_r_profile', 'channel_r_abook', + 'channel_r_storage', 'channel_r_pages', 'channel_w_stream', 'channel_w_wall', + 'channel_w_comment', 'channel_w_mail', 'channel_w_like', 'channel_w_tagwall', + 'channel_w_chat', 'channel_w_storage', 'channel_w_pages', 'channel_a_republish', + 'channel_a_delegate' ]; $clean = array(); @@ -3226,7 +3227,7 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { foreach($arr['abook'] as $abook) { - + $abconfig = null; @@ -3372,7 +3373,7 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { } } if(! $found) { - $r = q("INSERT INTO `groups` ( hash, uid, visible, deleted, gname ) + $r = q("INSERT INTO groups ( hash, uid, visible, deleted, gname ) VALUES( '%s', %d, %d, %d, '%s' ) ", dbesc($cl['collection']), intval($channel['channel_id']), @@ -3446,7 +3447,7 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { // if somebody is in the group that wasn't before - add them if(! $found) { - q("INSERT INTO `group_member` (`uid`, `gid`, `xchan`) + q("INSERT INTO group_member (uid, gid, xchan) VALUES( %d, %d, '%s' ) ", intval($channel['channel_id']), intval($y['id']), @@ -3503,7 +3504,7 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { foreach($profile as $k => $v) { if(in_array($k,$disallowed)) continue; - + if($k === 'name') $clean['fullname'] = $v; elseif($k === 'with') @@ -3514,15 +3515,16 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { $clean[$k] = $v; /** - * @TODO + * @TODO * We also need to import local photos if a custom photo is selected */ } if(count($clean)) { foreach($clean as $k => $v) { - $r = dbq("UPDATE profile set `" . dbesc($k) . "` = '" . dbesc($v) - . "' where profile_guid = '" . dbesc($profile['profile_guid']) . "' and uid = " . intval($channel['channel_id'])); + $r = dbq("UPDATE profile set " . TQUOT . dbesc($k) . TQUOT . " = '" . dbesc($v) + . "' where profile_guid = '" . dbesc($profile['profile_guid']) + . "' and uid = " . intval($channel['channel_id'])); } } } @@ -3556,6 +3558,7 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { * * \e string \b xchan_url * @return string */ + function get_rpost_path($observer) { if(! $observer) return ''; @@ -3571,6 +3574,7 @@ function get_rpost_path($observer) { * @param array $x * @return boolean|string return false or a hash */ + function import_author_zot($x) { $hash = make_xchan_hash($x['guid'],$x['guid_sig']); @@ -3610,6 +3614,7 @@ function import_author_zot($x) { * @param array $data * @return array */ + function zot_reply_message_request($data) { $ret = array('success' => false); @@ -3646,7 +3651,7 @@ function zot_reply_message_request($data) { if ($messages) { $env_recips = null; - $r = q("select * from hubloc where hubloc_hash = '%s' and hubloc_error = 0 and hubloc_deleted = 0", + $r = q("select hubloc.*, site.site_crypto from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and hubloc_error = 0 and hubloc_deleted = 0", dbesc($sender_hash) ); if (! $r) { @@ -3668,7 +3673,7 @@ function zot_reply_message_request($data) { * create a notify packet and drop the actual message packet in the queue for pickup */ - $n = zot_build_packet($c[0],'notify',$env_recips,(($private) ? $hub['hubloc_sitekey'] : null),$hash,array('message_id' => $data['message_id'])); + $n = zot_build_packet($c[0],'notify',$env_recips,(($private) ? $hub['hubloc_sitekey'] : null),$hub['site_crypto'],$hash,array('message_id' => $data['message_id'])); queue_insert(array( 'hash' => $hash, @@ -3715,18 +3720,18 @@ function zotinfo($arr) { } } - $ztarget_hash = (($ztarget && $zsig) ? make_xchan_hash($ztarget,$zsig) : '' ); + $ztarget_hash = (($ztarget && $zsig) ? make_xchan_hash($ztarget,$zsig) : '' ); $r = null; if(strlen($zhash)) { - $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash + $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash where channel_hash = '%s' limit 1", dbesc($zhash) ); } elseif(strlen($zguid) && strlen($zguid_sig)) { - $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash + $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($zguid), dbesc($zguid_sig) @@ -3745,7 +3750,7 @@ function zotinfo($arr) { /** * The special address '[system]' will return a system channel if one has been defined, - * Or the first valid channel we find if there are no system channels. + * Or the first valid channel we find if there are no system channels. * * This is used by magic-auth if we have no prior communications with this site - and * returns an identity on this site which we can use to create a valid hub record so that @@ -3760,7 +3765,7 @@ function zotinfo($arr) { $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash where channel_removed = 0 order by channel_id limit 1"); } - } + } } else { $ret['message'] = 'Invalid request'; @@ -3785,7 +3790,7 @@ function zotinfo($arr) { if($deleted || $censored || $sys_channel) $searchable = false; - + $public_forum = false; $role = get_pconfig($e['channel_id'],'system','permissions_role'); @@ -3818,14 +3823,14 @@ function zotinfo($arr) { if($p) { if(! intval($p[0]['publish'])) - $searchable = false; + $searchable = false; $profile['description'] = $p[0]['pdesc']; $profile['birthday'] = $p[0]['dob']; if(($profile['birthday'] != '0000-00-00') && (($bd = z_birthday($p[0]['dob'],$e['channel_timezone'])) !== '')) $profile['next_birthday'] = $bd; - if($age = age($p[0]['dob'],$e['channel_timezone'],'')) + if($age = age($p[0]['dob'],$e['channel_timezone'],'')) $profile['age'] = $age; $profile['gender'] = $p[0]['gender']; $profile['marital'] = $p[0]['marital']; @@ -3878,14 +3883,14 @@ function zotinfo($arr) { $ret['adult_content'] = $adult_channel; $ret['public_forum'] = $public_forum; if($deleted) - $ret['deleted'] = $deleted; + $ret['deleted'] = $deleted; if(intval($e['channel_removed'])) $ret['deleted_locally'] = true; // premium or other channel desiring some contact with potential followers before connecting. // This is a template - %s will be replaced with the follow_url we discover for the return channel. - if($special_channel) + if($special_channel) $ret['connect_url'] = z_root() . '/connect/' . $e['channel_address']; // This is a template for our follow url, %s will be replaced with a webbie @@ -3905,6 +3910,11 @@ function zotinfo($arr) { $permissions['connected'] = true; } + // encrypt this with the default aes256cbc since we cannot be sure at this point which + // algorithms are preferred for communications with the remote site; notably + // because ztarget refers to an xchan and we don't necessarily know the origination + // location. + $ret['permissions'] = (($ztarget && $zkey) ? crypto_encapsulate(json_encode($permissions),$zkey) : $permissions); if($permissions['view_profile']) @@ -3935,6 +3945,8 @@ function zotinfo($arr) { $ret['site']['directory_url'] = z_root() . '/dirsearch'; + $ret['site']['encryption'] = crypto_methods(); + // hide detailed site information if you're off the grid if($dirmode != DIRECTORY_MODE_STANDALONE) { @@ -3961,7 +3973,7 @@ function zotinfo($arr) { $ret['site']['access_policy'] = 'tiered'; $ret['site']['accounts'] = account_total(); - + require_once('include/channel.php'); $ret['site']['channels'] = channel_total(); @@ -3972,17 +3984,17 @@ function zotinfo($arr) { $r = q("select * from addon where hidden = 0"); if($r) foreach($r as $rr) - $visible_plugins[] = $rr['name']; + $visible_plugins[] = $rr['aname']; } - $ret['site']['plugins'] = $visible_plugins; - $ret['site']['sitehash'] = get_config('system','location_hash'); - $ret['site']['sitename'] = get_config('system','sitename'); - $ret['site']['sellpage'] = get_config('system','sellpage'); - $ret['site']['location'] = get_config('system','site_location'); - $ret['site']['realm'] = get_directory_realm(); - $ret['site']['project'] = Zotlabs\Lib\System::get_platform_name() . ' ' . Zotlabs\Lib\System::get_server_role(); - $ret['site']['version'] = Zotlabs\Lib\System::get_project_version(); + $ret['site']['plugins'] = $visible_plugins; + $ret['site']['sitehash'] = get_config('system','location_hash'); + $ret['site']['sitename'] = get_config('system','sitename'); + $ret['site']['sellpage'] = get_config('system','sellpage'); + $ret['site']['location'] = get_config('system','site_location'); + $ret['site']['realm'] = get_directory_realm(); + $ret['site']['project'] = Zotlabs\Lib\System::get_platform_name() . ' ' . Zotlabs\Lib\System::get_server_role(); + $ret['site']['version'] = Zotlabs\Lib\System::get_project_version(); } @@ -4002,7 +4014,7 @@ function check_zotinfo($channel,$locations,&$ret) { // This function will likely expand as we find more things to detect and fix. // 1. Because magic-auth is reliant on it, ensure that the system channel has a valid hubloc - // Force this to be the case if anything is found to be wrong with it. + // Force this to be the case if anything is found to be wrong with it. // @FIXME ensure that the system channel exists in the first place and has an xchan @@ -4022,9 +4034,9 @@ function check_zotinfo($channel,$locations,&$ret) { logger('System channel locations are not valid. Attempting repair.'); - // Don't trust any existing records. Just get rid of them, but only do this + // Don't trust any existing records. Just get rid of them, but only do this // for the sys channel as normal channels will be trickier. - + q("delete from hubloc where hubloc_hash = '%s'", dbesc($channel['channel_hash']) ); @@ -4081,7 +4093,7 @@ function delivery_report_is_storable($dr) { return false; - // is the recipient one of our connections, or do we want to store every report? + // is the recipient one of our connections, or do we want to store every report? $r = explode(' ', $dr['recipient']); $rxchan = $r[0]; @@ -4092,15 +4104,15 @@ function delivery_report_is_storable($dr) { // We always add ourself as a recipient to private and relayed posts // So if a remote site says they can't find us, that's no big surprise // and just creates a lot of extra report noise - + if(($dr['location'] !== z_root()) && ($dr['sender'] === $rxchan) && ($dr['status'] === 'recipient_not_found')) return false; // If you have a private post with a recipient list, every single site is going to report - // back a failed delivery for anybody on that list that isn't local to them. We're only + // back a failed delivery for anybody on that list that isn't local to them. We're only // concerned about this if we have a local hubloc record which says we expected them to // have a channel on that site. - + $r = q("select hubloc_id from hubloc where hubloc_hash = '%s' and hubloc_url = '%s'", dbesc($rxchan), dbesc($dr['location']) @@ -4130,7 +4142,7 @@ function update_hub_connected($hub,$sitekey = '') { * 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) @@ -4141,7 +4153,7 @@ function update_hub_connected($hub,$sitekey = '') { $sitekey = $hub['sitekey']; } - // $sender['sitekey'] is a new addition to the protocol to distinguish + // $sender['sitekey'] is a new addition to the protocol 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. @@ -4164,10 +4176,10 @@ function update_hub_connected($hub,$sitekey = '') { 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) + dbesc($sitekey) ); - if(intval($r[0]['hubloc_orphancheck'])) { - q("update hubloc set hubloc_orhpancheck = 0 where hubloc_id = %d and hubloc_sitekey = '%s' ", + if(intval($hub['hubloc_orphancheck'])) { + q("update hubloc set hubloc_orphancheck = 0 where hubloc_id = %d and hubloc_sitekey = '%s' ", intval($hub['hubloc_id']), dbesc($sitekey) ); @@ -4176,7 +4188,7 @@ function update_hub_connected($hub,$sitekey = '') { dbesc($hub['hubloc_hash']) ); } - + return $hub['hubloc_url']; } @@ -4189,7 +4201,7 @@ function zot_reply_ping() { // 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(); @@ -4263,7 +4275,7 @@ function zot_reply_pickup($data) { /* * 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). + * 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", @@ -4303,13 +4315,21 @@ function zot_reply_pickup($data) { } else $ret['pickup'][] = array('notify' => json_decode($rr['outq_notify'],true),'message' => $x); - + remove_queue_item($rr['outq_hash']); } } } - $encrypted = crypto_encapsulate(json_encode($ret),$sitekey); + // this is a bit of a hack because we don't have the hubloc_url here, only the callback url. + // worst case is we'll end up using aes256cbc if they've got a different post endpoint + + $x = q("select site_crypto from site where site_url = '%s' limit 1", + dbesc(str_replace('/post','',$data['callback'])) + ); + $algorithm = zot_best_algorithm(($x) ? $x[0]['site_crypto'] : ''); + + $encrypted = crypto_encapsulate(json_encode($ret),$sitekey,$algorithm); json_return_and_die($encrypted); /* pickup: end */ @@ -4325,7 +4345,7 @@ function zot_reply_auth_check($data,$encrypted_packet) { * 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 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 @@ -4347,9 +4367,9 @@ function zot_reply_auth_check($data,$encrypted_packet) { // 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. + // 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. Zotlabs\Zot\Verify::purge('auth','30 MINUTE'); @@ -4360,7 +4380,7 @@ function zot_reply_auth_check($data,$encrypted_packet) { // 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. + // 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.'); @@ -4433,7 +4453,7 @@ function zot_reply_purge($sender,$recipients) { if ($recipients) { // basically this means "unfriend" foreach ($recipients as $recip) { - $r = q("select channel.*,xchan.* from channel + $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']), @@ -4450,13 +4470,13 @@ function zot_reply_purge($sender,$recipients) { } } $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']); - remove_all_xchan_resources($sender_hash); + remove_all_xchan_resources($sender_hash); $ret['success'] = true; } @@ -4471,7 +4491,7 @@ function zot_reply_refresh($sender,$recipients) { // 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 + // 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. @@ -4480,7 +4500,7 @@ function zot_reply_refresh($sender,$recipients) { // This would be a permissions update, typically for one connection foreach ($recipients as $recip) { - $r = q("select channel.*,xchan.* from channel + $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']), @@ -4488,17 +4508,17 @@ function zot_reply_refresh($sender,$recipients) { ); $x = zot_refresh(array( - 'xchan_guid' => $sender['guid'], + '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' => $sender['guid'], 'xchan_guid_sig' => $sender['guid_sig'], 'hubloc_url' => $sender['url'] ), null, (($msgtype === 'force_refresh') ? true : false)); @@ -4521,7 +4541,7 @@ function zot_reply_notify($data) { if($async) { // add to receive queue // qreceive_add($data); - } + } else { $x = zot_fetch($data); $ret['delivery_report'] = $x; @@ -4530,4 +4550,4 @@ function zot_reply_notify($data) { $ret['success'] = true; json_return_and_die($ret); -}
\ No newline at end of file +} |