aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/Contact.php13
-rw-r--r--include/RedDAV/RedBrowser.php361
-rw-r--r--include/auth.php127
-rw-r--r--include/bbcode.php12
-rw-r--r--include/cli_startup.php6
-rw-r--r--include/config.php2
-rw-r--r--include/conversation.php6
-rwxr-xr-xinclude/diaspora.php77
-rw-r--r--include/follow.php2
-rw-r--r--include/hubloc.php6
-rw-r--r--include/identity.php41
-rwxr-xr-xinclude/items.php10
-rw-r--r--include/message.php2
-rw-r--r--include/nav.php12
-rw-r--r--include/permissions.php5
-rw-r--r--include/photo/photo_driver.php63
-rwxr-xr-xinclude/plugin.php1
-rw-r--r--include/poller.php7
-rw-r--r--include/reddav.php486
-rw-r--r--include/statistics_fns.php73
-rw-r--r--include/text.php36
-rw-r--r--include/widgets.php2
-rw-r--r--include/zot.php2
23 files changed, 870 insertions, 482 deletions
diff --git a/include/Contact.php b/include/Contact.php
index 8d50b1e5b..4440369dc 100644
--- a/include/Contact.php
+++ b/include/Contact.php
@@ -328,6 +328,19 @@ function mark_orphan_hubsxchans() {
intval(HUBLOC_OFFLINE)
);
+// $realm = get_directory_realm();
+// if($realm == DIRECTORY_REALM) {
+// $r = q("select * from site where site_access != 0 and site_register !=0 and ( site_realm = '%s' or site_realm = '') order by rand()",
+// dbesc($realm)
+// );
+// }
+// else {
+// $r = q("select * from site where site_access != 0 and site_register !=0 and site_realm = '%s' order by rand()",
+// dbesc($realm)
+// );
+// }
+
+
$r = q("select hubloc_id, hubloc_hash from hubloc where (hubloc_status & %d) and not (hubloc_flags & %d)",
intval(HUBLOC_OFFLINE),
intval(HUBLOC_FLAGS_ORPHANCHECK)
diff --git a/include/RedDAV/RedBrowser.php b/include/RedDAV/RedBrowser.php
new file mode 100644
index 000000000..dcd54ef82
--- /dev/null
+++ b/include/RedDAV/RedBrowser.php
@@ -0,0 +1,361 @@
+<?php
+/**
+ * RedMatrix - "The Network"
+ *
+ * @link http://github.com/friendica/red
+ * @license http://opensource.org/licenses/mit-license.php The MIT License (MIT)
+ */
+
+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
+ */
+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.
+ *
+ * Disable assets with $enableAssets = false. Should get some thumbnail views
+ * anyway.
+ *
+ * @param RedBasicAuth &$auth
+ */
+ public function __construct(&$auth) {
+ $this->auth = $auth;
+ parent::__construct(false, 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.
+ *
+ * @todo Maybe 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_user() && $this->auth->owner_id == local_user()) ? true : false);
+
+ if ($this->auth->getTimezone())
+ date_default_timezone_set($this->auth->getTimezone());
+
+ require_once('include/conversation.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 = split("/", $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'] = $this->userReadableSize($size);
+ $ft['lastmodified'] = (($lastmodified) ? datetime_convert('UTC', date_default_timezone_get(), $lastmodified) : '');
+
+ $f[] = $ft;
+ }
+
+ // 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('%1$s used');
+ $quotaDesc = sprintf($quotaDesc,
+ $this->userReadableSize($used));
+ }
+ if ($limit && $used) {
+ $quotaDesc = t('%1$s used of %2$s (%3$s&#37;)');
+ $quotaDesc = sprintf($quotaDesc,
+ $this->userReadableSize($used),
+ $this->userReadableSize($limit),
+ round($used / $limit, 1));
+ }
+
+ // prepare quota for template
+ $quota['used'] = $used;
+ $quota['limit'] = $limit;
+ $quota['desc'] = $quotaDesc;
+
+ $html .= replace_macros(get_markup_template('cloud_directory.tpl'), array(
+ '$header' => t('Files') . ": " . $this->escapeHTML($path) . "/",
+ '$parentpath' => $parentpath,
+ '$entries' => $f,
+ '$quota' => $quota,
+ '$name' => t('Name'),
+ '$type' => t('Type'),
+ '$size' => t('Size'),
+ '$lastmod' => t('Last Modified'),
+ '$parent' => t('parent'),
+ '$edit' => t('Edit'),
+ '$delete' => t('Delete'),
+ '$total' => t('Total')
+ ));
+
+ $output = '';
+ if ($this->enablePost) {
+ $this->server->broadcastEvent('onHTMLActionsPanel', array($parent, &$output));
+ }
+ $html .= $output;
+
+ get_app()->page['content'] = $html;
+ construct_page(get_app());
+ }
+
+ function userReadableSize($size) {
+ $ret = "";
+ if (is_numeric($size)) {
+ $incr = 0;
+ $k = 1024;
+ $unit = array('bytes', 'KB', 'MB', 'GB', 'TB', 'PB');
+ while (($size / $k) >= 1){
+ $incr++;
+ $size = round($size / $k, 2);
+ }
+ $ret = $size . " " . $unit[$incr];
+ }
+ return $ret;
+ }
+
+ /**
+ * @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;
+
+ $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')
+ ));
+ }
+
+ /**
+ * 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;
+ }
+} \ No newline at end of file
diff --git a/include/auth.php b/include/auth.php
index f8188f443..8f68fc562 100644
--- a/include/auth.php
+++ b/include/auth.php
@@ -1,11 +1,23 @@
-<?php /** @file */
-
+<?php
+/**
+ * @file include/auth.php
+ * @brief Functions and inline functionality for authentication.
+ *
+ * This file provides some functions for authentication handling and inline
+ * functionality. Look for auth parameters or re-validate an existing session
+ * also handles logout.
+ * Also provides a function for OpenID identiy matching.
+ */
require_once('include/security.php');
+/**
+ * @brief Resets the current session.
+ *
+ * @return void
+ */
function nuke_session() {
-
- new_cookie(0);
+ new_cookie(0); // 0 means delete on browser exit
unset($_SESSION['authenticated']);
unset($_SESSION['account_id']);
@@ -27,21 +39,24 @@ function nuke_session() {
}
/**
- * Verify login credentials
- *
- * Returns account record on success, null on failure
+ * @brief Verify login credentials.
*
+ * @param string $email
+ * The email address to verify.
+ * @param string $pass
+ * The provided password to verify.
+ * @return array|null
+ * Returns account record on success, null on failure.
*/
+function account_verify_password($email, $pass) {
-function account_verify_password($email,$pass) {
-
- $email_verify = get_config('system','verify_email');
- $register_policy = get_config('system','register_policy');
+ $email_verify = get_config('system', 'verify_email');
+ $register_policy = get_config('system', 'register_policy');
// Currently we only verify email address if there is an open registration policy.
// This isn't because of any policy - it's because the workflow gets too complicated if
// you have to verify the email and then go through the account approval workflow before
- // letting them login.
+ // letting them login.
if(($email_verify) && ($register_policy == REGISTER_OPEN) && ($record['account_flags'] & ACCOUNT_UNVERIFIED))
return null;
@@ -51,9 +66,10 @@ function account_verify_password($email,$pass) {
);
if(! ($r && count($r)))
return null;
+
foreach($r as $record) {
if(($record['account_flags'] == ACCOUNT_OK)
- && (hash('whirlpool',$record['account_salt'] . $pass) === $record['account_password'])) {
+ && (hash('whirlpool', $record['account_salt'] . $pass) === $record['account_password'])) {
logger('password verified for ' . $email);
return $record;
}
@@ -61,7 +77,6 @@ function account_verify_password($email,$pass) {
$error = 'password failed for ' . $email;
logger($error);
-
if($record['account_flags'] & ACCOUNT_UNVERIFIED)
logger('Account is unverified. account_flags = ' . $record['account_flags']);
if($record['account_flags'] & ACCOUNT_BLOCKED)
@@ -88,14 +103,12 @@ function account_verify_password($email,$pass) {
* also handles logout
*/
-
-if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-params'))) || ($_POST['auth-params'] !== 'login'))) {
-
+if((isset($_SESSION)) && (x($_SESSION, 'authenticated')) &&
+ ((! (x($_POST, 'auth-params'))) || ($_POST['auth-params'] !== 'login'))) {
// process a logout request
- if(((x($_POST,'auth-params')) && ($_POST['auth-params'] === 'logout')) || ($a->module === 'logout')) {
-
+ if(((x($_POST, 'auth-params')) && ($_POST['auth-params'] === 'logout')) || ($a->module === 'logout')) {
// process logout request
$args = array('channel_id' => local_user());
call_hooks('logging_out', $args);
@@ -106,16 +119,16 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p
// re-validate a visitor, optionally invoke "su" if permitted to do so
- if(x($_SESSION,'visitor_id') && (! x($_SESSION,'uid'))) {
+ if(x($_SESSION, 'visitor_id') && (! x($_SESSION, 'uid'))) {
// if our authenticated guest is allowed to take control of the admin channel, make it so.
- $admins = get_config('system','remote_admin');
- if($admins && is_array($admins) && in_array($_SESSION['visitor_id'],$admins)) {
+ $admins = get_config('system', 'remote_admin');
+ if($admins && is_array($admins) && in_array($_SESSION['visitor_id'], $admins)) {
$x = q("select * from account where account_email = '%s' and account_email != '' and ( account_flags & %d ) limit 1",
- dbesc(get_config('system','admin_email')),
+ dbesc(get_config('system', 'admin_email')),
intval(ACCOUNT_ROLE_ADMIN)
);
if($x) {
- new_cookie(60*60*24); // one day
+ new_cookie(60 * 60 * 24); // one day
$_SESSION['last_login_date'] = datetime_convert();
unset($_SESSION['visitor_id']); // no longer a visitor
authenticate_success($x[0], true, true);
@@ -137,20 +150,19 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p
// already logged in user returning
- if(x($_SESSION,'uid') || x($_SESSION,'account_id')) {
+ if(x($_SESSION, 'uid') || x($_SESSION, 'account_id')) {
// first check if we're enforcing that sessions can't change IP address
-
+ // @todo what to do with IPv6 addresses
if($_SESSION['addr'] && $_SESSION['addr'] != $_SERVER['REMOTE_ADDR']) {
logger('SECURITY: Session IP address changed: ' . $_SESSION['addr'] . ' != ' . $_SERVER['REMOTE_ADDR']);
- $partial1 = substr($_SESSION['addr'],0,strrpos($_SESSION['addr'],'.'));
- $partial2 = substr($_SERVER['REMOTE_ADDR'],0,strrpos($_SERVER['REMOTE_ADDR'],'.'));
+ $partial1 = substr($_SESSION['addr'], 0, strrpos($_SESSION['addr'], '.'));
+ $partial2 = substr($_SERVER['REMOTE_ADDR'], 0, strrpos($_SERVER['REMOTE_ADDR'], '.'));
-
- $paranoia = intval(get_pconfig($_SESSION['uid'],'system','paranoia'));
+ $paranoia = intval(get_pconfig($_SESSION['uid'], 'system', 'paranoia'));
if(! $paranoia)
- $paranoia = intval(get_config('system','paranoia'));
+ $paranoia = intval(get_config('system', 'paranoia'));
switch($paranoia) {
case 0:
@@ -158,8 +170,8 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p
break;
case 2:
// check 2 octets
- $partial1 = substr($partial1,0,strrpos($partial1,'.'));
- $partial2 = substr($partial2,0,strrpos($partial2,'.'));
+ $partial1 = substr($partial1, 0, strrpos($partial1, '.'));
+ $partial2 = substr($partial2, 0, strrpos($partial2, '.'));
if($partial1 == $partial2)
break;
case 1:
@@ -169,12 +181,11 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p
case 3:
default:
// check any difference at all
- logger('Session address changed. Paranoid setting in effect, blocking session. '
+ logger('Session address changed. Paranoid setting in effect, blocking session. '
. $_SESSION['addr'] . ' != ' . $_SERVER['REMOTE_ADDR']);
nuke_session();
goaway(z_root());
break;
-
}
}
@@ -191,17 +202,15 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p
if(strcmp(datetime_convert('UTC','UTC','now - 12 hours'), $_SESSION['last_login_date']) > 0 ) {
$_SESSION['last_login_date'] = datetime_convert();
$login_refresh = true;
- }
- authenticate_success($r[0], false, false, false, $login_refresh);
+ }
+ authenticate_success($r[0], false, false, false, $login_refresh);
}
else {
$_SESSION['account_id'] = 0;
nuke_session();
goaway(z_root());
}
-
- }
-
+ } // end logged in user returning
}
else {
@@ -211,10 +220,10 @@ else {
// handle a fresh login request
- if((x($_POST,'password')) && strlen($_POST['password']))
- $encrypted = hash('whirlpool',trim($_POST['password']));
+ if((x($_POST, 'password')) && strlen($_POST['password']))
+ $encrypted = hash('whirlpool', trim($_POST['password']));
- if((x($_POST,'auth-params')) && $_POST['auth-params'] === 'login') {
+ if((x($_POST, 'auth-params')) && $_POST['auth-params'] === 'login') {
$record = null;
@@ -239,8 +248,7 @@ else {
$record = $addon_auth['user_record'];
}
else {
-
- $record = get_app()->account = account_verify_password($_POST['username'],$_POST['password']);
+ $record = get_app()->account = account_verify_password($_POST['username'], $_POST['password']);
if(get_app()->account) {
$_SESSION['account_id'] = get_app()->account['account_id'];
@@ -249,21 +257,20 @@ else {
notice( t('Failed authentication') . EOL);
}
- logger('authenticate: ' . print_r(get_app()->account,true), LOGGER_DEBUG);
-
+ logger('authenticate: ' . print_r(get_app()->account, true), LOGGER_DEBUG);
}
if((! $record) || (! count($record))) {
$error = 'authenticate: failed login attempt: ' . notags(trim($_POST['username'])) . ' from IP ' . $_SERVER['REMOTE_ADDR'];
logger($error);
// Also log failed logins to a separate auth log to reduce overhead for server side intrusion prevention
- $authlog = get_config('system', 'authlog');
- if ($authlog)
- @file_put_contents($authlog, datetime_convert() . ':' . session_id() . ' ' . $error . "\n", FILE_APPEND);
+ $authlog = get_config('system', 'authlog');
+ if ($authlog)
+ @file_put_contents($authlog, datetime_convert() . ':' . session_id() . ' ' . $error . "\n", FILE_APPEND);
notice( t('Login failed.') . EOL );
goaway(z_root());
- }
+ }
// If the user specified to remember the authentication, then change the cookie
// to expire after one year (the default is when the browser is closed).
@@ -293,11 +300,25 @@ else {
}
+/**
+ * @brief Returns the channel_id for a given openid_identity.
+ *
+ * Queries the values from pconfig configuration for the given openid_identity
+ * and returns the corresponding channel_id.
+ *
+ * @fixme How do we prevent that an OpenID identity is used more than once?
+ *
+ * @param string $authid
+ * The given openid_identity
+ * @return int|bool
+ * Return channel_id from pconfig or false.
+ */
function match_openid($authid) {
- $r = q("select * from pconfig where cat = 'system' and k = 'openid' and v = '%s' limit 1",
+ // Query the uid/channel_id from pconfig for a given value.
+ $r = q("SELECT uid FROM pconfig WHERE cat = 'system' AND k = 'openid' AND v = '%s' LIMIT 1",
dbesc($authid)
);
if($r)
return $r[0]['uid'];
return false;
-}
+}
diff --git a/include/bbcode.php b/include/bbcode.php
index 0803ed365..d7a5ac457 100644
--- a/include/bbcode.php
+++ b/include/bbcode.php
@@ -159,6 +159,14 @@ function bb_parse_app($match) {
}
+function bb_parse_element($match) {
+ $j = json_decode(base64url_decode($match[1]),true);
+ if($j) {
+ $o = EOL . '<a href="' . z_root() . '" foo="baz" onclick="importElement(\'' . $match[1] . '\'); return false;" >' . t('Install design element: ') . $j['pagetitle'] . '</a>' . EOL;
+ }
+ return $o;
+}
+
function bb_qr($match) {
return '<img class="zrl" src="' . z_root() . '/photo/qr?f=&qr=' . urlencode($match[1]) . '" alt="' . t('QR code') . '" title="' . htmlspecialchars($match[1],ENT_QUOTES,'UTF-8') . '" />';
}
@@ -700,6 +708,10 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true) {
$Text = preg_replace_callback("/\[app\](.*?)\[\/app\]/ism",'bb_parse_app', $Text);
}
+ if(strpos($Text,'[/element]') !== false) {
+ $Text = preg_replace_callback("/\[element\](.*?)\[\/element\]/ism",'bb_parse_element', $Text);
+ }
+
// html5 video and audio
if (strpos($Text,'[/video]') !== false) {
diff --git a/include/cli_startup.php b/include/cli_startup.php
index 6bd4e7520..f90a75cd1 100644
--- a/include/cli_startup.php
+++ b/include/cli_startup.php
@@ -6,7 +6,7 @@ require_once('boot.php');
function cli_startup() {
- global $a, $db;
+ global $a, $db, $default_timezone;
if(is_null($a)) {
$a = new App;
@@ -14,6 +14,10 @@ function cli_startup() {
if(is_null($db)) {
@include(".htconfig.php");
+
+ $a->timezone = ((x($default_timezone)) ? $default_timezone : 'UTC');
+ date_default_timezone_set($a->timezone);
+
require_once('include/dba/dba_driver.php');
$db = dba_factory($db_host, $db_port, $db_user, $db_pass, $db_data);
unset($db_host, $db_port, $db_user, $db_pass, $db_data);
diff --git a/include/config.php b/include/config.php
index a77801717..3292059d1 100644
--- a/include/config.php
+++ b/include/config.php
@@ -248,7 +248,7 @@ function load_pconfig($uid) {
* @return mixed Stored value or false if it does not exist
*/
function get_pconfig($uid, $family, $key, $instore = false) {
- logger('include/config.php: get_pconfig() deprecated instore param used', LOGGER_DEBUG);
+// logger('include/config.php: get_pconfig() deprecated instore param used', LOGGER_DEBUG);
global $a;
if($uid === false)
diff --git a/include/conversation.php b/include/conversation.php
index b0a388a68..92ba18d13 100644
--- a/include/conversation.php
+++ b/include/conversation.php
@@ -1341,8 +1341,10 @@ function prepare_page($item) {
// the template will get passed an unobscured title.
$body = prepare_body($item,true);
-
- return replace_macros(get_markup_template('page_display.tpl'),array(
+ $tpl = get_pconfig($item['uid'],'system','pagetemplate');
+ if (! $tpl)
+ $tpl = 'page_display.tpl';
+ return replace_macros(get_markup_template($tpl),array(
'$author' => (($naked) ? '' : $item['author']['xchan_name']),
'$auth_url' => (($naked) ? '' : zid($item['author']['xchan_url'])),
'$date' => (($naked) ? '' : datetime_convert('UTC',date_default_timezone_get(),$item['created'],'Y-m-d H:i')),
diff --git a/include/diaspora.php b/include/diaspora.php
index f9fd3b4ee..f0687e51e 100755
--- a/include/diaspora.php
+++ b/include/diaspora.php
@@ -206,10 +206,7 @@ function diaspora_process_outbound($arr) {
if(! $contact['xchan_pubkey'])
continue;
- if(activity_match($target_item['verb'],ACTIVITY_DISLIKE)) {
- continue;
- }
- elseif(($target_item['item_restrict'] & ITEM_DELETED)
+ if(($target_item['item_restrict'] & ITEM_DELETED)
&& (($target_item['mid'] === $target_item['parent_mid']) || $arr['followup'])) {
// send both top-level retractions and relayable retractions for owner to relay
diaspora_send_retraction($target_item,$arr['channel'],$contact);
@@ -238,11 +235,7 @@ function diaspora_process_outbound($arr) {
$contact = $arr['hub'];
- if($target_item['verb'] === ACTIVITY_DISLIKE) {
- // unsupported
- return;
- }
- elseif(($target_item['deleted'])
+ if(($target_item['deleted'])
&& ($target_item['mid'] === $target_item['parent_mod'])) {
// top-level retraction
logger('delivery: diaspora retract: ' . $loc);
@@ -256,7 +249,6 @@ function diaspora_process_outbound($arr) {
return;
}
elseif($arr['top_level_post']) {
- // currently no workable solution for sending walltowall
logger('delivery: diaspora status: ' . $loc);
diaspora_send_status($target_item,$arr['channel'],$contact,true);
return;
@@ -910,6 +902,44 @@ function diaspora_post($importer,$xml,$msg) {
}
+
+function get_diaspora_reshare_xml($url,$recurse = 0) {
+
+ $x = z_fetch_url($url);
+ if(! $x['success'])
+ $x = z_fetch_url(str_replace('https://','http://',$url));
+ if(! $x['success']) {
+ logger('get_diaspora_reshare_xml: unable to fetch source url ' . $url);
+ return;
+ }
+ logger('get_diaspora_reshare_xml: source: ' . $x['body'], LOGGER_DEBUG);
+
+ $source_xml = parse_xml_string($x['body'],false);
+
+ if(! $source_xml) {
+ logger('get_diaspora_reshare_xml: unparseable result from ' . $url);
+ return '';
+ }
+
+ if($source_xml->post->status_message) {
+ return $source_xml;
+ }
+
+ // see if it's a reshare of a reshare
+
+ if($source_xml->root_diaspora_id && $source_xml->root_guid && $recurse < 15) {
+ $orig_author = notags(unxmlify($source_xml->root_diaspora_id));
+ $orig_guid = notags(unxmlify($source_xml->root_guid));
+ $source_url = 'https://' . substr($orig_author,strpos($orig_author,'@')+1) . '/p/' . $orig_guid . '.xml';
+ $y = get_diaspora_reshare_xml($source_url,$recurse+1);
+ if($y)
+ return $y;
+ }
+ return false;
+}
+
+
+
function diaspora_reshare($importer,$xml,$msg) {
logger('diaspora_reshare: init: ' . print_r($xml,true), LOGGER_DATA);
@@ -948,20 +978,16 @@ function diaspora_reshare($importer,$xml,$msg) {
$source_url = 'https://' . substr($orig_author,strpos($orig_author,'@')+1) . '/p/' . $orig_guid . '.xml';
$orig_url = 'https://'.substr($orig_author,strpos($orig_author,'@')+1).'/posts/'.$orig_guid;
- $x = z_fetch_url($source_url);
- if(! $x['success'])
- $x = z_fetch_url(str_replace('https://','http://',$source_url));
- if(! $x['success']) {
- logger('diaspora_reshare: unable to fetch source url ' . $source_url);
- return;
- }
- logger('diaspora_reshare: source: ' . $x['body'], LOGGER_DATA);
- $source_xml = parse_xml_string($x['body'],false);
+ $source_xml = get_diaspora_reshare_xml($source_url);
if($source_xml->post->status_message) {
$body = diaspora2bb($source_xml->post->status_message->raw_message);
+ $orig_author = notags(unxmlify($source_xml->post->status_message->diaspora_handle));
+ $orig_guid = notags(unxmlify($source_xml->post->status_message->guid));
+
+
// Checking for embedded pictures
if($source_xml->post->status_message->photo->remote_photo_path &&
$source_xml->post->status_message->photo->remote_photo_name) {
@@ -1003,9 +1029,9 @@ function diaspora_reshare($importer,$xml,$msg) {
. "' profile='" . $orig_author_link
. "' avatar='" . $orig_author_photo
. "' link='" . $orig_url
- . "' posted='" . datetime_convert('UTC','UTC',unxmlify($sourcexml->post->status_message->created_at))
+ . "' posted='" . datetime_convert('UTC','UTC',unxmlify($source_xml->post->status_message->created_at))
. "' message_id='" . unxmlify($source_xml->post->status_message->guid)
- . "]" . $body . "[/share]";
+ . "']" . $body . "[/share]";
$created = unxmlify($xml->created_at);
@@ -2401,7 +2427,7 @@ function diaspora_send_followup($item,$owner,$contact,$public_batch = false) {
// Diaspora doesn't support threaded comments, but some
// versions of Diaspora (i.e. Diaspora-pistos) support
// likes on comments
- if($item['verb'] === ACTIVITY_LIKE && $item['thr_parent']) {
+ if(($item['verb'] === ACTIVITY_LIKE || $item['verb'] === ACTIVITY_DISLIKE) && $item['thr_parent']) {
$p = q("select mid, parent_mid from item where mid = '%s' limit 1",
dbesc($item['thr_parent'])
);
@@ -2420,10 +2446,10 @@ function diaspora_send_followup($item,$owner,$contact,$public_batch = false) {
else
return;
- if($item['verb'] === ACTIVITY_LIKE) {
+ if(($item['verb'] === ACTIVITY_LIKE) && ($parent['mid'] === $parent['parent_mid'])) {
$tpl = get_markup_template('diaspora_like.tpl');
$like = true;
- $target_type = ( $parent['mid'] === $parent['parent_mid'] ? 'Post' : 'Comment');
+ $target_type = 'Post';
$positive = 'true';
if(($item_['item_restrict'] & ITEM_DELETED))
@@ -2604,6 +2630,9 @@ function diaspora_send_relay($item,$owner,$contact,$public_batch = false) {
$parentauthorsig = base64_encode(rsa_sign($sender_signed_text,$owner['channel_prvkey'],'sha256'));
+ if(! $text)
+ logger('diaspora_send_relay: no text');
+
$msg = replace_macros($tpl,array(
'$guid' => xmlify($item['mid']),
'$parent_guid' => xmlify($parent['mid']),
diff --git a/include/follow.php b/include/follow.php
index a967386e9..c8bd3c500 100644
--- a/include/follow.php
+++ b/include/follow.php
@@ -84,7 +84,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
// Premium channel, set confirm before callback to avoid recursion
- if(array_key_exists('connect_url',$j) && (! $confirm))
+ if(array_key_exists('connect_url',$j) && ($interactive) && (! $confirm))
goaway(zid($j['connect_url']));
diff --git a/include/hubloc.php b/include/hubloc.php
index cdc9de4af..fded434d2 100644
--- a/include/hubloc.php
+++ b/include/hubloc.php
@@ -105,9 +105,9 @@ function remove_obsolete_hublocs() {
dbesc($rr['hubloc_hash'])
);
if($x) {
-// proc_run('php','include/notifier.php','location',$x[0]['channel_id']);
-// if($interval)
-// @time_sleep_until(microtime(true) + (float) $interval);
+ proc_run('php','include/notifier.php','location',$x[0]['channel_id']);
+ if($interval)
+ @time_sleep_until(microtime(true) + (float) $interval);
}
}
}
diff --git a/include/identity.php b/include/identity.php
index bfdd02682..fafb97bbb 100644
--- a/include/identity.php
+++ b/include/identity.php
@@ -27,10 +27,13 @@ function identity_check_service_class($account_id) {
intval(PAGE_REMOVED)
);
if(! ($r && count($r))) {
+ $ret['total_identities'] = 0;
$ret['message'] = t('Unable to obtain identity information from database');
return $ret;
}
+ $ret['total_identities'] = intval($r[0]['total']);
+
if(! service_class_allows($account_id,'total_identities',$r[0]['total'])) {
$result['message'] .= upgrade_message();
return $result;
@@ -166,10 +169,12 @@ function create_identity($arr) {
$ret['message'] = t('No account identifier');
return $ret;
}
- $ret=identity_check_service_class($arr['account_id']);
+ $ret = identity_check_service_class($arr['account_id']);
if (!$ret['success']) {
return $ret;
}
+ // save this for auto_friending
+ $total_identities = $ret['total_identities'];
$nick = mb_strtolower(trim($arr['nickname']));
if(! $nick) {
@@ -249,8 +254,8 @@ function create_identity($arr) {
$r = q("insert into channel ( channel_account_id, channel_primary,
channel_name, channel_address, channel_guid, channel_guid_sig,
- channel_hash, channel_prvkey, channel_pubkey, channel_pageflags, channel_expire_days $perms_keys )
- values ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d $perms_vals ) ",
+ channel_hash, channel_prvkey, channel_pubkey, channel_pageflags, channel_expire_days, channel_timezone $perms_keys )
+ values ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s' $perms_vals ) ",
intval($arr['account_id']),
intval($primary),
@@ -262,7 +267,8 @@ function create_identity($arr) {
dbesc($key['prvkey']),
dbesc($key['pubkey']),
intval($pageflags),
- intval($expire)
+ intval($expire),
+ dbesc($a->timezone)
);
@@ -389,6 +395,20 @@ function create_identity($arr) {
}
}
+ // 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
+ // or it will impact registration time.
+
+ $accts = get_config('system','auto_follow');
+ if(($accts) && (! $total_identities)) {
+ if(! is_array($accts))
+ $accts = array($accts);
+ foreach($accts as $acct) {
+ if(trim($acct))
+ new_contact($newuid,trim($acct),$ret['channel'],false);
+ }
+ }
call_hooks('register_account', $newuid);
@@ -518,6 +538,17 @@ function identity_basic_export($channel_id, $items = false) {
$ret['photo'] = array('type' => $r[0]['type'], 'data' => base64url_encode($r[0]['data']));
}
+
+ // All other term types will be included in items, if requested.
+
+ $r = q("select * from term where type in (%d,%d) and uid = %d",
+ intval(TERM_SAVEDSEARCH),
+ intval(TERM_THING),
+ intval($channel_id)
+ );
+ if($r)
+ $ret['term'] = $r;
+
$r = q("select * from obj where obj_channel = %d",
intval($channel_id)
);
@@ -1338,7 +1369,7 @@ function get_default_profile_photo($size = 175) {
$scheme = get_config('system','default_profile_photo');
if(! $scheme)
$scheme = 'rainbow_man';
- return 'images/default_profile_photos/' . $scheme . '/' . $size . '.jpg';
+ return 'images/default_profile_photos/' . $scheme . '/' . $size . '.png';
}
diff --git a/include/items.php b/include/items.php
index 31364de60..c9810bb7c 100755
--- a/include/items.php
+++ b/include/items.php
@@ -759,6 +759,8 @@ function get_item_elements($x) {
$arr = array();
$arr['body'] = (($x['body']) ? htmlspecialchars($x['body'],ENT_COMPAT,'UTF-8',false) : '');
+ $key = get_config('system','pubkey');
+
$maxlen = get_max_import_size();
if($maxlen && mb_strlen($arr['body']) > $maxlen) {
@@ -813,7 +815,7 @@ function get_item_elements($x) {
$arr['sig'] = (($x['signature']) ? htmlspecialchars($x['signature'], ENT_COMPAT,'UTF-8',false) : '');
- $arr['diaspora_meta'] = (($x['diaspora_meta']) ? $x['diaspora_meta'] : '');
+ $arr['diaspora_meta'] = (($x['diaspora_signature']) ? json_encode(crypto_encapsulate($x['diaspora_signature'],$key)) : '');
$arr['object'] = activity_sanitise($x['object']);
$arr['target'] = activity_sanitise($x['target']);
@@ -865,7 +867,6 @@ function get_item_elements($x) {
// We have to do that here because we need to cleanse the input and prevent bad stuff from getting in,
// and we need plaintext to do that.
- $key = get_config('system','pubkey');
if(intval($arr['item_private'])) {
@@ -891,7 +892,6 @@ function get_item_elements($x) {
$arr['resource_type'] = $x['resource_type'];
$arr['item_restrict'] = $x['item_restrict'];
$arr['item_flags'] = $x['item_flags'];
- $arr['diaspora_meta'] = (($x['diaspora_meta']) ? json_encode(crypto_encapsulate($x['diaspora_meta'],$key)) : '');
$arr['attach'] = $x['attach'];
}
@@ -1078,7 +1078,6 @@ function encode_item($item,$mirror = false) {
$x['resource_type'] = $item['resource_type'];
$x['item_restrict'] = $item['item_restrict'];
$x['item_flags'] = $item['item_flags'];
- $x['diaspora_meta'] = crypto_unencapsulate(json_decode($item['diaspora_meta'],true),$key);
$x['attach'] = $item['attach'];
}
@@ -1127,6 +1126,9 @@ function encode_item($item,$mirror = false) {
if($item['term'])
$x['tags'] = encode_item_terms($item['term']);
+ if($item['diaspora_meta'])
+ $x['diaspora_signature'] = crypto_unencapsulate(json_decode($item['diaspora_meta'],true),$key);
+
logger('encode_item: ' . print_r($x,true), LOGGER_DATA);
return $x;
diff --git a/include/message.php b/include/message.php
index 88cfb7ba2..b063530d6 100644
--- a/include/message.php
+++ b/include/message.php
@@ -50,7 +50,7 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto='
// look for any existing conversation structure
if(strlen($replyto)) {
- $r = q("select convid from mail where uid = %d and ( mid = '%s' or parent_mid = '%s' ) limit 1",
+ $r = q("select convid from mail where channel_id = %d and ( mid = '%s' or parent_mid = '%s' ) limit 1",
intval(local_user()),
dbesc($replyto),
dbesc($replyto)
diff --git a/include/nav.php b/include/nav.php
index 98d1b644e..7c9abce49 100644
--- a/include/nav.php
+++ b/include/nav.php
@@ -185,7 +185,10 @@ EOT;
if(local_user()) {
- $nav['network'] = array('network', t('Matrix'), "", t('Your matrix'));
+ $network_options = get_pconfig(local_user(),'system','network_page_default');
+
+ $nav['network'] = array('network' . (($network_options) ? '?f=&' . $network_options : ''),
+ t('Matrix'), "", t('Your matrix'));
$nav['network']['mark'] = array('', t('Mark all matrix notifications seen'), '','');
$nav['home'] = array('channel/' . $channel['channel_address'], t('Channel Home'), "", t('Channel home'));
@@ -239,6 +242,12 @@ EOT;
$x = array('nav' => $nav, 'usermenu' => $userinfo );
call_hooks('nav', $x);
+// Not sure the best place to put this on the page. So I'm implementing it but leaving it
+// turned off until somebody discovers this and figures out a good location for it.
+$powered_by = '';
+
+// $powered_by = '<strong>red<img class="smiley" src="' . $a->get_baseurl() . '/images/rm-16.png" alt="r#" />matrix</strong>';
+
$tpl = get_markup_template('nav.tpl');
$a->page['nav'] .= replace_macros($tpl, array(
@@ -250,6 +259,7 @@ EOT;
'$userinfo' => $x['usermenu'],
'$localuser' => local_user(),
'$sel' => $a->nav_sel,
+ '$powered_by' => $powered_by,
'$pleasewait' => t('Please wait...')
));
diff --git a/include/permissions.php b/include/permissions.php
index 438b807d0..61ac8aea3 100644
--- a/include/permissions.php
+++ b/include/permissions.php
@@ -722,6 +722,11 @@ function get_role_perms($role) {
}
+ $x = get_config('system','role_perms');
+ // let system settings over-ride any or all
+ if($x && is_array($x) && array_key_exists($role,$x))
+ $ret = array_merge($ret,$x[$role]);
+
call_hooks('get_role_perms',$ret);
return $ret;
diff --git a/include/photo/photo_driver.php b/include/photo/photo_driver.php
index daf1bfc25..508d82957 100644
--- a/include/photo/photo_driver.php
+++ b/include/photo/photo_driver.php
@@ -3,11 +3,24 @@
function photo_factory($data, $type = null) {
$ph = null;
- if(class_exists('Imagick')) {
- require_once('include/photo/photo_imagick.php');
- $ph = new photo_imagick($data,$type);
+ $ignore_imagick = get_config('system', 'ignore_imagick');
+
+ if(class_exists('Imagick') && !$ignore_imagick) {
+ $v = Imagick::getVersion();
+ preg_match('/ImageMagick ([0-9]+\.[0-9]+\.[0-9]+)/', $v['versionString'], $m);
+ if(version_compare($m[1],'6.6.7') >= 0) {
+ require_once('include/photo/photo_imagick.php');
+ $ph = new photo_imagick($data,$type);
+ }
+ else {
+ // earlier imagick versions have issues with scaling png's
+ // don't log this because it will just fill the logfile.
+ // leave this note here so those who are looking for why
+ // we aren't using imagick can find it
+ }
}
- else {
+
+ if(! $ph) {
require_once('include/photo/photo_gd.php');
$ph = new photo_gd($data,$type);
}
@@ -480,11 +493,11 @@ abstract class photo_driver {
* Guess image mimetype from filename or from Content-Type header
*
* @arg $filename string Image filename
- * @arg $fromcurl boolean Check Content-Type header from curl request
+ * @arg $headers string Headers to check for Content-Type (from curl request)
*/
function guess_image_type($filename, $headers = '') {
- logger('Photo: guess_image_type: '.$filename . ($fromcurl?' from curl headers':''), LOGGER_DEBUG);
+ logger('Photo: guess_image_type: '.$filename . ($headers?' from curl headers':''), LOGGER_DEBUG);
$type = null;
if ($headers) {
$a = get_app();
@@ -494,21 +507,35 @@ function guess_image_type($filename, $headers = '') {
list($k,$v) = array_map("trim", explode(":", trim($l), 2));
$hdrs[$k] = $v;
}
+ logger('Curl headers: '.var_export($hdrs, true), LOGGER_DEBUG);
if (array_key_exists('Content-Type', $hdrs))
$type = $hdrs['Content-Type'];
}
if (is_null($type)){
-// FIXME!!!!
+
+ $ignore_imagick = get_config('system', 'ignore_imagick');
// Guessing from extension? Isn't that... dangerous?
- if(class_exists('Imagick') && file_exists($filename) && is_readable($filename)) {
- /**
- * Well, this not much better,
- * but at least it comes from the data inside the image,
- * we won't be tricked by a manipulated extension
- */
- $image = new Imagick($filename);
- $type = $image->getImageMimeType();
- } else {
+ if(class_exists('Imagick') && file_exists($filename) && is_readable($filename) && !$ignore_imagick) {
+ $v = Imagick::getVersion();
+ preg_match('/ImageMagick ([0-9]+\.[0-9]+\.[0-9]+)/', $v['versionString'], $m);
+ if(version_compare($m[1],'6.6.7') >= 0) {
+ /**
+ * Well, this not much better,
+ * but at least it comes from the data inside the image,
+ * we won't be tricked by a manipulated extension
+ */
+ $image = new Imagick($filename);
+ $type = $image->getImageMimeType();
+ }
+ else {
+ // earlier imagick versions have issues with scaling png's
+ // don't log this because it will just fill the logfile.
+ // leave this note here so those who are looking for why
+ // we aren't using imagick can find it
+ }
+ }
+
+ if(is_null($type)) {
$ext = pathinfo($filename, PATHINFO_EXTENSION);
$ph = photo_factory('');
$types = $ph->supportedTypes();
@@ -552,7 +579,7 @@ function import_profile_photo($photo,$xchan,$thing = false) {
if($photo) {
$filename = basename($photo);
- $type = guess_image_type($photo,true);
+ $type = guess_image_type($photo);
if(! $type)
$type = 'image/jpeg';
@@ -623,7 +650,7 @@ function import_profile_photo($photo,$xchan,$thing = false) {
$photo = $a->get_baseurl() . '/' . get_default_profile_photo();
$thumb = $a->get_baseurl() . '/' . get_default_profile_photo(80);
$micro = $a->get_baseurl() . '/' . get_default_profile_photo(48);
- $type = 'image/jpeg';
+ $type = 'image/png';
}
return(array($photo,$thumb,$micro,$type,$photo_failure));
diff --git a/include/plugin.php b/include/plugin.php
index c2e08a989..4f9ab71da 100755
--- a/include/plugin.php
+++ b/include/plugin.php
@@ -552,6 +552,7 @@ function theme_include($file, $root = '') {
$paths = array(
"{$root}view/theme/$theme/$ext/$file",
"{$root}view/theme/$parent/$ext/$file",
+ "{$root}view/site/$ext/$file",
"{$root}view/$ext/$file",
);
diff --git a/include/poller.php b/include/poller.php
index f689059b9..2febaeb32 100644
--- a/include/poller.php
+++ b/include/poller.php
@@ -149,6 +149,13 @@ function poller_run($argv, $argc){
update_birthdays();
+ //update statistics in config
+ require_once('include/statistics_fns.php');
+ update_channels_total_stat();
+ update_channels_active_halfyear_stat();
+ update_channels_active_monthly_stat();
+ update_local_posts_stat();
+
// expire any read notifications over a month old
q("delete from notify where seen = 1 and date < UTC_TIMESTAMP() - INTERVAL 30 DAY");
diff --git a/include/reddav.php b/include/reddav.php
index c4ef5bd08..5c93daf1f 100644
--- a/include/reddav.php
+++ b/include/reddav.php
@@ -9,22 +9,30 @@
* You find the original SabreDAV classes under @ref vendor/sabre/dav/.
* We need to use SabreDAV 1.8.x for PHP5.3 compatibility. SabreDAV >= 2.0
* requires PHP >= 5.4.
+ *
+ * @todo split up the classes into own files.
*/
use Sabre\DAV;
+
require_once('vendor/autoload.php');
require_once('include/attach.php');
-
/**
* @brief RedDirectory class.
*
* A class that represents a directory.
+ *
+ * @extends \Sabre\DAV\Node
+ * @implements \Sabre\DAV\ICollection
+ * @implements \Sabre\DAV\IQuota
*/
class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
/**
* @brief The path inside /cloud
+ *
+ * @var string
*/
private $red_path;
private $folder_hash;
@@ -32,6 +40,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
* @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 = '';
@@ -39,6 +48,8 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
/**
* @brief The real path on the filesystem.
* The actual path in store/ with the hashed names.
+ *
+ * @var string
*/
private $os_path = '';
@@ -107,7 +118,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
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.');
}
@@ -971,78 +982,111 @@ function RedFileData($file, &$auth, $test = false) {
/**
- * RedBasicAuth class.
+ * @brief Authentication backend class for RedDAV.
+ *
+ * This class also contains some data which is not necessary for authentication
+ * like timezone settings.
*
*/
class RedBasicAuth extends DAV\Auth\Backend\AbstractBasic {
- // @fixme mod/cloud.php:61
- public $channel_name = '';
- // @fixme mod/cloud.php:62
+ /**
+ * @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;
- // @fixme mod/cloud.php:63
+ /**
+ * channel_hash of the current channel of the logged-in account.
+ *
+ * @var string
+ */
public $channel_hash = '';
- // @fixme mod/cloud.php:68
+ /**
+ * Set in mod/cloud.php to observer_hash.
+ *
+ * @var string
+ */
public $observer = '';
- // @fixme include/reddav.php:51
+ /**
+ *
+ * @see RedBrowser::set_writeable()
+ * @var DAV\Browser\Plugin
+ */
public $browser;
- // @fixme include/reddav.php:92
- public $owner_id;
- // @fixme include/reddav.php:283
+ /**
+ * 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 = '';
- // @fixme mod/cloud.php:66
- public $timezone;
+ /**
+ * 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 DAV\Auth\Backend\AbstractBasic::validateUserPass
* @param string $username
* @param string $password
+ * @return bool
*/
protected function validateUserPass($username, $password) {
-
if (trim($password) === '+++') {
- logger('reddav: validateUserPass: guest ' . $username);
+ logger('(DAV): RedBasicAuth::validateUserPass(): 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",
+ $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) {
- $this->currentUser = $r[0]['channel_address'];
- $this->channel_name = $r[0]['channel_address'];
- $this->channel_id = $r[0]['channel_id'];
- $this->channel_hash = $this->observer = $r[0]['channel_hash'];
- $_SESSION['uid'] = $r[0]['channel_id'];
- $_SESSION['account_id'] = $r[0]['channel_account_id'];
- $_SESSION['authenticated'] = true;
- return true;
+ return $this->setAuthenticated($r[0]);
}
}
- $r = q("select * from channel where channel_address = '%s' limit 1",
+ $r = q("SELECT * FROM channel WHERE channel_address = '%s' LIMIT 1",
dbesc($username)
);
if ($r) {
- $x = q("select * from account where account_id = %d limit 1",
+ $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('(DAV) RedBasicAuth: password verified for ' . $username);
- $this->currentUser = $r[0]['channel_address'];
- $this->channel_name = $r[0]['channel_address'];
- $this->channel_id = $r[0]['channel_id'];
- $this->channel_hash = $this->observer = $r[0]['channel_hash'];
- $_SESSION['uid'] = $r[0]['channel_id'];
- $_SESSION['account_id'] = $r[0]['channel_account_id'];
- $_SESSION['authenticated'] = true;
- return true;
+ return $this->setAuthenticated($r[0]);
}
}
}
@@ -1051,340 +1095,88 @@ class RedBasicAuth extends DAV\Auth\Backend\AbstractBasic {
return false;
}
- public function setCurrentUser($name) {
- $this->currentUser = $name;
+ /**
+ * @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;
}
/**
- * @brief Set browser plugin.
+ * Sets the channel_name from the currently logged-in channel.
*
- * @see RedBrowser::set_writeable()
- * @param DAV\Browser\Plugin $browser
+ * @param string $name
+ * The channel's name
*/
- public function setBrowserPlugin($browser) {
- $this->browser = $browser;
- }
-
- // internal? logging function
- function log() {
- logger('dav: auth: channel_name ' . $this->channel_name, LOGGER_DATA);
- logger('dav: auth: channel_id ' . $this->channel_id, LOGGER_DATA);
- logger('dav: auth: channel_hash ' . $this->channel_hash, LOGGER_DATA);
- logger('dav: auth: observer ' . $this->observer, LOGGER_DATA);
- logger('dav: auth: owner_id ' . $this->owner_id, LOGGER_DATA);
- logger('dav: auth: owner_nick ' . $this->owner_nick, LOGGER_DATA);
- }
-
-} // class RedBasicAuth
-
-
-
-/**
- * RedBrowser class.
- *
- */
-class RedBrowser extends DAV\Browser\Plugin {
-
- private $auth;
-
- function __construct(&$auth) {
- $this->auth = $auth;
- $this->enableAssets = 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.
- 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;
- }
+ public function setCurrentUser($name) {
+ $this->channel_name = $name;
}
-
/**
- * @brief Creates the directory listing for the given path.
+ * Returns information about the currently logged-in channel.
*
- * @param string $path which should be displayed
+ * If nobody is currently logged in, this method should return null.
+ *
+ * @see DAV\Auth\Backend\AbstractBasic::getCurrentUser
+ * @return string|null
*/
- public function generateDirectoryIndex($path) {
- // (owner_id = channel_id) is visitor owner of this directory?
- $is_owner = ((local_user() && $this->auth->owner_id == local_user()) ? true : false);
-
- if ($this->auth->timezone)
- date_default_timezone_set($this->auth->timezone);
-
- require_once('include/conversation.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 = split("/", $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->channel_name;
- $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'] = $this->userReadableSize($size);
- $ft['lastmodified'] = (($lastmodified) ? datetime_convert('UTC', date_default_timezone_get(), $lastmodified) : '');
-
- $f[] = $ft;
- }
-
- // 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('%1$s used');
- $quotaDesc = sprintf($quotaDesc,
- $this->userReadableSize($used));
- }
- if ($limit && $used) {
- $quotaDesc = t('%1$s used of %2$s (%3$s&#37;)');
- $quotaDesc = sprintf($quotaDesc,
- $this->userReadableSize($used),
- $this->userReadableSize($limit),
- round($used / $limit, 1));
- }
-
- // prepare quota for template
- $quota['used'] = $used;
- $quota['limit'] = $limit;
- $quota['desc'] = $quotaDesc;
-
- $html .= replace_macros(get_markup_template('cloud_directory.tpl'), array(
- '$header' => t('Files') . ": " . $this->escapeHTML($path) . "/",
- '$parentpath' => $parentpath,
- '$entries' => $f,
- '$quota' => $quota,
- '$name' => t('Name'),
- '$type' => t('Type'),
- '$size' => t('Size'),
- '$lastmod' => t('Last Modified'),
- '$parent' => t('parent'),
- '$edit' => t('Edit'),
- '$delete' => t('Delete'),
- '$total' => t('Total')
- ));
-
- $output = '';
- if ($this->enablePost) {
- $this->server->broadcastEvent('onHTMLActionsPanel', array($parent, &$output));
- }
- $html .= $output;
-
- get_app()->page['content'] = $html;
- construct_page(get_app());
- }
-
- function userReadableSize($size) {
- $ret = "";
- if (is_numeric($size)) {
- $incr = 0;
- $k = 1024;
- $unit = array('bytes', 'KB', 'MB', 'GB', 'TB', 'PB');
- while (($size / $k) >= 1){
- $incr++;
- $size = round($size / $k, 2);
- }
- $ret = $size . " " . $unit[$incr];
- }
- return $ret;
+ public function getCurrentUser() {
+ return $this->channel_name;
}
/**
- * Creates a form to add new folders and upload files.
+ * @brief Sets the timezone from the channel in RedBasicAuth.
*
- * @param DAV\INode $node
- * @param string &$output
+ * Set in mod/cloud.php if the channel has a timezone set.
+ *
+ * @param string $timezone
+ * The channel's timezone.
+ * @return void
*/
- public function htmlActionsPanel(DAV\INode $node, &$output) {
-
- //Removed link to filestorage page
- //if($this->auth->owner_id && $this->auth->owner_id == $this->auth->channel_id) {
- // $channel = get_app()->get_channel();
- // if($channel) {
- // $output .= '<tr><td colspan="2"><a href="filestorage/' . $channel['channel_address'] . '" >' . t('Edit File properties') . '</a></td></tr><tr><td>&nbsp;</td></tr>';
- // }
- //}
-
- 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;
-
- $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')
- ));
+ public function setTimezone($timezone) {
+ $this->timezone = $timezone;
}
-
/**
- * This method takes a path/name of an asset and turns it into url
- * suiteable for http access.
+ * @brief Returns the timezone.
*
- * @param string $assetName
* @return string
+ * Return the channel's timezone.
*/
- protected function getAssetUrl($assetName) {
- return z_root() . '/cloud/?sabreAction=asset&assetName=' . urlencode($assetName);
+ public function getTimezone() {
+ return $this->timezone;
}
- protected function findAttachHash($owner, $parentHash, $attachName) {
- $r = q("SELECT * 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 Set browser plugin for SabreDAV.
+ *
+ * @see RedBrowser::set_writeable()
+ * @param DAV\Browser\Plugin $browser
+ */
+ public function setBrowserPlugin($browser) {
+ $this->browser = $browser;
}
- protected function findAttachIdByHash($attachHash) {
- $r = q("SELECT * 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;
+ /**
+ * Prints out all RedBasicAuth variables to logger().
+ *
+ * @return void
+ */
+ public function log() {
+ logger('dav: auth: channel_name ' . $this->channel_name, LOGGER_DATA);
+ logger('dav: auth: channel_id ' . $this->channel_id, LOGGER_DATA);
+ logger('dav: auth: channel_hash ' . $this->channel_hash, LOGGER_DATA);
+ logger('dav: auth: observer ' . $this->observer, LOGGER_DATA);
+ logger('dav: auth: owner_id ' . $this->owner_id, LOGGER_DATA);
+ logger('dav: auth: owner_nick ' . $this->owner_nick, LOGGER_DATA);
}
-} // class RedBrowser
+} // class RedBasicAuth
diff --git a/include/statistics_fns.php b/include/statistics_fns.php
new file mode 100644
index 000000000..4f72e6615
--- /dev/null
+++ b/include/statistics_fns.php
@@ -0,0 +1,73 @@
+<?php /** @file */
+
+function update_channels_total_stat() {
+ $r = q("select count(channel_id) as channels_total from channel left join account on account_id = channel_account_id
+ where account_flags = 0 ");
+ if($r) {
+ $channels_total_stat = intval($r[0]['channels_total']);
+ set_config('system','channels_total_stat',$channels_total_stat);
+ } else {
+ set_config('system','channels_total_stat',null);
+ }
+}
+
+function update_channels_active_halfyear_stat() {
+ $r = q("select channel_id from channel left join account on account_id = channel_account_id
+ where account_flags = 0 and account_lastlog > UTC_TIMESTAMP - INTERVAL 6 MONTH");
+ if($r) {
+ $s = '';
+ foreach($r as $rr) {
+ if($s)
+ $s .= ',';
+ $s .= intval($rr['channel_id']);
+ }
+ $x = q("select uid from item where uid in ( $s ) and (item_flags & %d) and created > UTC_TIMESTAMP - INTERVAL 6 MONTH group by uid",
+ intval(ITEM_WALL)
+ );
+ if($x) {
+ $channels_active_halfyear_stat = count($x);
+ set_config('system','channels_active_halfyear_stat',$channels_active_halfyear_stat);
+ } else {
+ set_config('system','channels_active_halfyear_stat',null);
+ }
+ } else {
+ set_config('system','channels_active_halfyear_stat',null);
+ }
+}
+
+function update_channels_active_monthly_stat() {
+ $r = q("select channel_id from channel left join account on account_id = channel_account_id
+ where account_flags = 0 and account_lastlog > UTC_TIMESTAMP - INTERVAL 1 MONTH");
+ if($r) {
+ $s = '';
+ foreach($r as $rr) {
+ if($s)
+ $s .= ',';
+ $s .= intval($rr['channel_id']);
+ }
+ $x = q("select uid from item where uid in ( $s ) and ( item_flags & %d ) and created > UTC_TIMESTAMP - INTERVAL 1 MONTH group by uid",
+ intval(ITEM_WALL)
+ );
+ if($x) {
+ $channels_active_monthly_stat = count($x);
+ set_config('system','channels_active_monthly_stat',$channels_active_monthly_stat);
+ } else {
+ set_config('system','channels_active_monthly_stat',null);
+ }
+ } else {
+ set_config('system','channels_active_monthly_stat',null);
+ }
+}
+
+function update_local_posts_stat() {
+ $posts = q("SELECT COUNT(*) AS local_posts FROM `item` WHERE (item_flags & %d) ",
+ intval(ITEM_WALL) );
+ if (is_array($posts)) {
+ $local_posts_stat = intval($posts[0]["local_posts"]);
+ set_config('system','local_posts_stat',$local_posts_stat);
+ } else {
+ set_config('system','local_posts_stat',null);
+ }
+}
+
+
diff --git a/include/text.php b/include/text.php
index 22cf17866..0e38de2d2 100644
--- a/include/text.php
+++ b/include/text.php
@@ -815,28 +815,26 @@ function micropro($contact, $redirect = false, $class = '', $textmode = false) {
function search($s,$id='search-box',$url='/search',$save = false) {
$a = get_app();
- $o = '<div id="' . $id . '">';
- $o .= '<form action="' . $a->get_baseurl((stristr($url,'network')) ? true : false) . $url . '" method="get" >';
- $o .= '<input type="text" class="icon-search" name="search" id="search-text" placeholder="&#xf002;" value="' . $s .'" onclick="this.submit();" />';
- $o .= '<input class="search-submit btn btn-default" type="submit" name="submit" id="search-submit" value="' . t('Search') . '" />';
- if(feature_enabled(local_user(),'savedsearch'))
- $o .= '<input class="search-save btn btn-default" type="submit" name="save" id="search-save" value="' . t('Save') . '" />';
- $o .= '</form></div>';
- return $o;
+ return replace_macros(get_markup_template('searchbox.tpl'),array(
+ '$s' => $s,
+ '$id' => $id,
+ '$action_url' => $a->get_baseurl((stristr($url,'network')) ? true : false) . $url,
+ '$search_label' => t('Search'),
+ '$save_label' => t('Save'),
+ '$savedsearch' => feature_enabled(local_user(),'savedsearch')
+ ));
}
function searchbox($s,$id='search-box',$url='/search',$save = false) {
- $a = get_app();
- $o = '<div id="' . $id . '">';
- $o .= '<form action="' . z_root() . '/' . $url . '" method="get" >';
- $o .= '<input type="hidden" name="f" value="" />';
- $o .= '<input type="text" class="icon-search" name="search" id="search-text" placeholder="&#xf002;" value="' . $s .'" onclick="this.submit();" />';
- $o .= '<input type="submit" name="submit" class="btn btn-default" id="search-submit" value="' . t('Search') . '" />';
- if(feature_enabled(local_user(),'savedsearch'))
- $o .= '<input type="submit" name="searchsave" class="btn btn-default" id="search-save" value="' . t('Save') . '" />';
- $o .= '</form></div>';
- return $o;
+ return replace_macros(get_markup_template('searchbox.tpl'),array(
+ '$s' => $s,
+ '$id' => $id,
+ '$action_url' => z_root() . '/' . $url,
+ '$search_label' => t('Search'),
+ '$save_label' => t('Save'),
+ '$savedsearch' => feature_enabled(local_user(),'savedsearch')
+ ));
}
@@ -2061,4 +2059,4 @@ function extra_query_args() {
}
}
return $s;
-} \ No newline at end of file
+}
diff --git a/include/widgets.php b/include/widgets.php
index f1c9ceada..8905df59a 100644
--- a/include/widgets.php
+++ b/include/widgets.php
@@ -532,6 +532,8 @@ function widget_mailmenu($arr) {
$a = get_app();
return replace_macros(get_markup_template('message_side.tpl'), array(
+ '$title' => t('Messages'),
+
'$tabs'=> array(),
'$check'=>array(
diff --git a/include/zot.php b/include/zot.php
index 828194c3a..3d59f00f3 100644
--- a/include/zot.php
+++ b/include/zot.php
@@ -1674,8 +1674,6 @@ function process_location_delivery($sender,$arr,$deliveries) {
logger('process_location_delivery: results: ' . print_r($x,true), LOGGER_DATA);
}
-// We need to merge this code with that in the import_xchan function so as to make it
-// easier to maintain changes.
function sync_locations($sender,$arr,$absolute = false) {