diff options
Diffstat (limited to 'include')
59 files changed, 2385 insertions, 2357 deletions
diff --git a/include/AccessList.php b/include/AccessList.php deleted file mode 100644 index 43f1de111..000000000 --- a/include/AccessList.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php - - -class AccessList { - - private $allow_cid; - private $allow_gid; - private $deny_cid; - private $deny_gid; - - /* indicates if we are using the default constructor values or values that have been set explicitly. */ - - private $explicit; - - function __construct($channel) { - - if($channel) { - $this->allow_cid = $channel['channel_allow_cid']; - $this->allow_gid = $channel['channel_allow_gid']; - $this->deny_cid = $channel['channel_deny_cid']; - $this->deny_gid = $channel['channel_deny_gid']; - } - else { - $this->allow_cid = ''; - $this->allow_gid = ''; - $this->deny_cid = ''; - $this->deny_gid = ''; - } - - $this->explicit = false; - } - - function get_explicit() { - return $this->explicit; - } - - /** - * Set AccessList from strings such as those in already - * existing stored data items - */ - - function set($arr,$explicit = true) { - $this->allow_cid = $arr['allow_cid']; - $this->allow_gid = $arr['allow_gid']; - $this->deny_cid = $arr['deny_cid']; - $this->deny_gid = $arr['deny_gid']; - - $this->explicit = $explicit; - } - - /** - * return an array consisting of the current - * access list components where the elements - * are directly storable. - */ - - function get() { - return array( - 'allow_cid' => $this->allow_cid, - 'allow_gid' => $this->allow_gid, - 'deny_cid' => $this->deny_cid, - 'deny_gid' => $this->deny_gid, - ); - } - - /** - * Set AccessList from arrays, such as those provided by - * acl_selector(). For convenience, a string (or non-array) input is - * assumed to be a comma-separated list and auto-converted into an array. - */ - - function set_from_array($arr,$explicit = true) { - $this->allow_cid = perms2str((is_array($arr['contact_allow'])) - ? $arr['contact_allow'] : explode(',',$arr['contact_allow'])); - $this->allow_gid = perms2str((is_array($arr['group_allow'])) - ? $arr['group_allow'] : explode(',',$arr['group_allow'])); - $this->deny_cid = perms2str((is_array($arr['contact_deny'])) - ? $arr['contact_deny'] : explode(',',$arr['contact_deny'])); - $this->deny_gid = perms2str((is_array($arr['group_deny'])) - ? $arr['group_deny'] : explode(',',$arr['group_deny'])); - - $this->explicit = $explicit; - } - - function is_private() { - return (($this->allow_cid || $this->allow_gid || $this->deny_cid || $this->deny_gid) ? true : false); - } - -} - -/** - * @brief Used to wrap ACL elements in angle brackets for storage. - * - * @param[in,out] array &$item - */ -function sanitise_acl(&$item) { - if (strlen($item)) - $item = '<' . notags(trim($item)) . '>'; - else - unset($item); -} - -/** - * @brief Convert an ACL array to a storable string. - * - * @param array $p - * @return array - */ -function perms2str($p) { - $ret = ''; - - if (is_array($p)) - $tmp = $p; - else - $tmp = explode(',', $p); - - if (is_array($tmp)) { - array_walk($tmp, 'sanitise_acl'); - $ret = implode('', $tmp); - } - - return $ret; -} - - -/** - * @brief Turn user/group ACLs stored as angle bracketed text into arrays. - * - * turn string array of angle-bracketed elements into string array - * e.g. "<123xyz><246qyo><sxo33e>" => array(123xyz,246qyo,sxo33e); - * - * @param string $s - * @return array - */ -function expand_acl($s) { - $ret = array(); - - if(strlen($s)) { - $t = str_replace('<','',$s); - $a = explode('>',$t); - foreach($a as $aa) { - if($aa) - $ret[] = $aa; - } - } - - return $ret; -} diff --git a/include/BaseObject.php b/include/BaseObject.php index 4bfac5fa0..a88978a83 100644 --- a/include/BaseObject.php +++ b/include/BaseObject.php @@ -1,4 +1,5 @@ <?php /** @file */ + if(class_exists('BaseObject')) return; diff --git a/include/Contact.php b/include/Contact.php index 3bd5f9936..1c61470e1 100644 --- a/include/Contact.php +++ b/include/Contact.php @@ -263,7 +263,7 @@ function rrmdir($path) return false; } -function channel_remove($channel_id, $local = true, $unset_session=true) { +function channel_remove($channel_id, $local = true, $unset_session=false) { if(! $channel_id) return; @@ -331,6 +331,7 @@ function channel_remove($channel_id, $local = true, $unset_session=true) { dbesc(datetime_convert()), intval($channel_id) ); + // if this was the default channel, set another one as default if($a->account['account_default_channel'] == $channel_id) { $r = q("select channel_id from channel where channel_account_id = %d and channel_removed = 0 limit 1", @@ -344,12 +345,11 @@ function channel_remove($channel_id, $local = true, $unset_session=true) { } else { $rr = q("update account set account_default_channel = 0 where account_id = %d", - intval($r[0]['channel_id']), - intval($a->account['account_id'])); + intval($a->account['account_id']) + ); } } - logger('deleting hublocs',LOGGER_DEBUG); $r = q("update hubloc set hubloc_deleted = 1 where hubloc_hash = '%s' and hubloc_url = '%s' ", @@ -389,8 +389,7 @@ function channel_remove($channel_id, $local = true, $unset_session=true) { proc_run('php','include/directory.php',$channel_id); if($channel_id == local_channel() && $unset_session) { - unset($_SESSION['authenticated']); - unset($_SESSION['uid']); + nuke_session(); goaway($a->get_baseurl()); } diff --git a/include/ConversationObject.php b/include/ConversationObject.php index 7e0d67c10..66f6cca0e 100644 --- a/include/ConversationObject.php +++ b/include/ConversationObject.php @@ -15,6 +15,7 @@ require_once('include/items.php'); */ class Conversation extends BaseObject { + private $threads = array(); private $mode = null; private $observer = null; @@ -52,14 +53,8 @@ class Conversation extends BaseObject { switch($mode) { case 'network': -// if(array_key_exists('firehose',$a->data) && intval($a->data['firehose'])) { -// $this->profile_owner = intval($a->data['firehose']); -// $this->writable = false; -// } -// else { - $this->profile_owner = local_channel(); - $this->writable = true; -// } + $this->profile_owner = local_channel(); + $this->writable = true; break; case 'channel': $this->profile_owner = $a->profile['profile_uid']; @@ -69,7 +64,6 @@ class Conversation extends BaseObject { // in this mode we set profile_owner after initialisation (from conversation()) and then // pull some trickery which allows us to re-invoke this function afterward // it's an ugly hack so FIXME -// $this->profile_owner = $a->profile['uid']; $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); break; case 'page': @@ -142,11 +136,11 @@ class Conversation extends BaseObject { public function add_thread($item) { $item_id = $item->get_id(); if(!$item_id) { - logger('[ERROR] Conversation::add_thread : Item has no ID!!', LOGGER_DEBUG); + logger('Item has no ID!!', LOGGER_DEBUG, LOG_ERR); return false; } if($this->get_thread($item->get_id())) { - logger('[WARN] Conversation::add_thread : Thread already exists ('. $item->get_id() .').', LOGGER_DEBUG); + logger('Thread already exists ('. $item->get_id() .').', LOGGER_DEBUG, LOG_WARNING); return false; } @@ -177,11 +171,6 @@ class Conversation extends BaseObject { } } require_once('include/identity.php'); -// $sys = get_sys_channel(); - -// if($sys && $item->get_data_value('uid') == $sys['channel_id']) { -// $item->set_commentable(false); -// } $item->set_conversation($this); $this->threads[] = $item; @@ -209,7 +198,7 @@ class Conversation extends BaseObject { $item_data = $item->get_template_data($conv_responses); } if(!$item_data) { - logger('[ERROR] Conversation::get_template_data : Failed to get item template data ('. $item->get_id() .').', LOGGER_DEBUG); + logger('Failed to get item template data ('. $item->get_id() .').', LOGGER_DEBUG, LOG_ERR); return false; } $result[] = $item_data; diff --git a/include/DReport.php b/include/DReport.php deleted file mode 100644 index ef86c8cbc..000000000 --- a/include/DReport.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php - - -class DReport { - - private $location; - private $sender; - private $recipient; - private $message_id; - private $status; - private $date; - - function __construct($location,$sender,$recipient,$message_id,$status = 'deliver') { - $this->location = $location; - $this->sender = $sender; - $this->recipient = $recipient; - $this->message_id = $message_id; - $this->status = $status; - $this->date = datetime_convert(); - } - - function update($status) { - $this->status = $status; - $this->date = datetime_convert(); - } - - function addto_recipient($name) { - $this->recipient = $this->recipient . ' ' . $name; - } - - function addto_update($status) { - $this->status = $this->status . ' ' . $status; - } - - - function set($arr) { - $this->location = $arr['location']; - $this->sender = $arr['sender']; - $this->recipient = $arr['recipient']; - $this->message_id = $arr['message_id']; - $this->status = $arr['status']; - $this->date = $arr['date']; - } - - function get() { - return array( - 'location' => $this->location, - 'sender' => $this->sender, - 'recipient' => $this->recipient, - 'message_id' => $this->message_id, - 'status' => $this->status, - 'date' => $this->date - ); - } -} diff --git a/include/ItemObject.php b/include/ItemObject.php index 8be99d91e..d42e993e6 100644 --- a/include/ItemObject.php +++ b/include/ItemObject.php @@ -241,7 +241,7 @@ class Item extends BaseObject { $has_bookmarks = false; if(is_array($item['term'])) { foreach($item['term'] as $t) { - if($t['type'] == TERM_BOOKMARK) + if(!UNO && $t['type'] == TERM_BOOKMARK) $has_bookmarks = true; } } @@ -264,7 +264,7 @@ class Item extends BaseObject { if($keep_reports === 0) $keep_reports = 30; - if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC',"now - $keep_reports days")) > 0) + if((! get_config('system','disable_dreport')) && strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC',"now - $keep_reports days")) > 0) $dreport = t('Delivery Report'); if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0) @@ -681,14 +681,20 @@ class Item extends BaseObject { $qc = ((local_channel()) ? get_pconfig(local_channel(),'system','qcomment') : null); $qcomment = (($qc) ? explode("\n",$qc) : null); + $arr = array('comment_buttons' => '','id' => $this->get_id()); + call_hooks('comment_buttons',$arr); + $comment_buttons = $arr['comment_buttons']; + + $comment_box = replace_macros($template,array( '$return_path' => '', '$threaded' => $this->is_threaded(), - '$jsreload' => (($conv->get_mode() === 'display') ? $_SESSION['return_url'] : ''), + '$jsreload' => '', //(($conv->get_mode() === 'display') ? $_SESSION['return_url'] : ''), '$type' => (($conv->get_mode() === 'channel') ? 'wall-comment' : 'net-comment'), '$id' => $this->get_id(), '$parent' => $this->get_id(), '$qcomment' => $qcomment, + '$comment_buttons' => $comment_buttons, '$profile_uid' => $conv->get_profile_owner(), '$mylink' => $observer['xchan_url'], '$mytitle' => t('This is you'), diff --git a/include/RedDAV/RedBasicAuth.php b/include/RedDAV/RedBasicAuth.php deleted file mode 100644 index 19dd9a5f0..000000000 --- a/include/RedDAV/RedBasicAuth.php +++ /dev/null @@ -1,212 +0,0 @@ -<?php - -namespace RedMatrix\RedDAV; - -use Sabre\DAV; - -/** - * @brief Authentication backend class for RedDAV. - * - * This class also contains some data which is not necessary for authentication - * like timezone settings. - * - * @extends Sabre\DAV\Auth\Backend\AbstractBasic - * - * @link http://github.com/friendica/red - * @license http://opensource.org/licenses/mit-license.php The MIT License (MIT) - */ -class RedBasicAuth extends DAV\Auth\Backend\AbstractBasic { - - /** - * @brief This variable holds the currently logged-in channel_address. - * - * It is used for building path in filestorage/. - * - * @var string|null - */ - protected $channel_name = null; - /** - * channel_id of the current channel of the logged-in account. - * - * @var int - */ - public $channel_id = 0; - /** - * channel_hash of the current channel of the logged-in account. - * - * @var string - */ - public $channel_hash = ''; - /** - * Set in mod/cloud.php to observer_hash. - * - * @var string - */ - public $observer = ''; - /** - * - * @see RedBrowser::set_writeable() - * @var \Sabre\DAV\Browser\Plugin - */ - public $browser; - /** - * channel_id of the current visited path. Set in RedDirectory::getDir(). - * - * @var int - */ - public $owner_id = 0; - /** - * channel_name of the current visited path. Set in RedDirectory::getDir(). - * - * Used for creating the path in cloud/ - * - * @var string - */ - public $owner_nick = ''; - /** - * Timezone from the visiting channel's channel_timezone. - * - * Used in @ref RedBrowser - * - * @var string - */ - protected $timezone = ''; - - - /** - * @brief Validates a username and password. - * - * Guest access is granted with the password "+++". - * - * @see \Sabre\DAV\Auth\Backend\AbstractBasic::validateUserPass - * @param string $username - * @param string $password - * @return bool - */ - protected function validateUserPass($username, $password) { - if (trim($password) === '+++') { - logger('guest: ' . $username); - return true; - } - - require_once('include/auth.php'); - $record = account_verify_password($username, $password); - if ($record && $record['account_default_channel']) { - $r = q("SELECT * FROM channel WHERE channel_account_id = %d AND channel_id = %d LIMIT 1", - intval($record['account_id']), - intval($record['account_default_channel']) - ); - if ($r) { - return $this->setAuthenticated($r[0]); - } - } - $r = q("SELECT * FROM channel WHERE channel_address = '%s' LIMIT 1", - dbesc($username) - ); - if ($r) { - $x = q("SELECT account_flags, account_salt, account_password FROM account WHERE account_id = %d LIMIT 1", - intval($r[0]['channel_account_id']) - ); - if ($x) { - // @fixme this foreach should not be needed? - foreach ($x as $record) { - if (($record['account_flags'] == ACCOUNT_OK) || ($record['account_flags'] == ACCOUNT_UNVERIFIED) - && (hash('whirlpool', $record['account_salt'] . $password) === $record['account_password'])) { - logger('password verified for ' . $username); - return $this->setAuthenticated($r[0]); - } - } - } - } - - $error = 'password failed for ' . $username; - logger($error); - log_failed_login($error); - - return false; - } - - /** - * @brief Sets variables and session parameters after successfull authentication. - * - * @param array $r - * Array with the values for the authenticated channel. - * @return bool - */ - protected function setAuthenticated($r) { - $this->channel_name = $r['channel_address']; - $this->channel_id = $r['channel_id']; - $this->channel_hash = $this->observer = $r['channel_hash']; - $_SESSION['uid'] = $r['channel_id']; - $_SESSION['account_id'] = $r['channel_account_id']; - $_SESSION['authenticated'] = true; - return true; - } - - /** - * Sets the channel_name from the currently logged-in channel. - * - * @param string $name - * The channel's name - */ - public function setCurrentUser($name) { - $this->channel_name = $name; - } - /** - * Returns information about the currently logged-in channel. - * - * If nobody is currently logged in, this method should return null. - * - * @see \Sabre\DAV\Auth\Backend\AbstractBasic::getCurrentUser - * @return string|null - */ - public function getCurrentUser() { - return $this->channel_name; - } - - /** - * @brief Sets the timezone from the channel in RedBasicAuth. - * - * Set in mod/cloud.php if the channel has a timezone set. - * - * @param string $timezone - * The channel's timezone. - * @return void - */ - public function setTimezone($timezone) { - $this->timezone = $timezone; - } - /** - * @brief Returns the timezone. - * - * @return string - * Return the channel's timezone. - */ - public function getTimezone() { - return $this->timezone; - } - - /** - * @brief Set browser plugin for SabreDAV. - * - * @see RedBrowser::set_writeable() - * @param \Sabre\DAV\Browser\Plugin $browser - */ - public function setBrowserPlugin($browser) { - $this->browser = $browser; - } - - /** - * @brief Prints out all RedBasicAuth variables to logger(). - * - * @return void - */ - public function log() { - logger('channel_name ' . $this->channel_name, LOGGER_DATA); - logger('channel_id ' . $this->channel_id, LOGGER_DATA); - logger('channel_hash ' . $this->channel_hash, LOGGER_DATA); - logger('observer ' . $this->observer, LOGGER_DATA); - logger('owner_id ' . $this->owner_id, LOGGER_DATA); - logger('owner_nick ' . $this->owner_nick, LOGGER_DATA); - } -}
\ No newline at end of file diff --git a/include/RedDAV/RedBrowser.php b/include/RedDAV/RedBrowser.php deleted file mode 100644 index 1aa5f435e..000000000 --- a/include/RedDAV/RedBrowser.php +++ /dev/null @@ -1,372 +0,0 @@ -<?php - -namespace RedMatrix\RedDAV; - -use Sabre\DAV; - -/** - * @brief Provides a DAV frontend for the webbrowser. - * - * RedBrowser is a SabreDAV server-plugin to provide a view to the DAV storage - * for the webbrowser. - * - * @extends \Sabre\DAV\Browser\Plugin - * - * @link http://github.com/friendica/red - * @license http://opensource.org/licenses/mit-license.php The MIT License (MIT) - */ -class RedBrowser extends DAV\Browser\Plugin { - - /** - * @see set_writeable() - * @see \Sabre\DAV\Auth\Backend\BackendInterface - * @var RedBasicAuth - */ - private $auth; - - /** - * @brief Constructor for RedBrowser class. - * - * $enablePost will be activated through set_writeable() in a later stage. - * At the moment the write_storage permission is only valid for the whole - * folder. No file specific permissions yet. - * @todo disable enablePost by default and only activate if permissions - * grant edit rights. - * - * Disable assets with $enableAssets = false. Should get some thumbnail views - * anyway. - * - * @param RedBasicAuth &$auth - */ - public function __construct(&$auth) { - $this->auth = $auth; - parent::__construct(true, false); - } - - /** - * The DAV browser is instantiated after the auth module and directory classes - * but before we know the current directory and who the owner and observer - * are. So we add a pointer to the browser into the auth module and vice versa. - * Then when we've figured out what directory is actually being accessed, we - * call the following function to decide whether or not to show web elements - * which include writeable objects. - * - * @fixme It only disable/enable the visible parts. Not the POST handler - * which handels the actual requests when uploading files or creating folders. - * - * @todo Maybe this whole way of doing this can be solved with some - * $server->subscribeEvent(). - */ - public function set_writeable() { - if (! $this->auth->owner_id) { - $this->enablePost = false; - } - - if (! perm_is_allowed($this->auth->owner_id, get_observer_hash(), 'write_storage')) { - $this->enablePost = false; - } else { - $this->enablePost = true; - } - } - - /** - * @brief Creates the directory listing for the given path. - * - * @param string $path which should be displayed - */ - public function generateDirectoryIndex($path) { - // (owner_id = channel_id) is visitor owner of this directory? - $is_owner = ((local_channel() && $this->auth->owner_id == local_channel()) ? true : false); - - if ($this->auth->getTimezone()) - date_default_timezone_set($this->auth->getTimezone()); - - require_once('include/conversation.php'); - require_once('include/text.php'); - if ($this->auth->owner_nick) { - $html = profile_tabs(get_app(), (($is_owner) ? true : false), $this->auth->owner_nick); - } - - $files = $this->server->getPropertiesForPath($path, array( - '{DAV:}displayname', - '{DAV:}resourcetype', - '{DAV:}getcontenttype', - '{DAV:}getcontentlength', - '{DAV:}getlastmodified', - ), 1); - - - $parent = $this->server->tree->getNodeForPath($path); - - $parentpath = array(); - // only show parent if not leaving /cloud/; TODO how to improve this? - if ($path && $path != "cloud") { - list($parentUri) = DAV\URLUtil::splitPath($path); - $fullPath = DAV\URLUtil::encodePath($this->server->getBaseUri() . $parentUri); - - $parentpath['icon'] = $this->enableAssets ? '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl('icons/parent' . $this->iconExtension) . '" width="24" alt="' . t('parent') . '"></a>' : ''; - $parentpath['path'] = $fullPath; - } - - $f = array(); - foreach ($files as $file) { - $ft = array(); - $type = null; - - // This is the current directory, we can skip it - if (rtrim($file['href'],'/') == $path) continue; - - list(, $name) = DAV\URLUtil::splitPath($file['href']); - - if (isset($file[200]['{DAV:}resourcetype'])) { - $type = $file[200]['{DAV:}resourcetype']->getValue(); - - // resourcetype can have multiple values - if (!is_array($type)) $type = array($type); - - foreach ($type as $k=>$v) { - // Some name mapping is preferred - switch ($v) { - case '{DAV:}collection' : - $type[$k] = t('Collection'); - break; - case '{DAV:}principal' : - $type[$k] = t('Principal'); - break; - case '{urn:ietf:params:xml:ns:carddav}addressbook' : - $type[$k] = t('Addressbook'); - break; - case '{urn:ietf:params:xml:ns:caldav}calendar' : - $type[$k] = t('Calendar'); - break; - case '{urn:ietf:params:xml:ns:caldav}schedule-inbox' : - $type[$k] = t('Schedule Inbox'); - break; - case '{urn:ietf:params:xml:ns:caldav}schedule-outbox' : - $type[$k] = t('Schedule Outbox'); - break; - case '{http://calendarserver.org/ns/}calendar-proxy-read' : - $type[$k] = 'Proxy-Read'; - break; - case '{http://calendarserver.org/ns/}calendar-proxy-write' : - $type[$k] = 'Proxy-Write'; - break; - } - } - $type = implode(', ', $type); - } - - // If no resourcetype was found, we attempt to use - // the contenttype property - if (!$type && isset($file[200]['{DAV:}getcontenttype'])) { - $type = $file[200]['{DAV:}getcontenttype']; - } - if (!$type) $type = t('Unknown'); - - $size = isset($file[200]['{DAV:}getcontentlength']) ? (int)$file[200]['{DAV:}getcontentlength'] : ''; - $lastmodified = ((isset($file[200]['{DAV:}getlastmodified'])) ? $file[200]['{DAV:}getlastmodified']->getTime()->format('Y-m-d H:i:s') : ''); - - $fullPath = DAV\URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path ? $path . '/' : '') . $name, '/')); - - - $displayName = isset($file[200]['{DAV:}displayname']) ? $file[200]['{DAV:}displayname'] : $name; - - $displayName = $this->escapeHTML($displayName); - $type = $this->escapeHTML($type); - - $icon = ''; - - if ($this->enableAssets) { - $node = $this->server->tree->getNodeForPath(($path ? $path . '/' : '') . $name); - foreach (array_reverse($this->iconMap) as $class=>$iconName) { - if ($node instanceof $class) { - $icon = '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl($iconName . $this->iconExtension) . '" alt="" width="24"></a>'; - break; - } - } - } - - $parentHash = ''; - $owner = $this->auth->owner_id; - $splitPath = explode('/', $fullPath); - if (count($splitPath) > 3) { - for ($i = 3; $i < count($splitPath); $i++) { - $attachName = urldecode($splitPath[$i]); - $attachHash = $this->findAttachHash($owner, $parentHash, $attachName); - $parentHash = $attachHash; - } - } - - $attachIcon = ""; // "<a href=\"attach/".$attachHash."\" title=\"".$displayName."\"><i class=\"icon-download\"></i></a>"; - - // put the array for this file together - $ft['attachId'] = $this->findAttachIdByHash($attachHash); - $ft['fileStorageUrl'] = substr($fullPath, 0, strpos($fullPath, "cloud/")) . "filestorage/" . $this->auth->getCurrentUser(); - $ft['icon'] = $icon; - $ft['attachIcon'] = (($size) ? $attachIcon : ''); - // @todo Should this be an item value, not a global one? - $ft['is_owner'] = $is_owner; - $ft['fullPath'] = $fullPath; - $ft['displayName'] = $displayName; - $ft['type'] = $type; - $ft['size'] = $size; - $ft['sizeFormatted'] = userReadableSize($size); - $ft['lastmodified'] = (($lastmodified) ? datetime_convert('UTC', date_default_timezone_get(), $lastmodified) : ''); - $ft['iconFromType'] = getIconFromType($type); - - $f[] = $ft; - } - - $output = ''; - if ($this->enablePost) { - $this->server->broadcastEvent('onHTMLActionsPanel', array($parent, &$output)); - } - - $html .= replace_macros(get_markup_template('cloud.tpl'), array( - '$header' => t('Files') . ": " . $this->escapeHTML($path) . "/", - '$total' => t('Total'), - '$actionspanel' => $output, - '$shared' => t('Shared'), - '$create' => t('Create'), - '$upload' => t('Upload'), - '$is_owner' => $is_owner, - '$parentpath' => $parentpath, - '$entries' => $f, - '$name' => t('Name'), - '$type' => t('Type'), - '$size' => t('Size'), - '$lastmod' => t('Last Modified'), - '$parent' => t('parent'), - '$edit' => t('Edit'), - '$delete' => t('Delete'), - '$nick' => $this->auth->getCurrentUser() - )); - - $a = get_app(); - $a->page['content'] = $html; - load_pdl($a); - - $theme_info_file = "view/theme/" . current_theme() . "/php/theme.php"; - if (file_exists($theme_info_file)){ - require_once($theme_info_file); - if (function_exists(str_replace('-', '_', current_theme()) . '_init')) { - $func = str_replace('-', '_', current_theme()) . '_init'; - $func($a); - } - } - construct_page($a); - } - - /** - * @brief Creates a form to add new folders and upload files. - * - * @param \Sabre\DAV\INode $node - * @param string &$output - */ - public function htmlActionsPanel(DAV\INode $node, &$output) { - if (! $node instanceof DAV\ICollection) - return; - - // We also know fairly certain that if an object is a non-extended - // SimpleCollection, we won't need to show the panel either. - if (get_class($node) === 'Sabre\\DAV\\SimpleCollection') - return; - - // Storage and quota for the account (all channels of the owner of this directory)! - $limit = service_class_fetch($owner, 'attach_upload_limit'); - $r = q("SELECT SUM(filesize) AS total FROM attach WHERE aid = %d", - intval($this->auth->channel_account_id) - ); - $used = $r[0]['total']; - if ($used) { - $quotaDesc = t('You are using %1$s of your available file storage.'); - $quotaDesc = sprintf($quotaDesc, - userReadableSize($used)); - } - if ($limit && $used) { - $quotaDesc = t('You are using %1$s of %2$s available file storage. (%3$s%)'); - $quotaDesc = sprintf($quotaDesc, - userReadableSize($used), - userReadableSize($limit), - round($used / $limit, 1) * 100); - } - - // prepare quota for template - $quota = array(); - $quota['used'] = $used; - $quota['limit'] = $limit; - $quota['desc'] = $quotaDesc; - $quota['warning'] = ((($limit) && ((round($used / $limit, 1) * 100) >= 90)) ? t('WARNING:') : ''); // 10485760 bytes = 100MB - - $output .= replace_macros(get_markup_template('cloud_actionspanel.tpl'), array( - '$folder_header' => t('Create new folder'), - '$folder_submit' => t('Create'), - '$upload_header' => t('Upload file'), - '$upload_submit' => t('Upload'), - '$quota' => $quota - )); - } - - /** - * This method takes a path/name of an asset and turns it into url - * suiteable for http access. - * - * @param string $assetName - * @return string - */ - protected function getAssetUrl($assetName) { - return z_root() . '/cloud/?sabreAction=asset&assetName=' . urlencode($assetName); - } - - /** - * @brief Return the hash of an attachment. - * - * Given the owner, the parent folder and and attach name get the attachment - * hash. - * - * @param int $owner - * The owner_id - * @param string $hash - * The parent's folder hash - * @param string $attachName - * The name of the attachment - * @return string - */ - - protected function findAttachHash($owner, $parentHash, $attachName) { - $r = q("SELECT hash FROM attach WHERE uid = %d AND folder = '%s' AND filename = '%s' ORDER BY edited DESC LIMIT 1", - intval($owner), - dbesc($parentHash), - dbesc($attachName) - ); - $hash = ""; - if ($r) { - foreach ($r as $rr) { - $hash = $rr['hash']; - } - } - return $hash; - } - - /** - * @brief Returns an attachment's id for a given hash. - * - * This id is used to access the attachment in filestorage/ - * - * @param string $attachHash - * The hash of an attachment - * @return string - */ - protected function findAttachIdByHash($attachHash) { - $r = q("SELECT id FROM attach WHERE hash = '%s' ORDER BY edited DESC LIMIT 1", - dbesc($attachHash) - ); - $id = ""; - if ($r) { - foreach ($r as $rr) { - $id = $rr['id']; - } - } - return $id; - } -} diff --git a/include/RedDAV/RedDirectory.php b/include/RedDAV/RedDirectory.php deleted file mode 100644 index 8d8af5bd3..000000000 --- a/include/RedDAV/RedDirectory.php +++ /dev/null @@ -1,536 +0,0 @@ -<?php - -namespace RedMatrix\RedDAV; - -use Sabre\DAV; - -/** - * @brief RedDirectory class. - * - * A class that represents a directory. - * - * @extends \Sabre\DAV\Node - * @implements \Sabre\DAV\ICollection - * @implements \Sabre\DAV\IQuota - * - * @link http://github.com/friendica/red - * @license http://opensource.org/licenses/mit-license.php The MIT License (MIT) - */ -class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota { - - /** - * @brief The path inside /cloud - * - * @var string - */ - private $red_path; - private $folder_hash; - /** - * @brief The full path as seen in the browser. - * /cloud + $red_path - * @todo I think this is not used anywhere, we always strip '/cloud' and only use it in debug - * @var string - */ - private $ext_path; - private $root_dir = ''; - private $auth; - /** - * @brief The real path on the filesystem. - * The actual path in store/ with the hashed names. - * - * @var string - */ - private $os_path = ''; - - /** - * @brief Sets up the directory node, expects a full path. - * - * @param string $ext_path a full path - * @param RedBasicAuth &$auth_plugin - */ - public function __construct($ext_path, &$auth_plugin) { -// $ext_path = urldecode($ext_path); - logger('directory ' . $ext_path, LOGGER_DATA); - $this->ext_path = $ext_path; - // remove "/cloud" from the beginning of the path - $modulename = get_app()->module; - $this->red_path = ((strpos($ext_path, '/' . $modulename) === 0) ? substr($ext_path, strlen($modulename) + 1) : $ext_path); - if (! $this->red_path) { - $this->red_path = '/'; - } - $this->auth = $auth_plugin; - $this->folder_hash = ''; - $this->getDir(); - - if ($this->auth->browser) { - $this->auth->browser->set_writeable(); - } - } - - private function log() { - logger('ext_path ' . $this->ext_path, LOGGER_DATA); - logger('os_path ' . $this->os_path, LOGGER_DATA); - logger('red_path ' . $this->red_path, LOGGER_DATA); - } - - /** - * @brief Returns an array with all the child nodes. - * - * @throw \Sabre\DAV\Exception\Forbidden - * @return array \Sabre\DAV\INode[] - */ - public function getChildren() { - logger('children for ' . $this->ext_path, LOGGER_DATA); - $this->log(); - - if (get_config('system', 'block_public') && (! $this->auth->channel_id) && (! $this->auth->observer)) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - if (($this->auth->owner_id) && (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'view_storage'))) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - $contents = RedCollectionData($this->red_path, $this->auth); - return $contents; - } - - /** - * @brief Returns a child by name. - * - * - * @throw \Sabre\DAV\Exception\Forbidden - * @throw \Sabre\DAV\Exception\NotFound - * @param string $name - */ - public function getChild($name) { - logger($name, LOGGER_DATA); - - if (get_config('system', 'block_public') && (! $this->auth->channel_id) && (! $this->auth->observer)) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - if (($this->auth->owner_id) && (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'view_storage'))) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - $modulename = get_app()->module; - if ($this->red_path === '/' && $name === $modulename) { - return new RedDirectory('/' . $modulename, $this->auth); - } - - $x = RedFileData($this->ext_path . '/' . $name, $this->auth); - if ($x) { - return $x; - } - - throw new DAV\Exception\NotFound('The file with name: ' . $name . ' could not be found.'); - } - - /** - * @brief Returns the name of the directory. - * - * @return string - */ - public function getName() { - //logger(basename($this->red_path), LOGGER_DATA); - return (basename($this->red_path)); - } - - /** - * @brief Renames the directory. - * - * @todo handle duplicate directory name - * - * @throw \Sabre\DAV\Exception\Forbidden - * @param string $name The new name of the directory. - * @return void - */ - public function setName($name) { - logger('old name ' . basename($this->red_path) . ' -> ' . $name, LOGGER_DATA); - - if ((! $name) || (! $this->auth->owner_id)) { - logger('permission denied ' . $name); - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - if (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) { - logger('permission denied '. $name); - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - list($parent_path, ) = DAV\URLUtil::splitPath($this->red_path); - $new_path = $parent_path . '/' . $name; - - $r = q("UPDATE attach SET filename = '%s' WHERE hash = '%s' AND uid = %d", - dbesc($name), - dbesc($this->folder_hash), - intval($this->auth->owner_id) - ); - - $this->red_path = $new_path; - } - - /** - * @brief Creates a new file in the directory. - * - * Data will either be supplied as a stream resource, or in certain cases - * as a string. Keep in mind that you may have to support either. - * - * After successful creation of the file, you may choose to return the ETag - * of the new file here. - * - * @throw \Sabre\DAV\Exception\Forbidden - * @param string $name Name of the file - * @param resource|string $data Initial payload - * @return null|string ETag - */ - public function createFile($name, $data = null) { - logger($name, LOGGER_DEBUG); - - if (! $this->auth->owner_id) { - logger('permission denied ' . $name); - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - if (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) { - logger('permission denied ' . $name); - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - $mimetype = z_mime_content_type($name); - - $c = q("SELECT * FROM channel WHERE channel_id = %d AND channel_removed = 0 LIMIT 1", - intval($this->auth->owner_id) - ); - - if (! $c) { - logger('no channel'); - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - $filesize = 0; - $hash = random_string(); - - $f = 'store/' . $this->auth->owner_nick . '/' . (($this->os_path) ? $this->os_path . '/' : '') . $hash; - - $direct = null; - - if($this->folder_hash) { - $r = q("select * from attach where hash = '%s' and is_dir = 1 and uid = %d limit 1", - dbesc($this->folder_hash), - intval($c[0]['channel_id']) - ); - if($r) - $direct = $r[0]; - } - - if(($direct) && (($direct['allow_cid']) || ($direct['allow_gid']) || ($direct['deny_cid']) || ($direct['deny_gid']))) { - $allow_cid = $direct['allow_cid']; - $allow_gid = $direct['allow_gid']; - $deny_cid = $direct['deny_cid']; - $deny_gid = $direct['deny_gid']; - } - else { - $allow_cid = $c[0]['channel_allow_cid']; - $allow_gid = $c[0]['channel_allow_gid']; - $deny_cid = $c[0]['channel_deny_cid']; - $deny_gid = $c[0]['channel_deny_gid']; - } - - $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, folder, os_storage, filetype, filesize, revision, is_photo, data, created, edited, allow_cid, allow_gid, deny_cid, deny_gid ) - VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", - intval($c[0]['channel_account_id']), - intval($c[0]['channel_id']), - dbesc($hash), - dbesc($this->auth->observer), - dbesc($name), - dbesc($this->folder_hash), - intval(1), - dbesc($mimetype), - intval($filesize), - intval(0), - intval($is_photo), - dbesc($f), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc($allow_cid), - dbesc($allow_gid), - dbesc($deny_cid), - dbesc($deny_gid) - ); - - - - // returns the number of bytes that were written to the file, or FALSE on failure - $size = file_put_contents($f, $data); - // delete attach entry if file_put_contents() failed - if ($size === false) { - logger('file_put_contents() failed to ' . $f); - attach_delete($c[0]['channel_id'], $hash); - return; - } - - // returns now - $edited = datetime_convert(); - - - - $is_photo = 0; - $x = @getimagesize($f); - logger('getimagesize: ' . print_r($x,true), LOGGER_DATA); - if(($x) && ($x[2] === IMAGETYPE_GIF || $x[2] === IMAGETYPE_JPEG || $x[2] === IMAGETYPE_PNG)) { - $is_photo = 1; - } - - - // updates entry with filesize and timestamp - $d = q("UPDATE attach SET filesize = '%s', is_photo = %d, edited = '%s' WHERE hash = '%s' AND uid = %d", - dbesc($size), - intval($is_photo), - dbesc($edited), - dbesc($hash), - intval($c[0]['channel_id']) - ); - - // update the folder's lastmodified timestamp - $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", - dbesc($edited), - dbesc($this->folder_hash), - intval($c[0]['channel_id']) - ); - - $maxfilesize = get_config('system', 'maxfilesize'); - if (($maxfilesize) && ($size > $maxfilesize)) { - attach_delete($c[0]['channel_id'], $hash); - return; - } - - // check against service class quota - $limit = service_class_fetch($c[0]['channel_id'], 'attach_upload_limit'); - if ($limit !== false) { - $x = q("SELECT SUM(filesize) AS total FROM attach WHERE aid = %d ", - intval($c[0]['channel_account_id']) - ); - if (($x) && ($x[0]['total'] + $size > $limit)) { - logger('service class limit exceeded for ' . $c[0]['channel_name'] . ' total usage is ' . $x[0]['total'] . ' limit is ' . $limit); - attach_delete($c[0]['channel_id'], $hash); - return; - } - } - - if($is_photo) { - $album = ''; - if($this->folder_hash) { - $f1 = q("select filename from attach WHERE hash = '%s' AND uid = %d", - dbesc($this->folder_hash), - intval($c[0]['channel_id']) - ); - if($f1) - $album = $f1[0]['filename']; - } - - require_once('include/photos.php'); - $args = array( 'resource_id' => $hash, 'album' => $album, 'os_path' => $f, 'filename' => $name, 'getimagesize' => $x, 'directory' => $direct); - $p = photo_upload($c[0],get_app()->get_observer(),$args); - } - - } - - /** - * @brief Creates a new subdirectory. - * - * @param string $name the directory to create - * @return void - */ - public function createDirectory($name) { - logger($name, LOGGER_DEBUG); - - if ((! $this->auth->owner_id) || (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage'))) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - $r = q("SELECT * FROM channel WHERE channel_id = %d AND channel_removed = 0 LIMIT 1", - intval($this->auth->owner_id) - ); - - if ($r) { - $result = attach_mkdir($r[0], $this->auth->observer, array('filename' => $name, 'folder' => $this->folder_hash)); - if (! $result['success']) { - logger('error ' . print_r($result, true), LOGGER_DEBUG); - } - } - } - - /** - * @brief delete directory - */ - - public function delete() { - logger('delete file ' . basename($this->red_path), LOGGER_DEBUG); - - if ((! $this->auth->owner_id) || (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage'))) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - if ($this->auth->owner_id !== $this->auth->channel_id) { - if (($this->auth->observer !== $this->data['creator']) || intval($this->data['is_dir'])) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } - } - - attach_delete($this->auth->owner_id, $this->folder_hash); - } - - - /** - * @brief Checks if a child exists. - * - * @param string $name - * The name to check if it exists. - * @return boolean - */ - public function childExists($name) { - // On /cloud we show a list of available channels. - // @todo what happens if no channels are available? - $modulename = get_app()->module; - if ($this->red_path === '/' && $name === $modulename) { - //logger('We are at ' $modulename . ' show a channel list', LOGGER_DEBUG); - return true; - } - - $x = RedFileData($this->ext_path . '/' . $name, $this->auth, true); - //logger('RedFileData returns: ' . print_r($x, true), LOGGER_DATA); - if ($x) - return true; - - return false; - } - - /** - * @todo add description of what this function does. - * - * @throw \Sabre\DAV\Exception\NotFound - * @return void - */ - function getDir() { - - logger('GetDir: ' . $this->ext_path, LOGGER_DEBUG); - $this->auth->log(); - $modulename = get_app()->module; - - $file = $this->ext_path; - - $x = strpos($file, '/' . $modulename); - if ($x === 0) { - $file = substr($file, strlen($modulename) + 1); - } - - if ((! $file) || ($file === '/')) { - return; - } - - $file = trim($file, '/'); - $path_arr = explode('/', $file); - - if (! $path_arr) - return; - - logger('paths: ' . print_r($path_arr, true), LOGGER_DATA); - - $channel_name = $path_arr[0]; - - $r = q("SELECT channel_id FROM channel WHERE channel_address = '%s' AND channel_removed = 0 LIMIT 1", - dbesc($channel_name) - ); - - if (! $r) { - throw new DAV\Exception\NotFound('The file with name: ' . $channel_name . ' could not be found.'); - } - - $channel_id = $r[0]['channel_id']; - $this->auth->owner_id = $channel_id; - $this->auth->owner_nick = $channel_name; - - $path = '/' . $channel_name; - $folder = ''; - $os_path = ''; - - for ($x = 1; $x < count($path_arr); $x++) { - $r = q("select id, hash, filename, flags, is_dir from attach where folder = '%s' and filename = '%s' and uid = %d and is_dir != 0", - dbesc($folder), - dbesc($path_arr[$x]), - intval($channel_id) - ); - if ($r && intval($r[0]['is_dir'])) { - $folder = $r[0]['hash']; - if (strlen($os_path)) - $os_path .= '/'; - $os_path .= $folder; - - $path = $path . '/' . $r[0]['filename']; - } - } - $this->folder_hash = $folder; - $this->os_path = $os_path; - } - - /** - * @brief Returns the last modification time for the directory, as a UNIX - * timestamp. - * - * It looks for the last edited file in the folder. If it is an empty folder - * it returns the lastmodified time of the folder itself, to prevent zero - * timestamps. - * - * @return int last modification time in UNIX timestamp - */ - public function getLastModified() { - $r = q("SELECT edited FROM attach WHERE folder = '%s' AND uid = %d ORDER BY edited DESC LIMIT 1", - dbesc($this->folder_hash), - intval($this->auth->owner_id) - ); - if (! $r) { - $r = q("SELECT edited FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", - dbesc($this->folder_hash), - intval($this->auth->owner_id) - ); - if (! $r) - return ''; - } - return datetime_convert('UTC', 'UTC', $r[0]['edited'], 'U'); - } - - /** - * @brief Return quota usage. - * - * @fixme Should guests relly see the used/free values from filesystem of the - * complete store directory? - * - * @return array with used and free values in bytes. - */ - public function getQuotaInfo() { - // values from the filesystem of the complete <i>store/</i> directory - $limit = disk_total_space('store'); - $free = disk_free_space('store'); - - if ($this->auth->owner_id) { - $c = q("select * from channel where channel_id = %d and channel_removed = 0 limit 1", - intval($this->auth->owner_id) - ); - - $ulimit = service_class_fetch($c[0]['channel_id'], 'attach_upload_limit'); - $limit = (($ulimit) ? $ulimit : $limit); - - $x = q("select sum(filesize) as total from attach where aid = %d", - intval($c[0]['channel_account_id']) - ); - $free = (($x) ? $limit - $x[0]['total'] : 0); - } - - return array( - $limit - $free, - $free - ); - } -}
\ No newline at end of file diff --git a/include/RedDAV/RedFile.php b/include/RedDAV/RedFile.php deleted file mode 100644 index 3283a6e88..000000000 --- a/include/RedDAV/RedFile.php +++ /dev/null @@ -1,322 +0,0 @@ -<?php - -namespace RedMatrix\RedDAV; - -use Sabre\DAV; - -/** - * @brief This class represents a file in DAV. - * - * It provides all functions to work with files in Red's cloud through DAV protocol. - * - * @extends \Sabre\DAV\Node - * @implements \Sabre\DAV\IFile - * - * @link http://github.com/friendica/red - * @license http://opensource.org/licenses/mit-license.php The MIT License (MIT) - */ -class RedFile extends DAV\Node implements DAV\IFile { - - /** - * The file from attach table. - * - * @var array - * data - * flags - * filename (string) - * filetype (string) - */ - private $data; - /** - * @see \Sabre\DAV\Auth\Backend\BackendInterface - * @var \RedMatrix\RedDAV\RedBasicAuth - */ - private $auth; - /** - * @var string - */ - private $name; - - /** - * Sets up the node, expects a full path name. - * - * @param string $name - * @param array $data from attach table - * @param &$auth - */ - public function __construct($name, $data, &$auth) { - $this->name = $name; - $this->data = $data; - $this->auth = $auth; - - logger(print_r($this->data, true), LOGGER_DATA); - } - - /** - * @brief Returns the name of the file. - * - * @return string - */ - public function getName() { - //logger(basename($this->name), LOGGER_DATA); - return basename($this->name); - } - - /** - * @brief Renames the file. - * - * @throw Sabre\DAV\Exception\Forbidden - * @param string $name The new name of the file. - * @return void - */ - public function setName($newName) { - logger('old name ' . basename($this->name) . ' -> ' . $newName, LOGGER_DATA); - - if ((! $newName) || (! $this->auth->owner_id) || (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage'))) { - logger('permission denied '. $newName); - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - $newName = str_replace('/', '%2F', $newName); - - $r = q("UPDATE attach SET filename = '%s' WHERE hash = '%s' AND id = %d", - dbesc($newName), - dbesc($this->data['hash']), - intval($this->data['id']) - ); - } - - /** - * @brief Updates the data of the file. - * - * @param resource $data - * @return void - */ - public function put($data) { - logger('put file: ' . basename($this->name), LOGGER_DEBUG); - $size = 0; - - // @todo only 3 values are needed - $c = q("SELECT * FROM channel WHERE channel_id = %d AND channel_removed = 0 LIMIT 1", - intval($this->auth->owner_id) - ); - - $is_photo = false; - $album = ''; - - $r = q("SELECT flags, folder, os_storage, filename, is_photo FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", - dbesc($this->data['hash']), - intval($c[0]['channel_id']) - ); - if ($r) { - if (intval($r[0]['os_storage'])) { - $d = q("select folder, data from attach where hash = '%s' and uid = %d limit 1", - dbesc($this->data['hash']), - intval($c[0]['channel_id']) - ); - if($d) { - if($d[0]['folder']) { - $f1 = q("select * from attach where is_dir = 1 and hash = '%s' and uid = %d limit 1", - dbesc($d[0]['folder']), - intval($c[0]['channel_id']) - ); - if($f1) { - $album = $f1[0]['filename']; - $direct = $f1[0]; - } - } - $fname = dbunescbin($d[0]['data']); - if(strpos($fname,'store') === false) - $f = 'store/' . $this->auth->owner_nick . '/' . $fname ; - else - $f = $fname; - - // @todo check return value and set $size directly - @file_put_contents($f, $data); - $size = @filesize($f); - logger('filename: ' . $f . ' size: ' . $size, LOGGER_DEBUG); - } - $gis = @getimagesize($f); - 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; - } - } - else { - // this shouldn't happen any more - $r = q("UPDATE attach SET data = '%s' WHERE hash = '%s' AND uid = %d", - dbescbin(stream_get_contents($data)), - dbesc($this->data['hash']), - intval($this->data['uid']) - ); - $r = q("SELECT length(data) AS fsize FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", - dbesc($this->data['hash']), - intval($this->data['uid']) - ); - if ($r) { - $size = $r[0]['fsize']; - } - } - } - - // returns now() - $edited = datetime_convert(); - - $d = q("UPDATE attach SET filesize = '%s', is_photo = %d, edited = '%s' WHERE hash = '%s' AND uid = %d", - dbesc($size), - intval($is_photo), - dbesc($edited), - dbesc($this->data['hash']), - intval($c[0]['channel_id']) - ); - - if($is_photo) { - require_once('include/photos.php'); - $args = array( 'resource_id' => $this->data['hash'], 'album' => $album, 'os_path' => $f, 'filename' => $r[0]['filename'], 'getimagesize' => $gis, 'directory' => $direct ); - $p = photo_upload($c[0],get_app()->get_observer(),$args); - } - - // update the folder's lastmodified timestamp - $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", - dbesc($edited), - dbesc($r[0]['folder']), - intval($c[0]['channel_id']) - ); - - // @todo do we really want to remove the whole file if an update fails - // because of maxfilesize or quota? - // There is an Exception "InsufficientStorage" or "PaymentRequired" for - // our service class from SabreDAV we could use. - - $maxfilesize = get_config('system', 'maxfilesize'); - if (($maxfilesize) && ($size > $maxfilesize)) { - attach_delete($c[0]['channel_id'], $this->data['hash']); - return; - } - - $limit = service_class_fetch($c[0]['channel_id'], 'attach_upload_limit'); - if ($limit !== false) { - $x = q("select sum(filesize) as total from attach where aid = %d ", - intval($c[0]['channel_account_id']) - ); - if (($x) && ($x[0]['total'] + $size > $limit)) { - logger('service class limit exceeded for ' . $c[0]['channel_name'] . ' total usage is ' . $x[0]['total'] . ' limit is ' . $limit); - attach_delete($c[0]['channel_id'], $this->data['hash']); - return; - } - } - } - - /** - * @brief Returns the raw data. - * - * @return string - */ - public function get() { - logger('get file ' . basename($this->name), LOGGER_DEBUG); - logger('os_path: ' . $this->os_path, LOGGER_DATA); - - $r = q("SELECT data, flags, os_storage, filename, filetype FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", - dbesc($this->data['hash']), - intval($this->data['uid']) - ); - if ($r) { - // @todo this should be a global definition - $unsafe_types = array('text/html', 'text/css', 'application/javascript'); - - if (in_array($r[0]['filetype'], $unsafe_types)) { - header('Content-disposition: attachment; filename="' . $r[0]['filename'] . '"'); - header('Content-type: text/plain'); - } - - if (intval($r[0]['os_storage'])) { - $x = dbunescbin($r[0]['data']); - if(strpos($x,'store') === false) - $f = 'store/' . $this->auth->owner_nick . '/' . (($this->os_path) ? $this->os_path . '/' : '') . $x; - else - $f = $x; - return fopen($f, 'rb'); - } - return dbunescbin($r[0]['data']); - } - } - - /** - * @brief Returns the ETag for a file. - * - * An ETag is a unique identifier representing the current version of the file. - * If the file changes, the ETag MUST change. - * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. - * - * Return null if the ETag can not effectively be determined. - * - * @return null|string - */ - public function getETag() { - $ret = null; - if ($this->data['hash']) { - $ret = '"' . $this->data['hash'] . '"'; - } - return $ret; - } - - /** - * @brief Returns the mime-type for a file. - * - * If null is returned, we'll assume application/octet-stream - * - * @return mixed - */ - public function getContentType() { - // @todo this should be a global definition. - $unsafe_types = array('text/html', 'text/css', 'application/javascript'); - if (in_array($this->data['filetype'], $unsafe_types)) { - return 'text/plain'; - } - return $this->data['filetype']; - } - - /** - * @brief Returns the size of the node, in bytes. - * - * @return int - * filesize in bytes - */ - public function getSize() { - return $this->data['filesize']; - } - - /** - * @brief Returns the last modification time for the file, as a unix - * timestamp. - * - * @return int last modification time in UNIX timestamp - */ - public function getLastModified() { - return datetime_convert('UTC', 'UTC', $this->data['edited'], 'U'); - } - - /** - * @brief Delete the file. - * - * This method checks the permissions and then calls attach_delete() function - * to actually remove the file. - * - * @throw \Sabre\DAV\Exception\Forbidden - */ - public function delete() { - logger('delete file ' . basename($this->name), LOGGER_DEBUG); - - if ((! $this->auth->owner_id) || (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage'))) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - if ($this->auth->owner_id !== $this->auth->channel_id) { - if (($this->auth->observer !== $this->data['creator']) || intval($this->data['is_dir'])) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } - } - - attach_delete($this->auth->owner_id, $this->data['hash']); - } -} diff --git a/include/account.php b/include/account.php index e448bdcc6..4c828003e 100644 --- a/include/account.php +++ b/include/account.php @@ -11,6 +11,7 @@ require_once('include/text.php'); require_once('include/language.php'); require_once('include/datetime.php'); require_once('include/crypto.php'); +require_once('include/identity.php'); function check_account_email($email) { @@ -329,7 +330,7 @@ function send_reg_approval_email($arr) { return($delivered ? true : false); } -function send_verification_email($email,$password) { +function send_register_success_email($email,$password) { $email_msg = replace_macros(get_intltext_template('register_open_eml.tpl'), array( '$sitename' => get_config('system','sitename'), @@ -353,7 +354,7 @@ function send_verification_email($email,$password) { * @param string $hash * @return array|boolean */ -function user_allow($hash) { +function account_allow($hash) { $ret = array('success' => false); @@ -406,6 +407,9 @@ function user_allow($hash) { pop_lang(); + if(get_config('system','auto_channel_create') || UNO) + auto_channel_create($register[0]['uid']); + if ($res) { info( t('Account approved.') . EOL ); return true; @@ -414,7 +418,7 @@ function user_allow($hash) { /** - * @brief Denies a user registration. + * @brief Denies an account registration. * * This does not have to go through user_remove() and save the nickname * permanently against re-registration, as the person was not yet @@ -423,7 +427,8 @@ function user_allow($hash) { * @param string $hash * @return boolean */ -function user_deny($hash) { + +function account_deny($hash) { $register = q("SELECT * FROM register WHERE hash = '%s' LIMIT 1", dbesc($hash) @@ -452,11 +457,14 @@ function user_deny($hash) { } +// called from regver to activate an account from the email verification link -function user_approve($hash) { +function account_approve($hash) { $ret = array('success' => false); + // 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", dbesc($hash) ); @@ -491,6 +499,10 @@ function user_approve($hash) { intval($register[0]['uid']) ); + + if(get_config('system','auto_channel_create') || UNO) + auto_channel_create($register[0]['uid']); + info( t('Account verified. Please login.') . EOL ); return true; diff --git a/include/acl_selectors.php b/include/acl_selectors.php index 4d44ec12e..3c8f34321 100644 --- a/include/acl_selectors.php +++ b/include/acl_selectors.php @@ -210,10 +210,13 @@ function fixacl(&$item) { $item = str_replace(array('<','>'),array('',''),$item); } -function populate_acl($defaults = null,$show_jotnets = true) { +function populate_acl($defaults = null,$show_jotnets = true, $showall = '') { $allow_cid = $allow_gid = $deny_cid = $deny_gid = false; + if(! $showall) + $showall = t('Visible to your default audience'); + if(is_array($defaults)) { $allow_cid = ((strlen($defaults['allow_cid'])) ? explode('><', $defaults['allow_cid']) : array() ); @@ -231,22 +234,21 @@ function populate_acl($defaults = null,$show_jotnets = true) { $jotnets = ''; if($show_jotnets) { -logger('jot_networks'); call_hooks('jot_networks', $jotnets); } $tpl = get_markup_template("acl_selector.tpl"); $o = replace_macros($tpl, array( - '$showall'=> t("Visible to your default audience"), - '$show' => t("Show"), - '$hide' => t("Don't show"), - '$allowcid' => json_encode($allow_cid), - '$allowgid' => json_encode($allow_gid), - '$denycid' => json_encode($deny_cid), - '$denygid' => json_encode($deny_gid), - '$jnetModalTitle' => t('Other networks and post services'), - '$jotnets' => $jotnets, - '$aclModalTitle' => t('Permissions'), + '$showall' => $showall, + '$show' => t("Show"), + '$hide' => t("Don't show"), + '$allowcid' => json_encode($allow_cid), + '$allowgid' => json_encode($allow_gid), + '$denycid' => json_encode($deny_cid), + '$denygid' => json_encode($deny_gid), + '$jnetModalTitle' => t('Other networks and post services'), + '$jotnets' => $jotnets, + '$aclModalTitle' => t('Permissions'), '$aclModalDismiss' => t('Close') )); diff --git a/include/api.php b/include/api.php index 5053977c5..e78e6cc37 100644 --- a/include/api.php +++ b/include/api.php @@ -2106,10 +2106,10 @@ require_once('include/api_auth.php'); 'private' => $private, 'textlimit' => $textlimit, 'sslserver' => $sslserver, 'ssl' => $ssl, 'shorturllength' => '30', 'hubzilla' => array( - 'PLATFORM_NAME' => PLATFORM_NAME, - 'RED_VERSION' => RED_VERSION, + 'PLATFORM_NAME' => Zotlabs\Project\System::get_platform_name(), + 'RED_VERSION' => Zotlabs\Project\System::get_project_version(), 'ZOT_REVISION' => ZOT_REVISION, - 'DB_UPDATE_VERSION' => DB_UPDATE_VERSION + 'DB_UPDATE_VERSION' => Zotlabs\Project\System::get_update_version() ) )); @@ -2142,12 +2142,12 @@ require_once('include/api_auth.php'); if($type === 'xml') { header("Content-type: application/xml"); - echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n" . '<version>' . RED_VERSION . '</version>' . "\r\n"; + echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n" . '<version>' . Zotlabs\Project\System::get_project_version() . '</version>' . "\r\n"; killme(); } elseif($type === 'json') { header("Content-type: application/json"); - echo '"' . RED_VERSION . '"'; + echo '"' . Zotlabs\Project\System::get_project_version() . '"'; killme(); } } diff --git a/include/attach.php b/include/attach.php index 8595d5d86..343922a52 100644 --- a/include/attach.php +++ b/include/attach.php @@ -13,6 +13,7 @@ require_once('include/permissions.php'); require_once('include/security.php'); +require_once('include/group.php'); /** * @brief Guess the mimetype from file ending. @@ -845,7 +846,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { // Caution: This re-uses $sql_options set further above - $r = q("select id, aid, uid, hash, creator, filename, filetype, filesize, revision, folder, os_storage, is_photo, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where uid = %d and hash = '%s' $sql_options limit 1", + $r = q("select * from attach where uid = %d and hash = '%s' $sql_options limit 1", intval($channel_id), dbesc($hash) ); @@ -1242,7 +1243,7 @@ function attach_delete($channel_id, $resource, $is_photo = 0) { $channel_address = (($c) ? $c[0]['channel_address'] : 'notfound'); $photo_sql = (($is_photo) ? " and is_photo = 1 " : ''); - $r = q("SELECT hash, flags, is_dir, is_photo, folder FROM attach WHERE hash = '%s' AND uid = %d $photo_sql limit 1", + $r = q("SELECT hash, os_storage, flags, is_dir, is_photo, folder FROM attach WHERE hash = '%s' AND uid = %d $photo_sql limit 1", dbesc($resource), intval($channel_id) ); @@ -1313,7 +1314,9 @@ function attach_delete($channel_id, $resource, $is_photo = 0) { intval($channel_id) ); - file_activity($channel_id, $object, $object['allow_cid'], $object['allow_gid'], $object['deny_cid'], $object['deny_gid'], 'update', $notify=0); + file_activity($channel_id, $object, $object['allow_cid'], $object['allow_gid'], $object['deny_cid'], $object['deny_gid'], 'update', $notify=1); + + return; } /** @@ -1468,7 +1471,7 @@ function pipe_streams($in, $out) { * @param string $deny_cid * @param string $deny_gid * @param string $verb - * @param boolean $no_activity + * @param boolean $notify */ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $verb, $notify) { @@ -1514,13 +1517,21 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, $mid = item_message_id(); - $arr = array(); + $objtype = ACTIVITY_OBJ_FILE; + $arr = array(); + $arr['aid'] = get_account_id(); + $arr['uid'] = $channel_id; $arr['item_wall'] = 1; $arr['item_origin'] = 1; $arr['item_unseen'] = 1; - - $objtype = ACTIVITY_OBJ_FILE; + $arr['author_xchan'] = $poster['xchan_hash']; + $arr['owner_xchan'] = $poster['xchan_hash']; + $arr['title'] = ''; + $arr['item_hidden'] = 1; + $arr['obj_type'] = $objtype; + $arr['resource_id'] = $object['hash']; + $arr['resource_type'] = 'attach'; $private = (($arr_allow_cid[0] || $arr_allow_gid[0] || $arr_deny_cid[0] || $arr_deny_gid[0]) ? 1 : 0); @@ -1548,36 +1559,27 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, } + //send update activity and create a new one if($update && $verb == 'post' ) { - //send update activity and create a new one - //updates should be sent to everybody with recursive perms and all eventual former allowed members ($object['allow_cid'] etc.). $u_arr_allow_cid = array_unique(array_merge($arr_allow_cid, expand_acl($object['allow_cid']))); $u_arr_allow_gid = array_unique(array_merge($arr_allow_gid, expand_acl($object['allow_gid']))); $u_arr_deny_cid = array_unique(array_merge($arr_deny_cid, expand_acl($object['deny_cid']))); $u_arr_deny_gid = array_unique(array_merge($arr_deny_gid, expand_acl($object['deny_gid']))); + $private = (($u_arr_allow_cid[0] || $u_arr_allow_gid[0] || $u_arr_deny_cid[0] || $u_arr_deny_gid[0]) ? 1 : 0); + $u_mid = item_message_id(); - $arr['aid'] = get_account_id(); - $arr['uid'] = $channel_id; $arr['mid'] = $u_mid; $arr['parent_mid'] = $u_mid; - $arr['author_xchan'] = $poster['xchan_hash']; - $arr['owner_xchan'] = $poster['xchan_hash']; - $arr['title'] = ''; - //updates should be visible to everybody -> perms may have changed - $arr['allow_cid'] = ''; - $arr['allow_gid'] = ''; - $arr['deny_cid'] = ''; - $arr['deny_gid'] = ''; - $arr['item_hidden'] = 1; - $arr['item_private'] = 0; + $arr['allow_cid'] = perms2str($u_arr_allow_cid); + $arr['allow_gid'] = perms2str($u_arr_allow_gid); + $arr['deny_cid'] = perms2str($u_arr_deny_cid); + $arr['deny_gid'] = perms2str($u_arr_deny_gid); + $arr['item_private'] = $private; $arr['verb'] = ACTIVITY_UPDATE; - $arr['obj_type'] = $objtype; $arr['object'] = $u_jsonobject; - $arr['resource_id'] = $object['hash']; - $arr['resource_type'] = 'attach'; $arr['body'] = ''; $post = item_store($arr); @@ -1593,32 +1595,25 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, //notice( t('File activity updated') . EOL); } + //don't create new activity if notify was not enabled if(! $notify) { return; } - $arr = array(); + //don't create new activity if we have an update request but there is no item to update + //this can e.g. happen when deleting images + if(! $y && $verb == 'update') { + return; + } - $arr['aid'] = get_account_id(); - $arr['uid'] = $channel_id; $arr['mid'] = $mid; $arr['parent_mid'] = $mid; - $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['allow_cid'] = perms2str($arr_allow_cid); $arr['allow_gid'] = perms2str($arr_allow_gid); $arr['deny_cid'] = perms2str($arr_deny_cid); $arr['deny_gid'] = perms2str($arr_deny_gid); - $arr['item_hidden'] = 1; $arr['item_private'] = $private; $arr['verb'] = (($update) ? ACTIVITY_UPDATE : ACTIVITY_POST); - $arr['obj_type'] = $objtype; - $arr['resource_id'] = $object['hash']; - $arr['resource_type'] = 'attach'; $arr['object'] = (($update) ? $u_jsonobject : $jsonobject); $arr['body'] = ''; @@ -1705,7 +1700,7 @@ function recursive_activity_recipients($arr_allow_cid, $arr_allow_gid, $arr_deny //turn allow_gid into allow_cid's foreach($arr_allow_gid as $gid) { - $in_group = in_group($gid); + $in_group = group_get_members($gid); $arr_allow_cid = array_unique(array_merge($arr_allow_cid, $in_group)); } @@ -1727,7 +1722,7 @@ function recursive_activity_recipients($arr_allow_cid, $arr_allow_gid, $arr_deny * */ if($parent_arr['allow_gid']) { foreach($parent_arr['allow_gid'][$count] as $gid) { - $in_group = in_group($gid); + $in_group = group_get_members($gid); $parent_arr['allow_cid'][$count] = array_unique(array_merge($parent_arr['allow_cid'][$count], $in_group)); } } @@ -1808,31 +1803,6 @@ function recursive_activity_recipients($arr_allow_cid, $arr_allow_gid, $arr_deny return $ret; } -/** - * @brief Returns members of a group. - * - * @param int $group_id id of the group to look up - */ -function in_group($group_id) { - $group_members = array(); - - /** @TODO make these two queries one with a join. */ - $x = q("SELECT id FROM groups WHERE hash = '%s'", - dbesc($group_id) - ); - - $r = q("SELECT xchan FROM group_member WHERE gid = %d", - intval($x[0]['id']) - ); - - foreach($r as $ig) { - $group_members[] = $ig['xchan']; - } - - return $group_members; -} - - function filepath_macro($s) { return str_replace( diff --git a/include/auth.php b/include/auth.php index 4f0c4c928..1a7110c20 100644 --- a/include/auth.php +++ b/include/auth.php @@ -12,33 +12,6 @@ require_once('include/api_auth.php'); require_once('include/security.php'); -/** - * @brief Resets the current session. - * - * @return void - */ -function nuke_session() { - new_cookie(0); // 0 means delete on browser exit - - unset($_SESSION['authenticated']); - unset($_SESSION['account_id']); - unset($_SESSION['uid']); - unset($_SESSION['visitor_id']); - unset($_SESSION['administrator']); - unset($_SESSION['cid']); - unset($_SESSION['theme']); - unset($_SESSION['mobile_theme']); - unset($_SESSION['show_mobile']); - unset($_SESSION['page_flags']); - unset($_SESSION['delegate']); - unset($_SESSION['delegate_channel']); - unset($_SESSION['my_url']); - unset($_SESSION['my_address']); - unset($_SESSION['addr']); - unset($_SESSION['return_url']); - unset($_SESSION['remote_service_class']); - unset($_SESSION['remote_hub']); -} /** * @brief Verify login credentials. diff --git a/include/bbcode.php b/include/bbcode.php index a8372d728..eefe7fe98 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -593,6 +593,11 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $Text = preg_replace("/\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '<a class="zrl" href="$1" target="_blank" >$1</a>', $Text); $Text = preg_replace("/\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<a class="zrl" href="$1" target="_blank" >$2</a>', $Text); } + + // Remove bookmarks from UNO + if (UNO) + $Text = str_replace('<span class="bookmark-identifier">#^</span>', '', $Text); + // Perform MAIL Search if (strpos($Text,'[/mail]') !== false) { $Text = preg_replace("/\[mail\]([$MAILSearchString]*)\[\/mail\]/", '<a href="mailto:$1" target="_blank" >$1</a>', $Text); @@ -684,7 +689,11 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) } // Check for centered text if (strpos($Text,'[/center]') !== false) { - $Text = preg_replace("(\[center\](.*?)\[\/center\])ism", "<div style=\"text-align:center;\">$1</div>", $Text); + $Text = preg_replace("(\[center\](.*?)\[\/center\])ism", "<div style=\"text-align:center;\">$1</div>", $Text); + } + // Check for footer + if (strpos($Text,'[/footer]') !== false) { + $Text = preg_replace("(\[footer\](.*?)\[\/footer\])ism", "<div class=\"wall-item-footer\">$1</div>", $Text); } // Check for list text $Text = str_replace("[*]", "<li>", $Text); diff --git a/include/cli_startup.php b/include/cli_startup.php index 027d62953..70ab1a24a 100644 --- a/include/cli_startup.php +++ b/include/cli_startup.php @@ -15,6 +15,9 @@ function cli_startup() { if(is_null($db)) { @include(".htconfig.php"); + if(! defined('UNO')) + define('UNO', 0); + $a->timezone = ((x($default_timezone)) ? $default_timezone : 'UTC'); date_default_timezone_set($a->timezone); diff --git a/include/comanche.php b/include/comanche.php index 1537226ca..ddf331321 100644 --- a/include/comanche.php +++ b/include/comanche.php @@ -275,6 +275,7 @@ function comanche_widget($name, $text) { $vars = array(); $matches = array(); + $cnt = preg_match_all("/\[var=(.*?)\](.*?)\[\/var\]/ism", $text, $matches, PREG_SET_ORDER); if ($cnt) { foreach ($matches as $mtch) { @@ -287,8 +288,7 @@ function comanche_widget($name, $text) { if(! function_exists($func)) { if(file_exists('widget/' . trim($name) . '.php')) require_once('widget/' . trim($name) . '.php'); - elseif(folder_exists('widget/'. trim($name)) - && (file_exists('widget/' . trim($name) . '/' . trim($name) . '.php'))) + elseif(file_exists('widget/' . trim($name) . '/' . trim($name) . '.php')) require_once('widget/' . trim($name) . '/' . trim($name) . '.php'); } else { diff --git a/include/config.php b/include/config.php index c94d25eb8..51d4e99ac 100644 --- a/include/config.php +++ b/include/config.php @@ -531,3 +531,86 @@ function del_xconfig($xchan, $family, $key) { ); return $ret; } + + +// account configuration storage is built on top of the under-utilised xconfig + +function load_aconfig($account_id) { + load_xconfig('a_' . $account_id); +} + +function get_aconfig($account_id, $family, $key) { + return get_xconfig('a_' . $account_id, $family, $key); +} + +function set_aconfig($account_id, $family, $key, $value) { + return set_xconfig('a_' . $account_id, $family, $key, $value); +} + +function del_aconfig($account_id, $family, $key) { + return del_xconfig('a_' . $account_id, $family, $key); +} + + +function load_abconfig($chash,$xhash) { + $r = q("select * from abconfig where chan = '%s' and xchan = '%s'", + dbesc($chash), + dbesc($xhash) + ); + return $r; +} + +function get_abconfig($chash,$xhash,$family,$key) { + $r = q("select * from abconfig where chan = '%s' and xchan = '%s' and cat = '%s' and k = '%s' limit 1", + dbesc($chash), + dbesc($xhash), + dbesc($family), + dbesc($key) + ); + if($r) { + return ((preg_match('|^a:[0-9]+:{.*}$|s', $r[0]['v'])) ? unserialize($r[0]['v']) : $r[0]['v']); + } + return false; +} + + +function set_abconfig($chash,$xhash,$family,$key,$value) { + + $dbvalue = ((is_array($value)) ? serialize($value) : $value); + $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); + + if(get_abconfig($chash,$xhash,$family,$key) === false) { + $r = q("insert into abconfig ( chan, xchan, cat, k, v ) values ( '%s', '%s', '%s', '%s', '%s' ) ", + dbesc($chash), + dbesc($xhash), + dbesc($family), + dbesc($key), + dbesc($dbvalue) + ); + } + else { + $r = q("update abconfig set v = '%s' where chan = '%s' and xchan = '%s' and cat = '%s' and k = '%s' ", + dbesc($dbvalue), + dbesc($chash), + dbesc($xhash), + dbesc($family), + dbesc($key) + ); + } + if($r) + return $value; + return false; +} + + +function del_abconfig($chash,$xhash,$family,$key) { + + $r = q("delete from abconfig where chan = '%s' and xchan = '%s' and cat = '%s' and k = '%s' ", + dbesc($chash), + dbesc($xhash), + dbesc($family), + dbesc($key) + ); + + return $r; +} diff --git a/include/contact_selectors.php b/include/contact_selectors.php index 8671f1bd1..0de4ece00 100644 --- a/include/contact_selectors.php +++ b/include/contact_selectors.php @@ -73,16 +73,18 @@ function contact_poll_interval($current, $disabled = false) { function network_to_name($s) { $nets = array( - NETWORK_DFRN => t('Friendica'), - NETWORK_OSTATUS => t('OStatus'), - NETWORK_FEED => t('RSS/Atom'), - NETWORK_MAIL => t('Email'), - NETWORK_DIASPORA => t('Diaspora'), - NETWORK_FACEBOOK => t('Facebook'), - NETWORK_ZOT => t('Zot!'), - NETWORK_LINKEDIN => t('LinkedIn'), - NETWORK_XMPP => t('XMPP/IM'), - NETWORK_MYSPACE => t('MySpace'), + NETWORK_DFRN => t('Friendica'), + NETWORK_FRND => t('Friendica'), + NETWORK_OSTATUS => t('OStatus'), + NETWORK_GNUSOCIAL => t('GNU-Social'), + NETWORK_FEED => t('RSS/Atom'), + NETWORK_MAIL => t('Email'), + NETWORK_DIASPORA => t('Diaspora'), + NETWORK_FACEBOOK => t('Facebook'), + NETWORK_ZOT => t('Zot'), + NETWORK_LINKEDIN => t('LinkedIn'), + NETWORK_XMPP => t('XMPP/IM'), + NETWORK_MYSPACE => t('MySpace'), ); call_hooks('network_to_name', $nets); diff --git a/include/conversation.php b/include/conversation.php index 747bb5d0a..676067f86 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -942,25 +942,35 @@ function item_photo_menu($item){ $clean_url = normalise_link($item['author-link']); } - $menu = Array( + $poco_rating = get_config('system','poco_rating_enable'); + // if unset default to enabled + if($poco_rating === false) + $poco_rating = true; + + $ratings_url = (($poco_rating) ? z_root() . '/ratings/' . urlencode($item['author_xchan']) : ''); + + $post_menu = Array( t("View Source") => $vsrc_link, t("Follow Thread") => $sub_link, t("Unfollow Thread") => $unsub_link, - t("View Status") => $status_link, + ); + + $author_menu = array( t("View Profile") => $profile_link, - t("View Photos") => $photos_link, t("Activity/Posts") => $posts_link, t("Connect") => $follow_url, t("Edit Connection") => $contact_url, - t("Send PM") => $pm_url, + t("Message") => $pm_url, + t('Ratings') => $ratings_url, t("Poke") => $poke_link ); - $args = array('item' => $item, 'menu' => $menu); + + $args = array('item' => $item, 'post_menu' => $post_menu, 'author_menu' => $author_menu); call_hooks('item_photo_menu', $args); - $menu = $args['menu']; + $menu = array_merge($args['post_menu'],$args['author_menu']); $o = ""; foreach($menu as $k=>$v){ @@ -1100,6 +1110,11 @@ function status_editor($a, $x, $popup = false) { $o = ''; + require_once('include/Contact.php'); + $c = channelx_by_n($x['profile_uid']); + if($c && $c['channel_moved']) + return $o; + $geotag = (($x['allow_location']) ? replace_macros(get_markup_template('jot_geotag.tpl'), array()) : ''); $plaintext = true; @@ -1585,8 +1600,17 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ if (is_null($nickname)) $nickname = $channel['channel_address']; + $uid = (($a->profile['profile_uid']) ? $a->profile['profile_uid'] : local_channel()); + if($uid == local_channel()) { + $cal_link = ''; + } + else { + $cal_link = '/cal/' . $nickname; + } + + if (get_pconfig($uid, 'system', 'noprofiletabs')) return; @@ -1634,6 +1658,17 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ ); } + if($p['view_stream'] && $cal_link) { + $tabs[] = array( + 'label' => t('Events'), + 'url' => $a->get_baseurl() . $cal_link, + 'sel' => ((argv(0) == 'cal' || argv(0) == 'events') ? 'active' : ''), + 'title' => t('Events'), + 'id' => 'event-tab', + ); + } + + if ($p['chat']) { require_once('include/chat.php'); $has_chats = chatroom_list_count($uid); diff --git a/include/crypto.php b/include/crypto.php index 494a2a5b9..3cddc7581 100644 --- a/include/crypto.php +++ b/include/crypto.php @@ -20,7 +20,23 @@ function rsa_verify($data,$sig,$key,$alg = 'sha256') { if(intval(OPENSSL_ALGO_SHA256) && $alg === 'sha256') $alg = OPENSSL_ALGO_SHA256; - $verify = openssl_verify($data,$sig,$key,$alg); + $verify = @openssl_verify($data,$sig,$key,$alg); + + if(! $verify) { + while($msg = openssl_error_string()) + logger('openssl_verify: ' . $msg,LOGGER_NORMAL,LOG_ERR); + logger('openssl_verify: key: ' . $key, LOGGER_DEBUG, LOG_ERR); + // provide a backtrace so that we can debug key issues + if(version_compare(PHP_VERSION, '5.4.0') >= 0) { + $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + if($stack) { + foreach($stack as $s) { + logger('stack: ' . basename($s['file']) . ':' . $s['line'] . ':' . $s['function'] . '()',LOGGER_DEBUG,LOG_ERR); + } + } + } + } + return $verify; } @@ -241,6 +257,7 @@ function pkcs1_encode($Modulus,$PublicExponent) { } +// http://stackoverflow.com/questions/27568570/how-to-convert-raw-modulus-exponent-to-rsa-public-key-pem-format function metopem($m,$e) { $der = pkcs8_encode($m,$e); $key = DerToPem($der,false); diff --git a/include/datetime.php b/include/datetime.php index 1d10e7ad7..8ee70a6fa 100644 --- a/include/datetime.php +++ b/include/datetime.php @@ -119,6 +119,8 @@ function datetime_convert($from = 'UTC', $to = 'UTC', $s = 'now', $fmt = "Y-m-d * @return string */ function dob($dob) { + $a = get_app(); + list($year, $month, $day) = sscanf($dob, '%4d-%2d-%2d'); $f = get_config('system', 'birthday_input_format'); if (! $f) @@ -129,7 +131,15 @@ function dob($dob) { else $value = (($year) ? datetime_convert('UTC','UTC',$dob,'Y-m-d') : datetime_convert('UTC','UTC',$dob,'m-d')); - $o = '<input type="text" name="dob" value="' . $value . '" placeholder="' . t('YYYY-MM-DD or MM-DD') . '" />'; + $o = replace_macros(get_markup_template("field_input.tpl"), array('$field' => array( + 'dob', + t('Birthday'), + $value, + ((intval($value)) ? t('Age: ') . age($value,$a->user['timezone'],$a->user['timezone']) : ''), + '', + 'placeholder="' . t('YYYY-MM-DD or MM-DD') .'"' + ))); + // if ($dob && $dob != '0000-00-00') // $o = datesel($f,mktime(0,0,0,0,0,1900),mktime(),mktime(0,0,0,$month,$day,$year),'dob'); @@ -269,15 +279,16 @@ function relative_date($posted_date, $format = null) { return t('less than a second ago'); } - $a = array( 12 * 30 * 24 * 60 * 60 => array( t('year'), t('years')), - 30 * 24 * 60 * 60 => array( t('month'), t('months')), - 7 * 24 * 60 * 60 => array( t('week'), t('weeks')), - 24 * 60 * 60 => array( t('day'), t('days')), - 60 * 60 => array( t('hour'), t('hours')), - 60 => array( t('minute'), t('minutes')), - 1 => array( t('second'), t('seconds')) + $a = array( 12 * 30 * 24 * 60 * 60 => 'y', + 30 * 24 * 60 * 60 => 'm', + 7 * 24 * 60 * 60 => 'w', + 24 * 60 * 60 => 'd', + 60 * 60 => 'h', + 60 => 'i', + 1 => 's' ); + foreach ($a as $secs => $str) { $d = $etime / $secs; if ($d >= 1) { @@ -285,11 +296,43 @@ function relative_date($posted_date, $format = null) { if (! $format) $format = t('%1$d %2$s ago', 'e.g. 22 hours ago, 1 minute ago'); - return sprintf($format, $r, (($r == 1) ? $str[0] : $str[1])); + return sprintf($format, $r, plural_dates($str,$r)); } } } +function plural_dates($k,$n) { + + switch($k) { + case 'y': + return tt('year','years',$n,'relative_date'); + break; + case 'm': + return tt('month','months',$n,'relative_date'); + break; + case 'w': + return tt('week','weeks',$n,'relative_date'); + break; + case 'd': + return tt('day','days',$n,'relative_date'); + break; + case 'h': + return tt('hour','hours',$n,'relative_date'); + break; + case 'i': + return tt('minute','minutes',$n,'relative_date'); + break; + case 's': + return tt('second','seconds',$n,'relative_date'); + break; + default: + return; + } +} + + + + /** * @brief Returns timezone correct age in years. * @@ -516,7 +559,7 @@ function update_birthdays() { $ev['event_xchan'] = $rr['xchan_hash']; $ev['start'] = datetime_convert('UTC', 'UTC', $rr['abook_dob']); $ev['finish'] = datetime_convert('UTC', 'UTC', $rr['abook_dob'] . ' + 1 day '); - $ev['adjust'] = 1; + $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'), '[zrl=' . $rr['xchan_url'] . ']' . $rr['xchan_name'] . '[/zrl]') ; diff --git a/include/dba/dba_driver.php b/include/dba/dba_driver.php index 1fb3d5c00..3c5b0b67e 100755 --- a/include/dba/dba_driver.php +++ b/include/dba/dba_driver.php @@ -303,9 +303,9 @@ function q($sql) { if($stmt === false) { if(version_compare(PHP_VERSION, '5.4.0') >= 0) logger('dba: vsprintf error: ' . - print_r(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1), true)); + print_r(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1), true),LOGGER_NORMAL,LOG_CRIT); else - logger('dba: vsprintf error: ' . print_r(debug_backtrace(), true)); + logger('dba: vsprintf error: ' . print_r(debug_backtrace(), true),LOGGER_NORMAL,LOG_CRIT); } return $db->q($stmt); } @@ -314,7 +314,7 @@ function q($sql) { * This will happen occasionally trying to store the * session data after abnormal program termination */ - logger('dba: no database: ' . print_r($args,true)); + logger('dba: no database: ' . print_r($args,true),LOGGER_NORMAL,LOG_CRIT); return false; } @@ -385,6 +385,7 @@ function db_getfunc($f) { if(isset($lookup[$f]) && isset($lookup[$f][ACTIVE_DBTYPE])) return $lookup[$f][ACTIVE_DBTYPE]; - logger('Unable to abstract DB function "'. $f . '" for dbtype ' . ACTIVE_DBTYPE, LOGGER_DEBUG); + logger('Unable to abstract DB function "'. $f . '" for dbtype ' . ACTIVE_DBTYPE, LOGGER_DEBUG, LOG_ERR); return $f; } + diff --git a/include/dba/dba_mysqli.php b/include/dba/dba_mysqli.php index 74a999974..6986d4586 100755 --- a/include/dba/dba_mysqli.php +++ b/include/dba/dba_mysqli.php @@ -32,7 +32,7 @@ class dba_mysqli extends dba_driver { if($this->error) { - logger('dba_mysqli: ERROR: ' . printable($sql) . "\n" . $this->error); + logger('dba_mysqli: 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); } @@ -40,13 +40,13 @@ class dba_mysqli extends dba_driver { if(($result === true) || ($result === false)) { if($this->debug) { - logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returns ' . (($result) ? 'true' : 'false')); + logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returns ' . (($result) ? 'true' : 'false'), LOGGER_NORMAL,(($result) ? LOG_INFO : LOG_ERR)); } return $result; } if($this->debug) { - logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returned ' . $result->num_rows . ' results.'); + logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returned ' . $result->num_rows . ' results.', LOGGER_NORMAL, LOG_INFO); } $r = array(); @@ -55,7 +55,7 @@ class dba_mysqli extends dba_driver { $r[] = $x; $result->free_result(); if($this->debug) { - logger('dba_mysqli: ' . printable(print_r($r,true))); + logger('dba_mysqli: ' . printable(print_r($r,true)), LOGGER_NORMAL, LOG_INFO); } } return $r; diff --git a/include/deliver.php b/include/deliver.php index 7ff0fa125..40df543d5 100644 --- a/include/deliver.php +++ b/include/deliver.php @@ -31,6 +31,7 @@ function deliver_run($argv, $argc) { if(($r[0]['outq_posturl'] === z_root() . '/post') && ($r[0]['outq_msg'])) { logger('deliver: local delivery', LOGGER_DEBUG); + // local delivery // we should probably batch these and save a few delivery processes @@ -72,7 +73,7 @@ function deliver_run($argv, $argc) { } } - // otherwise it's a remote delivery - call queue_deliver(); + // otherwise it's a remote delivery - call queue_deliver() with the $immediate flag queue_deliver($r[0],true); diff --git a/include/enotify.php b/include/enotify.php index c9b6e0463..e182377c0 100644 --- a/include/enotify.php +++ b/include/enotify.php @@ -273,14 +273,14 @@ function notification($params) { $preamble = sprintf( t('%1$s, you\'ve received an new connection request from \'%2$s\' at %3$s'), $recip['channel_name'], $sender['xchan_name'], $sitename); $epreamble = sprintf( t('%1$s, you\'ve received [zrl=%2$s]a new connection request[/zrl] from %3$s.'), $recip['channel_name'], - $itemlink, + $siteurl . '/connections/ifpending', '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]'); $body = sprintf( t('You may visit their profile at %s'),$sender['xchan_url']); $sitelink = t('Please visit %s to approve or reject the connection request.'); - $tsitelink = sprintf( $sitelink, $siteurl ); - $hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>'); - $itemlink = $params['link']; + $tsitelink = sprintf( $sitelink, $siteurl . '/connections/ifpending'); + $hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '/connections/ifpending">' . $sitename . '</a>'); + $itemlink = $params['link']; } if ($params['type'] == NOTIFY_SUGGEST) { @@ -529,6 +529,7 @@ function notification($params) { $tpl = get_markup_template('email_notify_html.tpl'); $email_html_body = replace_macros($tpl,array( '$banner' => $datarray['banner'], + '$notify_icon' => Zotlabs\Project\System::get_notify_icon(), '$product' => $datarray['product'], '$preamble' => $datarray['preamble'], '$sitename' => $datarray['sitename'], diff --git a/include/event.php b/include/event.php index 539bfe484..2969f4f61 100644 --- a/include/event.php +++ b/include/event.php @@ -67,7 +67,7 @@ function ical_wrapper($ev) { $o .= "BEGIN:VCALENDAR"; $o .= "\r\nVERSION:2.0"; $o .= "\r\nMETHOD:PUBLISH"; - $o .= "\r\nPRODID:-//" . get_config('system','sitename') . "//" . PLATFORM_NAME . "//" . strtoupper(get_app()->language). "\r\n"; + $o .= "\r\nPRODID:-//" . get_config('system','sitename') . "//" . Zotlabs\Project\System::get_platform_name() . "//" . strtoupper(get_app()->language). "\r\n"; if(array_key_exists('start', $ev)) $o .= format_event_ical($ev); else { @@ -440,6 +440,17 @@ function event_addtocal($item_id, $uid) { $ev['event_hash'] = $item['resource_id']; } + if($ev->private) + $ev['allow_cid'] = '<' . $channel['channel_hash'] . '>'; + else { + $acl = new Zotlabs\Access\AccessList($channel); + $x = $acl->get(); + $ev['allow_cid'] = $x['allow_cid']; + $ev['allow_gid'] = $x['allow_gid']; + $ev['deny_cid'] = $x['deny_cid']; + $ev['deny_gid'] = $x['deny_gid']; + } + $event = event_store_event($ev); if($event) { $r = q("update item set resource_id = '%s', resource_type = 'event' where id = %d and uid = %d", diff --git a/include/features.php b/include/features.php index 4e962b00e..ff6b71d4c 100644 --- a/include/features.php +++ b/include/features.php @@ -5,12 +5,19 @@ */ + + + function feature_enabled($uid,$feature) { - $x = get_pconfig($uid,'feature',$feature); + + $x = get_config('feature_lock',$feature); if($x === false) { - $x = get_config('feature',$feature); - if($x === false) - $x = get_feature_default($feature); + $x = get_pconfig($uid,'feature',$feature); + if($x === false) { + $x = get_config('feature',$feature); + if($x === false) + $x = get_feature_default($feature); + } } $arr = array('uid' => $uid, 'feature' => $feature, 'enabled' => $x); call_hooks('feature_enabled',$arr); @@ -18,7 +25,7 @@ function feature_enabled($uid,$feature) { } function get_feature_default($feature) { - $f = get_features(); + $f = get_features(false); foreach($f as $cat) { foreach($cat as $feat) { if(is_array($feat) && $feat[0] === $feature) @@ -29,66 +36,92 @@ function get_feature_default($feature) { } -function get_features() { +function get_features($filtered = true) { + + if(UNO && $filtered) + return array(); $arr = array( // General 'general' => array( t('General Features'), -// This is per post, and different from fixed expiration 'expire' which isn't working yet - array('content_expire', t('Content Expiration'), t('Remove posts/comments and/or private messages at a future time'), false), - array('multi_profiles', t('Multiple Profiles'), t('Ability to create multiple profiles'), false), - array('advanced_profiles', t('Advanced Profiles'), t('Additional profile sections and selections'),false), - array('profile_export', t('Profile Import/Export'), t('Save and load profile details across sites/channels'),false), - array('webpages', t('Web Pages'), t('Provide managed web pages on your channel'),false), - array('private_notes', t('Private Notes'), t('Enables a tool to store notes and reminders'),false), - array('nav_channel_select', t('Navigation Channel Select'), t('Change channels directly from within the navigation dropdown menu'),false), - array('photo_location', t('Photo Location'), t('If location data is available on uploaded photos, link this to a map.'),false), - - array('expert', t('Expert Mode'), t('Enable Expert Mode to provide advanced configuration options'),false), - array('premium_channel', t('Premium Channel'), t('Allows you to set restrictions and terms on those that connect with your channel'),false), + // This is per post, and different from fixed expiration 'expire' which isn't working yet + array('content_expire', t('Content Expiration'), t('Remove posts/comments and/or private messages at a future time'), false, get_config('feature_lock','content_expire')), + array('multi_profiles', t('Multiple Profiles'), t('Ability to create multiple profiles'), false, get_config('feature_lock','multi_profiles')), + array('advanced_profiles', t('Advanced Profiles'), t('Additional profile sections and selections'),false,get_config('feature_lock','advanced_profiles')), + array('profile_export', t('Profile Import/Export'), t('Save and load profile details across sites/channels'),false,get_config('feature_lock','profile_export')), + array('webpages', t('Web Pages'), t('Provide managed web pages on your channel'),false,get_config('feature_lock','webpages')), + array('hide_rating', t('Hide Rating'), t('Hide the rating buttons on your channel and profile pages. Note: People can still rate you somewhere else.'),false,get_config('feature_lock','hide_rating')), + array('private_notes', t('Private Notes'), t('Enables a tool to store notes and reminders (note: not encrypted)'),false,get_config('feature_lock','private_notes')), + array('nav_channel_select', t('Navigation Channel Select'), t('Change channels directly from within the navigation dropdown menu'),false,get_config('feature_lock','nav_channel_select')), + array('photo_location', t('Photo Location'), t('If location data is available on uploaded photos, link this to a map.'),false,get_config('feature_lock','photo_location')), + + array('smart_birthdays', t('Smart Birthdays'), t('Make birthday events timezone aware in case your friends are scattered across the planet.'),true,get_config('feature_lock','smart_birthdays')), + array('expert', t('Expert Mode'), t('Enable Expert Mode to provide advanced configuration options'),false,get_config('feature_lock','expert')), + array('premium_channel', t('Premium Channel'), t('Allows you to set restrictions and terms on those that connect with your channel'),false,get_config('feature_lock','premium_channel')), ), // Post composition 'composition' => array( t('Post Composition Features'), -// array('richtext', t('Richtext Editor'), t('Enable richtext editor'),false), - array('markdown', t('Use Markdown'), t('Allow use of "Markdown" to format posts'),false), - array('large_photos', t('Large Photos'), t('Include large (1024px) photo thumbnails in posts. If not enabled, use small (640px) photo thumbnails'),false), - array('channel_sources', t('Channel Sources'), t('Automatically import channel content from other channels or feeds'),false), - array('content_encrypt', t('Even More Encryption'), t('Allow optional encryption of content end-to-end with a shared secret key'),false), - array('consensus_tools', t('Enable Voting Tools'), t('Provide a class of post which others can vote on'),false), - array('delayed_posting', t('Delayed Posting'), t('Allow posts to be published at a later date'),false), - array('suppress_duplicates', t('Suppress Duplicate Posts/Comments'), t('Prevent posts with identical content to be published with less than two minutes in between submissions.'),true), +// array('richtext', t('Richtext Editor'), t('Enable richtext editor'),falseget_config('feature_lock','richtext')), +// array('markdown', t('Use Markdown'), t('Allow use of "Markdown" to format posts'),false,get_config('feature_lock','markdown')), + array('large_photos', t('Large Photos'), t('Include large (1024px) photo thumbnails in posts. If not enabled, use small (640px) photo thumbnails'),false,get_config('feature_lock','large_photos')), + array('channel_sources', t('Channel Sources'), t('Automatically import channel content from other channels or feeds'),false,get_config('feature_lock','channel_sources')), + array('content_encrypt', t('Even More Encryption'), t('Allow optional encryption of content end-to-end with a shared secret key'),false,get_config('feature_lock','content_encrypt')), + array('consensus_tools', t('Enable Voting Tools'), t('Provide a class of post which others can vote on'),false,get_config('feature_lock','consensus_tools')), + array('delayed_posting', t('Delayed Posting'), t('Allow posts to be published at a later date'),false,get_config('feature_lock','delayed_posting')), + array('suppress_duplicates', t('Suppress Duplicate Posts/Comments'), t('Prevent posts with identical content to be published with less than two minutes in between submissions.'),true,get_config('feature_lock','suppress_duplicates')), ), // Network Tools 'net_module' => array( t('Network and Stream Filtering'), - array('archives', t('Search by Date'), t('Ability to select posts by date ranges'),false), - array('groups', t('Collections Filter'), t('Enable widget to display Network posts only from selected collections'),false), - array('savedsearch', t('Saved Searches'), t('Save search terms for re-use'),false), - array('personal_tab', t('Network Personal Tab'), t('Enable tab to display only Network posts that you\'ve interacted on'),false), - array('new_tab', t('Network New Tab'), t('Enable tab to display all new Network activity'),false), - array('affinity', t('Affinity Tool'), t('Filter stream activity by depth of relationships'),false), - array('connfilter', t('Connection Filtering'), t('Filter incoming posts from connections based on keywords/content')), - array('suggest', t('Suggest Channels'), t('Show channel suggestions'),false), + array('archives', t('Search by Date'), t('Ability to select posts by date ranges'),false,get_config('feature_lock','archives')), + array('groups', t('Privacy Groups'), t('Enable management and selection of privacy groups'),true,get_config('feature_lock','groups')), + array('savedsearch', t('Saved Searches'), t('Save search terms for re-use'),false,get_config('feature_lock','savedsearch')), + array('personal_tab', t('Network Personal Tab'), t('Enable tab to display only Network posts that you\'ve interacted on'),false,get_config('feature_lock','personal_tab')), + array('new_tab', t('Network New Tab'), t('Enable tab to display all new Network activity'),false,get_config('feature_lock','new_tab')), + array('affinity', t('Affinity Tool'), t('Filter stream activity by depth of relationships'),false,get_config('feature_lock','affinity')), + array('connfilter', t('Connection Filtering'), t('Filter incoming posts from connections based on keywords/content'),false,get_config('feature_lock','connfilter')), + array('suggest', t('Suggest Channels'), t('Show channel suggestions'),false,get_config('feature_lock','suggest')), ), // Item tools 'tools' => array( t('Post/Comment Tools'), - array('commtag', t('Tagging'), t('Ability to tag existing posts'),false), - array('categories', t('Post Categories'), t('Add categories to your posts'),false), - array('filing', t('Saved Folders'), t('Ability to file posts under folders'),false), - array('dislike', t('Dislike Posts'), t('Ability to dislike posts/comments'),false), - array('star_posts', t('Star Posts'), t('Ability to mark special posts with a star indicator'),false), - array('tagadelic', t('Tag Cloud'), t('Provide a personal tag cloud on your channel page'),false), + array('commtag', t('Community Tagging'), t('Ability to tag existing posts'),false,get_config('feature_lock','commtag')), + array('categories', t('Post Categories'), t('Add categories to your posts'),false,get_config('feature_lock','categories')), + array('filing', t('Saved Folders'), t('Ability to file posts under folders'),false,get_config('feature_lock','filing')), + array('dislike', t('Dislike Posts'), t('Ability to dislike posts/comments'),false,get_config('feature_lock','dislike')), + array('star_posts', t('Star Posts'), t('Ability to mark special posts with a star indicator'),false,get_config('feature_lock','star_posts')), + array('tagadelic', t('Tag Cloud'), t('Provide a personal tag cloud on your channel page'),false,get_config('feature_lock','tagedelic')), ), ); + // removed any locked features and remove the entire category if this makes it empty + + if($filtered) { + foreach($arr as $k => $x) { + $has_items = false; + for($y = 0; $y < count($arr[$k]); $y ++) { + if(is_array($arr[$k][$y])) { + if($arr[$k][$y][4] === false) { + $has_items = true; + } + else { + unset($arr[$k][$y]); + } + } + } + if(! $has_items) { + unset($arr[$k]); + } + } + } + call_hooks('get_features',$arr); return $arr; } diff --git a/include/follow.php b/include/follow.php index 97be82da7..70e717cfc 100644 --- a/include/follow.php +++ b/include/follow.php @@ -122,6 +122,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) else $permissions = $j['permissions']; + foreach($permissions as $k => $v) { if($v) { $their_perms = $their_perms | intval($global_perms[$k][1]); @@ -133,26 +134,29 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) $their_perms = 0; $xchan_hash = ''; - $r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' limit 1", dbesc($url), dbesc($url) ); - if(! $r) { // attempt network auto-discovery - if(strpos($url,'@') && (! $is_http)) { - $d = discover_by_webbie($url); - } - elseif($is_http) { - if(get_config('system','feed_contacts')) + + $d = discover_by_webbie($url); + + if((! $d) && ($is_http)) { + + // try RSS discovery + + if(get_config('system','feed_contacts')) { $d = discover_by_url($url); + } else { $result['message'] = t('Protocol disabled.'); return $result; } } + if($d) { $r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' limit 1", dbesc($url), @@ -160,6 +164,9 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) ); } } + + // if discovery was a success we should have an xchan record in $r + if($r) { $xchan = $r[0]; $xchan_hash = $r[0]['xchan_hash']; @@ -167,13 +174,16 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) } } + if(! $xchan_hash) { $result['message'] = t('Channel discovery failed.'); logger('follow: ' . $result['message']); return $result; } - $x = array('channel_id' => $uid, 'follow_address' => $url, 'xchan' => $r[0], 'allowed' => 1, 'singleton' => 0); + $allowed = (($is_red || $r[0]['xchan_network'] === 'rss') ? 1 : 0); + + $x = array('channel_id' => $uid, 'follow_address' => $url, 'xchan' => $r[0], 'allowed' => $allowed, 'singleton' => 0); call_hooks('follow_allow',$x); @@ -183,27 +193,13 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) } $singleton = intval($x['singleton']); - if((local_channel()) && $uid == local_channel()) { - $aid = get_account_id(); - $hash = get_observer_hash(); - $ch = $a->get_channel(); - $default_group = $ch['channel_default_group']; - } - else { - $r = q("select * from channel where channel_id = %d limit 1", - intval($uid) - ); - if(! $r) { - $result['message'] = t('local account not found.'); - return $result; - } - $aid = $r[0]['channel_account_id']; - $hash = $r[0]['channel_hash']; - $default_group = $r[0]['channel_default_group']; - } + $aid = $channel['channel_account_id']; + $hash = get_observer_hash(); + $default_group = $channel['channel_default_group']; - if($is_http) { + if($xchan['xchan_network'] === 'rss') { + // check service class feed limits $r = q("select count(*) as total from abook where abook_account = %d and abook_feed = 1 ", intval($aid) @@ -226,6 +222,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) dbesc($xchan_hash), intval($uid) ); + if($r) { $abook_instance = $r[0]['abook_instance']; @@ -242,7 +239,6 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) ); } else { - $closeness = get_pconfig($uid,'system','new_abook_closeness'); if($closeness === false) $closeness = 80; @@ -270,12 +266,13 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) dbesc($xchan_hash), intval($uid) ); + if($r) { $result['abook'] = $r[0]; proc_run('php', 'include/notifier.php', 'permission_create', $result['abook']['abook_id']); } - $arr = array('channel_id' => $uid, 'abook' => $result['abook']); + $arr = array('channel_id' => $uid, 'channel' => $channel, 'abook' => $result['abook']); call_hooks('follow', $arr); diff --git a/include/group.php b/include/group.php index 0875b10f9..748ec0c13 100644 --- a/include/group.php +++ b/include/group.php @@ -200,7 +200,7 @@ function group_get_members($gid) { 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_blocked = 0 and abook_pending = 0 ORDER BY xchan_name ASC ", + 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()) @@ -211,6 +211,22 @@ function group_get_members($gid) { return $ret; } +function group_get_members_xchan($gid) { + $ret = array(); + if(intval($gid)) { + $r = q("SELECT xchan FROM group_member WHERE gid = %d AND uid = %d", + intval($gid), + intval(local_channel()) + ); + if(count($r)) { + foreach($r as $rr) { + $ret[] = $rr['xchan']; + } + } + } + return $ret; +} + function mini_group_select($uid,$group = '') { $grps = array(); @@ -229,7 +245,7 @@ function mini_group_select($uid,$group = '') { logger('mini_group_select: ' . print_r($grps,true), LOGGER_DATA); $o = replace_macros(get_markup_template('group_selection.tpl'), array( - '$label' => t('Add new connections to this collection (privacy group)'), + '$label' => t('Add new connections to this privacy group'), '$groups' => $grps )); return $o; @@ -292,10 +308,10 @@ function group_side($every="connections",$each="group",$edit = false, $group_id $tpl = get_markup_template("group_side.tpl"); $o = replace_macros($tpl, array( - '$title' => t('Collections'), - '$edittext' => t('Edit collection'), - '$createtext' => t('Add new collection'), - '$ungrouped' => (($every === 'contacts') ? t('Channels not in any collection') : ''), + '$title' => t('Privacy Groups'), + '$edittext' => t('Edit group'), + '$createtext' => t('Add privacy group'), + '$ungrouped' => (($every === 'contacts') ? t('Channels not in any privacy group') : ''), '$groups' => $groups, '$add' => t('add'), )); diff --git a/include/hubloc.php b/include/hubloc.php index a1171b0e2..695cada3c 100644 --- a/include/hubloc.php +++ b/include/hubloc.php @@ -134,10 +134,17 @@ function hubloc_change_primary($hubloc) { $r = q("select channel_id, channel_primary from channel where channel_hash = '%s' limit 1", dbesc($hubloc['hubloc_hash']) ); - if(($r) && (! $r[0]['channel_primary'])) { - q("update channel set channel_primary = 1 where channel_id = %d", - intval($r[0]['channel_id']) - ); + if($r) { + if(! $r[0]['channel_primary']) { + q("update channel set channel_primary = 1 where channel_id = %d", + intval($r[0]['channel_id']) + ); + } + else { + q("update channel set channel_primary = 0 where channel_id = %d", + intval($r[0]['channel_id']) + ); + } } // do we even have an xchan for this hubloc and if so is it already set as primary? diff --git a/include/identity.php b/include/identity.php index 98ba26bd8..5d18d69e0 100644 --- a/include/identity.php +++ b/include/identity.php @@ -178,7 +178,7 @@ function create_identity($arr) { $ret = array('success' => false); if(! $arr['account_id']) { - $ret['message'] = t('No account identifier'); + $ret['message'] = t('No account identifier'); return $ret; } $ret = identity_check_service_class($arr['account_id']); @@ -352,7 +352,7 @@ function create_identity($arr) { ); if($role_permissions) { - $myperms = ((array_key_exists('perms_auto',$role_permissions) && $role_permissions['perms_auto']) ? intval($role_permissions['perms_accept']) : 0); + $myperms = ((array_key_exists('perms_accept',$role_permissions)) ? intval($role_permissions['perms_accept']) : 0); } else $myperms = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK @@ -410,7 +410,15 @@ 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'); } - + + // UNO: channel defaults, incl addons (addons specific pconfig will only work after the relevant addon is enabled by the admin). It's located here, so members can modify these defaults after the channel is created. + if(UNO) { + //diaspora protocol addon + set_pconfig($ret['channel']['channel_id'],'system','diaspora_allowed', '1'); + set_pconfig($ret['channel']['channel_id'],'system','diaspora_public_comments', '1'); + set_pconfig($ret['channel']['channel_id'],'system','prevent_tag_hijacking', '0'); + } + // 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 @@ -482,7 +490,9 @@ function identity_basic_export($channel_id, $items = false) { $ret = array(); - $ret['compatibility'] = array('project' => PLATFORM_NAME, 'version' => RED_VERSION, 'database' => DB_UPDATE_VERSION); + // 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' => RED_VERSION, 'database' => DB_UPDATE_VERSION, 'server_role' => Zotlabs\Project\System::get_server_role()); $r = q("select * from channel where channel_id = %d limit 1", intval($channel_id) @@ -503,8 +513,12 @@ function identity_basic_export($channel_id, $items = false) { if($r) { $ret['abook'] = $r; - foreach($r as $rr) - $xchans[] = $rr['abook_xchan']; + for($x = 0; $x < count($ret['abook']); $x ++) { + $xchans[] = $ret['abook'][$x]['abook_chan']; + $abconfig = load_abconfig($ret['channel']['channel_hash'],$ret['abook'][$x]['abook_xchan']); + if($abconfig) + $ret['abook'][$x]['abconfig'] = $abconfig; + } stringify_array_elms($xchans); } @@ -898,6 +912,55 @@ function profile_load(&$a, $nickname, $profile = '') { } +function profile_edit_menu($uid) { + + $a = get_app(); + $ret = array(); + + $is_owner = (($uid == local_channel()) ? true : false); + + // show edit profile to profile owner + if($is_owner) { + $ret['menu'] = array( + 'chg_photo' => t('Change profile photo'), + 'entries' => array(), + ); + + $multi_profiles = feature_enabled(local_channel(), 'multi_profiles'); + if($multi_profiles) { + $ret['multi'] = 1; + $ret['edit'] = array($a->get_baseurl(). '/profiles', t('Edit Profiles'), '', t('Edit')); + $ret['menu']['cr_new'] = t('Create New Profile'); + } + else { + $ret['edit'] = array($a->get_baseurl() . '/profiles/' . $uid, t('Edit Profile'), '', t('Edit')); + } + + $r = q("SELECT * FROM profile WHERE uid = %d", + local_channel() + ); + + if($r) { + foreach($r as $rr) { + if(!($multi_profiles || $rr['is_default'])) + continue; + $ret['menu']['entries'][] = array( + 'photo' => $rr['thumb'], + 'id' => $rr['id'], + 'alt' => t('Profile Image'), + 'profile_name' => $rr['profile_name'], + 'isdefault' => $rr['is_default'], + 'visible_to_everybody' => t('Visible to everybody'), + 'edit_visibility' => t('Edit visibility'), + ); + } + } + } + + return $ret; + +} + /** * @brief Formats a profile for display in the sidebar. * @@ -911,7 +974,7 @@ function profile_load(&$a, $nickname, $profile = '') { * @return HTML string suitable for sidebar inclusion * Exceptions: Returns empty string if passed $profile is wrong type or not populated */ -function profile_sidebar($profile, $block = 0, $show_connect = true) { +function profile_sidebar($profile, $block = 0, $show_connect = true, $zcard = false) { $a = get_app(); @@ -922,17 +985,18 @@ function profile_sidebar($profile, $block = 0, $show_connect = true) { $pdesc = true; $reddress = true; + if(! perm_is_allowed($profile['uid'],((is_array($observer)) ? $observer['xchan_hash'] : ''),'view_profile')) { + $block = true; + } + if((! is_array($profile)) && (! count($profile))) return $o; head_set_icon($profile['thumb']); - $is_owner = (($profile['uid'] == local_channel()) ? true : false); - if(is_sys_channel($profile['uid'])) $show_connect = false; - $profile['picdate'] = urlencode($profile['picdate']); call_hooks('profile_sidebar_enter', $profile); @@ -954,42 +1018,6 @@ function profile_sidebar($profile, $block = 0, $show_connect = true) { $connect_url = z_root() . '/connect/' . $profile['channel_address']; } - // show edit profile to yourself - if($is_owner) { - $profile['menu'] = array( - 'chg_photo' => t('Change profile photo'), - 'entries' => array(), - ); - - $multi_profiles = feature_enabled(local_channel(), 'multi_profiles'); - if($multi_profiles) { - $profile['edit'] = array($a->get_baseurl(). '/profiles', t('Profiles'),"", t('Manage/edit profiles')); - $profile['menu']['cr_new'] = t('Create New Profile'); - } - else - $profile['edit'] = array($a->get_baseurl() . '/profiles/' . $profile['id'], t('Edit Profile'),'',t('Edit Profile')); - - $r = q("SELECT * FROM `profile` WHERE `uid` = %d", - local_channel()); - - if($r) { - foreach($r as $rr) { - if(!($multi_profiles || $rr['is_default'])) - continue; - $profile['menu']['entries'][] = array( - 'photo' => $rr['thumb'], - 'id' => $rr['id'], - 'alt' => t('Profile Image'), - 'profile_name' => $rr['profile_name'], - 'isdefault' => $rr['is_default'], - 'visible_to_everybody' => t('visible to everybody'), - 'edit_visibility' => t('Edit visibility'), - ); - } - } - } - - if((x($profile,'address') == 1) || (x($profile,'locality') == 1) || (x($profile,'region') == 1) @@ -1006,9 +1034,6 @@ function profile_sidebar($profile, $block = 0, $show_connect = true) { // logger('online: ' . $profile['online']); - if(! perm_is_allowed($profile['uid'],((is_array($observer)) ? $observer['xchan_hash'] : ''),'view_profile')) { - $block = true; - } if(($profile['hidewall'] && (! local_channel()) && (! remote_channel())) || $block ) { $location = $reddress = $pdesc = $gender = $marital = $homepage = False; @@ -1046,12 +1071,18 @@ function profile_sidebar($profile, $block = 0, $show_connect = true) { $channel_menu .= comanche_block($menublock); } - $tpl = get_markup_template('profile_vcard.tpl'); + if($zcard) + $tpl = get_markup_template('profile_vcard_short.tpl'); + else + $tpl = get_markup_template('profile_vcard.tpl'); require_once('include/widgets.php'); - $z = widget_rating(array('target' => $profile['channel_hash'])); + + if(! feature_enabled($profile['uid'],'hide_rating')) + $z = widget_rating(array('target' => $profile['channel_hash'])); $o .= replace_macros($tpl, array( + '$zcard' => $zcard, '$profile' => $profile, '$connect' => $connect, '$connect_url' => $connect_url, @@ -1065,6 +1096,7 @@ function profile_sidebar($profile, $block = 0, $show_connect = true) { '$reddress' => $reddress, '$rating' => $z, '$contact_block' => $contact_block, + '$editmenu' => profile_edit_menu($profile['uid']) )); $arr = array('profile' => &$profile, 'entry' => &$o); @@ -1227,11 +1259,25 @@ function advanced_profile(&$a) { if(! perm_is_allowed($a->profile['profile_uid'],get_observer_hash(),'view_profile')) return ''; - $o = ''; + if($a->profile['name']) { + + $profile_fields_basic = get_profile_fields_basic(); + $profile_fields_advanced = get_profile_fields_advanced(); + + $advanced = ((feature_enabled($a->profile['profile_uid'],'advanced_profiles')) ? true : false); + if($advanced) + $fields = $profile_fields_advanced; + else + $fields = $profile_fields_basic; + + $clean_fields = array(); + if($fields) { + foreach($fields as $k => $v) { + $clean_fields[] = trim($k); + } + } - $o .= '<h2>' . t('Profile') . '</h2>'; - if($a->profile['name']) { $tpl = get_markup_template('profile_advanced.tpl'); @@ -1258,7 +1304,7 @@ function advanced_profile(&$a) { $profile['like_button_label'] = tt('Like','Likes',$profile['like_count'],'noun'); if($likers) { foreach($likers as $l) - $profile['likers'][] = array('name' => $l['xchan_name'],'url' => zid($l['xchan_url'])); + $profile['likers'][] = array('name' => $l['xchan_name'],'photo' => zid($l['xchan_photo_s']), 'url' => zid($l['xchan_url'])); } if(($a->profile['dob']) && ($a->profile['dob'] != '0000-00-00')) { @@ -1350,6 +1396,8 @@ function advanced_profile(&$a) { '$canlike' => (($profile['canlike'])? true : false), '$likethis' => t('Like this thing'), '$profile' => $profile, + '$fields' => $clean_fields, + '$editmenu' => profile_edit_menu($a->profile['profile_uid']), '$things' => $things )); } @@ -1695,3 +1743,145 @@ function profiles_build_sync($channel_id) { build_sync_packet($channel_id,array('profile' => $r)); } } + + +function auto_channel_create($account_id) { + + if(! $account_id) + return false; + + $arr = array(); + $arr['account_id'] = $account_id; + $arr['name'] = get_aconfig($account_id,'register','channel_name'); + $arr['nickname'] = legal_webbie(get_aconfig($account_id,'register','channel_address')); + $arr['permissions_role'] = get_aconfig($account_id,'register','permissions_role'); + + del_aconfig($account_id,'register','channel_name'); + del_aconfig($account_id,'register','channel_address'); + del_aconfig($account_id,'register','permissions_role'); + + if((! $arr['name']) || (! $arr['nickname'])) { + $x = q("select * from account where account_id = %d limit 1", + intval($account_id) + ); + if($x) { + if(! $arr['name']) + $arr['name'] = substr($x[0]['account_email'],0,strpos($x[0]['account_email'],'@')); + if(! $arr['nickname']) + $arr['nickname'] = legal_webbie(substr($x[0]['account_email'],0,strpos($x[0]['account_email'],'@'))); + } + } + if(! $arr['permissions_role']) + $arr['permissions_role'] = 'social'; + + if(validate_channelname($arr['name'])) + return false; + if($arr['nickname'] === 'sys') + $arr['nickname'] = $arr['nickname'] . mt_rand(1000,9999); + + $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) { + + $r = q("select height, width, resource_id, type from photo where uid = %d and scale = %d and photo_usage = %d", + intval($channel_id), + intval($res), + intval(PHOTO_COVER) + ); + if(! $r) + return false; + + $output = false; + + $url = z_root() . '/photo/' . $r[0]['resource_id'] . '-' . $res ; + + switch($format) { + case 'bbcode': + $output = '[zrl=' . $r[0]['width'] . 'x' . $r[0]['height'] . ']' . $url . '[/zrl]'; + break; + case 'html': + $output = '<img class="zrl" width="' . $r[0]['width'] . '" height="' . $r[0]['height'] . '" src="' . $url . '" alt="' . t('cover photo') . '" />'; + break; + case 'array': + default: + $output = array( + 'width' => $r[0]['width'], + 'height' => $r[0]['type'], + 'type' => $r[0]['type'], + 'url' => $url + ); + break; + } + + return $output; + +} + +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; + + if($maxwidth <= 425) { + $width = 425; + $size = 'hz_small'; + $cover_size = PHOTO_RES_COVER_425; + $pphoto = array('type' => $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('type' => $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('type' => $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['channel_address'] . '@' . get_app()->get_hostname(); + $zcard = array('chan' => $channel); + + $r = q("select height, width, resource_id, scale, type from photo where uid = %d and scale = %d and photo_usage = %d", + intval($channel['channel_id']), + intval($cover_size), + intval(PHOTO_COVER) + ); + + if($r) { + $cover = $r[0]; + $cover['href'] = z_root() . '/photo/' . $r[0]['resource_id'] . '-' . $r[0]['scale']; + } + else { + $cover = $pphoto; + } + + $o .= replace_macros(get_markup_template('zcard.tpl'),array( + '$maxwidth' => $maxwidth, + '$scale' => $scale, + '$translate' => $translate, + '$size' => $size, + '$cover' => $cover, + '$pphoto' => $pphoto, + '$zcard' => $zcard + )); + + return $o; + +} diff --git a/include/import.php b/include/import.php index ffaea6c1a..b7ec1c565 100644 --- a/include/import.php +++ b/include/import.php @@ -2,7 +2,7 @@ require_once('include/menu.php'); -function import_channel($channel, $account_id) { +function import_channel($channel, $account_id, $seize) { if(! array_key_exists('channel_system',$channel)) { $channel['channel_system'] = (($channel['channel_pageflags'] & 0x1000) ? 1 : 0); @@ -496,6 +496,8 @@ function import_items($channel,$items) { } } + $deliver = false; // Don't deliver any messages or notifications when importing + foreach($items as $i) { $item = get_item_elements($i,$allow_code); if(! $item) @@ -509,16 +511,15 @@ function import_items($channel,$items) { if($item['edited'] > $r[0]['edited']) { $item['id'] = $r[0]['id']; $item['uid'] = $channel['channel_id']; - item_store_update($item); + item_store_update($item,$allow_code,$deliver); continue; } } else { $item['aid'] = $channel['channel_account_id']; $item['uid'] = $channel['channel_id']; - $item_result = item_store($item); + $item_result = item_store($item,$allow_code,$deliver); } - } } } diff --git a/include/items.php b/include/items.php index 44f9633a9..c3a0b82d2 100755 --- a/include/items.php +++ b/include/items.php @@ -159,7 +159,7 @@ function filter_insecure($channel_id, $arr) { $ret = array(); - if((! intval(get_pconfig($channel_id, 'system', 'filter_insecure_collections'))) || (! $arr)) + if((! intval(get_pconfig($channel_id, 'system', 'filter_insecure_privacy_groups'))) || (! $arr)) return $arr; $str = ''; @@ -552,6 +552,12 @@ function get_public_feed($channel, $params) { $params['top'] = ((x($params,'top')) ? intval($params['top']) : 0); $params['cat'] = ((x($params,'cat')) ? $params['cat'] : ''); + + // put a sane lower limit on feed requests if not specified + + if($params['begin'] === NULL_DATE) + $params['begin'] = datetime_convert('UTC','UTC','now - 1 month'); + switch($params['type']) { case 'json': header("Content-type: application/atom+json"); @@ -587,8 +593,8 @@ function get_feed_for($channel, $observer_hash, $params) { } $items = items_fetch(array( 'wall' => '1', - 'datequery' => $params['begin'], - 'datequery2' => $params['end'], + 'datequery' => $params['end'], + 'datequery2' => $params['begin'], 'start' => $params['start'], // FIXME 'records' => $params['records'], // FIXME 'direction' => $params['direction'], // FIXME @@ -604,8 +610,8 @@ function get_feed_for($channel, $observer_hash, $params) { $atom = ''; $atom .= replace_macros($feed_template, array( - '$version' => xmlify(RED_VERSION), - '$red' => xmlify(PLATFORM_NAME), + '$version' => xmlify(Zotlabs\Project\System::get_project_version()), + '$red' => xmlify(Zotlabs\Project\System::get_platform_name()), '$feed_id' => xmlify($channel['xchan_url']), '$feed_title' => xmlify($channel['channel_name']), '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', 'now' , ATOM_TIME)) , @@ -623,6 +629,7 @@ function get_feed_for($channel, $observer_hash, $params) { '$community' => '', )); + call_hooks('atom_feed', $atom); if($items) { @@ -842,7 +849,7 @@ function get_item_elements($x,$allow_code = false) { if($allow_code) $arr['body'] = $x['body']; else - $arr['body'] = (($x['body']) ? htmlspecialchars($x['body'],ENT_COMPAT,'UTF-8',false) : ''); + $arr['body'] = (($x['body']) ? htmlspecialchars($x['body'],ENT_COMPAT,'UTF-8',false) : ''); $key = get_config('system','pubkey'); @@ -910,6 +917,7 @@ function get_item_elements($x,$allow_code = false) { $arr['attach'] = activity_sanitise($x['attach']); $arr['term'] = decode_tags($x['tags']); + $arr['iconfig'] = decode_item_meta($x['meta']); $arr['item_private'] = ((array_key_exists('flags',$x) && is_array($x['flags']) && in_array('private',$x['flags'])) ? 1 : 0); @@ -1136,9 +1144,8 @@ function import_author_rss($x) { $photos = import_xchan_photo($x['photo']['src'],$x['url']); if($photos) { - /** @bug $arr is undefined in this SQL query */ $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_url = '%s' and xchan_network = 'rss'", - dbesc(datetime_convert('UTC', 'UTC', $arr['photo_updated'])), + dbesc(datetime_convert()), dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), @@ -1181,9 +1188,8 @@ function import_author_unknown($x) { $photos = import_xchan_photo($x['photo']['src'],$x['url']); if($photos) { - /** @bug $arr is undefined in this SQL query */ $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_url = '%s' and xchan_network = 'unknown'", - dbesc(datetime_convert('UTC','UTC',$arr['photo_updated'])), + dbesc(datetime_convert()), dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), @@ -1317,6 +1323,9 @@ function encode_item($item,$mirror = false) { if($item['term']) $x['tags'] = encode_item_terms($item['term'],$mirror); + if($item['iconfig']) + $x['meta'] = encode_item_meta($item['iconfig'],$mirror); + if($item['diaspora_meta']) { $z = json_decode($item['diaspora_meta'],true); if($z) { @@ -1427,6 +1436,30 @@ function encode_item_terms($terms,$mirror = false) { return $ret; } +function encode_item_meta($meta,$mirror = false) { + $ret = array(); + + if($meta) { + foreach($meta as $m) { + if($m['sharing'] || $mirror) + $ret[] = array('family' => $m['cat'], 'key' => $m['k'], 'value' => $m['v'], 'sharing' => intval($m['sharing'])); + } + } + + return $ret; +} + +function decode_item_meta($meta) { + $ret = array(); + + if(is_array($meta) && $meta) { + foreach($meta as $m) { + $ret[] = array('cat' => escape_tags($m['family']),'k' => escape_tags($m['key']),'v' => $m['value'],'sharing' => $m['sharing']); + } + } + return $ret; +} + /** * @brief * @@ -2058,6 +2091,10 @@ function get_atom_elements($feed, $item, &$author) { $res['obj_type'] = $child[NAMESPACE_ACTIVITY]['obj_type'][0]['data']; $obj['type'] = $child[NAMESPACE_ACTIVITY]['obj_type'][0]['data']; } + if($child[NAMESPACE_ACTIVITY]['object-type'][0]['data']) { + $res['obj_type'] = $child[NAMESPACE_ACTIVITY]['object-type'][0]['data']; + $obj['type'] = $child[NAMESPACE_ACTIVITY]['object-type'][0]['data']; + } if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'id') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data']) $obj['id'] = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data']; if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'link') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']) @@ -2091,6 +2128,10 @@ function get_atom_elements($feed, $item, &$author) { $res['tgt_type'] = $child[NAMESPACE_ACTIVITY]['obj_type'][0]['data']; $obj['type'] = $child[NAMESPACE_ACTIVITY]['obj_type'][0]['data']; } + if($child[NAMESPACE_ACTIVITY]['object-type'][0]['data']) { + $res['tgt_type'] = $child[NAMESPACE_ACTIVITY]['object-type'][0]['data']; + $obj['type'] = $child[NAMESPACE_ACTIVITY]['object-type'][0]['data']; + } if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'id') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data']) $obj['id'] = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data']; if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'link') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']) @@ -2115,9 +2156,6 @@ function get_atom_elements($feed, $item, &$author) { $res['target'] = $obj; } - $res['public_policy'] = 'specific'; - $res['comment_policy'] = 'none'; - $arr = array('feed' => $feed, 'item' => $item, 'result' => $res); call_hooks('parse_atom', $arr); @@ -2129,26 +2167,27 @@ function get_atom_elements($feed, $item, &$author) { } function encode_rel_links($links) { - $o = ''; + $o = array(); if(! ((is_array($links)) && (count($links)))) return $o; foreach($links as $link) { - $o .= '<link '; + $l = array(); if($link['attribs']['']['rel']) - $o .= 'rel="' . $link['attribs']['']['rel'] . '" '; + $l['rel'] = $link['attribs']['']['rel']; if($link['attribs']['']['type']) - $o .= 'type="' . $link['attribs']['']['type'] . '" '; + $l['type'] = $link['attribs']['']['type']; if($link['attribs']['']['href']) - $o .= 'href="' . $link['attribs']['']['href'] . '" '; + $l['href'] = $link['attribs']['']['href']; if( (x($link['attribs'],NAMESPACE_MEDIA)) && $link['attribs'][NAMESPACE_MEDIA]['width']) - $o .= 'media:width="' . $link['attribs'][NAMESPACE_MEDIA]['width'] . '" '; + $l['width'] = $link['attribs'][NAMESPACE_MEDIA]['width']; if( (x($link['attribs'],NAMESPACE_MEDIA)) && $link['attribs'][NAMESPACE_MEDIA]['height']) - $o .= 'media:height="' . $link['attribs'][NAMESPACE_MEDIA]['height'] . '" '; - $o .= ' />' . "\n" ; - } + $l['height'] = $link['attribs'][NAMESPACE_MEDIA]['height']; - return xmlify($o); + if($l) + $o[] = $l; + } + return $o; } /** @@ -2160,7 +2199,7 @@ function encode_rel_links($links) { * * \e boolean \b success * * \e int \b item_id */ -function item_store($arr, $allow_exec = false) { +function item_store($arr, $allow_exec = false, $deliver = true) { $d = array('item' => $arr, 'allow_exec' => $allow_exec); call_hooks('item_store', $d ); @@ -2439,6 +2478,13 @@ function item_store($arr, $allow_exec = false) { unset($arr['term']); } + $meta = null; + if(array_key_exists('iconfig',$arr)) { + $meta = $arr['iconfig']; + unset($arr['iconfig']); + } + + if(strlen($allow_cid) || strlen($allow_gid) || strlen($deny_cid) || strlen($deny_gid) || strlen($public_policy)) $private = 1; else @@ -2516,6 +2562,15 @@ function item_store($arr, $allow_exec = false) { $arr['term'] = $terms; } + if($meta) { + foreach($meta as $m) { + set_iconfig($current_post,$m['cat'],$m['k'],$m['v'],$m['sharing']); + } + $arr['iconfig'] = $meta; + } + + + call_hooks('post_remote_end',$arr); // update the commented timestamp on the parent @@ -2537,7 +2592,7 @@ function item_store($arr, $allow_exec = false) { // so that we have an item in the DB that's marked deleted and won't store a fresh post // that isn't aware that we were already told to delete it. - if(! intval($arr['item_deleted'])) { + if(($deliver) && (! intval($arr['item_deleted']))) { send_status_notifications($current_post,$arr); tag_deliver($arr['uid'],$current_post); } @@ -2550,7 +2605,7 @@ function item_store($arr, $allow_exec = false) { -function item_store_update($arr,$allow_exec = false) { +function item_store_update($arr,$allow_exec = false, $deliver = true) { $d = array('item' => $arr, 'allow_exec' => $allow_exec); call_hooks('item_store_update', $d ); @@ -2737,6 +2792,13 @@ function item_store_update($arr,$allow_exec = false) { unset($arr['term']); } + $meta = null; + if(array_key_exists('iconfig',$arr)) { + $meta = $arr['iconfig']; + unset($arr['iconfig']); + } + + dbesc_array($arr); logger('item_store_update: ' . print_r($arr,true), LOGGER_DATA); @@ -2778,11 +2840,24 @@ function item_store_update($arr,$allow_exec = false) { $arr['term'] = $terms; } + $r = q("delete from iconfig where iid = %d", + intval($orig_post_id) + ); + + if($meta) { + foreach($meta as $m) { + set_iconfig($orig_post_id,$m['cat'],$m['k'],$m['v'],$m['sharing']); + } + $arr['iconfig'] = $meta; + } + call_hooks('post_remote_update_end',$arr); - send_status_notifications($orig_post_id,$arr); + if($deliver) { + send_status_notifications($orig_post_id,$arr); + tag_deliver($uid,$orig_post_id); + } - tag_deliver($uid,$orig_post_id); $ret['success'] = true; $ret['item_id'] = $orig_post_id; @@ -2816,13 +2891,13 @@ function store_diaspora_comment_sig($datarray, $channel, $parent_item, $post_id, $signed_text = $datarray['mid'] . ';' . $parent_item['mid'] . ';' . $signed_body . ';' . $diaspora_handle; - /** @FIXME $uprvkey is undefined, do we still need this if-statement? */ - if( $uprvkey !== false ) + + if( $channel && $channel['channel_prvkey'] ) $authorsig = base64_encode(rsa_sign($signed_text, $channel['channel_prvkey'], 'sha256')); else $authorsig = ''; - $x = array('signer' => $diaspora_handle, 'body' => $signed_body, 'signed_text' => $signed_text, 'signature' => base64_encode($authorsig)); + $x = array('signer' => $diaspora_handle, 'body' => $signed_body, 'signed_text' => $signed_text, 'signature' => $authorsig); $y = json_encode($x); @@ -2831,6 +2906,7 @@ function store_diaspora_comment_sig($datarray, $channel, $parent_item, $post_id, intval($post_id) ); + if(! $r) logger('store_diaspora_comment_sig: DB write failed'); @@ -3436,6 +3512,7 @@ function check_item_source($uid, $item) { if(! $r[0]['src_patt']) return true; + require_once('include/html2plain.php'); $text = prepare_text($item['body'],$item['mimetype']); $text = html2plain($text); @@ -3651,10 +3728,6 @@ function mail_store($arr) { /** * @brief Process atom feed and update anything/everything we might need to update. * - * $hub = should we find a hub declation in the feed, pass it back to our calling process, who might (or - * might not) try and subscribe to it. - * $datedir sorts in reverse order - * * @param array $xml * The (atom) feed to consume - RSS isn't as fully supported but may work for simple feeds. * @param $importer @@ -3685,6 +3758,16 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { return; } + $sys_expire = intval(get_config('system','default_expire_days')); + $chn_expire = intval($importer['channel_expire_days']); + + $expire_days = $sys_expire; + + if(($chn_expire != 0) && ($chn_expire < $sys_expire)) + $expire_days = $chn_expire; + + // logger('expire_days: ' . $expire_days); + $feed = new SimplePie(); $feed->set_raw_data($xml); $feed->init(); @@ -3764,6 +3847,11 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { $author = array(); $datarray = get_atom_elements($feed,$item,$author); + if($contact['xchan_network'] === 'rss') { + $res['public_policy'] = 'specific'; + $res['comment_policy'] = 'none'; + } + if((! x($author,'author_name')) || ($author['author_is_feed'])) $author['author_name'] = $contact['xchan_name']; if((! x($author,'author_link')) || ($author['author_is_feed'])) @@ -3788,6 +3876,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { intval($importer['channel_id']) ); + // Update content if 'updated' changes if($r) { @@ -3804,6 +3893,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { } $datarray['parent_mid'] = $parent_mid; + $datarray['aid'] = $importer['channel_account_id']; $datarray['uid'] = $importer['channel_id']; logger('consume_feed: ' . print_r($datarray,true),LOGGER_DATA); @@ -3820,6 +3910,12 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { $author = array(); $datarray = get_atom_elements($feed,$item,$author); + if($contact['xchan_network'] === 'rss') { + $res['public_policy'] = 'specific'; + $res['comment_policy'] = 'none'; + } + + if(is_array($contact)) { if((! x($author,'author_name')) || ($author['author_is_feed'])) $author['author_name'] = $contact['xchan_name']; @@ -3846,6 +3942,17 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { $datarray['owner_xchan'] = $contact['xchan_hash']; + if(array_key_exists('created',$datarray) && $datarray['created'] != NULL_DATE && $expire_days) { + $t1 = $datarray['created']; + $t2 = datetime_convert('UTC','UTC','now - ' . $expire_days . 'days'); + if($t1 < $t2) { + logger('feed content older than expiration. Ignoring.', LOGGER_DEBUG, LOG_INFO); + continue; + } + } + + + $r = q("SELECT edited FROM item WHERE mid = '%s' AND uid = %d LIMIT 1", dbesc($item_id), intval($importer['channel_id']) @@ -3869,6 +3976,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { $datarray['parent_mid'] = $item_id; $datarray['uid'] = $importer['channel_id']; + $datarray['aid'] = $importer['channel_account_id']; if(! link_compare($author['owner_link'],$contact['xchan_url'])) { logger('consume_feed: Correcting item owner.', LOGGER_DEBUG); @@ -3892,6 +4000,140 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { } } + +/** + * @brief Process atom feed and return the first post and structure + * + * @param array $xml + * The (atom) feed to consume - RSS isn't as fully supported but may work for simple feeds. + * @param $importer + * The contact_record (joined to user_record) of the local user who owns this + * relationship. It is this person's stuff that is going to be updated. + */ + +function process_salmon_feed($xml, $importer) { + + $ret = array(); + + require_once('library/simplepie/simplepie.inc'); + + if(! strlen($xml)) { + logger('process_feed: empty input'); + return; + } + + $feed = new SimplePie(); + $feed->set_raw_data($xml); + $feed->init(); + + if($feed->error()) + logger('Error parsing XML: ' . $feed->error()); + + $permalink = $feed->get_permalink(); + + if($feed->get_item_quantity()) { + + // this should be exactly one + + logger('feed item count = ' . $feed->get_item_quantity(), LOGGER_DEBUG); + + $items = $feed->get_items(); + + foreach($items as $item) { + + $item_id = base64url_encode($item->get_id()); + + logger('processing ' . $item_id, LOGGER_DEBUG); + + $rawthread = $item->get_item_tags( NAMESPACE_THREAD,'in-reply-to'); + if(isset($rawthread[0]['attribs']['']['ref'])) { + $is_reply = true; + $parent_mid = base64url_encode($rawthread[0]['attribs']['']['ref']); + } + + if($is_reply) + $ret['parent_mid'] = $parent_mid; + + $ret['author'] = array(); + + $datarray = get_atom_elements($feed,$item,$ret['author']); + + // reset policies which are restricted by default for RSS connections + // This item is likely coming from GNU-social via salmon and allows public interaction + $datarray['public_policy'] = ''; + $datarray['comment_policy'] = ''; + + $ret['item'] = $datarray; + } + } + + return $ret; +} + +/* + * Given an xml (atom) feed, find author and hub links + */ + + +function feed_meta($xml) { + require_once('library/simplepie/simplepie.inc'); + + $ret = array(); + + if(! strlen($xml)) { + logger('empty input'); + return $ret; + } + + $feed = new SimplePie(); + $feed->set_raw_data($xml); + $feed->init(); + + if($feed->error()) { + logger('Error parsing XML: ' . $feed->error()); + return $ret; + } + + $ret['hubs'] = $feed->get_links('hub'); + +// logger('consume_feed: hubs: ' . print_r($hubs,true), LOGGER_DATA); + + $author = array(); + + $found_author = $feed->get_author(); + if($found_author) { + $author['author_name'] = unxmlify($found_author->get_name()); + $author['author_link'] = unxmlify($found_author->get_link()); + + $rawauthor = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author'); + logger('rawauthor: ' . print_r($rawauthor,true)); + + if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) { + $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; + foreach($base as $link) { + if(!x($author, 'author_photo') || ! $author['author_photo']) { + if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar') { + $author['author_photo'] = unxmlify($link['attribs']['']['href']); + break; + } + } + } + if($rawauthor[0]['child'][NAMESPACE_POCO]['displayName'][0]['data']) + $author['full_name'] = unxmlify($rawauthor[0]['child'][NAMESPACE_POCO]['displayName'][0]['data']); + } + } + + + if(substr($author['author_link'],-1,1) == '/') + $author['author_link'] = substr($author['author_link'],0,-1); + + $ret['author'] = $author; + + return $ret; +} + + + function update_feed_item($uid,$datarray) { logger('update_feed_item: not implemented! ' . $uid . ' ' . print_r($datarray,true), LOGGER_DATA); } @@ -3915,8 +4157,8 @@ function handle_feed($uid,$abook_id,$url) { //logger('handle_feed:' . print_r($z,true)); if($z['success']) { - consume_feed($z['body'],$channel,$x[0],0); consume_feed($z['body'],$channel,$x[0],1); + consume_feed($z['body'],$channel,$x[0],2); } } @@ -3975,12 +4217,25 @@ function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) { $o .= '<thr:in-reply-to ref="' . xmlify($parent_item) . '" type="text/html" href="' . xmlify($item['plink']) . '" />' . "\r\n"; } + if(activity_match($item['obj_type'],ACTIVITY_OBJ_EVENT) && activity_match($item['verb'],ACTIVITY_POST)) { + $obj = ((is_array($item['obj'])) ? $item['object'] : json_decode($item['object'],true)); + + $o .= '<title>' . xmlify($item['title']) . '</title>' . "\r\n"; + $o .= '<summary>' . xmlify(bbcode($obj['title'])) . '</summary>' . "\r\n"; + $o .= '<dtstart xmlns="urn:ietf:params:xml:ns:xcal">' . datetime_convert('UTC','UTC', $obj['start'],'Ymd\\THis' . (($obj['adjust']) ? '\\Z' : '')) . '</dtstart>' . "\r\n"; + $o .= '<dtend xmlns="urn:ietf:params:xml:ns:xcal">' . datetime_convert('UTC','UTC', $obj['finish'],'Ymd\\THis' . (($obj['adjust']) ? '\\Z' : '')) . '</dtend>' . "\r\n"; + $o .= '<location>' . bbcode($obj['location']) . '</location>' . "\r\n"; + $o .= '<content type="' . $type . '" >' . xmlify(bbcode($obj['description'])) . '</content>' . "\r\n"; + } + else { + $o .= '<title>' . xmlify($item['title']) . '</title>' . "\r\n"; + $o .= '<content type="' . $type . '" >' . xmlify(prepare_text($body,$item['mimetype'])) . '</content>' . "\r\n"; + } + $o .= '<id>' . xmlify($item['mid']) . '</id>' . "\r\n"; - $o .= '<title>' . xmlify($item['title']) . '</title>' . "\r\n"; $o .= '<published>' . xmlify(datetime_convert('UTC','UTC',$item['created'] . '+00:00',ATOM_TIME)) . '</published>' . "\r\n"; $o .= '<updated>' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '</updated>' . "\r\n"; - $o .= '<content type="' . $type . '" >' . xmlify(prepare_text($body,$item['mimetype'])) . '</content>' . "\r\n"; $o .= '<link rel="alternate" type="text/html" href="' . xmlify($item['plink']) . '" />' . "\r\n"; if($item['location']) { @@ -4643,6 +4898,10 @@ function fetch_post_tags($items,$link = false) { dbesc($tag_finder_str), intval(TERM_OBJ_POST) ); + $imeta = q("select * from iconfig where iid in ( %s )", + dbesc($tag_finder_str) + ); + } for($x = 0; $x < count($items); $x ++) { @@ -4666,6 +4925,26 @@ function fetch_post_tags($items,$link = false) { } } } + if($imeta) { + foreach($imeta as $i) { + if(array_key_exists('item_id',$items[$x])) { + if($i['iid'] == $items[$x]['item_id']) { + if(! is_array($items[$x]['iconfig'])) + $items[$x]['iconfig'] = array(); + $i['v'] = ((preg_match('|^a:[0-9]+:{.*}$|s',$i['v'])) ? unserialize($i['v']) : $i['v']); + $items[$x]['iconfig'][] = $i; + } + } + else { + if($i['iid'] == $items[$x]['id']) { + if(! is_array($items[$x]['iconfig'])) + $items[$x]['iconfig'] = array(); + $i['v'] = ((preg_match('|^a:[0-9]+:{.*}$|s',$i['v'])) ? unserialize($i['v']) : $i['v']); + $items[$x]['iconfig'][] = $i; + } + } + } + } } return $items; @@ -4835,7 +5114,7 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C intval($uid) ); if(! $r) { - $result['message'] = t('Collection not found.'); + $result['message'] = t('Privacy group not found.'); return $result; } @@ -4851,14 +5130,14 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C } } else { $contact_str = ' 0 '; - $result['message'] = t('Collection is empty.'); + $result['message'] = t('Privacy group is empty.'); return $result; } $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true $sql_options AND (( author_xchan IN ( $contact_str ) OR owner_xchan in ( $contact_str)) or allow_gid like '" . protect_sprintf('%<' . dbesc($r[0]['hash']) . '>%') . "' ) and id = parent $item_normal ) "; $x = group_rec_byhash($uid,$r[0]['hash']); - $result['headline'] = sprintf( t('Collection: %s'),$x['name']); + $result['headline'] = sprintf( t('Privacy group: %s'),$x['name']); } elseif($arr['cid'] && $uid) { @@ -4876,15 +5155,15 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C } if ($arr['datequery']) { - $sql_extra3 .= protect_sprintf(sprintf(" AND item.created <= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$arr['datequery'])))); + $sql_extra3 .= protect_sprintf(sprintf(" AND item.created <= '%s' ", dbesc(datetime_convert('UTC','UTC',$arr['datequery'])))); } if ($arr['datequery2']) { - $sql_extra3 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$arr['datequery2'])))); + $sql_extra3 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert('UTC','UTC',$arr['datequery2'])))); } if(! array_key_exists('nouveau',$arr)) { $sql_extra2 = " AND item.parent = item.id "; - $sql_extra3 = ''; +// $sql_extra3 = ''; } if($arr['search']) { @@ -5274,3 +5553,212 @@ function asencode_person($p) { return $ret; } + + +function send_profile_photo_activity($channel,$photo,$profile) { + + // for now only create activities for the default profile + + if(! intval($profile['is_default'])) + return; + + $arr = array(); + $arr['item_thread_top'] = 1; + $arr['item_origin'] = 1; + $arr['item_wall'] = 1; + $arr['obj_type'] = ACTIVITY_OBJ_PHOTO; + $arr['verb'] = ACTIVITY_UPDATE; + + $arr['object'] = json_encode(array( + 'type' => $arr['obj_type'], + 'id' => z_root() . '/photo/profile/l/' . $channel['channel_id'], + 'link' => array('rel' => 'photo', 'type' => $photo['type'], 'href' => z_root() . '/photo/profile/l/' . $channel['channel_id']) + )); + + if(stripos($profile['gender'],t('female')) !== false) + $t = t('%1$s updated her %2$s'); + elseif(stripos($profile['gender'],t('male')) !== false) + $t = t('%1$s updated his %2$s'); + else + $t = t('%1$s updated their %2$s'); + + $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]'; + + $arr['body'] = sprintf($t,$channel['channel_name'],$ptext) . "\n\n" . $ltext; + + $acl = new Zotlabs\Access\AccessList($channel); + $x = $acl->get(); + $arr['allow_cid'] = $x['allow_cid']; + + $arr['allow_gid'] = $x['allow_gid']; + $arr['deny_cid'] = $x['deny_cid']; + $arr['deny_gid'] = $x['deny_gid']; + + $arr['uid'] = $channel['channel_id']; + $arr['aid'] = $channel['channel_account_id']; + + $arr['owner_xchan'] = $channel['channel_hash']; + $arr['author_xchan'] = $channel['channel_hash']; + + post_activity_item($arr); + + +} + + + + + +function get_iconfig(&$item, $family, $key) { + + $is_item = false; + if(is_array($item)) { + $is_item = true; + if((! array_key_exists('iconfig',$item)) || (! is_array($item['iconfig']))) + $item['iconfig'] = array(); + + if(array_key_exists('item_id',$item)) + $iid = $item['item_id']; + else + $iid = $item['id']; + } + elseif(intval($item)) + $iid = $item; + + if(! $iid) + return false; + + if(is_array($item) && array_key_exists('iconfig',$item) && is_array($item['iconfig'])) { + foreach($item['iconfig'] as $c) { + if($c['iid'] == $iid && $c['cat'] == $family && $c['k'] == $key) + return $c['v']; + } + } + + $r = q("select * from iconfig where iid = %d and cat = '%s' and k = '%s' limit 1", + intval($iid), + dbesc($family), + dbesc($key) + ); + if($r) { + $r[0]['v'] = ((preg_match('|^a:[0-9]+:{.*}$|s',$r[0]['v'])) ? unserialize($r[0]['v']) : $r[0]['v']); + if($is_item) + $item['iconfig'][] = $r[0]; + return $r[0]['v']; + } + return false; + +} + +/** + * set_iconfig(&$item, $family, $key, $value, $sharing = false); + * + * $item - item array or item id. If passed an array the iconfig meta information is + * added to the item structure (which will need to be saved with item_store eventually). + * If passed an id, the DB is updated, but may not be federated and/or cloned. + * $family - namespace of meta variable + * $key - key of meta variable + * $value - value of meta variable + * $sharing - boolean (default false); if true the meta information is propagated with the item + * to other sites/channels, mostly useful when $item is an array and has not yet been stored/delivered. + * If the meta information is added after delivery and you wish it to be shared, it may be necessary to + * alter the item edited timestamp and invoke the delivery process on the updated item. The edited + * timestamp needs to be altered in order to trigger an item_store_update() at the receiving end. + */ + + +function set_iconfig(&$item, $family, $key, $value, $sharing = false) { + + $dbvalue = ((is_array($value)) ? serialize($value) : $value); + $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); + + $is_item = false; + $idx = null; + + if(is_array($item)) { + $is_item = true; + if((! array_key_exists('iconfig',$item)) || (! is_array($item['iconfig']))) + $item['iconfig'] = array(); + elseif($item['iconfig']) { + for($x = 0; $x < count($item['iconfig']); $x ++) { + if($item['iconfig'][$x]['cat'] == $family && $item['iconfig'][$x]['k'] == $key) { + $idx = $x; + } + } + } + $entry = array('cat' => $family, 'k' => $key, 'v' => $value, 'sharing' => $sharing); + + if(is_null($idx)) + $item['iconfig'][] = $entry; + else + $item['iconfig'][$idx] = $entry; + return $value; + } + + if(intval($item)) + $iid = intval($item); + + if(! $iid) + return false; + + if(get_iconfig($item, $family, $key) === false) { + $r = q("insert into iconfig( iid, cat, k, v, sharing ) values ( %d, '%s', '%s', '%s', %d ) ", + intval($iid), + dbesc($family), + dbesc($key), + dbesc($dbvalue), + intval($sharing) + ); + } + else { + $r = q("update iconfig set v = '%s', sharing = %d where iid = %d and cat = '%s' and k = '%s' ", + dbesc($dbvalue), + intval($sharing), + intval($iid), + dbesc($family), + dbesc($key) + ); + } + + if(! $r) + return false; + + return $value; +} + + + +function del_iconfig(&$item, $family, $key) { + + + $is_item = false; + $idx = null; + + if(is_array($item)) { + $is_item = true; + if(is_array($item['iconfig'])) { + for($x = 0; $x < count($item['iconfig']); $x ++) { + if($item['iconfig'][$x]['cat'] == $family && $item['iconfig'][$x]['k'] == $key) { + unset($item['iconfig'][$x]); + } + } + } + return true; + } + + if(intval($item)) + $iid = intval($item); + + if(! $iid) + return false; + + return q("delete from iconfig where iid = %d and cat = '%s' and k = '%s' ", + intval($iid), + dbesc($family), + dbesc($key) + ); + +} + diff --git a/include/language.php b/include/language.php index 59979aa85..121816ae6 100644 --- a/include/language.php +++ b/include/language.php @@ -65,15 +65,22 @@ function get_best_language() { if(isset($langs) && count($langs)) { foreach ($langs as $lang => $v) { $lang = strtolower($lang); - if(file_exists("view/$lang") && is_dir("view/$lang")) { + if(is_dir("view/$lang")) { $preferred = $lang; break; } } } - if(isset($preferred)) - return $preferred; + if(! isset($preferred)) + $preferred = 'unset'; + + $arr = array('langs' => $langs, 'preferred' => $preferred); + + call_hooks('get_best_language',$arr); + + if($arr['preferred'] !== 'unset') + return $arr['preferred']; $a = get_app(); return ((isset($a->config['system']['language'])) ? $a->config['system']['language'] : 'en'); @@ -182,7 +189,7 @@ function t($s, $ctx = '') { function translate_projectname($s) { - return str_replace(array('$projectname','$Projectname'),array(PLATFORM_NAME,ucfirst(PLATFORM_NAME)),$s); + return str_replace(array('$projectname','$Projectname'),array(Zotlabs\Project\System::get_platform_name(),ucfirst(Zotlabs\Project\System::get_platform_name())),$s); } diff --git a/include/menu.php b/include/menu.php index 075372515..d90cefddb 100644 --- a/include/menu.php +++ b/include/menu.php @@ -299,7 +299,7 @@ function menu_add_item($menu_id, $uid, $arr) { $channel = get_app()->get_channel(); } - $acl = new AccessList($channel); + $acl = new Zotlabs\Access\AccessList($channel); $acl->set_from_array($arr); $p = $acl->get(); @@ -340,7 +340,7 @@ function menu_edit_item($menu_id, $uid, $arr) { $channel = get_app()->get_channel(); } - $acl = new AccessList($channel); + $acl = new Zotlabs\Access\AccessList($channel); $acl->set_from_array($arr); $p = $acl->get(); diff --git a/include/message.php b/include/message.php index 940fcc275..f24bb6e4b 100644 --- a/include/message.php +++ b/include/message.php @@ -11,6 +11,7 @@ require_once('include/attach.php'); function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto='',$expires = ''){ $ret = array('success' => false); + $is_reply = false; $a = get_app(); $observer_hash = get_observer_hash(); @@ -51,6 +52,7 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto=' $conv_guid = ''; if(strlen($replyto)) { + $is_reply = true; $r = q("select conv_guid from mail where channel_id = %d and ( mid = '%s' or parent_mid = '%s' ) limit 1", intval(local_channel()), dbesc($replyto), @@ -187,8 +189,8 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto=' - $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 ) - VALUES ( %d, '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )", + $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 )", intval($channel['channel_account_id']), dbesc($conv_guid), intval(1), @@ -201,7 +203,8 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto=' dbesc($mid), dbesc($replyto), dbesc(datetime_convert()), - dbescdate($expires) + dbescdate($expires), + intval($is_reply) ); // verify the save diff --git a/include/nav.php b/include/nav.php index 1630c4731..5f630745c 100644 --- a/include/nav.php +++ b/include/nav.php @@ -76,7 +76,7 @@ EOT; if(local_channel()) { - if($chans && count($chans) > 1 && feature_enabled(local_channel(),'nav_channel_select')) + if($chans && count($chans) > 1 && feature_enabled(local_channel(),'nav_channel_select') && (! UNO)) $nav['channels'] = $chans; $nav['logout'] = Array('logout',t('Logout'), "", t('End this session'),'logout_nav_btn'); @@ -84,7 +84,7 @@ EOT; // user menu $nav['usermenu'][] = Array('channel/' . $channel['channel_address'], t('Home'), "", t('Your posts and conversations'),'channel_nav_btn'); $nav['usermenu'][] = Array('profile/' . $channel['channel_address'], t('View Profile'), "", t('Your profile page'),'profile_nav_btn'); - if(feature_enabled(local_channel(),'multi_profiles')) + if(feature_enabled(local_channel(),'multi_profiles') && (! UNO)) $nav['usermenu'][] = Array('profiles', t('Edit Profiles'),"", t('Manage/Edit profiles'),'profiles_nav_btn'); else $nav['usermenu'][] = Array('profiles/' . $prof[0]['id'], t('Edit Profile'),"", t('Edit your profile'),'profiles_nav_btn'); @@ -94,16 +94,17 @@ EOT; require_once('include/chat.php'); $has_chats = chatroom_list_count(local_channel()); - $nav['usermenu'][] = Array('chat/' . $channel['channel_address'] . (($has_chats) ? '' : '/new'), t('Chat'),"",t('Your chatrooms'),'chat_nav_btn'); + if(! UNO) + $nav['usermenu'][] = Array('chat/' . $channel['channel_address'] . (($has_chats) ? '' : '/new'), t('Chat'),"",t('Your chatrooms'),'chat_nav_btn'); require_once('include/menu.php'); $has_bookmarks = menu_list_count(local_channel(),'',MENU_BOOKMARK) + menu_list_count(local_channel(),'',MENU_SYSTEM|MENU_BOOKMARK); - if($has_bookmarks) { + if(($has_bookmarks) && (! UNO)) { $nav['usermenu'][] = Array('bookmarks', t('Bookmarks'), "", t('Your bookmarks'),'bookmarks_nav_btn'); } - if(feature_enabled($channel['channel_id'],'webpages')) + if(feature_enabled($channel['channel_id'],'webpages') && (! UNO)) $nav['usermenu'][] = Array('webpages/' . $channel['channel_address'],t('Webpages'),"",t('Your webpages'),'webpages_nav_btn'); } else { @@ -154,7 +155,8 @@ EOT; $nav['help'] = array($help_url, t('Help'), "", t('Help and documentation'),'help_nav_btn'); - $nav['apps'] = array('apps', t('Apps'), "", t('Applications, utilities, links, games'),'apps_nav_btn'); + if(! UNO) + $nav['apps'] = array('apps', t('Apps'), "", t('Applications, utilities, links, games'),'apps_nav_btn'); $nav['search'] = array('search', t('Search'), "", t('Search site @name, #tag, ?docs, content')); @@ -196,8 +198,9 @@ EOT; $nav['all_events'] = array('events', t('Events'), "", t('Event Calendar'),'events_nav_btn'); $nav['all_events']['all']=array('events', t('See all events'), "", ""); $nav['all_events']['mark'] = array('', t('Mark all events seen'), '',''); - - $nav['manage'] = array('manage', t('Channel Manager'), "", t('Manage Your Channels'),'manage_nav_btn'); + + if(! UNO) + $nav['manage'] = array('manage', t('Channel Manager'), "", t('Manage Your Channels'),'manage_nav_btn'); $nav['settings'] = array('settings', t('Settings'),"", t('Account/Channel Settings'),'settings_nav_btn'); diff --git a/include/network.php b/include/network.php index 859a60650..3efc82447 100644 --- a/include/network.php +++ b/include/network.php @@ -40,7 +40,7 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { $ch = @curl_init($url); if(($redirects > 8) || (! $ch)) - return false; + return $ret; @curl_setopt($ch, CURLOPT_HEADER, true); @curl_setopt($ch, CURLINFO_HEADER_OUT, true); @@ -171,7 +171,7 @@ function z_post_url($url,$params, $redirects = 0, $opts = array()) { $ch = curl_init($url); if(($redirects > 8) || (! $ch)) - return ret; + return $ret; @curl_setopt($ch, CURLOPT_HEADER, true); @curl_setopt($ch, CURLINFO_HEADER_OUT, true); @@ -185,9 +185,11 @@ function z_post_url($url,$params, $redirects = 0, $opts = array()) { if($ciphers) @curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, $ciphers); - if(x($opts,'headers')) + if(x($opts,'headers')) { @curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['headers']); - +logger('headers: ' . print_r($opts['headers'],true) . 'redir: ' . $redirects); + } + if(x($opts,'nobody')) @curl_setopt($ch, CURLOPT_NOBODY, $opts['nobody']); @@ -236,6 +238,21 @@ function z_post_url($url,$params, $redirects = 0, $opts = array()) { $base = substr($base,strlen($chunk)); } + // would somebody take lighttpd and just shoot it? + + if($http_code == 417) { + curl_close($ch); + if($opts) { + if($opts['headers']) + $opts['headers'][] = 'Expect:'; + else + $opts['headers'] = array('Expect:'); + } + else + $opts = array('headers' => array('Expect:')); + return z_post_url($url,$params,++$redirects,$opts); + } + if($http_code == 301 || $http_code == 302 || $http_code == 303 || $http_code == 307 || $http_code == 308) { $matches = array(); preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches); @@ -294,8 +311,8 @@ function z_post_url_json($url, $params, $redirects = 0, $opts = array()) { } -function json_return_and_die($x) { - header("content-type: application/json"); +function json_return_and_die($x, $content_type = 'application/json') { + header("Content-type: $content_type"); echo json_encode($x); killme(); } @@ -1043,75 +1060,191 @@ function discover_by_url($url,$arr = null) { } + +function convert_salmon_key($key) { + + if(strstr($key,',')) + $rawkey = substr($key,strpos($key,',')+1); + else + $rawkey = substr($key,5); + + $key_info = explode('.',$rawkey); + + $m = base64url_decode($key_info[1]); + $e = base64url_decode($key_info[2]); + + logger('key details: ' . print_r($key_info,true), LOGGER_DEBUG); + $salmon_key = metopem($m,$e); + return $salmon_key; + +} + + function discover_by_webbie($webbie) { require_once('library/HTML5/Parser.php'); + $result = array(); + $network = null; + $diaspora = false; + $gnusoc = false; + + $has_salmon = false; + $salmon_key = false; + $atom_feed = false; + $diaspora_base = ''; + $diaspora_guid = ''; + $diaspora_key = ''; + $dfrn = false; + $webbie = strtolower($webbie); $x = webfinger_rfc7033($webbie,true); if($x && array_key_exists('links',$x) && $x['links']) { foreach($x['links'] as $link) { - if(array_key_exists('rel',$link) && $link['rel'] == 'http://purl.org/zot/protocol') { - logger('discover_by_webbie: zot found for ' . $webbie, LOGGER_DEBUG); - if(array_key_exists('zot',$x) && $x['zot']['success']) - $i = import_xchan($x['zot']); - else { - $z = z_fetch_url($link['href']); - if($z['success']) { - $j = json_decode($z['body'],true); - $i = import_xchan($j); - return true; + if(array_key_exists('rel',$link)) { + if($link['rel'] == 'http://purl.org/zot/protocol') { + logger('discover_by_webbie: zot found for ' . $webbie, LOGGER_DEBUG); + if(array_key_exists('zot',$x) && $x['zot']['success']) + $i = import_xchan($x['zot']); + else { + $z = z_fetch_url($link['href']); + if($z['success']) { + $j = json_decode($z['body'],true); + $i = import_xchan($j); + return true; + } + } + } + if($link['rel'] == 'magic-public-key') { + if(substr($link['href'],0,5) === 'data:') { + $salmon_key = convert_salmon_key($link['href']); } } + if($link['rel'] == 'salmon') { + $has_salmon = true; + $salmon = $link['href']; + } + if($link['rel'] == 'http://schemas.google.com/g/2010#updates-from') { + $atom_feed = $link['href']; + } } } } - $arr = array('address' => $webbie, 'success' => false); - call_hooks('discover_by_webbie', $arr); + logger('webfing: ' . print_r($x,true), LOGGER_DATA, LOG_INFO); + + $arr = array('address' => $webbie, 'success' => false, 'webfinger' => $x); + call_hooks('discover_channel_webfinger', $arr); if($arr['success']) return true; - $result = array(); - $network = null; - $diaspora = false; + if($salmon_key && $has_salmon && $atom_feed) { + + $gnusoc = true; + $addr = $x['address']; - $diaspora_base = ''; - $diaspora_guid = ''; - $diaspora_key = ''; - $dfrn = false; + $m = parse_url($x['location']); + + $k = z_fetch_url($atom_feed); + if($k['success']) + $feed_meta = feed_meta($k['body']); + + // stash any discovered pubsubhubbub hubs in case we need to follow them + // this will save an expensive lookup later + + if($feed_meta['hubs']) + set_xconfig($addr,'system','push_hubs',$feed_meta['hubs']); - $x = old_webfinger($webbie); - if($x) { - logger('old_webfinger: ' . print_r($x,true)); - foreach($x as $link) { - if($link['@attributes']['rel'] === NAMESPACE_DFRN) - $dfrn = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === 'salmon') - $notify = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === NAMESPACE_FEED) - $poll = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard') - $hcard = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page') - $profile = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === 'http://portablecontacts.net/spec/1.0') - $poco = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === 'http://joindiaspora.com/seed_location') { - $diaspora_base = unamp($link['@attributes']['href']); - $diaspora = true; + if($feed_meta && $feed_meta['author']) { + $r = q("select * from xchan where xchan_hash = '%s' limit 1", + dbesc($addr) + ); + if($r) { + $r = q("update xchan set xchan_name = '%s', xchan_network = '%s', xchan_name_date = '%s' where xchan_hash = '%s' limit 1", + dbesc(($feed_meta['author']['author_name']) ? $feed_meta['author']['author_name'] : $x['nickname']), + dbesc('gnusoc'), + dbesc(datetime_convert()), + dbesc($addr) + ); } - if($link['@attributes']['rel'] === 'http://joindiaspora.com/guid') { - $diaspora_guid = unamp($link['@attributes']['href']); - $diaspora = true; + else { + + $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_pubkey, xchan_addr, xchan_url, xchan_name, xchan_network, xchan_name_date ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", + dbesc($addr), + dbesc($x['location']), + dbesc($salmon_key), + dbesc($addr), + dbesc($x['location']), + dbesc(($feed_meta['author']['author_name']) ? $feed_meta['author']['author_name'] : $x['nickname']), + dbesc('gnusoc'), + dbescdate(datetime_convert()) + ); } - if($link['@attributes']['rel'] === 'diaspora-public-key') { - $diaspora_key = base64_decode(unamp($link['@attributes']['href'])); - if(strstr($diaspora_key,'RSA ')) - $pubkey = rsatopem($diaspora_key); - else - $pubkey = $diaspora_key; - $diaspora = true; + + $r = q("select * from hubloc where hubloc_hash = '%s' limit 1", + dbesc($addr) + ); + + if(! $r) { + + $r = q("insert into hubloc ( hubloc_guid, hubloc_hash, hubloc_addr, hubloc_network, hubloc_url, hubloc_host, hubloc_callback, hubloc_updated, hubloc_primary ) values ('%s','%s','%s','%s','%s','%s','%s','%s', 1)", + dbesc($x['location']), + dbesc($addr), + dbesc($addr), + dbesc('gnusoc'), + dbesc($m['scheme'] . '://' . $m['host']), + dbesc($m['host']), + dbesc($salmon), + dbescdate(datetime_convert()) + ); + } + $photos = import_xchan_photo($feed_meta['author']['author_photo'],$addr); + $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()), + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc($photos[3]), + dbesc($addr) + ); + return true; + + } + } + else { + + $x = old_webfinger($webbie); + if($x) { + logger('old_webfinger: ' . print_r($x,true)); + foreach($x as $link) { + if($link['@attributes']['rel'] === NAMESPACE_DFRN) + $dfrn = unamp($link['@attributes']['href']); + if($link['@attributes']['rel'] === 'salmon') + $notify = unamp($link['@attributes']['href']); + if($link['@attributes']['rel'] === NAMESPACE_FEED) + $poll = unamp($link['@attributes']['href']); + if($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard') + $hcard = unamp($link['@attributes']['href']); + if($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page') + $profile = unamp($link['@attributes']['href']); + if($link['@attributes']['rel'] === 'http://portablecontacts.net/spec/1.0') + $poco = unamp($link['@attributes']['href']); + if($link['@attributes']['rel'] === 'http://joindiaspora.com/seed_location') { + $diaspora_base = unamp($link['@attributes']['href']); + $diaspora = true; + } + if($link['@attributes']['rel'] === 'http://joindiaspora.com/guid') { + $diaspora_guid = unamp($link['@attributes']['href']); + $diaspora = true; + } + if($link['@attributes']['rel'] === 'diaspora-public-key') { + $diaspora_key = base64_decode(unamp($link['@attributes']['href'])); + if(strstr($diaspora_key,'RSA ')) + $pubkey = rsatopem($diaspora_key); + else + $pubkey = $diaspora_key; + $diaspora = true; + } } } @@ -1144,6 +1277,10 @@ function discover_by_webbie($webbie) { dbesc($addr) ); + // fix relative urls + if($vcard['photo'] && (strpos($vcard['photo'],'http') !== 0)) + $vcard['photo'] = $diaspora_base . '/' . $vcard['photo']; + /** * * Diaspora communications are notoriously unreliable and receiving profile update messages (indeed any messages) @@ -1163,7 +1300,7 @@ function discover_by_webbie($webbie) { } else { - $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_pubkey, xchan_addr, xchan_url, xchan_name, xchan_network, xchan_instance_url, xchan_name_date ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", + $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_pubkey, xchan_addr, xchan_url, xchan_name, xchan_network, xchan_name_date ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", dbesc($addr), dbesc($guid), dbesc($pubkey), @@ -1171,7 +1308,6 @@ function discover_by_webbie($webbie) { dbesc($profile), dbesc($vcard['fn']), dbesc($network), - dbesc(z_root()), dbescdate(datetime_convert()) ); } @@ -1195,7 +1331,7 @@ function discover_by_webbie($webbie) { } $photos = import_xchan_photo($vcard['photo'],$addr); $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'])), + dbescdate(datetime_convert()), dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), @@ -1205,7 +1341,7 @@ function discover_by_webbie($webbie) { return true; } - + } return false; /* @@ -1274,32 +1410,115 @@ LSIeXnd14lQYK/uxW/8cTFjcmddsKxeXysoQxbSa9VdDK+KkpZdgYXYrTTofXs6v+ ) */ - - - - } } function webfinger_rfc7033($webbie,$zot = false) { - if(! strpos($webbie,'@')) - return false; - $lhs = substr($webbie,0,strpos($webbie,'@')); - $rhs = substr($webbie,strpos($webbie,'@')+1); - - $resource = 'acct:' . $webbie; + if(strpos($webbie,'@')) { + $lhs = substr($webbie,0,strpos($webbie,'@')); + $rhs = substr($webbie,strpos($webbie,'@')+1); + $resource = 'acct:' . $webbie; + } + else { + $m = parse_url($webbie); + if($m) { + if($m['scheme'] !== 'https') + return false; + $rhs = $m['host'] . (($m['port']) ? ':' . $m['port'] : ''); + $resource = urlencode($webbie); + } + else + return false; + } + logger('fetching url from resource: ' . $rhs . ':' . $webbie); $s = z_fetch_url('https://' . $rhs . '/.well-known/webfinger?f=&resource=' . $resource . (($zot) ? '&zot=1' : '')); - if($s['success']) + if($s['success']) { $j = json_decode($s['body'],true); + + // 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 + // 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 + // or provide lookup by url + + $j['address'] = find_webfinger_address($j,$rhs); + $j['location'] = find_webfinger_location($j,$rhs); + if($j['address']) + $j['nickname'] = substr($j['address'],0,strpos($j['address'],'@')); + } else return false; + return($j); } +function find_webfinger_address($j,$rhs) { + if(is_array($j) && ($j)) { + if(strpos($j['subject'],'acct:') !== false && strpos($j['subject'],'@' . $rhs)) + return str_replace('acct:','',$j['subject']); + if($j['aliases']) { + foreach($j['aliases'] as $alias) { + if(strpos($alias,'acct:') !== false && strpos($alias,'@' . $rhs)) { + return str_replace('acct:','',$alias); + } + } + } + } + return ''; +} + + +function find_webfinger_location($j,$rhs) { + if(is_array($j) && ($j)) { + if(strpos($j['subject'],'http') === 0) { + $x = match_webfinger_location($j['subject'],$rhs); + if($x) + return $x; + } + if($j['aliases']) { + foreach($j['aliases'] as $alias) { + if(strpos($alias,'http') === 0) { + $x = match_webfinger_location($alias,$rhs); + if($x) + return($x); + } + } + } + } + return ''; +} + +function match_webfinger_location($s,$h) { + + // GNU-social and the older StatusNet + if(preg_match('|' . $h . '/user/([0-9]*?)$|',$s)) + return $s; + // Redmatrix / hubzilla + if(preg_match('|' . $h . '/channel/|',$s)) + return $s; + // Friendica + if(preg_match('|' . $h . '/profile/|',$s)) + return $s; + + $arr = array('test' => $s, 'host' => $h, 'success' => false); + call_hooks('match_webfinger_location',$arr); + if($arr['success']) + return $s; + return ''; +} + + + + + + function old_webfinger($webbie) { @@ -1451,8 +1670,25 @@ function scrape_vcard($url) { if(attribute_contains($item->getAttribute('class'), 'vcard')) { $level2 = $item->getElementsByTagName('*'); foreach($level2 as $x) { + if(attribute_contains($x->getAttribute('id'),'pod_location')) + $ret['pod_location'] = $x->textContent; if(attribute_contains($x->getAttribute('class'),'fn')) $ret['fn'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'uid')) + $ret['uid'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'nickname')) + $ret['nick'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'searchable')) + $ret['searchable'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'key')) + $ret['public_key'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'given_name')) + $ret['given_name'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'family_name')) + $ret['family_name'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'url')) + $ret['url'] = $x->textContent; + if((attribute_contains($x->getAttribute('class'),'photo')) || (attribute_contains($x->getAttribute('class'),'avatar'))) { $size = intval($x->getAttribute('width')); @@ -1461,10 +1697,6 @@ function scrape_vcard($url) { $largest_photo = $size; } } - if((attribute_contains($x->getAttribute('class'),'nickname')) - || (attribute_contains($x->getAttribute('class'),'uid'))) { - $ret['nick'] = $x->textContent; - } } } } @@ -1615,18 +1847,19 @@ function format_and_send_email($sender,$xchan,$item) { // load the template for private message notifications $tpl = get_markup_template('email_notify_html.tpl'); $email_html_body = replace_macros($tpl,array( - '$banner' => $banner, - '$product' => $product, - '$preamble' => '', - '$sitename' => $sitename, - '$siteurl' => $siteurl, + '$banner' => $banner, + '$notify_icon' => Zotlabs\Project\System::get_notify_icon(), + '$product' => $product, + '$preamble' => '', + '$sitename' => $sitename, + '$siteurl' => $siteurl, '$source_name' => $sender['xchan_name'], '$source_link' => $sender['xchan_url'], '$source_photo' => $sender['xchan_photo_m'], - '$username' => $xchan['xchan_name'], + '$username' => $xchan['xchan_name'], '$hsitelink' => $datarray['hsitelink'], '$hitemlink' => $datarray['hitemlink'], - '$thanks' => $thanks, + '$thanks' => $thanks, '$site_admin' => $site_admin, '$title' => $title, '$htmlversion' => $htmlversion, @@ -1635,20 +1868,20 @@ function format_and_send_email($sender,$xchan,$item) { // load the template for private message notifications $tpl = get_markup_template('email_notify_text.tpl'); $email_text_body = replace_macros($tpl, array( - '$banner' => $banner, - '$product' => $product, - '$preamble' => '', - '$sitename' => $sitename, - '$siteurl' => $siteurl, + '$banner' => $banner, + '$product' => $product, + '$preamble' => '', + '$sitename' => $sitename, + '$siteurl' => $siteurl, '$source_name' => $sender['xchan_name'], '$source_link' => $sender['xchan_url'], '$source_photo' => $sender['xchan_photo_m'], - '$username' => $xchan['xchan_name'], - '$hsitelink' => $datarray['hsitelink'], - '$hitemlink' => $datarray['hitemlink'], - '$thanks' => $thanks, + '$username' => $xchan['xchan_name'], + '$hsitelink' => $datarray['hsitelink'], + '$hitemlink' => $datarray['hitemlink'], + '$thanks' => $thanks, '$site_admin' => $site_admin, - '$title' => $title, + '$title' => $title, '$textversion' => $textversion )); @@ -1662,13 +1895,13 @@ function format_and_send_email($sender,$xchan,$item) { // use the EmailNotification library to send the message enotify::send(array( - 'fromName' => $product, - 'fromEmail' => $sender_email, - 'replyTo' => $sender_email, - 'toEmail' => str_replace('mailto:','',$xchan['xchan_addr']), - 'messageSubject' => (($title) ? $title : t('No Subject')), - 'htmlVersion' => $email_html_body, - 'textVersion' => $email_text_body, + 'fromName' => $product, + 'fromEmail' => $sender_email, + 'replyTo' => $sender_email, + 'toEmail' => str_replace('mailto:','',$xchan['xchan_addr']), + 'messageSubject' => (($title) ? $title : t('No Subject')), + 'htmlVersion' => $email_html_body, + 'textVersion' => $email_text_body, 'additionalMailHeader' => '', )); @@ -1717,7 +1950,7 @@ function get_site_info() { global $a; $register_policy = Array('REGISTER_CLOSED', 'REGISTER_APPROVE', 'REGISTER_OPEN'); - $directory_mode = Array('DIRECTORY_MODE_NORMAL', 'DIRECTORY_MODE_SECONDARY','DIRECTORY_MODE_PRIMARY', 256 => 'DIRECTORY_MODE_STANDALONE'); + $directory_mode = Array('DIRECTORY_MODE_NORMAL', 'DIRECTORY_MODE_PRIMARY', 'DIRECTORY_MODE_SECONDARY', 256 => 'DIRECTORY_MODE_STANDALONE'); $sql_extra = ''; @@ -1763,16 +1996,13 @@ function get_site_info() { $site_info = get_config('system','info'); $site_name = get_config('system','sitename'); if(! get_config('system','hidden_version_siteinfo')) { - $version = RED_VERSION; - $tag = get_std_version(); + $version = Zotlabs\Project\System::get_project_version(); + $tag = Zotlabs\Project\System::get_std_version(); if(@is_dir('.git') && function_exists('shell_exec')) { $commit = trim( @shell_exec('git log -1 --format="%h"')); -// if(! get_config('system','hidden_tag_siteinfo')) -// $tag = trim( @shell_exec('git describe --tags --abbrev=0')); -// else -// $tag = ''; } + if(! isset($commit) || strlen($commit) > 16) $commit = ''; } @@ -1788,10 +2018,22 @@ function get_site_info() { $hide_in_statistics = intval(get_config('system','hide_in_statistics')); $site_expire = intval(get_config('system', 'default_expire_days')); + load_config('feature_lock'); + $locked_features = array(); + if(is_array($a->config['feature_lock']) && count($a->config['feature_lock'])) { + foreach($a->config['feature_lock'] as $k => $v) { + if($k === 'config_loaded') + continue; + $locked_features[$k] = intval($v); + } + } + + $data = Array( 'version' => $version, 'version_tag' => $tag, + 'server_role' => Zotlabs\Project\System::get_server_role(), 'commit' => $commit, 'url' => z_root(), 'plugins' => $visible_plugins, @@ -1799,12 +2041,13 @@ function get_site_info() { 'invitation_only' => intval(get_config('system','invitation_only')), 'directory_mode' => $directory_mode[get_config('system','directory_mode')], 'language' => get_config('system','language'), - 'rss_connections' => get_config('system','feed_contacts'), + 'rss_connections' => intval(get_config('system','feed_contacts')), 'expiration' => $site_expire, 'default_service_restrictions' => $service_class, + 'locked_features' => $locked_features, 'admin' => $admin, 'site_name' => (($site_name) ? $site_name : ''), - 'platform' => PLATFORM_NAME, + 'platform' => Zotlabs\Project\System::get_platform_name(), 'dbdriver' => $db->getdriver(), 'lastpoll' => get_config('system','lastpoll'), 'info' => (($site_info) ? $site_info : ''), diff --git a/include/notifier.php b/include/notifier.php index 50981df9d..628847d54 100644 --- a/include/notifier.php +++ b/include/notifier.php @@ -68,7 +68,6 @@ require_once('include/html2plain.php'); require_once('include/cli_startup.php'); require_once('include/zot.php'); require_once('include/queue_fn.php'); -require_once('include/session.php'); require_once('include/datetime.php'); require_once('include/items.php'); require_once('include/bbcode.php'); @@ -188,6 +187,7 @@ function notifier_run($argv, $argc){ $recipients[] = $r[0]['abook_xchan']; $private = false; $packet_type = 'refresh'; + $packet_recips = array(array('guid' => $r[0]['xchan_guid'],'guid_sig' => $r[0]['xchan_guid_sig'],'hash' => $r[0]['xchan_hash'])); } } } @@ -297,7 +297,7 @@ function notifier_run($argv, $argc){ $channel = $s[0]; if($channel['channel_hash'] !== $target_item['author_xchan'] && $channel['channel_hash'] !== $target_item['owner_xchan']) { - logger("notifier: Sending channel {$channel['channel_hash']} is not owner {$target_item['owner_xchan']} or author {$target_item['author_xchan']}"); + logger("notifier: Sending channel {$channel['channel_hash']} is not owner {$target_item['owner_xchan']} or author {$target_item['author_xchan']}", LOGGER_NORMAL, LOG_WARNING); return; } @@ -316,7 +316,7 @@ function notifier_run($argv, $argc){ return; if(strpos($r[0]['postopts'],'nodeliver') !== false) { - logger('notifier: target item is undeliverable', LOGGER_DEBUG); + logger('notifier: target item is undeliverable', LOGGER_DEBUG, LOG_NOTICE); return; } @@ -352,8 +352,8 @@ function notifier_run($argv, $argc){ // $cmd === 'relay' indicates the owner is sending it to the original recipients // don't allow the item in the relay command to relay to owner under any circumstances, it will loop - logger('notifier: relay_to_owner: ' . (($relay_to_owner) ? 'true' : 'false'), LOGGER_DATA); - logger('notifier: top_level_post: ' . (($top_level_post) ? 'true' : 'false'), LOGGER_DATA); + logger('notifier: relay_to_owner: ' . (($relay_to_owner) ? 'true' : 'false'), LOGGER_DATA, LOG_DEBUG); + logger('notifier: top_level_post: ' . (($top_level_post) ? 'true' : 'false'), LOGGER_DATA, LOG_DEBUG); // tag_deliver'd post which needs to be sent back to the original author @@ -395,7 +395,7 @@ function notifier_run($argv, $argc){ // TODO verify this is needed - copied logic from same place in old code if(intval($target_item['item_deleted']) && (! intval($target_item['item_wall']))) { - logger('notifier: ignoring delete notification for non-wall item'); + logger('notifier: ignoring delete notification for non-wall item', LOGGER_NORMAL, LOG_NOTICE); return; } } @@ -410,18 +410,19 @@ function notifier_run($argv, $argc){ $x = $encoded_item; $x['title'] = 'private'; $x['body'] = 'private'; - logger('notifier: encoded item: ' . print_r($x,true), LOGGER_DATA); + logger('notifier: encoded item: ' . print_r($x,true), LOGGER_DATA, LOG_DEBUG); stringify_array_elms($recipients); if(! $recipients) return; -// logger('notifier: recipients: ' . print_r($recipients,true)); +// logger('notifier: recipients: ' . print_r($recipients,true), LOGGER_NORMAL, LOG_DEBUG); $env_recips = (($private) ? array() : null); $details = q("select xchan_hash, xchan_instance_url, xchan_network, xchan_addr, xchan_guid, xchan_guid_sig from xchan where xchan_hash in (" . implode(',',$recipients) . ")"); + $recip_list = array(); if($details) { @@ -436,15 +437,42 @@ function notifier_run($argv, $argc){ if(! $delivery_options) format_and_send_email($channel,$d,$target_item); } + } + } - - } + $narr = array( + 'channel' => $channel, + 'env_recips' => $env_recips, + 'packet_recips' => $packet_recips, + 'recipients' => $recipients, + 'item' => $item, + 'target_item' => $target_item, + 'top_level_post' => $top_level_post, + 'private' => $private, + 'followup' => $followup, + 'relay_to_owner' => $relay_to_owner, + 'uplink' => $uplink, + 'cmd' => $cmd, + 'mail' => $mail, + 'location' => $location, + 'request' => $request, + 'normal_mode' => $normal_mode, + 'packet_type' => $packet_type, + 'walltowall' => $walltowall, + 'queued' => array() + ); + + call_hooks('notifier_process', $narr); + if($narr['queued']) { + foreach($narr['queued'] as $pq) + $deliveries[] = $pq; } + if(($private) && (! $env_recips)) { // shouldn't happen - logger('notifier: private message with no envelope recipients.' . print_r($argv,true)); + logger('notifier: private message with no envelope recipients.' . print_r($argv,true), LOGGER_NORMAL, LOG_NOTICE); } logger('notifier: recipients (may be delivered to more if public): ' . print_r($recip_list,true), LOGGER_DEBUG); @@ -459,7 +487,7 @@ function notifier_run($argv, $argc){ if(! $r) { - logger('notifier: no hubs'); + logger('notifier: no hubs', LOGGER_NORMAL, LOG_NOTICE); return; } @@ -482,7 +510,7 @@ function notifier_run($argv, $argc){ foreach($hubs as $hub) { if(in_array($hub['hubloc_url'],$dead_hubs)) { - logger('skipping dead hub: ' . $hub['hubloc_url'], LOGGER_DEBUG); + logger('skipping dead hub: ' . $hub['hubloc_url'], LOGGER_DEBUG, LOG_INFO); continue; } @@ -502,8 +530,8 @@ function notifier_run($argv, $argc){ } } - logger('notifier: will notify/deliver to these hubs: ' . print_r($hublist,true), LOGGER_DEBUG); - + logger('notifier: will notify/deliver to these hubs: ' . print_r($hublist,true), LOGGER_DEBUG, LOG_DEBUG); + foreach($dhubs as $hub) { @@ -512,6 +540,7 @@ function notifier_run($argv, $argc){ $narr = array( 'channel' => $channel, 'env_recips' => $env_recips, + 'packet_recips' => $packet_recips, 'recipients' => $recipients, 'item' => $item, 'target_item' => $target_item, @@ -547,7 +576,7 @@ function notifier_run($argv, $argc){ $packet = null; if($packet_type === 'refresh' || $packet_type === 'purge') { - $packet = zot_build_packet($channel,$packet_type); + $packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null)); } elseif($packet_type === 'request') { $packet = zot_build_packet($channel,$packet_type,$env_recips,$hub['hubloc_sitekey'],$hash, @@ -576,7 +605,7 @@ function notifier_run($argv, $argc){ )); // only create delivery reports for normal undeleted items - if(is_array($target_item) && array_key_exists('postopts',$target_item) && (! $target_item['item_deleted'])) { + if(is_array($target_item) && array_key_exists('postopts',$target_item) && (! $target_item['item_deleted']) && (! get_config('system','disable_dreport'))) { q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_result, dreport_time, dreport_xchan, dreport_queue ) values ( '%s','%s','%s','%s','%s','%s','%s' ) ", dbesc($target_item['mid']), dbesc($hub['hubloc_host']), diff --git a/include/oembed.php b/include/oembed.php index e50d34c7d..fb7a30e65 100755 --- a/include/oembed.php +++ b/include/oembed.php @@ -3,6 +3,34 @@ function oembed_replacecb($matches){ $embedurl=$matches[1]; + + // site white/black list + + if(($x = get_config('system','embed_deny'))) { + $l = explode("\n",$x); + if($l) { + foreach($l as $ll) { + if(trim($ll) && strpos($embedurl,trim($ll)) !== false) + return '<a href="' . $embedurl . '">' . $embedurl . '</a>'; + } + } + } + if(($x = get_config('system','embed_allow'))) { + $found = false; + $l = explode("\n",$x); + if($l) { + foreach($l as $ll) { + if(trim($ll) && strpos($embedurl,trim($ll)) !== false) { + $found = true; + break; + } + } + } + if(! $found) { + return '<a href="' . $embedurl . '">' . $embedurl . '</a>'; + } + } + // implements a personal embed white/black list for logged in members if(local_channel()) { if(($x = get_pconfig(local_channel(),'system','embed_deny'))) { @@ -53,6 +81,10 @@ function oembed_fetch_url($embedurl){ $a = get_app(); + $embedurl = str_replace('&','&', $embedurl); + +// logger('fetch: ' . $embedurl); + $txt = Cache::get($a->videowidth . $embedurl); if(strstr($txt,'youtu') && strstr(z_root(),'https:')) { @@ -78,7 +110,6 @@ function oembed_fetch_url($embedurl){ else { // try oembed autodiscovery $redirects = 0; - $result = z_fetch_url($embedurl, false, $redirects, array('timeout' => 15, 'accept_content' => "text/*", 'novalidate' => true )); if($result['success']) $html_text = $result['body']; @@ -88,8 +119,8 @@ function oembed_fetch_url($embedurl){ if ($dom){ $xpath = new DOMXPath($dom); $attr = "oembed"; - $xattr = oe_build_xpath("class","oembed"); + $entries = $xpath->query("//link[@type='application/json+oembed']"); foreach($entries as $e){ $href = $e->getAttributeNode("href")->nodeValue; @@ -121,20 +152,29 @@ function oembed_fetch_url($embedurl){ if ($txt[0]!="{") $txt='{"type":"error"}'; //save in cache - Cache::set($a->videowidth . $embedurl,$txt); + + if(! get_config('system','oembed_cache_disable')) + Cache::set($a->videowidth . $embedurl,$txt); } $j = json_decode($txt); $j->embedurl = $embedurl; + +// logger('fetch return: ' . print_r($j,true)); + return $j; + + } function oembed_format_object($j){ $a = get_app(); $embedurl = $j->embedurl; +// logger('format: ' . print_r($j,true)); + $jhtml = oembed_iframe($j->embedurl,(isset($j->width) ? $j->width : null), (isset($j->height) ? $j->height : null)); $ret="<span class='oembed ".$j->type."'>"; @@ -173,6 +213,14 @@ function oembed_format_object($j){ $ret.="<br>"; }; break; case "link": { + if($j->thumbnail_url) { + if(is_matrix_url($embedurl)) { + $embedurl = zid($embedurl); + $j->thumbnail_url = zid($j->thumbnail_url); + } + $ret = '<a href="' . $embedurl . '" ><img src="' . $j->thumbnail_url . '" alt="thumbnail" /></a><br /><br />'; + } + //$ret = "<a href='".$embedurl."'>".$j->title."</a>"; }; break; case "rich": { @@ -184,23 +232,29 @@ function oembed_format_object($j){ // add link to source if not present in "rich" type if ( $j->type!='rich' || !strpos($j->html,$embedurl) ){ $embedlink = (isset($j->title))?$j->title:$embedurl; - $ret .= '<span class="bookmark-identifier">#^</span>' . "<a href='$embedurl' rel='oembed'>$embedlink</a>"; - $ret .= "<br>"; + $ret .= '<br />' . "<a href='$embedurl' rel='oembed'>$embedlink</a>"; + $ret .= "<br />"; if (isset($j->author_name)) $ret.=" by ".$j->author_name; if (isset($j->provider_name)) $ret.=" on ".$j->provider_name; } else { // add <a> for html2bbcode conversion - $ret .= "<a href='$embedurl' rel='oembed'/>"; + $ret .= "<br /><a href='$embedurl' rel='oembed'>$embedurl</a>"; } $ret.="<br style='clear:left'></span>"; return mb_convert_encoding($ret, 'HTML-ENTITIES', mb_detect_encoding($ret)); } function oembed_iframe($src,$width,$height) { - if(! $width || strstr($width,'%')) + $scroll = ' scrolling="no" '; + if(! $width || strstr($width,'%')) { $width = '640'; - if(! $height || strstr($height,'%')) + $scroll = ' scrolling="auto" '; + } + if(! $height || strstr($height,'%')) { $height = '300'; + $scroll = ' scrolling="auto" '; + } + // try and leave some room for the description line. $height = intval($height) + 80; $width = intval($width) + 40; @@ -209,7 +263,7 @@ function oembed_iframe($src,$width,$height) { // Make sure any children are sandboxed within their own iframe. - return '<iframe height="' . $height . '" width="' . $width . '" src="' . $s . '" frameborder="no" >' + return '<iframe ' . $scroll . 'height="' . $height . '" width="' . $width . '" src="' . $s . '" frameborder="no" >' . t('Embedded content') . '</iframe>'; } diff --git a/include/permissions.php b/include/permissions.php index a8e761f87..8f593640c 100644 --- a/include/permissions.php +++ b/include/permissions.php @@ -151,9 +151,13 @@ function get_all_perms($uid, $observer_xchan, $internal_use = true) { // Check if this $uid is actually the $observer_xchan - if it's your content // you always have permission to do anything + // if you've moved elsewhere, you will only have read only access if(($observer_xchan) && ($r[0]['channel_hash'] === $observer_xchan)) { - $ret[$perm_name] = true; + if($r[0]['channel_moved'] && (! $permission[2])) + $ret[$perm_name] = false; + else + $ret[$perm_name] = true; continue; } @@ -286,7 +290,7 @@ function perm_is_allowed($uid, $observer_xchan, $permission) { $channel_perm = $global_perms[$permission][0]; - $r = q("select %s, channel_pageflags, channel_hash from channel where channel_id = %d limit 1", + $r = q("select %s, channel_pageflags, channel_moved, channel_hash from channel where channel_id = %d limit 1", dbesc($channel_perm), intval($uid) ); @@ -325,9 +329,15 @@ function perm_is_allowed($uid, $observer_xchan, $permission) { return false; // Check if this $uid is actually the $observer_xchan + // you will have full access unless the channel was moved - + // in which case you will have read_only access - if($r[0]['channel_hash'] === $observer_xchan) - return true; + if($r[0]['channel_hash'] === $observer_xchan) { + if($r[0]['channel_moved'] && (! $global_perms[$permission][2])) + return false; + else + return true; + } if($r[0][$channel_perm] & PERMS_PUBLIC) return true; @@ -626,7 +636,7 @@ function get_role_perms($role) { $ret['channel_w_mail'] = PERMS_SPECIFIC; $ret['channel_w_chat'] = PERMS_SPECIFIC; $ret['channel_a_delegate'] = PERMS_SPECIFIC; - $ret['channel_r_storage'] = PERMS_PUBLIC; + $ret['channel_r_storage'] = PERMS_SPECIFIC; $ret['channel_w_storage'] = PERMS_SPECIFIC; $ret['channel_r_pages'] = PERMS_PUBLIC; $ret['channel_w_pages'] = PERMS_SPECIFIC; @@ -641,10 +651,10 @@ function get_role_perms($role) { $ret['directory_publish'] = true; $ret['online'] = false; $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT + |PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE|PERMS_W_TAGWALL; $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT + |PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE|PERMS_W_TAGWALL; $ret['channel_r_stream'] = PERMS_PUBLIC; $ret['channel_r_profile'] = PERMS_PUBLIC; @@ -864,10 +874,10 @@ function get_role_perms($role) { */ function get_roles() { $roles = array( - t('Social Networking') => array('social' => t('Mostly Public'), 'social_restricted' => t('Restricted'), 'social_private' => t('Private')), - t('Community Forum') => array('forum' => t('Mostly Public'), 'forum_restricted' => t('Restricted'), 'forum_private' => t('Private')), - t('Feed Republish') => array('feed' => t('Mostly Public'), 'feed_restricted' => t('Restricted')), - t('Special Purpose') => array('soapbox' => t('Celebrity/Soapbox'), 'repository' => t('Group Repository')), + t('Social Networking') => array('social' => t('Social - Mostly Public'), 'social_restricted' => t('Social - Restricted'), 'social_private' => t('Social - Private')), + t('Community Forum') => array('forum' => t('Forum - Mostly Public'), 'forum_restricted' => t('Forum - Restricted'), 'forum_private' => t('Forum - Private')), + t('Feed Republish') => array('feed' => t('Feed - Mostly Public'), 'feed_restricted' => t('Feed - Restricted')), + t('Special Purpose') => array('soapbox' => t('Special - Celebrity/Soapbox'), 'repository' => t('Special - Group Repository')), t('Other') => array('custom' => t('Custom/Expert Mode')) ); diff --git a/include/photo/photo_driver.php b/include/photo/photo_driver.php index 0de3b9c97..dce92d8da 100644 --- a/include/photo/photo_driver.php +++ b/include/photo/photo_driver.php @@ -69,6 +69,8 @@ abstract class photo_driver { abstract function cropImage($max,$x,$y,$w,$h); + abstract function cropImageRect($maxx,$maxy,$x,$y,$w,$h); + abstract function imageString(); @@ -229,6 +231,7 @@ abstract class photo_driver { $this->doScaleImage($dest_width,$dest_height); } + public function scaleImageSquare($dim) { if(!$this->is_valid()) return FALSE; diff --git a/include/photo/photo_gd.php b/include/photo/photo_gd.php index 2ac7287e4..24bdc204f 100644 --- a/include/photo/photo_gd.php +++ b/include/photo/photo_gd.php @@ -108,6 +108,23 @@ class photo_gd extends photo_driver { $this->setDimensions(); } + public function cropImageRect($maxx,$maxy,$x,$y,$w,$h) { + if(!$this->is_valid()) + return FALSE; + + $dest = imagecreatetruecolor( $maxx, $maxy ); + imagealphablending($dest, false); + imagesavealpha($dest, true); + if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha + imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $maxx, $maxy, $w, $h); + if($this->image) + imagedestroy($this->image); + $this->image = $dest; + $this->setDimensions(); + } + + + public function imageString() { if(!$this->is_valid()) return FALSE; diff --git a/include/photo/photo_imagick.php b/include/photo/photo_imagick.php index 3f84fd06c..32bb61342 100644 --- a/include/photo/photo_imagick.php +++ b/include/photo/photo_imagick.php @@ -163,6 +163,24 @@ class photo_imagick extends photo_driver { $this->doScaleImage($max,$max); } + public function cropImageRect($maxx,$maxy,$x,$y,$w,$h) { + if(!$this->is_valid()) + return FALSE; + + $this->image->setFirstIterator(); + do { + $this->image->cropImage($w, $h, $x, $y); + /** + * We need to remove the canvas, + * or the image is not resized to the crop: + * http://php.net/manual/en/imagick.cropimage.php#97232 + */ + $this->image->setImagePage(0, 0, 0, 0); + } while ($this->image->nextImage()); + + $this->doScaleImage($maxx,$maxy); + } + public function imageString() { if(!$this->is_valid()) return FALSE; diff --git a/include/photos.php b/include/photos.php index 93511d2c0..5dd5f3c62 100644 --- a/include/photos.php +++ b/include/photos.php @@ -48,7 +48,7 @@ function photo_upload($channel, $observer, $args) { // 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 - $acl = new AccessList($channel); + $acl = new Zotlabs\Access\AccessList($channel); if(array_key_exists('directory',$args) && $args['directory']) $acl->set($args['directory']); if(array_key_exists('allow_cid',$args)) @@ -296,11 +296,11 @@ function photo_upload($channel, $observer, $args) { $photo_link = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo_hash . ']' . t('a new photo') . '[/zrl]'; - $album_link = '[zrl=' . z_root() . '/photos/album/' . bin2hex($album) . ']' . $album . '[/zrl]'; + $album_link = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/album/' . bin2hex($album) . ']' . $album . '[/zrl]'; $activity_format = sprintf(t('%1$s posted %2$s to %3$s','photo_upload'), $author_link, $photo_link, $album_link); - $summary = $activity_format . "\n\n" . (($args['body']) ? $args['body'] . "\n\n" : ''); + $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]' @@ -391,8 +391,8 @@ function photo_upload($channel, $observer, $args) { $arr['deny_cid'] = $ac['deny_cid']; $arr['deny_gid'] = $ac['deny_gid']; $arr['verb'] = ACTIVITY_POST; - $arr['obj_type'] = ACTIVITY_OBJ_PHOTO; - $arr['object'] = json_encode($object); + $arr['obj_type'] = ACTIVITY_OBJ_PHOTO; + $arr['object'] = json_encode($object); $arr['tgt_type'] = ACTIVITY_OBJ_ALBUM; $arr['target'] = json_encode($target); $arr['item_wall'] = 1; @@ -400,7 +400,7 @@ function photo_upload($channel, $observer, $args) { $arr['item_thread_top'] = 1; $arr['item_private'] = intval($acl->is_private()); $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . $arr['mid']; - $arr['body'] = $summary; + $arr['body'] = $summary; // this one is tricky because the item and the photo have the same permissions, those of the photo. diff --git a/include/plugin.php b/include/plugin.php index 4a35a0170..bd844442f 100755 --- a/include/plugin.php +++ b/include/plugin.php @@ -291,7 +291,7 @@ function call_hooks($name, &$data = null) { $func = $hook[1]; $func($a, $data); } else { - // remove orphan hooks + q("DELETE FROM hook WHERE hook = '%s' AND file = '%s' AND function = '%s'", dbesc($name), dbesc($hook[0]), @@ -313,7 +313,6 @@ function call_hooks($name, &$data = null) { * * Version: 1.2.3 * * Author: John <profile url> * * Author: Jane <email> - * * Compat: Red [(version)], Friendica [(version)] * * *\endcode * @param string $plugin the name of the plugin @@ -325,8 +324,9 @@ function get_plugin_info($plugin){ 'name' => $plugin, 'description' => '', 'author' => array(), + 'maintainer' => array(), 'version' => '', - 'compat' => '' + 'requires' => '' ); if (!is_file("addon/$plugin/$plugin.php")) @@ -342,17 +342,16 @@ function get_plugin_info($plugin){ if ($l != ""){ list($k, $v) = array_map("trim", explode(":", $l, 2)); $k = strtolower($k); - if ($k == 'author'){ + if ($k == 'author' || $k == 'maintainer'){ $r = preg_match("|([^<]+)<([^>]+)>|", $v, $m); if ($r) { - $info['author'][] = array('name' => $m[1], 'link' => $m[2]); + $info[$k][] = array('name' => $m[1], 'link' => $m[2]); } else { - $info['author'][] = array('name' => $v); - } - } else { - if (array_key_exists($k, $info)){ - $info[$k] = $v; + $info[$k][] = array('name' => $v); } + } + else { + $info[$k] = $v; } } } @@ -361,6 +360,51 @@ function get_plugin_info($plugin){ return $info; } +function check_plugin_versions($info) { + + if(! is_array($info)) + return true; + + if(array_key_exists('minversion',$info)) { + if(! version_compare(STD_VERSION,trim($info['minversion']), '>=')) { + logger('minversion limit: ' . $info['name'],LOGGER_NORMAL,LOG_WARNING); + return false; + } + } + if(array_key_exists('maxversion',$info)) { + if(version_compare(STD_VERSION,trim($info['maxversion']), '>')) { + logger('maxversion limit: ' . $info['name'],LOGGER_NORMAL,LOG_WARNING); + return false; + } + } + if(array_key_exists('minphpversion',$info)) { + if(! version_compare(PHP_VERSION,trim($info['minphpversion']), '>=')) { + logger('minphpversion limit: ' . $info['name'],LOGGER_NORMAL,LOG_WARNING); + return false; + } + } + + if(array_key_exists('requires',$info)) { + $arr = explode(',',$info['requires']); + $found = true; + if($arr) { + foreach($arr as $test) { + $test = trim($test); + if(! $test) + continue; + if(! in_array($test,get_app()->plugins)) + $found = false; + } + } + if(! $found) + return false; + } + + return true; +} + + + /** * @brief Parse theme comment in search of theme infos. @@ -626,17 +670,6 @@ function get_markup_template($s, $root = '') { return $template; } -// return the standardised version. Since we can't easily compare -// before the STD_VERSION definition was applied, we have to treat -// all prior release versions the same. You can dig through them -// with other means (such as RED_VERSION) if necessary. - -function get_std_version() { - if(defined('STD_VERSION')) - return STD_VERSION; - return '0.0.0'; -} - function folder_exists($folder) { diff --git a/include/poller.php b/include/poller.php index 031fb4533..808b54ee5 100644 --- a/include/poller.php +++ b/include/poller.php @@ -205,7 +205,7 @@ function poller_run($argv, $argc){ $keep_reports = intval(get_config('system','expire_delivery_reports')); if($keep_reports === 0) - $keep_reports = 30; + $keep_reports = 10; q("delete from dreport where dreport_time < %s - INTERVAL %s", db_utcnow(), diff --git a/include/profile_selectors.php b/include/profile_selectors.php index a80677cb3..9f993f803 100644 --- a/include/profile_selectors.php +++ b/include/profile_selectors.php @@ -7,7 +7,7 @@ function gender_selector($current="",$suffix="") { call_hooks('gender_selector', $select); - $o .= "<select name=\"gender$suffix\" id=\"gender-select$suffix\" size=\"1\" >"; + $o .= "<select class=\"form-control\" name=\"gender$suffix\" id=\"gender-select$suffix\" size=\"1\" >"; foreach($select as $selection) { if($selection !== 'NOTRANSLATION') { $selected = (($selection == $current) ? ' selected="selected" ' : ''); @@ -24,7 +24,7 @@ function gender_selector_min($current="",$suffix="") { call_hooks('gender_selector_min', $select); - $o .= "<select name=\"gender$suffix\" id=\"gender-select$suffix\" size=\"1\" >"; + $o .= "<select class=\"form-control\" name=\"gender$suffix\" id=\"gender-select$suffix\" size=\"1\" >"; foreach($select as $selection) { if($selection !== 'NOTRANSLATION') { $selected = (($selection == $current) ? ' selected="selected" ' : ''); @@ -44,7 +44,7 @@ function sexpref_selector($current="",$suffix="") { call_hooks('sexpref_selector', $select); - $o .= "<select name=\"sexual$suffix\" id=\"sexual-select$suffix\" size=\"1\" >"; + $o .= "<select class=\"form-control\" name=\"sexual$suffix\" id=\"sexual-select$suffix\" size=\"1\" >"; foreach($select as $selection) { if($selection !== 'NOTRANSLATION') { $selected = (($selection == $current) ? ' selected="selected" ' : ''); @@ -62,7 +62,7 @@ function sexpref_selector_min($current="",$suffix="") { call_hooks('sexpref_selector_min', $select); - $o .= "<select name=\"sexual$suffix\" id=\"sexual-select$suffix\" size=\"1\" >"; + $o .= "<select class=\"form-control\" name=\"sexual$suffix\" id=\"sexual-select$suffix\" size=\"1\" >"; foreach($select as $selection) { if($selection !== 'NOTRANSLATION') { $selected = (($selection == $current) ? ' selected="selected" ' : ''); @@ -81,7 +81,7 @@ function marital_selector($current="",$suffix="") { call_hooks('marital_selector', $select); - $o .= "<select name=\"marital\" id=\"marital-select\" size=\"1\" >"; + $o .= "<select class=\"form-control\" name=\"marital\" id=\"marital-select\" size=\"1\" >"; foreach($select as $selection) { if($selection !== 'NOTRANSLATION') { $selected = (($selection == $current) ? ' selected="selected" ' : ''); @@ -98,7 +98,7 @@ function marital_selector_min($current="",$suffix="") { call_hooks('marital_selector_min', $select); - $o .= "<select name=\"marital\" id=\"marital-select\" size=\"1\" >"; + $o .= "<select class=\"form-control\" name=\"marital\" id=\"marital-select\" size=\"1\" >"; foreach($select as $selection) { if($selection !== 'NOTRANSLATION') { $selected = (($selection == $current) ? ' selected="selected" ' : ''); diff --git a/include/queue_fn.php b/include/queue_fn.php index 1e53d7488..676620e2f 100644 --- a/include/queue_fn.php +++ b/include/queue_fn.php @@ -88,6 +88,7 @@ function queue_deliver($outq, $immediate = false) { } } else { + // zot sites should all have a site record, unless they've been dead for as long as // your site has existed. Since we don't know for sure what these sites are, // call them unknown @@ -100,6 +101,11 @@ function queue_deliver($outq, $immediate = false) { } } + $arr = array('outq' => $outq, 'base' => $base, 'handled' => false, 'immediate' => $immediate); + call_hooks('queue_deliver',$arr); + if($arr['handled']) + return; + // "post" queue driver - used for diaspora and friendica-over-diaspora communications. if($outq['outq_driver'] === 'post') { @@ -118,11 +124,31 @@ function queue_deliver($outq, $immediate = false) { dbesc($outq['outq_hash']) ); remove_queue_item($outq['outq_hash']); + + // server is responding - see if anything else is going to this destination and is piled up + // and try to send some more. We're relying on the fact that do_delivery() results in an + // immediate delivery otherwise we could get into a queue loop. + + if(! $immediate) { + $x = q("select outq_hash from outq where outq_posturl = '%s' and outq_delivered = 0", + dbesc($outq['outq_posturl']) + ); + + $piled_up = array(); + if($x) { + foreach($x as $xx) { + $piled_up[] = $xx['outq_hash']; + } + } + if($piled_up) { + do_delivery($piled_up); + } + } } else { logger('deliver: queue post returned ' . $result['return_code'] . ' from ' . $outq['outq_posturl'],LOGGER_DEBUG); - update_queue_item($argv[$x]); + update_queue_item($outq['outq_posturl']); } return; } diff --git a/include/ratenotif.php b/include/ratenotif.php index e94f30247..2c636c710 100644 --- a/include/ratenotif.php +++ b/include/ratenotif.php @@ -11,7 +11,6 @@ function ratenotif_run($argv, $argc){ $a = get_app(); - require_once("session.php"); require_once("datetime.php"); require_once('include/items.php'); require_once('include/Contact.php'); diff --git a/include/reddav.php b/include/reddav.php index c592597a9..a0bd1b1fc 100644 --- a/include/reddav.php +++ b/include/reddav.php @@ -19,13 +19,13 @@ */ use Sabre\DAV; -use RedMatrix\RedDAV; +use Zotlabs\Storage; require_once('vendor/autoload.php'); require_once('include/attach.php'); -require_once('include/RedDAV/RedFile.php'); -require_once('include/RedDAV/RedDirectory.php'); -require_once('include/RedDAV/RedBasicAuth.php'); +//require_once('Zotlabs/Storage/File.php'); +//require_once('Zotlabs/Storage/Directory.php'); +//require_once('Zotlabs/Storage/BasicAuth.php'); /** * @brief Returns an array with viewable channels. @@ -51,7 +51,7 @@ function RedChannelList(&$auth) { if (perm_is_allowed($rr['channel_id'], $auth->observer, 'view_storage')) { logger('found channel: /cloud/' . $rr['channel_address'], LOGGER_DATA); // @todo can't we drop '/cloud'? It gets stripped off anyway in RedDirectory - $ret[] = new RedDAV\RedDirectory('/cloud/' . $rr['channel_address'], $auth); + $ret[] = new Zotlabs\Storage\Directory('/cloud/' . $rr['channel_address'], $auth); } } } @@ -167,9 +167,9 @@ function RedCollectionData($file, &$auth) { foreach ($r as $rr) { //logger('filename: ' . $rr['filename'], LOGGER_DEBUG); if (intval($rr['is_dir'])) { - $ret[] = new RedDAV\RedDirectory($path . '/' . $rr['filename'], $auth); + $ret[] = new Zotlabs\Storage\Directory($path . '/' . $rr['filename'], $auth); } else { - $ret[] = new RedDAV\RedFile($path . '/' . $rr['filename'], $rr, $auth); + $ret[] = new Zotlabs\Storage\File($path . '/' . $rr['filename'], $rr, $auth); } } @@ -204,7 +204,7 @@ function RedFileData($file, &$auth, $test = false) { if ((! $file) || ($file === '/')) { - return new RedDAV\RedDirectory('/', $auth); + return new Zotlabs\Storage\Directory('/', $auth); } $file = trim($file, '/'); @@ -274,7 +274,7 @@ function RedFileData($file, &$auth, $test = false) { if ($test) return true; // final component was a directory. - return new RedDAV\RedDirectory($file, $auth); + return new Zotlabs\Storage\Directory($file, $auth); } if ($errors) { @@ -293,9 +293,9 @@ function RedFileData($file, &$auth, $test = false) { return true; if (intval($r[0]['is_dir'])) { - return new RedDAV\RedDirectory($path . '/' . $r[0]['filename'], $auth); + return new Zotlabs\Storage\Directory($path . '/' . $r[0]['filename'], $auth); } else { - return new RedDAV\RedFile($path . '/' . $r[0]['filename'], $r[0], $auth); + return new Zotlabs\Storage\File($path . '/' . $r[0]['filename'], $r[0], $auth); } } return false; diff --git a/include/security.php b/include/security.php index d4ebe0024..f3cf2d1b6 100644 --- a/include/security.php +++ b/include/security.php @@ -18,20 +18,13 @@ function authenticate_success($user_record, $login_initial = false, $interactive $_SESSION['addr'] = $_SERVER['REMOTE_ADDR']; + $lastlog_updated = false; + if(x($user_record, 'account_id')) { $a->account = $user_record; $_SESSION['account_id'] = $user_record['account_id']; $_SESSION['authenticated'] = 1; - if($login_initial || $update_lastlog) { - q("update account set account_lastlog = '%s' where account_id = %d", - dbesc(datetime_convert()), - intval($_SESSION['account_id']) - ); - $a->account['account_lastlog'] = datetime_convert(); - call_hooks('logged_in', $a->account); - - } $uid_to_load = (((x($_SESSION,'uid')) && (intval($_SESSION['uid']))) ? intval($_SESSION['uid']) @@ -42,9 +35,19 @@ function authenticate_success($user_record, $login_initial = false, $interactive change_channel($uid_to_load); } + if($login_initial || $update_lastlog) { + q("update account set account_lastlog = '%s' where account_id = %d", + dbesc(datetime_convert()), + intval($_SESSION['account_id']) + ); + $a->account['account_lastlog'] = datetime_convert(); + $lastlog_updated = true; + call_hooks('logged_in', $a->account); + } + } - if($login_initial) { + if(($login_initial) && (! $lastlog_updated)) { call_hooks('logged_in', $user_record); @@ -145,7 +148,7 @@ function change_channel($change_channel) { * @return string additional SQL where statement */ -function permissions_sql($owner_id, $remote_observer = null) { +function permissions_sql($owner_id, $remote_observer = null, $table = '') { $local_channel = local_channel(); @@ -155,10 +158,14 @@ function permissions_sql($owner_id, $remote_observer = null) { * default permissions - anonymous user */ - $sql = " AND allow_cid = '' - AND allow_gid = '' - AND deny_cid = '' - AND deny_gid = '' + if($table) + $table .= '.'; + + + $sql = " AND {$table}allow_cid = '' + AND {$table}allow_gid = '' + AND {$table}deny_cid = '' + AND {$table}deny_gid = '' "; /** @@ -178,7 +185,7 @@ function permissions_sql($owner_id, $remote_observer = null) { */ else { - $observer = (($remote_observer) ? $remote_observer : get_observer_hash()); + $observer = ((! is_null($remote_observer)) ? $remote_observer : get_observer_hash()); if($observer) { $groups = init_groups_visitor($observer); @@ -190,8 +197,8 @@ function permissions_sql($owner_id, $remote_observer = null) { } $regexop = db_getfunc('REGEXP'); $sql = sprintf( - " AND ( NOT (deny_cid like '%s' OR deny_gid $regexop '%s') - AND ( allow_cid like '%s' OR allow_gid $regexop '%s' OR ( allow_cid = '' AND allow_gid = '') ) + " AND ( NOT ({$table}deny_cid like '%s' OR {$table}deny_gid $regexop '%s') + AND ( {$table}allow_cid like '%s' OR {$table}allow_gid $regexop '%s' OR ( {$table}allow_cid = '' AND {$table}allow_gid = '') ) ) ", dbesc(protect_sprintf( '%<' . $observer . '>%')), @@ -408,7 +415,7 @@ function init_groups_visitor($contact_id) { $r = q("SELECT hash FROM `groups` left join group_member on groups.id = group_member.gid WHERE xchan = '%s' ", dbesc($contact_id) ); - if(count($r)) { + if($r) { foreach($r as $rr) $groups[] = $rr['hash']; } diff --git a/include/session.php b/include/session.php index 92004bc18..6060e4712 100644 --- a/include/session.php +++ b/include/session.php @@ -1,4 +1,5 @@ <?php + /** * @file include/session.php * @@ -11,11 +12,43 @@ $session_exists = 0; $session_expire = 180000; + +/** + * @brief Resets the current session. + * + * @return void + */ + +function nuke_session() { + new_cookie(0); // 0 means delete on browser exit + + unset($_SESSION['authenticated']); + unset($_SESSION['account_id']); + unset($_SESSION['uid']); + unset($_SESSION['visitor_id']); + unset($_SESSION['administrator']); + unset($_SESSION['cid']); + unset($_SESSION['theme']); + unset($_SESSION['mobile_theme']); + unset($_SESSION['show_mobile']); + unset($_SESSION['page_flags']); + unset($_SESSION['delegate']); + unset($_SESSION['delegate_channel']); + unset($_SESSION['my_url']); + unset($_SESSION['my_address']); + unset($_SESSION['addr']); + unset($_SESSION['return_url']); + unset($_SESSION['remote_service_class']); + unset($_SESSION['remote_hub']); +} + + + function new_cookie($time) { $old_sid = session_id(); -// ??? This shouldn't have any effect if called after session_start() -// We probably need to set the session expiration and change the PHPSESSID cookie. + // ??? This shouldn't have any effect if called after session_start() + // We probably need to set the session expiration and change the PHPSESSID cookie. session_set_cookie_params($time); session_regenerate_id(false); @@ -108,8 +141,9 @@ ini_set('session.use_only_cookies', 1); ini_set('session.cookie_httponly', 1); /* - * PHP function which sets our user-level session storage functions. + * Set our session storage functions. */ + session_set_save_handler( 'ref_session_open', 'ref_session_close', @@ -117,4 +151,17 @@ session_set_save_handler( 'ref_session_write', 'ref_session_destroy', 'ref_session_gc' -);
\ No newline at end of file +); + + + // Force cookies to be secure (https only) if this site is SSL enabled. Must be done before session_start(). + + if(intval(get_app()->config['system']['ssl_cookie_protection'])) { + $arr = session_get_cookie_params(); + session_set_cookie_params( + ((isset($arr['lifetime'])) ? $arr['lifetime'] : 0), + ((isset($arr['path'])) ? $arr['path'] : '/'), + ((isset($arr['domain'])) ? $arr['domain'] : get_app()->get_hostname()), + ((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false), + ((isset($arr['httponly'])) ? $arr['httponly'] : true)); + }
\ No newline at end of file diff --git a/include/taxonomy.php b/include/taxonomy.php index 0bf89a7c1..5ef106938 100644 --- a/include/taxonomy.php +++ b/include/taxonomy.php @@ -398,16 +398,17 @@ function get_things($profile_hash,$uid) { $things[$k] = null; foreach($r as $rr) { - $l = q("select xchan_name, 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 likee = xchan_hash where target_type = '%s' and target_id = '%s' and channel_id = %d", dbesc(ACTIVITY_OBJ_THING), dbesc($rr['obj_obj']), intval($uid) ); - for($x = 0; $x < count($l); $x ++) + for($x = 0; $x < count($l); $x ++) { $l[$x]['xchan_url'] = zid($l[$x]['xchan_url']); - + $l[$x]['xchan_photo_s'] = zid($l[$x]['xchan_photo_s']); + } if(! $things[$rr['obj_verb']]) $things[$rr['obj_verb']] = array(); diff --git a/include/text.php b/include/text.php index dd52b16b6..621f4cf93 100644 --- a/include/text.php +++ b/include/text.php @@ -536,9 +536,10 @@ function attribute_contains($attr, $s) { * * @param string $msg Message to log * @param int $level A log level. + * @param int $priority - compatible with syslog */ -function logger($msg, $level = 0) { +function logger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) { // turn off logger in install mode global $a; global $db; @@ -559,8 +560,8 @@ function logger($msg, $level = 0) { $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': '; } - $s = datetime_convert() . ':' . session_id() . ' ' . $where . $msg . PHP_EOL; - $pluginfo = array('filename' => $logfile, 'loglevel' => $level, 'message' => $s,'logged' => false); + $s = datetime_convert() . ':' . log_priority_str($priority) . ':' . session_id() . ':' . $where . $msg . PHP_EOL; + $pluginfo = array('filename' => $logfile, 'loglevel' => $level, 'message' => $s,'priority' => $priority, 'logged' => false); call_hooks('logger',$pluginfo); @@ -568,6 +569,23 @@ function logger($msg, $level = 0) { @file_put_contents($pluginfo['filename'], $pluginfo['message'], FILE_APPEND); } +function log_priority_str($priority) { + $parr = array( + LOG_EMERG => 'LOG_EMERG', + LOG_ALERT => 'LOG_ALERT', + LOG_CRIT => 'LOG_CRIT', + LOG_ERR => 'LOG_ERR', + LOG_WARNING => 'LOG_WARNING', + LOG_NOTICE => 'LOG_NOTICE', + LOG_INFO => 'LOG_INFO', + LOG_DEBUG => 'LOG_DEBUG' + ); + + if($parr[$priority]) + return $parr[$priority]; + return 'LOG_UNDEFINED'; +} + /** * @brief This is a special logging facility for developers. * @@ -794,7 +812,7 @@ function contact_block() { ); if(count($r)) { - $contacts = sprintf( tt('%d Connection','%d Connections', $total),$total); + $contacts = t('Connections'); $micropro = Array(); foreach($r as $rr) { $rr['archived'] = (intval($rr['abook_archived']) ? true : false); @@ -807,7 +825,7 @@ function contact_block() { $o = replace_macros($tpl, array( '$contacts' => $contacts, '$nickname' => $a->profile['channel_address'], - '$viewconnections' => t('View Connections'), + '$viewconnections' => (($total > $shown) ? sprintf(t('View all %s connections'),$total) : ''), '$micropro' => $micropro, )); @@ -950,16 +968,24 @@ function get_poke_verbs() { // index is present tense verb // value is array containing past tense verb, translation of present, translation of past - $arr = array( - 'poke' => array( 'poked', t('poke'), t('poked')), - 'ping' => array( 'pinged', t('ping'), t('pinged')), - 'prod' => array( 'prodded', t('prod'), t('prodded')), - 'slap' => array( 'slapped', t('slap'), t('slapped')), - 'finger' => array( 'fingered', t('finger'), t('fingered')), - 'rebuff' => array( 'rebuffed', t('rebuff'), t('rebuffed')), - ); + if(get_config('system','poke_basic')) { + $arr = array( + 'poke' => array( 'poked', t('poke'), t('poked')), + ); + } + else { + $arr = array( + 'poke' => array( 'poked', t('poke'), t('poked')), + 'ping' => array( 'pinged', t('ping'), t('pinged')), + 'prod' => array( 'prodded', t('prod'), t('prodded')), + 'slap' => array( 'slapped', t('slap'), t('slapped')), + 'finger' => array( 'fingered', t('finger'), t('fingered')), + 'rebuff' => array( 'rebuffed', t('rebuff'), t('rebuffed')), + ); + + call_hooks('poke_verbs', $arr); + } - call_hooks('poke_verbs', $arr); return $arr; } @@ -972,7 +998,7 @@ function get_mood_verbs() { 'tired' => t('tired'), 'perky' => t('perky'), 'angry' => t('angry'), - 'stupefied' => t('stupified'), + 'stupefied' => t('stupefied'), 'puzzled' => t('puzzled'), 'interested' => t('interested'), 'bitter' => t('bitter'), @@ -1418,7 +1444,7 @@ function prepare_body(&$item,$attach = false) { call_hooks('prepare_body_init', $item); - + $s = ''; $photo = ''; $is_photo = ((($item['verb'] === ACTIVITY_POST) && ($item['obj_type'] === ACTIVITY_OBJ_PHOTO)) ? true : false); @@ -1428,7 +1454,7 @@ function prepare_body(&$item,$attach = false) { // if original photo width is <= 640px prepend it to item body if($object['link'][0]['width'] && $object['link'][0]['width'] <= 640) { - $s = '<div class="inline-photo-item-wrapper"><a href="' . zid(rawurldecode($object['id'])) . '" target="_blank"><img class="inline-photo-item" style="max-width:' . $object['link'][0]['width'] . 'px; width:100%; height:auto;" src="' . zid(rawurldecode($object['link'][0]['href'])) . '"></a></div>' . $s; + $s .= '<div class="inline-photo-item-wrapper"><a href="' . zid(rawurldecode($object['id'])) . '" target="_blank"><img class="inline-photo-item" style="max-width:' . $object['link'][0]['width'] . 'px; width:100%; height:auto;" src="' . zid(rawurldecode($object['link'][0]['href'])) . '"></a></div>' . $s; } // if original photo width is > 640px make it a cover photo @@ -1438,7 +1464,7 @@ function prepare_body(&$item,$attach = false) { } } - $s = prepare_text($item['body'],$item['mimetype'], false); + $s .= prepare_text($item['body'],$item['mimetype'], false); $event = (($item['obj_type'] === ACTIVITY_OBJ_EVENT) ? format_event($item['object']) : false); @@ -2698,5 +2724,67 @@ function item_url_replace($channel,&$item,$old,$new) { $item['sig'] = base64url_encode(rsa_sign($item['body'],$channel['channel_prvkey'])); $item['item_verified'] = 1; } + + // @fixme item['plink'] and item['llink'] + +} + + +/** + * @brief Used to wrap ACL elements in angle brackets for storage. + * + * @param[in,out] array &$item + */ +function sanitise_acl(&$item) { + if (strlen($item)) + $item = '<' . notags(trim($item)) . '>'; + else + unset($item); +} + +/** + * @brief Convert an ACL array to a storable string. + * + * @param array $p + * @return array + */ +function perms2str($p) { + $ret = ''; + + if (is_array($p)) + $tmp = $p; + else + $tmp = explode(',', $p); + + if (is_array($tmp)) { + array_walk($tmp, 'sanitise_acl'); + $ret = implode('', $tmp); + } + + return $ret; +} + + +/** + * @brief Turn user/group ACLs stored as angle bracketed text into arrays. + * + * turn string array of angle-bracketed elements into string array + * e.g. "<123xyz><246qyo><sxo33e>" => array(123xyz,246qyo,sxo33e); + * + * @param string $s + * @return array + */ +function expand_acl($s) { + $ret = array(); + + if(strlen($s)) { + $t = str_replace('<','',$s); + $a = explode('>',$t); + foreach($a as $aa) { + if($aa) + $ret[] = $aa; + } + } + return $ret; } diff --git a/include/widgets.php b/include/widgets.php index 4b14d6c94..deb514915 100644 --- a/include/widgets.php +++ b/include/widgets.php @@ -8,7 +8,7 @@ require_once('include/dir_fns.php'); require_once('include/contact_widgets.php'); require_once('include/attach.php'); - +require_once('include/Contact.php'); function widget_profile($args) { $a = get_app(); @@ -16,6 +16,16 @@ function widget_profile($args) { return profile_sidebar($a->profile, $block, true); } +function widget_zcard($args) { + $a = get_app(); + $block = (((get_config('system', 'block_public')) && (! local_channel()) && (! remote_channel())) ? true : false); + $channel = channelx_by_n($a->profile_uid); + return get_zcard($channel,get_observer_hash(),array('width' => 875)); +} + + + + // FIXME The problem with the next widget is that we don't have a search function for webpages that we can send the links to. // Then we should also provide an option to search webpages and conversations. @@ -170,8 +180,8 @@ function widget_follow($args) { } return replace_macros(get_markup_template('follow.tpl'),array( '$connect' => t('Add New Connection'), - '$desc' => t('Enter the channel address'), - '$hint' => t('Example: bob@example.com, http://example.com/barbara'), + '$desc' => t('Enter channel address'), + '$hint' => t('Examples: bob@example.com, https://example.com/barbara'), '$follow' => t('Connect'), '$abook_usage_message' => $abook_usage_message )); @@ -369,6 +379,17 @@ function widget_fullprofile($arr) { return profile_sidebar($a->profile, $block); } +function widget_shortprofile($arr) { + $a = get_app(); + if(! $a->profile['profile_uid']) + return; + + $block = (((get_config('system', 'block_public')) && (! local_channel()) && (! remote_channel())) ? true : false); + + return profile_sidebar($a->profile, $block, true, true); +} + + function widget_categories($arr) { $a = get_app(); @@ -493,37 +514,42 @@ function widget_settings_menu($arr) { 'selected' => ((argv(1) === 'channel') ? 'active' : ''), ), - array( - 'label' => t('Additional features'), - 'url' => $a->get_baseurl(true).'/settings/features', - 'selected' => ((argv(1) === 'features') ? 'active' : ''), - ), + ); - array( - 'label' => t('Feature/Addon settings'), - 'url' => $a->get_baseurl(true).'/settings/featured', - 'selected' => ((argv(1) === 'featured') ? 'active' : ''), - ), + if(get_features()) { + $tabs[] = array( + 'label' => t('Additional features'), + 'url' => $a->get_baseurl(true).'/settings/features', + 'selected' => ((argv(1) === 'features') ? 'active' : ''), + ); + } - array( - 'label' => t('Display settings'), - 'url' => $a->get_baseurl(true).'/settings/display', - 'selected' => ((argv(1) === 'display') ? 'active' : ''), - ), + $tabs[] = array( + 'label' => t('Feature/Addon settings'), + 'url' => $a->get_baseurl(true).'/settings/featured', + 'selected' => ((argv(1) === 'featured') ? 'active' : ''), + ); - array( - 'label' => t('Connected apps'), - 'url' => $a->get_baseurl(true) . '/settings/oauth', - 'selected' => ((argv(1) === 'oauth') ? 'active' : ''), - ), + $tabs[] = array( + 'label' => t('Display settings'), + 'url' => $a->get_baseurl(true).'/settings/display', + 'selected' => ((argv(1) === 'display') ? 'active' : ''), + ); - array( + $tabs[] = array( + 'label' => t('Connected apps'), + 'url' => $a->get_baseurl(true) . '/settings/oauth', + 'selected' => ((argv(1) === 'oauth') ? 'active' : ''), + ); + + // IF can go away when UNO export and import is fully functional + if(! UNO) { + $tabs[] = array( 'label' => t('Export channel'), 'url' => $a->get_baseurl(true) . '/uexport', 'selected' => '' - ), - - ); + ); + } if($role === false || $role === 'custom') { $tabs[] = array( @@ -804,21 +830,39 @@ function widget_suggestedchats($arr) { } function widget_item($arr) { - // FIXME there is no $a here - $uid = $a->profile['profile_uid']; - if((! $uid) || (! $arr['mid'])) + + $channel_id = 0; + if(array_key_exists('channel_id',$arr) && intval($arr['channel_id'])) + $channel_id = intval($arr['channel_id']); + if(! $channel_id) + $channel_id = get_app()->profile_uid; + if(! $channel_id) return ''; - if(! perm_is_allowed($uid, get_observer_hash(), 'view_pages')) + + if((! $arr['mid']) && (! $arr['title'])) return ''; - require_once('include/security.php'); - $sql_extra = item_permissions_sql($uid); + if(! perm_is_allowed($channel_id, get_observer_hash(), 'view_pages')) + return ''; - $r = q("select * from item where mid = '%s' and uid = %d and item_type = " . intval(ITEM_TYPE_WEBPAGE) . " $sql_extra limit 1", - dbesc($arr['mid']), - intval($uid) - ); + require_once('include/security.php'); + $sql_extra = item_permissions_sql($channel_id); + + if($arr['title']) { + $r = q("select item.* from item left join item_id on item.id = item_id.iid + where item.uid = %d and sid = '%s' and service = 'WEBPAGE' and item_type = %d $sql_options $revision limit 1", + intval($channel_id), + dbesc($arr['title']), + intval(ITEM_TYPE_WEBPAGE) + ); + } + else { + $r = q("select * from item where mid = '%s' and uid = %d and item_type = " . intval(ITEM_TYPE_WEBPAGE) . " $sql_extra limit 1", + dbesc($arr['mid']), + intval($channel_id) + ); + } if(! $r) return ''; @@ -912,8 +956,8 @@ function widget_photo($arr) { // ensure they can't sneak in an eval(js) function - if(strpos($style,'(') !== false) - return ''; + if(strpbrk($style,'(\'"<>') !== false) + $style = ''; if(array_key_exists('zrl', $arr) && isset($arr['zrl'])) $zrl = (($arr['zrl']) ? true : false); @@ -933,6 +977,62 @@ function widget_photo($arr) { } +function widget_cover_photo($arr) { + + require_once('include/identity.php'); + $o = ''; + + $a = get_app(); + + if($a->module == 'channel' && $_REQUEST['mid']) + return ''; + + $channel_id = 0; + if(array_key_exists('channel_id', $arr) && intval($arr['channel_id'])) + $channel_id = intval($arr['channel_id']); + if(! $channel_id) + $channel_id = $a->profile_uid; + if(! $channel_id) + return ''; + + $channel = channelx_by_n($channel_id); + + if(array_key_exists('style', $arr) && isset($arr['style'])) + $style = $arr['style']; + else + $style = 'width:100%; height: auto;'; + + // ensure they can't sneak in an eval(js) function + + if(strpbrk($style,'(\'"<>') !== false) + $style = ''; + + if(array_key_exists('title', $arr) && isset($arr['title'])) + $title = $arr['title']; + else + $title = $channel['channel_name']; + + if(array_key_exists('subtitle', $arr) && isset($arr['subtitle'])) + $subtitle = $arr['subtitle']; + else + $subtitle = $channel['xchan_addr']; + + $c = get_cover_photo($channel_id,'html'); + + if($c) { + $photo_html = (($style) ? str_replace('alt=',' style="' . $style . '" alt=',$c) : $c); + + $o = replace_macros(get_markup_template('cover_photo_widget.tpl'),array( + '$photo_html' => $photo_html, + '$title' => $title, + '$subtitle' => $subtitle, + '$hovertitle' => t('Click to show more'), + )); + } + return $o; +} + + function widget_photo_rand($arr) { require_once('include/photos.php'); @@ -1075,16 +1175,18 @@ function widget_rating($arr) { } + + $o = '<div class="widget">'; + $o .= '<h3>' . t('Rating Tools') . '</h3>'; + if((($remote) || (local_channel())) && (! $self)) { - $o = '<div class="widget rateme">'; if($remote) - $o .= '<a class="rateme" href="' . $url . '"><i class="icon-pencil"></i> ' . t('Rate Me') . '</a>'; + $o .= '<a class="btn btn-block btn-primary btn-sm" href="' . $url . '"><i class="icon-pencil"></i> ' . t('Rate Me') . '</a>'; else - $o .= '<div class="rateme fakelink" onclick="doRatings(\'' . $hash . '\'); return false;"><i class="icon-pencil"></i> ' . t('Rate Me') . '</div>'; - $o .= '</div>'; + $o .= '<div class="btn btn-block btn-primary btn-sm" onclick="doRatings(\'' . $hash . '\'); return false;"><i class="icon-pencil"></i> ' . t('Rate Me') . '</div>'; } - $o .= '<div class="widget rateme"><a class="rateme" href="ratings/' . $hash . '"><i class="icon-eye-open"></i> ' . t('View Ratings') . '</a>'; + $o .= '<a class="btn btn-block btn-default btn-sm" href="ratings/' . $hash . '"><i class="icon-eye-open"></i> ' . t('View Ratings') . '</a>'; $o .= '</div>'; return $o; @@ -1119,7 +1221,7 @@ function widget_forums($arr) { $perms_sql = item_permissions_sql(local_channel()) . item_normal(); - $r1 = q("select * from abook left join xchan on abook_xchan = xchan_hash where ( xchan_pubforum = 1 or ((abook_their_perms & %d ) != 0 and (abook_their_perms & %d ) = 0) ) and abook_channel = %d order by xchan_name $limit ", + $r1 = q("select * from abook left join xchan on abook_xchan = xchan_hash where ( xchan_pubforum = 1 or ((abook_their_perms & %d ) != 0 and (abook_their_perms & %d ) = 0) ) and xchan_deleted = 0 and abook_channel = %d order by xchan_name $limit ", intval(PERMS_W_TAGWALL), intval(PERMS_W_STREAM), intval(local_channel()) @@ -1212,12 +1314,14 @@ function widget_admin($arr) { $aside = array( 'site' => array(z_root() . '/admin/site/', t('Site'), 'site'), - 'users' => array(z_root() . '/admin/users/', t('Accounts'), 'users'), + 'users' => array(z_root() . '/admin/users/', t('Accounts'), 'users', 'pending-update', t('Member registrations waiting for confirmation')), 'channels' => array(z_root() . '/admin/channels/', t('Channels'), 'channels'), + 'security' => array(z_root() . '/admin/security/', t('Security'), 'security'), + 'features' => array(z_root() . '/admin/features/', t('Features'), 'features'), 'plugins' => array(z_root() . '/admin/plugins/', t('Plugins'), 'plugins'), 'themes' => array(z_root() . '/admin/themes/', t('Themes'), 'themes'), 'queue' => array(z_root() . '/admin/queue', t('Inspect queue'), 'queue'), - 'profs' => array(z_root() . '/admin/profs', t('Profile Config'), 'profs'), + 'profs' => array(z_root() . '/admin/profs', t('Profile Fields'), 'profs'), 'dbsync' => array(z_root() . '/admin/dbsync/', t('DB updates'), 'dbsync') ); @@ -1226,24 +1330,29 @@ function widget_admin($arr) { $r = q("SELECT * FROM addon WHERE plugin_admin = 1"); - $aside['plugins_admin'] = array(); + $plugins = array(); if($r) { foreach ($r as $h){ $plugin = $h['name']; - $aside['plugins_admin'][] = array(z_root() . '/admin/plugins/' . $plugin, $plugin, 'plugin'); + $plugins[] = array(z_root() . '/admin/plugins/' . $plugin, $plugin, 'plugin'); // temp plugins with admin $a->plugins_admin[] = $plugin; } } - $aside['logs'] = array(z_root() . '/admin/logs/', t('Logs'), 'logs'); + $logs = array(z_root() . '/admin/logs/', t('Logs'), 'logs'); + + $arr = array('links' => $aside,'plugins' => $plugins,'logs' => $logs); + call_hooks('admin_aside',$arr); $o .= replace_macros(get_markup_template('admin_aside.tpl'), array( '$admin' => $aside, '$admtxt' => t('Admin'), '$plugadmtxt' => t('Plugin Features'), + '$plugins' => $plugins, '$logtxt' => t('Logs'), - '$h_pending' => t('User registrations waiting for confirmation'), + '$logs' => $logs, + '$h_pending' => t('Member registrations waiting for confirmation'), '$admurl'=> z_root() . '/admin/' )); diff --git a/include/zot.php b/include/zot.php index 922637bc1..d8cae3954 100644 --- a/include/zot.php +++ b/include/zot.php @@ -11,7 +11,6 @@ require_once('include/crypto.php'); require_once('include/items.php'); require_once('include/hubloc.php'); -require_once('include/DReport.php'); require_once('include/queue_fn.php'); @@ -142,7 +141,7 @@ function zot_build_packet($channel, $type = 'notify', $recipients = null, $remot $data[$k] = $v; } - logger('zot_build_packet: ' . print_r($data,true), LOGGER_DATA); + logger('zot_build_packet: ' . print_r($data,true), LOGGER_DATA, LOG_DEBUG); // Hush-hush ultra top-secret mode @@ -194,7 +193,7 @@ function zot_finger($webbie, $channel = null, $autofallback = true) { logger('zot_finger: no address :' . $webbie); return array('success' => false); } - logger('using xchan_addr: ' . $xchan_addr, LOGGER_DATA); + 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 @@ -211,7 +210,7 @@ function zot_finger($webbie, $channel = null, $autofallback = true) { 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); + logger('url: '.$url.', net: '.var_export($r[0]['hubloc_network'],true), LOGGER_DATA, LOG_DEBUG); return array('success' => false); } } else { @@ -288,9 +287,9 @@ function zot_refresh($them, $channel = null, $force = false) { return true; } - logger('zot_refresh: them: ' . print_r($them,true), LOGGER_DATA); + logger('zot_refresh: them: ' . print_r($them,true), LOGGER_DATA, LOG_DEBUG); if ($channel) - logger('zot_refresh: channel: ' . print_r($channel,true), LOGGER_DATA); + logger('zot_refresh: channel: ' . print_r($channel,true), LOGGER_DATA, LOG_DEBUG); $url = null; @@ -353,7 +352,7 @@ function zot_refresh($them, $channel = null, $force = false) { $result = z_post_url($url . $rhs,$postvars); - logger('zot_refresh: zot-info: ' . print_r($result,true), LOGGER_DATA); + logger('zot_refresh: zot-info: ' . print_r($result,true), LOGGER_DATA, LOG_DEBUG); if ($result['success']) { @@ -381,7 +380,7 @@ function zot_refresh($them, $channel = null, $force = false) { $channel['channel_prvkey']); if($permissions) $permissions = json_decode($permissions,true); - logger('decrypted permissions: ' . print_r($permissions,true), LOGGER_DATA); + logger('decrypted permissions: ' . print_r($permissions,true), LOGGER_DATA, LOG_DEBUG); } else $permissions = $j['permissions']; @@ -523,6 +522,11 @@ function zot_refresh($them, $channel = null, $force = false) { unset($new_connection[0]['abook_id']); unset($new_connection[0]['abook_account']); unset($new_connection[0]['abook_channel']); + + $abconfig = load_abconfig($channel['channel_hash'],$new_connection['abook_xchan']); + if($abconfig) + $new_connection['abconfig'] = $abconfig; + build_sync_packet($channel['channel_id'], array('abook' => $new_connection)); } } @@ -614,7 +618,7 @@ function zot_register_hub($arr) { $x = z_fetch_url($url); - logger('zot_register_hub: ' . print_r($x,true), LOGGER_DATA); + logger('zot_register_hub: ' . print_r($x,true), LOGGER_DATA, LOG_DEBUG); if($x['success']) { $record = json_decode($x['body'],true); @@ -754,8 +758,8 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { dbesc($xchan_hash) ); - logger('import_xchan: update: existing: ' . print_r($r[0],true), LOGGER_DATA); - logger('import_xchan: update: new: ' . print_r($arr,true), LOGGER_DATA); + logger('import_xchan: update: existing: ' . print_r($r[0],true), LOGGER_DATA, LOG_DEBUG); + logger('import_xchan: update: new: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); $what .= 'xchan '; $changed = true; } @@ -955,7 +959,7 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { $ret['hash'] = $xchan_hash; } - logger('import_xchan: result: ' . print_r($ret,true), LOGGER_DATA); + logger('import_xchan: result: ' . print_r($ret,true), LOGGER_DATA, LOG_DEBUG); return $ret; } @@ -980,7 +984,7 @@ function zot_process_response($hub, $arr, $outq) { if (! $x) { logger('zot_process_response: No json from ' . $hub); - logger('zot_process_response: headers: ' . print_r($arr['header'],true), LOGGER_DATA); + logger('zot_process_response: headers: ' . print_r($arr['header'],true), LOGGER_DATA, LOG_DEBUG); } if(is_array($x) && array_key_exists('delivery_report',$x) && is_array($x['delivery_report'])) { @@ -1015,7 +1019,7 @@ function zot_process_response($hub, $arr, $outq) { // async messages remain in the queue until processed. if(intval($outq['outq_async'])) - queue_set_delivered($outq['outq_hash'],$outq['outq_channel']); + remove_queue_item($outq['outq_hash'],$outq['outq_channel']); logger('zot_process_response: ' . print_r($x,true), LOGGER_DEBUG); } @@ -1037,7 +1041,7 @@ function zot_process_response($hub, $arr, $outq) { */ function zot_fetch($arr) { - logger('zot_fetch: ' . print_r($arr,true), LOGGER_DATA); + logger('zot_fetch: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); $url = $arr['sender']['url'] . $arr['callback']; @@ -1134,7 +1138,7 @@ function zot_import($arr, $sender_url) { $i['notify'] = json_decode(crypto_unencapsulate($i['notify'],get_config('system','prvkey')),true); } - logger('zot_import: notify: ' . print_r($i['notify'],true), LOGGER_DATA); + logger('zot_import: notify: ' . print_r($i['notify'],true), LOGGER_DATA, LOG_DEBUG); $hub = zot_gethub($i['notify']['sender']); if((! $hub) || ($hub['hubloc_url'] != $sender_url)) { @@ -1151,7 +1155,7 @@ function zot_import($arr, $sender_url) { if(array_key_exists('message',$i) && array_key_exists('type',$i['message']) && $i['message']['type'] === 'rating') { // rating messages are processed only by directory servers - logger('Rating received: ' . print_r($arr,true), LOGGER_DATA); + logger('Rating received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); $result = process_rating_delivery($i['notify']['sender'],$i['message']); continue; } @@ -1261,8 +1265,8 @@ function zot_import($arr, $sender_url) { continue; } - logger('Activity received: ' . print_r($arr,true), LOGGER_DATA); - logger('Activity recipients: ' . print_r($deliveries,true), LOGGER_DATA); + logger('Activity received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); + logger('Activity recipients: ' . print_r($deliveries,true), LOGGER_DATA, LOG_DEBUG); $relay = ((array_key_exists('flags',$i['message']) && in_array('relay',$i['message']['flags'])) ? true : false); $result = process_delivery($i['notify']['sender'],$arr,$deliveries,$relay,false,$message_request); @@ -1270,16 +1274,16 @@ function zot_import($arr, $sender_url) { elseif($i['message']['type'] === 'mail') { $arr = get_mail_elements($i['message']); - logger('Mail received: ' . print_r($arr,true), LOGGER_DATA); - logger('Mail recipients: ' . print_r($deliveries,true), LOGGER_DATA); + logger('Mail received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); + logger('Mail recipients: ' . print_r($deliveries,true), LOGGER_DATA, LOG_DEBUG); $result = process_mail_delivery($i['notify']['sender'],$arr,$deliveries); } elseif($i['message']['type'] === 'profile') { $arr = get_profile_elements($i['message']); - logger('Profile received: ' . print_r($arr,true), LOGGER_DATA); - logger('Profile recipients: ' . print_r($deliveries,true), LOGGER_DATA); + logger('Profile received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); + logger('Profile recipients: ' . print_r($deliveries,true), LOGGER_DATA, LOG_DEBUG); $result = process_profile_delivery($i['notify']['sender'],$arr,$deliveries); } @@ -1288,16 +1292,16 @@ function zot_import($arr, $sender_url) { $arr = $i['message']; - logger('Channel sync received: ' . print_r($arr,true), LOGGER_DATA); - logger('Channel sync recipients: ' . print_r($deliveries,true), LOGGER_DATA); + logger('Channel sync received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); + logger('Channel sync recipients: ' . print_r($deliveries,true), LOGGER_DATA, LOG_DEBUG); $result = process_channel_sync_delivery($i['notify']['sender'],$arr,$deliveries); } elseif($i['message']['type'] === 'location') { $arr = $i['message']; - logger('Location message received: ' . print_r($arr,true), LOGGER_DATA); - logger('Location message recipients: ' . print_r($deliveries,true), LOGGER_DATA); + logger('Location message received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); + logger('Location message recipients: ' . print_r($deliveries,true), LOGGER_DATA, LOG_DEBUG); $result = process_location_delivery($i['notify']['sender'],$arr,$deliveries); } @@ -1483,7 +1487,7 @@ function public_recips($msg) { } } - logger('public_recips: ' . print_r($r,true), LOGGER_DATA); + logger('public_recips: ' . print_r($r,true), LOGGER_DATA, LOG_DEBUG); return $r; } @@ -1501,7 +1505,7 @@ function public_recips($msg) { */ function allowed_public_recips($msg) { - logger('allowed_public_recips: ' . print_r($msg,true),LOGGER_DATA); + logger('allowed_public_recips: ' . print_r($msg,true),LOGGER_DATA, LOG_DEBUG); if(array_key_exists('public_scope',$msg['message'])) $scope = $msg['message']['public_scope']; @@ -1589,7 +1593,7 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ foreach($deliveries as $d) { $local_public = $public; - $DR = new DReport(z_root(),$sender['hash'],$d['hash'],$arr['mid']); + $DR = new Zotlabs\Zot\DReport(z_root(),$sender['hash'],$d['hash'],$arr['mid']); $r = q("select * from channel where channel_hash = '%s' limit 1", dbesc($d['hash']) @@ -2068,7 +2072,7 @@ function process_mail_delivery($sender, $arr, $deliveries) { foreach($deliveries as $d) { - $DR = new DReport(z_root(),$sender['hash'],$d['hash'],$arr['mid']); + $DR = new Zotlabs\Zot\DReport(z_root(),$sender['hash'],$d['hash'],$arr['mid']); $r = q("select * from channel where channel_hash = '%s' limit 1", dbesc($d['hash']) @@ -2230,6 +2234,62 @@ 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 + * ability to post comments as of 10-Mar-2016). + * 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 + * sync_locations + */ + + + +function check_location_move($sender_hash,$locations) { + + if(! $locations) + return; + + if(! UNO) + return; + + if(count($locations) != 1) + return; + + $loc = $locations[0]; + + $r = q("select * from channel where channel_hash = '%s' limit 1", + dbesc($sender_hash) + ); + + if(! $r) + return; + + if($loc['url'] !== z_root()) { + $x = q("update channel set channel_moved = '%s' where channel_hash = '%s' limit 1", + dbesc($loc['url']), + dbesc($sender_hash) + ); + + // 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); + + } + +} + + +/** * @brief Synchronises locations. * * @param array $sender @@ -2243,6 +2303,10 @@ function sync_locations($sender, $arr, $absolute = false) { if($arr['locations']) { + if($absolute) + check_location_move($sender['hash'],$arr['locations']); + + $xisting = q("select hubloc_id, hubloc_url, hubloc_sitekey from hubloc where hubloc_hash = '%s'", dbesc($sender['hash']) ); @@ -2868,12 +2932,15 @@ function import_site($arr, $pubkey) { */ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { + if(UNO) + return; + $a = get_app(); logger('build_sync_packet'); if($packet) - logger('packet: ' . print_r($packet, true),LOGGER_DATA); + logger('packet: ' . print_r($packet, true),LOGGER_DATA, LOG_DEBUG); if(! $uid) $uid = local_channel(); @@ -2967,7 +3034,9 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { $interval = ((get_config('system','delivery_interval') !== false) ? intval(get_config('system','delivery_interval')) : 2 ); - logger('build_sync_packet: packet: ' . print_r($info,true), LOGGER_DATA); + logger('build_sync_packet: packet: ' . print_r($info,true), LOGGER_DATA, LOG_DEBUG); + + $total = count($synchubs); foreach($synchubs as $hub) { $hash = random_string(); @@ -2982,7 +3051,9 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { )); proc_run('php', 'include/deliver.php', $hash); - if($interval) + $total = $total - 1; + + if($interval && $total) @time_sleep_until(microtime(true) + (float) $interval); } } @@ -2997,6 +3068,9 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { */ function process_channel_sync_delivery($sender, $arr, $deliveries) { + if(UNO) + return; + require_once('include/import.php'); /** @FIXME this will sync red structures (channel, pconfig and abook). Eventually we need to make this application agnostic. */ @@ -3115,6 +3189,11 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { foreach($arr['abook'] as $abook) { + $abconfig = null; + + if(array_key_exists('abconfig',$abook) && is_array($abook['abconfig']) && count($abook['abconfig'])) + $abconfig = $abook['abconfig']; + if(! array_key_exists('abook_blocked',$abook)) { // convert from redmatrix $abook['abook_blocked'] = (($abook['abook_flags'] & 0x0001) ? 1 : 0); @@ -3186,8 +3265,9 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { logger('process_channel_sync_delivery: total_feeds service class limit exceeded'); continue; } - q("insert into abook ( abook_xchan, abook_channel ) values ('%s', %d ) ", + q("insert into abook ( abook_xchan, abook_account, abook_channel ) values ('%s', %d, %d ) ", dbesc($clean['abook_xchan']), + intval($channel['channel_account_id']), intval($channel['channel_id']) ); $total_friends ++; @@ -3204,8 +3284,13 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { } } - - + if($abconfig) { + // @fixme does not handle sync of del_abconfig + foreach($abconfig as $abc) { + if($abc['chan'] === $channel['channel_hash']) + set_abconfig($abc['chan'],$abc['xchan'],$abc['cat'],$abc['k'],$abc['v']); + } + } } } @@ -3403,14 +3488,12 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { // we should probably do this for all items, but usually we only send one. - require_once('include/DReport.php'); - if(array_key_exists('item',$arr) && is_array($arr['item'][0])) { - $DR = new DReport(z_root(),$d['hash'],$d['hash'],$arr['item'][0]['message_id'],'channel sync processed'); + $DR = new Zotlabs\Zot\DReport(z_root(),$d['hash'],$d['hash'],$arr['item'][0]['message_id'],'channel sync processed'); $DR->addto_recipient($channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>'); } else - $DR = new DReport(z_root(),$d['hash'],$d['hash'],'sync packet','channel sync delivered'); + $DR = new Zotlabs\Zot\DReport(z_root(),$d['hash'],$d['hash'],'sync packet','channel sync delivered'); $result[] = $DR->get(); @@ -3828,7 +3911,7 @@ function zotinfo($arr) { $ret['site']['channels'] = channel_total(); - $ret['site']['version'] = PLATFORM_NAME . ' ' . RED_VERSION . '[' . DB_UPDATE_VERSION . ']'; + $ret['site']['version'] = Zotlabs\Project\System::get_platform_name() . ' ' . RED_VERSION . '[' . DB_UPDATE_VERSION . ']'; $ret['site']['admin'] = get_config('system','admin_email'); @@ -3848,7 +3931,7 @@ function zotinfo($arr) { $ret['site']['sellpage'] = get_config('system','sellpage'); $ret['site']['location'] = get_config('system','site_location'); $ret['site']['realm'] = get_directory_realm(); - $ret['site']['project'] = PLATFORM_NAME; + $ret['site']['project'] = Zotlabs\Project\System::get_platform_name(); } @@ -3864,7 +3947,7 @@ function zotinfo($arr) { function check_zotinfo($channel,$locations,&$ret) { -// logger('locations: ' . print_r($locations,true),LOGGER_DATA); +// logger('locations: ' . print_r($locations,true),LOGGER_DATA, LOG_DEBUG); // 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 @@ -3924,6 +4007,9 @@ function check_zotinfo($channel,$locations,&$ret) { function delivery_report_is_storable($dr) { + if(get_config('system','disable_dreport')) + return false; + call_hooks('dreport_is_storable',$dr); // let plugins accept or reject - if neither, continue on @@ -4085,7 +4171,7 @@ function zot_reply_pickup($data) { $sitekey = $hubsite['hubloc_sitekey']; - logger('mod_zot: Checking sitekey: ' . $sitekey, LOGGER_DATA); + logger('mod_zot: Checking sitekey: ' . $sitekey, LOGGER_DATA, LOG_DEBUG); if(rsa_verify($data['callback'],base64url_decode($data['callback_sig']),$sitekey)) { $forgery = false; |