aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/Contact.php132
-rw-r--r--include/ConversationObject.php20
-rw-r--r--include/Import/Importer.php82
-rw-r--r--include/Import/refimport.php280
-rw-r--r--include/ItemObject.php101
-rw-r--r--include/RedDAV/RedBasicAuth.php212
-rw-r--r--include/RedDAV/RedBrowser.php374
-rw-r--r--include/RedDAV/RedDirectory.php462
-rw-r--r--include/RedDAV/RedFile.php279
-rw-r--r--include/account.php61
-rw-r--r--include/acl_selectors.php5
-rw-r--r--include/api.php82
-rw-r--r--include/apps.php9
-rw-r--r--include/attach.php271
-rw-r--r--include/auth.php160
-rw-r--r--include/bb2diaspora.php114
-rw-r--r--include/bbcode.php53
-rw-r--r--include/cache.php2
-rw-r--r--include/chat.php24
-rw-r--r--include/cli_startup.php10
-rw-r--r--include/config.php384
-rw-r--r--include/contact_widgets.php3
-rw-r--r--include/conversation.php308
-rw-r--r--include/datetime.php213
-rwxr-xr-xinclude/dba/dba_driver.php302
-rwxr-xr-xinclude/dba/dba_mysql.php4
-rwxr-xr-xinclude/dba/dba_mysqli.php6
-rw-r--r--include/dba/dba_postgres.php116
-rw-r--r--include/deliver.php36
-rwxr-xr-xinclude/diaspora.php567
-rw-r--r--include/dir_fns.php46
-rw-r--r--include/directory.php4
-rw-r--r--include/enotify.php4
-rw-r--r--include/event.php94
-rw-r--r--include/expire.php10
-rw-r--r--include/externals.php15
-rw-r--r--include/features.php5
-rw-r--r--include/fixd.php33
-rw-r--r--include/follow.php35
-rw-r--r--include/group.php12
-rw-r--r--include/html2bbcode.php7
-rw-r--r--include/hubloc.php218
-rw-r--r--include/identity.php253
-rwxr-xr-xinclude/items.php473
-rw-r--r--include/js_strings.php1
-rw-r--r--include/language.php2
-rw-r--r--include/menu.php16
-rw-r--r--include/message.php73
-rw-r--r--include/nav.php87
-rw-r--r--include/network.php95
-rw-r--r--include/notifier.php121
-rwxr-xr-xinclude/oembed.php22
-rw-r--r--include/onedirsync.php26
-rw-r--r--include/onepoll.php40
-rw-r--r--include/permissions.php456
-rw-r--r--include/photo/photo_driver.php77
-rw-r--r--include/photo/photo_gd.php2
-rw-r--r--include/photos.php12
-rwxr-xr-xinclude/plugin.php7
-rw-r--r--include/poller.php105
-rw-r--r--include/queue.php24
-rw-r--r--include/queue_fn.php4
-rw-r--r--include/reddav.php1214
-rw-r--r--include/security.php151
-rw-r--r--include/session.php11
-rw-r--r--include/socgraph.php57
-rw-r--r--include/statistics_fns.php79
-rw-r--r--include/taxonomy.php46
-rw-r--r--include/text.php847
-rw-r--r--include/widgets.php304
-rw-r--r--include/zot.php878
71 files changed, 7342 insertions, 3296 deletions
diff --git a/include/Contact.php b/include/Contact.php
index 0590f9d1e..4fd43db44 100644
--- a/include/Contact.php
+++ b/include/Contact.php
@@ -22,7 +22,7 @@ function rconnect_url($channel_id,$xchan) {
if(($r) && ($r[0]['xchan_follow']))
return $r[0]['xchan_follow'];
- $r = q("select hubloc_url from hubloc where hubloc_hash = '%s' and ( hubloc_flags & %d ) limit 1",
+ $r = q("select hubloc_url from hubloc where hubloc_hash = '%s' and ( hubloc_flags & %d )>0 limit 1",
dbesc($xchan),
intval(HUBLOC_FLAGS_PRIMARY)
);
@@ -35,7 +35,7 @@ function rconnect_url($channel_id,$xchan) {
function abook_connections($channel_id, $sql_conditions = '') {
$r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d
- and not ( abook_flags & %d ) $sql_conditions",
+ and not ( abook_flags & %d )>0 $sql_conditions",
intval($channel_id),
intval(ABOOK_FLAG_SELF)
);
@@ -44,7 +44,7 @@ function abook_connections($channel_id, $sql_conditions = '') {
function abook_self($channel_id) {
$r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d
- and ( abook_flags & %d ) limit 1",
+ and ( abook_flags & %d )>0 limit 1",
intval($channel_id),
intval(ABOOK_FLAG_SELF)
);
@@ -52,7 +52,7 @@ function abook_self($channel_id) {
}
function channelx_by_nick($nick) {
- $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_address = '%s' and not ( channel_pageflags & %d ) LIMIT 1",
+ $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_address = '%s' and not ( channel_pageflags & %d )>0 LIMIT 1",
dbesc($nick),
intval(PAGE_REMOVED)
);
@@ -60,7 +60,7 @@ function channelx_by_nick($nick) {
}
function channelx_by_hash($hash) {
- $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_hash = '%s' and not ( channel_pageflags & %d ) LIMIT 1",
+ $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_hash = '%s' and not ( channel_pageflags & %d )>0 LIMIT 1",
dbesc($hash),
intval(PAGE_REMOVED)
);
@@ -68,7 +68,7 @@ function channelx_by_hash($hash) {
}
function channelx_by_n($id) {
- $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_id = %d and not ( channel_pageflags & %d ) LIMIT 1",
+ $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_id = %d and not ( channel_pageflags & %d )>0 LIMIT 1",
dbesc($id),
intval(PAGE_REMOVED)
);
@@ -128,17 +128,19 @@ function vcard_from_xchan($xchan, $observer = null, $mode = '') {
function abook_toggle_flag($abook,$flag) {
- $r = q("UPDATE abook set abook_flags = (abook_flags ^ %d) where abook_id = %d and abook_channel = %d limit 1",
- intval($flag),
- intval($abook['abook_id']),
- intval($abook['abook_channel'])
+ $r = q("UPDATE abook set abook_flags = (abook_flags %s %d) where abook_id = %d and abook_channel = %d",
+ db_getfunc('^'),
+ intval($flag),
+ intval($abook['abook_id']),
+ intval($abook['abook_channel'])
);
+
// if unsetting the archive bit, update the timestamps so we'll try to connect for an additional 30 days.
if(($flag === ABOOK_FLAG_ARCHIVED) && ($abook['abook_flags'] & ABOOK_FLAG_ARCHIVED)) {
$r = q("update abook set abook_connected = '%s', abook_updated = '%s'
- where abook_id = %d and abook_channel = %d limit 1",
+ where abook_id = %d and abook_channel = %d",
dbesc(datetime_convert()),
dbesc(datetime_convert()),
intval($abook['abook_id']),
@@ -173,7 +175,7 @@ function account_remove($account_id,$local = true,$unset_session=true) {
// Don't let anybody nuke the only admin account.
- $r = q("select account_id from account where (account_roles & %d)",
+ $r = q("select account_id from account where (account_roles & %d)>0",
intval(ACCOUNT_ROLE_ADMIN)
);
@@ -201,10 +203,11 @@ function account_remove($account_id,$local = true,$unset_session=true) {
}
}
- $r = q("delete from account where account_id = %d limit 1",
+ $r = q("delete from account where account_id = %d",
intval($account_id)
);
+
if ($unset_session) {
unset($_SESSION['authenticated']);
unset($_SESSION['uid']);
@@ -214,6 +217,28 @@ function account_remove($account_id,$local = true,$unset_session=true) {
return $r;
}
+// recursively delete a directory
+function rrmdir($path)
+{
+ if (is_dir($path) === true)
+ {
+ $files = array_diff(scandir($path), array('.', '..'));
+
+ foreach ($files as $file)
+ {
+ rrmdir(realpath($path) . '/' . $file);
+ }
+
+ return rmdir($path);
+ }
+
+ else if (is_file($path) === true)
+ {
+ return unlink($path);
+ }
+
+ return false;
+}
function channel_remove($channel_id, $local = true, $unset_session=true) {
@@ -239,17 +264,19 @@ function channel_remove($channel_id, $local = true, $unset_session=true) {
channel_r_photos = 0, channel_r_abook = 0, channel_w_stream = 0, channel_w_wall = 0, channel_w_tagwall = 0,
channel_w_comment = 0, channel_w_mail = 0, channel_w_photos = 0, channel_w_chat = 0, channel_a_delegate = 0,
channel_r_storage = 0, channel_w_storage = 0, channel_r_pages = 0, channel_w_pages = 0, channel_a_republish = 0
- where channel_id = %d limit 1",
+ where channel_id = %d",
dbesc(datetime_convert()),
intval(PAGE_REMOVED),
intval($channel_id)
);
+
$r = q("update hubloc set hubloc_flags = (hubloc_flags | %d) where hubloc_hash = '%s'",
intval(HUBLOC_FLAGS_DELETED),
dbesc($channel['channel_hash'])
);
+
$r = q("update xchan set xchan_flags = (xchan_flags | %d) where xchan_hash = '%s'",
intval(XCHAN_FLAGS_DELETED),
dbesc($channel['channel_hash'])
@@ -257,7 +284,6 @@ function channel_remove($channel_id, $local = true, $unset_session=true) {
proc_run('php','include/notifier.php','purge_all',$channel_id);
-
}
q("DELETE FROM `groups` WHERE `uid` = %d", intval($channel_id));
@@ -274,12 +300,12 @@ function channel_remove($channel_id, $local = true, $unset_session=true) {
q("DELETE FROM `spam` WHERE `uid` = %d", intval($channel_id));
- q("delete from abook where abook_xchan = '%s' and (abook_flags & %d) limit 1",
+ q("delete from abook where abook_xchan = '%s' and (abook_flags & %d)>0",
dbesc($channel['channel_hash']),
dbesc(ABOOK_FLAG_SELF)
);
- $r = q("update channel set channel_deleted = '%s', channel_pageflags = (channel_pageflags | %d) where channel_id = %d limit 1",
+ $r = q("update channel set channel_deleted = '%s', channel_pageflags = (channel_pageflags | %d) where channel_id = %d",
dbesc(datetime_convert()),
intval(PAGE_REMOVED),
intval($channel_id)
@@ -291,11 +317,36 @@ function channel_remove($channel_id, $local = true, $unset_session=true) {
dbesc(z_root())
);
- $r = q("update xchan set xchan_flags = (xchan_flags | %d) where xchan_hash = '%s' ",
- intval(XCHAN_FLAGS_DELETED),
- dbesc($channel['channel_hash'])
+ // Do we have any valid hublocs remaining?
+
+ $hublocs = 0;
+
+ $r = q("select hubloc_id from hubloc where hubloc_hash = '%s' and not (hubloc_flags & %d)>0",
+ dbesc($channel['channel_hash']),
+ intval(HUBLOC_FLAGS_DELETED)
);
+ if($r)
+ $hublocs = count($r);
+ if(! $hublocs) {
+ $r = q("update xchan set xchan_flags = (xchan_flags | %d) where xchan_hash = '%s' ",
+ intval(XCHAN_FLAGS_DELETED),
+ dbesc($channel['channel_hash'])
+ );
+ }
+
+ //remove from file system
+ $r = q("select channel_address from channel where channel_id = %d limit 1",
+ intval($channel_id)
+ );
+ if($r)
+ $channel_address = $r[0]['channel_address'] ;
+ if ($channel_address !== '') {
+ $f = 'store/' . $channel_address.'/';
+ logger ('delete '. $f);
+ if(is_dir($f))
+ @rrmdir($f);
+ }
proc_run('php','include/directory.php',$channel_id);
@@ -322,13 +373,27 @@ function mark_orphan_hubsxchans() {
if($dirmode == DIRECTORY_MODE_NORMAL)
return;
- $r = q("update hubloc set hubloc_status = (hubloc_status | %d) where not (hubloc_status & %d)
- and hubloc_network != 'zot' and hubloc_connected < utc_timestamp() - interval 36 day",
+ $r = q("update hubloc set hubloc_status = (hubloc_status | %d) where not (hubloc_status & %d)>0
+ and hubloc_network = 'zot' and hubloc_connected < %s - interval %s",
+ intval(HUBLOC_OFFLINE),
intval(HUBLOC_OFFLINE),
- intval(HUBLOC_OFFLINE)
+ db_utcnow(), db_quoteinterval('36 day')
);
- $r = q("select hubloc_id, hubloc_hash from hubloc where (hubloc_status & %d) and not (hubloc_flags & %d)",
+// $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)>0 and not (hubloc_flags & %d)>0",
intval(HUBLOC_OFFLINE),
intval(HUBLOC_FLAGS_ORPHANCHECK)
);
@@ -338,7 +403,7 @@ function mark_orphan_hubsxchans() {
// see if any other hublocs are still alive for this channel
- $x = q("select * from hubloc where hubloc_hash = '%s' and not (hubloc_status & %d)",
+ $x = q("select * from hubloc where hubloc_hash = '%s' and not (hubloc_status & %d)>0",
dbesc($rr['hubloc_hash']),
intval(HUBLOC_OFFLINE)
);
@@ -346,7 +411,7 @@ function mark_orphan_hubsxchans() {
// yes - if the xchan was marked as an orphan, undo it
- $y = q("update xchan set xchan_flags = (xchan_flags ^ %d) where (xchan_flags & %d) and xchan_hash = '%s' limit 1",
+ $y = q("update xchan set xchan_flags = (xchan_flags & ~%d) where (xchan_flags & %d)>0 and xchan_hash = '%s'",
intval(XCHAN_FLAGS_ORPHAN),
intval(XCHAN_FLAGS_ORPHAN),
dbesc($rr['hubloc_hash'])
@@ -357,7 +422,7 @@ function mark_orphan_hubsxchans() {
// nope - mark the xchan as an orphan
- $y = q("update xchan set xchan_flags = (xchan_flags | %d) where xchan_hash = '%s' limit 1",
+ $y = q("update xchan set xchan_flags = (xchan_flags | %d) where xchan_hash = '%s'",
intval(XCHAN_FLAGS_ORPHAN),
dbesc($rr['hubloc_hash'])
);
@@ -365,7 +430,7 @@ function mark_orphan_hubsxchans() {
// mark that we've checked this entry so we don't need to do it again
- $y = q("update hubloc set hubloc_flags = (hubloc_flags | %d) where hubloc_id = %d limit 1",
+ $y = q("update hubloc set hubloc_flags = (hubloc_flags | %d) where hubloc_id = %d",
intval(HUBLOC_FLAGS_ORPHANCHECK),
dbesc($rr['hubloc_id'])
);
@@ -423,7 +488,7 @@ function remove_all_xchan_resources($xchan, $channel_id = 0) {
if($dirmode === false || $dirmode == DIRECTORY_MODE_NORMAL) {
- $r = q("delete from xchan where xchan_hash = '%s' limit 1",
+ $r = q("delete from xchan where xchan_hash = '%s'",
dbesc($xchan)
);
$r = q("delete from hubloc where hubloc_hash = '%s'",
@@ -456,7 +521,7 @@ function contact_remove($channel_id, $abook_id) {
$archive = get_pconfig($channel_id, 'system','archive_removed_contacts');
if($archive) {
- q("update abook set abook_flags = ( abook_flags | %d ) where abook_id = %d and abook_channel = %d limit 1",
+ q("update abook set abook_flags = ( abook_flags | %d ) where abook_id = %d and abook_channel = %d",
intval(ABOOK_FLAG_ARCHIVED),
intval($abook_id),
intval($channel_id)
@@ -488,7 +553,7 @@ function contact_remove($channel_id, $abook_id) {
}
}
- q("delete from abook where abook_id = %d and abook_channel = %d limit 1",
+ q("delete from abook where abook_id = %d and abook_channel = %d",
intval($abook['abook_id']),
intval($channel_id)
);
@@ -515,7 +580,10 @@ function contact_remove($channel_id, $abook_id) {
function random_profile() {
- $r = q("select xchan_url from xchan left join hubloc on hubloc_hash = xchan_hash where hubloc_connected > UTC_TIMESTAMP() - interval 30 day order by rand() limit 1");
+ $randfunc = db_getfunc('rand');
+ $r = q("select xchan_url from xchan left join hubloc on hubloc_hash = xchan_hash where hubloc_connected > %s - interval %s order by $randfunc limit 1",
+ db_utcnow(), db_quoteinterval('30 day')
+ );
if($r)
return $r[0]['xchan_url'];
return '';
diff --git a/include/ConversationObject.php b/include/ConversationObject.php
index 767ef7360..a02ba102f 100644
--- a/include/ConversationObject.php
+++ b/include/ConversationObject.php
@@ -52,14 +52,14 @@ 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 {
+// 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_user();
$this->writable = true;
- }
+// }
break;
case 'channel':
$this->profile_owner = $a->profile['profile_uid'];
@@ -177,11 +177,11 @@ class Conversation extends BaseObject {
}
}
require_once('include/identity.php');
- $sys = get_sys_channel();
+// $sys = get_sys_channel();
- if($sys && $item->get_data_value('uid') == $sys['channel_id']) {
- $item->set_commentable(false);
- }
+// if($sys && $item->get_data_value('uid') == $sys['channel_id']) {
+// $item->set_commentable(false);
+// }
$item->set_conversation($this);
$this->threads[] = $item;
diff --git a/include/Import/Importer.php b/include/Import/Importer.php
new file mode 100644
index 000000000..5e684cd8e
--- /dev/null
+++ b/include/Import/Importer.php
@@ -0,0 +1,82 @@
+<?php /** @file */
+
+namespace RedMatrix\Import;
+
+/**
+ * @brief Class Import
+ *
+ * @package RedMatrix\Import
+ */
+class Import {
+
+ private $credentials = null;
+
+ protected $itemlist = null;
+ protected $src_items = null;
+ protected $items = null;
+
+ function get_credentials() {
+ return $this->credentials;
+ }
+
+ function get_itemlist() {
+ return $this->itemlist;
+ }
+
+ function get_item_ident($item) {
+
+ }
+
+ function get_item($item_ident) {
+
+ }
+
+ function get_taxonomy($item_ident) {
+
+ }
+
+ function get_children($item_ident) {
+
+ }
+
+ function convert_item($item_ident) {
+
+ }
+
+ function convert_taxonomy($item_ident) {
+
+ }
+
+ function convert_child($child) {
+
+ }
+
+ function store($item, $update = false) {
+
+ }
+
+ function run() {
+ $this->credentials = $this->get_credentials();
+ $this->itemlist = $this->get_itemlist();
+ if($this->itemlist) {
+ $this->src_items = array();
+ $this->items = array();
+ $cnt = 0;
+ foreach($this->itemlist as $item) {
+ $ident = $item->get_item_ident($item);
+ $this->src_items[$ident]['item'] = $this->get_item($ident);
+ $this->src_items[$ident]['taxonomy'] = $this->get_taxonomy($ident);
+ $this->src_items[$ident]['children'] = $this->get_children($ident);
+ $this->items[$cnt]['item'] = $this->convert_item($ident);
+ $this->items[$cnt]['item']['term'] = $this->convert_taxonomy($ident);
+ if($this->src_items[$ident]['children']) {
+ $this->items[$cnt]['children'] = array();
+ foreach($this->src_items[$ident]['children'] as $child) {
+ $this[$cnt]['children'][] = $this->convert_child($child);
+ }
+ }
+ $cnt ++;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/include/Import/refimport.php b/include/Import/refimport.php
new file mode 100644
index 000000000..181b2b398
--- /dev/null
+++ b/include/Import/refimport.php
@@ -0,0 +1,280 @@
+<?php
+
+require_once('include/html2bbcode.php');
+require_once('include/hubloc.php');
+
+// Sample module for importing conversation data from Reflection CMS. Some preparation was used to
+// dump relevant posts, categories and comments into individual JSON files, and also JSON dump of
+// the user table to search for avatars. Importation was also batched in sets of 20 posts per page
+// visit so as to survive shared hosting process limits. This provides some clues as how to handle
+// WordPress imports, which use a somewhat similar DB structure. The batching and individual files
+// might not be needed in VPS environments. As such this could be considered an extreme test case, but
+// the importation was successful in all regards using this code. The module URL was visited repeatedly
+// with a browser until all the posts had been imported.
+
+
+define('REDMATRIX_IMPORTCHANNEL','mike');
+define('REFLECT_EXPORTUSERNAME','mike');
+define('REFLECT_BLOGNAME','Diary and Other Rantings');
+define('REFLECT_BASEURL','http://example.com/');
+define('REFLECT_USERFILE','user.json');
+
+// set to true if you need to process everything again
+define('REFLECT_OVERWRITE',false);
+
+// we'll only process a small number of posts at a time on a shared host.
+
+define('REFLECT_MAXPERRUN',30);
+
+function reflect_get_channel() {
+
+ // this will be the channel_address or nickname of the red channel
+
+ $c = q("select * from channel left join xchan on channel_hash = xchan_hash
+ where channel_address = '%s' limit 1",
+ dbesc(REDMATRIX_IMPORTCHANNEL)
+ );
+ return $c[0];
+}
+
+
+function refimport_content(&$a) {
+
+ $channel = reflect_get_channel();
+
+ // load the user file. We need that to find the commenter's avatars
+
+ $u = file_get_contents(REFLECT_USERFILE);
+ if($u) {
+ $users = json_decode($u,true);
+ }
+
+ $ignored = 0;
+ $processed = 0;
+
+ $files = glob('article/*');
+ if(! $files)
+ return;
+
+ foreach($files as $f) {
+ $s = file_get_contents($f);
+ $j = json_decode($s,true);
+
+ if(! $j)
+ continue;
+
+ $arr = array();
+
+ // see if this article was already processed
+ $r = q("select * from item where mid = '%s' and uid = %d limit 1",
+ dbesc($j['guid']),
+ intval($channel['channel_id'])
+ );
+ if($r) {
+ if(REFLECT_OVERWRITE)
+ $arr['id'] = $r[0]['id'];
+ else {
+ $ignored ++;
+ rename($f,str_replace('article','done',$f));
+ continue;
+ }
+ }
+
+ $arr['uid'] = $channel['channel_account_id'];
+ $arr['aid'] = $channel['channel_id'];
+ $arr['mid'] = $arr['parent_mid'] = $j['guid'];
+ $arr['created'] = $j['created'];
+ $arr['edited'] = $j['edited'];
+ $arr['author_xchan'] = $channel['channel_hash'];
+ $arr['owner_xchan'] = $channel['channel_hash'];
+ $arr['app'] = REFLECT_BLOGNAME;
+ $arr['item_flags'] = ITEM_ORIGIN|ITEM_WALL|ITEM_THREAD_TOP;
+ $arr['verb'] = ACTIVITY_POST;
+
+ // this is an assumption
+ $arr['comment_policy'] = 'contacts';
+
+
+ // import content. In this case the content is XHTML.
+
+ $arr['title'] = html2bbcode($j['title']);
+ $arr['title'] = htmlspecialchars($arr['title'],ENT_COMPAT,'UTF-8',false);
+
+
+ $arr['body'] = html2bbcode($j['body']);
+ $arr['body'] = htmlspecialchars($arr['body'],ENT_COMPAT,'UTF-8',false);
+
+
+ // convert relative urls to other posts on that service to absolute url on our service.
+ $arr['body'] = preg_replace_callback("/\[url\=\/+article\/(.*?)\](.*?)\[url\]/",'reflect_article_callback',$arr['body']);
+
+ // also import any photos
+ $arr['body'] = preg_replace_callback("/\[img(.*?)\](.*?)\[\/img\]/",'reflect_photo_callback',$arr['body']);
+
+
+ // add categories
+
+ if($j['taxonomy'] && is_array($j['taxonomy']) && count($j['taxonomy'])) {
+ $arr['term'] = array();
+ foreach($j['taxonomy'] as $tax) {
+ $arr['term'][] = array(
+ 'uid' => $channel['channel_id'],
+ 'type' => TERM_CATEGORY,
+ 'otype' => TERM_OBJ_POST,
+ 'term' => trim($tax['name']),
+ 'url' => $channel['xchan_url'] . '?f=&cat=' . urlencode(trim($tax['name']))
+ );
+ }
+ }
+
+ // store the item
+
+ if($arr['id'])
+ item_store_update($arr);
+ else
+ item_store($arr);
+
+ // if there are any comments, process them
+ // $comment['registered'] is somebody with an account on the system. Others are mostly anonymous
+
+ if($j['comments']) {
+ foreach($j['comments'] as $comment) {
+ $user = (($comment['registered']) ? reflect_find_user($users,$comment['author']) : null);
+ reflect_comment_store($channel,$arr,$comment,$user);
+ }
+ }
+ $processed ++;
+
+ if(REFLECT_MAXPERRUN && $processed > REFLECT_MAXPERRUN)
+ break;
+ }
+ return 'processed: ' . $processed . EOL . 'completed: ' . $ignored . EOL;
+
+}
+
+function reflect_article_callback($matches) {
+ return '[zrl=' . z_root() . '/display/'. $matches[1] . ']' . $matches[2] . '[/zrl]';
+}
+
+function reflect_photo_callback($matches) {
+
+ if(strpos($matches[2],'http') !== false)
+ return $matches[0];
+
+ $prefix = REFLECT_BASEURL;
+ $x = z_fetch_url($prefix.$matches[2],true);
+
+ $hash = basename($matches[2]);
+
+ if($x['success']) {
+ $channel = reflect_get_channel();
+ require_once('include/photos.php');
+ $p = photo_upload($channel,$channel,
+ array('data' => $x['body'],
+ 'resource_id' => str_replace('-','',$hash),
+ 'filename' => $hash . '.jpg',
+ 'type' => 'image/jpeg',
+ 'not_visible' => true
+ )
+ );
+
+ if($p['success'])
+ $newlink = $p['resource_id'] . '-0.jpg';
+
+
+ // import photo and locate the link for it.
+ return '[zmg]' . z_root() . '/photo/' . $newlink . '[/zmg]';
+
+ }
+ // no replacement. Leave it alone.
+ return $matches[0];
+}
+
+function reflect_find_user($users,$name) {
+ if($users) {
+ foreach($users as $x) {
+ if($x['name'] === $name) {
+ return $x;
+ }
+ }
+ }
+
+ return false;
+
+}
+
+function reflect_comment_store($channel,$post,$comment,$user) {
+
+ // if the commenter was the channel owner, use their redmatrix xchan
+
+ if($comment['author'] === REFLECT_EXPORTUSERNAME && $comment['registered'])
+ $hash = $channel['xchan_hash'];
+ else {
+ // we need a unique hash for the commenter. We don't know how many may have supplied
+ // http://yahoo.com as their URL, so we'll use their avatar guid if they have one.
+ // anonymous folks may get more than one xchan_hash if they commented more than once.
+
+ $hash = (($comment['registered'] && $user) ? $user['avatar'] : '');
+ if(! $hash)
+ $hash = random_string() . '.unknown';
+
+ // create an xchan for them which will also import their profile photo
+ // they will have a network type 'unknown'.
+
+ $x = array(
+ 'hash' => $hash,
+ 'guid' => $hash,
+ 'url' => (($comment['url']) ? $comment['url'] : z_root()),
+ 'photo' => (($user) ? REFLECT_BASEURL . $user['avatar'] : z_root() . '/' . get_default_profile_photo()),
+ 'name' => $comment['author']
+ );
+ xchan_store($x);
+
+ }
+
+ $arr = array();
+
+ $r = q("select * from item where mid = '%s' and uid = %d limit 1",
+ dbesc($comment['guid']),
+ intval($channel['channel_id'])
+ );
+ if($r) {
+ if(REFLECT_OVERWRITE)
+ $arr['id'] = $r[0]['id'];
+ else
+ return;
+ }
+
+ // this is a lot like storing the post except for subtle differences, like parent_mid, flags, author_xchan,
+ // and we don't have a comment edited field so use creation date
+
+ $arr['uid'] = $channel['channel_account_id'];
+ $arr['aid'] = $channel['channel_id'];
+ $arr['mid'] = $comment['guid'];
+ $arr['parent_mid'] = $post['mid'];
+ $arr['created'] = $comment['created'];
+ $arr['edited'] = $comment['created'];
+ $arr['author_xchan'] = $hash;
+ $arr['owner_xchan'] = $channel['channel_hash'];
+ $arr['item_flags'] = ITEM_ORIGIN|ITEM_WALL;
+ $arr['verb'] = ACTIVITY_POST;
+ $arr['comment_policy'] = 'contacts';
+
+
+ $arr['title'] = html2bbcode($comment['title']);
+ $arr['title'] = htmlspecialchars($arr['title'],ENT_COMPAT,'UTF-8',false);
+
+
+ $arr['body'] = html2bbcode($comment['body']);
+ $arr['body'] = htmlspecialchars($arr['body'],ENT_COMPAT,'UTF-8',false);
+ $arr['body'] = preg_replace_callback("/\[url\=\/+article\/(.*?)\](.*?)\[url\]/",'reflect_article_callback',$arr['body']);
+ $arr['body'] = preg_replace_callback("/\[img(.*?)\](.*?)\[\/img\]/",'reflect_photo_callback',$arr['body']);
+
+ // logger('comment: ' . print_r($arr,true));
+
+ if($arr['id'])
+ item_store_update($arr);
+ else
+ item_store($arr);
+
+}
diff --git a/include/ItemObject.php b/include/ItemObject.php
index b4a60762b..ec8116297 100644
--- a/include/ItemObject.php
+++ b/include/ItemObject.php
@@ -28,6 +28,7 @@ class Item extends BaseObject {
private $threaded = false;
private $visiting = false;
private $channel = null;
+ private $display_mode = 'normal';
public function __construct($data) {
@@ -64,8 +65,6 @@ class Item extends BaseObject {
public function get_template_data($alike, $dlike, $thread_level=1) {
- $t1 = dba_timer();
-
$result = array();
$a = $this->get_app();
@@ -80,6 +79,7 @@ class Item extends BaseObject {
$indent = '';
$osparkle = '';
$total_children = $this->count_descendants();
+ $unseen_comments = (($item['real_uid']) ? 0 : $this->count_unseen_descendants());
$conv = $this->get_conversation();
$observer = $conv->get_observer();
@@ -88,7 +88,7 @@ class Item extends BaseObject {
|| strlen($item['deny_cid']) || strlen($item['deny_gid']))))
? t('Private Message')
: false);
- $shareable = ((($conv->get_profile_owner() == local_user()) && ($item['item_private'] != 1)) ? true : false);
+ $shareable = ((($conv->get_profile_owner() == local_user() && local_user()) && ($item['item_private'] != 1)) ? true : false);
// allow an exemption for sharing stuff from your private feeds
if($item['author']['xchan_network'] === 'rss')
@@ -101,11 +101,19 @@ class Item extends BaseObject {
else
$edpost = false;
+
if($observer['xchan_hash'] == $this->get_data_value('author_xchan')
|| $observer['xchan_hash'] == $this->get_data_value('owner_xchan')
|| $this->get_data_value('uid') == local_user())
$dropping = true;
+
+ if(array_key_exists('real_uid',$item)) {
+ $edpost = false;
+ $dropping = false;
+ }
+
+
if($dropping) {
$drop = array(
'dropping' => $dropping,
@@ -119,7 +127,7 @@ class Item extends BaseObject {
);
}
- $filer = (($conv->get_profile_owner() == local_user()) ? t("Save to Folder") : false);
+ $filer = ((($conv->get_profile_owner() == local_user()) && (! array_key_exists('real_uid',$item))) ? t("Save to Folder") : false);
$profile_avatar = $item['author']['xchan_photo_m'];
$profile_link = chanlink_url($item['author']['xchan_url']);
@@ -163,7 +171,7 @@ class Item extends BaseObject {
if($this->is_toplevel()) {
// FIXME check this permission
- if($conv->get_profile_owner() == local_user()) {
+ if(($conv->get_profile_owner() == local_user()) && (! array_key_exists('real_uid',$item))) {
// FIXME we don't need all this stuff, some can be done in the template
@@ -183,7 +191,8 @@ class Item extends BaseObject {
}
- $verified = (($item['item_flags'] & ITEM_VERIFIED) ? t('Message is verified') : '');
+ $verified = (($item['item_flags'] & ITEM_VERIFIED) ? t('Message signature validated') : '');
+ $forged = ((($item['sig']) && (! ($item['item_flags'] & ITEM_VERIFIED))) ? t('Message signature incorrect') : '');
$unverified = '' ; // (($this->is_wall_to_wall() && (! ($item['item_flags'] & ITEM_VERIFIED))) ? t('Message cannot be verified') : '');
@@ -219,15 +228,22 @@ class Item extends BaseObject {
if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0)
$indent .= ' shiny';
- $t2 = dba_timer();
localize_item($item);
-
- $t3 = dba_timer();
-
$body = prepare_body($item,true);
- $t4 = dba_timer();
+ // $viewthread (below) is only valid in list mode. If this is a channel page, build the thread viewing link
+ // since we can't depend on llink or plink pointing to the right local location.
+
+ $owner_address = substr($item['owner']['xchan_addr'],0,strpos($item['owner']['xchan_addr'],'@'));
+ $viewthread = $item['llink'];
+ if($conv->get_mode() === 'channel')
+ $viewthread = z_root() . '/channel/' . $owner_address . '?f=&mid=' . $item['mid'];
+
+ $comment_count_txt = sprintf( tt('%d comment','%d comments',$total_children),$total_children );
+ $list_unseen_txt = (($unseen_comments) ? sprintf('%d unseen',$unseen_comments) : '');
+
+ $children = $this->get_children();
$tmp_item = array(
'template' => $this->get_template(),
@@ -239,6 +255,8 @@ class Item extends BaseObject {
'id' => $this->get_id(),
'linktitle' => sprintf( t('View %s\'s profile - %s'), $profile_name, $item['author']['xchan_addr']),
'olinktitle' => sprintf( t('View %s\'s profile - %s'), $this->get_owner_name(), $item['owner']['xchan_addr']),
+ 'llink' => $item['llink'],
+ 'viewthread' => $viewthread,
'to' => t('to'),
'via' => t('via'),
'wall' => t('Wall-to-Wall'),
@@ -250,16 +268,18 @@ class Item extends BaseObject {
'osparkle' => $osparkle,
'sparkle' => $sparkle,
'title' => $item['title'],
+ 'title_tosource' => get_pconfig($conv->get_profile_owner(),'system','title_tosource'),
'ago' => relative_date($item['created']),
'app' => $item['app'],
'str_app' => sprintf( t(' from %s'), $item['app']),
'isotime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'c'),
'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'),
'editedtime' => (($item['edited'] != $item['created']) ? sprintf( t('last edited: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r')) : ''),
- 'expiretime' => (($item['expires'] !== '0000-00-00 00:00:00') ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')):''),
+ 'expiretime' => (($item['expires'] !== NULL_DATE) ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')):''),
'lock' => $lock,
'verified' => $verified,
'unverified' => $unverified,
+ 'forged' => $forged,
'location' => $location,
'indent' => $indent,
'owner_url' => $this->get_owner_url(),
@@ -272,15 +292,21 @@ class Item extends BaseObject {
'share' => $share,
'rawmid' => $item['mid'],
'plink' => get_plink($item),
- 'edpost' => ((feature_enabled($conv->get_profile_owner(),'edit_posts')) ? $edpost : ''),
+ 'edpost' => $edpost, // ((feature_enabled($conv->get_profile_owner(),'edit_posts')) ? $edpost : ''),
'star' => ((feature_enabled($conv->get_profile_owner(),'star_posts')) ? $star : ''),
'tagger' => ((feature_enabled($conv->get_profile_owner(),'commtag')) ? $tagger : ''),
'filer' => ((feature_enabled($conv->get_profile_owner(),'filing')) ? $filer : ''),
- 'bookmark' => (($conv->get_profile_owner() == local_user() && $has_bookmarks) ? t('Save Bookmarks') : ''),
+ 'bookmark' => (($conv->get_profile_owner() == local_user() && local_user() && $has_bookmarks) ? t('Save Bookmarks') : ''),
'addtocal' => (($has_event) ? t('Add to Calendar') : ''),
'drop' => $drop,
'multidrop' => ((feature_enabled($conv->get_profile_owner(),'multi_delete')) ? $multidrop : ''),
// end toolbar buttons
+
+ 'unseen_comments' => $unseen_comments,
+ 'comment_count' => $total_children,
+ 'comment_count_txt' => $comment_count_txt,
+ 'list_unseen_txt' => $list_unseen_txt,
+ 'markseen' => t('Mark all seen'),
'like_count' => $like_count,
'like_list' => $like_list,
'like_list_part' => $like_list_part,
@@ -300,24 +326,22 @@ class Item extends BaseObject {
'thread_level' => $thread_level
);
- $t5 = dba_timer();
-
$arr = array('item' => $item, 'output' => $tmp_item);
call_hooks('display_item', $arr);
$result = $arr['output'];
$result['children'] = array();
- $children = $this->get_children();
$nb_children = count($children);
- if($nb_children > 0) {
+
+ if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) {
foreach($children as $child) {
$result['children'][] = $child->get_template_data($alike, $dlike, $thread_level + 1);
}
// Collapse
if(($nb_children > 2) || ($thread_level > 1)) {
$result['children'][0]['comment_firstcollapsed'] = true;
- $result['children'][0]['num_comments'] = sprintf( tt('%d comment','%d comments',$total_children),$total_children );
+ $result['children'][0]['num_comments'] = $comment_count_txt;
$result['children'][0]['hide_text'] = t('[+] show all');
if($thread_level > 1) {
$result['children'][$nb_children - 1]['comment_lastcollapsed'] = true;
@@ -339,14 +363,6 @@ class Item extends BaseObject {
$result['flatten'] = true;
$result['threaded'] = false;
}
- $t6 = dba_timer();
-
-// profiler($t1,$t2,'t2');
-// profiler($t2,$t3,'t3');
-// profiler($t3,$t4,'t4');
-// profiler($t4,$t5,'t5');
-// profiler($t5,$t6,'t6');
-// profiler($t1,$t6,'item total');
return $result;
}
@@ -355,6 +371,14 @@ class Item extends BaseObject {
return $this->get_data_value('id');
}
+ public function get_display_mode() {
+ return $this->display_mode;
+ }
+
+ public function set_display_mode($mode) {
+ $this->display_mode = $mode;
+ }
+
public function is_threaded() {
return $this->threaded;
}
@@ -506,12 +530,12 @@ class Item extends BaseObject {
/**
* Get template
*/
- private function get_template() {
+ public function get_template() {
return $this->template;
}
- private function set_template($t) {
+ public function set_template($t) {
$this->template = $t;
}
@@ -536,6 +560,23 @@ class Item extends BaseObject {
return $total;
}
+ private function count_unseen_descendants() {
+ $children = $this->get_children();
+ $total = count($children);
+ if($total > 0) {
+ $total = 0;
+ foreach($children as $child) {
+ if((! visible_activity($child->data)) || array_key_exists('author_blocked',$child->data)) {
+ continue;
+ }
+ if($child->data['item_flags'] & ITEM_UNSEEN)
+ $total ++;
+ }
+ }
+ return $total;
+ }
+
+
/**
* Get the template for the comment box
*/
@@ -594,7 +635,7 @@ class Item extends BaseObject {
'$edimg' => t('Image'),
'$edurl' => t('Link'),
'$edvideo' => t('Video'),
- '$preview' => ((feature_enabled($conv->get_profile_owner(),'preview')) ? t('Preview') : ''),
+ '$preview' => t('Preview'), // ((feature_enabled($conv->get_profile_owner(),'preview')) ? t('Preview') : ''),
'$indent' => $indent,
'$feature_encrypt' => ((feature_enabled($conv->get_profile_owner(),'content_encrypt')) ? true : false),
'$encrypt' => t('Encrypt text'),
diff --git a/include/RedDAV/RedBasicAuth.php b/include/RedDAV/RedBasicAuth.php
new file mode 100644
index 000000000..19dd9a5f0
--- /dev/null
+++ b/include/RedDAV/RedBasicAuth.php
@@ -0,0 +1,212 @@
+<?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
new file mode 100644
index 000000000..eb08fd79f
--- /dev/null
+++ b/include/RedDAV/RedBrowser.php
@@ -0,0 +1,374 @@
+<?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_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 = array();
+ $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;
+ load_pdl(get_app());
+ construct_page(get_app());
+ }
+
+ /**
+ * @brief Returns a human readable formatted string for filesizes.
+ *
+ * Don't we need such a functionality in other places, too?
+ *
+ * @param int $size filesize in bytes
+ * @return string
+ */
+ 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/RedDAV/RedDirectory.php b/include/RedDAV/RedDirectory.php
new file mode 100644
index 000000000..85af0d57f
--- /dev/null
+++ b/include/RedDAV/RedDirectory.php
@@ -0,0 +1,462 @@
+<?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) {
+ //logger('directory ' . $ext_path, LOGGER_DATA);
+ $this->ext_path = $ext_path;
+ // remove "/cloud" from the beginning of the path
+ $this->red_path = ((strpos($ext_path, '/cloud') === 0) ? substr($ext_path, 6) : $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.');
+ }
+
+ if ($this->red_path === '/' && $name === 'cloud') {
+ return new RedDirectory('/cloud', $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 NOT (channel_pageflags & %d)>0 LIMIT 1",
+ intval($this->auth->owner_id),
+ intval(PAGE_REMOVED)
+ );
+
+ if (! $c) {
+ logger('no channel');
+ throw new DAV\Exception\Forbidden('Permission denied.');
+ }
+
+ $filesize = 0;
+ $hash = random_string();
+
+ $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, folder, flags, filetype, filesize, revision, data, created, edited, allow_cid, allow_gid, deny_cid, deny_gid )
+ VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', %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),
+ dbesc(ATTACH_FLAG_OS),
+ dbesc($mimetype),
+ intval($filesize),
+ intval(0),
+ dbesc($this->os_path . '/' . $hash),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ dbesc($c[0]['channel_allow_cid']),
+ dbesc($c[0]['channel_allow_gid']),
+ dbesc($c[0]['channel_deny_cid']),
+ dbesc($c[0]['channel_deny_gid'])
+ );
+
+ $f = 'store/' . $this->auth->owner_nick . '/' . (($this->os_path) ? $this->os_path . '/' : '') . $hash;
+
+ // 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();
+
+ // updates entry with filesize and timestamp
+ $d = q("UPDATE attach SET filesize = '%s', edited = '%s' WHERE hash = '%s' AND uid = %d",
+ dbesc($size),
+ 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;
+ }
+ }
+ }
+
+ /**
+ * @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 NOT (channel_pageflags & %d)>0 LIMIT 1",
+ intval($this->auth->owner_id),
+ intval(PAGE_REMOVED)
+ );
+
+ 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 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?
+ if ($this->red_path === '/' && $name === 'cloud') {
+ //logger('We are at /cloud 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($this->ext_path, LOGGER_DEBUG);
+ $this->auth->log();
+
+ $file = $this->ext_path;
+
+ $x = strpos($file, '/cloud');
+ if ($x === false)
+ return;
+ if ($x === 0) {
+ $file = substr($file, 6);
+ }
+
+ 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 NOT ( channel_pageflags & %d )>0 LIMIT 1",
+ dbesc($channel_name),
+ intval(PAGE_REMOVED)
+ );
+
+ 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 from attach where folder = '%s' and filename = '%s' and uid = %d and (flags & %d)>0",
+ dbesc($folder),
+ dbesc($path_arr[$x]),
+ intval($channel_id),
+ intval(ATTACH_FLAG_DIR)
+ );
+
+ if ($r && ( $r[0]['flags'] & ATTACH_FLAG_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 not (channel_pageflags & %d)>0 limit 1",
+ intval($this->auth->owner_id),
+ intval(PAGE_REMOVED)
+ );
+
+ $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
new file mode 100644
index 000000000..3a5230dc1
--- /dev/null
+++ b/include/RedDAV/RedFile.php
@@ -0,0 +1,279 @@
+<?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($this->data['filename']),
+ 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 NOT (channel_pageflags & %d)>0 LIMIT 1",
+ intval($this->auth->owner_id),
+ intval(PAGE_REMOVED)
+ );
+
+ $r = q("SELECT flags, folder, data FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1",
+ dbesc($this->data['hash']),
+ intval($c[0]['channel_id'])
+ );
+ if ($r) {
+ if ($r[0]['flags'] & ATTACH_FLAG_OS) {
+ $f = 'store/' . $this->auth->owner_nick . '/' . (($r[0]['data']) ? $r[0]['data'] : '');
+ // @todo check return value and set $size directly
+ @file_put_contents($f, $data);
+ $size = @filesize($f);
+ logger('filename: ' . $f . ' size: ' . $size, LOGGER_DEBUG);
+ } else {
+ $r = q("UPDATE attach SET data = '%s' WHERE hash = '%s' AND uid = %d",
+ dbesc(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', edited = '%s' WHERE hash = '%s' AND uid = %d",
+ dbesc($size),
+ dbesc($edited),
+ dbesc($this->data['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($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);
+
+ $r = q("SELECT data, flags, 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 ($r[0]['flags'] & ATTACH_FLAG_OS ) {
+ $f = 'store/' . $this->auth->owner_nick . '/' . (($this->os_path) ? $this->os_path . '/' : '') . $r[0]['data'];
+ return fopen($f, 'rb');
+ }
+ return $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']) || ($this->data['flags'] & ATTACH_FLAG_DIR)) {
+ throw new DAV\Exception\Forbidden('Permission denied.');
+ }
+ }
+
+ attach_delete($this->auth->owner_id, $this->data['hash']);
+ }
+} \ No newline at end of file
diff --git a/include/account.php b/include/account.php
index 6cc203dc8..8df44acba 100644
--- a/include/account.php
+++ b/include/account.php
@@ -108,7 +108,7 @@ function create_account($arr) {
$parent = ((x($arr,'parent')) ? intval($arr['parent']) : 0 );
$flags = ((x($arr,'account_flags')) ? intval($arr['account_flags']) : ACCOUNT_OK);
$roles = ((x($arr,'account_roles')) ? intval($arr['account_roles']) : 0 );
- $expires = ((x($arr,'expires')) ? intval($arr['expires']) : '0000-00-00 00:00:00');
+ $expires = ((x($arr,'expires')) ? intval($arr['expires']) : NULL_DATE);
$default_service_class = get_config('system','default_service_class');
@@ -202,7 +202,7 @@ function create_account($arr) {
// Set the parent record to the current record_id if no parent was provided
if(! $parent) {
- $r = q("update account set account_parent = %d where account_id = %d limit 1",
+ $r = q("update account set account_parent = %d where account_id = %d",
intval($result['account']['account_id']),
intval($result['account']['account_id'])
);
@@ -367,16 +367,16 @@ function user_allow($hash) {
if(! $account)
return $ret;
- $r = q("DELETE FROM register WHERE hash = '%s' LIMIT 1",
+ $r = q("DELETE FROM register WHERE hash = '%s'",
dbesc($register[0]['hash'])
);
- $r = q("update account set account_flags = (account_flags ^ %d) where (account_flags & %d) and account_id = %d limit 1",
+ $r = q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d",
intval(ACCOUNT_BLOCKED),
intval(ACCOUNT_BLOCKED),
intval($register[0]['uid'])
);
- $r = q("update account set account_flags = (account_flags ^ %d) where (account_flags & %d) and account_id = %d limit 1",
+ $r = q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d",
intval(ACCOUNT_PENDING),
intval(ACCOUNT_PENDING),
intval($register[0]['uid'])
@@ -430,11 +430,11 @@ function user_deny($hash) {
if(! $account)
return false;
- $r = q("DELETE FROM account WHERE account_id = %d LIMIT 1",
+ $r = q("DELETE FROM account WHERE account_id = %d",
intval($register[0]['uid'])
);
- $r = q("DELETE FROM `register` WHERE id = %d LIMIT 1",
+ $r = q("DELETE FROM `register` WHERE id = %d",
dbesc($register[0]['id'])
);
notice( sprintf(t('Registration revoked for %s'), $account[0]['account_email']) . EOL);
@@ -463,21 +463,21 @@ function user_approve($hash) {
if(! $account)
return $ret;
- $r = q("DELETE FROM register WHERE hash = '%s' and password = 'verify' LIMIT 1",
+ $r = q("DELETE FROM register WHERE hash = '%s' and password = 'verify'",
dbesc($register[0]['hash'])
);
- $r = q("update account set account_flags = (account_flags ^ %d) where (account_flags & %d) and account_id = %d limit 1",
+ $r = q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d",
intval(ACCOUNT_BLOCKED),
intval(ACCOUNT_BLOCKED),
intval($register[0]['uid'])
);
- $r = q("update account set account_flags = (account_flags ^ %d) where (account_flags & %d) and account_id = %d limit 1",
+ $r = q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d",
intval(ACCOUNT_PENDING),
intval(ACCOUNT_PENDING),
intval($register[0]['uid'])
);
- $r = q("update account set account_flags = (account_flags ^ %d) where (account_flags & %d) and account_id = %d limit 1",
+ $r = q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d",
intval(ACCOUNT_UNVERIFIED),
intval(ACCOUNT_UNVERIFIED),
intval($register[0]['uid'])
@@ -510,10 +510,12 @@ function user_approve($hash) {
function downgrade_accounts() {
- $r = q("select * from account where not ( account_flags & %d )
- and account_expires != '0000-00-00 00:00:00'
- and account_expires < UTC_TIMESTAMP() ",
- intval(ACCOUNT_EXPIRED)
+ $r = q("select * from account where not ( account_flags & %d )>0
+ and account_expires != '%s'
+ and account_expires < %s ",
+ intval(ACCOUNT_EXPIRED),
+ dbesc(NULL_DATE),
+ db_getfunc('UTC_TIMESTAMP')
);
if(! $r)
@@ -526,9 +528,9 @@ function downgrade_accounts() {
if(($basic) && ($rr['account_service_class']) && ($rr['account_service_class'] != $basic)) {
$x = q("UPDATE account set account_service_class = '%s', account_expires = '%s'
- where account_id = %d limit 1",
+ where account_id = %d",
dbesc($basic),
- dbesc('0000-00-00 00:00:00'),
+ dbesc(NULL_DATE),
intval($rr['account_id'])
);
$ret = array('account' => $rr);
@@ -536,7 +538,7 @@ function downgrade_accounts() {
logger('downgrade_accounts: Account id ' . $rr['account_id'] . ' downgraded.');
}
else {
- $x = q("UPDATE account SET account_flags = (account_flags | %d) where account_id = %d limit 1",
+ $x = q("UPDATE account SET account_flags = (account_flags | %d) where account_id = %d",
intval(ACCOUNT_EXPIRED),
intval($rr['account_id'])
);
@@ -615,6 +617,29 @@ function service_class_fetch($uid,$property) {
return((array_key_exists($property,$arr)) ? $arr[$property] : false);
}
+// like service_class_fetch but queries by account rather than channel
+
+function account_service_class_fetch($aid,$property) {
+
+ $r = q("select account_service_class as service_class from account where account_id = %d limit 1",
+ intval($aid)
+ );
+ if($r !== false && count($r)) {
+ $service_class = $r[0]['service_class'];
+ }
+
+ if(! x($service_class))
+ return false; // everything is allowed
+
+ $arr = get_config('service_class',$service_class);
+
+ if(! is_array($arr) || (! count($arr)))
+ return false;
+
+ return((array_key_exists($property,$arr)) ? $arr[$property] : false);
+}
+
+
function upgrade_link($bbcode = false) {
$l = get_config('service_class','upgrade_link');
if(! $l)
diff --git a/include/acl_selectors.php b/include/acl_selectors.php
index 0b68ba227..243e7a549 100644
--- a/include/acl_selectors.php
+++ b/include/acl_selectors.php
@@ -171,7 +171,7 @@ function contact_select($selname, $selclass, $preselected = false, $size = 4, $p
$o .= "<select name=\"{$selname}[]\" id=\"$selclass\" class=\"$selclass\" multiple=\"multiple\" size=\"$size\" $tabindex >\r\n";
$r = q("SELECT abook_id, xchan_name, xchan_url, xchan_photo_s from abook left join xchan on abook_xchan = xchan_hash
- where abook_flags = 0 or not ( abook_flags & %d ) and abook_channel = %d
+ where abook_flags = 0 or not ( abook_flags & %d )>0 and abook_channel = %d
$sql_extra
ORDER BY xchan_name ASC ",
intval(ABOOK_FLAG_SELF),
@@ -248,8 +248,7 @@ function populate_acl($defaults = null,$show_jotnets = true) {
'$aclModalTitle' => t('Permissions'),
'$aclModalDismiss' => t('Close')
));
-
-
+
return $o;
}
diff --git a/include/api.php b/include/api.php
index c0f54af19..aeee95d3b 100644
--- a/include/api.php
+++ b/include/api.php
@@ -197,7 +197,10 @@ require_once('include/items.php');
case "json":
header ("Content-Type: application/json");
foreach($r as $rr)
- return json_encode($rr);
+ $json = json_encode($rr);
+ if ($_GET['callback'])
+ $json = $_GET['callback']."(".$json.")";
+ return $json;
break;
case "rss":
header ("Content-Type: application/rss+xml");
@@ -306,7 +309,7 @@ require_once('include/items.php');
return False;
} else {
$user = local_user();
- $extra_query = " AND abook_channel = %d AND (abook_flags & " . ABOOK_FLAG_SELF . " ) ";
+ $extra_query = " AND abook_channel = %d AND (abook_flags & " . ABOOK_FLAG_SELF . " )>0 ";
}
}
@@ -333,7 +336,7 @@ require_once('include/items.php');
// count public wall messages
$r = q("SELECT COUNT(`id`) as `count` FROM `item`
WHERE `uid` = %d
- AND ( item_flags & %d ) and item_restrict = 0
+ AND ( item_flags & %d )>0 and item_restrict = 0
AND `allow_cid`='' AND `allow_gid`='' AND `deny_cid`='' AND `deny_gid`=''",
intval($usr[0]['channel_id']),
intval(ITEM_WALL)
@@ -360,7 +363,7 @@ require_once('include/items.php');
$countfollowers = $r[0]['count'];
}
- $r = q("SELECT count(`id`) as `count` FROM item where ( item_flags & %d ) and uid = %d and item_restrict = 0",
+ $r = q("SELECT count(`id`) as `count` FROM item where ( item_flags & %d )>0 and uid = %d and item_restrict = 0",
intval($uinfo[0]['channel_id']),
intval(ITEM_STARRED)
);
@@ -546,8 +549,8 @@ require_once('include/items.php');
}
require_once('include/identity.php');
-
- json_return_and_die(identity_basic_export(api_user()));
+
+ json_return_and_die(identity_basic_export(api_user(),(($_REQUEST['posts']) ? intval($_REQUEST['posts']) : 0 )));
}
api_register_func('api/export/basic','api_export_basic', true);
api_register_func('api/red/channel/export/basic','api_export_basic', true);
@@ -582,10 +585,55 @@ require_once('include/items.php');
api_register_func('api/red/photos','api_photos', true);
+ function api_group_members(&$a,$type) {
+ if(api_user() === false)
+ return false;
+
+ if($_REQUEST['group_id']) {
+ $r = q("select * from groups where uid = %d and id = %d limit 1",
+ intval(api_user()),
+ intval($_REQUEST['group_id'])
+ );
+ if($r) {
+ $x = q("select * from group_member left join xchan on group_member.xchan = xchan.xchan_hash
+ left join abook on abook_xchan = xchan_hash where gid = %d",
+ intval($_REQUEST['group_id'])
+ );
+ json_return_and_die($x);
+ }
+ }
+ }
+
+ api_register_func('api/red/group_members','api_group_members', true);
+
+ function api_group(&$a,$type) {
+ if(api_user() === false)
+ return false;
+
+ $r = q("select * from groups where uid = %d",
+ intval(api_user())
+ );
+ json_return_and_die($r);
+ }
+ api_register_func('api/red/group','api_group', true);
+
+ function api_red_xchan(&$a,$type) {
+ if(api_user() === false)
+ return false;
+ require_once('include/hubloc.php');
+ if($_SERVER['request_method'] === 'POST') {
+ $r = xchan_store($_REQUEST);
+ }
+ $r = xchan_fetch($_REQUEST);
+ json_return_and_die($r);
+ };
+
+ api_register_func('api/red/xchan','api_red_xchan',true);
+
function api_statuses_mediap(&$a, $type) {
if (api_user() === false) {
@@ -956,8 +1004,8 @@ require_once('include/items.php');
// at the network timeline just mark everything seen.
if (api_user() == $user_info['uid']) {
- $r = q("UPDATE `item` SET item_flags = ( item_flags ^ %d )
- WHERE item_flags & %d and uid = %d",
+ $r = q("UPDATE `item` SET item_flags = ( item_flags & ~%d )
+ WHERE (item_flags & %d)>0 and uid = %d",
intval(ITEM_UNSEEN),
intval(ITEM_UNSEEN),
intval($user_info['uid'])
@@ -1014,10 +1062,10 @@ require_once('include/items.php');
and uid in ( " . stream_perms_api_uids() . " )
$sql_extra
AND id > %d group by mid
- order by received desc LIMIT %d, %d ",
+ order by received desc LIMIT %d OFFSET %d ",
intval($since_id),
- intval($start),
- intval($count)
+ intval($count),
+ intval($start)
);
xchan_query($r,true);
@@ -1658,9 +1706,9 @@ require_once('include/items.php');
// For Red, the closest thing we can do to figure out if you're friends is if both of you are sending each other your streams.
// This won't work if either of you send your stream to everybody on the network
if($qtype == 'friends')
- $sql_extra = sprintf(" AND ( abook_their_perms & %d ) and ( abook_my_perms & %d ) ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM));
+ $sql_extra = sprintf(" AND ( abook_their_perms & %d )>0 and ( abook_my_perms & %d )>0 ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM));
if($qtype == 'followers')
- $sql_extra = sprintf(" AND ( abook_my_perms & %d ) and not ( abook_their_perms & %d ) ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM));
+ $sql_extra = sprintf(" AND ( abook_my_perms & %d )>0 and not ( abook_their_perms & %d )>0 ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM));
$r = q("SELECT abook_id FROM abook where abook_flags = 0 and abook_channel = %d $sql_extra",
intval(api_user())
@@ -1774,9 +1822,9 @@ require_once('include/items.php');
// This won't work if either of you send your stream to everybody on the network
if($qtype == 'friends')
- $sql_extra = sprintf(" AND ( abook_their_perms & %d ) and ( abook_my_perms & %d ) ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM));
+ $sql_extra = sprintf(" AND ( abook_their_perms & %d )>0 and ( abook_my_perms & %d )>0 ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM));
if($qtype == 'followers')
- $sql_extra = sprintf(" AND ( abook_my_perms & %d ) and not ( abook_their_perms & %d ) ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM));
+ $sql_extra = sprintf(" AND ( abook_my_perms & %d )>0 and not ( abook_their_perms & %d )>0 ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM));
$r = q("SELECT abook_id FROM abook where abook_flags = 0 and abook_channel = %d $sql_extra",
intval(api_user())
@@ -1892,9 +1940,9 @@ require_once('include/items.php');
$sql_extra = "`from-url`!='".dbesc( $profile_url )."'";
}
- $r = q("SELECT * FROM `mail` WHERE uid=%d AND $sql_extra ORDER BY created DESC LIMIT %d,%d",
+ $r = q("SELECT * FROM `mail` WHERE uid=%d AND $sql_extra ORDER BY created DESC LIMIT %d OFFSET %d",
intval(api_user()),
- intval($start), intval($count)
+ intval($count), intval($start)
);
$ret = Array();
diff --git a/include/apps.php b/include/apps.php
index 91012b0ef..9c4fe826a 100644
--- a/include/apps.php
+++ b/include/apps.php
@@ -11,7 +11,10 @@ require_once('include/identity.php');
function get_system_apps() {
$ret = array();
- $files = glob('app/*.apd');
+ if(is_dir('apps'))
+ $files = glob('apps/*.apd');
+ else
+ $files = glob('app/*.apd');
if($files) {
foreach($files as $f) {
$x = parse_app_description($f);
@@ -264,7 +267,7 @@ function app_install($uid,$app) {
function app_destroy($uid,$app) {
if($uid && $app['guid']) {
- $r = q("delete from app where app_id = '%s' and app_channel = %d limit 1",
+ $r = q("delete from app where app_id = '%s' and app_channel = %d",
dbesc($app['guid']),
intval($uid)
);
@@ -385,7 +388,7 @@ function app_update($arr) {
$darray['app_page'] = ((x($arr,'page')) ? escape_tags($arr['page']) : '');
$darray['app_requires'] = ((x($arr,'requires')) ? escape_tags($arr['requires']) : '');
- $r = q("update app set app_sig = '%s', app_author = '%s', app_name = '%s', app_desc = '%s', app_url = '%s', app_photo = '%s', app_version = '%s', app_addr = '%s', app_price = '%s', app_page = '%s', app_requires = '%s' where app_id = '%s' and app_channel = %d limit 1",
+ $r = q("update app set app_sig = '%s', app_author = '%s', app_name = '%s', app_desc = '%s', app_url = '%s', app_photo = '%s', app_version = '%s', app_addr = '%s', app_price = '%s', app_page = '%s', app_requires = '%s' where app_id = '%s' and app_channel = %d",
dbesc($darray['app_sig']),
dbesc($darray['app_author']),
dbesc($darray['app_name']),
diff --git a/include/attach.php b/include/attach.php
index 0df2e82a5..ad6ca1b21 100644
--- a/include/attach.php
+++ b/include/attach.php
@@ -26,77 +26,74 @@ function z_mime_content_type($filename) {
$mime_types = array(
- 'txt' => 'text/plain',
- 'htm' => 'text/html',
- 'html' => 'text/html',
- 'php' => 'text/html',
- 'css' => 'text/css',
- 'js' => 'application/javascript',
- 'json' => 'application/json',
- 'xml' => 'application/xml',
- 'swf' => 'application/x-shockwave-flash',
- 'flv' => 'video/x-flv',
- 'epub' => 'application/epub+zip',
-
- // images
- 'png' => 'image/png',
- 'jpe' => 'image/jpeg',
- 'jpeg' => 'image/jpeg',
- 'jpg' => 'image/jpeg',
- 'gif' => 'image/gif',
- 'bmp' => 'image/bmp',
- 'ico' => 'image/vnd.microsoft.icon',
- 'tiff' => 'image/tiff',
- 'tif' => 'image/tiff',
- 'svg' => 'image/svg+xml',
- 'svgz' => 'image/svg+xml',
-
- // archives
- 'zip' => 'application/zip',
- 'rar' => 'application/x-rar-compressed',
- 'exe' => 'application/x-msdownload',
- 'msi' => 'application/x-msdownload',
- 'cab' => 'application/vnd.ms-cab-compressed',
-
- // audio/video
- 'mp3' => 'audio/mpeg',
- 'wav' => 'audio/wav',
- 'qt' => 'video/quicktime',
- 'mov' => 'video/quicktime',
- 'ogg' => 'application/ogg',
-
- // adobe
- 'pdf' => 'application/pdf',
- 'psd' => 'image/vnd.adobe.photoshop',
- 'ai' => 'application/postscript',
- 'eps' => 'application/postscript',
- 'ps' => 'application/postscript',
-
- // ms office
- 'doc' => 'application/msword',
- 'rtf' => 'application/rtf',
- 'xls' => 'application/vnd.ms-excel',
- 'ppt' => 'application/vnd.ms-powerpoint',
-
-
- // open office
- 'odt' => 'application/vnd.oasis.opendocument.text',
- 'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
+ 'txt' => 'text/plain',
+ 'htm' => 'text/html',
+ 'html' => 'text/html',
+ 'php' => 'text/html',
+ 'css' => 'text/css',
+ 'js' => 'application/javascript',
+ 'json' => 'application/json',
+ 'xml' => 'application/xml',
+ 'swf' => 'application/x-shockwave-flash',
+ 'flv' => 'video/x-flv',
+ 'epub' => 'application/epub+zip',
+
+ // images
+ 'png' => 'image/png',
+ 'jpe' => 'image/jpeg',
+ 'jpeg' => 'image/jpeg',
+ 'jpg' => 'image/jpeg',
+ 'gif' => 'image/gif',
+ 'bmp' => 'image/bmp',
+ 'ico' => 'image/vnd.microsoft.icon',
+ 'tiff' => 'image/tiff',
+ 'tif' => 'image/tiff',
+ 'svg' => 'image/svg+xml',
+ 'svgz' => 'image/svg+xml',
+
+ // archives
+ 'zip' => 'application/zip',
+ 'rar' => 'application/x-rar-compressed',
+ 'exe' => 'application/x-msdownload',
+ 'msi' => 'application/x-msdownload',
+ 'cab' => 'application/vnd.ms-cab-compressed',
+
+ // audio/video
+ 'mp3' => 'audio/mpeg',
+ 'wav' => 'audio/wav',
+ 'qt' => 'video/quicktime',
+ 'mov' => 'video/quicktime',
+ 'ogg' => 'application/ogg',
+
+ // adobe
+ 'pdf' => 'application/pdf',
+ 'psd' => 'image/vnd.adobe.photoshop',
+ 'ai' => 'application/postscript',
+ 'eps' => 'application/postscript',
+ 'ps' => 'application/postscript',
+
+ // ms office
+ 'doc' => 'application/msword',
+ 'rtf' => 'application/rtf',
+ 'xls' => 'application/vnd.ms-excel',
+ 'ppt' => 'application/vnd.ms-powerpoint',
+
+ // open office
+ 'odt' => 'application/vnd.oasis.opendocument.text',
+ 'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
);
- $dot = strpos($filename,'.');
- if($dot !== false) {
- $ext = strtolower(substr($filename,$dot+1));
+ $dot = strpos($filename, '.');
+ if ($dot !== false) {
+ $ext = strtolower(substr($filename, $dot + 1));
if (array_key_exists($ext, $mime_types)) {
return $mime_types[$ext];
}
}
return 'application/octet-stream';
-
}
-
/**
* @brief Count files/attachments.
*
@@ -138,8 +135,8 @@ function attach_count_files($channel_id, $observer, $hash = '', $filename = '',
$ret['success'] = ((is_array($r)) ? true : false);
$ret['results'] = ((is_array($r)) ? count($r) : false);
- return $ret;
+ return $ret;
}
/**
@@ -190,8 +187,8 @@ function attach_list_files($channel_id, $observer, $hash = '', $filename = '', $
$ret['success'] = ((is_array($r)) ? true : false);
$ret['results'] = ((is_array($r)) ? $r : false);
- return $ret;
+ return $ret;
}
/**
@@ -246,8 +243,8 @@ function attach_by_hash($hash, $rev = 0) {
$ret['success'] = true;
$ret['data'] = $r[0];
- return $ret;
+ return $ret;
}
/**
@@ -301,7 +298,6 @@ function attach_by_hash_nodata($hash, $rev = 0) {
$ret['success'] = true;
$ret['data'] = $r[0];
return $ret;
-
}
/**
@@ -400,14 +396,15 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
if(! isset($hash))
$hash = random_string();
+
$created = datetime_convert();
if($options === 'replace') {
- $r = q("update attach set filename = '%s', filetype = '%s', filesize = %d, data = '%s', edited = '%s' where id = %d and uid = %d limit 1",
+ $r = q("update attach set filename = '%s', filetype = '%s', filesize = %d, data = '%s', edited = '%s' where id = %d and uid = %d",
dbesc($filename),
dbesc($mimetype),
intval($filesize),
- dbesc(@file_get_contents($src)),
+ dbescbin(@file_get_contents($src)),
dbesc($created),
intval($existing_id),
intval($channel_id)
@@ -424,7 +421,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
dbesc($mimetype),
intval($filesize),
intval($x[0]['revision'] + 1),
- dbesc(@file_get_contents($src)),
+ dbescbin(@file_get_contents($src)),
dbesc($created),
dbesc($created),
dbesc($x[0]['allow_cid']),
@@ -432,10 +429,10 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
dbesc($x[0]['deny_cid']),
dbesc($x[0]['deny_gid'])
);
- }
+ }
elseif($options === 'update') {
$r = q("update attach set filename = '%s', filetype = '%s', edited = '%s',
- allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where id = %d and uid = %d limit 1",
+ allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where id = %d and uid = %d",
dbesc((array_key_exists('filename',$arr)) ? $arr['filename'] : $x[0]['filename']),
dbesc((array_key_exists('filetype',$arr)) ? $arr['filetype'] : $x[0]['filetype']),
dbesc($created),
@@ -446,7 +443,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
intval($x[0]['id']),
intval($x[0]['uid'])
);
- }
+ }
else {
$r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, filesize, revision, data, created, edited, allow_cid, allow_gid,deny_cid, deny_gid )
VALUES ( %d, %d, '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ",
@@ -458,7 +455,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
dbesc($mimetype),
intval($filesize),
intval(0),
- dbesc(@file_get_contents($src)),
+ dbescbin(@file_get_contents($src)),
dbesc($created),
dbesc($created),
dbesc(($arr && array_key_exists('allow_cid',$arr)) ? $arr['allow_cid'] : '<' . $channel['channel_hash'] . '>'),
@@ -466,7 +463,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
dbesc(($arr && array_key_exists('deny_cid',$arr)) ? $arr['deny_cid'] : ''),
dbesc(($arr && array_key_exists('deny_gid',$arr)) ? $arr['deny_gid'] : '')
);
- }
+ }
if($options !== 'update')
@unlink($src);
@@ -490,6 +487,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
$ret['success'] = true;
$ret['data'] = $r[0];
+
return $ret;
}
@@ -507,8 +505,8 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
* $ret['data'] = array of attach DB entries without data component
*/
function z_readdir($channel_id, $observer_hash, $pathname, $parent_hash = '') {
-
$ret = array('success' => false);
+
if(! perm_is_allowed($r[0]['uid'], get_observer_hash(), 'view_storage')) {
$ret['message'] = t('Permission denied.');
return $ret;
@@ -519,7 +517,7 @@ function z_readdir($channel_id, $observer_hash, $pathname, $parent_hash = '') {
if(count($paths) > 1) {
$curpath = array_shift($paths);
- $r = q("select hash, id from attach where uid = %d and filename = '%s' and (flags & %d ) " . permissions_sql($channel_id) . " limit 1",
+ $r = q("select hash, id from attach where uid = %d and filename = '%s' and (flags & %d )>0 " . permissions_sql($channel_id) . " limit 1",
intval($channel_id),
dbesc($curpath),
intval(ATTACH_FLAG_DIR)
@@ -535,7 +533,7 @@ function z_readdir($channel_id, $observer_hash, $pathname, $parent_hash = '') {
else
$paths = array($pathname);
- $r = q("select id, aid, uid, hash, creator, filename, filetype, filesize, revision, folder, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where id = %d and folder = '%s' and filename = '%s' and (flags & %d ) " . permissions_sql($channel_id),
+ $r = q("select id, aid, uid, hash, creator, filename, filetype, filesize, revision, folder, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where id = %d and folder = '%s' and filename = '%s' and (flags & %d )>0 " . permissions_sql($channel_id),
intval($channel_id),
dbesc($parent_hash),
dbesc($paths[0]),
@@ -547,6 +545,7 @@ function z_readdir($channel_id, $observer_hash, $pathname, $parent_hash = '') {
}
$ret['success'] = true;
$ret['data'] = $r;
+
return $ret;
}
@@ -568,6 +567,7 @@ function z_readdir($channel_id, $observer_hash, $pathname, $parent_hash = '') {
* $arr['deny_cid']
* $arr['deny_gid']
*/
+
function attach_mkdir($channel, $observer_hash, $arr = null) {
$ret = array('success' => false);
@@ -618,7 +618,7 @@ function attach_mkdir($channel, $observer_hash, $arr = null) {
$sql_options = permissions_sql($channel['channel_id']);
do {
- $r = q("select filename, hash, flags, folder from attach where uid = %d and hash = '%s' and ( flags & %d )
+ $r = q("select filename, hash, flags, folder from attach where uid = %d and hash = '%s' and ( flags & %d )>0
$sql_options limit 1",
intval($channel['channel_id']),
dbesc($lfile),
@@ -670,7 +670,7 @@ function attach_mkdir($channel, $observer_hash, $arr = null) {
$ret['data'] = $arr;
// update the parent folder's lastmodified timestamp
- $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d LIMIT 1",
+ $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d",
dbesc($created),
dbesc($arr['folder']),
intval($channel_id)
@@ -686,7 +686,6 @@ function attach_mkdir($channel, $observer_hash, $arr = null) {
}
return $ret;
-
}
/**
@@ -724,7 +723,7 @@ function attach_change_permissions($channel_id, $resource, $allow_cid, $allow_gi
}
}
- $x = q("update attach set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where hash = '%s' and uid = %d limit 1",
+ $x = q("update attach set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where hash = '%s' and uid = %d",
dbesc($allow_cid),
dbesc($allow_gid),
dbesc($deny_cid),
@@ -732,15 +731,19 @@ function attach_change_permissions($channel_id, $resource, $allow_cid, $allow_gi
dbesc($resource),
intval($channel_id)
);
-
- return;
}
/**
- * @brief Delete a file/directory.
- *
+ * @brief Delete a file/directory from a channel.
+ *
+ * If the provided resource hash is from a directory it will delete everything
+ * recursively under this directory.
+ *
* @param int $channel_id
- * @param string $resource a hash to delete
+ * The id of the channel
+ * @param string $resource
+ * The hash to delete
+ * @return void
*/
function attach_delete($channel_id, $resource) {
@@ -760,7 +763,7 @@ function attach_delete($channel_id, $resource) {
// If resource is a directory delete everything in the directory recursive
if($r[0]['flags'] & ATTACH_FLAG_DIR) {
- $x = q("select hash, flags from attach where folder = '%s' and uid = %d",
+ $x = q("SELECT hash, flags FROM attach WHERE folder = '%s' AND uid = %d",
dbesc($resource),
intval($channel_id)
);
@@ -788,30 +791,32 @@ function attach_delete($channel_id, $resource) {
}
// delete from database
- $z = q("DELETE FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1",
+ $z = q("DELETE FROM attach WHERE hash = '%s' AND uid = %d",
dbesc($resource),
intval($channel_id)
);
// update the parent folder's lastmodified timestamp
- $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d LIMIT 1",
+ $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d",
dbesc(datetime_convert()),
dbesc($r[0]['folder']),
intval($channel_id)
);
-
- return;
}
/**
* @brief Returns path to file in cloud/.
*
- * @param $arr
- * @return string with the path the file to cloud/
+ * @param array
+ * $arr[uid] int the channels uid
+ * $arr[folder] string
+ * $arr[filename]] string
+ * @return string
+ * path to the file in cloud/
*/
function get_cloudpath($arr) {
-
$basepath = 'cloud/';
+
if($arr['uid']) {
$r = q("select channel_address from channel where channel_id = %d limit 1",
intval($arr['uid'])
@@ -823,12 +828,11 @@ function get_cloudpath($arr) {
$path = $basepath;
if($arr['folder']) {
-
$lpath = '';
$lfile = $arr['folder'];
do {
- $r = q("select filename, hash, flags, folder from attach where uid = %d and hash = '%s' and ( flags & %d )
+ $r = q("select filename, hash, flags, folder from attach where uid = %d and hash = '%s' and ( flags & %d )>0
limit 1",
intval($arr['uid']),
dbesc($lfile),
@@ -842,60 +846,83 @@ function get_cloudpath($arr) {
$lpath = $r[0]['filename'] . '/' . $lpath;
$lfile = $r[0]['folder'];
- } while ( ($r[0]['folder']) && ($r[0]['flags'] & ATTACH_FLAG_DIR)) ;
+ } while ( ($r[0]['folder']) && ($r[0]['flags'] & ATTACH_FLAG_DIR));
- $path .= $lpath;
+ $path .= $lpath;
}
-
$path .= $arr['filename'];
+
return $path;
}
/**
* @brief Returns path to parent folder in cloud/.
- *
- * @param $arr
- * @return string with the folder path
+ *
+ * @param int $channel_id
+ * The id of the channel
+ * @param string $channel_name
+ * The name of the channel
+ * @param string $attachHash
+ * @return string with the full folder path
*/
function get_parent_cloudpath($channel_id, $channel_name, $attachHash) {
- //Build directory tree and redirect
+ // build directory tree
$parentHash = $attachHash;
do {
$parentHash = find_folder_hash_by_attach_hash($channel_id, $parentHash);
if ($parentHash) {
$parentName = find_filename_by_hash($channel_id, $parentHash);
- $parentFullPath = $parentName."/".$parentFullPath;
+ $parentFullPath = $parentName . '/' . $parentFullPath;
}
} while ($parentHash);
- $parentFullPath = z_root() . "/cloud/" . $channel_name . "/" . $parentFullPath;
+ $parentFullPath = z_root() . '/cloud/' . $channel_name . '/' . $parentFullPath;
+
return $parentFullPath;
}
+
+/**
+ * @brief Return the hash of an attachment's folder.
+ *
+ * @param int $channel_id
+ * The id of the channel
+ * @param string $attachHash
+ * The hash of the attachment
+ * @return string
+ */
function find_folder_hash_by_attach_hash($channel_id, $attachHash) {
- $r = q("select * from attach where uid = %d and hash = '%s' limit 1",
- intval($channel_id), dbesc($attachHash)
+ $r = q("SELECT folder FROM attach WHERE uid = %d AND hash = '%s' LIMIT 1",
+ intval($channel_id),
+ dbesc($attachHash)
);
- $hash = "";
- if($r) {
- foreach($r as $rr) {
- $hash = $rr['folder'];
- }
+ $hash = '';
+ if ($r) {
+ $hash = $r[0]['folder'];
}
- return $hash;
+ return $hash;
}
+
+/**
+ * @brief Returns the filename of an attachment in a given channel.
+ *
+ * @param mixed $channel_id
+ * The id of the channel
+ * @param mixed $attachHash
+ * The hash of the attachment
+ * @return string
+ * The filename of the attachment
+ */
function find_filename_by_hash($channel_id, $attachHash) {
- $r = q("select * from attach where uid = %d and hash = '%s' limit 1",
- intval($channel_id), dbesc($attachHash)
+ $r = q("SELECT filename FROM attach WHERE uid = %d AND hash = '%s' LIMIT 1",
+ intval($channel_id),
+ dbesc($attachHash)
);
- $filename = "";
- if($r) {
- foreach($r as $rr) {
- $filename = $rr['filename'];
- }
+ $filename = '';
+ if ($r) {
+ $filename = $r[0]['filename'];
}
- return $filename;
+ return $filename;
}
-
/**
*
* @param $in
@@ -904,6 +931,6 @@ function find_filename_by_hash($channel_id, $attachHash) {
function pipe_streams($in, $out) {
$size = 0;
while (!feof($in))
- $size += fwrite($out, fread($in,8192));
+ $size += fwrite($out, fread($in, 8192));
return $size;
}
diff --git a/include/auth.php b/include/auth.php
index cc07917b7..545fbe8c9 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,27 @@ function nuke_session() {
}
/**
- * Verify login credentials
+ * @brief Verify login credentials.
*
- * Returns account record on success, null on failure
+ * If system <i>authlog</i> is set a log entry will be added for failed login
+ * attempts.
*
+ * @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,23 +69,47 @@ 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;
}
}
$error = 'password failed for ' . $email;
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);
+
+ if($record['account_flags'] & ACCOUNT_UNVERIFIED)
+ logger('Account is unverified. account_flags = ' . $record['account_flags']);
+ if($record['account_flags'] & ACCOUNT_BLOCKED)
+ logger('Account is blocked. account_flags = ' . $record['account_flags']);
+ if($record['account_flags'] & ACCOUNT_EXPIRED)
+ logger('Account is expired. account_flags = ' . $record['account_flags']);
+ if($record['account_flags'] & ACCOUNT_REMOVED)
+ logger('Account is removed. account_flags = ' . $record['account_flags']);
+ if($record['account_flags'] & ACCOUNT_PENDING)
+ logger('Account is pending. account_flags = ' . $record['account_flags']);
+
+ log_failed_login($error);
return null;
}
+/**
+ * @brief Log failed logins to a separate auth log.
+ *
+ * Can be used to reduce overhead for server side intrusion prevention, like
+ * parse the authlog file with something like fail2ban, OSSEC, etc.
+ *
+ * @param string $errormsg
+ * Error message to display for failed login.
+ */
+function log_failed_login($errormsg) {
+ $authlog = get_config('system', 'authlog');
+ if ($authlog)
+ @file_put_contents($authlog, datetime_convert() . ':' . session_id() . ' ' . $errormsg . PHP_EOL, FILE_APPEND);
+}
/**
* Inline - not a function
@@ -75,14 +117,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);
@@ -93,16 +133,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)) {
- $x = q("select * from account where account_email = '%s' and account_email != '' and ( account_flags & %d ) limit 1",
- dbesc(get_config('system','admin_email')),
+ $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 )>0 limit 1",
+ 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);
@@ -124,20 +164,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:
@@ -145,8 +184,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:
@@ -156,12 +195,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;
-
}
}
@@ -178,17 +216,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 {
@@ -198,10 +234,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;
@@ -226,8 +262,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'];
@@ -236,21 +271,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).
@@ -280,11 +314,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/bb2diaspora.php b/include/bb2diaspora.php
index 5fb708706..3c4f07568 100644
--- a/include/bb2diaspora.php
+++ b/include/bb2diaspora.php
@@ -79,19 +79,30 @@ function share_unshield($m) {
function diaspora_mention_callback($matches) {
- $webbie = $matches[2];
+ $webbie = $matches[2] . '@' . $matches[3];
$link = '';
if($webbie) {
$r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_addr = '%s' limit 1",
dbesc($webbie)
);
+ if(! $r) {
+ $x = discover_by_webbie($webbie);
+ if($x) {
+ $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_addr = '%s' limit 1",
+ dbesc($webbie)
+ );
+ }
+ }
if($r)
$link = $r[0]['xchan_url'];
}
if(! $link)
$link = 'https://' . $matches[3] . '/u/' . $matches[2];
- return '@[url=' . $link . ']' . trim($matches[1]) . '[/url]';
+ if($r && $r[0]['hubloc_network'] === 'zot')
+ return '@[zrl=' . $link . ']' . trim($matches[1]) . ((substr($matches[0],-1,1) === '+') ? '+' : '') . '[/zrl]' ;
+ else
+ return '@[url=' . $link . ']' . trim($matches[1]) . ((substr($matches[0],-1,1) === '+') ? '+' : '') . '[/url]' ;
}
@@ -105,6 +116,8 @@ function diaspora_mention_callback($matches) {
function diaspora2bb($s,$use_zrl = false) {
+ $s = str_replace("&#xD;\n&gt;","",$s);
+
$s = html_entity_decode($s,ENT_COMPAT,'UTF-8');
// Too many new lines. So deactivated the following line
@@ -119,6 +132,10 @@ function diaspora2bb($s,$use_zrl = false) {
// $s = preg_replace('/\@\{(.+?)\; (.+?)\@(.+?)\}/','@[url=https://$3/u/$2]$1[/url]',$s);
+ // first try plustags
+
+ $s = preg_replace_callback('/\@\{(.+?)\; (.+?)\@(.+?)\}\+/','diaspora_mention_callback',$s);
+
$s = preg_replace_callback('/\@\{(.+?)\; (.+?)\@(.+?)\}/','diaspora_mention_callback',$s);
// Escaping the hash tags - doesn't always seem to work
@@ -246,26 +263,95 @@ function bb2dmention_callback($match) {
}
+function bb2diaspora_itemwallwall(&$item) {
+
+ $author_exists = true;
+ if(! array_key_exists('author',$item)) {
+ $author_exists = false;
+ logger('bb2diaspora_itemwallwall: no author');
+ $r = q("select * from xchan where xchan_hash = '%s' limit 1",
+ dbesc($item['author_xchan'])
+ );
+ if($r)
+ $item['author'] = $r[0];
+ }
+
+ if(($item['mid'] == $item['parent_mid']) && ($item['author_xchan'] != $item['owner_xchan']) && (is_array($item['author']))) {
+ logger('bb2diaspora_itemwallwall: author: ' . print_r($item['author'],true), LOGGER_DATA);
+ }
+
+ if(($item['mid'] == $item['parent_mid']) && ($item['author_xchan'] != $item['owner_xchan']) && (is_array($item['author'])) && $item['author']['xchan_url'] && $item['author']['xchan_name'] && $item['author']['xchan_photo_m']) {
+ logger('bb2diaspora_itemwallwall: wall to wall post',LOGGER_DEBUG);
+ // post will come across with the owner's identity. Throw a preamble onto the post to indicate the true author.
+ $item['body'] = "\n\n"
+ . '[img]' . $item['author']['xchan_photo_m'] . '[/img]'
+ . '[url=' . $item['author']['xchan_url'] . ']' . $item['author']['xchan_name'] . '[/url]' . "\n\n"
+ . $item['body'];
+ }
+
+ // We have to do something similar for wall-to-wall comments. ITEM_WALL|ITEM_ORIGIN indicates that it was posted on this site.
+ // Regular matrix comments may have one of these bits set, but not both.
+
+ // Update: this is getting triggered way too often and unnecessarily. Commenting out until we find a better solution.
+ // It's not an easy problem. For now we'll live with the mis-attributions, as wall to wall comments are much less frequent
+ // than wall-to-wall posts.
+
+// if(($item['mid'] != $item['parent_mid']) && ($item['author_xchan'] != $item['owner_xchan']) && (($item['item_flags'] & (ITEM_WALL|ITEM_ORIGIN)) == (ITEM_WALL|ITEM_ORIGIN)) && (is_array($item['author'])) && $item['author']['xchan_url'] && $item['author']['xchan_name'] && $item['author']['xchan_photo_m']) {
+// logger('bb2diaspora_itemwallwall: wall to wall comment',LOGGER_DEBUG);
+ // post will come across with the owner's identity. Throw a preamble onto the post to indicate the true author.
+// $item['body'] = "\n\n"
+// . '[img]' . $item['author']['xchan_photo_m'] . '[/img]'
+// . '[url=' . $item['author']['xchan_url'] . ']' . $item['author']['xchan_name'] . '[/url]' . "\n\n"
+// . $item['body'];
+// }
+
+ // $item['author'] might cause a surprise further down the line if it wasn't expected to be here.
+
+ if(! $author_exists)
+ $unset($item['author']);
+
+}
-function bb2diaspora_itembody($item) {
- if($item['diaspora_meta']) {
- $j = json_decode($item['diaspora_meta'],true);
- if($j && $j['body']) {
- logger('bb2diaspora_itembody: cached ');
- return $j['body'];
+function bb2diaspora_itembody($item,$force_update = false) {
+
+ if(($item['diaspora_meta']) && (! $force_update)) {
+ $diaspora_meta = json_decode($item['diaspora_meta'],true);
+ if($diaspora_meta) {
+ if(array_key_exists('iv',$diaspora_meta)) {
+ $key = get_config('system','prvkey');
+ $meta = json_decode(crypto_unencapsulate($diaspora_meta,$key),true);
+ }
+ else {
+ $meta = $diaspora_meta;
+ }
+ if($meta) {
+ logger('bb2diaspora_itembody: cached ');
+ $newitem = $item;
+ $newitem['body'] = $meta['body'];
+// this won't work - the post is now in markdown
+// bb2diaspora_itemwallwall($newitem);
+ return $newitem['body'];
+ }
}
}
- $body = $item['body'];
+ $newitem = $item;
if(array_key_exists('item_flags',$item) && ($item['item_flags'] & ITEM_OBSCURED)) {
$key = get_config('system','prvkey');
- $title = (($item['title']) ? crypto_unencapsulate(json_decode($item['title'],true),$key) : '');
- $body = (($item['body']) ? crypto_unencapsulate(json_decode($item['body'],true),$key) : '');
+ $b = json_decode($item['body'],true);
+ // if called from diaspora_process_outbound, this decoding has already been done.
+ // Everything else that calls us will not yet be decoded.
+ if($b && is_array($b) && array_key_exists('iv',$b)) {
+ $newitem['title'] = (($item['title']) ? crypto_unencapsulate(json_decode($item['title'],true),$key) : '');
+ $newitem['body'] = (($item['body']) ? crypto_unencapsulate(json_decode($item['body'],true),$key) : '');
+ }
}
- $body = preg_replace('/\#\^http/i', 'http', $body);
+ bb2diaspora_itemwallwall($newitem);
+
+ $body = preg_replace('/\#\^http/i', 'http', $newitem['body']);
// protect tags and mentions from hijacking
@@ -302,7 +388,7 @@ function bb2diaspora_itembody($item) {
}
}
- logger('bb2diaspora_itembody : ' . $body);
+// logger('bb2diaspora_itembody : ' . $body, LOGGER_DATA);
return html_entity_decode($body);
@@ -376,7 +462,7 @@ function format_event_diaspora($ev) {
$bd_format = t('l F d, Y \@ g:i A') ; // Friday January 18, 2011 @ 8 AM
- $o = 'Friendica event notification:' . "\n";
+ $o = t('Redmatrix event notification:') . "\n";
$o .= '**' . (($ev['summary']) ? bb2diaspora($ev['summary']) : bb2diaspora($ev['desc'])) . '**' . "\n";
diff --git a/include/bbcode.php b/include/bbcode.php
index a7055fc45..1037db045 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="#" 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') . '" />';
}
@@ -270,6 +278,17 @@ function rpost_callback($match) {
}
}
+function bb_map_coords($match) {
+ // the extra space in the following line is intentional
+ return str_replace($match[0],'<div class="map" >' . generate_map(str_replace('/',' ',$match[1])) . '</div>', $match[0]);
+}
+
+function bb_map_location($match) {
+ // the extra space in the following line is intentional
+ return str_replace($match[0],'<div class="map" >' . generate_named_map($match[1]) . '</div>', $match[0]);
+}
+
+
function bb_sanitize_style($input) {
//whitelist property limits (0 = no limitation)
$w = array( // color properties
@@ -423,14 +442,16 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true) {
// replace [observer.baseurl]
if ($observer) {
+ $s1 = '<span class="bb_observer">';
+ $s2 = '</span>';
$obsBaseURL = $observer['xchan_connurl'];
$obsBaseURL = preg_replace("/\/poco\/.*$/", '', $obsBaseURL);
$Text = str_replace('[observer.baseurl]', $obsBaseURL, $Text);
$Text = str_replace('[observer.url]',$observer['xchan_url'], $Text);
- $Text = str_replace('[observer.name]',$observer['xchan_name'], $Text);
- $Text = str_replace('[observer.address]',$observer['xchan_addr'], $Text);
- $Text = str_replace('[observer.webname]',substr($observer['xchan_addr'],0,strpos($observer['xchan_addr'],'@')), $Text);
- $Text = str_replace('[observer.photo]','[zmg]'.$observer['xchan_photo_l'].'[/zmg]', $Text);
+ $Text = str_replace('[observer.name]',$s1 . $observer['xchan_name'] . $s2, $Text);
+ $Text = str_replace('[observer.address]',$s1 . $observer['xchan_addr'] . $s2, $Text);
+ $Text = str_replace('[observer.webname]',$s1 . substr($observer['xchan_addr'],0,strpos($observer['xchan_addr'],'@')) . $s2, $Text);
+ $Text = str_replace('[observer.photo]',$s1 . '[zmg]'.$observer['xchan_photo_l'].'[/zmg]' . $s2, $Text);
} else {
$Text = str_replace('[observer.baseurl]', '', $Text);
$Text = str_replace('[observer.url]','', $Text);
@@ -482,6 +503,22 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true) {
$Text = preg_replace("/\[mail\]([$MAILSearchString]*)\[\/mail\]/", '<a href="mailto:$1">$1</a>', $Text);
$Text = preg_replace("/\[mail\=([$MAILSearchString]*)\](.*?)\[\/mail\]/", '<a href="mailto:$1">$2</a>', $Text);
}
+
+ // leave open the posibility of [map=something]
+ // this is replaced in prepare_body() which has knowledge of the item location
+
+ if (strpos($Text,'[/map]') !== false) {
+ $Text = preg_replace_callback("/\[map\](.*?)\[\/map\]/ism",'bb_map_location',$Text);
+ }
+
+ if (strpos($Text,'[map=') !== false) {
+ $Text = preg_replace_callback("/\[map=(.*?)\]/ism",'bb_map_coords',$Text);
+ }
+
+ if (strpos($Text,'[map]') !== false) {
+ $Text = preg_replace("/\[map\]/", '<div class="map"></div>', $Text);
+ }
+
// Check for bold text
if (strpos($Text,'[b]') !== false) {
$Text = preg_replace("(\[b\](.*?)\[\/b\])ism",'<strong>$1</strong>',$Text);
@@ -610,7 +647,7 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true) {
// Check for [spoiler=Author] text
- $t_wrote = t('$1 wrote:');
+ $t_wrote = t('$1 spoiler');
// handle nested quotes
$endlessloop = 0;
@@ -700,6 +737,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) {
@@ -811,7 +852,7 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true) {
// fix any escaped ampersands that may have been converted into links
$Text = preg_replace("/\<(.*?)(src|href)=(.*?)\&amp\;(.*?)\>/ism",'<$1$2=$3&$4>',$Text);
- $Text = preg_replace("/\<(.*?)(src|href)=\"[^hfm](.*?)\>/ism",'<$1$2="">',$Text);
+ $Text = preg_replace("/\<(.*?)(src|href)=\"[^hfm#](.*?)\>/ism",'<$1$2="">',$Text);
call_hooks('bbcode',$Text);
diff --git a/include/cache.php b/include/cache.php
index a70650b5e..4a3f453e1 100644
--- a/include/cache.php
+++ b/include/cache.php
@@ -21,7 +21,7 @@
dbesc($key)
);
if($r) {
- q("UPDATE cache SET v = '%s', updated = '%s' WHERE k = '%s' limit 1",
+ q("UPDATE cache SET v = '%s', updated = '%s' WHERE k = '%s'",
dbesc($value),
dbesc(datetime_convert()),
dbesc($key));
diff --git a/include/chat.php b/include/chat.php
index b8fb185df..533c03dde 100644
--- a/include/chat.php
+++ b/include/chat.php
@@ -77,7 +77,7 @@ function chatroom_destroy($channel,$arr) {
return $ret;
}
- q("delete from chatroom where cr_id = %d limit 1",
+ q("delete from chatroom where cr_id = %d",
intval($r[0]['cr_id'])
);
if($r[0]['cr_id']) {
@@ -129,8 +129,11 @@ function chatroom_enter($observer_xchan,$room_id,$status,$client) {
}
if(intval($x[0]['cr_expire'])) {
- $sql = "delete from chat where created < UTC_TIMESTAMP() - INTERVAL " . intval($x[0]['cr_expire']) . " MINUTE and chat_room = " . intval($x[0]['cr_id']);
- $r = q($sql);
+ $r = q("delete from chat where created < %s - INTERVAL %s and chat_room = %d",
+ db_utcnow(),
+ db_quoteinterval( intval($x[0]['cr_expire']) . ' MINUTE' ),
+ intval($x[0]['cr_id'])
+ );
}
$r = q("select * from chatpresence where cp_xchan = '%s' and cp_room = %d limit 1",
@@ -138,7 +141,7 @@ function chatroom_enter($observer_xchan,$room_id,$status,$client) {
intval($room_id)
);
if($r) {
- q("update chatpresence set cp_last = '%s' where cp_id = %d and cp_client = '%s' limit 1",
+ q("update chatpresence set cp_last = '%s' where cp_id = %d and cp_client = '%s'",
dbesc(datetime_convert()),
intval($r[0]['cp_id']),
dbesc($client)
@@ -169,7 +172,7 @@ function chatroom_leave($observer_xchan,$room_id,$client) {
dbesc($client)
);
if($r) {
- q("delete from chatpresence where cp_id = %d limit 1",
+ q("delete from chatpresence where cp_id = %d",
intval($r[0]['cp_id'])
);
}
@@ -189,6 +192,17 @@ function chatroom_list($uid) {
return $r;
}
+function chatroom_list_count($uid) {
+ require_once('include/security.php');
+ $sql_extra = permissions_sql($uid);
+
+ $r = q("select count(*) as total from chatroom where cr_uid = %d $sql_extra",
+ intval($uid)
+ );
+
+ return $r[0]['total'];
+}
+
/**
* create a chat message via API.
* It is the caller's responsibility to enter the room.
diff --git a/include/cli_startup.php b/include/cli_startup.php
index 6bd4e7520..027d62953 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,9 +14,13 @@ 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);
+ $db = dba_factory($db_host, $db_port, $db_user, $db_pass, $db_data, $db_type);
+ unset($db_host, $db_port, $db_user, $db_pass, $db_data, $db_type);
};
require_once('include/session.php');
diff --git a/include/config.php b/include/config.php
index add7b5f71..87b91e51a 100644
--- a/include/config.php
+++ b/include/config.php
@@ -1,30 +1,56 @@
-<?php /** @file */
-
+<?php
/**
+ * @file include/config.php
+ * @brief Arbitrary configuration storage.
*
- * Arbitrary configuration storage
* Note:
* Please do not store booleans - convert to 0/1 integer values
* The get_?config() functions return boolean false for keys that are unset,
* and this could lead to subtle bugs.
*
- * There are a few places in the code (such as the admin panel) where boolean
- * configurations need to be fixed as of 10/08/2011.
+ * Arrays get stored as serialize strings.
+ *
+ * @todo There are a few places in the code (such as the admin panel) where
+ * boolean configurations need to be fixed as of 10/08/2011.
+ *
+ * - <b>config</b> is used for hub specific configurations. It overrides the
+ * configurations from .htconfig file. The storage is of size TEXT.
+ * - <b>pconfig</b> is used for channel specific configurations and takes a
+ * <i>channel_id</i> as identifier. It stores for example which features are
+ * enabled per channel. The storage is of size MEDIUMTEXT.
+ * @code $var = get_pconfig(local_user(), 'category', 'key');@endcode
+ * - <b>xconfig</b> is the same as pconfig, except that it uses <i>xchan</i> as
+ * an identifier. This is for example for people who do not have a local account.
+ * The storage is of size MEDIUMTEXT.
+ * @code $observer = $a->get_observer_hash();
+ * if ($observer) {
+ * $var = get_xconfig($observer, 'category', 'key');
+ * }@endcode
+ *
+ * - get_config() and set_config() can also be done through the command line tool
+ * @ref util/config
+ * - get_pconfig() and set_pconfig() can also be done through the command line tool
+ * @ref util/pconfig and takes a channel_id as first argument.
+ *
*/
-
-// retrieve a "family" of config variables from database to cached storage
-
+/**
+ * @brief Loads the hub's configuration from database to a cached storage.
+ *
+ * Retrieve a category ($family) of config variables from database to a cached
+ * storage in the global $a->config[$family].
+ *
+ * @param string $family
+ * The category of the configuration value
+ */
function load_config($family) {
global $a;
- if(! array_key_exists($family,$a->config))
+ if(! array_key_exists($family, $a->config))
$a->config[$family] = array();
- if(! array_key_exists('config_loaded',$a->config[$family])) {
-
+ if(! array_key_exists('config_loaded', $a->config[$family])) {
$r = q("SELECT * FROM config WHERE cat = '%s'", dbesc($family));
-
if($r !== false) {
if($r) {
foreach($r as $rr) {
@@ -37,24 +63,31 @@ function load_config($family) {
}
}
-// get a particular config variable given the family name
-// and key. Returns false if not set.
-// $instore is only used by the set_config function
-// to determine if the key already exists in the DB
-// If a key is found in the DB but doesn't exist in
-// local config cache, pull it into the cache so we don't have
-// to hit the DB again for this item.
-
-
+/**
+ * @brief Get a particular config variable given the category name ($family)
+ * and a key.
+ *
+ * Get a particular config variable from the given category ($family) and the
+ * $key from a cached storage in $a->config[$family]. If a key is found in the
+ * DB but does not exist in local config cache, pull it into the cache so we
+ * do not have to hit the DB again for this item.
+ *
+ * Returns false if not set.
+ *
+ * @param string $family
+ * The category of the configuration value
+ * @param string $key
+ * The configuration key to query
+ * @return mixed Return value or false on error or if not set
+ */
function get_config($family, $key) {
-
global $a;
- if((! array_key_exists($family,$a->config)) || (! array_key_exists('config_loaded',$a->config[$family])))
+ if((! array_key_exists($family, $a->config)) || (! array_key_exists('config_loaded', $a->config[$family])))
load_config($family);
- if(array_key_exists('config_loaded',$a->config[$family])) {
- if(! array_key_exists($key,$a->config[$family])) {
+ if(array_key_exists('config_loaded', $a->config[$family])) {
+ if(! array_key_exists($key, $a->config[$family])) {
return false;
}
return ((! is_array($a->config[$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', $a->config[$family][$key]))
@@ -65,57 +98,94 @@ function get_config($family, $key) {
return false;
}
-function get_config_from_storage($family,$key) {
- $ret = q("select * from config where cat = '%s' and k = '%s' limit 1",
+/**
+ * @brief Returns a value directly from the database configuration storage.
+ *
+ * This function queries directly the database and bypasses the chached storage
+ * from get_config($family, $key).
+ *
+ * @param string $family
+ * The category of the configuration value
+ * @param string $key
+ * The configuration key to query
+ * @return mixed
+ */
+function get_config_from_storage($family, $key) {
+ $ret = q("SELECT * FROM config WHERE cat = '%s' AND k = '%s' LIMIT 1",
dbesc($family),
dbesc($key)
);
return $ret;
}
-
-
-// Store a config value ($value) in the category ($family)
-// under the key ($key)
-// Return the value, or false if the database update failed
-
-function set_config($family,$key,$value) {
+/**
+ * @brief Sets a configuration value for the hub.
+ *
+ * Stores a config value ($value) in the category ($family) under the key ($key).
+ *
+ * Please do not store booleans - convert to 0/1 integer values!
+ *
+ * @param string $family
+ * The category of the configuration value
+ * @param string $key
+ * The configuration key to set
+ * @param mixed $value
+ * The value to store in the configuration
+ * @return mixed
+ * Return the set value, or false if the database update failed
+ */
+function set_config($family, $key, $value) {
global $a;
+
// manage array value
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
- if(get_config($family,$key) === false || (! get_config_from_storage($family,$key))) {
- $a->config[$family][$key] = $value;
-
+ if(get_config($family, $key) === false || (! get_config_from_storage($family, $key))) {
$ret = q("INSERT INTO config ( cat, k, v ) VALUES ( '%s', '%s', '%s' ) ",
dbesc($family),
dbesc($key),
dbesc($dbvalue)
);
- if($ret)
- return $value;
+ if($ret) {
+ $a->config[$family][$key] = $value;
+ $ret = $value;
+ }
return $ret;
}
- $ret = q("UPDATE config SET v = '%s' WHERE cat = '%s' AND k = '%s' LIMIT 1",
+ $ret = q("UPDATE config SET v = '%s' WHERE cat = '%s' AND k = '%s'",
dbesc($dbvalue),
dbesc($family),
dbesc($key)
);
- $a->config[$family][$key] = $value;
-
- if($ret)
- return $value;
+ if($ret) {
+ $a->config[$family][$key] = $value;
+ $ret = $value;
+ }
return $ret;
}
-function del_config($family,$key) {
+/**
+ * @brief Deletes the given key from the hub's configuration database.
+ *
+ * Removes the configured value from the stored cache in $a->config[$family]
+ * and removes it from the database.
+ *
+ * @param string $family
+ * The category of the configuration value
+ * @param string $key
+ * The configuration key to delete
+ * @return mixed
+ */
+function del_config($family, $key) {
global $a;
- if(array_key_exists($family,$a->config) && array_key_exists($key,$a->config[$family]))
+ $ret = false;
+
+ if(array_key_exists($family, $a->config) && array_key_exists($key, $a->config[$family]))
unset($a->config[$family][$key]);
- $ret = q("DELETE FROM config WHERE cat = '%s' AND k = '%s' LIMIT 1",
+ $ret = q("DELETE FROM config WHERE cat = '%s' AND k = '%s'",
dbesc($family),
dbesc($key)
);
@@ -123,18 +193,26 @@ function del_config($family,$key) {
}
-function load_pconfig($uid,$family = '') {
+/**
+ * @brief Loads all configuration values of a channel into a cached storage.
+ *
+ * All configuration values of the given channel are stored in global cache
+ * which is available under the global variable $a->config[$uid].
+ *
+ * @param string $uid
+ * The channel_id
+ * @return void|false Nothing or false if $uid is false
+ */
+function load_pconfig($uid) {
global $a;
if($uid === false)
return false;
- if(! array_key_exists($uid,$a->config))
+ if(! array_key_exists($uid, $a->config))
$a->config[$uid] = array();
- // family is no longer used - load entire user config
-
- $r = q("SELECT * FROM `pconfig` WHERE `uid` = %d",
+ $r = q("SELECT * FROM pconfig WHERE uid = %d",
intval($uid)
);
@@ -142,59 +220,88 @@ function load_pconfig($uid,$family = '') {
foreach($r as $rr) {
$k = $rr['k'];
$c = $rr['cat'];
- if(! array_key_exists($c,$a->config[$uid])) {
+ if(! array_key_exists($c, $a->config[$uid])) {
$a->config[$uid][$c] = array();
$a->config[$uid][$c]['config_loaded'] = true;
}
$a->config[$uid][$c][$k] = $rr['v'];
}
- }
+ }
}
-
-
-
-function get_pconfig($uid,$family, $key, $instore = false) {
-
+/**
+ * @brief Get a particular channel's config variable given the category name
+ * ($family) and a key.
+ *
+ * Get a particular channel's config value from the given category ($family)
+ * and the $key from a cached storage in $a->config[$uid].
+ *
+ * Returns false if not set.
+ *
+ * @param string $uid
+ * The channel_id
+ * @param string $family
+ * The category of the configuration value
+ * @param string $key
+ * The configuration key to query
+ * @param boolean $instore (deprecated, without function)
+ * @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);
global $a;
if($uid === false)
return false;
- if(! array_key_exists($uid,$a->config))
+ if(! array_key_exists($uid, $a->config))
load_pconfig($uid);
- if((! array_key_exists($family,$a->config[$uid])) || (! array_key_exists($key,$a->config[$uid][$family])))
+ if((! array_key_exists($family, $a->config[$uid])) || (! array_key_exists($key, $a->config[$uid][$family])))
return false;
-
+
return ((! is_array($a->config[$uid][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', $a->config[$uid][$family][$key]))
? unserialize($a->config[$uid][$family][$key])
: $a->config[$uid][$family][$key]
);
}
-function set_pconfig($uid,$family,$key,$value) {
-
+/**
+ * @brief Sets a configuration value for a channel.
+ *
+ * Stores a config value ($value) in the category ($family) under the key ($key)
+ * for the channel_id $uid.
+ *
+ * Please do not store booleans - convert to 0/1 integer values!
+ *
+ * @param string $uid
+ * The channel_id
+ * @param string $family
+ * The category of the configuration value
+ * @param string $key
+ * The configuration key to query
+ * @return mixed Stored $value or false
+ */
+function set_pconfig($uid, $family, $key, $value) {
global $a;
-
// manage array value
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
- if(get_pconfig($uid,$family,$key) === false) {
- if(! array_key_exists($uid,$a->config))
+ if(get_pconfig($uid, $family, $key) === false) {
+ if(! array_key_exists($uid, $a->config))
$a->config[$uid] = array();
- if(! array_key_exists($family,$a->config[$uid]))
+ if(! array_key_exists($family, $a->config[$uid]))
$a->config[$uid][$family] = array();
// keep a separate copy for all variables which were
// set in the life of this page. We need this to
// synchronise channel clones.
- if(! array_key_exists('transient',$a->config[$uid]))
+ if(! array_key_exists('transient', $a->config[$uid]))
$a->config[$uid]['transient'] = array();
- if(! array_key_exists($family,$a->config[$uid]['transient']))
+ if(! array_key_exists($family, $a->config[$uid]['transient']))
$a->config[$uid]['transient'][$family] = array();
$a->config[$uid][$family][$key] = $value;
@@ -211,7 +318,7 @@ function set_pconfig($uid,$family,$key,$value) {
return $ret;
}
- $ret = q("UPDATE pconfig SET v = '%s' WHERE uid = %d and cat = '%s' AND k = '%s' LIMIT 1",
+ $ret = q("UPDATE pconfig SET v = '%s' WHERE uid = %d and cat = '%s' AND k = '%s'",
dbesc($dbvalue),
intval($uid),
dbesc($family),
@@ -222,9 +329,9 @@ function set_pconfig($uid,$family,$key,$value) {
// set in the life of this page. We need this to
// synchronise channel clones.
- if(! array_key_exists('transient',$a->config[$uid]))
+ if(! array_key_exists('transient', $a->config[$uid]))
$a->config[$uid]['transient'] = array();
- if(! array_key_exists($family,$a->config[$uid]['transient']))
+ if(! array_key_exists($family, $a->config[$uid]['transient']))
$a->config[$uid]['transient'][$family] = array();
$a->config[$uid][$family][$key] = $value;
@@ -235,13 +342,27 @@ function set_pconfig($uid,$family,$key,$value) {
return $ret;
}
-
-function del_pconfig($uid,$family,$key) {
-
+/**
+ * @brief Deletes the given key from the channel's configuration.
+ *
+ * Removes the configured value from the stored cache in $a->config[$uid]
+ * and removes it from the database.
+ *
+ * @param string $uid
+ * The channel_id
+ * @param string $family
+ * The category of the configuration value
+ * @param string $key
+ * The configuration key to delete
+ * @return mixed
+ */
+function del_pconfig($uid, $family, $key) {
global $a;
- if(x($a->config[$uid][$family],$key))
+ $ret = false;
+
+ if(x($a->config[$uid][$family], $key))
unset($a->config[$uid][$family][$key]);
- $ret = q("DELETE FROM pconfig WHERE uid = %d AND cat = '%s' AND k = '%s' LIMIT 1",
+ $ret = q("DELETE FROM pconfig WHERE uid = %d AND cat = '%s' AND k = '%s'",
intval($uid),
dbesc($family),
dbesc($key)
@@ -250,19 +371,26 @@ function del_pconfig($uid,$family,$key) {
}
-
-function load_xconfig($xchan,$family = '') {
+/**
+ * @brief Loads a full xchan's configuration into a cached storage.
+ *
+ * All configuration values of the given observer hash are stored in global
+ * cache which is available under the global variable $a->config[$xchan].
+ *
+ * @param string $xchan
+ * The observer's hash
+ * @return void|false Returns false if xchan is not set
+ */
+function load_xconfig($xchan) {
global $a;
if(! $xchan)
return false;
- if(! array_key_exists($xchan,$a->config))
+ if(! array_key_exists($xchan, $a->config))
$a->config[$xchan] = array();
- // family is no longer used. Entire config is loaded
-
- $r = q("SELECT * FROM `xconfig` WHERE `xchan` = '%s'",
+ $r = q("SELECT * FROM xconfig WHERE xchan = '%s'",
dbesc($xchan)
);
@@ -270,51 +398,77 @@ function load_xconfig($xchan,$family = '') {
foreach($r as $rr) {
$k = $rr['k'];
$c = $rr['cat'];
- if(! array_key_exists($c,$a->config[$xchan])) {
+ if(! array_key_exists($c, $a->config[$xchan])) {
$a->config[$xchan][$c] = array();
$a->config[$xchan][$c]['config_loaded'] = true;
}
$a->config[$xchan][$c][$k] = $rr['v'];
}
- }
+ }
}
-
-
-
-function get_xconfig($xchan,$family, $key) {
-
+/**
+ * @brief Get a particular observer's config variable given the category
+ * name ($family) and a key.
+ *
+ * Get a particular observer's config value from the given category ($family)
+ * and the $key from a cached storage in $a->config[$xchan].
+ *
+ * Returns false if not set.
+ *
+ * @param string $xchan
+ * The observer's hash
+ * @param string $family
+ * The category of the configuration value
+ * @param string $key
+ * The configuration key to query
+ * @return mixed Stored $value or false if it does not exist
+ */
+function get_xconfig($xchan, $family, $key) {
global $a;
if(! $xchan)
return false;
- if(! array_key_exists($xchan,$a->config))
+ if(! array_key_exists($xchan, $a->config))
load_xconfig($xchan);
- if((! array_key_exists($family,$a->config[$xchan])) || (! array_key_exists($key,$a->config[$xchan][$family])))
+ if((! array_key_exists($family, $a->config[$xchan])) || (! array_key_exists($key, $a->config[$xchan][$family])))
return false;
return ((! is_array($a->config[$xchan][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', $a->config[$xchan][$family][$key]))
? unserialize($a->config[$xchan][$family][$key])
: $a->config[$xchan][$family][$key]
);
-
}
-
-function set_xconfig($xchan,$family,$key,$value) {
-
+/**
+ * @brief Sets a configuration value for an observer.
+ *
+ * Stores a config value ($value) in the category ($family) under the key ($key)
+ * for the observer's $xchan hash.
+ *
+ * Please do not store booleans - convert to 0/1 integer values!
+ *
+ * @param string $xchan
+ * The observer's hash
+ * @param string $family
+ * The category of the configuration value
+ * @param string $key
+ * The configuration key to set
+ * @return mixed Stored $value or false
+ */
+function set_xconfig($xchan, $family, $key, $value) {
global $a;
// manage array value
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
- if(get_xconfig($xchan,$family,$key) === false) {
- if(! array_key_exists($xchan,$a->config))
+ if(get_xconfig($xchan, $family, $key) === false) {
+ if(! array_key_exists($xchan, $a->config))
$a->config[$xchan] = array();
- if(! array_key_exists($family,$a->config[$xchan]))
+ if(! array_key_exists($family, $a->config[$xchan]))
$a->config[$xchan][$family] = array();
$a->config[$xchan][$family][$key] = $value;
@@ -329,7 +483,7 @@ function set_xconfig($xchan,$family,$key,$value) {
return $ret;
}
- $ret = q("UPDATE xconfig SET v = '%s' WHERE xchan = '%s' and cat = '%s' AND k = '%s' LIMIT 1",
+ $ret = q("UPDATE xconfig SET v = '%s' WHERE xchan = '%s' and cat = '%s' AND k = '%s'",
dbesc($dbvalue),
dbesc($xchan),
dbesc($family),
@@ -341,22 +495,32 @@ function set_xconfig($xchan,$family,$key,$value) {
if($ret)
return $value;
return $ret;
-
}
-
-function del_xconfig($xchan,$family,$key) {
-
+/**
+ * @brief Deletes the given key from the observer's config.
+ *
+ * Removes the configured value from the stored cache in $a->config[$xchan]
+ * and removes it from the database.
+ *
+ * @param string $xchan
+ * The observer's hash
+ * @param string $family
+ * The category of the configuration value
+ * @param string $key
+ * The configuration key to delete
+ * @return mixed
+ */
+function del_xconfig($xchan, $family, $key) {
global $a;
- if(x($a->config[$xchan][$family],$key))
+ $ret = false;
+
+ if(x($a->config[$xchan][$family], $key))
unset($a->config[$xchan][$family][$key]);
- $ret = q("DELETE FROM `xconfig` WHERE `xchan` = '%s' AND `cat` = '%s' AND `k` = '%s' LIMIT 1",
+ $ret = q("DELETE FROM xconfig WHERE xchan = '%s' AND cat = '%s' AND k = '%s'",
dbesc($xchan),
dbesc($family),
dbesc($key)
);
return $ret;
}
-
-
-
diff --git a/include/contact_widgets.php b/include/contact_widgets.php
index 28a9fcfd3..ee9394e95 100644
--- a/include/contact_widgets.php
+++ b/include/contact_widgets.php
@@ -29,8 +29,7 @@ function findpeople_widget() {
'$random' => t('Random Profile'),
'$inv' => t('Invite Friends'),
'$advanced_search' => $advanced_search,
- '$advanced_hint' => t('Exammple: name=fred and country=iceland'),
- '$find_advanced' => t('Advanced Find'),
+ '$advanced_hint' => "\r\n" . t('Advanced example: name=fred and country=iceland'),
'$loggedin' => local_user()
));
diff --git a/include/conversation.php b/include/conversation.php
index c346a204c..a2fb3d162 100644
--- a/include/conversation.php
+++ b/include/conversation.php
@@ -419,8 +419,6 @@ function visible_activity($item) {
function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $prepared_item = '') {
- $tstart = dba_timer();
- $t0 = $t1 = $t2 = $t3 = $t4 = $t5 = $t6 = null;
$content_html = '';
$o = '';
@@ -433,14 +431,18 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $
$arr_blocked = null;
- if(local_user()) {
+ if(local_user())
$str_blocked = get_pconfig(local_user(),'system','blocked');
- if($str_blocked) {
+ if(! local_user() && ($mode == 'network')) {
+ $sys = get_sys_channel();
+ $id = $sys['channel_id'];
+ $str_blocked = get_pconfig($id,'system','blocked');
+ }
+
+ if($str_blocked) {
$arr_blocked = explode(',',$str_blocked);
for($x = 0; $x < count($arr_blocked); $x ++)
- $arr_blocked[$x] = trim($arr_blocked[$x]);
- }
-
+ $arr_blocked[$x] = trim($arr_blocked[$x]);
}
@@ -453,64 +455,56 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $
if($mode === 'network') {
- $t1 = dba_timer();
-
$profile_owner = local_user();
$page_writeable = true;
- if(!$update) {
- // The special div is needed for liveUpdate to kick in for this page.
- // We only launch liveUpdate if you aren't filtering in some incompatible
- // way and also you aren't writing a comment (discovered in javascript).
-
- $live_update_div = '<div id="live-network"></div>' . "\r\n"
- . "<script> var profile_uid = " . $_SESSION['uid']
- . "; var netargs = '" . substr($a->cmd,8)
- . '?f='
- . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : '')
- . ((x($_GET,'search')) ? '&search=' . $_GET['search'] : '')
- . ((x($_GET,'star')) ? '&star=' . $_GET['star'] : '')
- . ((x($_GET,'order')) ? '&order=' . $_GET['order'] : '')
- . ((x($_GET,'bmark')) ? '&bmark=' . $_GET['bmark'] : '')
- . ((x($_GET,'liked')) ? '&liked=' . $_GET['liked'] : '')
- . ((x($_GET,'conv')) ? '&conv=' . $_GET['conv'] : '')
- . ((x($_GET,'spam')) ? '&spam=' . $_GET['spam'] : '')
- . ((x($_GET,'nets')) ? '&nets=' . $_GET['nets'] : '')
- . ((x($_GET,'cmin')) ? '&cmin=' . $_GET['cmin'] : '')
- . ((x($_GET,'cmax')) ? '&cmax=' . $_GET['cmax'] : '')
- . ((x($_GET,'file')) ? '&file=' . $_GET['file'] : '')
- . ((x($_GET,'uri')) ? '&uri=' . $_GET['uri'] : '')
-
- . "'; var profile_page = " . $a->pager['page'] . "; </script>\r\n";
- }
-
-
+ if(!$update) {
+ // The special div is needed for liveUpdate to kick in for this page.
+ // We only launch liveUpdate if you aren't filtering in some incompatible
+ // way and also you aren't writing a comment (discovered in javascript).
+
+ $live_update_div = '<div id="live-network"></div>' . "\r\n"
+ . "<script> var profile_uid = " . $_SESSION['uid']
+ . "; var netargs = '" . substr($a->cmd,8)
+ . '?f='
+ . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : '')
+ . ((x($_GET,'search')) ? '&search=' . $_GET['search'] : '')
+ . ((x($_GET,'star')) ? '&star=' . $_GET['star'] : '')
+ . ((x($_GET,'order')) ? '&order=' . $_GET['order'] : '')
+ . ((x($_GET,'bmark')) ? '&bmark=' . $_GET['bmark'] : '')
+ . ((x($_GET,'liked')) ? '&liked=' . $_GET['liked'] : '')
+ . ((x($_GET,'conv')) ? '&conv=' . $_GET['conv'] : '')
+ . ((x($_GET,'spam')) ? '&spam=' . $_GET['spam'] : '')
+ . ((x($_GET,'nets')) ? '&nets=' . $_GET['nets'] : '')
+ . ((x($_GET,'cmin')) ? '&cmin=' . $_GET['cmin'] : '')
+ . ((x($_GET,'cmax')) ? '&cmax=' . $_GET['cmax'] : '')
+ . ((x($_GET,'file')) ? '&file=' . $_GET['file'] : '')
+ . ((x($_GET,'uri')) ? '&uri=' . $_GET['uri'] : '')
+ . "'; var profile_page = " . $a->pager['page'] . "; </script>\r\n";
+ }
}
elseif($mode === 'channel') {
$profile_owner = $a->profile['profile_uid'];
$page_writeable = ($profile_owner == local_user());
- if(!$update) {
- $tab = notags(trim($_GET['tab']));
- if($tab === 'posts') {
- // This is ugly, but we can't pass the profile_uid through the session to the ajax updater,
- // because browser prefetching might change it on us. We have to deliver it with the page.
-
- $live_update_div = '<div id="live-channel"></div>' . "\r\n"
- . "<script> var profile_uid = " . $a->profile['profile_uid']
- . "; var netargs = '?f='; var profile_page = " . $a->pager['page'] . "; </script>\r\n";
- }
- }
+ if(!$update) {
+ $tab = notags(trim($_GET['tab']));
+ if($tab === 'posts') {
+ // This is ugly, but we can't pass the profile_uid through the session to the ajax updater,
+ // because browser prefetching might change it on us. We have to deliver it with the page.
+ $live_update_div = '<div id="live-channel"></div>' . "\r\n"
+ . "<script> var profile_uid = " . $a->profile['profile_uid']
+ . "; var netargs = '?f='; var profile_page = " . $a->pager['page'] . "; </script>\r\n";
+ }
+ }
}
elseif($mode === 'display') {
$profile_owner = local_user();
$page_writeable = false;
-
- $live_update_div = '<div id="live-display"></div>' . "\r\n";
-
+ $live_update_div = '<div id="live-display"></div>' . "\r\n";
}
elseif($mode === 'page') {
@@ -519,10 +513,10 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $
$live_update_div = '<div id="live-page"></div>' . "\r\n";
}
+ elseif($mode === 'search') {
+ $live_update_div = '<div id="live-search"></div>' . "\r\n";
+ }
- elseif($mode === 'search') {
- $live_update_div = '<div id="live-search"></div>' . "\r\n";
- }
elseif($mode === 'photos') {
$profile_onwer = $a->profile['profile_uid'];
$page_writeable = ($profile_owner == local_user());
@@ -555,7 +549,6 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $
$alike = array();
$dlike = array();
-
// array with html for each thread (parent+comments)
$threads = array();
$threadsid = -1;
@@ -603,12 +596,11 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $
}
else
$nickname = $a->user['nickname'];
-
+
$profile_name = ((strlen($item['author-name'])) ? $item['author-name'] : $item['name']);
if($item['author-link'] && (! $item['author-name']))
$profile_name = $item['author-link'];
-
$tags=array();
$hashtags = array();
@@ -631,7 +623,6 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $
$profile_link = $item['author']['xchan_url'];
$profile_avatar = $item['author']['xchan_photo_m'];
-
$location = format_location($item);
localize_item($item);
@@ -659,10 +650,12 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $
$likebuttons = false;
$shareable = false;
- $verified = (($item['item_flags'] & ITEM_VERIFIED) ? t('Message is verified') : '');
- $unverified = '';
+ $verified = (($item['item_flags'] & ITEM_VERIFIED) ? t('Message signature validated') : '');
+ $forged = ((($item['sig']) && (! ($item['item_flags'] & ITEM_VERIFIED))) ? t('Message signature incorrect') : '');
+ $unverified = '';
+
$tags=array();
$terms = get_terms_oftype($item['term'],array(TERM_HASHTAG,TERM_MENTION,TERM_UNKNOWN));
@@ -673,7 +666,6 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $
$body = prepare_body($item,true);
- //$tmp_item = replace_macros($tpl,array(
$tmp_item = array(
'template' => $tpl,
'toplevel' => 'toplevel_item',
@@ -693,13 +685,13 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $
'mentions' => $mentions,
'verified' => $verified,
'unverified' => $unverified,
+ 'forged' => $forged,
'txt_cats' => t('Categories:'),
- 'txt_folders' => t('Filed under:'),
- 'has_cats' => ((count($categories)) ? 'true' : ''),
- 'has_folders' => ((count($folders)) ? 'true' : ''),
- 'categories' => $categories,
- 'folders' => $folders,
-
+ 'txt_folders' => t('Filed under:'),
+ 'has_cats' => ((count($categories)) ? 'true' : ''),
+ 'has_folders' => ((count($folders)) ? 'true' : ''),
+ 'categories' => $categories,
+ 'folders' => $folders,
'text' => strip_tags($body),
'ago' => relative_date($item['created']),
'app' => $item['app'],
@@ -707,7 +699,7 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $
'isotime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'c'),
'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'),
'editedtime' => (($item['edited'] != $item['created']) ? sprintf( t('last edited: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r')) : ''),
- 'expiretime' => (($item['expires'] !== '0000-00-00 00:00:00') ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')):''),
+ 'expiretime' => (($item['expires'] !== NULL_DATE) ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')):''),
'location' => $location,
'indent' => '',
'owner_name' => $owner_name,
@@ -743,10 +735,10 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $
// Normal View
// logger('conv: items: ' . print_r($items,true));
- require_once('include/ConversationObject.php');
- require_once('include/ItemObject.php');
+ require_once('include/ConversationObject.php');
+ require_once('include/ItemObject.php');
- $conv = new Conversation($mode, $preview, $prepared_item);
+ $conv = new Conversation($mode, $preview, $prepared_item);
// In the display mode we don't have a profile owner.
@@ -754,12 +746,12 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $
$conv->set_profile_owner($items[0]['uid']);
- // get all the topmost parents
- // this shouldn't be needed, as we should have only them in our array
- // But for now, this array respects the old style, just in case
+ // get all the topmost parents
+ // this shouldn't be needed, as we should have only them in our array
+ // But for now, this array respects the old style, just in case
- $threads = array();
- foreach($items as $item) {
+ $threads = array();
+ foreach($items as $item) {
// Check for any blocked authors
@@ -774,7 +766,7 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $
if($blocked)
continue;
}
-
+
// Check all the kids too
if($arr_blocked && $item['children']) {
@@ -786,46 +778,35 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $
}
}
+ like_puller($a, $item, $alike, 'like');
+ if(feature_enabled($profile_owner, 'dislike'))
+ like_puller($a, $item, $dlike, 'dislike');
- like_puller($a,$item,$alike,'like');
-
- if(feature_enabled($profile_owner,'dislike'))
- like_puller($a,$item,$dlike,'dislike');
+ if(! visible_activity($item)) {
+ continue;
+ }
- if(! visible_activity($item)) {
- continue;
- }
+ $item['pagedrop'] = $page_dropping;
- $item['pagedrop'] = $page_dropping;
+ if($item['id'] == $item['parent']) {
- if($item['id'] == $item['parent']) {
-// $tx1 = dba_timer();
- $item_object = new Item($item);
- $conv->add_thread($item_object);
- if($page_mode === 'list')
+ $item_object = new Item($item);
+ $conv->add_thread($item_object);
+ if($page_mode === 'list') {
$item_object->set_template('conv_list.tpl');
-
-// $tx2 = dba_timer();
-// if($mode === 'network')
-// profiler($tx1,$tx2,'add thread ' . $item['id']);
- }
- }
- $t2 = dba_timer();
- $threads = $conv->get_template_data($alike, $dlike);
- if(!$threads) {
- logger('[ERROR] conversation : Failed to get template data.', LOGGER_DEBUG);
- $threads = array();
- }
- $t3 = dba_timer();
- if($mode === 'network') {
- profiler($t1,$t2,'Conversation prepare');
- profiler($t2,$t3,'Conversation get_template');
+ $item_object->set_display_mode('list');
+ }
+ }
}
-
- }
- }
+ $threads = $conv->get_template_data($alike, $dlike);
+ if(!$threads) {
+ logger('[ERROR] conversation : Failed to get template data.', LOGGER_DEBUG);
+ $threads = array();
+ }
+ }
+ }
if($page_mode === 'traditional' || $page_mode === 'preview') {
$page_template = get_markup_template("threaded_conversation.tpl");
@@ -838,8 +819,8 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $
$threads = null;
}
- if($page_mode === 'preview')
- logger('preview: ' . print_r($threads,true));
+// if($page_mode === 'preview')
+// logger('preview: ' . print_r($threads,true));
// Do not un-comment if smarty3 is in use
// logger('page_template: ' . $page_template);
@@ -859,16 +840,7 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $
'$dropping' => ($page_dropping?t('Delete Selected Items'):False),
));
- if($mode === 'network') {
- $t4 = dba_timer();
- profiler($t3,$t4,'conversation template');
- }
-
- if($page_mode === 'preview')
- logger('preview: ' . $o);
-
- return $o;
-
+ return $o;
}
@@ -917,15 +889,18 @@ function item_photo_menu($item){
$vsrc_link = "";
$follow_url = "";
- if(local_user()) {
+
+ $local_user = local_user();
+
+ if($local_user) {
$ssl_state = true;
if(! count($a->contacts))
- load_contact_links(local_user());
+ load_contact_links($local_user);
$channel = $a->get_channel();
$channel_hash = (($channel) ? $channel['channel_hash'] : '');
}
- if((local_user()) && local_user() == $item['uid']) {
+ if(($local_user) && $local_user == $item['uid']) {
$vsrc_link = 'javascript:viewsrc(' . $item['id'] . '); return false;';
if($item['parent'] == $item['id'] && $channel && ($channel_hash != $item['author_xchan'])) {
$sub_link = 'javascript:dosubthread(' . $item['id'] . '); return false;';
@@ -933,17 +908,19 @@ function item_photo_menu($item){
}
$profile_link = chanlink_hash($item['author_xchan']);
- $pm_url = $a->get_baseurl($ssl_state) . '/mail/new/?f=&hash=' . $item['author_xchan'];
+ if($item['uid'] > 0)
+ $pm_url = $a->get_baseurl($ssl_state) . '/mail/new/?f=&hash=' . $item['author_xchan'];
if($a->contacts && array_key_exists($item['author_xchan'],$a->contacts))
$contact = $a->contacts[$item['author_xchan']];
else
- if(local_user() && $item['author']['xchan_addr'])
+ if($local_user && $item['author']['xchan_addr'])
$follow_url = z_root() . '/follow/?f=&url=' . $item['author']['xchan_addr'];
if($contact) {
$poke_link = $a->get_baseurl($ssl_state) . '/poke/?f=&c=' . $contact['abook_id'];
- $contact_url = $a->get_baseurl($ssl_state) . '/connedit/' . $contact['abook_id'];
+ if (!($contact['abook_flags'] & ABOOK_FLAG_SELF))
+ $contact_url = $a->get_baseurl($ssl_state) . '/connedit/' . $contact['abook_id'];
$posts_link = $a->get_baseurl($ssl_state) . '/network/?cid=' . $contact['abook_id'];
$clean_url = normalise_link($item['author-link']);
@@ -981,26 +958,43 @@ function item_photo_menu($item){
return $o;
}
-
-function like_puller($a,$item,&$arr,$mode) {
+/**
+ * @brief Returns a like/dislike entry.
+ * It gives back a HTML link to the channel that liked/disliked.
+ *
+ * @param array $a (not used)
+ * @param array $item
+ * @param array &$arr
+ * @param string $mode like/dislike
+ * @return void
+ */
+function like_puller($a, $item, &$arr, $mode) {
$url = '';
- $sparkle = '';
$verb = (($mode === 'like') ? ACTIVITY_LIKE : ACTIVITY_DISLIKE);
- if((activity_match($item['verb'],$verb)) && ($item['id'] != $item['parent'])) {
- $url = chanlink_url($item['author']['xchan_url']);
+ if((activity_match($item['verb'], $verb)) && ($item['id'] != $item['parent'])) {
+
+ if($item['author']['xchan_url'])
+ $url = chanlink_url($item['author']['xchan_url']);
if(! $item['thr_parent'])
$item['thr_parent'] = $item['parent_mid'];
if(! ((isset($arr[$item['thr_parent'] . '-l'])) && (is_array($arr[$item['thr_parent'] . '-l']))))
$arr[$item['thr_parent'] . '-l'] = array();
+
if(! isset($arr[$item['thr_parent']]))
$arr[$item['thr_parent']] = 1;
else
$arr[$item['thr_parent']] ++;
- $arr[$item['thr_parent'] . '-l'][] = '<a href="'. $url . '">' . $item['author']['xchan_name'] . '</a>';
+
+ $name = (($item['author']['xchan_name']) ? $item['author']['xchan_name'] : t('Unknown'));
+
+ if($url)
+ $arr[$item['thr_parent'] . '-l'][] = '<a href="'. $url . '">' . $name . '</a>';
+ else
+ $arr[$item['thr_parent'] . '-l'][] = '<a href="#" class="disabled">' . $name . '</a>';
}
return;
}
@@ -1105,8 +1099,8 @@ function status_editor($a,$x,$popup=false) {
$jotplugins = '';
$jotnets = '';
-
- $preview = ((feature_enabled($x['profile_uid'],'preview')) ? t('Preview') : '');
+ $preview = t('Preview');
+// $preview = ((feature_enabled($x['profile_uid'],'preview')) ? t('Preview') : '');
if(x($x,'nopreview'))
$preview = '';
@@ -1138,13 +1132,13 @@ function status_editor($a,$x,$popup=false) {
'$shortaudio' => t('audio link'),
'$setloc' => t('Set your location'),
'$shortsetloc' => t('set location'),
- '$noloc' => t('Clear browser location'),
+ '$noloc' => ((get_pconfig($x['profile_uid'],'system','use_browser_location')) ? t('Clear browser location') : ''),
'$shortnoloc' => t('clear location'),
'$title' => ((x($x,'title')) ? htmlspecialchars($x['title'], ENT_COMPAT,'UTF-8') : ''),
- '$placeholdertitle' => t('Set title'),
+ '$placeholdertitle' => t('Title (optional)'),
'$catsenabled' => ((feature_enabled($x['profile_uid'],'categories') && (! $webpage)) ? 'categories' : ''),
'$category' => "",
- '$placeholdercategory' => t('Categories (comma-separated list)'),
+ '$placeholdercategory' => t('Categories (optional, comma-separated list)'),
'$wait' => t('Please wait'),
'$permset' => t('Permission settings'),
'$shortpermset' => t('permissions'),
@@ -1340,8 +1334,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')),
@@ -1486,8 +1482,12 @@ function network_tabs() {
function profile_tabs($a, $is_owner=False, $nickname=Null){
- //echo "<pre>"; var_dump($a->user); killme();
-
+
+ // Don't provide any profile tabs if we're running as the sys channel
+
+ if($a->is_sys)
+ return;
+
$channel = $a->get_channel();
if (is_null($nickname))
@@ -1544,19 +1544,23 @@ function profile_tabs($a, $is_owner=False, $nickname=Null){
);
}
- require_once('include/chat.php');
- $chats = chatroom_list($uid);
- if (count($chats)) {
- $tabs[] = array(
- 'label' => t('Chatrooms'),
- 'url' => $a->get_baseurl() . '/chat/' . $nickname,
- 'sel' => ((argv(0) == 'chat') ? 'active' : '' ),
- 'title' => t('Chatrooms'),
- 'id' => 'chat-tab',
- );
+ if($p['chat']) {
+ require_once('include/chat.php');
+ $has_chats = chatroom_list_count($uid);
+ if ($has_chats) {
+ $tabs[] = array(
+ 'label' => t('Chatrooms'),
+ 'url' => $a->get_baseurl() . '/chat/' . $nickname,
+ 'sel' => ((argv(0) == 'chat') ? 'active' : '' ),
+ 'title' => t('Chatrooms'),
+ 'id' => 'chat-tab',
+ );
+ }
}
- if($is_owner) {
+ require_once('include/menu.php');
+ $has_bookmarks = menu_list_count(local_user(),'',MENU_BOOKMARK) + menu_list_count(local_user(),'',MENU_SYSTEM|MENU_BOOKMARK);
+ if($is_owner && $has_bookmarks) {
$tabs[] = array(
'label' => t('Bookmarks'),
'url' => $a->get_baseurl() . '/bookmarks',
diff --git a/include/datetime.php b/include/datetime.php
index 0214b9e4c..59dad2045 100644
--- a/include/datetime.php
+++ b/include/datetime.php
@@ -130,135 +130,119 @@ function datetime_convert($from = 'UTC', $to = 'UTC', $s = 'now', $fmt = "Y-m-d
function dob($dob) {
list($year,$month,$day) = sscanf($dob,'%4d-%2d-%2d');
- $y = datetime_convert('UTC',date_default_timezone_get(),'now','Y');
$f = get_config('system','birthday_input_format');
if(! $f)
$f = 'ymd';
- $o = datesel($f,'',1920,$y,true,$year,$month,$day);
- return $o;
-}
+ if($dob === '0000-00-00')
+ $value = '';
+ else
+ $value = (($year) ? datetime_convert('UTC','UTC',$dob,'Y-m-d') : datetime_convert('UTC','UTC',$dob,'m-d'));
-function datesel_format($f) {
+ $o = '<input type="text" name="dob" value="' . $value . '" placeholder="' . t('YYYY-MM-DD or MM-DD') . '" />';
- $o = '';
+// 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');
+// else
+// $o = datesel($f,mktime(0,0,0,0,0,1900),mktime(),false,'dob');
- if(strlen($f)) {
- for($x = 0; $x < strlen($f); $x ++) {
- switch($f[$x]) {
- case 'y':
- if(strlen($o))
- $o .= '-';
- $o .= t('year');
- break;
- case 'm':
- if(strlen($o))
- $o .= '-';
- $o .= t('month');
- break;
- case 'd':
- if(strlen($o))
- $o .= '-';
- $o .= t('day');
- break;
- default:
- break;
- }
- }
- }
return $o;
}
-// returns a date selector.
-// $f = format string, e.g. 'ymd' or 'mdy'
-// $pre = prefix (if needed) for HTML name and class fields
-// $ymin = first year shown in selector dropdown
-// $ymax = last year shown in selector dropdown
-// $allow_blank = allow an empty response on any field
-// $y = already selected year
-// $m = already selected month
-// $d = already selected day
-
-
-function datesel($f,$pre,$ymin,$ymax,$allow_blank,$y,$m,$d) {
-
- $o = '';
-
- if(strlen($f)) {
- for($z = 0; $z < strlen($f); $z ++) {
- if($f[$z] === 'y') {
-
- $o .= "<select name=\"{$pre}year\" class=\"{$pre}year\" size=\"1\">";
- if($allow_blank) {
- $sel = (($y == '0000') ? " selected=\"selected\" " : "");
- $o .= "<option value=\"0000\" $sel ></option>";
- }
-
- if($ymax > $ymin) {
- for($x = $ymax; $x >= $ymin; $x --) {
- $sel = (($x == $y) ? " selected=\"selected\" " : "");
- $o .= "<option value=\"$x\" $sel>$x</option>";
- }
- }
- else {
- for($x = $ymax; $x <= $ymin; $x ++) {
- $sel = (($x == $y) ? " selected=\"selected\" " : "");
- $o .= "<option value=\"$x\" $sel>$x</option>";
- }
- }
- }
- elseif($f[$z] == 'm') {
-
- $o .= "</select> <select name=\"{$pre}month\" class=\"{$pre}month\" size=\"1\">";
- for($x = (($allow_blank) ? 0 : 1); $x <= 12; $x ++) {
- $sel = (($x == $m) ? " selected=\"selected\" " : "");
- $y = (($x) ? $x : '');
- $o .= "<option value=\"$x\" $sel>$y</option>";
- }
- }
- elseif($f[$z] == 'd') {
-
- $o .= "</select> <select name=\"{$pre}day\" class=\"{$pre}day\" size=\"1\">";
- for($x = (($allow_blank) ? 0 : 1); $x <= 31; $x ++) {
- $sel = (($x == $d) ? " selected=\"selected\" " : "");
- $y = (($x) ? $x : '');
- $o .= "<option value=\"$x\" $sel>$y</option>";
- }
- }
- }
- }
+/**
+ * returns a date selector
+ * @param $format
+ * format string, e.g. 'ymd' or 'mdy'. Not currently supported
+ * @param $min
+ * unix timestamp of minimum date
+ * @param $max
+ * unix timestap of maximum date
+ * @param $default
+ * unix timestamp of default date
+ * @param $id
+ * id and name of datetimepicker (defaults to "datetimepicker")
+ */
+function datesel($format, $min, $max, $default, $id = 'datepicker') {
+ return datetimesel($format,$min,$max,$default,$id,true,false, '','');
+}
- $o .= "</select>";
- return $o;
+/**
+ * returns a time selector
+ * @param $format
+ * format string, e.g. 'ymd' or 'mdy'. Not currently supported
+ * @param $h
+ * already selected hour
+ * @param $m
+ * already selected minute
+ * @param $id
+ * id and name of datetimepicker (defaults to "timepicker")
+ */
+function timesel($format, $h, $m, $id='timepicker') {
+ return datetimesel($format,new DateTime(),new DateTime(),new DateTime("$h:$m"),$id,false,true);
}
+/**
+ * returns a datetime selector
+ * @param $format
+ * format string, e.g. 'ymd' or 'mdy'. Not currently supported
+ * @param $min
+ * unix timestamp of minimum date
+ * @param $max
+ * unix timestap of maximum date
+ * @param $default
+ * unix timestamp of default date
+ * @param $id
+ * id and name of datetimepicker (defaults to "datetimepicker")
+ * @param $pickdate
+ * true to show date picker (default)
+ * @param $picktime
+ * true to show time picker (default)
+ * @param $minfrom
+ * set minimum date from picker with id $minfrom (none by default)
+ * @param $maxfrom
+ * set maximum date from picker with id $maxfrom (none by default)
+ */
+function datetimesel($format, $min, $max, $default, $id = 'datetimepicker', $pickdate = true, $picktime = true, $minfrom = '', $maxfrom = '') {
+ // Once browser support is better this could probably be replaced with native HTML5 date picker
+ $o = '';
-function timesel($pre,$h,$m) {
+ $dateformat = '';
- $o = '';
- $o .= "<select name=\"{$pre}hour\" class=\"{$pre}hour\" size=\"1\">";
- for($x = 0; $x < 24; $x ++) {
- $sel = (($x == $h) ? " selected=\"selected\" " : "");
- $o .= "<option value=\"$x\" $sel>$x</option>";
- }
- $o .= "</select> : <select name=\"{$pre}minute\" class=\"{$pre}minute\" size=\"1\">";
- for($x = 0; $x < 60; $x ++) {
- $sel = (($x == $m) ? " selected=\"selected\" " : "");
- $o .= "<option value=\"$x\" $sel>$x</option>";
- }
+ if($pickdate) $dateformat .= 'Y-m-d';
+ if($pickdate && $picktime) $dateformat .= ' ';
+ if($picktime) $dateformat .= 'H:i';
- $o .= "</select>";
+ $minjs = $min ? ",minDate: new Date({$min->getTimestamp()}*1000), yearStart: " . $min->format('Y') : '';
+ $maxjs = $max ? ",maxDate: new Date({$max->getTimestamp()}*1000), yearEnd: " . $max->format('Y') : '';
+
+ $input_text = $default ? 'value="' . date($dateformat, $default->getTimestamp()) . '"' : '';
+ $defaultdatejs = $default ? ",defaultDate: new Date({$default->getTimestamp()}*1000)" : '';
+
+ $pickers = '';
+ if(!$pickdate) $pickers .= ',datepicker: false';
+ if(!$picktime) $pickers .= ',timepicker: false';
+
+ $extra_js = '';
+ if($minfrom != '')
+ $extra_js .= "\$('#$minfrom').data('xdsoft_datetimepicker').setOptions({onChangeDateTime: function (currentDateTime) { \$('#$id').data('xdsoft_datetimepicker').setOptions({minDate: currentDateTime})}})";
+
+ if($maxfrom != '')
+ $extra_js .= "\$('#$maxfrom').data('xdsoft_datetimepicker').setOptions({onChangeDateTime: function (currentDateTime) { \$('#$id').data('xdsoft_datetimepicker').setOptions({maxDate: currentDateTime})}})";
+
+ $readable_format = $dateformat;
+ $readable_format = str_replace('Y','yyyy',$readable_format);
+ $readable_format = str_replace('m','mm',$readable_format);
+ $readable_format = str_replace('d','dd',$readable_format);
+ $readable_format = str_replace('H','HH',$readable_format);
+ $readable_format = str_replace('i','MM',$readable_format);
+
+ $o .= "<div class='date'><input type='text' placeholder='$readable_format' name='$id' id='$id' $input_text />";
+ $o .= '</div>';
+ $o .= "<script type='text/javascript'>\$(function () {var picker = \$('#$id').datetimepicker({step:5,format:'$dateformat' $minjs $maxjs $pickers $defaultdatejs}); $extra_js})</script>";
return $o;
}
-
-
-
-
-
-
-
// implements "3 seconds ago" etc.
// based on $posted_date, (UTC).
// Results relative to current timezone
@@ -271,7 +255,7 @@ function relative_date($posted_date,$format = null) {
$abs = strtotime($localtime);
- if (is_null($posted_date) || $posted_date === '0000-00-00 00:00:00' || $abs === False) {
+ if (is_null($posted_date) || $posted_date === NULL_DATE || $abs === False) {
return t('never');
}
@@ -487,7 +471,10 @@ function update_birthdays() {
require_once('include/permissions.php');
$r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash
- WHERE abook_dob > utc_timestamp() + interval 7 day and abook_dob < utc_timestamp() + interval 14 day");
+ WHERE abook_dob > %s + interval %s and abook_dob < %s + interval %s",
+ db_utcnow(), db_quoteinterval('7 day'),
+ db_utcnow(), db_quoteinterval('14 day')
+ );
if($r) {
foreach($r as $rr) {
@@ -509,11 +496,11 @@ function update_birthdays() {
$z = event_store_event($ev);
if($z) {
$item_id = event_store_item($ev,$z);
- q("update abook set abook_dob = '%s' where abook_id = %d limit 1",
+ q("update abook set abook_dob = '%s' where abook_id = %d",
dbesc(intval($rr['abook_dob']) + 1 . substr($rr['abook_dob'],4)),
intval($rr['abook_id'])
);
}
}
}
-} \ No newline at end of file
+}
diff --git a/include/dba/dba_driver.php b/include/dba/dba_driver.php
index c829c3714..4a0f5e37b 100755
--- a/include/dba/dba_driver.php
+++ b/include/dba/dba_driver.php
@@ -1,42 +1,134 @@
-<?php /** @file */
+<?php
+/**
+ * @file dba_driver.php
+ * @brief some database related functions and abstract driver class.
+ *
+ * This file contains the abstract database driver class dba_driver and some
+ * functions for working with databases.
+ */
-function dba_factory($server, $port,$user,$pass,$db,$install = false) {
+/**
+ * @brief Returns the database driver object.
+ *
+ * If available it will use PHP's mysqli otherwise mysql driver.
+ *
+ * @param string $server DB server name
+ * @param string $port DB port
+ * @param string $user DB username
+ * @param string $pass DB password
+ * @param string $db database name
+ * @param string $dbtype 0 for mysql, 1 for postgres
+ * @param bool $install Defaults to false
+ * @return null|dba_driver A database driver object (dba_mysql|dba_mysqli) or null if no driver found.
+ */
+function dba_factory($server, $port,$user,$pass,$db,$dbtype,$install = false) {
$dba = null;
- if(class_exists('mysqli')) {
- if (is_null($port)) $port = ini_get("mysqli.default_port");
- require_once('include/dba/dba_mysqli.php');
- $dba = new dba_mysqli($server, $port,$user,$pass,$db,$install);
- }
- else {
- if (is_null($port)) $port = "3306";
- require_once('include/dba/dba_mysql.php');
- $dba = new dba_mysql($server, $port,$user,$pass,$db,$install);
+
+ $dbtype = intval($dbtype);
+
+ if($dbtype == DBTYPE_POSTGRES) {
+ require_once('include/dba/dba_postgres.php');
+ if(is_null($port)) $port = 5432;
+ $dba = new dba_postgres($server, $port, $user, $pass, $db, $install);
+ } else {
+ if(class_exists('mysqli')) {
+ if (is_null($port)) $port = ini_get("mysqli.default_port");
+ require_once('include/dba/dba_mysqli.php');
+ $dba = new dba_mysqli($server, $port,$user,$pass,$db,$install);
+ } else {
+ if (is_null($port)) $port = "3306";
+ require_once('include/dba/dba_mysql.php');
+ $dba = new dba_mysql($server, $port,$user,$pass,$db,$install);
+ }
}
+ define('NULL_DATE', $dba->get_null_date());
+ define('ACTIVE_DBTYPE', $dbtype);
return $dba;
}
-
+/**
+ * @brief abstract database driver class.
+ *
+ * This class gets extended by the real database driver classes, e.g. dba_mysql,
+ * dba_mysqli.
+ */
abstract class dba_driver {
-
+ // legacy behavior
+ const INSTALL_SCRIPT='install/schema_mysql.sql';
+ const NULL_DATE = '0000-00-00 00:00:00';
+ const UTC_NOW = 'UTC_TIMESTAMP()';
+
protected $debug = 0;
protected $db;
public $connected = false;
public $error = false;
- abstract function connect($server, $port, $user,$pass,$db);
+ /**
+ * @brief Connect to the database.
+ *
+ * This abstract function needs to be implemented in the real driver.
+ *
+ * @param string $server DB server name
+ * @param string $port DB port
+ * @param string $user DB username
+ * @param string $pass DB password
+ * @param string $db database name
+ * @return bool
+ */
+ abstract function connect($server, $port, $user, $pass, $db);
+
+ /**
+ * @brief Perform a DB query with the SQL statement $sql.
+ *
+ * This abstract function needs to be implemented in the real driver.
+ *
+ * @param string $sql The SQL query to execute
+ */
abstract function q($sql);
+
+ /**
+ * @brief Escape a string before being passed to a DB query.
+ *
+ * This abstract function needs to be implemented in the real driver.
+ *
+ * @param string $str The string to escape.
+ */
abstract function escape($str);
+
+ /**
+ * @brief Close the database connection.
+ *
+ * This abstract function needs to be implemented in the real driver.
+ */
abstract function close();
+ /**
+ * @brief Return text name for db driver
+ *
+ * This abstract function needs to be implemented in the real driver.
+ */
+ abstract function getdriver();
+
function __construct($server, $port, $user,$pass,$db,$install = false) {
- if(($install) && (! $this->install($server, $port, $user,$pass,$db))) {
+ if(($install) && (! $this->install($server, $port, $user, $pass, $db))) {
return;
}
- $this->connect($server, $port, $user,$pass,$db);
+ $this->connect($server, $port, $user, $pass, $db);
}
+ function get_null_date() {
+ return static::NULL_DATE;
+ }
+
+ function get_install_script() {
+ return static::INSTALL_SCRIPT;
+ }
+
+ function utcnow() {
+ return static::UTC_NOW;
+ }
function install($server,$user,$pass,$db) {
if (!(strlen($server) && strlen($user))){
@@ -56,7 +148,11 @@ abstract class dba_driver {
return true;
}
-
+ /**
+ * @brief Sets the database driver's debugging state.
+ *
+ * @param int $dbg 0 to disable debugging
+ */
function dbg($dbg) {
$this->debug = $dbg;
}
@@ -67,9 +163,30 @@ abstract class dba_driver {
}
}
-}
+ function quote_interval($txt) {
+ return $txt;
+ }
+
+ function optimize_table($table) {
+ q('OPTIMIZE TABLE '.$table);
+ }
+
+ function concat($fld, $sep) {
+ return 'GROUP_CONCAT(DISTINCT '.$fld.' SEPARATOR \''.$sep.'\')';
+ }
+
+ function escapebin($str) {
+ return $this->escape($str);
+ }
+
+ function unescapebin($str) {
+ return $str;
+ }
+} // end abstract dba_driver class
+
+// Procedural functions
function printable($s) {
$s = preg_replace("~([\x01-\x08\x0E-\x0F\x10-\x1F\x7F-\xFF])~",".", $s);
@@ -79,24 +196,79 @@ function printable($s) {
return $s;
}
-// Procedural functions
-
+/**
+ * @brief set database driver debugging state.
+ *
+ * @param int $state 0 to disable debugging
+ */
function dbg($state) {
global $db;
+
if($db)
- $db->dbg($state);
+ $db->dbg($state);
}
-
+/**
+ * @brief Escape strings being passed to DB queries.
+ *
+ * Always escape strings being used in DB queries. This function returns the
+ * escaped string. Integer DB parameters should all be proven integers by
+ * wrapping with intval().
+ *
+ * @param string $str A string to pass to a DB query
+ * @return Return an escaped string of the value to pass to a DB query.
+ */
function dbesc($str) {
global $db;
+
if($db && $db->connected)
return($db->escape($str));
else
- return(str_replace("'","\\'",$str));
+ return(str_replace("'", "\\'", $str));
+}
+function dbescbin($str) {
+ global $db;
+ return $db->escapebin($str);
+}
+
+function dbunescbin($str) {
+ global $db;
+ return $db->unescapebin($str);
}
+function dbescdate($date) {
+ if(ACTIVE_DBTYPE == DBTYPE_POSTGRES && $date == '0000-00-00 00:00:00') {
+ $date = NULL_DATE;
+ } else if(ACTIVE_DBTYPE != DBTYPE_POSTGRES && $date == '0001-01-01 00:00:00') {
+ $date = NULL_DATE;
+ }
+ return $date;
+}
+
+function db_quoteinterval($txt) {
+ global $db;
+ return $db->quote_interval($txt);
+}
+
+function dbesc_identifier($str) {
+ global $db;
+ return $db->escape_identifier($txt);
+}
+
+function db_utcnow() {
+ global $db;
+ return $db->utcnow();
+}
+
+function db_optimizetable($table) {
+ global $db;
+ $db->optimize_table($table);
+}
+function db_concat($fld, $sep) {
+ global $db;
+ return $db->concat($fld, $sep);
+}
// Function: q($sql,$args);
// Description: execute SQL query with printf style args.
@@ -104,64 +276,114 @@ function dbesc($str) {
// 'user', 1);
+/**
+ * @brief Execute a SQL query with printf style args.
+ *
+ * printf style arguments %s and %d are replaced with variable arguments, which
+ * should each be appropriately dbesc() or intval().
+ * SELECT queries return an array of results or false if SQL or DB error. Other
+ * queries return true if the command was successful or false if it wasn't.
+ *
+ * Example:
+ * $r = q("SELECT * FROM `%s` WHERE `uid` = %d",
+ * 'user', 1);
+ *
+ * @param string $sql The SQL query to execute
+ * @return bool|array
+ */
function q($sql) {
-
global $db;
+
$args = func_get_args();
unset($args[0]);
if($db && $db->connected) {
- $stmt = vsprintf($sql,$args);
- if($stmt === false)
- logger('dba: vsprintf error: ' . print_r(debug_backtrace(),true));
+ $stmt = vsprintf($sql, $args);
+ 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));
+ else
+ logger('dba: vsprintf error: ' . print_r(debug_backtrace(), true));
+ }
return $db->q($stmt);
}
- /**
- *
+ /*
* This will happen occasionally trying to store the
* session data after abnormal program termination
- *
*/
logger('dba: no database: ' . print_r($args,true));
- return false;
+ return false;
}
/**
+ * @brief Raw DB query, no arguments.
*
- * Raw db query, no arguments
+ * This function executes a raw DB query without any arguments.
*
+ * @param string $sql The SQL query to execute
*/
-
-
function dbq($sql) {
-
global $db;
+
if($db && $db->connected)
$ret = $db->q($sql);
else
$ret = false;
+
return $ret;
}
-// Caller is responsible for ensuring that any integer arguments to
+
+// Caller is responsible for ensuring that any integer arguments to
// dbesc_array are actually integers and not malformed strings containing
// SQL injection vectors. All integer array elements should be specifically
// cast to int to avoid trouble.
-
-
function dbesc_array_cb(&$item, $key) {
- if(is_string($item))
+ if(is_string($item)) {
+ if($item == '0000-00-00 00:00:00' && ACTIVE_DBTYPE == DBTYPE_POSTGRES)
+ $item = '0001-01-01 00:00:00';
+ else if($item == '0001-01-01 00:00:00' && ACTIVE_DBTYPE == DBTYPE_MYSQL)
+ $item = '0000-00-00 00:00:00';
$item = dbesc($item);
+ }
}
-
function dbesc_array(&$arr) {
if(is_array($arr) && count($arr)) {
array_walk($arr,'dbesc_array_cb');
}
}
+
+function db_getfunc($f) {
+ $lookup = array(
+ 'rand'=>array(
+ DBTYPE_MYSQL=>'RAND()',
+ DBTYPE_POSTGRES=>'RANDOM()'
+ ),
+ 'utc_timestamp'=>array(
+ DBTYPE_MYSQL=>'UTC_TIMESTAMP()',
+ DBTYPE_POSTGRES=>"now() at time zone 'UTC'"
+ ),
+ 'regexp'=>array(
+ DBTYPE_MYSQL=>'REGEXP',
+ DBTYPE_POSTGRES=>'~'
+ ),
+ '^'=>array(
+ DBTYPE_MYSQL=>'^',
+ DBTYPE_POSTGRES=>'#'
+ )
+ );
+ $f = strtolower($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);
+ return $f;
+}
+
diff --git a/include/dba/dba_mysql.php b/include/dba/dba_mysql.php
index f5a2a47ba..3cadad6dc 100755
--- a/include/dba/dba_mysql.php
+++ b/include/dba/dba_mysql.php
@@ -59,5 +59,9 @@ class dba_mysql extends dba_driver {
mysql_close($this->db);
$this->connected = false;
}
+
+ function getdriver() {
+ return 'mysql';
+ }
}
diff --git a/include/dba/dba_mysqli.php b/include/dba/dba_mysqli.php
index 19907705b..74a999974 100755
--- a/include/dba/dba_mysqli.php
+++ b/include/dba/dba_mysqli.php
@@ -40,7 +40,7 @@ class dba_mysqli extends dba_driver {
if(($result === true) || ($result === false)) {
if($this->debug) {
- logger('dba_mysqli: DEBUG: returns ' . (($result) ? 'true' : 'false'));
+ logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returns ' . (($result) ? 'true' : 'false'));
}
return $result;
}
@@ -72,5 +72,9 @@ class dba_mysqli extends dba_driver {
$this->db->close();
$this->connected = false;
}
+
+ function getdriver() {
+ return 'mysqli';
+ }
} \ No newline at end of file
diff --git a/include/dba/dba_postgres.php b/include/dba/dba_postgres.php
new file mode 100644
index 000000000..ba4366d13
--- /dev/null
+++ b/include/dba/dba_postgres.php
@@ -0,0 +1,116 @@
+<?php
+
+require_once('include/dba/dba_driver.php');
+
+
+class dba_postgres extends dba_driver {
+ const INSTALL_SCRIPT='install/schema_postgres.sql';
+ const NULL_DATE = '0001-01-01 00:00:00';
+ const UTC_NOW = "now() at time zone 'UTC'";
+
+ function connect($server,$port,$user,$pass,$db) {
+ if(!$port) $port = 5432;
+ $connstr = 'host=' . $server . ' port='.$port . ' user=' . $user . ' password=' . $pass . ' dbname='. $db;
+ $this->db = pg_connect($connstr);
+ if($this->db !== false) {
+ $this->connected = true;
+ } else {
+ $this->connected = false;
+ }
+ $this->q("SET standard_conforming_strings = 'off'; SET backslash_quote = 'on';"); // emulate mysql string escaping to prevent massive code-clobber
+ return $this->connected;
+ }
+
+ function q($sql) {
+ if((! $this->db) || (! $this->connected))
+ return false;
+
+ if(!strpos($sql, ';'))
+ $sql .= ';';
+
+ if(strpos($sql, '`')) // this is a hack. quoted identifiers should be replaced everywhere in the code with dbesc_identifier(), remove this once it is
+ $sql = str_replace('`', '"', $sql);
+
+ $this->error = '';
+ $result = @pg_query($this->db, $sql);
+ if(file_exists('db-allqueries.out')) {
+ $bt = debug_backtrace();
+ $trace = array();
+ foreach($bt as $frame) {
+ if(!empty($frame['file']) && @strstr($frame['file'], $_SERVER['DOCUMENT_ROOT']))
+ $frame['file'] = substr($frame['file'], strlen($_SERVER['DOCUMENT_ROOT'])+1);
+
+ $trace[] = $frame['file'] . ':' . $frame['function'] . '():' . $frame['line'] ;
+ }
+ $compact = join(', ', $trace);
+ file_put_contents('db-allqueries.out', datetime_convert() . ": " . $sql . ' is_resource: '.var_export(is_resource($result), true).', backtrace: '.$compact."\n\n", FILE_APPEND);
+ }
+
+ if($result === false)
+ $this->error = pg_last_error($this->db);
+
+ if($result === false || $this->error) {
+ //logger('dba_postgres: ' . printable($sql) . ' returned false.' . "\n" . $this->error);
+ if(file_exists('dbfail.out'))
+ file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . ' returned false' . "\n" . $this->error . "\n", FILE_APPEND);
+ }
+
+ if(($result === true) || ($result === false))
+ return $result;
+
+ if(pg_result_status($result) == PGSQL_COMMAND_OK)
+ return true;
+
+ $r = array();
+ if(pg_num_rows($result)) {
+ while($x = pg_fetch_array($result, null, PGSQL_ASSOC))
+ $r[] = $x;
+ pg_free_result($result);
+ if($this->debug)
+ logger('dba_postgres: ' . printable(print_r($r,true)));
+ }
+ return $r;
+ }
+
+ function escape($str) {
+ if($this->db && $this->connected) {
+ $x = @pg_escape_string($this->db, $str);
+ return $x;
+ }
+ }
+
+ function escapebin($str) {
+ return pg_escape_bytea($str);
+ }
+
+ function unescapebin($str) {
+ return pg_unescape_bytea($str);
+ }
+
+ function close() {
+ if($this->db)
+ pg_close($this->db);
+ $this->connected = false;
+ }
+
+ function quote_interval($txt) {
+ return "'$txt'";
+ }
+
+ function escape_identifier($str) {
+ return pg_escape_identifier($this->db, $str);
+ }
+
+ function optimize_table($table) {
+ // perhaps do some equivalent thing here, vacuum, etc? I think this is the DBA's domain anyway. Applications should not need to muss with this.
+ // for now do nothing without a compelling reason. function overrides default legacy mysql.
+ }
+
+ function concat($fld, $sep) {
+ return 'string_agg(' . $fld . ',\'' . $sep . '\')';
+ }
+
+ function getdriver() {
+ return 'pgsql';
+ }
+} \ No newline at end of file
diff --git a/include/deliver.php b/include/deliver.php
index f4fae6061..47d8562df 100644
--- a/include/deliver.php
+++ b/include/deliver.php
@@ -24,13 +24,13 @@ function deliver_run($argv, $argc) {
$result = z_post_url($r[0]['outq_posturl'],$r[0]['outq_msg']);
if($result['success'] && $result['return_code'] < 300) {
logger('deliver: queue post success to ' . $r[0]['outq_posturl'], LOGGER_DEBUG);
- $y = q("delete from outq where outq_hash = '%s' limit 1",
+ $y = q("delete from outq where outq_hash = '%s'",
dbesc($argv[$x])
);
}
else {
logger('deliver: queue post returned ' . $result['return_code'] . ' from ' . $r[0]['outq_posturl'],LOGGER_DEBUG);
- $y = q("update outq set outq_updated = '%s' where outq_hash = '%s' limit 1",
+ $y = q("update outq set outq_updated = '%s' where outq_hash = '%s'",
dbesc(datetime_convert()),
dbesc($argv[$x])
);
@@ -38,16 +38,34 @@ function deliver_run($argv, $argc) {
continue;
}
- if($r[0]['outq_posturl'] === z_root() . '/post') {
+ $notify = json_decode($r[0]['outq_notify'],true);
+
+ // Check if this is a conversation request packet. It won't have outq_msg
+ // but will be an encrypted packet - so will need to be handed off to
+ // web delivery rather than processed inline.
+
+ $sendtoweb = false;
+ if(array_key_exists('iv',$notify) && (! $r[0]['outq_msg']))
+ $sendtoweb = true;
+
+ if(($r[0]['outq_posturl'] === z_root() . '/post') && (! $sendtoweb)) {
logger('deliver: local delivery', LOGGER_DEBUG);
// local delivery
// we should probably batch these and save a few delivery processes
- // If there is no outq_msg, this is a refresh_all message which does not require local handling
- if($r[0]['outq_msg']) {
- $msg = array('body' => json_encode(array('pickup' => array(array('notify' => json_decode($r[0]['outq_notify'],true),'message' => json_decode($r[0]['outq_msg'],true))))));
- zot_import($msg,z_root());
- $r = q("delete from outq where outq_hash = '%s' limit 1",
+ if($r[0]['outq_msg']) {
+ $m = json_decode($r[0]['outq_msg'],true);
+ if(array_key_exists('message_list',$m)) {
+ foreach($m['message_list'] as $mm) {
+ $msg = array('body' => json_encode(array('pickup' => array(array('notify' => $notify,'message' => $mm)))));
+ zot_import($msg,z_root());
+ }
+ }
+ else {
+ $msg = array('body' => json_encode(array('pickup' => array(array('notify' => $notify,'message' => $m)))));
+ zot_import($msg,z_root());
+ }
+ $r = q("delete from outq where outq_hash = '%s'",
dbesc($argv[$x])
);
}
@@ -59,7 +77,7 @@ function deliver_run($argv, $argc) {
zot_process_response($r[0]['outq_posturl'],$result, $r[0]);
}
else {
- $y = q("update outq set outq_updated = '%s' where outq_hash = '%s' limit 1",
+ $y = q("update outq set outq_updated = '%s' where outq_hash = '%s'",
dbesc(datetime_convert()),
dbesc($argv[$x])
);
diff --git a/include/diaspora.php b/include/diaspora.php
index 0b598ffb2..e494aac0f 100755
--- a/include/diaspora.php
+++ b/include/diaspora.php
@@ -58,7 +58,7 @@ function diaspora_dispatch($importer,$msg,$attempt=1) {
$xmlbase = $parsed_xml->post;
- logger('diaspora_dispatch: ' . print_r($xmlbase,true), LOGGER_DEBUG);
+// logger('diaspora_dispatch: ' . print_r($xmlbase,true), LOGGER_DATA);
if($xmlbase->request) {
@@ -142,6 +142,7 @@ function diaspora_process_outbound($arr) {
'cmd' => $cmd,
'expire' => $expire,
'mail' => $mail,
+ 'location' => $location,
'fsuggest' => $fsuggest,
'normal_mode' => $normal_mode,
'packet_type' => $packet_type,
@@ -150,6 +151,10 @@ function diaspora_process_outbound($arr) {
*/
+ if($arr['location'])
+ return;
+
+
$target_item = $arr['target_item'];
if($target_item && array_key_exists('item_flags',$target_item) && ($target_item['item_flags'] & ITEM_OBSCURED)) {
@@ -160,8 +165,7 @@ function diaspora_process_outbound($arr) {
$target_item['body'] = crypto_unencapsulate(json_decode($target_item['body'],true),$key);
}
- if($arr['walltowall'])
- return;
+
if($arr['env_recips']) {
$hashes = array();
@@ -202,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);
@@ -234,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);
@@ -252,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;
@@ -541,7 +537,7 @@ function diaspora_decode($importer,$xml) {
* </decrypted_header>
*/
- logger('decrypted: ' . $decrypted, LOGGER_DEBUG);
+ logger('decrypted: ' . $decrypted, LOGGER_DATA);
$idom = parse_xml_string($decrypted,false);
$inner_iv = base64_decode($idom->iv);
@@ -661,7 +657,7 @@ function diaspora_request($importer,$xml) {
$newperms = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK|PERMS_W_STREAM|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT|PERMS_R_STORAGE|PERMS_R_PAGES;
- $r = q("update abook set abook_their_perms = %d where abook_id = %d and abook_channel = %d limit 1",
+ $r = q("update abook set abook_their_perms = %d where abook_id = %d and abook_channel = %d",
intval($newperms),
intval($contact['abook_id']),
intval($importer['channel_id'])
@@ -677,19 +673,18 @@ function diaspora_request($importer,$xml) {
return;
}
- $default_perms = 0;
- // look for default permissions to apply in return - e.g. auto-friend
- $z = q("select * from abook where abook_channel = %d and (abook_flags & %d) limit 1",
- intval($importer['channel_id']),
- intval(ABOOK_FLAG_SELF)
- );
-
- if($z)
- $default_perms = intval($z[0]['abook_my_perms']);
-
+ $role = get_pconfig($channel['channel_id'],'system','permissions_role');
+ if($role) {
+ $x = get_role_perms($role);
+ if($x['perms_auto'])
+ $default_perms = $x['perms_accept'];
+ }
+ if(! $default_perms)
+ $default_perms = intval(get_pconfig($channel['channel_id'],'system','autoperms'));
+
$their_perms = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK|PERMS_W_STREAM|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT|PERMS_R_STORAGE|PERMS_R_PAGES;
- $r = q("insert into abook ( abook_account, abook_channel, abook_xchan, abook_my_perms, abook_their_perms, abook_closeness, abook_rating, abook_created, abook_updated, abook_connected, abook_dob, abook_flags, abook_profile) values ( %d, %d, '%s' %d %d, %d, %d, '%s', '%s', '%s', '%s', %d, '%s')",
+ $r = q("insert into abook ( abook_account, abook_channel, abook_xchan, abook_my_perms, abook_their_perms, abook_closeness, abook_rating, abook_created, abook_updated, abook_connected, abook_dob, abook_flags) values ( %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', %d )",
intval($importer['channel_account_id']),
intval($importer['channel_id']),
dbesc($ret['xchan_hash']),
@@ -700,7 +695,7 @@ function diaspora_request($importer,$xml) {
dbesc(datetime_convert()),
dbesc(datetime_convert()),
dbesc(datetime_convert()),
- dbesc('0000-00-00 00:00:00'),
+ dbesc(NULL_DATE),
intval(($default_perms) ? 0 : ABOOK_FLAG_PENDING)
);
@@ -708,7 +703,7 @@ function diaspora_request($importer,$xml) {
if($r) {
logger("New Diaspora introduction received for {$importer['channel_name']}");
- $new_connection = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_xchan = '%s' order by abook_created desc limit 1",
+ $new_connection = q("select * from abook left join xchan on abook_xchan = xchan_hash left join hubloc on hubloc_hash = xchan_hash where abook_channel = %d and abook_xchan = '%s' order by abook_created desc limit 1",
intval($importer['channel_id']),
dbesc($ret['xchan_hash'])
);
@@ -723,7 +718,7 @@ function diaspora_request($importer,$xml) {
if($default_perms) {
// Send back a sharing notification to them
- diaspora_share($importer['channel_id'],$new_connection[0]);
+ diaspora_share($importer,$new_connection[0]);
}
}
@@ -761,8 +756,6 @@ function diaspora_post($importer,$xml,$msg) {
$app = notags(xmlify($xml->provider_display_name));
-
-
if($diaspora_handle != $msg['author']) {
logger('diaspora_post: Potential forgery. Message handle is not the same as envelope sender.');
return 202;
@@ -805,6 +798,12 @@ function diaspora_post($importer,$xml,$msg) {
$body = diaspora2bb($xml->raw_message);
+ if($xml->photo) {
+ $body = '[img]' . $xml->photo->remote_photo_path . $xml->photo->remote_photo_name . '[/img]' . "\n\n" . $body;
+ $body = scale_external_images($body);
+ }
+
+
//WTF? FIXME
// Add OEmbed and other information to the body
// $body = add_page_info_to_body($body, false, true);
@@ -821,7 +820,7 @@ function diaspora_post($importer,$xml,$msg) {
foreach($tags as $tag) {
if(strpos($tag,'#') === 0) {
- if(strpos($tag,'[url='))
+ if((strpos($tag,'[url=')) || (strpos($tag,'[zrl')))
continue;
// don't link tags that are already embedded in links
@@ -858,6 +857,22 @@ function diaspora_post($importer,$xml,$msg) {
}
}
+ $cnt = preg_match_all('/@\[zrl=(.*?)\](.*?)\[\/zrl\]/ism',$body,$matches,PREG_SET_ORDER);
+ if($cnt) {
+ foreach($matches as $mtch) {
+ // don't include plustags in the term
+ $term = ((substr($mtch[2],-1,1) === '+') ? substr($mtch[2],0,-1) : $mtch[2]);
+ $datarray['term'][] = array(
+ 'uid' => $importer['channel_id'],
+ 'type' => TERM_MENTION,
+ 'otype' => TERM_OBJ_POST,
+ 'term' => $term,
+ 'url' => $mtch[1]
+ );
+ }
+ }
+
+
// this won't work for Friendica or Redmatrix but it's probably the best we can do.
$plink = 'https://'.substr($diaspora_handle,strpos($diaspora_handle,'@')+1).'/posts/'.$guid;
@@ -886,9 +901,52 @@ 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->post->reshare)
+ $xml = $source_xml->post->reshare;
+ else
+ return false;
+
+ if($xml->root_diaspora_id && $xml->root_guid && $recurse < 15) {
+ $orig_author = notags(unxmlify($xml->root_diaspora_id));
+ $orig_guid = notags(unxmlify($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('diaspora_reshare: init: ' . print_r($xml,true), LOGGER_DATA);
$a = get_app();
$guid = notags(unxmlify($xml->guid));
@@ -924,32 +982,18 @@ 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']);
-
- $x = str_replace(array('<activity_streams-photo>','</activity_streams-photo>'),array('<asphoto>','</asphoto>'),$x['body']);
- $source_xml = parse_xml_string($x,false);
+ $source_xml = get_diaspora_reshare_xml($source_url);
- if(strlen($source_xml->post->asphoto->objectId) && ($source_xml->post->asphoto->objectId != 0) && ($source_xml->post->asphoto->image_url)) {
- $body = '[url=' . notags(unxmlify($source_xml->post->asphoto->image_url)) . '][img]' . notags(unxmlify($source_xml->post->asphoto->objectId)) . '[/img][/url]' . "\n";
- $body = scale_external_images($body,false);
- }
- elseif($source_xml->post->asphoto->image_url) {
- $body = '[img]' . notags(unxmlify($source_xml->post->asphoto->image_url)) . '[/img]' . "\n";
- $body = scale_external_images($body);
- }
- elseif($source_xml->post->status_message) {
+ 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 AND
+ if($source_xml->post->status_message->photo->remote_photo_path &&
$source_xml->post->status_message->photo->remote_photo_name) {
$remote_photo_path = notags(unxmlify($source_xml->post->status_message->photo->remote_photo_path));
@@ -979,12 +1023,19 @@ function diaspora_reshare($importer,$xml,$msg) {
$person = find_diaspora_person_by_handle($orig_author);
- /*if(is_array($person) && x($person,'name') && x($person,'url'))
- $details = '[url=' . $person['url'] . ']' . $person['name'] . '[/url]';
- else
- $details = $orig_author;
+ if($person) {
+ $orig_author_name = $person['xchan_name'];
+ $orig_author_link = $person['xchan_url'];
+ $orig_author_photo = $person['xchan_photo_m'];
+ }
- $prefix = html_entity_decode("&#x2672; ", ENT_QUOTES, 'UTF-8') . $details . "\n";*/
+ $newbody = "[share author='" . urlencode($orig_author_name)
+ . "' profile='" . $orig_author_link
+ . "' avatar='" . $orig_author_photo
+ . "' link='" . $orig_url
+ . "' posted='" . datetime_convert('UTC','UTC',unxmlify($source_xml->post->status_message->created_at))
+ . "' message_id='" . unxmlify($source_xml->post->status_message->guid)
+ . "']" . $body . "[/share]";
$created = unxmlify($xml->created_at);
@@ -994,7 +1045,7 @@ function diaspora_reshare($importer,$xml,$msg) {
$str_tags = '';
- $tags = get_tags($body);
+ $tags = get_tags($newbody);
if(count($tags)) {
@@ -1003,18 +1054,18 @@ function diaspora_reshare($importer,$xml,$msg) {
foreach($tags as $tag) {
if(strpos($tag,'#') === 0) {
- if(strpos($tag,'[url='))
+ if((strpos($tag,'[url=')) || (strpos($tag,'[zrl')))
continue;
// don't link tags that are already embedded in links
- if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$body))
+ if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$newbody))
continue;
- if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$body))
+ if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$newbody))
continue;
$basetag = str_replace('_',' ',substr($tag,1));
- $body = str_replace($tag,'#[url=' . $a->get_baseurl() . '/search?tag=' . rawurlencode($basetag) . ']' . $basetag . '[/url]',$body);
+ $newbody = str_replace($tag,'#[url=' . $a->get_baseurl() . '/search?tag=' . rawurlencode($basetag) . ']' . $basetag . '[/url]',$newbody);
$datarray['term'][] = array(
'uid' => $importer['channel_id'],
@@ -1027,7 +1078,7 @@ function diaspora_reshare($importer,$xml,$msg) {
}
}
- $cnt = preg_match_all('/@\[url=(.*?)\](.*?)\[\/url\]/ism',$body,$matches,PREG_SET_ORDER);
+ $cnt = preg_match_all('/@\[url=(.*?)\](.*?)\[\/url\]/ism',$newbody,$matches,PREG_SET_ORDER);
if($cnt) {
foreach($matches as $mtch) {
$datarray['term'][] = array(
@@ -1040,7 +1091,7 @@ function diaspora_reshare($importer,$xml,$msg) {
}
}
- // This won't work
+ // This won't work on redmatrix
$plink = 'https://'.substr($diaspora_handle,strpos($diaspora_handle,'@')+1).'/posts/'.$guid;
$datarray['uid'] = $importer['channel_id'];
@@ -1049,9 +1100,9 @@ function diaspora_reshare($importer,$xml,$msg) {
$datarray['item_private'] = $private;
$datarray['plink'] = $plink;
$datarray['owner_xchan'] = $contact['xchan_hash'];
- $datarray['author_xchan'] = $person['xchan_hash'];
+ $datarray['author_xchan'] = $contact['xchan_hash'];
- $datarray['body'] = $body;
+ $datarray['body'] = $newbody;
$datarray['app'] = 'Diaspora';
@@ -1290,7 +1341,7 @@ function diaspora_comment($importer,$xml,$msg) {
foreach($tags as $tag) {
if(strpos($tag,'#') === 0) {
- if(strpos($tag,'[url='))
+ if((strpos($tag,'[url=')) || (strpos($tag,'[zrl')))
continue;
// don't link tags that are already embedded in links
@@ -1332,6 +1383,8 @@ function diaspora_comment($importer,$xml,$msg) {
$datarray['mid'] = $guid;
$datarray['parent_mid'] = $parent_item['mid'];
+ // set the route to that of the parent so downstream hubs won't reject it.
+ $datarray['route'] = $parent_item['route'];
// No timestamps for comments? OK, we'll the use current time.
$datarray['changed'] = $datarray['created'] = $datarray['edited'] = datetime_convert();
@@ -1342,11 +1395,18 @@ function diaspora_comment($importer,$xml,$msg) {
$datarray['body'] = $body;
- $datarray['app'] = 'Diaspora';
+ if(strstr($person['xchan_network'],'friendica'))
+ $app = 'Friendica';
+ else
+ $app = 'Diaspora';
+
+ $datarray['app'] = $app;
if(! $parent_author_signature) {
- $datarray['diaspora_meta'] = array('signer' => $diaspora_handle, 'body' => $text,
- 'signed_text' => $signed_data, 'signature' => base64_encode($author_signature));
+ $key = get_config('system','pubkey');
+ $x = array('signer' => $diaspora_handle, 'body' => $text,
+ 'signed_text' => $signed_data, 'signature' => base64_encode($author_signature));
+ $datarray['diaspora_meta'] = json_encode(crypto_encapsulate(json_encode($x),$key));
}
$result = item_store($datarray);
@@ -1355,13 +1415,6 @@ function diaspora_comment($importer,$xml,$msg) {
$message_id = $result['item_id'];
if(($parent_item['item_flags'] & ITEM_ORIGIN) && (! $parent_author_signature)) {
- q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
- intval($message_id),
- dbesc($signed_data),
- dbesc(base64_encode($author_signature)),
- dbesc($diaspora_handle)
- );
-
// if the message isn't already being relayed, notify others
// the existence of parent_author_signature means the parent_author or owner
// is already relaying.
@@ -1408,7 +1461,8 @@ function diaspora_conversation($importer,$xml,$msg) {
return;
}
- if(($contact['rel'] == CONTACT_IS_FOLLOWER) || ($contact['blocked']) || ($contact['readonly'])) {
+
+ if(! perm_is_allowed($importer['channel_id'],$contact['xchan_hash'],'post_mail')) {
logger('diaspora_conversation: Ignoring this author.');
return 202;
}
@@ -1508,14 +1562,20 @@ function diaspora_conversation($importer,$xml,$msg) {
continue;
}
- q("insert into mail ( `uid`, `convid`, `from_xchan`,`to_xchan`,`title`,`body`,`mail_flags`,`mid`,`parent_mid`,`created`) values ( %d, %d, '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s')",
+ $key = get_config('system','pubkey');
+ if($subject)
+ $subject = json_encode(crypto_encapsulate($subject,$key));
+ if($body)
+ $body = json_encode(crypto_encapsulate($body,$key));
+
+ q("insert into mail ( `channel_id`, `convid`, `from_xchan`,`to_xchan`,`title`,`body`,`mail_flags`,`mid`,`parent_mid`,`created`) values ( %d, %d, '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s')",
intval($importer['channel_id']),
intval($conversation['id']),
dbesc($person['xchan_hash']),
dbesc($importer['channel_hash']),
dbesc($subject),
dbesc($body),
- 0,
+ intval(MAIL_OBSCURED),
dbesc($msg_guid),
dbesc($parent_uri),
dbesc($msg_created_at)
@@ -1564,7 +1624,7 @@ function diaspora_message($importer,$xml,$msg) {
$msg_diaspora_handle = notags(unxmlify($xml->diaspora_handle));
$msg_conversation_guid = notags(unxmlify($xml->conversation_guid));
- $parent_uri = $diaspora_handle . ':' . $msg_parent_guid;
+ $parent_uri = $msg_parent_guid;
$contact = diaspora_get_contact_by_handle($importer['channel_id'],$msg_diaspora_handle);
if(! $contact) {
@@ -1583,7 +1643,7 @@ function diaspora_message($importer,$xml,$msg) {
intval($importer['channel_id']),
dbesc($msg_conversation_guid)
);
- if(count($c))
+ if($c)
$conversation = $c[0];
else {
logger('diaspora_message: conversation not available.');
@@ -1592,6 +1652,7 @@ function diaspora_message($importer,$xml,$msg) {
$reply = 0;
+ $subject = $conversation['subject'];
$body = diaspora2bb($msg_text);
$message_id = $msg_diaspora_handle . ':' . $msg_guid;
@@ -1601,8 +1662,8 @@ function diaspora_message($importer,$xml,$msg) {
$author_signature = base64_decode($msg_author_signature);
$person = find_diaspora_person_by_handle($msg_diaspora_handle);
- if(is_array($person) && x($person,'pubkey'))
- $key = $person['pubkey'];
+ if(is_array($person) && x($person,'xchan_pubkey'))
+ $key = $person['xchan_pubkey'];
else {
logger('diaspora_message: unable to find author details');
return;
@@ -1613,28 +1674,30 @@ function diaspora_message($importer,$xml,$msg) {
return;
}
- $r = q("select id from mail where `uri` = '%s' and uid = %d limit 1",
+ $r = q("select id from mail where mid = '%s' and channel_id = %d limit 1",
dbesc($message_id),
intval($importer['channel_id'])
);
- if(count($r)) {
+ if($r) {
logger('diaspora_message: duplicate message already delivered.', LOGGER_DEBUG);
return;
}
- q("insert into mail ( `uid`, `guid`, `convid`, `from-name`,`from-photo`,`from-url`,`contact-id`,`title`,`body`,`seen`,`reply`,`uri`,`parent-uri`,`created`) values ( %d, '%s', %d, '%s', '%s', '%s', %d, '%s', '%s', %d, %d, '%s','%s','%s')",
+ $key = get_config('system','pubkey');
+ if($subject)
+ $subject = json_encode(crypto_encapsulate($subject,$key));
+ if($body)
+ $body = json_encode(crypto_encapsulate($body,$key));
+
+ q("insert into mail ( `channel_id`, `convid`, `from_xchan`,`to_xchan`,`title`,`body`,`mail_flags`,`mid`,`parent_mid`,`created`) values ( %d, %d, '%s', '%s', '%s', '%s', '%d','%s','%s','%s')",
intval($importer['channel_id']),
- dbesc($msg_guid),
intval($conversation['id']),
- dbesc($person['name']),
- dbesc($person['photo']),
- dbesc($person['url']),
- intval($contact['id']),
- dbesc($conversation['subject']),
+ dbesc($person['xchan_hash']),
+ dbesc($importer['xchan_hash']),
+ dbesc($subject),
dbesc($body),
- 0,
- 1,
- dbesc($message_id),
+ intval(MAIL_OBSCURED),
+ dbesc($msg_guid),
dbesc($parent_uri),
dbesc($msg_created_at)
);
@@ -1681,36 +1744,29 @@ function diaspora_photo($importer,$xml,$msg,$attempt=1) {
return 202;
}
- $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
+ $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `mid` = '%s' LIMIT 1",
intval($importer['channel_id']),
dbesc($status_message_guid)
);
- if(! count($r)) {
- if($attempt <= 3) {
- q("INSERT INTO dsprphotoq (uid, msg, attempt) VALUES (%d, '%s', %d)",
- intval($importer['channel_id']),
- dbesc(serialize($msg)),
- intval($attempt + 1)
- );
- }
+ if(! $r) {
logger('diaspora_photo: attempt = ' . $attempt . '; status message not found: ' . $status_message_guid . ' for photo: ' . $guid);
return;
}
- $parent_item = $r[0];
+// $parent_item = $r[0];
- $link_text = '[img]' . $remote_photo_path . $remote_photo_name . '[/img]' . "\n";
+// $link_text = '[img]' . $remote_photo_path . $remote_photo_name . '[/img]' . "\n";
- $link_text = scale_external_images($link_text, true,
- array($remote_photo_name, 'scaled_full_' . $remote_photo_name));
+// $link_text = scale_external_images($link_text, true,
+// array($remote_photo_name, 'scaled_full_' . $remote_photo_name));
- if(strpos($parent_item['body'],$link_text) === false) {
- $r = q("update item set `body` = '%s', `visible` = 1 where `id` = %d and `uid` = %d",
- dbesc($link_text . $parent_item['body']),
- intval($parent_item['id']),
- intval($parent_item['uid'])
- );
- }
+// if(strpos($parent_item['body'],$link_text) === false) {
+// $r = q("update item set `body` = '%s', `visible` = 1 where `id` = %d and `uid` = %d",
+// dbesc($link_text . $parent_item['body']),
+// intval($parent_item['id']),
+// intval($parent_item['uid'])
+// );
+// }
return;
}
@@ -1737,7 +1793,7 @@ function diaspora_like($importer,$xml,$msg) {
$contact = diaspora_get_contact_by_handle($importer['channel_id'],$msg['author']);
if(! $contact) {
- logger('diaspora_like: cannot find contact: ' . $msg['author']);
+ logger('diaspora_like: cannot find contact: ' . $msg['author'] . ' for channel ' . $importer['channel_name']);
return;
}
@@ -1747,7 +1803,7 @@ function diaspora_like($importer,$xml,$msg) {
return 202;
}
- $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
+ $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `mid` = '%s' LIMIT 1",
intval($importer['channel_id']),
dbesc($parent_guid)
);
@@ -1758,7 +1814,7 @@ function diaspora_like($importer,$xml,$msg) {
$parent_item = $r[0];
- $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
+ $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `mid` = '%s' LIMIT 1",
intval($importer['channel_id']),
dbesc($guid)
);
@@ -1771,15 +1827,19 @@ function diaspora_like($importer,$xml,$msg) {
// It looks like "RelayableRetractions" are used for "unlike" instead
if($positive === 'false') {
logger('diaspora_like: received a like with positive set to "false"...ignoring');
-/* q("UPDATE `item` SET `deleted` = 1 WHERE `id` = %d AND `uid` = %d",
- intval($r[0]['id']),
- intval($importer['channel_id'])
- );*/
+ // perhaps call drop_item()
// FIXME--actually don't unless it turns out that Diaspora does indeed send out "false" likes
// send notification via proc_run()
return;
}
}
+
+ $i = q("select * from xchan where xchan_hash = '%s' limit 1",
+ dbesc($parent_item['author_xchan'])
+ );
+ if($i)
+ $item_author = $i[0];
+
// Note: I don't think "Like" objects with positive = "false" are ever actually used
// It looks like "RelayableRetractions" are used for "unlike" instead
if($positive === 'false') {
@@ -1801,7 +1861,12 @@ function diaspora_like($importer,$xml,$msg) {
who sent the salmon
*/
- $signed_data = $guid . ';' . $target_type . ';' . $parent_guid . ';' . $positive . ';' . $diaspora_handle;
+ // 2014-09-10 let's try this: signatures are failing. I'll try and make a signable string from
+ // the parameters in the order they were presented in the post. This is how D* creates the signable string.
+
+
+ $signed_data = $positive . ';' . $guid . ';' . $target_type . ';' . $parent_guid . ';' . $diaspora_handle;
+
$key = $msg['key'];
if($parent_author_signature) {
@@ -1837,6 +1902,8 @@ function diaspora_like($importer,$xml,$msg) {
}
}
}
+
+ logger('diaspora_like: signature check complete.',LOGGER_DEBUG);
// Phew! Everything checks out. Now create an item.
@@ -1857,93 +1924,79 @@ function diaspora_like($importer,$xml,$msg) {
$uri = $diaspora_handle . ':' . $guid;
$activity = ACTIVITY_LIKE;
- $post_type = (($parent_item['resource-id']) ? t('photo') : t('status'));
- $objtype = (($parent_item['resource-id']) ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE );
- $link = xmlify('<link rel="alternate" type="text/html" href="' . $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $parent_item['id'] . '" />' . "\n") ;
+
+ $post_type = (($parent_item['resource_type'] === 'photo') ? t('photo') : t('status'));
+
+ $links = array(array('rel' => 'alternate','type' => 'text/html', 'href' => $parent_item['plink']));
+ $objtype = (($parent_item['resource_type'] === 'photo') ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE );
+
$body = $parent_item['body'];
- $obj = <<< EOT
- <object>
- <type>$objtype</type>
- <local>1</local>
- <id>{$parent_item['uri']}</id>
- <link>$link</link>
- <title></title>
- <content>$body</content>
- </object>
-EOT;
+ $object = json_encode(array(
+ 'type' => $post_type,
+ 'id' => $parent_item['mid'],
+ 'parent' => (($parent_item['thr_parent']) ? $parent_item['thr_parent'] : $parent_item['parent_mid']),
+ 'link' => $links,
+ 'title' => $parent_item['title'],
+ 'content' => $parent_item['body'],
+ 'created' => $parent_item['created'],
+ 'edited' => $parent_item['edited'],
+ 'author' => array(
+ 'name' => $item_author['xchan_name'],
+ 'address' => $item_author['xchan_addr'],
+ 'guid' => $item_author['xchan_guid'],
+ 'guid_sig' => $item_author['xchan_guid_sig'],
+ 'link' => array(
+ array('rel' => 'alternate', 'type' => 'text/html', 'href' => $item_author['xchan_url']),
+ array('rel' => 'photo', 'type' => $item_author['xchan_photo_mimetype'], 'href' => $item_author['xchan_photo_m'])),
+ ),
+ ));
+
+
$bodyverb = t('%1$s likes %2$s\'s %3$s');
$arr = array();
- $arr['uri'] = $uri;
$arr['uid'] = $importer['channel_id'];
- $arr['guid'] = $guid;
- $arr['network'] = NETWORK_DIASPORA;
- $arr['contact-id'] = $contact['id'];
- $arr['type'] = 'activity';
- $arr['wall'] = $parent_item['wall'];
- $arr['gravity'] = GRAVITY_LIKE;
- $arr['parent'] = $parent_item['id'];
- $arr['parent-uri'] = $parent_item['uri'];
-
- $arr['owner-name'] = $parent_item['name'];
- $arr['owner-link'] = $parent_item['url'];
- //$arr['owner-avatar'] = $parent_item['thumb'];
- $arr['owner-avatar'] = ((x($parent_item,'thumb')) ? $parent_item['thumb'] : $parent_item['photo']);
-
- $arr['author-name'] = $person['name'];
- $arr['author-link'] = $person['url'];
- $arr['author-avatar'] = ((x($person,'thumb')) ? $person['thumb'] : $person['photo']);
+ $arr['aid'] = $importer['channel_account_id'];
+ $arr['mid'] = $guid;
+ $arr['parent_mid'] = $parent_item['mid'];
+ $arr['owner_xchan'] = $parent_item['owner_xchan'];
+ $arr['author_xchan'] = $person['xchan_hash'];
$ulink = '[url=' . $contact['url'] . ']' . $contact['name'] . '[/url]';
$alink = '[url=' . $parent_item['author-link'] . ']' . $parent_item['author-name'] . '[/url]';
- //$plink = '[url=' . $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $parent_item['id'] . ']' . $post_type . '[/url]';
- $plink = '[url='.$a->get_baseurl().'/display/'.$guid.']'.$post_type.'[/url]';
+ $plink = '[url='. z_root() .'/display/'.$guid.']'.$post_type.'[/url]';
$arr['body'] = sprintf( $bodyverb, $ulink, $alink, $plink );
$arr['app'] = 'Diaspora';
- $arr['private'] = $parent_item['private'];
+ // set the route to that of the parent so downstream hubs won't reject it.
+ $arr['route'] = $parent_item['route'];
+
+ $arr['item_private'] = $parent_item['item_private'];
$arr['verb'] = $activity;
- $arr['object-type'] = $objtype;
- $arr['object'] = $obj;
- $arr['visible'] = 1;
- $arr['unseen'] = 1;
- $arr['last-child'] = 0;
+ $arr['obj_type'] = $objtype;
+ $arr['object'] = $object;
if(! $parent_author_signature) {
- $datarray['diaspora_meta'] = array('signer' => $diaspora_handle, 'body' => $text,
- 'signed_text' => $signed_data, 'signature' => base64_encode($author_signature));
+ $key = get_config('system','pubkey');
+ $x = array('signer' => $diaspora_handle, 'body' => $text,
+ 'signed_text' => $signed_data, 'signature' => base64_encode($author_signature));
+ $arr['diaspora_meta'] = json_encode(crypto_encapsulate(json_encode($x),$key));
}
+ $x = item_store($arr);
- $message_id = item_store($arr);
-
-
- //if($message_id) {
- // q("update item set plink = '%s' where id = %d",
- // //dbesc($a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $message_id),
- // dbesc($a->get_baseurl().'/display/'.$guid),
- // intval($message_id)
- // );
- //}
-
- if(! $parent_author_signature) {
- q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
- intval($message_id),
- dbesc($signed_data),
- dbesc(base64_encode($author_signature)),
- dbesc($diaspora_handle)
- );
- }
+ if($x)
+ $message_id = $x['item_id'];
// if the message isn't already being relayed, notify others
// the existence of parent_author_signature means the parent_author or owner
// is already relaying. The parent_item['origin'] indicates the message was created on our system
- if(($parent_item['origin']) && (! $parent_author_signature))
+ if(($parent_item['item_flags'] & ITEM_ORIGIN) && (! $parent_author_signature))
proc_run('php','include/notifier.php','comment-import',$message_id);
return;
@@ -1962,19 +2015,16 @@ function diaspora_retraction($importer,$xml) {
if($type === 'Person') {
require_once('include/Contact.php');
- contact_remove($contact['id']);
+ contact_remove($importer['channel_id'],$contact['abook_id']);
}
elseif($type === 'Post') {
- $r = q("select * from item where guid = '%s' and uid = %d and not file like '%%[%%' limit 1",
+ $r = q("select * from item where mid = '%s' and uid = %d limit 1",
dbesc('guid'),
intval($importer['channel_id'])
);
if(count($r)) {
- if(link_compare($r[0]['author-link'],$contact['url'])) {
- q("update item set `deleted` = 1, `changed` = '%s' where `id` = %d",
- dbesc(datetime_convert()),
- intval($r[0]['id'])
- );
+ if(link_compare($r[0]['author_xchan'],$contact['xchan_hash'])) {
+ drop_item($r[0]['id'],false);
}
}
}
@@ -2036,35 +2086,33 @@ function diaspora_signed_retraction($importer,$xml,$msg) {
}
if($type === 'StatusMessage' || $type === 'Comment' || $type === 'Like') {
- $r = q("select * from item where guid = '%s' and uid = %d and not file like '%%[%%' limit 1",
+ $r = q("select * from item where mid = '%s' and uid = %d limit 1",
dbesc($guid),
intval($importer['channel_id'])
);
- if(count($r)) {
- if(link_compare($r[0]['author-link'],$contact['url'])) {
- q("update item set `deleted` = 1, `edited` = '%s', `changed` = '%s', `body` = '' , `title` = '' where `id` = %d",
- dbesc(datetime_convert()),
- dbesc(datetime_convert()),
- intval($r[0]['id'])
- );
+ if($r) {
+ if($r[0]['author_xchan'] == $contact['xchan_hash']) {
+
+ drop_item($r[0]['id'],false, DROPITEM_PHASE1);
// Now check if the retraction needs to be relayed by us
//
// The first item in the `item` table with the parent id is the parent. However, MySQL doesn't always
// return the items ordered by `item`.`id`, in which case the wrong item is chosen as the parent.
// The only item with `parent` and `id` as the parent id is the parent item.
- $p = q("select origin from item where parent = %d and id = %d limit 1",
+ $p = q("select item_flags from item where parent = %d and id = %d limit 1",
$r[0]['parent'],
$r[0]['parent']
);
- if(count($p)) {
- if(($p[0]['origin']) && (! $parent_author_signature)) {
- q("insert into sign (`retract_iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
- $r[0]['id'],
- dbesc($signed_data),
- dbesc($sig),
- dbesc($diaspora_handle)
- );
+ if($p) {
+ if(($p[0]['item_flags'] & ITEM_ORIGIN) && (! $parent_author_signature)) {
+// FIXME so we can relay this
+// q("insert into sign (`retract_iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
+// $r[0]['id'],
+// dbesc($signed_data),
+// dbesc($sig),
+// dbesc($diaspora_handle)
+// );
// the existence of parent_author_signature would have meant the parent_author or owner
// is already relaying.
@@ -2118,15 +2166,9 @@ function diaspora_profile($importer,$xml,$msg) {
$image_url = "http://" . $handle_parts[1] . $image_url;
}
-/* $r = q("SELECT DISTINCT ( `resource-id` ) FROM `photo` WHERE `uid` = %d AND `contact-id` = %d AND `album` = 'Contact Photos' ",
- intval($importer['channel_id']),
- intval($contact['id'])
- );
- $oldphotos = ((count($r)) ? $r : null);*/
-
- require_once('include/Photo.php');
+ require_once('include/photo/photo_driver.php');
- $images = import_profile_photo($image_url,$importer['channel_id'],$contact['id']);
+ $images = import_profile_photo($image_url,$contact['xchan_hash']);
// Generic birthday. We don't know the timezone. The year is irrelevant.
@@ -2143,7 +2185,7 @@ function diaspora_profile($importer,$xml,$msg) {
// TODO: update name on item['author-name'] if the name changed. See consume_feed()
// Not doing this currently because D* protocol is scheduled for revision soon.
- $r = q("UPDATE `contact` SET `name` = '%s', `name-date` = '%s', `photo` = '%s', `thumb` = '%s', `micro` = '%s', `avatar-date` = '%s' , `bd` = '%s' WHERE `id` = %d AND `uid` = %d",
+/* $r = q("UPDATE `contact` SET `name` = '%s', `name-date` = '%s', `photo` = '%s', `thumb` = '%s', `micro` = '%s', `avatar-date` = '%s' , `bd` = '%s' WHERE `id` = %d AND `uid` = %d",
dbesc($name),
dbesc(datetime_convert()),
dbesc($images[0]),
@@ -2154,7 +2196,7 @@ function diaspora_profile($importer,$xml,$msg) {
intval($contact['id']),
intval($importer['channel_id'])
);
-
+*/
/* if($r) {
if($oldphotos) {
foreach($oldphotos as $ph) {
@@ -2171,9 +2213,9 @@ function diaspora_profile($importer,$xml,$msg) {
}
-function diaspora_share($me,$contact) {
+function diaspora_share($owner,$contact) {
$a = get_app();
- $myaddr = $me['channel_address'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
+ $myaddr = $owner['channel_address'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
if(! array_key_exists('xchan_hash',$contact)) {
$c = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where xchan_hash = '%s' limit 1",
@@ -2194,23 +2236,23 @@ function diaspora_share($me,$contact) {
'$recipient' => $theiraddr
));
- $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$me,$contact,$me['channel_prvkey'],$contact['xchan_pubkey'])));
+ $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['channel_prvkey'],$contact['xchan_pubkey'])));
return(diaspora_transmit($owner,$contact,$slap, false));
}
-function diaspora_unshare($me,$contact) {
+function diaspora_unshare($owner,$contact) {
$a = get_app();
- $myaddr = $me['channel_address'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
+ $myaddr = $owner['channel_address'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
$tpl = get_markup_template('diaspora_retract.tpl');
$msg = replace_macros($tpl, array(
- '$guid' => $me['channel_guid'],
+ '$guid' => $owner['channel_guid'],
'$type' => 'Person',
'$handle' => $myaddr
));
- $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$me,$contact,$me['channel_prvkey'],$contact['xchan_pubkey'])));
+ $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['channel_prvkey'],$contact['xchan_pubkey'])));
return(diaspora_transmit($owner,$contact,$slap, false));
}
@@ -2224,7 +2266,7 @@ function diaspora_send_status($item,$owner,$contact,$public_batch = false) {
$images = array();
$title = $item['title'];
- $body = bb2diaspora_itembody($item);
+ $body = bb2diaspora_itembody($item,true);
/*
// We're trying to match Diaspora's split message/photo protocol but
@@ -2386,13 +2428,13 @@ function diaspora_send_images($item,$owner,$contact,$images,$public_batch = fals
function diaspora_send_followup($item,$owner,$contact,$public_batch = false) {
$a = get_app();
- $myaddr = $owner['channel_address'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
+ $myaddr = $owner['channel_address'] . '@' . get_app()->get_hostname();
$theiraddr = $contact['xchan_addr'];
// 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'])
);
@@ -2411,10 +2453,11 @@ 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))
@@ -2426,13 +2469,19 @@ function diaspora_send_followup($item,$owner,$contact,$public_batch = false) {
}
if($item['diaspora_meta'] && ! $like) {
- $j = json_decode($item['diaspora_meta'],true);
- if($j) {
- $signed_text = $j['signed_text'];
- $text = $j['body'];
- $signer = $j['signer'];
- $authorsig = $j['signature'];
+ $diaspora_meta = json_decode($item['diaspora_meta'],true);
+ if($diaspora_meta) {
+ if(array_key_exists('iv',$diaspora_meta)) {
+ $key = get_config('system','prvkey');
+ $meta = json_decode(crypto_unencapsulate($diaspora_meta,$key),true);
+ }
+ else
+ $meta = $diaspora_meta;
}
+ $signed_text = $meta['signed_text'];
+ $authorsig = $meta['signature'];
+ $signer = $meta['signer'];
+ $text = $meta['body'];
}
else {
$text = bb2diaspora_itembody($item);
@@ -2471,7 +2520,7 @@ function diaspora_send_relay($item,$owner,$contact,$public_batch = false) {
$a = get_app();
- $myaddr = $owner['channel_address'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
+ $myaddr = $owner['channel_address'] . '@' . get_app()->get_hostname();
$text = bb2diaspora_itembody($item);
@@ -2530,10 +2579,16 @@ function diaspora_send_relay($item,$owner,$contact,$public_batch = false) {
$diaspora_meta = (($item['diaspora_meta']) ? json_decode($item['diaspora_meta'],true) : '');
if($diaspora_meta) {
- $sender_signed_text = $diaspora_meta['signed_text'];
- $authorsig = $diaspora_meta['signature'];
- $handle = $diaspora_meta['signer'];
- $text = $diaspora_meta['body'];
+ if(array_key_exists('iv',$diaspora_meta)) {
+ $key = get_config('system','prvkey');
+ $meta = json_decode(crypto_unencapsulate($diaspora_meta,$key),true);
+ }
+ else
+ $meta = $diaspora_meta;
+ $sender_signed_text = $meta['signed_text'];
+ $authorsig = $meta['signature'];
+ $handle = $meta['signer'];
+ $text = $meta['body'];
}
else
logger('diaspora_send_relay: original author signature not found');
@@ -2565,7 +2620,7 @@ function diaspora_send_relay($item,$owner,$contact,$public_batch = false) {
if($relay_retract)
$sender_signed_text = $item['mid'] . ';' . $target_type;
elseif($like)
- $sender_signed_text = $item['mid'] . ';' . $target_type . ';' . $parent['mid'] . ';' . $positive . ';' . $handle;
+ $sender_signed_text = $positive . ';' . $item['mid'] . ';' . $target_type . ';' . $parent['mid'] . ';' . $handle;
else
$sender_signed_text = $item['mid'] . ';' . $parent['mid'] . ';' . $text . ';' . $handle;
}
@@ -2583,6 +2638,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']),
@@ -2607,7 +2665,7 @@ function diaspora_send_relay($item,$owner,$contact,$public_batch = false) {
function diaspora_send_retraction($item,$owner,$contact,$public_batch = false) {
$a = get_app();
- $myaddr = $owner['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
+ $myaddr = $owner['channel_address'] . '@' . get_app()->get_hostname();
// Check whether the retraction is for a top-level post or whether it's a relayable
if( $item['mid'] !== $item['parent_mid'] ) {
@@ -2638,11 +2696,11 @@ function diaspora_send_retraction($item,$owner,$contact,$public_batch = false) {
function diaspora_send_mail($item,$owner,$contact) {
$a = get_app();
- $myaddr = $owner['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
+ $myaddr = $owner['channel_address'] . '@' . get_app()->get_hostname();
$r = q("select * from conv where id = %d and uid = %d limit 1",
intval($item['convid']),
- intval($item['uid'])
+ intval($item['channel_id'])
);
if(! count($r)) {
@@ -2659,16 +2717,25 @@ function diaspora_send_mail($item,$owner,$contact) {
'participant_handles' => xmlify($cnv['recips'])
);
+ if(array_key_exists('mail_flags',$item) && ($item['mail_flags'] & MAIL_OBSCURED)) {
+ $key = get_config('system','prvkey');
+// if($item['title'])
+// $item['title'] = crypto_unencapsulate(json_decode_plus($item['title']),$key);
+ if($item['body'])
+ $item['body'] = crypto_unencapsulate(json_decode_plus($item['body']),$key);
+ }
+
+
$body = bb2diaspora($item['body']);
$created = datetime_convert('UTC','UTC',$item['created'],'Y-m-d H:i:s \U\T\C');
- $signed_text = $item['guid'] . ';' . $cnv['guid'] . ';' . $body . ';'
+ $signed_text = $item['mid'] . ';' . $cnv['guid'] . ';' . $body . ';'
. $created . ';' . $myaddr . ';' . $cnv['guid'];
$sig = base64_encode(rsa_sign($signed_text,$owner['channel_prvkey'],'sha256'));
$msg = array(
- 'guid' => xmlify($item['guid']),
+ 'guid' => xmlify($item['mid']),
'parent_guid' => xmlify($cnv['guid']),
'parent_author_signature' => (($item['reply']) ? null : xmlify($sig)),
'author_signature' => xmlify($sig),
diff --git a/include/dir_fns.php b/include/dir_fns.php
index 5fad6567e..af6f78c01 100644
--- a/include/dir_fns.php
+++ b/include/dir_fns.php
@@ -38,7 +38,7 @@ function check_upstream_directory() {
*/
$directory = get_config('system','directory_server');
if ($directory) {
- $r = q("select * from site where site_url = '%s' and (site_flags & %d) ",
+ $r = q("select * from site where site_url = '%s' and (site_flags & %d) > 0 ",
dbesc($directory),
intval(DIRECTORY_MODE_PRIMARY|DIRECTORY_MODE_SECONDARY|DIRECTORY_MODE_STANDALONE)
);
@@ -51,12 +51,32 @@ function check_upstream_directory() {
}
function dir_sort_links() {
+ // Build urls without order and pubforums so it's easy to tack on the changed value
+ // Probably there's an easier way to do this
+
+ $current_order = (($_REQUEST['order']) ? $_REQUEST['order'] : 'normal');
+ $url = 'directory?';
+ $tmp = $_REQUEST;
+ unset($tmp['order']);
+ $sorturl = $url . http_build_query($tmp);
+ $tmp = $_REQUEST;
+
+ unset($tmp['pubforums']);
+ $forumsurl = $url . http_build_query($tmp);
$o = replace_macros(get_markup_template('dir_sort_links.tpl'), array(
- '$header' => t('Sort Options'),
+ '$header' => t('Directory Options'),
'$normal' => t('Alphabetic'),
'$reverse' => t('Reverse Alphabetic'),
- '$date' => t('Newest to Oldest')
+ '$date' => t('Newest to Oldest'),
+ '$reversedate' => t('Oldest to Newest'),
+ '$pubforums' => t('Public Forums Only'),
+ '$pubforumsonly' => x($_REQUEST,'pubforums') ? $_REQUEST['pubforums'] : '',
+ '$sort' => t('Sort'),
+ '$selected_sort' => $current_order,
+ '$sorturl' => $sorturl,
+ '$forumsurl' => $forumsurl,
+
));
return $o;
}
@@ -86,14 +106,14 @@ function sync_directories($dirmode) {
$realm = get_directory_realm();
if($realm == DIRECTORY_REALM) {
- $r = q("select * from site where (site_flags & %d) and site_url != '%s' and ( site_realm = '%s' or site_realm = '') ",
+ $r = q("select * from site where (site_flags & %d) > 0 and site_url != '%s' and ( site_realm = '%s' or site_realm = '') ",
intval(DIRECTORY_MODE_PRIMARY|DIRECTORY_MODE_SECONDARY),
dbesc(z_root()),
dbesc($realm)
);
}
else {
- $r = q("select * from site where (site_flags & %d) and site_url != '%s' and site_realm like '%s' ",
+ $r = q("select * from site where (site_flags & %d) > 0 and site_url != '%s' and site_realm like '%s' ",
intval(DIRECTORY_MODE_PRIMARY|DIRECTORY_MODE_SECONDARY),
dbesc(z_root()),
dbesc(protect_sprintf('%' . $realm . '%'))
@@ -107,7 +127,7 @@ function sync_directories($dirmode) {
$r = array(
'site_url' => DIRECTORY_FALLBACK_MASTER,
'site_flags' => DIRECTORY_MODE_PRIMARY,
- 'site_update' => '0000-00-00 00:00:00',
+ 'site_update' => NULL_DATE,
'site_directory' => DIRECTORY_FALLBACK_MASTER . '/dirsearch',
'site_realm' => DIRECTORY_REALM
);
@@ -120,7 +140,7 @@ function sync_directories($dirmode) {
dbesc($r[0]['site_realm'])
);
- $r = q("select * from site where (site_flags & %d) and site_url != '%s'",
+ $r = q("select * from site where (site_flags & %d) > 0 and site_url != '%s'",
intval(DIRECTORY_MODE_PRIMARY|DIRECTORY_MODE_SECONDARY),
dbesc(z_root())
);
@@ -137,7 +157,7 @@ function sync_directories($dirmode) {
// for brand new directory servers, only load the last couple of days. Everything before that will be repeats.
- $syncdate = (($rr['site_sync'] === '0000-00-00 00:00:00') ? datetime_convert('UTC','UTC','now - 2 days') : $rr['site_sync']);
+ $syncdate = (($rr['site_sync'] === NULL_DATE) ? datetime_convert('UTC','UTC','now - 2 days') : $rr['site_sync']);
$x = z_fetch_url($rr['site_directory'] . '?f=&sync=' . urlencode($syncdate));
if(! $x['success'])
@@ -146,7 +166,7 @@ function sync_directories($dirmode) {
if((! $j['transactions']) || (! is_array($j['transactions'])))
continue;
- q("update site set site_sync = '%s' where site_url = '%s' limit 1",
+ q("update site set site_sync = '%s' where site_url = '%s'",
dbesc(datetime_convert()),
dbesc($rr['site_url'])
);
@@ -212,7 +232,7 @@ function update_directory_entry($ud) {
function local_dir_update($uid,$force) {
- logger('local_dir_update', LOGGER_DEBUG);
+ logger('local_dir_update: uid: ' . $uid, LOGGER_DEBUG);
$p = q("select channel.channel_hash, channel_address, channel_timezone, profile.* from profile left join channel on channel_id = uid where uid = %d and is_default = 1",
intval($uid)
@@ -267,7 +287,7 @@ function local_dir_update($uid,$force) {
if($new_flags != $r[0]['xchan_flags']) {
- $r = q("update xchan set xchan_flags = %d where xchan_hash = '%s' limit 1",
+ $r = q("update xchan set xchan_flags = %d where xchan_hash = '%s'",
intval($new_flags),
dbesc($p[0]['channel_hash'])
);
@@ -281,10 +301,10 @@ function local_dir_update($uid,$force) {
}
else {
// they may have made it private
- $r = q("delete from xprof where xprof_hash = '%s' limit 1",
+ $r = q("delete from xprof where xprof_hash = '%s'",
dbesc($hash)
);
- $r = q("delete from xtag where xtag_hash = '%s' limit 1",
+ $r = q("delete from xtag where xtag_hash = '%s'",
dbesc($hash)
);
}
diff --git a/include/directory.php b/include/directory.php
index 60070f7ec..a7324a99a 100644
--- a/include/directory.php
+++ b/include/directory.php
@@ -42,7 +42,7 @@ function directory_run($argv, $argc){
local_dir_update($argv[1],$force);
- q("update channel set channel_dirdate = '%s' where channel_id = %d limit 1",
+ q("update channel set channel_dirdate = '%s' where channel_id = %d",
dbesc(datetime_convert()),
intval($channel['channel_id'])
);
@@ -85,7 +85,7 @@ function directory_run($argv, $argc){
);
}
else {
- q("update channel set channel_dirdate = '%s' where channel_id = %d limit 1",
+ q("update channel set channel_dirdate = '%s' where channel_id = %d",
dbesc(datetime_convert()),
intval($channel['channel_id'])
);
diff --git a/include/enotify.php b/include/enotify.php
index 2503f9ab0..f3eb80117 100644
--- a/include/enotify.php
+++ b/include/enotify.php
@@ -18,7 +18,7 @@ function notification($params) {
}
if($params['to_xchan']) {
$y = q("select channel.*, account.* from channel left join account on channel_account_id = account_id
- where channel_hash = '%s' and not (channel_pageflags & %d) limit 1",
+ where channel_hash = '%s' and not (channel_pageflags & %d)>0 limit 1",
dbesc($params['to_xchan']),
intval(PAGE_REMOVED)
);
@@ -394,7 +394,7 @@ function notification($params) {
if(($a->language === 'en' || (! $a->language)) && strpos($msg,', '))
$msg = substr($msg,strpos($msg,', ')+1);
- $r = q("update notify set msg = '%s' where id = %d and uid = %d limit 1",
+ $r = q("update notify set msg = '%s' where id = %d and uid = %d",
dbesc($msg),
intval($notify_id),
intval($datarray['uid'])
diff --git a/include/event.php b/include/event.php
index 0c29d26f6..1ed541d99 100644
--- a/include/event.php
+++ b/include/event.php
@@ -45,6 +45,56 @@ function format_event_html($ev) {
return $o;
}
+
+
+function ical_wrapper($ev) {
+
+ if(! ((is_array($ev)) && count($ev)))
+ return '';
+
+ $o .= "BEGIN:VCALENDAR";
+ $o .= "\nVERSION:2.0";
+ $o .= "\nMETHOD:PUBLISH";
+ $o .= "\nPRODID:-//" . get_config('system','sitename') . "//" . RED_PLATFORM . "//" . strtoupper(get_app()->language). "\n";
+ if(array_key_exists('start',$ev))
+ $o .= format_event_ical($ev);
+ else {
+ foreach($ev as $e) {
+ $o .= format_event_ical($e);
+ }
+ }
+ $o .= "\nEND:VCALENDAR\n";
+
+ return $o;
+}
+
+function format_event_ical($ev) {
+
+ $o = '';
+
+ $o .= "\nBEGIN:VEVENT";
+ if($ev['start'])
+ $o .= "\nDTSTART:" . datetime_convert('UTC','UTC', $ev['start'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : ''));
+ if($ev['finish'] && ! $ev['nofinish'])
+ $o .= "\nDTEND:" . datetime_convert('UTC','UTC', $ev['finish'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : ''));
+ if($ev['summary'])
+ $o .= "\nSUMMARY:" . format_ical_text($ev['summary']);
+ if($ev['location'])
+ $o .= "\nLOCATION:" . format_ical_text($ev['location']);
+ if($ev['description'])
+ $o .= "\nDESCRIPTION:" . format_ical_text($ev['description']);
+ $o .= "\nEND:VEVENT\n";
+ return $o;
+}
+
+function format_ical_text($s) {
+
+ require_once('include/bbcode.php');
+ require_once('include/html2plain.php');
+ return(wordwrap(html2plain(bbcode($s)),72,"\n ",true));
+}
+
+
function format_event_bbcode($ev) {
$o = '';
@@ -183,7 +233,7 @@ function event_store_event($arr) {
`allow_gid` = '%s',
`deny_cid` = '%s',
`deny_gid` = '%s'
- WHERE `id` = %d AND `uid` = %d LIMIT 1",
+ WHERE `id` = %d AND `uid` = %d",
dbesc($arr['edited']),
dbesc($arr['start']),
@@ -284,7 +334,7 @@ function event_addtocal($item_id, $uid) {
$event = event_store_event($ev);
if($event) {
- $r = q("update item set resource_id = '%s', resource_type = 'event' where id = %d and uid = %d limit 1",
+ $r = q("update item set resource_id = '%s', resource_type = 'event' where id = %d and uid = %d",
dbesc($event['event_hash']),
intval($item['id']),
intval($channel['channel_id'])
@@ -359,7 +409,7 @@ function event_store_item($arr,$event) {
$private = (($arr['allow_cid'] || $arr['allow_gid'] || $arr['deny_cid'] || $arr['deny_gid']) ? 1 : 0);
- q("UPDATE item SET title = '%s', body = '%s', object = '%s', allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', edited = '%s', item_flags = %d, item_private = %d WHERE id = %d AND uid = %d LIMIT 1",
+ q("UPDATE item SET title = '%s', body = '%s', object = '%s', allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', edited = '%s', item_flags = %d, item_private = %d WHERE id = %d AND uid = %d",
dbesc($arr['summary']),
dbesc($prefix . format_event_bbcode($arr)),
dbesc($object),
@@ -374,14 +424,33 @@ function event_store_item($arr,$event) {
intval($arr['uid'])
);
+
+ $s = q("delete from term where oid = %d and otype = %d",
+ intval($r[0]['id']),
+ intval(TERM_OBJ_POST)
+ );
+
+ if(($arr['term']) && (is_array($arr['term']))) {
+ foreach($arr['term'] as $t) {
+ q("insert into term (uid,oid,otype,type,term,url)
+ values(%d,%d,%d,%d,'%s','%s') ",
+ intval($arr['uid']),
+ intval($r[0]['id']),
+ intval(TERM_OBJ_POST),
+ intval($t['type']),
+ dbesc($t['term']),
+ dbesc($t['url'])
+ );
+ }
+ }
+
$item_id = $r[0]['id'];
call_hooks('event_updated', $event['id']);
return $item_id;
}
else {
- $z = q("select * from channel where channel_hash = '%s' and channel_id = %d limit 1",
- dbesc($event['event_xchan']),
+ $z = q("select * from channel where channel_id = %d limit 1",
intval($arr['uid'])
);
@@ -393,7 +462,7 @@ function event_store_item($arr,$event) {
$item_arr['id'] = $item['id'];
}
else {
- $wall = (($z) ? true : false);
+ $wall = (($z[0]['channel_hash'] == $event['event_xchan']) ? true : false);
$item_flags = ITEM_THREAD_TOP;
if($wall) {
@@ -424,6 +493,10 @@ function event_store_item($arr,$event) {
$item_arr['item_private'] = $private;
$item_arr['verb'] = ACTIVITY_POST;
+
+ if(array_key_exists('term',$arr))
+ $item_arr['term'] = $arr['term'];
+
$item_arr['resource_type'] = 'event';
$item_arr['resource_id'] = $event['event_hash'];
@@ -431,7 +504,14 @@ function event_store_item($arr,$event) {
$item_arr['body'] = $prefix . format_event_bbcode($arr);
- $item_arr['plink'] = z_root() . '/channel/' . $z[0]['channel_address'] . '/?f=&mid=' . $item_arr['mid'];
+ // if it's local send the permalink to the channel page.
+ // otherwise we'll fallback to /display/$message_id
+
+ if($wall)
+ $item_arr['plink'] = z_root() . '/channel/' . $z[0]['channel_address'] . '/?f=&mid=' . $item_arr['mid'];
+ else
+ $item_arr['plink'] = z_root() . '/display/' . $item_arr['mid'];
+
$x = q("select * from xchan where xchan_hash = '%s' limit 1",
dbesc($arr['event_xchan'])
diff --git a/include/expire.php b/include/expire.php
index 442914a39..a229bd4ac 100644
--- a/include/expire.php
+++ b/include/expire.php
@@ -7,9 +7,10 @@ function expire_run($argv, $argc){
cli_startup();
- $r = q("select id from item where (item_restrict & %d) and not (item_restrict & %d) and changed < UTC_TIMESTAMP() - INTERVAL 10 DAY",
+ $r = q("select id from item where (item_restrict & %d)>0 and not (item_restrict & %d)>0 and changed < %s - INTERVAL %s",
intval(ITEM_DELETED),
- intval(ITEM_PENDING_REMOVE)
+ intval(ITEM_PENDING_REMOVE),
+ db_utcnow(), db_quoteinterval('10 DAY')
);
if($r) {
foreach($r as $rr) {
@@ -19,8 +20,9 @@ function expire_run($argv, $argc){
// physically remove anything that has been deleted for more than two months
- $r = q("delete from item where ( item_restrict & %d ) and changed < UTC_TIMESTAMP() - INTERVAL 36 DAY",
- intval(ITEM_PENDING_REMOVE)
+ $r = q("delete from item where ( item_restrict & %d )>0 and changed < %s - INTERVAL %s",
+ intval(ITEM_PENDING_REMOVE),
+ db_utcnow(), db_quoteinterval('36 DAY')
);
// make this optional as it could have a performance impact on large sites
diff --git a/include/externals.php b/include/externals.php
index ad2c71ac9..b0f853dc6 100644
--- a/include/externals.php
+++ b/include/externals.php
@@ -14,6 +14,8 @@ function externals_run($argv, $argc){
$total = 0;
$attempts = 0;
+ logger('externals: startup', LOGGER_DEBUG);
+
// pull in some public posts
@@ -25,7 +27,8 @@ function externals_run($argv, $argc){
$url = $arr['url'];
}
else {
- $r = q("select site_url, site_pull from site where site_url != '%s' and site_flags != %d order by rand() limit 1",
+ $randfunc = db_getfunc('RAND');
+ $r = q("select site_url, site_pull from site where site_url != '%s' and site_flags != %d order by $randfunc limit 1",
dbesc(z_root()),
intval(DIRECTORY_MODE_STANDALONE)
);
@@ -60,7 +63,7 @@ function externals_run($argv, $argc){
}
if($url) {
- if($r[0]['site_pull'] !== '0000-00-00 00:00:00')
+ if($r[0]['site_pull'] !== NULL_DATE)
$mindate = urlencode(datetime_convert('','',$r[0]['site_pull'] . ' - 1 day'));
else {
$days = get_config('externals','since_days');
@@ -76,7 +79,7 @@ function externals_run($argv, $argc){
$x = z_fetch_url($feedurl);
if(($x) && ($x['success'])) {
- q("update site set site_pull = '%s' where site_url = '%s' limit 1",
+ q("update site set site_pull = '%s' where site_url = '%s'",
dbesc(datetime_convert()),
dbesc($url)
);
@@ -85,6 +88,8 @@ function externals_run($argv, $argc){
if($j['success'] && $j['messages']) {
$sys = get_sys_channel();
foreach($j['messages'] as $message) {
+ // on these posts, clear any route info.
+ $message['route'] = '';
$results = process_delivery(array('hash' => 'undefined'), get_item_elements($message),
array(array('hash' => $sys['xchan_hash'])), false, true);
$total ++;
@@ -97,12 +102,12 @@ $z = null;
$flag_bits = ITEM_WALL|ITEM_ORIGIN|ITEM_UPLINK;
// preserve the source
- $r = q("update item set source_xchan = owner_xchan where id = %d limit 1",
+ $r = q("update item set source_xchan = owner_xchan where id = %d",
intval($z[0]['id'])
);
$r = q("update item set item_flags = ( item_flags | %d ), owner_xchan = '%s'
- where id = %d limit 1",
+ where id = %d",
intval($flag_bits),
dbesc($sys['xchan_hash']),
intval($z[0]['id'])
diff --git a/include/features.php b/include/features.php
index 7530158ec..7009b1d6b 100644
--- a/include/features.php
+++ b/include/features.php
@@ -45,9 +45,10 @@ function get_features() {
t('Post Composition Features'),
// array('richtext', t('Richtext Editor'), t('Enable richtext editor')),
array('markdown', t('Use Markdown'), t('Allow use of "Markdown" to format posts')),
- array('preview', t('Post Preview'), t('Allow previewing posts and comments before publishing them')),
+// array('preview', t('Post Preview'), t('Allow previewing posts and comments before publishing them')),
array('channel_sources', t('Channel Sources'), t('Automatically import channel content from other channels or feeds')),
array('content_encrypt', t('Even More Encryption'), t('Allow optional encryption of content end-to-end with a shared secret key')),
+ array('adult_photo_flagging', t('Flag Adult Photos'), t('Provide photo edit option to hide adult photos from default album view')),
),
// Network Tools
@@ -66,7 +67,7 @@ function get_features() {
'tools' => array(
t('Post/Comment Tools'),
// array('multi_delete', t('Multiple Deletion'), t('Select and delete multiple posts/comments at once')),
- array('edit_posts', t('Edit Sent Posts'), t('Edit and correct posts and comments after sending')),
+// array('edit_posts', t('Edit Sent Posts'), t('Edit and correct posts and comments after sending')),
array('commtag', t('Tagging'), t('Ability to tag existing posts')),
array('categories', t('Post Categories'), t('Add categories to your posts')),
array('filing', t('Saved Folders'), t('Ability to file posts under folders')),
diff --git a/include/fixd.php b/include/fixd.php
new file mode 100644
index 000000000..bce5eb348
--- /dev/null
+++ b/include/fixd.php
@@ -0,0 +1,33 @@
+<?php
+
+ require_once('include/cli_startup.php');
+
+ cli_startup();
+
+ $rand = db_getfunc('RAND');
+ $r = q("select xchan_addr, hubloc_url from xchan left join hubloc on hubloc_hash = xchan_hash where xchan_network like '%%diaspora%%' order by $rand");
+
+ if(! $r)
+ killme();
+
+ require_once('include/network.php');
+ $total = 0;
+ foreach ($r as $rr) {
+ if($rr['hubloc_url']) {
+ continue;
+ }
+ $total ++;
+ }
+
+ print $total . "\n";
+
+ foreach ($r as $rr) {
+ if($rr['hubloc_url']) {
+ continue;
+ }
+
+ $webbie = $rr['xchan_addr'];
+ print $webbie . ' ';
+
+ discover_by_webbie($webbie);
+ }
diff --git a/include/follow.php b/include/follow.php
index 18a9e66ea..1abd0e3b9 100644
--- a/include/follow.php
+++ b/include/follow.php
@@ -13,12 +13,17 @@ require_once('include/zot.php');
function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) {
+
+
$result = array('success' => false,'message' => '');
$a = get_app();
$is_red = false;
$is_http = ((strpos($url,'://') !== false) ? true : false);
+ if($is_http && substr($url,-1,1) === '/')
+ $url = substr($url,0,-1);
+
if(! allowed_url($url)) {
$result['message'] = t('Channel is blocked on this site.');
return $result;
@@ -32,7 +37,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
// check service class limits
- $r = q("select count(*) as total from abook where abook_channel = %d and not (abook_flags & %d) ",
+ $r = q("select count(*) as total from abook where abook_channel = %d and not (abook_flags & %d)>0 ",
intval($uid),
intval(ABOOK_FLAG_SELF)
);
@@ -59,9 +64,16 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
$j = json_decode($ret['body'],true);
}
+ $my_perms = get_channel_default_perms($uid);
+
if($is_red && $j) {
- $my_perms = PERMS_W_STREAM|PERMS_W_MAIL;
+ $role = get_pconfig($uid,'system','permissions_role');
+ if($role) {
+ $x = get_role_perms($role);
+ if($x['perms_follow'])
+ $my_perms = $x['perms_follow'];
+ }
logger('follow: ' . $url . ' ' . print_r($j,true), LOGGER_DEBUG);
@@ -74,7 +86,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']));
@@ -125,7 +137,6 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
}
}
- $my_perms = 0;
$their_perms = 0;
$xchan_hash = '';
@@ -136,7 +147,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
if(! $r) {
// attempt network auto-discovery
- if(strpos($url,'@')) {
+ if(strpos($url,'@') && (! $is_http)) {
$r = discover_by_webbie($url);
}
elseif($is_http) {
@@ -152,7 +163,12 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
if($r) {
$xchan_hash = $r[0]['xchan_hash'];
$their_perms = 0;
- $my_perms = PERMS_W_STREAM|PERMS_W_MAIL;
+ $role = get_pconfig($uid,'system','permissions_role');
+ if($role) {
+ $x = get_role_perms($role);
+ if($x['perms_follow'])
+ $my_perms = $x['perms_follow'];
+ }
}
}
@@ -187,8 +203,9 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
return $result;
}
- $r = q("select count(*) as total from abook where abook_account = %d and ( abook_flags & ABOOK_FLAG_FEED )",
- intval($aid)
+ $r = q("select count(*) as total from abook where abook_account = %d and ( abook_flags & %d )>0",
+ intval($aid),
+ intval(ABOOK_FLAG_FEED)
);
if($r)
$total_feeds = $r[0]['total'];
@@ -209,7 +226,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
intval($uid)
);
if($r) {
- $x = q("update abook set abook_their_perms = %d where abook_id = %d limit 1",
+ $x = q("update abook set abook_their_perms = %d where abook_id = %d",
intval($their_perms),
intval($r[0]['abook_id'])
);
diff --git a/include/group.php b/include/group.php
index acb65df28..28cf5d80d 100644
--- a/include/group.php
+++ b/include/group.php
@@ -18,10 +18,11 @@ function group_add($uid,$name,$public = 0) {
intval($r)
);
if(count($z) && $z[0]['deleted']) {
- $r = q("UPDATE `groups` SET `deleted` = 0 WHERE `uid` = %d AND `name` = '%s' LIMIT 1",
+ /*$r = q("UPDATE `groups` SET `deleted` = 0 WHERE `uid` = %d AND `name` = '%s' LIMIT 1",
intval($uid),
dbesc($name)
- );
+ );*/
+ q('UPDATE groups SET deleted = 0 WHERE id = %d', intval($z[0]['id']));
notice( t('A deleted group with this name was revived. Existing item permissions <strong>may</strong> apply to this group and any future members. If this is not what you intended, please create another group with a different name.') . EOL);
}
return true;
@@ -107,7 +108,7 @@ function group_rmv($uid,$name) {
);
// remove group
- $r = q("UPDATE `groups` SET `deleted` = 1 WHERE `uid` = %d AND `name` = '%s' LIMIT 1",
+ $r = q("UPDATE `groups` SET `deleted` = 1 WHERE `uid` = %d AND `name` = '%s'",
intval($uid),
dbesc($name)
);
@@ -152,7 +153,7 @@ function group_rmv_member($uid,$name,$member) {
return false;
if(! ( $uid && $gid && $member))
return false;
- $r = q("DELETE FROM `group_member` WHERE `uid` = %d AND `gid` = %d AND xchan = '%s' LIMIT 1 ",
+ $r = q("DELETE FROM `group_member` WHERE `uid` = %d AND `gid` = %d AND xchan = '%s' ",
intval($uid),
intval($gid),
dbesc($member)
@@ -199,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 not ( xchan_flags & %d ) and not ( abook_flags & %d ) and not ( abook_flags & %d ) ORDER BY xchan_name ASC ",
+ WHERE `gid` = %d AND abook_channel = %d and `group_member`.`uid` = %d and not ( xchan_flags & %d )>0 and not ( abook_flags & %d )>0 and not ( abook_flags & %d )>0 ORDER BY xchan_name ASC ",
intval($gid),
intval(local_user()),
intval(local_user()),
@@ -280,6 +281,7 @@ function group_side($every="connections",$each="group",$edit = false, $group_id
$groups[] = array(
'id' => $rr['id'],
+ 'enc_cid' => base64url_encode($cid),
'cid' => $cid,
'text' => $rr['name'],
'selected' => $selected,
diff --git a/include/html2bbcode.php b/include/html2bbcode.php
index df430e6c7..9ffc85a82 100644
--- a/include/html2bbcode.php
+++ b/include/html2bbcode.php
@@ -16,7 +16,7 @@ function node2bbcode(&$doc, $oldnode, $attributes, $startbb, $endbb)
function node2bbcodesub(&$doc, $oldnode, $attributes, $startbb, $endbb)
{
- $savestart = str_replace('$', '%', $startbb);
+ $savestart = str_replace('$', '\x01', $startbb);
$replace = false;
$xpath = new DomXPath($doc);
@@ -37,7 +37,7 @@ function node2bbcodesub(&$doc, $oldnode, $attributes, $startbb, $endbb)
foreach ($attributes as $attribute => $value) {
- $startbb = str_replace('%'.++$i, '$1', $startbb);
+ $startbb = str_replace('\x01'.++$i, '$1', $startbb);
if (strpos('*'.$startbb, '$1') > 0) {
@@ -283,8 +283,9 @@ function html2bbcode($message)
array('[b]', '[/b]', '[i]', '[/i]'), $message);
// Handling Yahoo style of mails
- $message = str_replace('[hr][b]From:[/b]', '[quote][b]From:[/b]', $message);
+ // $message = str_replace('[hr][b]From:[/b]', '[quote][b]From:[/b]', $message);
+ $message = htmlspecialchars($message,ENT_COMPAT,'UTF-8',false);
return(trim($message));
}
diff --git a/include/hubloc.php b/include/hubloc.php
index 566875ce9..b5a3d47c5 100644
--- a/include/hubloc.php
+++ b/include/hubloc.php
@@ -33,7 +33,7 @@ function prune_hub_reinstalls() {
// allow some slop period, say 3 days - just in case this is a glitch or transient occurrence
// Then remove any hublocs pointing to the oldest entry.
- if($d1 < $d2) {
+ if(($d1 < $d2) && ($x[0]['hubloc_sitekey'])) {
logger('prune_hub_reinstalls: removing dead hublocs at ' . $rr['site_url']);
$y = q("delete from hubloc where hubloc_sitekey = '%s'",
dbesc($x[0]['hubloc_sitekey'])
@@ -42,4 +42,220 @@ function prune_hub_reinstalls() {
}
}
}
+}
+
+function remove_obsolete_hublocs() {
+
+ logger('remove_obsolete_hublocs',LOGGER_DEBUG);
+
+ // Get rid of any hublocs which are ours but aren't valid anymore -
+ // e.g. they point to a different and perhaps transient URL that we aren't using.
+
+ // I need to stress that this shouldn't happen. fix_system_urls() fixes hublocs
+ // when it discovers the URL has changed. So it's unclear how we could end up
+ // with URLs pointing to the old site name. But it happens. This may be an artifact
+ // of an old bug or maybe a regression in some newer code. In any event, they
+ // mess up communications and we have to take action if we find any.
+
+ // First make sure we have any hublocs (at all) with this URL and sitekey.
+ // We don't want to perform this operation while somebody is in the process
+ // of renaming their hub or installing certs.
+
+ $r = q("select hubloc_id from hubloc where hubloc_url = '%s' and hubloc_sitekey = '%s'",
+ dbesc(z_root()),
+ dbesc(get_config('system','pubkey'))
+ );
+ if((! $r) || (! count($r)))
+ return;
+
+ $channels = array();
+
+ // Good. We have at least one *valid* hubloc.
+
+ // Do we have any invalid ones?
+
+ $r = q("select hubloc_id from hubloc where hubloc_sitekey = '%s' and hubloc_url != '%s'",
+ dbesc(get_config('system','pubkey')),
+ dbesc(z_root())
+ );
+ $p = q("select hubloc_id from hubloc where hubloc_sitekey != '%s' and hubloc_url = '%s'",
+ dbesc(get_config('system','pubkey')),
+ dbesc(z_root())
+ );
+ if(is_array($r) && is_array($p))
+ $r = array_merge($r,$p);
+
+ if(! $r)
+ return;
+
+ // We've got invalid hublocs. Get rid of them.
+
+ logger('remove_obsolete_hublocs: removing ' . count($r) . ' hublocs.');
+
+ $interval = ((get_config('system','delivery_interval') !== false)
+ ? intval(get_config('system','delivery_interval')) : 2 );
+
+ foreach($r as $rr) {
+ q("update hubloc set hubloc_flags = (hubloc_flags | %d) where hubloc_id = %d",
+ intval(HUBLOC_FLAGS_DELETED),
+ intval($rr['hubloc_id'])
+ );
+
+ $x = q("select channel_id from channel where channel_hash = '%s' limit 1",
+ 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);
+ }
+ }
+}
+
+
+// This actually changes other structures to match the given (presumably current) hubloc primary selection
+
+function hubloc_change_primary($hubloc) {
+
+ if(! is_array($hubloc)) {
+ logger('no hubloc');
+ return false;
+ }
+ if(! ($hubloc['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY)) {
+ logger('not primary: ' . $hubloc['hubloc_url']);
+ return false;
+ }
+
+ logger('setting primary: ' . $hubloc['hubloc_url']);
+
+ // See if there's a local channel
+
+ $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'])
+ );
+ }
+
+ // do we even have an xchan for this hubloc and if so is it already set as primary?
+
+ $r = q("select * from xchan where xchan_hash = '%s' limit 1",
+ dbesc($hubloc['hubloc_hash'])
+ );
+ if(! $r) {
+ logger('xchan not found');
+ return false;
+ }
+ if($r[0]['xchan_addr'] === $hubloc['hubloc_addr']) {
+ logger('xchan already changed');
+ return false;
+ }
+
+ $url = $hubloc['hubloc_url'];
+ $lwebbie = substr($hubloc['hubloc_addr'],0,strpos($hubloc['hubloc_addr'],'@'));
+
+ $r = q("update xchan set xchan_addr = '%s', xchan_url = '%s', xchan_follow = '%s', xchan_connurl = '%s' where xchan_hash = '%s'",
+ dbesc($hubloc['hubloc_addr']),
+ dbesc($url . '/channel/' . $lwebbie),
+ dbesc($url . '/follow?f=&url=%s'),
+ dbesc($url . '/poco/' . $lwebbie),
+ dbesc($hubloc['hubloc_hash'])
+ );
+ if(! $r)
+ logger('xchan_update failed.');
+
+ logger('primary hubloc changed.' . print_r($hubloc,true),LOGGER_DEBUG);
+ return true;
+
+}
+
+
+function xchan_store($arr) {
+
+ if(! $arr['hash'])
+ $arr['hash'] = $arr['guid'];
+ if(! $arr['hash'])
+ return false;
+
+ $r = q("select * from xchan where xchan_hash = '%s' limit 1",
+ dbesc($arr['hash'])
+ );
+ if($r)
+ return true;
+
+ if(! $arr['network'])
+ $arr['network'] = 'unknown';
+ if(! $arr['name'])
+ $arr['name'] = 'unknown';
+ if(! $arr['url'])
+ $arr['url'] = z_root();
+ if(! $arr['photo'])
+ $arr['photo'] = get_default_profile_photo();
+
+ $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_guid_sig, xchan_pubkey, xchan_addr, xchan_url, xchan_connurl, xchan_follow, xchan_connpage, xchan_name, xchan_network, xchan_instance_url, xchan_flags, xchan_name_date ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s','%s','%s','%s',%d,'%s') ",
+ dbesc($arr['hash']),
+ dbesc($arr['guid']),
+ dbesc($arr['guid_sig']),
+ dbesc($arr['pubkey']),
+ dbesc($arr['address']),
+ dbesc($arr['url']),
+ dbesc($arr['connurl']),
+ dbesc($arr['follow']),
+ dbesc($arr['connpage']),
+ dbesc($arr['name']),
+ dbesc($arr['network']),
+ dbesc($arr['instance_url']),
+ intval($arr['flags']),
+ dbesc(datetime_convert())
+ );
+ if(! $r)
+ return $r;
+
+ $photos = import_profile_photo($arr['photo'],$arr['hash']);
+ $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'",
+ dbesc(datetime_convert()),
+ dbesc($photos[0]),
+ dbesc($photos[1]),
+ dbesc($photos[2]),
+ dbesc($photos[3]),
+ dbesc($arr['hash'])
+ );
+ return $r;
+
+}
+
+
+function xchan_fetch($arr) {
+
+ $key = '';
+ if($arr['hash']) {
+ $key = 'xchan_hash';
+ $v = $arr['hash'];
+ }
+ elseif($arr['guid']) {
+ $key = 'xchan_guid';
+ $v = $arr['guid'];
+ }
+ elseif($arr['address']) {
+ $key = 'xchan_addr';
+ $v = $arr['address'];
+ }
+
+ if(! $key)
+ return false;
+
+ $r = q("select * from xchan where $key = '$v'");
+ if(! $r)
+ return false;
+
+ $ret = array();
+ foreach($r as $k => $v) {
+ if($k === 'xchan_addr')
+ $ret['address'] = $v;
+ else
+ $ret[str_replace('xchan_','',$k)] = $v;
+ }
+ return $ret;
} \ No newline at end of file
diff --git a/include/identity.php b/include/identity.php
index 282d323be..7ae8e48f6 100644
--- a/include/identity.php
+++ b/include/identity.php
@@ -22,15 +22,18 @@ require_once('include/crypto.php');
function identity_check_service_class($account_id) {
$ret = array('success' => false, $message => '');
- $r = q("select count(channel_id) as total from channel where channel_account_id = %d and not ( channel_pageflags & %d ) ",
+ $r = q("select count(channel_id) as total from channel where channel_account_id = %d and not ( channel_pageflags & %d )>0 ",
intval($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;
@@ -101,7 +104,7 @@ function create_sys_channel() {
}
function get_sys_channel() {
- $r = q("select * from channel left join xchan on channel_hash = xchan_hash where (channel_pageflags & %d) limit 1",
+ $r = q("select * from channel left join xchan on channel_hash = xchan_hash where (channel_pageflags & %d)>0 limit 1",
intval(PAGE_SYSTEM)
);
if($r)
@@ -129,7 +132,7 @@ function is_sys_channel($channel_id) {
*/
function channel_total() {
- $r = q("select channel_id from channel where not ( channel_pageflags & %d )",
+ $r = q("select channel_id from channel where not ( channel_pageflags & %d )>0",
intval(PAGE_REMOVED)
);
@@ -166,10 +169,13 @@ 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) {
@@ -215,22 +221,42 @@ function create_identity($arr) {
if(array_key_exists('primary', $arr))
$primary = intval($arr['primary']);
+
$perms_sql = '';
- $defperms = site_default_perms();
+ $role_permissions = null;
$global_perms = get_perms();
- foreach($defperms as $p => $v) {
- $perms_keys .= ', ' . $global_perms[$p][0];
- $perms_vals .= ', ' . intval($v);
+
+ if(array_key_exists('permissions_role',$arr) && $arr['permissions_role']) {
+ $role_permissions = get_role_perms($arr['permissions_role']);
+
+ if($role_permissions) {
+ foreach($role_permissions as $p => $v) {
+ if(strpos($p,'channel_') !== false) {
+ $perms_keys .= ', ' . $p;
+ $perms_vals .= ', ' . intval($v);
+ }
+ if($p === 'directory_publish')
+ $publish = intval($v);
+ }
+ }
+ }
+ else {
+ $defperms = site_default_perms();
+ foreach($defperms as $p => $v) {
+ $perms_keys .= ', ' . $global_perms[$p][0];
+ $perms_vals .= ', ' . intval($v);
+ }
}
+
$expire = get_config('system', 'default_expire_days');
$expire = (($expire===false)? '0': $expire);
-
+
$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),
@@ -242,12 +268,11 @@ function create_identity($arr) {
dbesc($key['prvkey']),
dbesc($key['pubkey']),
intval($pageflags),
- intval($expire)
+ intval($expire),
+ dbesc($a->timezone)
);
-
-
$r = q("select * from channel where channel_account_id = %d
and channel_guid = '%s' limit 1",
intval($arr['account_id']),
@@ -267,8 +292,8 @@ function create_identity($arr) {
// Create a verified hub location pointing to this site.
$r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_flags,
- hubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey )
- values ( '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s' )",
+ hubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey, hubloc_network )
+ values ( '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s' )",
dbesc($guid),
dbesc($sig),
dbesc($hash),
@@ -278,7 +303,8 @@ function create_identity($arr) {
dbesc(base64url_encode(rsa_sign(z_root(),$ret['channel']['channel_prvkey']))),
dbesc(get_app()->get_hostname()),
dbesc(z_root() . '/post'),
- dbesc(get_config('system','pubkey'))
+ dbesc(get_config('system','pubkey')),
+ dbesc('zot')
);
if(! $r)
logger('create_identity: Unable to store hub location');
@@ -322,24 +348,76 @@ function create_identity($arr) {
dbesc($a->get_baseurl() . "/photo/profile/m/{$newuid}")
);
- $r = q("insert into abook ( abook_account, abook_channel, abook_xchan, abook_closeness, abook_created, abook_updated, abook_flags )
- values ( %d, %d, '%s', %d, '%s', '%s', %d ) ",
+ if($role_permissions) {
+ $myperms = ((array_key_exists('perms_auto',$role_permissions) && $role_permissions['perms_auto']) ? intval($role_permissions['perms_accept']) : 0);
+ }
+ else
+ $myperms = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK
+ |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT
+ |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE;
+
+ $r = q("insert into abook ( abook_account, abook_channel, abook_xchan, abook_closeness, abook_created, abook_updated, abook_flags, abook_my_perms )
+ values ( %d, %d, '%s', %d, '%s', '%s', %d, %d ) ",
intval($ret['channel']['channel_account_id']),
intval($newuid),
dbesc($hash),
intval(0),
dbesc(datetime_convert()),
dbesc(datetime_convert()),
- intval(ABOOK_FLAG_SELF)
+ intval(ABOOK_FLAG_SELF),
+ intval($myperms)
);
if(intval($ret['channel']['channel_account_id'])) {
- // Create a group with no members. This allows somebody to use it
+ // Save our permissions role so we can perhaps call it up and modify it later.
+
+ if($role_permissions) {
+ set_pconfig($newuid,'system','permissions_role',$arr['permissions_role']);
+ if(array_key_exists('online',$role_permissions))
+ set_pconfig($newuid,'system','hide_presence',1-intval($role_permissions['online']));
+ if(array_key_exists('perms_auto',$role_permissions))
+ set_pconfig($newuid,'system','autoperms',(($role_permissions['perms_auto']) ? $role_permissions['perms_accept'] : 0));
+ }
+
+ // Create a group with yourself as a member. This allows somebody to use it
// right away as a default group for new contacts.
require_once('include/group.php');
group_add($newuid, t('Friends'));
+ group_add_member($newuid,t('Friends'),$ret['channel']['channel_hash']);
+
+ // if our role_permissions indicate that we're using a default collection ACL, add it.
+
+ if(is_array($role_permissions) && $role_permissions['default_collection']) {
+ $r = q("select hash from groups where uid = %d and name = '%s' limit 1",
+ intval($newuid),
+ dbesc( t('Friends') )
+ );
+ if($r) {
+ q("update channel set channel_default_group = '%s', channel_allow_gid = '%s' where channel_id = %d",
+ dbesc($r[0]['hash']),
+ dbesc('<' . $r[0]['hash'] . '>'),
+ intval($newuid)
+ );
+ }
+ }
+
+ // 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)) {
+ require_once('include/follow.php');
+ 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);
@@ -373,7 +451,7 @@ function set_default_login_identity($account_id,$channel_id,$force = true) {
);
if($r) {
if((intval($r[0]['account_default_channel']) == 0) || ($force)) {
- $r = q("update account set account_default_channel = %d where account_id = %d limit 1",
+ $r = q("update account set account_default_channel = %d where account_id = %d",
intval($channel_id),
intval($account_id)
);
@@ -382,21 +460,22 @@ function set_default_login_identity($account_id,$channel_id,$force = true) {
}
/**
- * @function identity_basic_export($channel_id)
+ * @function identity_basic_export($channel_id,$items = false)
* Create an array representing the important channel information
* which would be necessary to create a nomadic identity clone. This includes
* most channel resources and connection information with the exception of content.
*
* @param int $channel_id
* Channel_id to export
- *
+ * @param boolean $items
+ * Include channel posts (wall items), default false
*
* @returns array
* See function for details
*
*/
-function identity_basic_export($channel_id) {
+function identity_basic_export($channel_id, $items = false) {
/*
* Red basic channel export
@@ -469,7 +548,62 @@ function identity_basic_export($channel_id) {
}
+ // 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)
+ );
+
+ if($r)
+ $ret['obj'] = $r;
+
+
+ if(! $items)
+ return $ret;
+
+
+ $r = q("select likes.*, item.mid from likes left join item on likes.iid = item.id where likes.channel_id = %d",
+ intval($channel_id)
+ );
+
+ if($r)
+ $ret['likes'] = $r;
+
+
+ $r = q("select item_id.*, item.mid from item_id left join item on item_id.iid = item.id where item_id.uid = %d",
+ intval($channel_id)
+ );
+
+ if($r)
+ $ret['item_id'] = $r;
+
+ $key = get_config('system','prvkey');
+
+ // warning: this may run into memory limits on smaller systems
+
+ $r = q("select * from item where (item_flags & %d)>0 and not (item_restrict & %d)>0 and uid = %d",
+ intval(ITEM_WALL),
+ intval(ITEM_DELETED),
+ intval($channel_id)
+ );
+ if($r) {
+ $ret['item'] = array();
+ xchan_query($r);
+ $r = fetch_post_tags($r,true);
+ foreach($r as $rr)
+ $ret['item'][] = encode_item($rr,true);
+
+ }
return $ret;
+
}
@@ -544,7 +678,7 @@ function profile_load(&$a, $nickname, $profile = '') {
if(! $p) {
$p = q("SELECT profile.uid AS profile_uid, profile.*, channel.* FROM profile
LEFT JOIN channel ON profile.uid = channel.channel_id
- WHERE channel.channel_address = '%s' and not ( channel_pageflags & %d )
+ WHERE channel.channel_address = '%s' and not ( channel_pageflags & %d )>0
AND profile.is_default = 1 LIMIT 1",
dbesc($nickname),
intval(PAGE_REMOVED)
@@ -756,6 +890,8 @@ function profile_sidebar($profile, $block = 0, $show_connect = true) {
|| (x($profile,'country_name') == 1))
$location = t('Location:');
+ $profile['homepage'] = linkify($profile['homepage']);
+
$gender = ((x($profile,'gender') == 1) ? t('Gender:') : False);
$marital = ((x($profile,'marital') == 1) ? t('Status:') : False);
$homepage = ((x($profile,'homepage') == 1) ? t('Homepage:') : False);
@@ -977,7 +1113,7 @@ logger('online: ' . $profile['online']);
function advanced_profile(&$a) {
-
+ require_once('include/text.php');
if(! perm_is_allowed($a->profile['profile_uid'],get_observer_hash(),'view_profile'))
return '';
@@ -1042,7 +1178,7 @@ function advanced_profile(&$a) {
if($a->profile['with']) $profile['marital']['with'] = bbcode($a->profile['with']);
- if(strlen($a->profile['howlong']) && $a->profile['howlong'] !== '0000-00-00 00:00:00') {
+ if(strlen($a->profile['howlong']) && $a->profile['howlong'] !== NULL_DATE) {
$profile['howlong'] = relative_date($a->profile['howlong'], t('for %1$d %2$s'));
}
@@ -1069,7 +1205,18 @@ function advanced_profile(&$a) {
if($txt = prepare_text($a->profile['contact'])) $profile['contact'] = array( t('Contact information and Social Networks:'), $txt);
- if($txt = prepare_text($a->profile['channels'])) $profile['channels'] = array( t('My other channels:'), $txt);
+ // Support tags in the other channels field (probably want to restrict it to channels only?)
+ $txt = $a->profile['channels'];
+ $matches = get_tags($txt);
+ $access_tag = '';
+ $str_tags = '';
+ foreach($matches as $m) {
+ $success = handle_tag($a, $txt, $access_tag, $str_tags, $a->profile_uid, $m); // Use uid of the profile maker
+ }
+
+ if($txt = prepare_text($txt)) {
+ $profile['channels'] = array( t('My other channels:'), $txt);
+ }
if($txt = prepare_text($a->profile['music'])) $profile['music'] = array( t('Musical interests:'), $txt);
@@ -1244,7 +1391,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';
}
@@ -1337,7 +1484,7 @@ function get_channel_by_nick($nick) {
function identity_selector() {
if(local_user()) {
- $r = q("select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel.channel_account_id = %d and not ( channel_pageflags & %d ) order by channel_name ",
+ $r = q("select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel.channel_account_id = %d and (channel_pageflags & %d) = 0 order by channel_name ",
intval(get_account_id()),
intval(PAGE_REMOVED)
);
@@ -1400,4 +1547,48 @@ function get_profile_fields_advanced($filter = 0) {
return $x;
}
+/**
+ * @function notifications_off($channel_id)
+ * Clear notifyflags for a channel - most likely during bulk import of content or other activity that is likely
+ * to generate huge amounts of undesired notifications.
+ * @param int $channel_id
+ * The channel to disable notifications for
+ * @returns int
+ * Current notification flag value. Send this to notifications_on() to restore the channel settings when finished
+ * with the activity requiring notifications_off();
+ */
+
+
+
+function notifications_off($channel_id) {
+ $r = q("select channel_notifyflags from channel where channel_id = %d limit 1",
+ intval($channel_id)
+ );
+ $x = q("update channel set channel_notifyflags = 0 where channel_id = %d",
+ intval($channel_id)
+ );
+
+ return intval($r[0]['channel_notifyflags']);
+
+}
+
+
+function notifications_on($channel_id,$value) {
+ $x = q("update channel set channel_notifyflags = %d where channel_id = %d",
+ intval($value),
+ intval($channel_id)
+ );
+ return $x;
+}
+
+
+function get_channel_default_perms($uid) {
+ $r = q("select abook_my_perms from abook where abook_channel = %d and (abook_flags & %d) > 0 limit 1",
+ intval($uid),
+ intval(ABOOK_FLAG_SELF)
+ );
+ if($r)
+ return $r[0]['abook_my_perms'];
+ return 0;
+}
diff --git a/include/items.php b/include/items.php
index 44d4abdf8..c488e1953 100755
--- a/include/items.php
+++ b/include/items.php
@@ -30,7 +30,7 @@ function collect_recipients($item,&$private_envelope) {
// as that would allow the denied person to see the post by logging out.
if((! $item['allow_cid']) && (! $item['allow_gid'])) {
- $r = q("select * from abook where abook_channel = %d and not (abook_flags & %d) ",
+ $r = q("select * from abook where abook_channel = %d and not (abook_flags & %d)>0 ",
intval($item['uid']),
intval(ABOOK_FLAG_SELF|ABOOK_FLAG_PENDING|ABOOK_FLAG_ARCHIVED)
);
@@ -66,20 +66,44 @@ function collect_recipients($item,&$private_envelope) {
// by the directives in $item['public_policy'].
$private_envelope = false;
+ require_once('include/identity.php');
+ $sys = get_sys_channel();
if(array_key_exists('public_policy',$item) && $item['public_policy'] !== 'self') {
- $r = q("select abook_xchan from abook where abook_channel = %d and not (abook_flags & %d) ",
+ $r = q("select abook_xchan, xchan_network from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and not (abook_flags & %d)>0 ",
intval($item['uid']),
intval(ABOOK_FLAG_SELF|ABOOK_FLAG_PENDING|ABOOK_FLAG_ARCHIVED)
);
if($r) {
+
+ // filter out restrictive public_policy settings from remote networks
+ // which don't have this concept and will treat them as public.
+
+ $policy = substr($item['public_policy'],0,3);
foreach($r as $rr) {
- $recipients[] = $rr['abook_xchan'];
+ switch($policy) {
+ case 'net':
+ case 'aut':
+ case 'sit':
+ case 'any':
+ case 'con':
+ if($rr['xchan_network'] != 'zot')
+ break;
+ case 'pub':
+ case '':
+ default:
+ $recipients[] = $rr['abook_xchan'];
+ break;
+ }
}
}
+// we probably want to check that discovery channel delivery is allowed before uncommenting this.
+// if($policy === 'pub')
+// $recipients[] = $sys['xchan_hash'];
}
}
+
// This is a somewhat expensive operation but important.
// Don't send this item to anybody who isn't allowed to see it
@@ -144,7 +168,7 @@ function filter_insecure($channel_id,$arr) {
function comments_are_now_closed($item) {
- if($item['comments_closed'] !== '0000-00-00 00:00:00') {
+ if($item['comments_closed'] !== NULL_DATE) {
$d = datetime_convert();
if($d > $item['comments_closed'])
return true;
@@ -195,6 +219,7 @@ function can_comment_on_post($observer_xchan,$item) {
break;
case 'any connections':
case 'contacts':
+ case 'authenticated':
case '':
if(array_key_exists('owner',$item)) {
if(($item['owner']['abook_xchan']) && ($item['owner']['abook_their_perms'] & PERMS_W_COMMENT))
@@ -238,7 +263,7 @@ function add_source_route($iid,$hash) {
);
if($r) {
$new_route = (($r[0]['route']) ? $r[0]['route'] . ',' : '') . $hash;
- q("update item set route = '%s' where id = %d limit 1",
+ q("update item set route = '%s' where id = %d",
(dbesc($new_route)),
intval($iid)
);
@@ -448,7 +473,7 @@ function post_activity_item($arr) {
function get_public_feed($channel,$params) {
$type = 'xml';
- $begin = '0000-00-00 00:00:00';
+ $begin = NULL_DATE;
$end = '';
$start = 0;
$records = 40;
@@ -459,7 +484,7 @@ function get_public_feed($channel,$params) {
$params = array();
$params['type'] = ((x($params,'type')) ? $params['type'] : 'xml');
- $params['begin'] = ((x($params,'begin')) ? $params['begin'] : '0000-00-00 00:00:00');
+ $params['begin'] = ((x($params,'begin')) ? $params['begin'] : NULL_DATE);
$params['end'] = ((x($params,'end')) ? $params['end'] : datetime_convert('UTC','UTC','now'));
$params['start'] = ((x($params,'start')) ? $params['start'] : 0);
$params['records'] = ((x($params,'records')) ? $params['records'] : 40);
@@ -741,6 +766,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) {
@@ -758,14 +785,14 @@ function get_item_elements($x) {
$arr['expires'] = ((x($x,'expires') && $x['expires'])
? datetime_convert('UTC','UTC',$x['expires'])
- : '0000-00-00 00:00:00');
+ : NULL_DATE);
$arr['commented'] = ((x($x,'commented') && $x['commented'])
? datetime_convert('UTC','UTC',$x['commented'])
: $arr['created']);
$arr['comments_closed'] = ((x($x,'comments_closed') && $x['comments_closed'])
? datetime_convert('UTC','UTC',$x['comments_closed'])
- : '0000-00-00 00:00:00');
+ : NULL_DATE);
$arr['title'] = (($x['title']) ? htmlspecialchars($x['title'], ENT_COMPAT,'UTF-8',false) : '');
@@ -795,7 +822,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']);
@@ -847,15 +874,35 @@ 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.
+
+
if(intval($arr['item_private'])) {
$arr['item_flags'] = $arr['item_flags'] | ITEM_OBSCURED;
- $key = get_config('system','pubkey');
if($arr['title'])
$arr['title'] = json_encode(crypto_encapsulate($arr['title'],$key));
if($arr['body'])
$arr['body'] = json_encode(crypto_encapsulate($arr['body'],$key));
}
+
+ if(array_key_exists('revision',$x)) {
+ // extended export encoding
+
+ $arr['revision'] = $x['revision'];
+ $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['layout_mid'] = $x['layout_mid'];
+ $arr['postopts'] = $x['postopts'];
+ $arr['resource_id'] = $x['resource_id'];
+ $arr['resource_type'] = $x['resource_type'];
+ $arr['item_restrict'] = $x['item_restrict'];
+ $arr['item_flags'] = $x['item_flags'];
+ $arr['attach'] = $x['attach'];
+
+ }
+
return $arr;
}
@@ -878,6 +925,10 @@ function import_author_xchan($x) {
$y = import_author_rss($x);
}
+ if($x['network'] === 'unknown') {
+ $y = import_author_unknown($x);
+ }
+
return(($y) ? $y : false);
}
@@ -918,12 +969,12 @@ function import_author_rss($x) {
dbesc(($name) ? $name : t('(Unknown)')),
dbesc('rss')
);
- if($r) {
+ if($r && $x['photo']) {
- $photos = import_profile_photo($x['photo'],$x['url']);
+ $photos = import_profile_photo($x['photo']['src'],$x['url']);
if($photos) {
- $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' limit 1",
+ $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($photos[0]),
dbesc($photos[1]),
@@ -940,8 +991,52 @@ function import_author_rss($x) {
}
+function import_author_unknown($x) {
+
+ if(! $x['url'])
+ return false;
+
+ $r = q("select xchan_hash from xchan where xchan_network = 'unknown' and xchan_url = '%s' limit 1",
+ dbesc($x['url'])
+ );
+ if($r) {
+ logger('import_author_unknown: in cache' , LOGGER_DEBUG);
+ return $r[0]['xchan_hash'];
+ }
+
+ $name = trim($x['name']);
-function encode_item($item) {
+ $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_url, xchan_name, xchan_network )
+ values ( '%s', '%s', '%s', '%s', '%s' )",
+ dbesc($x['url']),
+ dbesc($x['url']),
+ dbesc($x['url']),
+ dbesc(($name) ? $name : t('(Unknown)')),
+ dbesc('unknown')
+ );
+ if($r && $x['photo']) {
+
+ $photos = import_profile_photo($x['photo']['src'],$x['url']);
+
+ if($photos) {
+ $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($photos[0]),
+ dbesc($photos[1]),
+ dbesc($photos[2]),
+ dbesc($photos[3]),
+ dbesc($x['url'])
+ );
+ if($r)
+ return $x['url'];
+ }
+ }
+
+ return false;
+
+}
+
+function encode_item($item,$mirror = false) {
$x = array();
$x['type'] = 'activity';
$x['encoding'] = 'zot';
@@ -963,21 +1058,44 @@ function encode_item($item) {
$c_scope = map_scope($comment_scope);
+ $key = get_config('system','prvkey');
+
if(array_key_exists('item_flags',$item) && ($item['item_flags'] & ITEM_OBSCURED)) {
- $key = get_config('system','prvkey');
if($item['title'])
$item['title'] = crypto_unencapsulate(json_decode_plus($item['title']),$key);
if($item['body'])
$item['body'] = crypto_unencapsulate(json_decode_plus($item['body']),$key);
}
+ // If we're trying to backup an item so that it's recoverable or for export/imprt,
+ // add all the attributes we need to recover it
+
+ if($mirror) {
+ $x['id'] = $item['id'];
+ $x['parent'] = $item['parent'];
+ $x['uid'] = $item['uid'];
+ $x['allow_cid'] = $item['allow_cid'];
+ $x['allow_gid'] = $item['allow_gid'];
+ $x['deny_cid'] = $item['deny_cid'];
+ $x['deny_gid'] = $item['deny_gid'];
+ $x['revision'] = $item['revision'];
+ $x['layout_mid'] = $item['layout_mid'];
+ $x['postopts'] = $item['postopts'];
+ $x['resource_id'] = $item['resource_id'];
+ $x['resource_type'] = $item['resource_type'];
+ $x['item_restrict'] = $item['item_restrict'];
+ $x['item_flags'] = $item['item_flags'];
+ $x['attach'] = $item['attach'];
+ }
+
$x['message_id'] = $item['mid'];
$x['message_top'] = $item['parent_mid'];
$x['message_parent'] = $item['thr_parent'];
$x['created'] = $item['created'];
$x['edited'] = $item['edited'];
- $x['expires'] = $item['expires'];
+ // always send 0's over the wire
+ $x['expires'] = (($item['expires'] == '0001-01-01 00:00:00') ? '0000-00-00 00:00:00' : $item['expires']);
$x['commented'] = $item['commented'];
$x['mimetype'] = $item['mimetype'];
$x['title'] = $item['title'];
@@ -1003,7 +1121,7 @@ function encode_item($item) {
if($y = encode_item_flags($item))
$x['flags'] = $y;
- if($item['comments_closed'] !== '0000-00-00 00:00:00')
+ if($item['comments_closed'] !== NULL_DATE)
$x['comments_closed'] = $item['comments_closed'];
$x['public_scope'] = $scope;
@@ -1016,6 +1134,9 @@ function encode_item($item) {
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;
@@ -1242,8 +1363,8 @@ function get_mail_elements($x) {
$arr['title'] = (($x['title'])? htmlspecialchars($x['title'],ENT_COMPAT,'UTF-8',false) : '');
$arr['created'] = datetime_convert('UTC','UTC',$x['created']);
- if((! array_key_exists('expires',$x)) || ($x['expires'] === '0000-00-00 00:00:00'))
- $arr['expires'] = '0000-00-00 00:00:00';
+ if((! array_key_exists('expires',$x)) || ($x['expires'] === NULL_DATE))
+ $arr['expires'] = NULL_DATE;
else
$arr['expires'] = datetime_convert('UTC','UTC',$x['expires']);
@@ -1329,16 +1450,23 @@ function get_atom_elements($feed,$item,&$author) {
if($found_author) {
$author['author_name'] = unxmlify($found_author->get_name());
$author['author_link'] = unxmlify($found_author->get_link());
+ $author['author_is_feed'] = false;
}
else {
$author['author_name'] = unxmlify($feed->get_title());
$author['author_link'] = unxmlify($feed->get_permalink());
+ $author['author_is_feed'] = true;
}
- $res['mid'] = unxmlify($item->get_id());
+ if(substr($author['author_link'],-1,1) == '/')
+ $author['author_link'] = substr($author['author_link'],0,-1);
+
+ $res['mid'] = base64url_encode(unxmlify($item->get_id()));
$res['title'] = unxmlify($item->get_title());
$res['body'] = unxmlify($item->get_content());
$res['plink'] = unxmlify($item->get_link(0));
+ $res['item_flags'] = ITEM_RSS;
+
// removing the content of the title if its identically to the body
// This helps with auto generated titles e.g. from tumblr
@@ -1382,6 +1510,16 @@ function get_atom_elements($feed,$item,&$author) {
}
}
+ // check for a yahoo media element (github etc.)
+
+ if(! $author['author_photo']) {
+ $rawmedia = $item->get_item_tags(NAMESPACE_YMEDIA,'thumbnail');
+ if($rawmedia && $rawmedia[0]['attribs']['']['url']) {
+ $author['author_photo'] = strip_tags(unxmlify($rawmedia[0]['attribs']['']['url']));
+ }
+ }
+
+
// No photo/profile-link on the item - look at the feed level
if((! (x($author,'author_link'))) || (! (x($author,'author_photo')))) {
@@ -1389,8 +1527,10 @@ function get_atom_elements($feed,$item,&$author) {
if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
$base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
foreach($base as $link) {
- if($link['attribs']['']['rel'] === 'alternate' && (! $author['author_link']))
+ if($link['attribs']['']['rel'] === 'alternate' && (! $author['author_link'])) {
$author['author_link'] = unxmlify($link['attribs']['']['href']);
+ $author['author_is_feed'] = true;
+ }
if(! $author['author_photo']) {
if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
$author['author_photo'] = unxmlify($link['attribs']['']['href']);
@@ -1438,7 +1578,7 @@ function get_atom_elements($feed,$item,&$author) {
// We could probably turn these old Friendica bbcode bookmarks into bookmark tags but we'd have to
// create a term table item for them. For now just make sure they stay as links.
- $res['body'] = preg_replace('/\[bookmark(.*?)\](.*?)\[\/bookmark\]','[url$1]$2[/url]',$res['body']);
+ $res['body'] = preg_replace('/\[bookmark(.*?)\](.*?)\[\/bookmark\]/','[url$1]$2[/url]',$res['body']);
}
@@ -1477,6 +1617,28 @@ function get_atom_elements($feed,$item,&$author) {
$res['body'] = escape_tags($res['body']);
}
+ if($res['plink'] && $res['title']) {
+ $res['body'] = '#^[url=' . $res['plink'] . ']' . $res['title'] . '[/url]' . "\n\n" . $res['body'];
+ $terms = array();
+ $terms[] = array(
+ 'otype' => TERM_OBJ_POST,
+ 'type' => TERM_BOOKMARK,
+ 'url' => $res['plink'],
+ 'term' => $res['title'],
+ );
+ }
+ elseif($res['plink']) {
+ $res['body'] = '#^[url]' . $res['plink'] . '[/url]' . "\n\n" . $res['body'];
+ $terms = array();
+ $terms[] = array(
+ 'otype' => TERM_OBJ_POST,
+ 'type' => TERM_BOOKMARK,
+ 'url' => $res['plink'],
+ 'term' => $res['plink'],
+ );
+ }
+
+
$private = $item->get_item_tags(NAMESPACE_DFRN,'private');
if($private && intval($private[0]['data']) > 0)
$res['item_private'] = ((intval($private[0]['data'])) ? 1 : 0);
@@ -1565,7 +1727,8 @@ function get_atom_elements($feed,$item,&$author) {
$cats = $item->get_categories();
if($cats) {
- $terms = array();
+ if(is_null($terms))
+ $terms = array();
foreach($cats as $cat) {
$term = $cat->get_term();
if(! $term)
@@ -1590,9 +1753,11 @@ function get_atom_elements($feed,$item,&$author) {
);
}
}
- $res['term'] = $terms;
}
+ if(! is_null($terms))
+ $res['term'] = $terms;
+
$attach = $item->get_enclosures();
if($attach) {
$res['attach'] = array();
@@ -1692,6 +1857,7 @@ function get_atom_elements($feed,$item,&$author) {
call_hooks('parse_atom', $arr);
logger('get_atom_elements: author: ' . print_r($author,true),LOGGER_DATA);
+
logger('get_atom_elements: ' . print_r($res,true),LOGGER_DATA);
return $res;
@@ -1839,9 +2005,9 @@ function item_store($arr,$allow_exec = false) {
$arr['owner_xchan'] = ((x($arr,'owner_xchan')) ? notags(trim($arr['owner_xchan'])) : '');
$arr['created'] = ((x($arr,'created') !== false) ? datetime_convert('UTC','UTC',$arr['created']) : datetime_convert());
$arr['edited'] = ((x($arr,'edited') !== false) ? datetime_convert('UTC','UTC',$arr['edited']) : datetime_convert());
- $arr['expires'] = ((x($arr,'expires') !== false) ? datetime_convert('UTC','UTC',$arr['expires']) : '0000-00-00 00:00:00');
+ $arr['expires'] = ((x($arr,'expires') !== false) ? datetime_convert('UTC','UTC',$arr['expires']) : NULL_DATE);
$arr['commented'] = ((x($arr,'commented') !== false) ? datetime_convert('UTC','UTC',$arr['commented']) : datetime_convert());
- $arr['comments_closed'] = ((x($arr,'comments_closed') !== false) ? datetime_convert('UTC','UTC',$arr['comments_closed']) : '0000-00-00 00:00:00');
+ $arr['comments_closed'] = ((x($arr,'comments_closed') !== false) ? datetime_convert('UTC','UTC',$arr['comments_closed']) : NULL_DATE);
$arr['received'] = datetime_convert();
$arr['changed'] = datetime_convert();
@@ -1914,6 +2080,7 @@ function item_store($arr,$allow_exec = false) {
return $ret;
}
+
// is the new message multi-level threaded?
// even though we don't support it now, preserve the info
// and re-attach to the conversation parent.
@@ -2052,7 +2219,7 @@ function item_store($arr,$allow_exec = false) {
$r = q("UPDATE item SET parent = %d, allow_cid = '%s', allow_gid = '%s',
deny_cid = '%s', deny_gid = '%s', public_policy = '%s', item_private = %d, comments_closed = '%s'
- WHERE id = %d LIMIT 1",
+ WHERE id = %d",
intval($parent_id),
dbesc($allow_cid),
dbesc($allow_gid),
@@ -2097,13 +2264,13 @@ function item_store($arr,$allow_exec = false) {
// update the commented timestamp on the parent
- $z = q("select max(created) as commented from item where parent_mid = '%s' and uid = %d and not ( item_restrict & %d ) ",
+ $z = q("select max(created) as commented from item where parent_mid = '%s' and uid = %d and not ( item_restrict & %d )>0 ",
dbesc($arr['parent_mid']),
intval($arr['uid']),
intval(ITEM_DELAYED_PUBLISH)
);
- q("UPDATE item set commented = '%s', changed = '%s' WHERE id = %d LIMIT 1",
+ q("UPDATE item set commented = '%s', changed = '%s' WHERE id = %d",
dbesc(($z) ? $z[0]['commented'] : (datetime_convert())),
dbesc(datetime_convert()),
intval($parent_id)
@@ -2248,7 +2415,7 @@ function item_store_update($arr,$allow_exec = false) {
$arr['edited'] = ((x($arr,'edited') !== false) ? datetime_convert('UTC','UTC',$arr['edited']) : datetime_convert());
$arr['expires'] = ((x($arr,'expires') !== false) ? datetime_convert('UTC','UTC',$arr['expires']) : $orig[0]['expires']);
- if(array_key_exists('comments_closed',$arr) && $arr['comments_closed'] != '0000-00-00 00:00:00')
+ if(array_key_exists('comments_closed',$arr) && $arr['comments_closed'] != NULL_DATE)
$arr['comments_closed'] = datetime_convert('UTC','UTC',$arr['comments_closed']);
else
$arr['comments_closed'] = $orig[0]['comments_closed'];
@@ -2256,7 +2423,7 @@ function item_store_update($arr,$allow_exec = false) {
$arr['commented'] = $orig[0]['commented'];
$arr['received'] = datetime_convert();
$arr['changed'] = datetime_convert();
-
+ $arr['route'] = ((array_key_exists('route',$arr)) ? trim($arr['route']) : $orig[0]['route']);
$arr['diaspora_meta'] = ((x($arr,'diaspora_meta')) ? $arr['diaspora_meta'] : $orig[0]['diaspora_meta']);
$arr['location'] = ((x($arr,'location')) ? notags(trim($arr['location'])) : $orig[0]['location']);
$arr['coord'] = ((x($arr,'coord')) ? notags(trim($arr['coord'])) : $orig[0]['coord']);
@@ -2315,7 +2482,7 @@ function item_store_update($arr,$allow_exec = false) {
$str .= " `" . $k . "` = '" . $v . "' ";
}
- $r = dbq("update `item` set " . $str . " where id = " . $orig_post_id . " limit 1");
+ $r = dbq("update `item` set " . $str . " where id = " . $orig_post_id );
if($r)
logger('item_store_update: updated item ' . $orig_post_id, LOGGER_DEBUG);
@@ -2357,31 +2524,26 @@ function item_store_update($arr,$allow_exec = false) {
return $ret;
}
-function store_diaspora_comment_sig($datarray, $channel, $parent_item, $post_id) {
+function store_diaspora_comment_sig($datarray, $channel, $parent_item, $post_id, $walltowall = false) {
// We won't be able to sign Diaspora comments for authenticated visitors
// - we don't have their private key
// since Diaspora doesn't handle edits we can only do this for the original text and not update it.
- $enabled = intval(get_config('system','diaspora_enabled'));
- if(! $enabled) {
- logger('mod_item: diaspora support disabled, not storing comment signature', LOGGER_DEBUG);
- return;
- }
+ require_once('include/bb2diaspora.php');
+ $signed_body = bb2diaspora_itembody($datarray,$walltowall);
- $body = $datarray['body'];
- if(array_key_exists('item_flags',$datarray) && ($datarray['item_flags'] & ITEM_OBSCURED)) {
- $key = get_config('system','prvkey');
- if($datarray['body'])
- $body = crypto_unencapsulate(json_decode($datarray['body'],true),$key);
+ if($walltowall) {
+ logger('wall to wall comment',LOGGER_DEBUG);
+ // post will come across with the owner's identity. Throw a preamble onto the post to indicate the true author.
+ $signed_body = "\n\n"
+ . '![' . $datarray['author']['xchan_name'] . '](' . $datarray['author']['xchan_photo_m'] . ')'
+ . '[' . $datarray['author']['xchan_name'] . '](' . $datarray['author']['xchan_url'] . ')' . "\n\n"
+ . $signed_body;
}
- logger('mod_item: storing diaspora comment signature',LOGGER_DEBUG);
-
- require_once('include/bb2diaspora.php');
-
- $signed_body = html_entity_decode(bb2diaspora($body));
+ logger('storing diaspora comment signature',LOGGER_DEBUG);
$diaspora_handle = $channel['channel_address'] . '@' . get_app()->get_hostname();
@@ -2394,17 +2556,14 @@ function store_diaspora_comment_sig($datarray, $channel, $parent_item, $post_id)
$x = array('signer' => $diaspora_handle, 'body' => $signed_body, 'signed_text' => $signed_text, 'signature' => base64_encode($authorsig));
- $r = q("update item set diaspora_meta = '%s' where id = %d limit 1",
- dbesc(json_encode($x)),
+ $key = get_config('system','pubkey');
+ $y = crypto_encapsulate(json_encode($x),$key);
+
+ $r = q("update item set diaspora_meta = '%s' where id = %d",
+ dbesc(json_encode($y)),
intval($post_id)
);
- $r = q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
- intval($post_id),
- dbesc($signed_text),
- dbesc(base64_encode($authorsig)),
- dbesc($diaspora_handle)
- );
if(! $r)
logger('store_diaspora_comment_sig: DB write failed');
@@ -2596,7 +2755,7 @@ function tag_deliver($uid,$item_id) {
$taglink = get_rel_link($j_obj['link'],'alternate');
store_item_tag($u[0]['channel_id'],$p[0]['id'],TERM_OBJ_POST,TERM_HASHTAG,$j_obj['title'],$j_obj['id']);
- $x = q("update item set edited = '%s', received = '%s', changed = '%s' where mid = '%s' and uid = %d limit 1",
+ $x = q("update item set edited = '%s', received = '%s', changed = '%s' where mid = '%s' and uid = %d",
dbesc(datetime_convert()),
dbesc(datetime_convert()),
dbesc(datetime_convert()),
@@ -2662,7 +2821,7 @@ function tag_deliver($uid,$item_id) {
if($mention) {
logger('tag_deliver: mention found for ' . $u[0]['channel_name']);
- $r = q("update item set item_flags = ( item_flags | %d ) where id = %d limit 1",
+ $r = q("update item set item_flags = ( item_flags | %d ) where id = %d",
intval(ITEM_MENTIONSME),
intval($item_id)
);
@@ -2777,7 +2936,7 @@ function tgroup_check($uid,$item) {
// or is a followup and we have already accepted the top level post as an uplink
if($item['mid'] != $item['parent_mid']) {
- $r = q("select id from item where mid = '%s' and uid = %d and ( item_flags & %d ) limit 1",
+ $r = q("select id from item where mid = '%s' and uid = %d and ( item_flags & %d )>0 limit 1",
dbesc($item['parent_mid']),
intval($uid),
intval(ITEM_UPLINK)
@@ -2821,7 +2980,15 @@ function tgroup_check($uid,$item) {
// At this point we've determined that the person receiving this post was mentioned in it.
// Now let's check if this mention was inside a reshare so we don't spam a forum
- $body = preg_replace('/\[share(.*?)\[\/share\]/','',$item['body']);
+
+ $body = $item['body'];
+
+ if(array_key_exists('item_flags',$item) && ($item['item_flags'] & ITEM_OBSCURED) && $body) {
+ $key = get_config('system','prvkey');
+ $body = crypto_unencapsulate(json_decode($body,true),$key);
+ }
+
+ $body = preg_replace('/\[share(.*?)\[\/share\]/','',$body);
$pattern = '/@\!?\[zrl\=' . preg_quote($term['url'],'/') . '\]' . preg_quote($term['term'] . '+','/') . '\[\/zrl\]/';
@@ -2867,14 +3034,14 @@ function start_delivery_chain($channel,$item,$item_id,$parent) {
// when we created the delivery fork
if($parent) {
- $r = q("update item set source_xchan = '%s' where id = %d limit 1",
+ $r = q("update item set source_xchan = '%s' where id = %d",
dbesc($parent['source_xchan']),
intval($item_id)
);
}
else {
$flag_bits = $flag_bits | ITEM_UPLINK;
- $r = q("update item set source_xchan = owner_xchan where id = %d limit 1",
+ $r = q("update item set source_xchan = owner_xchan where id = %d",
intval($item_id)
);
}
@@ -2904,7 +3071,7 @@ function start_delivery_chain($channel,$item,$item_id,$parent) {
}
$r = q("update item set item_flags = %d, owner_xchan = '%s', allow_cid = '%s', allow_gid = '%s',
- deny_cid = '%s', deny_gid = '%s', item_private = %d, public_policy = '%s', comment_policy = '%s', title = '%s', body = '%s' where id = %d limit 1",
+ deny_cid = '%s', deny_gid = '%s', item_private = %d, public_policy = '%s', comment_policy = '%s', title = '%s', body = '%s' where id = %d",
intval($flag_bits),
dbesc($channel['channel_hash']),
dbesc($channel['channel_allow_cid']),
@@ -2944,7 +3111,7 @@ function start_delivery_chain($channel,$item,$item_id,$parent) {
function check_item_source($uid,$item) {
- $r = q("select * from source where src_channel_id = %d and ( src_xchan = '%s' || src_xchan = '*' ) limit 1",
+ $r = q("select * from source where src_channel_id = %d and ( src_xchan = '%s' or src_xchan = '*' ) limit 1",
intval($uid),
dbesc(($item['source_xchan']) ? $item['source_xchan'] : $item['owner_xchan'])
);
@@ -3018,7 +3185,7 @@ function mail_store($arr) {
$arr['from_xchan'] = ((x($arr,'from_xchan')) ? notags(trim($arr['from_xchan'])) : '');
$arr['to_xchan'] = ((x($arr,'to_xchan')) ? notags(trim($arr['to_xchan'])) : '');
$arr['created'] = ((x($arr,'created') !== false) ? datetime_convert('UTC','UTC',$arr['created']) : datetime_convert());
- $arr['expires'] = ((x($arr,'expires') !== false) ? datetime_convert('UTC','UTC',$arr['expires']) : '0000-00-00 00:00:00');
+ $arr['expires'] = ((x($arr,'expires') !== false) ? datetime_convert('UTC','UTC',$arr['expires']) : NULL_DATE);
$arr['title'] = ((x($arr,'title')) ? notags(trim($arr['title'])) : '');
$arr['parent_mid'] = ((x($arr,'parent_mid')) ? notags(trim($arr['parent_mid'])) : '');
$arr['body'] = ((x($arr,'body')) ? trim($arr['body']) : '');
@@ -3101,7 +3268,6 @@ function mail_store($arr) {
}
-
/**
*
* consume_feed - process atom feed and update anything/everything we might need to update
@@ -3169,7 +3335,7 @@ function consume_feed($xml,$importer,&$contact,$pass = 0) {
if($deleted && is_array($contact)) {
$r = q("SELECT * from item where mid = '%s' and author_xchan = '%s' and uid = %d limit 1",
- dbesc($mid),
+ dbesc(base64url_encode($mid)),
dbesc($contact['xchan_hash']),
intval($importer['channel_id'])
);
@@ -3178,7 +3344,7 @@ function consume_feed($xml,$importer,&$contact,$pass = 0) {
$item = $r[0];
if(! ($item['item_restrict'] & ITEM_DELETED)) {
- logger('consume_feed: deleting item ' . $item['id'] . ' mid=' . $item['mid'], LOGGER_DEBUG);
+ logger('consume_feed: deleting item ' . $item['id'] . ' mid=' . base64url_decode($item['mid']), LOGGER_DEBUG);
drop_item($item['id'],false);
}
}
@@ -3197,14 +3363,14 @@ function consume_feed($xml,$importer,&$contact,$pass = 0) {
foreach($items as $item) {
$is_reply = false;
- $item_id = $item->get_id();
+ $item_id = base64url_encode($item->get_id());
logger('consume_feed: 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 = $rawthread[0]['attribs']['']['ref'];
+ $parent_mid = base64url_encode($rawthread[0]['attribs']['']['ref']);
}
if($is_reply) {
@@ -3215,17 +3381,32 @@ function consume_feed($xml,$importer,&$contact,$pass = 0) {
// Have we seen it? If not, import it.
- $item_id = $item->get_id();
+ $item_id = base64url_encode($item->get_id());
$author = array();
$datarray = get_atom_elements($feed,$item,$author);
- if(! x($author,'author_name'))
+ if((! x($author,'author_name')) || ($author['author_is_feed']))
$author['author_name'] = $contact['xchan_name'];
- if(! x($author,'author_link'))
+ if((! x($author,'author_link')) || ($author['author_is_feed']))
$author['author_link'] = $contact['xchan_url'];
- if(! x($author,'author_photo'))
+ if((! x($author,'author_photo'))|| ($author['author_is_feed']))
$author['author_photo'] = $contact['xchan_photo_m'];
+ $datarray['author_xchan'] = '';
+
+ if($author['author_link'] != $contact['xchan_url']) {
+ $x = import_author_unknown(array('name' => $author['author_name'],'url' => $author['author_link'],'photo' => array('src' => $author['author_photo'])));
+ if($x)
+ $datarray['author_xchan'] = $x;
+
+ }
+ if(! $datarray['author_xchan'])
+ $datarray['author_xchan'] = $contact['xchan_hash'];
+
+
+ $datarray['owner_xchan'] = $contact['xchan_hash'];
+
+
$r = q("SELECT edited FROM item WHERE mid = '%s' AND uid = %d LIMIT 1",
dbesc($item_id),
intval($importer['channel_id'])
@@ -3249,10 +3430,6 @@ function consume_feed($xml,$importer,&$contact,$pass = 0) {
$datarray['parent_mid'] = $parent_mid;
$datarray['uid'] = $importer['channel_id'];
-//FIXME
- $datarray['owner_xchan'] = $datarray['author_xchan'] = $contact['xchan_hash'];
-
- // FIXME pull out the author and owner
logger('consume_feed: ' . print_r($datarray,true),LOGGER_DATA);
@@ -3266,16 +3443,16 @@ function consume_feed($xml,$importer,&$contact,$pass = 0) {
// Head post of a conversation. Have we seen it? If not, import it.
- $item_id = $item->get_id();
+ $item_id = base64url_encode($item->get_id());
$author = array();
$datarray = get_atom_elements($feed,$item,$author);
if(is_array($contact)) {
- if(! x($author,'author_name'))
+ if((! x($author,'author_name')) || ($author['author_is_feed']))
$author['author_name'] = $contact['xchan_name'];
- if(! x($author,'author_link'))
+ if((! x($author,'author_link')) || ($author['author_is_feed']))
$author['author_link'] = $contact['xchan_url'];
- if(! x($author,'author_photo'))
+ if((! x($author,'author_photo'))|| ($author['author_is_feed']))
$author['author_photo'] = $contact['xchan_photo_m'];
}
@@ -3284,6 +3461,20 @@ function consume_feed($xml,$importer,&$contact,$pass = 0) {
continue;
}
+ $datarray['author_xchan'] = '';
+
+ if($author['author_link'] != $contact['xchan_url']) {
+ $x = import_author_unknown(array('name' => $author['author_name'],'url' => $author['author_link'],'photo' => array('src' => $author['author_photo'])));
+ if($x)
+ $datarray['author_xchan'] = $x;
+
+ }
+ if(! $datarray['author_xchan'])
+ $datarray['author_xchan'] = $contact['xchan_hash'];
+
+
+ $datarray['owner_xchan'] = $contact['xchan_hash'];
+
$r = q("SELECT edited FROM item WHERE mid = '%s' AND uid = %d LIMIT 1",
dbesc($item_id),
@@ -3309,9 +3500,7 @@ function consume_feed($xml,$importer,&$contact,$pass = 0) {
$datarray['parent_mid'] = $item_id;
$datarray['uid'] = $importer['channel_id'];
-//FIXME
- $datarray['owner_xchan'] = $datarray['author_xchan'] = $contact['xchan_hash'];
-
+
if(! link_compare($author['owner_link'],$contact['xchan_url'])) {
logger('consume_feed: Correcting item owner.', LOGGER_DEBUG);
$author['owner_name'] = $contact['name'];
@@ -3319,7 +3508,7 @@ function consume_feed($xml,$importer,&$contact,$pass = 0) {
$author['owner_avatar'] = $contact['thumb'];
}
- logger('consume_feed: author ' . print_r($author,true));
+ logger('consume_feed: author ' . print_r($author,true),LOGGER_DEBUG);
logger('consume_feed: ' . print_r($datarray,true),LOGGER_DATA);
@@ -3337,8 +3526,7 @@ function consume_feed($xml,$importer,&$contact,$pass = 0) {
function update_feed_item($uid,$datarray) {
- logger('update_feed_item: ' . $uid . ' ' . print_r($datarray,true), LOGGER_DATA);
-
+ logger('update_feed_item: not implemented! ' . $uid . ' ' . print_r($datarray,true), LOGGER_DATA);
}
@@ -3663,17 +3851,17 @@ function item_expire($uid,$days) {
$expire_network_only = 1;
- $sql_extra = ((intval($expire_network_only)) ? " AND not (item_flags & " . intval(ITEM_WALL) . ") " : "");
+ $sql_extra = ((intval($expire_network_only)) ? " AND not (item_flags & " . intval(ITEM_WALL) . ")>0 " : "");
$r = q("SELECT * FROM `item`
WHERE `uid` = %d
- AND `created` < UTC_TIMESTAMP() - INTERVAL %d DAY
+ AND `created` < %s - INTERVAL %s
AND `id` = `parent`
$sql_extra
- AND NOT ( item_flags & %d )
+ AND NOT ( item_flags & %d )>0
AND (item_restrict = 0 ) ",
intval($uid),
- intval($days),
+ db_utcnow(), db_quoteinterval(intval($days).' DAY'),
intval(ITEM_RETAINED)
);
@@ -3711,7 +3899,7 @@ function item_expire($uid,$days) {
}
function retain_item($id) {
- $r = q("update item set item_flags = (item_flags | %d ) where id = %d limit 1",
+ $r = q("update item set item_flags = (item_flags | %d ) where id = %d",
intval(ITEM_RETAINED),
intval($id)
);
@@ -3787,7 +3975,7 @@ function drop_item($id,$interactive = true,$stage = DROPITEM_NORMAL) {
// set the deleted flag immediately on this item just in case the
// hook calls a remote process which loops. We'll delete it properly in a second.
- $r = q("UPDATE item SET item_restrict = ( item_restrict | %d ) WHERE id = %d LIMIT 1",
+ $r = q("UPDATE item SET item_restrict = ( item_restrict | %d ) WHERE id = %d",
intval(ITEM_DELETED),
intval($item['id'])
);
@@ -3814,7 +4002,12 @@ function drop_item($id,$interactive = true,$stage = DROPITEM_NORMAL) {
// send the notification upstream/downstream as the case may be
// only send notifications to others if this is the owner's wall item.
- if(($item['item_flags'] & ITEM_WALL) && ($stage != DROPITEM_PHASE2))
+ // This isn't optimal. We somehow need to pass to this function whether or not
+ // to call the notifier, or we need to call the notifier from the calling function.
+ // We'll rely on the undocumented behaviour that DROPITEM_PHASE1 is (hopefully) only
+ // set if we know we're going to send delete notifications out to others.
+
+ if((($item['item_flags'] & ITEM_WALL) && ($stage != DROPITEM_PHASE2)) || ($stage == DROPITEM_PHASE1))
proc_run('php','include/notifier.php','drop',$notify_id);
goaway($a->get_baseurl() . '/' . $_SESSION['return_url']);
@@ -3839,7 +4032,7 @@ function delete_item_lowlevel($item,$stage = DROPITEM_NORMAL) {
switch($stage) {
case DROPITEM_PHASE2:
$r = q("UPDATE item SET item_restrict = ( item_restrict | %d ), body = '', title = '',
- changed = '%s', edited = '%s' WHERE id = %d LIMIT 1",
+ changed = '%s', edited = '%s' WHERE id = %d",
intval(ITEM_PENDING_REMOVE),
dbesc(datetime_convert()),
dbesc(datetime_convert()),
@@ -3849,7 +4042,7 @@ function delete_item_lowlevel($item,$stage = DROPITEM_NORMAL) {
case DROPITEM_PHASE1:
$r = q("UPDATE item SET item_restrict = ( item_restrict | %d ),
- changed = '%s', edited = '%s' WHERE id = %d LIMIT 1",
+ changed = '%s', edited = '%s' WHERE id = %d",
intval(ITEM_DELETED),
dbesc(datetime_convert()),
dbesc(datetime_convert()),
@@ -3860,7 +4053,7 @@ function delete_item_lowlevel($item,$stage = DROPITEM_NORMAL) {
case DROPITEM_NORMAL:
default:
$r = q("UPDATE item SET item_restrict = ( item_restrict | %d ), body = '', title = '',
- changed = '%s', edited = '%s' WHERE id = %d LIMIT 1",
+ changed = '%s', edited = '%s' WHERE id = %d",
intval(ITEM_DELETED),
dbesc(datetime_convert()),
dbesc(datetime_convert()),
@@ -3872,7 +4065,7 @@ function delete_item_lowlevel($item,$stage = DROPITEM_NORMAL) {
// immediately remove any undesired profile likes.
- q("delete from likes where iid = %d and channel_id = %d limit 1",
+ q("delete from likes where iid = %d and channel_id = %d",
intval($item['id']),
intval($item['uid'])
);
@@ -3883,7 +4076,7 @@ function delete_item_lowlevel($item,$stage = DROPITEM_NORMAL) {
if(strlen($item['resource_id'])) {
if($item['resource_type'] === 'event') {
- q("delete from event where event_hash = '%s' and uid = %d limit 1",
+ q("delete from event where event_hash = '%s' and uid = %d",
dbesc($item['resource_id']),
intval($item['uid'])
);
@@ -3903,12 +4096,12 @@ function delete_item_lowlevel($item,$stage = DROPITEM_NORMAL) {
if($stage == DROPITEM_PHASE1)
return true;
- $r = q("delete from term where otype = %d and oid = %d limit 1",
+ $r = q("delete from term where otype = %d and oid = %d",
intval(TERM_OBJ_POST),
intval($item['id'])
);
- q("delete from item_id where iid = %d and uid = %d limit 1",
+ q("delete from item_id where iid = %d and uid = %d",
intval($item['id']),
intval($item['uid'])
);
@@ -3927,7 +4120,7 @@ function delete_item_lowlevel($item,$stage = DROPITEM_NORMAL) {
function first_post_date($uid,$wall = false) {
- $wall_sql = (($wall) ? sprintf(" and item_flags & %d ", ITEM_WALL) : "" );
+ $wall_sql = (($wall) ? sprintf(" and (item_flags & %d)>0 ", ITEM_WALL) : "" );
$r = q("select id, created from item
where item_restrict = %d and uid = %d and id = parent $wall_sql
@@ -3949,10 +4142,13 @@ function first_post_date($uid,$wall = false) {
* current flat list of all representative dates.
*/
-function list_post_dates($uid,$wall) {
+function list_post_dates($uid,$wall,$mindate) {
$dnow = datetime_convert('',date_default_timezone_get(),'now','Y-m-d');
- $dthen = first_post_date($uid,$wall);
+ if($mindate)
+ $dthen = datetime_convert('',date_default_timezone_get(),$mindate);
+ else
+ $dthen = first_post_date($uid,$wall);
if(! $dthen)
return array();
@@ -3962,7 +4158,7 @@ function list_post_dates($uid,$wall) {
if(intval(substr($dnow,8)) > 28)
$dnow = substr($dnow,0,8) . '28';
if(intval(substr($dthen,8)) > 28)
- $dnow = substr($dthen,0,8) . '28';
+ $dthen = substr($dthen,0,8) . '28';
$ret = array();
// Starting with the current month, get the first and last days of every
@@ -3996,7 +4192,7 @@ function posted_dates($uid,$wall) {
if(intval(substr($dnow,8)) > 28)
$dnow = substr($dnow,0,8) . '28';
if(intval(substr($dthen,8)) > 28)
- $dnow = substr($dthen,0,8) . '28';
+ $dthen = substr($dthen,0,8) . '28';
$ret = array();
// Starting with the current month, get the first and last days of every
@@ -4070,17 +4266,29 @@ function fetch_post_tags($items,$link = false) {
-function zot_feed($uid,$observer_xchan,$mindate) {
+function zot_feed($uid,$observer_xchan,$arr) {
$result = array();
- $mindate = datetime_convert('UTC','UTC',$mindate);
- if(! $mindate)
- $mindate = '0000-00-00 00:00:00';
+ $mindate = null;
+ $message_id = null;
+ if(array_key_exists('mindate',$arr)) {
+ $mindate = datetime_convert('UTC','UTC',$arr['mindate']);
+ }
+
+ if(array_key_exists('message_id',$arr)) {
+ $message_id = $arr['message_id'];
+ }
+
+
+ if(! $mindate)
+ $mindate = NULL_DATE;
$mindate = dbesc($mindate);
- logger('zot_feed: ' . $uid);
+ logger('zot_feed: requested for uid ' . $uid . ' from observer ' . $observer_xchan, LOGGER_DEBUG);
+ if($message_id)
+ logger('message_id: ' . $message_id,LOGGER_DEBUG);
if(! perm_is_allowed($uid,$observer_xchan,'view_stream')) {
logger('zot_feed: permission denied.');
@@ -4092,30 +4300,35 @@ function zot_feed($uid,$observer_xchan,$mindate) {
$sql_extra = item_permissions_sql($uid);
}
- if($mindate != '0000-00-00 00:00:00') {
+ if($mindate != NULL_DATE) {
$sql_extra .= " and ( created > '$mindate' or edited > '$mindate' ) ";
$limit = "";
}
else
$limit = " limit 0, 50 ";
+ if($message_id) {
+ $sql_extra .= " and mid = '" . dbesc($message_id) . "' ";
+ $limit = '';
+ }
+
$items = array();
if(is_sys_channel($uid)) {
require_once('include/security.php');
- $r = q("SELECT distinct parent from item
+ $r = q("SELECT distinct parent, created from item
WHERE uid != %d
and uid in (" . stream_perms_api_uids(PERMS_PUBLIC) . ") AND item_restrict = 0
- AND (item_flags & %d)
+ AND (item_flags & %d)>0
and item_private = 0 $sql_extra ORDER BY created ASC $limit",
intval($uid),
intval(ITEM_WALL)
);
}
else {
- $r = q("SELECT distinct parent from item
+ $r = q("SELECT distinct parent, created from item
WHERE uid = %d AND item_restrict = 0
- AND (item_flags & %d)
+ AND (item_flags & %d)>0
$sql_extra ORDER BY created ASC $limit",
intval($uid),
intval(ITEM_WALL)
@@ -4178,12 +4391,12 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C
}
if($arr['star'])
- $sql_options .= " and (item_flags & " . intval(ITEM_STARRED) . ") ";
+ $sql_options .= " and (item_flags & " . intval(ITEM_STARRED) . ")>0 ";
if($arr['wall'])
- $sql_options .= " and (item_flags & " . intval(ITEM_WALL) . ") ";
+ $sql_options .= " and (item_flags & " . intval(ITEM_WALL) . ")>0 ";
- $sql_extra = " AND item.parent IN ( SELECT parent FROM item WHERE (item_flags & " . intval(ITEM_THREAD_TOP) . ") $sql_options ) ";
+ $sql_extra = " AND item.parent IN ( SELECT parent FROM item WHERE (item_flags & " . intval(ITEM_THREAD_TOP) . ")>0 $sql_options ) ";
if($arr['since_id'])
$sql_extra .= " and item.id > " . $since_id . " ";
@@ -4221,7 +4434,7 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C
}
elseif($arr['cid'] && $uid) {
- $r = q("SELECT abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_channel = %d and not ( abook_flags & " . intval(ABOOK_FLAG_BLOCKED) . ") limit 1",
+ $r = q("SELECT abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_channel = %d and not ( abook_flags & " . intval(ABOOK_FLAG_BLOCKED) . ")>0 limit 1",
intval($arr['cid']),
intval(local_user())
);
@@ -4261,7 +4474,7 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C
}
if($arr['conv'] && $channel) {
- $sql_extra .= sprintf(" AND parent IN (SELECT distinct parent from item where ( author_xchan like '%s' or ( item_flags & %d ))) ",
+ $sql_extra .= sprintf(" AND parent IN (SELECT distinct parent from item where ( author_xchan like '%s' or ( item_flags & %d )>0)) ",
dbesc(protect_sprintf($uidhash)),
intval(ITEM_MENTIONSME)
);
@@ -4277,11 +4490,11 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C
else {
$itemspage = (($channel) ? get_pconfig($uid,'system','itemspage') : 20);
$a->set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20));
- $pager_sql = sprintf(" LIMIT %d, %d ",intval(get_app()->pager['start']), intval(get_app()->pager['itemspage']));
+ $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(get_app()->pager['itemspage']), intval(get_app()->pager['start']));
}
if(isset($arr['start']) && isset($arr['records']))
- $pager_sql = sprintf(" LIMIT %d, %d ",intval($arr['start']), intval($arr['records']));
+ $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval($arr['records']), intval($arr['start']));
if(array_key_exists('cmin',$arr) || array_key_exists('cmax',$arr)) {
if(($arr['cmin'] != 0) || ($arr['cmax'] != 99)) {
@@ -4303,7 +4516,7 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C
}
}
- $simple_update = (($client_mode & CLIENT_MODE_UPDATE) ? " and ( item.item_flags & " . intval(ITEM_UNSEEN) . " ) " : '');
+ $simple_update = (($client_mode & CLIENT_MODE_UPDATE) ? " and ( item.item_flags & " . intval(ITEM_UNSEEN) . " )>0 " : '');
if($client_mode & CLIENT_MODE_LOAD)
$simple_update = '';
@@ -4347,7 +4560,7 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C
// Fetch a page full of parent items for this page
- $r = q("SELECT distinct item.id AS item_id FROM item
+ $r = q("SELECT distinct item.id AS item_id, item.$ordering FROM item
left join abook on item.author_xchan = abook.abook_xchan
WHERE $item_uids $item_restrict
AND item.parent = item.id
@@ -4445,7 +4658,7 @@ function update_remote_id($channel,$post_id,$webpage,$pagetitle,$namespace,$remo
dbesc($page_type)
);
if($r) {
- q("update item_id set sid = '%s' where id = %d limit 1",
+ q("update item_id set sid = '%s' where id = %d",
dbesc(($pagetitle) ? $pagetitle : substr($mid,0,16)),
intval($r[0]['id'])
);
@@ -4476,7 +4689,7 @@ function item_add_cid($xchan_hash,$mid,$uid) {
dbesc('<' . $xchan_hash . '>')
);
if(! $r) {
- $r = q("update item set allow_cid = concat(allow_cid,'%s') where mid = '%s' and uid = %d limit 1",
+ $r = q("update item set allow_cid = concat(allow_cid,'%s') where mid = '%s' and uid = %d",
dbesc('<' . $xchan_hash . '>'),
dbesc($mid),
intval($uid)
@@ -4491,7 +4704,7 @@ function item_remove_cid($xchan_hash,$mid,$uid) {
dbesc('<' . $xchan_hash . '>')
);
if($r) {
- $x = q("update item set allow_cid = '%s' where mid = '%s' and uid = %d limit 1",
+ $x = q("update item set allow_cid = '%s' where mid = '%s' and uid = %d",
dbesc(str_replace('<' . $xchan_hash . '>','',$r[0]['allow_cid'])),
dbesc($mid),
intval($uid)
diff --git a/include/js_strings.php b/include/js_strings.php
index cda66a09c..f4c0a631d 100644
--- a/include/js_strings.php
+++ b/include/js_strings.php
@@ -15,6 +15,7 @@ function js_strings() {
'$passhint' => t('Passphrase hint'),
'$permschange' => t('Notice: Permissions have changed but have not yet been submitted.'),
'$closeAll' => t('close all'),
+ '$nothingnew' => t('Nothing new here'),
'$t01' => ((t('timeago.prefixAgo') != 'timeago.prefixAgo') ? t('timeago.prefixAgo') : ''),
'$t02' => ((t('timeago.prefixFromNow') != 'timeago.prefixFromNow') ? t('timeago.prefixFromNow') : ''),
diff --git a/include/language.php b/include/language.php
index 855d94505..9db57dceb 100644
--- a/include/language.php
+++ b/include/language.php
@@ -146,7 +146,7 @@ function load_translation_table($lang, $install = false) {
*
* @param $s string that should get translated
* @param $ctx optional context to appear in po file
- * @return translated string if exsists, otherwise s
+ * @return translated string if exists, otherwise return $s
*
*/
function t($s,$ctx = '') {
diff --git a/include/menu.php b/include/menu.php
index 4b0a11f10..9dc236605 100644
--- a/include/menu.php
+++ b/include/menu.php
@@ -124,7 +124,17 @@ function menu_list($channel_id, $name = '', $flags = 0) {
return $r;
}
+function menu_list_count($channel_id, $name = '', $flags = 0) {
+ $sel_options = '';
+ $sel_options .= (($name) ? " and menu_name = '" . protect_sprintf(dbesc($name)) . "' " : '');
+ $sel_options .= (($flags) ? " and menu_flags = " . intval($flags) . " " : '');
+
+ $r = q("select count(*) as total from menu where menu_channel_id = %d $sel_options",
+ intval($channel_id)
+ );
+ return $r[0]['total'];
+}
function menu_edit($arr) {
@@ -166,7 +176,7 @@ function menu_edit($arr) {
}
return q("update menu set menu_name = '%s', menu_desc = '%s', menu_flags = %d
- where menu_id = %d and menu_channel_id = %d limit 1",
+ where menu_id = %d and menu_channel_id = %d",
dbesc($menu_name),
dbesc($menu_desc),
intval($menu_flags),
@@ -293,7 +303,7 @@ function menu_edit_item($menu_id, $uid, $arr) {
}
- $r = q("update menu_item set mitem_link = '%s', mitem_desc = '%s', mitem_flags = %d, allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', mitem_order = %d where mitem_channel_id = %d and mitem_menu_id = %d and mitem_id = %d limit 1",
+ $r = q("update menu_item set mitem_link = '%s', mitem_desc = '%s', mitem_flags = %d, allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', mitem_order = %d where mitem_channel_id = %d and mitem_menu_id = %d and mitem_id = %d",
dbesc($mitem_link),
dbesc($mitem_desc),
intval($mitem_flags),
@@ -313,7 +323,7 @@ function menu_edit_item($menu_id, $uid, $arr) {
function menu_del_item($menu_id,$uid,$item_id) {
- $r = q("delete from menu_item where mitem_menu_id = %d and mitem_channel_id = %d and mitem_id = %d limit 1",
+ $r = q("delete from menu_item where mitem_menu_id = %d and mitem_channel_id = %d and mitem_id = %d",
intval($menu_id),
intval($uid),
intval($item_id)
diff --git a/include/message.php b/include/message.php
index 607166ec9..49278f273 100644
--- a/include/message.php
+++ b/include/message.php
@@ -23,10 +23,13 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto='
$subject = t('[no subject]');
// if(! $expires)
-// $expires = '0000-00-00 00:00:00';
+// $expires = NULL_DATE;
// else
// $expires = datetime_convert(date_default_timezone_get(),'UTC',$expires);
+
+
+
if($uid) {
$r = q("select * from channel where channel_id = %d limit 1",
intval($uid)
@@ -43,6 +46,59 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto='
return $ret;
}
+
+ // look for any existing conversation structure
+
+ if(strlen($replyto)) {
+ $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)
+ );
+ if($r)
+ $convid = $r[0]['convid'];
+ }
+
+ if(! $convid) {
+
+ // create a new conversation
+
+ $conv_guid = random_string();
+
+ $recip = q("select * from xchan where xchan_hash = '%s' limit 1",
+ dbesc($recipient)
+ );
+ if($recip)
+ $recip_handle = $recip[0]['xchan_addr'];
+
+ $sender_handle = $channel['channel_address'] . '@' . get_app()->get_hostname();
+
+ $handles = $recip_handle . ';' . $sender_handle;
+
+ $r = q("insert into conv (uid,guid,creator,created,updated,subject,recips) values(%d, '%s', '%s', '%s', '%s', '%s', '%s') ",
+ intval(local_user()),
+ dbesc($conv_guid),
+ dbesc($sender_handle),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ dbesc($subject),
+ dbesc($handles)
+ );
+
+ $r = q("select * from conv where guid = '%s' and uid = %d limit 1",
+ dbesc($conv_guid),
+ intval(local_user())
+ );
+ if($r)
+ $convid = $r[0]['id'];
+ }
+
+ if(! $convid) {
+ $ret['message'] = 'conversation not found';
+ return $ret;
+ }
+
+
// generate a unique message_id
do {
@@ -115,9 +171,10 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto='
- $r = q("INSERT INTO mail ( account_id, mail_flags, channel_id, from_xchan, to_xchan, title, body, attach, mid, parent_mid, created, expires )
- VALUES ( %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )",
+ $r = q("INSERT INTO mail ( account_id, convid, mail_flags, channel_id, from_xchan, to_xchan, title, body, attach, mid, parent_mid, created, expires )
+ VALUES ( %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )",
intval($channel['channel_account_id']),
+ intval($convid),
intval(MAIL_OBSCURED),
intval($channel['channel_id']),
dbesc($channel['channel_hash']),
@@ -186,7 +243,7 @@ function private_messages_list($uid, $mailbox = '', $start = 0, $numitems = 0) {
$limit = '';
if($numitems)
- $limit = " LIMIT " . intval($start) . ", " . intval($numitems);
+ $limit = " LIMIT " . intval($numitems) . " OFFSET " . intval($start);
if($mailbox !== '') {
$x = q("select channel_hash from channel where channel_id = %d limit 1",
@@ -227,9 +284,7 @@ function private_messages_list($uid, $mailbox = '', $start = 0, $numitems = 0) {
$r[$k]['to'] = find_xchan_in_array($rr['to_xchan'],$c);
$r[$k]['seen'] = (($rr['mail_flags'] & MAIL_SEEN) ? 1 : 0);
if($r[$k]['mail_flags'] & MAIL_OBSCURED) {
- logger('unencrypting');
$key = get_config('system','prvkey');
-
if($r[$k]['title'])
$r[$k]['title'] = crypto_unencapsulate(json_decode_plus($r[$k]['title']),$key);
if($r[$k]['body'])
@@ -277,7 +332,7 @@ function private_messages_fetch_message($channel_id, $messageitem_id, $updatesee
}
if($updateseen) {
- $r = q("UPDATE `mail` SET mail_flags = (mail_flags ^ %d) where not (mail_flags & %d) and id = %d AND channel_id = %d",
+ $r = q("UPDATE `mail` SET mail_flags = (mail_flags | %d) where not (mail_flags & %d)>0 and id = %d AND channel_id = %d",
intval(MAIL_SEEN),
intval(MAIL_SEEN),
dbesc($messageitem_id),
@@ -308,7 +363,7 @@ function private_messages_drop($channel_id, $messageitem_id, $drop_conversation
}
}
else {
- $r = q("DELETE FROM mail WHERE id = %d AND channel_id = %d LIMIT 1",
+ $r = q("DELETE FROM mail WHERE id = %d AND channel_id = %d",
intval($messageitem_id),
intval($channel_id)
);
@@ -366,7 +421,7 @@ function private_messages_fetch_conversation($channel_id, $messageitem_id, $upda
if($updateseen) {
- $r = q("UPDATE `mail` SET mail_flags = (mail_flags ^ %d) where not (mail_flags & %d) and parent_mid = '%s' AND channel_id = %d",
+ $r = q("UPDATE `mail` SET mail_flags = (mail_flags | %d) where not (mail_flags & %d)>0 and parent_mid = '%s' AND channel_id = %d",
intval(MAIL_SEEN),
intval(MAIL_SEEN),
dbesc($r[0]['parent_mid']),
diff --git a/include/nav.php b/include/nav.php
index 799faf5ce..a9bff4b29 100644
--- a/include/nav.php
+++ b/include/nav.php
@@ -38,7 +38,7 @@ EOT;
intval($channel['channel_id'])
);
- $chans = q("select channel_name, channel_id from channel where channel_account_id = %d and not ( channel_pageflags & %d ) order by channel_name ",
+ $chans = q("select channel_name, channel_id from channel where channel_account_id = %d and not ( channel_pageflags & %d )>0 order by channel_name ",
intval(get_account_id()),
intval(PAGE_REMOVED)
);
@@ -84,6 +84,7 @@ EOT;
$nav['usermenu']=array();
$userinfo = null;
+ $nav['loginmenu']=array();
if(local_user()) {
@@ -91,54 +92,57 @@ EOT;
if($chans && count($chans) > 1 && feature_enabled(local_user(),'nav_channel_select'))
$nav['channels'] = $chans;
- $nav['logout'] = Array('logout',t('Logout'), "", t('End this session'));
+ $nav['logout'] = Array('logout',t('Logout'), "", t('End this session'),'logout_nav_btn');
// user menu
- $nav['usermenu'][] = Array('channel/' . $channel['channel_address'], t('Home'), "", t('Your posts and conversations'));
- $nav['usermenu'][] = Array('profile/' . $channel['channel_address'], t('View Profile'), "", t('Your profile page'));
+ $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_user(),'multi_profiles'))
- $nav['usermenu'][] = Array('profiles', t('Edit Profiles'),"", t('Manage/Edit profiles'));
+ $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'));
+ $nav['usermenu'][] = Array('profiles/' . $prof[0]['id'], t('Edit Profile'),"", t('Edit your profile'),'profiles_nav_btn');
- $nav['usermenu'][] = Array('photos/' . $channel['channel_address'], t('Photos'), "", t('Your photos'));
- $nav['usermenu'][] = Array('cloud/' . $channel['channel_address'],t('Files'),"",t('Your files'));
+ $nav['usermenu'][] = Array('photos/' . $channel['channel_address'], t('Photos'), "", t('Your photos'),'photos_nav_btn');
+ $nav['usermenu'][] = Array('cloud/' . $channel['channel_address'],t('Files'),"",t('Your files'),'cloud_nav_btn');
require_once('include/chat.php');
- $chats = chatroom_list(local_user());
- if (count($chats)) {
- $nav['usermenu'][] = Array('chat/' . $channel['channel_address'],t('Chat'),"",t('Your chatrooms'));
+ $has_chats = chatroom_list_count(local_user());
+ if($has_chats) {
+ $nav['usermenu'][] = Array('chat/' . $channel['channel_address'],t('Chat'),"",t('Your chatrooms'),'chat_nav_btn');
+ }
+
+ require_once('include/menu.php');
+ $has_bookmarks = menu_list_count(local_user(),'',MENU_BOOKMARK) + menu_list_count(local_user(),'',MENU_SYSTEM|MENU_BOOKMARK);
+ if($has_bookmarks) {
+ $nav['usermenu'][] = Array('bookmarks', t('Bookmarks'), "", t('Your bookmarks'),'bookmarks_nav_btn');
}
- $nav['usermenu'][] = Array('bookmarks', t('Bookmarks'), "", t('Your bookmarks'));
if(feature_enabled($channel['channel_id'],'webpages'))
- $nav['usermenu'][] = Array('webpages/' . $channel['channel_address'],t('Webpages'),"",t('Your webpages'));
+ $nav['usermenu'][] = Array('webpages/' . $channel['channel_address'],t('Webpages'),"",t('Your webpages'),'webpages_nav_btn');
}
else {
- if(! get_account_id())
- $nav['login'] = Array('login',t('Login'), ($a->module == 'login'?'selected':''), t('Sign in'));
+ if(! get_account_id()) {
+ $nav['loginmenu'][] = Array('login',t('Login'),'',t('Sign in'),'login_nav_btn');
+ }
else
- $nav['alogout'] = Array('logout',t('Logout'), "", t('End this session'));
+ $nav['alogout'] = Array('logout',t('Logout'), "", t('End this session'),'logout_nav_btn');
}
if($observer) {
$userinfo = array(
- 'icon' => $observer['xchan_photo_m'],
+ 'icon' => $observer['xchan_photo_s'],
'name' => $observer['xchan_addr'],
);
}
if($observer) {
- $nav['locked'] = true;
$nav['lock'] = array('logout','','lock',
sprintf( t('%s - click to logout'), $observer['xchan_addr']));
}
else {
- $nav['locked'] = false;
- $nav['lock'] = array('rmagic','','unlock',
- t('Click to authenticate to your home hub'));
+ $nav['loginmenu'][] = Array('rmagic',t('Remote authentication'),'',t('Click to authenticate to your home hub'),'rmagic_nav_btn');
}
/**
@@ -152,24 +156,24 @@ EOT;
}
if(($a->module != 'home') && (! (local_user())))
- $nav['home'] = array($homelink, t('Home'), "", t('Home Page'));
+ $nav['home'] = array($homelink, t('Home'), "", t('Home Page'),'home_nav_btn');
if(($a->config['system']['register_policy'] == REGISTER_OPEN) && (! local_user()) && (! remote_user()))
- $nav['register'] = array('register',t('Register'), "", t('Create an account'));
+ $nav['register'] = array('register',t('Register'), "", t('Create an account'),'register_nav_btn');
$help_url = z_root() . '/help?f=&cmd=' . $a->cmd;
if(! get_config('system','hide_help'))
- $nav['help'] = array($help_url, t('Help'), "", t('Help and documentation'));
+ $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'));
+ $nav['apps'] = array('apps', t('Apps'), "", t('Applications, utilities, links, games'),'apps_nav_btn');
$nav['search'] = array('search', t('Search'), "", t('Search site content'));
- $nav['directory'] = array('directory', t('Directory'), "", t('Channel Locator'));
+ $nav['directory'] = array('directory', t('Directory'), "", t('Channel Directory'),'directory_nav_btn');
/**
@@ -180,21 +184,24 @@ 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'),'network_nav_btn');
$nav['network']['mark'] = array('', t('Mark all matrix notifications seen'), '','');
- $nav['home'] = array('channel/' . $channel['channel_address'], t('Channel Home'), "", t('Channel home'));
+ $nav['home'] = array('channel/' . $channel['channel_address'], t('Channel Home'), "", t('Channel home'),'home_nav_btn');
$nav['home']['mark'] = array('', t('Mark all channel notifications seen'), '','');
- $nav['intros'] = array('connections/ifpending', t('Connections'), "", t('Connections'));
+ $nav['intros'] = array('connections/ifpending', t('Connections'), "", t('Connections'),'connections_nav_btn');
- $nav['notifications'] = array('notifications/system', t('Notices'), "", t('Notifications'));
+ $nav['notifications'] = array('notifications/system', t('Notices'), "", t('Notifications'),'notifications_nav_btn');
$nav['notifications']['all']=array('notifications/system', t('See all notifications'), "", "");
$nav['notifications']['mark'] = array('', t('Mark all system notifications seen'), '','');
- $nav['messages'] = array('message', t('Mail'), "", t('Private mail'));
+ $nav['messages'] = array('message', t('Mail'), "", t('Private mail'),'mail_nav_btn');
$nav['messages']['all']=array('message', t('See all private messages'), "", "");
$nav['messages']['mark'] = array('', t('Mark all private messages seen'), '','');
$nav['messages']['inbox'] = array('message', t('Inbox'), "", t('Inbox'));
@@ -202,13 +209,13 @@ EOT;
$nav['messages']['new'] = array('mail/new', t('New Message'), "", t('New Message'));
- $nav['all_events'] = array('events', t('Events'), "", t('Event Calendar'));
+ $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'));
+ $nav['manage'] = array('manage', t('Channel Manager'), "", t('Manage Your Channels'),'manage_nav_btn');
- $nav['settings'] = array('settings', t('Settings'),"", t('Account/Channel Settings'));
+ $nav['settings'] = array('settings', t('Settings'),"", t('Account/Channel Settings'),'settings_nav_btn');
}
@@ -216,7 +223,7 @@ EOT;
* Admin page
*/
if (is_site_admin()){
- $nav['admin'] = array('admin/', t('Admin'), "", t('Site Setup and Configuration'));
+ $nav['admin'] = array('admin/', t('Admin'), "", t('Site Setup and Configuration'),'admin_nav_btn');
}
@@ -234,6 +241,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(
@@ -241,10 +254,12 @@ EOT;
'$sitelocation' => $sitelocation,
'$nav' => $x['nav'],
'$banner' => $banner,
- '$emptynotifications' => t('Nothing new here'),
+ '$emptynotifications' => t('Loading...'),
'$userinfo' => $x['usermenu'],
'$localuser' => local_user(),
'$sel' => $a->nav_sel,
+ '$powered_by' => $powered_by,
+ '$help' => t('@name, #tag, content'),
'$pleasewait' => t('Please wait...')
));
diff --git a/include/network.php b/include/network.php
index 5ad391491..98c411cd8 100644
--- a/include/network.php
+++ b/include/network.php
@@ -38,6 +38,7 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) {
return false;
@curl_setopt($ch, CURLOPT_HEADER, true);
+ @curl_setopt($ch, CURLINFO_HEADER_OUT, true);
@curl_setopt($ch, CURLOPT_CAINFO, get_capath());
@curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
@curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
@@ -47,11 +48,8 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) {
if($ciphers)
@curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, $ciphers);
- if (x($opts,'accept_content')){
- @curl_setopt($ch,CURLOPT_HTTPHEADER, array (
- "Accept: " . $opts['accept_content']
- ));
- }
+ if(x($opts,'headers'))
+ @curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['headers']);
if(x($opts,'timeout') && intval($opts['timeout'])) {
@curl_setopt($ch, CURLOPT_TIMEOUT, $opts['timeout']);
@@ -126,12 +124,39 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) {
}
$ret['body'] = substr($s,strlen($header));
$ret['header'] = $header;
+
+ if(x($opts,'debug')) {
+ $ret['debug'] = $curl_info;
+ }
@curl_close($ch);
return($ret);
}
+/**
+ * @function z_post_url
+ * @param string $url
+ * URL to post
+ * @param mixed $params
+ * The full data to post in a HTTP "POST" operation. This parameter can
+ * either be passed as a urlencoded string like 'para1=val1&para2=val2&...'
+ * or as an array with the field name as key and field data as value. If value
+ * is an array, the Content-Type header will be set to multipart/form-data.
+ * @param int $redirects = 0
+ * internal use, recursion counter
+ * @param array $opts (optional parameters)
+ * 'accept_content' => supply Accept: header with 'accept_content' as the value
+ * 'timeout' => int seconds, default system config value or 60 seconds
+ * 'http_auth' => username:password
+ * 'novalidate' => do not validate SSL certs, default is to validate using our CA list
+ *
+ * @returns array
+ * 'return_code' => HTTP return code or 0 if timeout or failure
+ * 'success' => boolean true (if HTTP 2xx result) or false
+ * 'header' => HTTP headers
+ * 'body' => fetched content
+ */
function z_post_url($url,$params, $redirects = 0, $opts = array()) {
@@ -143,6 +168,7 @@ function z_post_url($url,$params, $redirects = 0, $opts = array()) {
return ret;
@curl_setopt($ch, CURLOPT_HEADER, true);
+ @curl_setopt($ch, CURLINFO_HEADER_OUT, true);
@curl_setopt($ch, CURLOPT_CAINFO, get_capath());
@curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
@curl_setopt($ch, CURLOPT_POST,1);
@@ -153,12 +179,6 @@ function z_post_url($url,$params, $redirects = 0, $opts = array()) {
if($ciphers)
@curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, $ciphers);
-
- if (x($opts,'accept_content')){
- @curl_setopt($ch,CURLOPT_HTTPHEADER, array (
- "Accept: " . $opts['accept_content']
- ));
- }
if(x($opts,'headers'))
@curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['headers']);
@@ -235,11 +255,24 @@ function z_post_url($url,$params, $redirects = 0, $opts = array()) {
$ret['body'] = substr($s,strlen($header));
$ret['header'] = $header;
+
+ if(x($opts,'debug')) {
+ $ret['debug'] = $curl_info;
+ }
+
+
curl_close($ch);
return($ret);
}
+function z_post_url_json($url,$params,$redirects = 0, $opts = array()) {
+
+ $opts = array_merge($opts,array('headers' => array('Content-Type: application/json')));
+ return z_post_url($url,json_encode($params),$redirects,$opts);
+
+}
+
function json_return_and_die($x) {
header("content-type: application/json");
@@ -369,7 +402,7 @@ function validate_email($addr) {
return false;
$h = substr($addr,strpos($addr,'@') + 1);
- if(($h) && (dns_get_record($h, DNS_A + DNS_CNAME + DNS_PTR + DNS_MX) || filter_var($h['host'], FILTER_VALIDATE_IP) )) {
+ if(($h) && (dns_get_record($h, DNS_A + DNS_CNAME + DNS_PTR + DNS_MX) || filter_var($h, FILTER_VALIDATE_IP) )) {
return true;
}
return false;
@@ -958,7 +991,7 @@ function discover_by_url($url,$arr = null) {
);
$photos = import_profile_photo($photo,$guid);
- $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' limit 1",
+ $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'",
dbesc(datetime_convert()),
dbesc($photos[0]),
dbesc($photos[1]),
@@ -1059,25 +1092,27 @@ function discover_by_webbie($webbie) {
$r = q("select * from xchan where xchan_hash = '%s' limit 1",
dbesc($webbie)
);
- if($r)
- return true;
-
- $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') ",
- dbesc($addr),
- dbesc($guid),
- dbesc($pubkey),
- dbesc($addr),
- dbesc($profile),
- dbesc($vcard['fn']),
- dbesc($network),
- dbesc(z_root()),
- dbesc(datetime_convert())
- );
+ if(! $r) {
+
+ $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') ",
+ dbesc($addr),
+ dbesc($guid),
+ dbesc($pubkey),
+ dbesc($addr),
+ dbesc($profile),
+ dbesc($vcard['fn']),
+ dbesc($network),
+ dbesc(z_root()),
+ dbescdate(datetime_convert())
+ );
+ }
$r = q("select * from hubloc where hubloc_hash = '%s' limit 1",
dbesc($webbie)
);
+
if(! $r) {
+
$r = q("insert into hubloc ( hubloc_guid, hubloc_hash, hubloc_addr, hubloc_network, hubloc_url, hubloc_host, hubloc_callback, hubloc_updated, hubloc_flags ) values ('%s','%s','%s','%s','%s','%s','%s','%s', %d)",
dbesc($guid),
dbesc($addr),
@@ -1086,13 +1121,13 @@ function discover_by_webbie($webbie) {
dbesc(trim($diaspora_base,'/')),
dbesc($hostname),
dbesc($notify),
- dbesc(datetime_convert()),
+ dbescdate(datetime_convert()),
intval(HUBLOC_FLAGS_PRIMARY)
);
}
$photos = import_profile_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' limit 1",
- dbesc(datetime_convert('UTC','UTC',$arr['photo_updated'])),
+ $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'])),
dbesc($photos[0]),
dbesc($photos[1]),
dbesc($photos[2]),
diff --git a/include/notifier.php b/include/notifier.php
index 88bb9a0cb..e1eb0c554 100644
--- a/include/notifier.php
+++ b/include/notifier.php
@@ -57,6 +57,8 @@ require_once('include/html2plain.php');
* purge_all channel_id
* expire channel_id
* relay item_id (item was relayed to owner, we will deliver it as owner)
+ * location channel_id
+ * request channel_id xchan_hash message_id
*
*/
@@ -97,7 +99,7 @@ function notifier_run($argv, $argc){
// Get the recipient
$r = q("select abook.*, hubloc.* from abook
left join hubloc on hubloc_hash = abook_xchan
- where abook_id = %d and not ( abook_flags & %d ) limit 1",
+ where abook_id = %d and not ( abook_flags & %d )>0 limit 1",
intval($item_id),
intval(ABOOK_FLAG_SELF)
);
@@ -141,9 +143,11 @@ function notifier_run($argv, $argc){
$expire = false;
+ $request = false;
$mail = false;
$fsuggest = false;
$top_level = false;
+ $location = false;
$recipients = array();
$url_recipients = array();
$normal_mode = true;
@@ -156,7 +160,7 @@ function notifier_run($argv, $argc){
$message = q("SELECT * FROM `mail` WHERE `id` = %d LIMIT 1",
intval($item_id)
);
- if(! count($message)){
+ if(! $message) {
return;
}
xchan_mail_query($message[0]);
@@ -173,6 +177,22 @@ function notifier_run($argv, $argc){
$channel = $s[0];
}
+ elseif($cmd === 'request') {
+ $channel_id = $item_id;
+ $xchan = $argv[3];
+ $request_message_id = $argv[4];
+
+ $s = q("select * from channel where channel_id = %d limit 1",
+ intval($channel_id)
+ );
+ if($s)
+ $channel = $s[0];
+
+ $private = true;
+ $recipients[] = $xchan;
+ $packet_type = 'request';
+ $normal_mode = false;
+ }
elseif($cmd === 'expire') {
// FIXME
@@ -185,11 +205,12 @@ function notifier_run($argv, $argc){
$normal_mode = false;
$expire = true;
- $items = q("SELECT * FROM item WHERE uid = %d AND ( item_flags & %d )
- AND ( item_restrict & %d ) AND `changed` > UTC_TIMESTAMP() - INTERVAL 10 MINUTE",
+ $items = q("SELECT * FROM item WHERE uid = %d AND ( item_flags & %d )>0
+ AND ( item_restrict & %d )>0 AND `changed` > %s - INTERVAL %s",
intval($item_id),
intval(ITEM_WALL),
- intval(ITEM_DELETED)
+ intval(ITEM_DELETED),
+ db_utcnow(), db_quoteinterval('10 MINUTE')
);
$uid = $item_id;
$item_id = 0;
@@ -230,6 +251,30 @@ function notifier_run($argv, $argc){
$private = false;
$packet_type = 'refresh';
}
+ elseif($cmd === 'location') {
+ logger('notifier: location: ' . $item_id);
+ $s = q("select * from channel where channel_id = %d limit 1",
+ intval($item_id)
+ );
+ if($s)
+ $channel = $s[0];
+ $uid = $item_id;
+ $recipients = array();
+ $r = q("select abook_xchan from abook where abook_channel = %d",
+ intval($item_id)
+ );
+ if($r) {
+ foreach($r as $rr) {
+ $recipients[] = $rr['abook_xchan'];
+ }
+ }
+
+ $encoded_item = array('locations' => zot_encode_locations($channel),'type' => 'location', 'encoding' => 'zot');
+ $target_item = array('aid' => $channel['channel_account_id'],'uid' => $channel['channel_id']);
+ $private = false;
+ $packet_type = 'location';
+ $location = true;
+ }
elseif($cmd === 'purge_all') {
logger('notifier: purge_all: ' . $item_id);
$s = q("select * from channel where channel_id = %d limit 1",
@@ -429,8 +474,10 @@ function notifier_run($argv, $argc){
// for public posts always include our own hub
+// this shouldn't be needed any more. collect_recipients should take care of it.
+// $sql_extra = (($private) ? "" : " or hubloc_url = '" . dbesc(z_root()) . "' ");
- $sql_extra = (($private) ? "" : " or hubloc_url = '" . dbesc(z_root()) . "' ");
+ logger('notifier: hub choice: ' . intval($relay_to_owner) . ' ' . intval($private) . ' ' . $cmd, LOGGER_DEBUG);
if($relay_to_owner && (! $private) && ($cmd !== 'relay')) {
@@ -446,28 +493,51 @@ function notifier_run($argv, $argc){
$r = q("select hubloc_guid, hubloc_url, hubloc_sitekey, hubloc_network, hubloc_flags, hubloc_callback, hubloc_host from hubloc
- where hubloc_hash in (" . implode(',',$recipients) . ") group by hubloc_sitekey order by hubloc_connected desc limit 1");
+ where hubloc_hash in (" . implode(',',$recipients) . ") order by hubloc_connected desc limit 1");
}
else {
$r = q("select hubloc_guid, hubloc_url, hubloc_sitekey, hubloc_network, hubloc_flags, hubloc_callback, hubloc_host from hubloc
- where hubloc_hash in (" . implode(',',$recipients) . ") $sql_extra group by hubloc_sitekey");
- }
+ where hubloc_hash in (" . implode(',',$recipients) . ") and not (hubloc_flags & %d) > 0 and not (hubloc_status & %d) > 0",
+ intval(HUBLOC_FLAGS_DELETED),
+ intval(HUBLOC_OFFLINE)
+ );
+ }
if(! $r) {
logger('notifier: no hubs');
return;
}
+
$hubs = $r;
- $hublist = array();
- $keys = array();
+
+ /**
+ * Reduce the hubs to those that are unique. For zot hubs, we need to verify uniqueness by the sitekey, since it may have been
+ * a re-install which has not yet been detected and pruned.
+ * For other networks which don't have or require sitekeys, we'll have to use the URL
+ */
+
+
+ $hublist = array(); // this provides an easily printable list for the logs
+ $dhubs = array(); // delivery hubs where we store our resulting unique array
+ $keys = array(); // array of keys to check uniquness for zot hubs
+ $urls = array(); // array of urls to check uniqueness of hubs from other networks
+
foreach($hubs as $hub) {
- // don't try to deliver to deleted hublocs - and inexplicably SQL "distinct" and "group by"
- // both return records with duplicate keys in rare circumstances
- if((! ($hub['hubloc_flags'] & HUBLOC_FLAGS_DELETED)) && (! in_array($hub['hubloc_sitekey'],$keys))) {
- $hublist[] = $hub['hubloc_host'];
- $keys[] = $hub['hubloc_sitekey'];
+ if($hub['hubloc_network'] == 'zot') {
+ if(! in_array($hub['hubloc_sitekey'],$keys)) {
+ $hublist[] = $hub['hubloc_host'];
+ $dhubs[] = $hub;
+ $keys[] = $hub['hubloc_sitekey'];
+ }
+ }
+ else {
+ if(! in_array($hub['hubloc_url'],$urls)) {
+ $hublist[] = $hub['hubloc_host'];
+ $dhubs[] = $hub;
+ $urls[] = $hub['hubloc_url'];
+ }
}
}
@@ -483,7 +553,7 @@ function notifier_run($argv, $argc){
$deliver = array();
- foreach($hubs as $hub) {
+ foreach($dhubs as $hub) {
if(defined('DIASPORA_RELIABILITY_EMULATION')) {
$cointoss = mt_rand(0,2);
@@ -514,7 +584,9 @@ function notifier_run($argv, $argc){
'cmd' => $cmd,
'expire' => $expire,
'mail' => $mail,
+ 'location' => $location,
'fsuggest' => $fsuggest,
+ 'request' => $request,
'normal_mode' => $normal_mode,
'packet_type' => $packet_type,
'walltowall' => $walltowall
@@ -544,6 +616,21 @@ function notifier_run($argv, $argc){
dbesc('')
);
}
+ elseif($packet_type === 'request') {
+ $n = zot_build_packet($channel,'request',$env_recips,$hub['hubloc_sitekey'],$hash,array('message_id' => $request_message_id));
+ q("insert into outq ( outq_hash, outq_account, outq_channel, outq_driver, outq_posturl, outq_async, outq_created, outq_updated, outq_notify, outq_msg ) values ( '%s', %d, %d, '%s', '%s', %d, '%s', '%s', '%s', '%s' )",
+ dbesc($hash),
+ intval($channel['channel_account_id']),
+ intval($channel['channel_id']),
+ dbesc('zot'),
+ dbesc($hub['hubloc_callback']),
+ intval(1),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ dbesc($n),
+ dbesc('')
+ );
+ }
else {
$n = zot_build_packet($channel,'notify',$env_recips,(($private) ? $hub['hubloc_sitekey'] : null),$hash);
q("insert into outq ( outq_hash, outq_account, outq_channel, outq_driver, outq_posturl, outq_async, outq_created, outq_updated, outq_notify, outq_msg ) values ( '%s', %d, %d, '%s', '%s', %d, '%s', '%s', '%s', '%s' )",
diff --git a/include/oembed.php b/include/oembed.php
index de3a6edc8..e08b287d1 100755
--- a/include/oembed.php
+++ b/include/oembed.php
@@ -56,6 +56,15 @@ function oembed_fetch_url($embedurl){
$txt = $x['body'];
break;
}
+ // soundcloud is now using text/json+oembed instead of application/json+oembed,
+ // others may be also
+ $entries = $xpath->query("//link[@type='text/json+oembed']");
+ foreach($entries as $e){
+ $href = $e->getAttributeNode("href")->nodeValue;
+ $x = z_fetch_url($href . '&maxwidth=' . $a->videowidth);
+ $txt = $x['body'];
+ break;
+ }
}
}
}
@@ -83,7 +92,8 @@ function oembed_fetch_url($embedurl){
function oembed_format_object($j){
$a = get_app();
$embedurl = $j->embedurl;
- $jhtml = oembed_iframe($j->embedurl,(isset($j->width) ? $j->width : null), (isset($j->height) ? $j->height : null) );
+
+ $jhtml = oembed_iframe($j->embedurl,(isset($j->width) ? $j->width : null), (isset($j->height) ? $j->height : null));
$ret="<span class='oembed ".$j->type."'>";
switch ($j->type) {
@@ -118,7 +128,6 @@ function oembed_format_object($j){
}; break;
case "photo": {
$ret.= "<img width='".$j->width."' src='".$j->url."'>";
- //$ret.= "<img width='".$j->width."' height='".$j->height."' src='".$j->url."'>";
$ret.="<br>";
}; break;
case "link": {
@@ -154,13 +163,12 @@ function oembed_iframe($src,$width,$height) {
$height = intval($height) + 80;
$width = intval($width) + 40;
- $a = get_app();
-
- $sandbox = ((strpos($src,get_app()->get_hostname())) ? ' sandbox="allow-scripts" ' : '');
+ $s = z_root() . '/oembed/' . base64url_encode($src);
- $s = $a->get_baseurl()."/oembed/".base64url_encode($src);
+ // Make sure any children are sandboxed within their own iframe.
- return '<iframe ' . $sandbox . ' height="' . $height . '" width="' . $width . '" src="' . $s . '" frameborder="no" >' . t('Embedded content') . '</iframe>';
+ return '<iframe height="' . $height . '" width="' . $width . '" src="' . $s . '" frameborder="no" >'
+ . t('Embedded content') . '</iframe>';
}
diff --git a/include/onedirsync.php b/include/onedirsync.php
index 8ae1df5e5..a1450e183 100644
--- a/include/onedirsync.php
+++ b/include/onedirsync.php
@@ -35,13 +35,13 @@ function onedirsync_run($argv, $argc){
// (where we received this update from) ?
// If we have, we don't need to do anything except mark any older entries updated
- $x = q("select * from updates where ud_addr = '%s' and ud_date > '%s' and ( ud_flags & %d ) order by ud_date desc limit 1",
+ $x = q("select * from updates where ud_addr = '%s' and ud_date > '%s' and ( ud_flags & %d )>0 order by ud_date desc limit 1",
dbesc($r[0]['ud_addr']),
dbesc($r[0]['ud_date']),
intval(UPDATE_FLAGS_UPDATED)
);
if($x) {
- $y = q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not ( ud_flags & %d ) and ud_date < '%s' ",
+ $y = q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 and ud_date < '%s' ",
intval(UPDATE_FLAGS_UPDATED),
dbesc($r[0]['ud_addr']),
intval(UPDATE_FLAGS_UPDATED),
@@ -50,6 +50,28 @@ function onedirsync_run($argv, $argc){
return;
}
+ // ignore doing an update if this ud_addr refers to a known dead hubloc
+
+ $h = q("select * from hubloc where hubloc_addr = '%s' limit 1",
+ dbesc($r[0]['ud_addr'])
+ );
+ if(($h) && ($h[0]['hubloc_status'] & HUBLOC_OFFLINE)) {
+ $y = q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 and ud_date < '%s' ",
+ intval(UPDATE_FLAGS_UPDATED),
+ dbesc($r[0]['ud_addr']),
+ intval(UPDATE_FLAGS_UPDATED),
+ dbesc($x[0]['ud_date'])
+ );
+
+ return;
+ }
+
+ // we might have to pull this out some day, but for now update_directory_entry()
+ // runs zot_finger() and is kind of zot specific
+
+ if($h && $h[0]['hubloc_network'] !== 'zot')
+ return;
+
update_directory_entry($r[0]);
return;
diff --git a/include/onepoll.php b/include/onepoll.php
index d64785f92..095edd095 100644
--- a/include/onepoll.php
+++ b/include/onepoll.php
@@ -15,12 +15,6 @@ function onepoll_run($argv, $argc){
logger('onepoll: start');
- $manual_id = 0;
- $generation = 0;
-
- $force = false;
- $restart = false;
-
if(($argc > 1) && (intval($argv[1])))
$contact_id = intval($argv[1]);
@@ -28,14 +22,14 @@ function onepoll_run($argv, $argc){
logger('onepoll: no contact');
return;
}
-
+
$d = datetime_convert();
$contacts = q("SELECT abook.*, xchan.*, account.*
FROM abook LEFT JOIN account on abook_account = account_id left join xchan on xchan_hash = abook_xchan
where abook_id = %d
- AND (( abook_flags & %d ) OR ( abook_flags = %d ))
- AND NOT ( abook_flags & %d )
+ AND (( abook_flags & %d )>0 OR ( abook_flags = %d ))
+ AND NOT ( abook_flags & %d )>0
AND (( account_flags = %d ) OR ( account_flags = %d )) limit 1",
intval($contact_id),
intval(ABOOK_FLAG_HIDDEN|ABOOK_FLAG_PENDING|ABOOK_FLAG_UNCONNECTED|ABOOK_FLAG_FEED),
@@ -67,7 +61,7 @@ function onepoll_run($argv, $argc){
logger("onepoll: poll: ({$contact['id']}) IMPORTER: {$importer['xchan_name']}, CONTACT: {$contact['xchan_name']}");
- $last_update = ((($contact['abook_updated'] === $contact['abook_created']) || ($contact['abook_updated'] === '0000-00-00 00:00:00'))
+ $last_update = ((($contact['abook_updated'] === $contact['abook_created']) || ($contact['abook_updated'] === NULL_DATE))
? datetime_convert('UTC','UTC','now - 7 days')
: datetime_convert('UTC','UTC',$contact['abook_updated'] . ' - 2 days')
);
@@ -75,7 +69,7 @@ function onepoll_run($argv, $argc){
if($contact['xchan_network'] === 'rss') {
logger('onepoll: processing feed ' . $contact['xchan_name'], LOGGER_DEBUG);
handle_feed($importer['channel_id'],$contact_id,$contact['xchan_hash']);
- q("update abook set abook_connected = '%s' where abook_id = %d limit 1",
+ q("update abook set abook_connected = '%s' where abook_id = %d",
dbesc(datetime_convert()),
intval($contact['abook_id'])
);
@@ -90,18 +84,19 @@ function onepoll_run($argv, $argc){
$x = zot_refresh($contact,$importer);
$responded = false;
- $updated = datetime_convert();
+ $updated = datetime_convert();
+ $connected = datetime_convert();
if(! $x) {
// mark for death by not updating abook_connected, this is caught in include/poller.php
- q("update abook set abook_updated = '%s' where abook_id = %d limit 1",
+ q("update abook set abook_updated = '%s' where abook_id = %d",
dbesc($updated),
intval($contact['abook_id'])
);
}
else {
- q("update abook set abook_updated = '%s', abook_connected = '%s' where abook_id = %d limit 1",
- dbesc($updated),
+ q("update abook set abook_updated = '%s', abook_connected = '%s' where abook_id = %d",
dbesc($updated),
+ dbesc($connected),
intval($contact['abook_id'])
);
$responded = true;
@@ -120,7 +115,9 @@ function onepoll_run($argv, $argc){
if($fetch_feed) {
$feedurl = str_replace('/poco/','/zotfeed/',$contact['xchan_connurl']);
- $x = z_fetch_url($feedurl . '?f=&mindate=' . urlencode($last_update));
+ $feedurl .= '?f=&mindate=' . urlencode($last_update);
+
+ $x = z_fetch_url($feedurl);
logger('feed_update: ' . print_r($x,true), LOGGER_DATA);
@@ -135,7 +132,7 @@ function onepoll_run($argv, $argc){
foreach($j['messages'] as $message) {
$results = process_delivery(array('hash' => $contact['xchan_hash']), get_item_elements($message),
array(array('hash' => $importer['xchan_hash'])), false);
- logger('onepoll: feed_update: process_delivery: ' . print_r($results,true));
+ logger('onepoll: feed_update: process_delivery: ' . print_r($results,true), LOGGER_DATA);
$total ++;
}
logger("onepoll: $total messages processed");
@@ -143,13 +140,14 @@ function onepoll_run($argv, $argc){
}
}
- // fetch some items
- // set last updated timestamp
+
+ // update the poco details for this connection
if($contact['xchan_connurl']) {
$r = q("SELECT xlink_id from xlink
- where xlink_xchan = '%s' and xlink_updated > UTC_TIMESTAMP() - INTERVAL 1 DAY limit 1",
- intval($contact['xchan_hash'])
+ where xlink_xchan = '%s' and xlink_updated > %s - INTERVAL %s limit 1",
+ intval($contact['xchan_hash']),
+ db_utcnow(), db_quoteinterval('1 DAY')
);
if(! $r) {
poco_load($contact['xchan_hash'],$contact['xchan_connurl']);
diff --git a/include/permissions.php b/include/permissions.php
index 8e4676f51..563d574db 100644
--- a/include/permissions.php
+++ b/include/permissions.php
@@ -1,6 +1,19 @@
-<?php /** @file */
-
+<?php
+/**
+ * @file incldue/permissions.php
+ *
+ * This file conntains functions to check and work with permissions.
+ */
+/**
+ * @brief Return an array with all available permissions.
+ *
+ * These are channel specific permissions.
+ * The list of available permissions can get manipulated by the <i>hook</i>
+ * <b>global_permissions</b>.
+ *
+ * @return array associative array containing all permissions
+ */
function get_perms() {
// thinking about making element[2] a bitmask instead of boolean so that we can provide a list of applicable selections
@@ -14,8 +27,8 @@ function get_perms() {
'view_profile' => array('channel_r_profile', intval(PERMS_R_PROFILE), true, t('Can view my default channel profile'), ''),
'view_photos' => array('channel_r_photos', intval(PERMS_R_PHOTOS), true, t('Can view my photo albums'), ''),
'view_contacts' => array('channel_r_abook', intval(PERMS_R_ABOOK), true, t('Can view my connections'), ''),
- 'view_storage' => array('channel_r_storage', intval(PERMS_R_STORAGE), true, t('Can view my file storage'), ''),
- 'view_pages' => array('channel_r_pages', intval(PERMS_R_PAGES), true, t('Can view my webpages'), ''),
+ 'view_storage' => array('channel_r_storage', intval(PERMS_R_STORAGE), true, t('Can view my file storage'), ''),
+ 'view_pages' => array('channel_r_pages', intval(PERMS_R_PAGES), true, t('Can view my webpages'), ''),
// Write permissions
'send_stream' => array('channel_w_stream', intval(PERMS_W_STREAM), false, t('Can send me their channel stream and posts'), ''),
@@ -23,19 +36,20 @@ function get_perms() {
'post_comments' => array('channel_w_comment', intval(PERMS_W_COMMENT), false, t('Can comment on or like my posts'), ''),
'post_mail' => array('channel_w_mail', intval(PERMS_W_MAIL), false, t('Can send me private mail messages'), ''),
'post_photos' => array('channel_w_photos', intval(PERMS_W_PHOTOS), false, t('Can post photos to my photo albums'), ''),
- 'post_like' => array('channel_w_like', intval(PERMS_W_LIKE), false, t('Can like/dislike stuff'), t('Profiles and things other than posts/comments')),
+ 'post_like' => array('channel_w_like', intval(PERMS_W_LIKE), false, t('Can like/dislike stuff'), t('Profiles and things other than posts/comments')),
'tag_deliver' => array('channel_w_tagwall', intval(PERMS_W_TAGWALL), false, t('Can forward to all my channel contacts via post @mentions'), t('Advanced - useful for creating group forum channels')),
'chat' => array('channel_w_chat', intval(PERMS_W_CHAT), false, t('Can chat with me (when available)'), t('')),
- 'write_storage' => array('channel_w_storage', intval(PERMS_W_STORAGE), false, t('Can write to my file storage'), ''),
- 'write_pages' => array('channel_w_pages', intval(PERMS_W_PAGES), false, t('Can edit my webpages'), ''),
+ 'write_storage' => array('channel_w_storage', intval(PERMS_W_STORAGE), false, t('Can write to my file storage'), ''),
+ 'write_pages' => array('channel_w_pages', intval(PERMS_W_PAGES), false, t('Can edit my webpages'), ''),
- 'republish' => array('channel_a_republish', intval(PERMS_A_REPUBLISH), false, t('Can source my public posts in derived channels'), t('Somewhat advanced - very useful in open communities')),
+ 'republish' => array('channel_a_republish', intval(PERMS_A_REPUBLISH), false, t('Can source my public posts in derived channels'), t('Somewhat advanced - very useful in open communities')),
- 'delegate' => array('channel_a_delegate', intval(PERMS_A_DELEGATE), false, t('Can administer my channel resources'), t('Extremely advanced. Leave this alone unless you know what you are doing')),
+ 'delegate' => array('channel_a_delegate', intval(PERMS_A_DELEGATE), false, t('Can administer my channel resources'), t('Extremely advanced. Leave this alone unless you know what you are doing')),
);
$ret = array('global_permissions' => $global_perms);
- call_hooks('global_permissions',$ret);
+ call_hooks('global_permissions', $ret);
+
return $ret['global_permissions'];
}
@@ -43,13 +57,13 @@ function get_perms() {
/**
* get_all_perms($uid,$observer_xchan)
*
- * @param $uid : The channel_id associated with the resource owner
- * @param $observer_xchan: The xchan_hash representing the observer
+ * @param int $uid The channel_id associated with the resource owner
+ * @param string $observer_xchan The xchan_hash representing the observer
+ * @param bool $internal_use (default true)
*
- * @returns: array of all permissions, key is permission name, value is true or false
+ * @returns array of all permissions, key is permission name, value is true or false
*/
-
-function get_all_perms($uid,$observer_xchan,$internal_use = true) {
+function get_all_perms($uid, $observer_xchan, $internal_use = true) {
$global_perms = get_perms();
@@ -85,7 +99,6 @@ function get_all_perms($uid,$observer_xchan,$internal_use = true) {
continue;
}
-
// Next we're going to check for blocked or ignored contacts.
// These take priority over all other settings.
@@ -94,10 +107,10 @@ function get_all_perms($uid,$observer_xchan,$internal_use = true) {
$ret[$perm_name] = true;
continue;
}
-
+
if(! $abook_checked) {
$x = q("select abook_my_perms, abook_flags, xchan_network from abook left join xchan on abook_xchan = xchan_hash
- where abook_channel = %d and abook_xchan = '%s' and not ( abook_flags & %d ) limit 1",
+ where abook_channel = %d and abook_xchan = '%s' and not ( abook_flags & %d )>0 limit 1",
intval($uid),
dbesc($observer_xchan),
intval(ABOOK_FLAG_SELF)
@@ -105,7 +118,7 @@ function get_all_perms($uid,$observer_xchan,$internal_use = true) {
if(! $x) {
// not in address book, see if they've got an xchan
$y = q("select xchan_network from xchan where xchan_hash = '%s' limit 1",
- dbesc($observer_xchan)
+ dbesc($observer_xchan)
);
}
@@ -113,7 +126,7 @@ function get_all_perms($uid,$observer_xchan,$internal_use = true) {
}
// If they're blocked - they can't read or write
-
+
if(($x) && ($x[0]['abook_flags'] & ABOOK_FLAG_BLOCKED)) {
$ret[$perm_name] = false;
continue;
@@ -130,7 +143,7 @@ function get_all_perms($uid,$observer_xchan,$internal_use = true) {
// system is blocked to anybody who is not authenticated
- if((! $observer_xchan) && intval(get_config('system','block_public'))) {
+ if((! $observer_xchan) && intval(get_config('system', 'block_public'))) {
$ret[$perm_name] = false;
continue;
}
@@ -177,14 +190,14 @@ function get_all_perms($uid,$observer_xchan,$internal_use = true) {
$onsite_checked = true;
}
-
+
if($c)
$ret[$perm_name] = true;
else
$ret[$perm_name] = false;
continue;
- }
+ }
// From here on we require that the observer be a connection and
// handle whether we're allowing any, approved or specific ones
@@ -226,7 +239,6 @@ function get_all_perms($uid,$observer_xchan,$internal_use = true) {
$ret[$perm_name] = false;
continue;
-
}
$arr = array(
@@ -235,11 +247,23 @@ function get_all_perms($uid,$observer_xchan,$internal_use = true) {
'permissions' => $ret);
call_hooks('get_all_perms',$arr);
+
return $arr['permissions'];
}
-
-function perm_is_allowed($uid,$observer_xchan,$permission) {
+/**
+ * @brief Checks if given permission is allowed for given observer on a channel.
+ *
+ * Checks if the given observer with the hash $observer_xchan has permission
+ * $permission on channel_id $uid.
+ * $permission is one defined in get_perms();
+ *
+ * @param int $uid The channel_id associated with the resource owner
+ * @param string $observer_xchan The xchan_hash representing the observer
+ * @param string $permission
+ * @return bool true if permission is allowed for observer on channel
+ */
+function perm_is_allowed($uid, $observer_xchan, $permission) {
$arr = array(
'channel_id' => $uid,
@@ -247,7 +271,7 @@ function perm_is_allowed($uid,$observer_xchan,$permission) {
'permission' => $permission,
'result' => false);
- call_hooks('perm_is_allowed',$arr);
+ call_hooks('perm_is_allowed', $arr);
if($arr['result'])
return true;
@@ -257,7 +281,7 @@ function perm_is_allowed($uid,$observer_xchan,$permission) {
$channel_perm = $global_perms[$permission][0];
- $r = q("select %s, channel_hash from channel where channel_id = %d limit 1",
+ $r = q("select %s, channel_pageflags, channel_hash from channel where channel_id = %d limit 1",
dbesc($channel_perm),
intval($uid)
);
@@ -269,7 +293,7 @@ function perm_is_allowed($uid,$observer_xchan,$permission) {
return true;
$x = q("select abook_my_perms, abook_flags, xchan_network from abook left join xchan on abook_xchan = xchan_hash
- where abook_channel = %d and abook_xchan = '%s' and not ( abook_flags & %d ) limit 1",
+ where abook_channel = %d and abook_xchan = '%s' and not ( abook_flags & %d )>0 limit 1",
intval($uid),
dbesc($observer_xchan),
intval(ABOOK_FLAG_SELF)
@@ -279,21 +303,21 @@ function perm_is_allowed($uid,$observer_xchan,$permission) {
if(($x) && ($x[0]['abook_flags'] & ABOOK_FLAG_BLOCKED))
return false;
-
+
if(($x) && (! $global_perms[$permission][2]) && ($x[0]['abook_flags'] & ABOOK_FLAG_IGNORED))
return false;
if(! $x) {
// not in address book, see if they've got an xchan
$y = q("select xchan_network from xchan where xchan_hash = '%s' limit 1",
- dbesc($observer_xchan)
+ dbesc($observer_xchan)
);
}
}
// system is blocked to anybody who is not authenticated
- if((! $observer_xchan) && intval(get_config('system','block_public')))
+ if((! $observer_xchan) && intval(get_config('system', 'block_public')))
return false;
// Check if this $uid is actually the $observer_xchan
@@ -301,7 +325,6 @@ function perm_is_allowed($uid,$observer_xchan,$permission) {
if($r[0]['channel_hash'] === $observer_xchan)
return true;
-
if($r[0][$channel_perm] & PERMS_PUBLIC)
return true;
@@ -326,6 +349,7 @@ function perm_is_allowed($uid,$observer_xchan,$permission) {
);
if($c)
return true;
+
return false;
}
@@ -361,25 +385,32 @@ function perm_is_allowed($uid,$observer_xchan,$permission) {
// No permissions allowed.
- return false;
+ return false;
}
// Check a simple array of observers against a permissions
// return a simple array of those with permission
-function check_list_permissions($uid,$arr,$perm) {
+function check_list_permissions($uid, $arr, $perm) {
$result = array();
if($arr)
foreach($arr as $x)
- if(perm_is_allowed($uid,$x,$perm))
+ if(perm_is_allowed($uid, $x, $perm))
$result[] = $x;
+
return($result);
}
-
+/**
+ * @brief Sets site wide default permissions.
+ *
+ * @return array
+ */
function site_default_perms() {
+ $ret = array();
+
$typical = array(
'view_stream' => PERMS_PUBLIC,
'view_profile' => PERMS_PUBLIC,
@@ -401,14 +432,14 @@ function site_default_perms() {
);
$global_perms = get_perms();
- $ret = array();
foreach($global_perms as $perm => $v) {
- $x = get_config('default_perms',$perm);
+ $x = get_config('default_perms', $perm);
if($x === false)
$x = $typical[$perm];
$ret[$perm] = $x;
}
+
return $ret;
}
@@ -419,11 +450,12 @@ function site_default_perms() {
*
* Given a string for the channel role ('social','forum', etc)
* return an array of all permission fields pre-filled for this role.
- * This includes the channel permission scope indicators as well as
- * perms_auto: The permissions to apply automatically on receipt of a connection request
+ * This includes the channel permission scope indicators (anything beginning with 'channel_') as well as
+ * perms_auto: true or false to create auto-permissions for this channel
* perms_follow: The permissions to apply when initiating a connection request to another channel
* perms_accept: The permissions to apply when accepting a connection request from another channel (not automatic)
- *
+ * default_collection: true or false to make the default ACL include the channel's default collection
+ * directory_publish: true or false to publish this channel in the directory
* Any attributes may be extended (new roles defined) and modified (specific permissions altered) by plugins
*
*/
@@ -436,7 +468,10 @@ function get_role_perms($role) {
switch($role) {
case 'social':
- $ret['perms_auto'] = 0;
+ $ret['perms_auto'] = false;
+ $ret['default_collection'] = false;
+ $ret['directory_publish'] = true;
+ $ret['online'] = true;
$ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK
|PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT
|PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE;
@@ -445,7 +480,231 @@ function get_role_perms($role) {
|PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE;
$ret['channel_r_stream'] = PERMS_PUBLIC;
$ret['channel_r_profile'] = PERMS_PUBLIC;
- $ret['channel_r_photos'] = PERMS_PUBLIC;
+ $ret['channel_r_photos'] = PERMS_PUBLIC;
+ $ret['channel_r_abook'] = PERMS_PUBLIC;
+ $ret['channel_w_stream'] = PERMS_CONTACTS;
+ $ret['channel_w_wall'] = PERMS_CONTACTS;
+ $ret['channel_w_tagwall'] = PERMS_SPECIFIC;
+ $ret['channel_w_comment'] = PERMS_CONTACTS;
+ $ret['channel_w_mail'] = PERMS_CONTACTS;
+ $ret['channel_w_photos'] = 0;
+ $ret['channel_w_chat'] = PERMS_CONTACTS;
+ $ret['channel_a_delegate'] = 0;
+ $ret['channel_r_storage'] = PERMS_PUBLIC;
+ $ret['channel_w_storage'] = 0;
+ $ret['channel_r_pages'] = PERMS_PUBLIC;
+ $ret['channel_w_pages'] = 0;
+ $ret['channel_a_republish'] = PERMS_SPECIFIC;
+ $ret['channel_w_like'] = PERMS_NETWORK;
+
+ break;
+
+ case 'social_restricted':
+ $ret['perms_auto'] = false;
+ $ret['default_collection'] = true;
+ $ret['directory_publish'] = true;
+ $ret['online'] = true;
+ $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK
+ |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT
+ |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE;
+ $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK
+ |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT
+ |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE;
+ $ret['channel_r_stream'] = PERMS_PUBLIC;
+ $ret['channel_r_profile'] = PERMS_PUBLIC;
+ $ret['channel_r_photos'] = PERMS_PUBLIC;
+ $ret['channel_r_abook'] = PERMS_PUBLIC;
+ $ret['channel_w_stream'] = PERMS_CONTACTS;
+ $ret['channel_w_wall'] = PERMS_CONTACTS;
+ $ret['channel_w_tagwall'] = PERMS_SPECIFIC;
+ $ret['channel_w_comment'] = PERMS_CONTACTS;
+ $ret['channel_w_mail'] = PERMS_CONTACTS;
+ $ret['channel_w_photos'] = 0;
+ $ret['channel_w_chat'] = PERMS_CONTACTS;
+ $ret['channel_a_delegate'] = 0;
+ $ret['channel_r_storage'] = PERMS_PUBLIC;
+ $ret['channel_w_storage'] = 0;
+ $ret['channel_r_pages'] = PERMS_PUBLIC;
+ $ret['channel_w_pages'] = 0;
+ $ret['channel_a_republish'] = PERMS_SPECIFIC;
+ $ret['channel_w_like'] = PERMS_CONTACTS;
+
+ break;
+
+ case 'social_private':
+ $ret['perms_auto'] = false;
+ $ret['default_collection'] = true;
+ $ret['directory_publish'] = false;
+ $ret['online'] = false;
+ $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK
+ |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT
+ |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE;
+ $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK
+ |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT
+ |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE;
+ $ret['channel_r_stream'] = PERMS_PUBLIC;
+ $ret['channel_r_profile'] = PERMS_PUBLIC;
+ $ret['channel_r_photos'] = PERMS_PUBLIC;
+ $ret['channel_r_abook'] = PERMS_CONTACTS;
+ $ret['channel_w_stream'] = PERMS_CONTACTS;
+ $ret['channel_w_wall'] = PERMS_CONTACTS;
+ $ret['channel_w_tagwall'] = PERMS_SPECIFIC;
+ $ret['channel_w_comment'] = PERMS_CONTACTS;
+ $ret['channel_w_mail'] = PERMS_SPECIFIC;
+ $ret['channel_w_photos'] = 0;
+ $ret['channel_w_chat'] = PERMS_SPECIFIC;
+ $ret['channel_a_delegate'] = 0;
+ $ret['channel_r_storage'] = PERMS_PUBLIC;
+ $ret['channel_w_storage'] = 0;
+ $ret['channel_r_pages'] = PERMS_PUBLIC;
+ $ret['channel_w_pages'] = 0;
+ $ret['channel_a_republish'] = PERMS_SPECIFIC;
+ $ret['channel_w_like'] = PERMS_CONTACTS;
+
+ break;
+
+ case 'forum':
+ $ret['perms_auto'] = true;
+ $ret['default_collection'] = false;
+ $ret['directory_publish'] = true;
+ $ret['online'] = false;
+ $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK
+ |PERMS_W_STREAM|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_PHOTOS|PERMS_R_ABOOK
+ |PERMS_W_STREAM|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;
+ $ret['channel_r_photos'] = PERMS_PUBLIC;
+ $ret['channel_r_abook'] = PERMS_PUBLIC;
+ $ret['channel_w_stream'] = 0;
+ $ret['channel_w_wall'] = PERMS_CONTACTS;
+ $ret['channel_w_tagwall'] = PERMS_CONTACTS;
+ $ret['channel_w_comment'] = PERMS_CONTACTS;
+ $ret['channel_w_mail'] = PERMS_CONTACTS;
+ $ret['channel_w_photos'] = 0;
+ $ret['channel_w_chat'] = PERMS_CONTACTS;
+ $ret['channel_a_delegate'] = 0;
+ $ret['channel_r_storage'] = PERMS_PUBLIC;
+ $ret['channel_w_storage'] = 0;
+ $ret['channel_r_pages'] = PERMS_PUBLIC;
+ $ret['channel_w_pages'] = 0;
+ $ret['channel_a_republish'] = PERMS_SPECIFIC;
+ $ret['channel_w_like'] = PERMS_NETWORK;
+
+ break;
+
+ case 'forum_restricted':
+ $ret['perms_auto'] = false;
+ $ret['default_collection'] = true;
+ $ret['directory_publish'] = true;
+ $ret['online'] = false;
+ $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK
+ |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT
+ |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE|PERMS_W_TAGWALL;
+ $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK
+ |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT
+ |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE|PERMS_W_TAGWALL;
+ $ret['channel_r_stream'] = PERMS_PUBLIC;
+ $ret['channel_r_profile'] = PERMS_PUBLIC;
+ $ret['channel_r_photos'] = PERMS_PUBLIC;
+ $ret['channel_r_abook'] = PERMS_PUBLIC;
+ $ret['channel_w_stream'] = 0;
+ $ret['channel_w_wall'] = PERMS_CONTACTS;
+ $ret['channel_w_tagwall'] = PERMS_SPECIFIC;
+ $ret['channel_w_comment'] = PERMS_CONTACTS;
+ $ret['channel_w_mail'] = PERMS_CONTACTS;
+ $ret['channel_w_photos'] = 0;
+ $ret['channel_w_chat'] = PERMS_CONTACTS;
+ $ret['channel_a_delegate'] = 0;
+ $ret['channel_r_storage'] = PERMS_PUBLIC;
+ $ret['channel_w_storage'] = 0;
+ $ret['channel_r_pages'] = PERMS_PUBLIC;
+ $ret['channel_w_pages'] = 0;
+ $ret['channel_a_republish'] = PERMS_SPECIFIC;
+ $ret['channel_w_like'] = PERMS_CONTACTS;
+
+ break;
+
+ case 'forum_private':
+ $ret['perms_auto'] = false;
+ $ret['default_collection'] = true;
+ $ret['directory_publish'] = false;
+ $ret['online'] = false;
+ $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK
+ |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT
+ |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE;
+ $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK
+ |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT
+ |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE;
+ $ret['channel_r_stream'] = PERMS_PUBLIC;
+ $ret['channel_r_profile'] = PERMS_CONTACTS;
+ $ret['channel_r_photos'] = PERMS_CONTACTS;
+ $ret['channel_r_abook'] = PERMS_CONTACTS;
+ $ret['channel_w_stream'] = 0;
+ $ret['channel_w_wall'] = PERMS_CONTACTS;
+ $ret['channel_w_tagwall'] = 0;
+ $ret['channel_w_comment'] = PERMS_CONTACTS;
+ $ret['channel_w_mail'] = PERMS_SPECIFIC;
+ $ret['channel_w_photos'] = 0;
+ $ret['channel_w_chat'] = PERMS_SPECIFIC;
+ $ret['channel_a_delegate'] = 0;
+ $ret['channel_r_storage'] = PERMS_CONTACTS;
+ $ret['channel_w_storage'] = 0;
+ $ret['channel_r_pages'] = PERMS_CONTACTS;
+ $ret['channel_w_pages'] = 0;
+ $ret['channel_a_republish'] = PERMS_SPECIFIC;
+ $ret['channel_w_like'] = PERMS_CONTACTS;
+
+ break;
+
+ case 'feed':
+ $ret['perms_auto'] = true;
+ $ret['default_collection'] = false;
+ $ret['directory_publish'] = true;
+ $ret['online'] = false;
+ $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK
+ |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL
+ |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE;
+ $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK
+ |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL
+ |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE;
+ $ret['channel_r_stream'] = PERMS_PUBLIC;
+ $ret['channel_r_profile'] = PERMS_PUBLIC;
+ $ret['channel_r_photos'] = PERMS_PUBLIC;
+ $ret['channel_r_abook'] = PERMS_PUBLIC;
+ $ret['channel_w_stream'] = PERMS_CONTACTS;
+ $ret['channel_w_wall'] = PERMS_CONTACTS;
+ $ret['channel_w_tagwall'] = PERMS_SPECIFIC;
+ $ret['channel_w_comment'] = PERMS_CONTACTS;
+ $ret['channel_w_mail'] = PERMS_CONTACTS;
+ $ret['channel_w_photos'] = 0;
+ $ret['channel_w_chat'] = PERMS_CONTACTS;
+ $ret['channel_a_delegate'] = 0;
+ $ret['channel_r_storage'] = PERMS_PUBLIC;
+ $ret['channel_w_storage'] = 0;
+ $ret['channel_r_pages'] = PERMS_PUBLIC;
+ $ret['channel_w_pages'] = 0;
+ $ret['channel_a_republish'] = PERMS_NETWORK;
+ $ret['channel_w_like'] = PERMS_NETWORK;
+
+ break;
+
+ case 'feed_restricted':
+ $ret['perms_auto'] = false;
+ $ret['default_collection'] = true;
+ $ret['directory_publish'] = false;
+ $ret['online'] = false;
+ $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK
+ |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL
+ |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE;
+ $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK
+ |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL
+ |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE;
+ $ret['channel_r_stream'] = PERMS_PUBLIC;
+ $ret['channel_r_profile'] = PERMS_PUBLIC;
+ $ret['channel_r_photos'] = PERMS_PUBLIC;
$ret['channel_r_abook'] = PERMS_PUBLIC;
$ret['channel_w_stream'] = PERMS_CONTACTS;
$ret['channel_w_wall'] = PERMS_CONTACTS;
@@ -456,18 +715,123 @@ function get_role_perms($role) {
$ret['channel_w_chat'] = PERMS_CONTACTS;
$ret['channel_a_delegate'] = 0;
$ret['channel_r_storage'] = PERMS_PUBLIC;
+ $ret['channel_w_storage'] = 0;
+ $ret['channel_r_pages'] = PERMS_PUBLIC;
+ $ret['channel_w_pages'] = 0;
+ $ret['channel_a_republish'] = PERMS_SPECIFIC;
+ $ret['channel_w_like'] = PERMS_NETWORK;
+
+ break;
+
+ case 'soapbox':
+ $ret['perms_auto'] = true;
+ $ret['default_collection'] = false;
+ $ret['directory_publish'] = true;
+ $ret['online'] = false;
+ $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK
+ |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE;
+ $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK
+ |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE;
+ $ret['channel_r_stream'] = PERMS_PUBLIC;
+ $ret['channel_r_profile'] = PERMS_PUBLIC;
+ $ret['channel_r_photos'] = PERMS_PUBLIC;
+ $ret['channel_r_abook'] = PERMS_PUBLIC;
+ $ret['channel_w_stream'] = 0;
+ $ret['channel_w_wall'] = 0;
+ $ret['channel_w_tagwall'] = 0;
+ $ret['channel_w_comment'] = 0;
+ $ret['channel_w_mail'] = 0;
+ $ret['channel_w_photos'] = 0;
+ $ret['channel_w_chat'] = 0;
+ $ret['channel_a_delegate'] = 0;
+ $ret['channel_r_storage'] = PERMS_PUBLIC;
+ $ret['channel_w_storage'] = 0;
$ret['channel_r_pages'] = PERMS_PUBLIC;
$ret['channel_w_pages'] = 0;
$ret['channel_a_republish'] = PERMS_SPECIFIC;
$ret['channel_w_like'] = PERMS_NETWORK;
-
+
+ break;
+
+ case 'repository':
+ $ret['perms_auto'] = true;
+ $ret['default_collection'] = false;
+ $ret['directory_publish'] = true;
+ $ret['online'] = false;
+ $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK
+ |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT
+ |PERMS_R_STORAGE|PERMS_W_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE|PERMS_W_TAGWALL;
+ $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK
+ |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT
+ |PERMS_R_STORAGE|PERMS_W_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;
+ $ret['channel_r_photos'] = PERMS_PUBLIC;
+ $ret['channel_r_abook'] = PERMS_PUBLIC;
+ $ret['channel_w_stream'] = PERMS_CONTACTS;
+ $ret['channel_w_wall'] = PERMS_CONTACTS;
+ $ret['channel_w_tagwall'] = PERMS_CONTACTS;
+ $ret['channel_w_comment'] = PERMS_CONTACTS;
+ $ret['channel_w_mail'] = PERMS_CONTACTS;
+ $ret['channel_w_photos'] = PERMS_CONTACTS;
+ $ret['channel_w_chat'] = PERMS_CONTACTS;
+ $ret['channel_a_delegate'] = 0;
+ $ret['channel_r_storage'] = PERMS_PUBLIC;
+ $ret['channel_w_storage'] = PERMS_CONTACTS;
+ $ret['channel_r_pages'] = PERMS_PUBLIC;
+ $ret['channel_w_pages'] = PERMS_CONTACTS;
+ $ret['channel_a_republish'] = PERMS_SPECIFIC;
+ $ret['channel_w_like'] = PERMS_NETWORK;
+
+ break;
+
+ default:
break;
-
}
+ $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;
}
+/**
+ * @brief Creates a HTML select field with all available roles.
+ *
+ * @param string $current The current role
+ * @return string Returns the complete HTML code for this privacy-role-select field.
+ */
+function role_selector($current) {
+
+ if(! $current)
+ $current = 'custom';
+
+ $roles = array(
+ 'social' => array( t('Social Networking'),
+ array('social' => t('Mostly Public'), 'social_restricted' => t('Restricted'), 'social_private' => t('Private'))),
+ 'forum' => array( t('Community Forum'),
+ array('forum' => t('Mostly Public'), 'forum_restricted' => t('Restricted'), 'forum_private' => t('Private'))),
+ 'feed' => array( t('Feed Republish'),
+ array('feed' => t('Mostly Public'), 'feed_restricted' => t('Restricted'))),
+ 'special' => array( t('Special Purpose'),
+ array('soapbox' => t('Celebrity/Soapbox'), 'repository' => t('Group Repository'))),
+ 'other' => array( t('Other'),
+ array('custom' => t('Custom/Expert Mode'))));
+
+ $o = '<select name="permissions_role" id="privacy-role-select">';
+ foreach($roles as $k => $v) {
+ $o .= '<optgroup label="'. htmlspecialchars($v[0]) . '">';
+ foreach($v[1] as $kk => $vv) {
+ $selected = (($kk === $current) ? ' selected="selected"' : '');
+ $o .= '<option value="' . $kk . '"' . $selected . '>' . htmlspecialchars($vv) . '</option>';
+ }
+ $o .= '</optgroup>';
+ }
+ $o .= '</select>';
+ return $o;
+}
diff --git a/include/photo/photo_driver.php b/include/photo/photo_driver.php
index daf1bfc25..e63125671 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);
}
@@ -328,7 +341,7 @@ abstract class photo_driver {
`allow_gid` = '%s',
`deny_cid` = '%s',
`deny_gid` = '%s'
- where id = %d limit 1",
+ where id = %d",
intval($p['aid']),
intval($p['uid']),
@@ -341,7 +354,7 @@ abstract class photo_driver {
dbesc($p['album']),
intval($this->getHeight()),
intval($this->getWidth()),
- dbesc($this->imageString()),
+ dbescbin($this->imageString()),
intval(strlen($this->imageString())),
intval($p['scale']),
intval($p['profile']),
@@ -370,7 +383,7 @@ abstract class photo_driver {
dbesc($p['album']),
intval($this->getHeight()),
intval($this->getWidth()),
- dbesc($this->imageString()),
+ dbescbin($this->imageString()),
intval(strlen($this->imageString())),
intval($p['scale']),
intval($p['profile']),
@@ -415,7 +428,7 @@ abstract class photo_driver {
`allow_gid` = '%s',
`deny_cid` = '%s',
`deny_gid` = '%s'
- where id = %d limit 1",
+ where id = %d",
intval($aid),
intval($uid),
@@ -428,7 +441,7 @@ abstract class photo_driver {
dbesc($album),
intval($this->getHeight()),
intval($this->getWidth()),
- dbesc($this->imageString()),
+ dbescbin($this->imageString()),
intval(strlen($this->imageString())),
intval($scale),
intval($profile),
@@ -454,7 +467,7 @@ abstract class photo_driver {
dbesc($album),
intval($this->getHeight()),
intval($this->getWidth()),
- dbesc($this->imageString()),
+ dbescbin($this->imageString()),
intval(strlen($this->imageString())),
intval($scale),
intval($profile),
@@ -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();
@@ -535,7 +562,7 @@ function import_profile_photo($photo,$xchan,$thing = false) {
if($thing)
$hash = photo_new_resource();
else {
- $r = q("select resource_id from photo where xchan = '%s' and (photo_flags & %d ) and scale = 4 limit 1",
+ $r = q("select resource_id from photo where xchan = '%s' and (photo_flags & %d )>0 and scale = 4 limit 1",
dbesc($xchan),
intval(PHOTO_XCHAN)
);
@@ -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/photo/photo_gd.php b/include/photo/photo_gd.php
index 466f8c23a..fa1f700e9 100644
--- a/include/photo/photo_gd.php
+++ b/include/photo/photo_gd.php
@@ -10,7 +10,7 @@ class photo_gd extends photo_driver {
$t = array();
$t['image/jpeg'] ='jpg';
if (imagetypes() & IMG_PNG) $t['image/png'] = 'png';
-
+ if (imagetypes() & IMG_GIF) $t['image/gif'] = 'gif';
return $t;
}
diff --git a/include/photos.php b/include/photos.php
index 06a99457a..2393153c6 100644
--- a/include/photos.php
+++ b/include/photos.php
@@ -44,9 +44,10 @@ function photo_upload($channel, $observer, $args) {
*
*/
- $r = q("SELECT * FROM photo WHERE album = '%s' AND uid = %d AND created > UTC_TIMESTAMP() - INTERVAL 3 HOUR ",
+ $r = q("SELECT * FROM photo WHERE album = '%s' AND uid = %d AND created > %s - INTERVAL %s ",
dbesc($album),
- intval($channel_id)
+ intval($channel_id),
+ db_utcnow(), db_quoteinterval('3 HOUR')
);
if((! $r) || ($album == t('Profile Photos')))
$visible = 1;
@@ -178,7 +179,7 @@ function photo_upload($channel, $observer, $args) {
if($args['title'])
$p['title'] = $args['title'];
if($args['description'])
- $p['desciprion'] = $args['description'];
+ $p['description'] = $args['description'];
$r1 = $ph->save($p);
@@ -266,6 +267,7 @@ function photo_upload($channel, $observer, $args) {
proc_run('php', "include/notifier.php", 'wall-new', $item_id);
$ret['success'] = true;
+ $ret['item'] = $arr;
$ret['body'] = $arr['body'];
$ret['resource_id'] = $photo_hash;
$ret['photoitem_id'] = $item_id;
@@ -290,7 +292,7 @@ function photos_albums_list($channel,$observer) {
$sql_extra = permissions_sql($channel_id);
- $albums = q("SELECT count( distinct resource_id ) as total, album from photo where uid = %d and ( photo_flags = %d or photo_flags = %d ) $sql_extra group by album order by created desc",
+ $albums = q("SELECT count( distinct resource_id ) as total, album from photo where uid = %d and ( photo_flags = %d or photo_flags = %d ) $sql_extra group by album order by max(created) desc",
intval($channel_id),
intval(PHOTO_NORMAL),
intval(PHOTO_PROFILE)
@@ -430,7 +432,7 @@ function photos_create_item($channel, $creator_hash, $photo, $visible = false) {
// Create item container
$item_flags = ITEM_WALL|ITEM_ORIGIN|ITEM_THREAD_TOP;
- $item_restrict = (($visible) ? ITEM_HIDDEN : ITEM_VISIBLE);
+ $item_restrict = (($visible) ? ITEM_VISIBLE : ITEM_HIDDEN);
$title = '';
$mid = item_message_id();
diff --git a/include/plugin.php b/include/plugin.php
index c2e08a989..e500ccc56 100755
--- a/include/plugin.php
+++ b/include/plugin.php
@@ -102,7 +102,7 @@ function load_plugin($plugin) {
// This way the system won't fall over dead during the update.
if(file_exists('addon/' . $plugin . '/.hidden')) {
- q("update addon set hidden = 1 where name = '%s' limit 1",
+ q("update addon set hidden = 1 where name = '%s'",
dbesc($plugin)
);
}
@@ -158,7 +158,7 @@ function reload_plugins() {
$func = $pl . '_load';
$func();
}
- q("UPDATE `addon` SET `timestamp` = %d WHERE `id` = %d LIMIT 1",
+ q("UPDATE `addon` SET `timestamp` = %d WHERE `id` = %d",
intval($t),
intval($i['id'])
);
@@ -208,7 +208,7 @@ function register_hook($hook, $file, $function, $priority = 0) {
* @return mixed
*/
function unregister_hook($hook, $file, $function) {
- $r = q("DELETE FROM hook WHERE hook = '%s' AND `file` = '%s' AND `function` = '%s' LIMIT 1",
+ $r = q("DELETE FROM hook WHERE hook = '%s' AND `file` = '%s' AND `function` = '%s'",
dbesc($hook),
dbesc($file),
dbesc($function)
@@ -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 b26b9e696..e13c6829d 100644
--- a/include/poller.php
+++ b/include/poller.php
@@ -25,6 +25,16 @@ function poller_run($argv, $argc){
if(! $interval)
$interval = ((get_config('system','delivery_interval') === false) ? 3 : intval(get_config('system','delivery_interval')));
+ // Check for a lockfile. If it exists, but is over an hour old, it's stale. Ignore it.
+ $lockfile = 'store/[data]/poller';
+ if((file_exists($lockfile)) && (filemtime($lockfile) > (time() - 3600))
+ && (! get_config('system','override_poll_lockfile'))) {
+ logger("poller: Already running");
+ return;
+ }
+
+ // Create a lockfile. Needs two vars, but $x doesn't need to contain anything.
+ file_put_contents($lockfile, $x);
logger('poller: start');
@@ -35,12 +45,17 @@ function poller_run($argv, $argc){
// expire any expired mail
- q("delete from mail where expires != '0000-00-00 00:00:00' and expires < UTC_TIMESTAMP() ");
+ q("delete from mail where expires != '%s' and expires < %s ",
+ dbesc(NULL_DATE),
+ db_utcnow()
+ );
// expire any expired items
- $r = q("select id from item where expires != '0000-00-00 00:00:00' and expires < UTC_TIMESTAMP()
- and not ( item_restrict & %d ) ",
+ $r = q("select id from item where expires != '%s' and expires < %s
+ and ( item_restrict & %d ) = 0 ",
+ dbesc(NULL_DATE),
+ db_utcnow(),
intval(ITEM_DELETED)
);
if($r) {
@@ -54,7 +69,10 @@ function poller_run($argv, $argc){
// channels and sites that quietly vanished and prevent the directory from accumulating stale
// or dead entries.
- $r = q("select channel_id from channel where channel_dirdate < UTC_TIMESTAMP() - INTERVAL 30 DAY");
+ $r = q("select channel_id from channel where channel_dirdate < %s - INTERVAL %s",
+ db_utcnow(),
+ db_quoteinterval('30 DAY')
+ );
if($r) {
foreach($r as $rr) {
proc_run('php','include/directory.php',$rr['channel_id'],'force');
@@ -66,12 +84,13 @@ function poller_run($argv, $argc){
// publish any applicable items that were set to be published in the future
// (time travel posts)
- $r = q("select id from item where ( item_restrict & %d ) and created <= UTC_TIMESTAMP() ",
- intval(ITEM_DELAYED_PUBLISH)
+ $r = q("select id from item where ( item_restrict & %d ) > 0 and created <= %s ",
+ intval(ITEM_DELAYED_PUBLISH),
+ db_utcnow()
);
if($r) {
foreach($r as $rr) {
- $x = q("update item set item_restrict = ( item_restrict ^ %d ) where id = %d limit 1",
+ $x = q("update item set item_restrict = ( item_restrict & ~%d ) where id = %d",
intval(ITEM_DELAYED_PUBLISH),
intval($rr['id'])
);
@@ -111,8 +130,8 @@ function poller_run($argv, $argc){
if(($d2 != $d1) && ($h1 == $h2)) {
- require_once('include/dir_fns.php');
- check_upstream_directory();
+ require_once('include/dir_fns.php');
+ check_upstream_directory();
call_hooks('cron_daily',datetime_convert());
@@ -146,9 +165,18 @@ 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");
+ q("delete from notify where seen = 1 and date < %s - INTERVAL %s",
+ db_utcnow(), db_quoteinterval('30 DAY')
+ );
// expire any expired accounts
downgrade_accounts();
@@ -168,6 +196,9 @@ function poller_run($argv, $argc){
proc_run('php','include/expire.php');
proc_run('php','include/cli_suggest.php');
+ require_once('include/hubloc.php');
+ remove_obsolete_hublocs();
+
/**
* End Cron Daily
*/
@@ -177,13 +208,16 @@ function poller_run($argv, $argc){
// This should be rare
$r = q("select xchan_photo_l, xchan_hash from xchan where xchan_photo_l != '' and xchan_photo_m = ''
- and xchan_photo_date < UTC_TIMESTAMP() - INTERVAL 1 DAY");
+ and xchan_photo_date < %s - INTERVAL %s",
+ db_utcnow(),
+ db_quoteinterval('1 DAY')
+ );
if($r) {
require_once('include/photo/photo_driver.php');
foreach($r as $rr) {
$photos = import_profile_photo($rr['xchan_photo_l'],$rr['xchan_hash']);
$x = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s'
- where xchan_hash = '%s' limit 1",
+ where xchan_hash = '%s'",
dbesc($photos[0]),
dbesc($photos[1]),
dbesc($photos[2]),
@@ -222,13 +256,13 @@ function poller_run($argv, $argc){
}
- $sql_extra = (($manual_id) ? " AND abook_id = $manual_id " : "");
+ $sql_extra = (($manual_id) ? " AND abook_id = " . intval($manual_id) . " " : "");
reload_plugins();
$d = datetime_convert();
- //TODO check to see if there are any cronhooks before wasting a process
+ // TODO check to see if there are any cronhooks before wasting a process
if(! $restart)
proc_run('php','include/cronhooks.php');
@@ -236,16 +270,17 @@ function poller_run($argv, $argc){
// Only poll from those with suitable relationships
$abandon_sql = (($abandon_days)
- ? sprintf(" AND account_lastlog > UTC_TIMESTAMP() - INTERVAL %d DAY ", intval($abandon_days))
+ ? sprintf(" AND account_lastlog > %s - INTERVAL %s ", db_utcnow(), db_quoteinterval(intval($abandon_days).' DAY'))
: ''
);
-
- $contacts = q("SELECT abook_id, abook_flags, abook_updated, abook_connected, abook_closeness, abook_channel
- FROM abook LEFT JOIN account on abook_account = account_id where 1
+ $randfunc = db_getfunc('RAND');
+
+ $contacts = q("SELECT abook_id, abook_flags, abook_updated, abook_connected, abook_closeness, abook_xchan, abook_channel, xchan_network
+ FROM abook LEFT JOIN xchan on abook_xchan = xchan_hash LEFT JOIN account on abook_account = account_id
$sql_extra
- AND (( abook_flags & %d ) OR ( abook_flags = %d ))
- AND (( account_flags = %d ) OR ( account_flags = %d )) $abandon_sql ORDER BY RAND()",
+ AND (( abook_flags & %d ) > 0 OR ( abook_flags = %d ))
+ AND (( account_flags = %d ) OR ( account_flags = %d )) $abandon_sql ORDER BY $randfunc",
intval(ABOOK_FLAG_HIDDEN|ABOOK_FLAG_PENDING|ABOOK_FLAG_UNCONNECTED|ABOOK_FLAG_FEED),
intval(0),
intval(ACCOUNT_OK),
@@ -257,6 +292,9 @@ function poller_run($argv, $argc){
foreach($contacts as $contact) {
+ if($contact['abook_flags'] & ABOOK_FLAG_SELF)
+ continue;
+
$update = false;
$t = $contact['abook_updated'];
@@ -278,15 +316,19 @@ function poller_run($argv, $argc){
}
+ if($contact['xchan_network'] !== 'zot')
+ continue;
+
if($c == $t) {
if(datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 day"))
$update = true;
}
else {
+
// if we've never connected with them, start the mark for death countdown from now
- if($c == '0000-00-00 00:00:00') {
- $r = q("update abook set abook_connected = '%s' where abook_id = %d limit 1",
+ if($c == NULL_DATE) {
+ $r = q("update abook set abook_connected = '%s' where abook_id = %d",
dbesc(datetime_convert()),
intval($contact['abook_id'])
);
@@ -297,7 +339,7 @@ function poller_run($argv, $argc){
// He's dead, Jim
if(strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $c . " + 30 day")) > 0) {
- $r = q("update abook set abook_flags = (abook_flags | %d) where abook_id = %d limit 1",
+ $r = q("update abook set abook_flags = (abook_flags | %d) where abook_id = %d",
intval(ABOOK_FLAG_ARCHIVED),
intval($contact['abook_id'])
);
@@ -324,9 +366,11 @@ function poller_run($argv, $argc){
$update = true;
}
-
}
+ if($contact['abook_flags'] & (ABOOK_FLAG_PENDING|ABOOK_FLAG_ARCHIVED|ABOOK_FLAG_IGNORED))
+ continue;
+
if((! $update) && (! $force))
continue;
@@ -338,8 +382,10 @@ function poller_run($argv, $argc){
}
if($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
- $r = q("select distinct ud_addr, updates.* from updates where not ( ud_flags & %d ) and ud_addr != '' and ( ud_last = '0000-00-00 00:00:00' OR ud_last > UTC_TIMESTAMP() - INTERVAL 7 DAY ) group by ud_addr ",
- intval(UPDATE_FLAGS_UPDATED)
+ $r = q("select distinct ud_addr, updates.* from updates where ( ud_flags & %d ) = 0 and ud_addr != '' and ( ud_last = '%s' OR ud_last > %s - INTERVAL %s ) group by ud_addr ",
+ intval(UPDATE_FLAGS_UPDATED),
+ dbesc(NULL_DATE),
+ db_utcnow(), db_quoteinterval('7 DAY')
);
if($r) {
foreach($r as $rr) {
@@ -347,7 +393,7 @@ function poller_run($argv, $argc){
// If they didn't respond when we attempted before, back off to once a day
// After 7 days we won't bother anymore
- if($rr['ud_last'] != '0000-00-00 00:00:00')
+ if($rr['ud_last'] != NULL_DATE)
if($rr['ud_last'] > datetime_convert('UTC','UTC', 'now - 1 day'))
continue;
proc_run('php','include/onedirsync.php',$rr['ud_id']);
@@ -356,7 +402,10 @@ function poller_run($argv, $argc){
}
}
}
-
+
+ //All done - clear the lockfile
+ @unlink($lockfile);
+
return;
}
diff --git a/include/queue.php b/include/queue.php
index 222ebada4..b6a540ef9 100644
--- a/include/queue.php
+++ b/include/queue.php
@@ -22,7 +22,9 @@ function queue_run($argv, $argc){
logger('queue: start');
- $r = q("DELETE FROM outq WHERE outq_created < UTC_TIMESTAMP() - INTERVAL 3 DAY");
+ $r = q("DELETE FROM outq WHERE outq_created < %s - INTERVAL %s",
+ db_utcnow(), db_quoteinterval('3 DAY')
+ );
if($queue_id) {
$r = q("SELECT * FROM outq WHERE outq_hash = '%s' LIMIT 1",
@@ -37,8 +39,18 @@ function queue_run($argv, $argc){
// so that we don't start off a thousand deliveries for a couple of dead hubs.
// The zot driver will deliver everything destined for a single hub once contact is made (*if* contact is made).
// Other drivers will have to do something different here and may need their own query.
-
- $r = q("SELECT * FROM outq WHERE outq_delivered = 0 and (( outq_created > UTC_TIMESTAMP() - INTERVAL 12 HOUR and outq_updated < UTC_TIMESTAMP() - INTERVAL 15 MINUTE ) OR ( outq_updated < UTC_TIMESTAMP() - INTERVAL 1 HOUR )) group by outq_posturl");
+ if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
+ $prefix = 'DISTINCT ON (outq_posturl)';
+ $suffix = 'ORDER BY outq_posturl';
+ } else {
+ $prefix = '';
+ $suffix = 'GROUP BY outq_posturl';
+ }
+ $r = q("SELECT $prefix * FROM outq WHERE outq_delivered = 0 and (( outq_created > %s - INTERVAL %s and outq_updated < %s - INTERVAL %s ) OR ( outq_updated < %s - INTERVAL %s )) $suffix",
+ db_utcnow(), db_quoteinterval('12 HOUR'),
+ db_utcnow(), db_quoteinterval('15 MINUTE'),
+ db_utcnow(), db_quoteinterval('1 HOUR')
+ );
}
if(! $r)
return;
@@ -51,13 +63,13 @@ function queue_run($argv, $argc){
$result = z_post_url($rr['outq_posturl'],$rr['outq_msg']);
if($result['success'] && $result['return_code'] < 300) {
logger('queue: queue post success to ' . $rr['outq_posturl'], LOGGER_DEBUG);
- $y = q("delete from outq where outq_hash = '%s' limit 1",
+ $y = q("delete from outq where outq_hash = '%s'",
dbesc($rr['ouq_hash'])
);
}
else {
logger('queue: queue post returned ' . $result['return_code'] . ' from ' . $rr['outq_posturl'],LOGGER_DEBUG);
- $y = q("update outq set outq_updated = '%s' where outq_hash = '%s' limit 1",
+ $y = q("update outq set outq_updated = '%s' where outq_hash = '%s'",
dbesc(datetime_convert()),
dbesc($rr['outq_hash'])
);
@@ -70,7 +82,7 @@ function queue_run($argv, $argc){
}
else {
$deadguys[] = $rr['outq_posturl'];
- $y = q("update outq set outq_updated = '%s' where outq_hash = '%s' limit 1",
+ $y = q("update outq set outq_updated = '%s' where outq_hash = '%s'",
dbesc(datetime_convert()),
dbesc($rr['outq_hash'])
);
diff --git a/include/queue_fn.php b/include/queue_fn.php
index 512edb531..22580bc48 100644
--- a/include/queue_fn.php
+++ b/include/queue_fn.php
@@ -2,7 +2,7 @@
function update_queue_time($id) {
logger('queue: requeue item ' . $id);
- q("UPDATE outq SET outq_updated = '%s' WHERE outq_hash = '%s' LIMIT 1",
+ q("UPDATE outq SET outq_updated = '%s' WHERE outq_hash = '%s'",
dbesc(datetime_convert()),
dbesc($id)
);
@@ -10,7 +10,7 @@ function update_queue_time($id) {
function remove_queue_item($id) {
logger('queue: remove queue item ' . $id);
- q("DELETE FROM outq WHERE hash = '%s' LIMIT 1",
+ q("DELETE FROM outq WHERE hash = '%s'",
dbesc($id)
);
}
diff --git a/include/reddav.php b/include/reddav.php
index c4ef5bd08..750ca1b24 100644
--- a/include/reddav.php
+++ b/include/reddav.php
@@ -1,713 +1,31 @@
<?php
/**
* @file include/reddav.php
- * @brief DAV related classes from SabreDAV for Red Matrix.
+ * @brief some DAV related functions for RedMatrix.
*
- * This file contains the classes from SabreDAV that got extended to adapt it
- * for Red Matrix.
+ * This file contains some functions which did not fit into one of the RedDAV
+ * classes.
*
- * You find the original SabreDAV classes under @ref vendor/sabre/dav/.
+ * The extended SabreDAV classes you will find in the RedDAV namespace under
+ * @ref includes/RedDAV/.
+ * The original SabreDAV classes you can find under @ref vendor/sabre/dav/.
* We need to use SabreDAV 1.8.x for PHP5.3 compatibility. SabreDAV >= 2.0
* requires PHP >= 5.4.
- */
-
-use Sabre\DAV;
-require_once('vendor/autoload.php');
-require_once('include/attach.php');
-
-
-/**
- * @brief RedDirectory class.
*
- * A class that represents a directory.
- */
-class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
-
- /**
- * @brief The path inside /cloud
- */
- 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
- */
- private $ext_path;
- private $root_dir = '';
- private $auth;
- /**
- * @brief The real path on the filesystem.
- * The actual path in store/ with the hashed names.
- */
- 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) {
- logger('RedDirectory::__construct() ' . $ext_path, LOGGER_DATA);
- $this->ext_path = $ext_path;
- // remove "/cloud" from the beginning of the path
- $this->red_path = ((strpos($ext_path, '/cloud') === 0) ? substr($ext_path, 6) : $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('RedDirectory::log() ext_path ' . $this->ext_path, LOGGER_DATA);
- logger('RedDirectory::log() os_path ' . $this->os_path, LOGGER_DATA);
- logger('RedDirectory::log() red_path ' . $this->red_path, LOGGER_DATA);
- }
-
- /**
- * @brief Returns an array with all the child nodes.
- *
- * @throws DAV\Exception\Forbidden
- * @return array DAV\INode[]
- */
- public function getChildren() {
- logger('RedDirectory::getChildren() called 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 DAV\Exception\Forbidden
- * @throw DAV\Exception\NotFound
- * @param string $name
- */
- public function getChild($name) {
- logger('RedDirectory::getChild(): ' . $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.');
- }
-
- if ($this->red_path === '/' && $name === 'cloud') {
- return new RedDirectory('/cloud', $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('RedDirectory::getName() returns: ' . basename($this->red_path), LOGGER_DATA);
- return (basename($this->red_path));
- }
-
- /**
- * @brief Renames the directory.
- *
- * @todo handle duplicate directory name
- *
- * @throw DAV\Exception\Forbidden
- * @param string $name The new name of the directory.
- * @return void
- */
- public function setName($name) {
- logger('RedDirectory::setName(): ' . basename($this->red_path) . ' -> ' . $name, LOGGER_DATA);
-
- if ((! $name) || (! $this->auth->owner_id)) {
- logger('RedDirectory::setName(): permission denied');
- throw new DAV\Exception\Forbidden('Permission denied.');
- }
-
- if (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) {
- logger('RedDirectory::setName(): permission denied');
- 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 LIMIT 1",
- 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.
- *
- * @throws 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('RedDirectory::createFile(): ' . $name, LOGGER_DATA);
-
- if (! $this->auth->owner_id) {
- logger('RedDirectory::createFile(): permission denied');
- throw new DAV\Exception\Forbidden('Permission denied.');
- }
-
- if (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) {
- logger('RedDirectory::createFile(): permission denied');
- throw new DAV\Exception\Forbidden('Permission denied.');
- }
-
- $mimetype = z_mime_content_type($name);
-
- $c = q("SELECT * FROM channel WHERE channel_id = %d AND NOT (channel_pageflags & %d) LIMIT 1",
- intval($this->auth->owner_id),
- intval(PAGE_REMOVED)
- );
-
- if (! $c) {
- logger('RedDirectory::createFile(): no channel');
- throw new DAV\Exception\Forbidden('Permission denied.');
- }
-
- $filesize = 0;
- $hash = random_string();
-
- $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, folder, flags, filetype, filesize, revision, data, created, edited, allow_cid, allow_gid, deny_cid, deny_gid )
- VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', %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),
- dbesc(ATTACH_FLAG_OS),
- dbesc($mimetype),
- intval($filesize),
- intval(0),
- dbesc($this->os_path . '/' . $hash),
- dbesc(datetime_convert()),
- dbesc(datetime_convert()),
- dbesc($c[0]['channel_allow_cid']),
- dbesc($c[0]['channel_allow_gid']),
- dbesc($c[0]['channel_deny_cid']),
- dbesc($c[0]['channel_deny_gid'])
- );
-
- $f = 'store/' . $this->auth->owner_nick . '/' . (($this->os_path) ? $this->os_path . '/' : '') . $hash;
-
- // 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('RedDirectory::createFile(): file_put_contents() failed for ' . $name, LOGGER_DEBUG);
- attach_delete($c[0]['channel_id'], $hash);
- return;
- }
-
- // returns now
- $edited = datetime_convert();
-
- // updates entry with filesize and timestamp
- $d = q("UPDATE attach SET filesize = '%s', edited = '%s' WHERE hash = '%s' AND uid = %d LIMIT 1",
- dbesc($size),
- 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 LIMIT 1",
- 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('reddav: 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;
- }
- }
- }
-
- /**
- * @brief Creates a new subdirectory.
- *
- * @param string $name the directory to create
- * @return void
- */
- public function createDirectory($name) {
- logger('RedDirectory::createDirectory(): ' . $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 NOT (channel_pageflags & %d) LIMIT 1",
- intval($this->auth->owner_id),
- intval(PAGE_REMOVED)
- );
-
- if ($r) {
- $result = attach_mkdir($r[0], $this->auth->observer, array('filename' => $name, 'folder' => $this->folder_hash));
- if (! $result['success']) {
- logger('RedDirectory::createDirectory(): ' . print_r($result, true), LOGGER_DEBUG);
- }
- }
- }
-
- /**
- * @brief Checks if a child exists.
- *
- * @param string $name
- * @return boolean
- */
- public function childExists($name) {
- // On /cloud we show a list of available channels.
- // @todo what happens if no channels are available?
- if ($this->red_path === '/' && $name === 'cloud') {
- logger('RedDirectory::childExists() /cloud: true', LOGGER_DATA);
- 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 DAV\Exception\NotFound
- * @return void
- */
- function getDir() {
- logger('RedDirectory::getDir(): ' . $this->ext_path, LOGGER_DEBUG);
- $this->auth->log();
-
- $file = $this->ext_path;
-
- $x = strpos($file, '/cloud');
- if ($x === false)
- return;
- if ($x === 0) {
- $file = substr($file, 6);
- }
-
- if ((! $file) || ($file === '/')) {
- return;
- }
-
- $file = trim($file, '/');
- $path_arr = explode('/', $file);
-
- if (! $path_arr)
- return;
-
- logger('RedDirectory::getDir(): path: ' . print_r($path_arr, true), LOGGER_DATA);
-
- $channel_name = $path_arr[0];
-
- $r = q("SELECT channel_id FROM channel WHERE channel_address = '%s' AND NOT ( channel_pageflags & %d ) LIMIT 1",
- dbesc($channel_name),
- intval(PAGE_REMOVED)
- );
-
- if (! $r) {
- throw new DAV\Exception\NotFound('The file with name: ' . $channel_name . ' could not be found.');
- return;
- }
-
- $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 from attach where folder = '%s' and filename = '%s' and uid = %d and (flags & %d)",
- dbesc($folder),
- dbesc($path_arr[$x]),
- intval($channel_id),
- intval(ATTACH_FLAG_DIR)
- );
-
- if ($r && ( $r[0]['flags'] & ATTACH_FLAG_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;
- return;
- }
-
- /**
- * @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.
- *
- * Do 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 not (channel_pageflags & %d) limit 1",
- intval($this->auth->owner_id),
- intval(PAGE_REMOVED)
- );
-
- $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
- );
- }
-} // class RedDirectory
-
-
-
-/**
- * RedFile class.
+ * @todo split up the classes into own files.
*
+ * @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 {
-
- private $data;
- private $auth;
- 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('RedFile::__construct(): ' . print_r($this->data, true), LOGGER_DATA);
- }
-
- /**
- * @brief Returns the name of the file.
- *
- * @return string
- */
- public function getName() {
- logger('RedFile::getName(): ' . basename($this->name), LOGGER_DEBUG);
- return basename($this->name);
- }
- /**
- * @brief Renames the file.
- *
- * @throw DAV\Exception\Forbidden
- * @param string $name The new name of the file.
- * @return void
- */
- public function setName($newName) {
- logger('RedFile::setName(): ' . basename($this->name) . ' -> ' . $newName, LOGGER_DEBUG);
-
- if ((! $newName) || (! $this->auth->owner_id) || (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage'))) {
- 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 LIMIT 1",
- dbesc($this->data['filename']),
- intval($this->data['id'])
- );
- }
-
- /**
- * @brief Updates the data of the file.
- *
- * @param resource $data
- * @return void
- */
- public function put($data) {
- logger('RedFile::put(): ' . basename($this->name), LOGGER_DEBUG);
- $size = 0;
-
- // @todo only 3 values are needed
- $c = q("SELECT * FROM channel WHERE channel_id = %d AND NOT (channel_pageflags & %d) LIMIT 1",
- intval($this->auth->owner_id),
- intval(PAGE_REMOVED)
- );
-
- $r = q("SELECT flags, folder, data FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1",
- dbesc($this->data['hash']),
- intval($c[0]['channel_id'])
- );
- if ($r) {
- if ($r[0]['flags'] & ATTACH_FLAG_OS) {
- $f = 'store/' . $this->auth->owner_nick . '/' . (($r[0]['data']) ? $r[0]['data'] : '');
- // @todo check return value and set $size directly
- @file_put_contents($f, $data);
- $size = @filesize($f);
- logger('RedFile::put(): filename: ' . $f . ' size: ' . $size, LOGGER_DEBUG);
- } else {
- $r = q("UPDATE attach SET data = '%s' WHERE hash = '%s' AND uid = %d LIMIT 1",
- dbesc(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', edited = '%s' WHERE hash = '%s' AND uid = %d LIMIT 1",
- dbesc($size),
- dbesc($edited),
- dbesc($this->data['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 LIMIT 1",
- 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('RedFile::put(): 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('RedFile::get(): ' . basename($this->name), LOGGER_DEBUG);
-
- $r = q("select data, flags, 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 ($r[0]['flags'] & ATTACH_FLAG_OS ) {
- $f = 'store/' . $this->auth->owner_nick . '/' . (($this->os_path) ? $this->os_path . '/' : '') . $r[0]['data'];
- return fopen($f, 'rb');
- }
- return $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 mixed
- */
- 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
- */
- 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.
- *
- * @throw DAV\Exception\Forbidden
- * @return void
- */
- public function delete() {
- logger('RedFile::delete(): ' . 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']) || ($this->data['flags'] & ATTACH_FLAG_DIR)) {
- throw new DAV\Exception\Forbidden('Permission denied.');
- }
- }
-
- attach_delete($this->auth->owner_id, $this->data['hash']);
- }
-} // class RedFile
+use Sabre\DAV;
+use RedMatrix\RedDAV;
+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');
/**
* @brief Returns an array with viewable channels.
@@ -716,14 +34,15 @@ class RedFile extends DAV\Node implements DAV\IFile {
* has <b>view_storage</b> perms.
*
* @todo Is there any reason why this is not inside RedDirectory class?
+ * @fixme function name looks like a class name, should we rename it?
*
- * @param $auth
- * @return array containing RedDirectory objects
+ * @param RedBasicAuth &$auth
+ * @return array RedDirectory[]
*/
function RedChannelList(&$auth) {
$ret = array();
- $r = q("SELECT channel_id, channel_address FROM channel WHERE NOT (channel_pageflags & %d) AND NOT (channel_pageflags & %d)",
+ $r = q("SELECT channel_id, channel_address FROM channel WHERE NOT (channel_pageflags & %d)>0 AND NOT (channel_pageflags & %d)>0",
intval(PAGE_REMOVED),
intval(PAGE_HIDDEN)
);
@@ -731,9 +50,9 @@ function RedChannelList(&$auth) {
if ($r) {
foreach ($r as $rr) {
if (perm_is_allowed($rr['channel_id'], $auth->observer, 'view_storage')) {
- logger('RedChannelList: ' . '/cloud/' . $rr['channel_address'], LOGGER_DATA);
+ logger('found channel: /cloud/' . $rr['channel_address'], LOGGER_DATA);
// @todo can't we drop '/cloud'? It gets stripped off anyway in RedDirectory
- $ret[] = new RedDirectory('/cloud/' . $rr['channel_address'], $auth);
+ $ret[] = new RedDAV\RedDirectory('/cloud/' . $rr['channel_address'], $auth);
}
}
}
@@ -746,11 +65,15 @@ function RedChannelList(&$auth) {
*
* Array with all RedDirectory and RedFile DAV\Node items for the given path.
*
- * @todo Is there any reason why this is not inside RedDirectory class? Seems only to be used there and we could simplify it a bit there.
+ * @todo Is there any reason why this is not inside RedDirectory class? Seems
+ * only to be used there and we could simplify it a bit there.
+ * @fixme function name looks like a class name, should we rename it?
*
* @param string $file path to a directory
- * @param &$auth
- * @returns array DAV\INode[]
+ * @param RedBasicAuth &$auth
+ * @returns null|array \Sabre\DAV\INode[]
+ * @throw \Sabre\DAV\Exception\Forbidden
+ * @throw \Sabre\DAV\Exception\NotFound
*/
function RedCollectionData($file, &$auth) {
$ret = array();
@@ -767,7 +90,7 @@ function RedCollectionData($file, &$auth) {
$file = trim($file, '/');
$path_arr = explode('/', $file);
-
+
if (! $path_arr)
return null;
@@ -792,7 +115,7 @@ function RedCollectionData($file, &$auth) {
$permission_error = false;
for ($x = 1; $x < count($path_arr); $x++) {
- $r = q("SELECT id, hash, filename, flags FROM attach WHERE folder = '%s' AND filename = '%s' AND uid = %d AND (flags & %d) $perms LIMIT 1",
+ $r = q("SELECT id, hash, filename, flags FROM attach WHERE folder = '%s' AND filename = '%s' AND uid = %d AND (flags & %d)>0 $perms LIMIT 1",
dbesc($folder),
dbesc($path_arr[$x]),
intval($channel_id),
@@ -801,7 +124,7 @@ function RedCollectionData($file, &$auth) {
if (! $r) {
// path wasn't found. Try without permissions to see if it was the result of permissions.
$errors = true;
- $r = q("select id, hash, filename, flags from attach where folder = '%s' and filename = '%s' and uid = %d and (flags & %d) limit 1",
+ $r = q("select id, hash, filename, flags from attach where folder = '%s' and filename = '%s' and uid = %d and (flags & %d)>0 limit 1",
dbesc($folder),
basename($path_arr[$x]),
intval($channel_id),
@@ -829,23 +152,28 @@ function RedCollectionData($file, &$auth) {
// This should no longer be needed since we just returned errors for paths not found
if ($path !== '/' . $file) {
- logger("RedCollectionData: Path mismatch: $path !== /$file");
+ logger("Path mismatch: $path !== /$file");
return NULL;
}
-
- $r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, created, edited from attach where folder = '%s' and uid = %d $perms group by filename",
+ if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
+ $prefix = 'DISTINCT ON (filename)';
+ $suffix = 'ORDER BY filename';
+ } else {
+ $prefix = '';
+ $suffix = 'GROUP BY filename';
+ }
+ $r = q("select $prefix id, uid, hash, filename, filetype, filesize, revision, folder, flags, created, edited from attach where folder = '%s' and uid = %d $perms $suffix",
dbesc($folder),
intval($channel_id)
);
foreach ($r as $rr) {
- logger('RedCollectionData: filename: ' . $rr['filename'], LOGGER_DATA);
-
+ //logger('filename: ' . $rr['filename'], LOGGER_DEBUG);
if ($rr['flags'] & ATTACH_FLAG_DIR) {
// @todo can't we drop '/cloud'? it gets stripped off anyway in RedDirectory
- $ret[] = new RedDirectory('/cloud' . $path . '/' . $rr['filename'], $auth);
+ $ret[] = new RedDAV\RedDirectory('/cloud' . $path . '/' . $rr['filename'], $auth);
} else {
- $ret[] = new RedFile('/cloud' . $path . '/' . $rr['filename'], $rr, $auth);
+ $ret[] = new RedDAV\RedFile('/cloud' . $path . '/' . $rr['filename'], $rr, $auth);
}
}
@@ -856,12 +184,17 @@ function RedCollectionData($file, &$auth) {
/**
* @brief TODO What exactly is this function for?
*
+ * @fixme function name looks like a class name, should we rename it?
+ *
* @param string $file
- * @param &$auth
+ * path to file or directory
+ * @param RedBasicAuth &$auth
* @param boolean $test (optional) enable test mode
+ * @return RedFile|RedDirectory|boolean|null
+ * @throw \Sabre\DAV\Exception\Forbidden
*/
function RedFileData($file, &$auth, $test = false) {
- logger('RedFileData:' . $file . (($test) ? ' (test mode) ' : ''), LOGGER_DEBUG);
+ logger($file . (($test) ? ' (test mode) ' : ''), LOGGER_DATA);
$x = strpos($file, '/cloud');
if ($x === 0) {
@@ -869,13 +202,13 @@ function RedFileData($file, &$auth, $test = false) {
}
if ((! $file) || ($file === '/')) {
- return new RedDirectory('/', $auth);
+ return new RedDAV\RedDirectory('/', $auth);
}
$file = trim($file, '/');
$path_arr = explode('/', $file);
-
+
if (! $path_arr)
return null;
@@ -904,7 +237,7 @@ function RedFileData($file, &$auth, $test = false) {
$errors = false;
for ($x = 1; $x < count($path_arr); $x++) {
- $r = q("select id, hash, filename, flags from attach where folder = '%s' and filename = '%s' and uid = %d and (flags & %d) $perms",
+ $r = q("select id, hash, filename, flags from attach where folder = '%s' and filename = '%s' and uid = %d and (flags & %d)>0 $perms",
dbesc($folder),
dbesc($path_arr[$x]),
intval($channel_id),
@@ -914,10 +247,10 @@ function RedFileData($file, &$auth, $test = false) {
if ($r && ( $r[0]['flags'] & ATTACH_FLAG_DIR)) {
$folder = $r[0]['hash'];
$path = $path . '/' . $r[0]['filename'];
- }
+ }
if (! $r) {
$r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, created, edited from attach
- where folder = '%s' and filename = '%s' and uid = %d $perms group by filename limit 1",
+ where folder = '%s' and filename = '%s' and uid = %d $perms order by filename limit 1",
dbesc($folder),
dbesc(basename($file)),
intval($channel_id)
@@ -926,7 +259,7 @@ function RedFileData($file, &$auth, $test = false) {
if (! $r) {
$errors = true;
$r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, created, edited from attach
- where folder = '%s' and filename = '%s' and uid = %d group by filename limit 1",
+ where folder = '%s' and filename = '%s' and uid = %d order by filename limit 1",
dbesc($folder),
dbesc(basename($file)),
intval($channel_id)
@@ -940,15 +273,15 @@ function RedFileData($file, &$auth, $test = false) {
if ($test)
return true;
// final component was a directory.
- return new RedDirectory('/cloud/' . $file, $auth);
+ return new RedDAV\RedDirectory('/cloud/' . $file, $auth);
}
if ($errors) {
- logger('RedFileData: not found');
+ logger('not found ' . $file);
if ($test)
return false;
if ($permission_error) {
- logger('RedFileData: permission error');
+ logger('permission error ' . $file);
throw new DAV\Exception\Forbidden('Permission denied.');
}
return;
@@ -960,431 +293,10 @@ function RedFileData($file, &$auth, $test = false) {
if ($r[0]['flags'] & ATTACH_FLAG_DIR) {
// @todo can't we drop '/cloud'? it gets stripped off anyway in RedDirectory
- return new RedDirectory('/cloud' . $path . '/' . $r[0]['filename'], $auth);
+ return new RedDAV\RedDirectory('/cloud' . $path . '/' . $r[0]['filename'], $auth);
} else {
- return new RedFile('/cloud' . $path . '/' . $r[0]['filename'], $r[0], $auth);
+ return new RedDAV\RedFile('/cloud' . $path . '/' . $r[0]['filename'], $r[0], $auth);
}
}
return false;
-}
-
-
-
-/**
- * RedBasicAuth class.
- *
- */
-class RedBasicAuth extends DAV\Auth\Backend\AbstractBasic {
-
- // @fixme mod/cloud.php:61
- public $channel_name = '';
- // @fixme mod/cloud.php:62
- public $channel_id = 0;
- // @fixme mod/cloud.php:63
- public $channel_hash = '';
- // @fixme mod/cloud.php:68
- public $observer = '';
- // @fixme include/reddav.php:51
- public $browser;
- // @fixme include/reddav.php:92
- public $owner_id;
- // @fixme include/reddav.php:283
- public $owner_nick = '';
- // @fixme mod/cloud.php:66
- public $timezone;
-
- /**
- *
- * @param string $username
- * @param string $password
- */
- protected function validateUserPass($username, $password) {
-
- if (trim($password) === '+++') {
- logger('reddav: 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",
- 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;
- }
- }
- $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",
- intval($r[0]['channel_account_id'])
- );
- if ($x) {
- 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;
- }
- }
- }
- }
- logger('(DAV) RedBasicAuth: password failed for ' . $username);
- return false;
- }
-
- public function setCurrentUser($name) {
- $this->currentUser = $name;
- }
-
- /**
- * @brief Set browser plugin.
- *
- * @see RedBrowser::set_writeable()
- * @param DAV\Browser\Plugin $browser
- */
- 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;
- }
- }
-
- /**
- * @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->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;
- }
-
- /**
- * Creates a form to add new folders and upload files.
- *
- * @param DAV\INode $node
- * @param string &$output
- */
- 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')
- ));
- }
-
- /**
- * 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);
- }
-
- 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;
- }
-
- 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;
- }
-
-} // class RedBrowser
+} \ No newline at end of file
diff --git a/include/security.php b/include/security.php
index 2ccfc6973..07b6a96aa 100644
--- a/include/security.php
+++ b/include/security.php
@@ -1,18 +1,30 @@
-<?php /** @file */
-
-function authenticate_success($user_record, $login_initial = false, $interactive = false,$return = false,$update_lastlog = false) {
+<?php
+/**
+ * @file include/security.php
+ *
+ * Some security related functions.
+ */
+
+/**
+ * @param int $user_record The account_id
+ * @param bool $login_initial default false
+ * @param bool $interactive default false
+ * @param bool $return
+ * @param bool $update_lastlog
+ */
+function authenticate_success($user_record, $login_initial = false, $interactive = false, $return = false, $update_lastlog = false) {
$a = get_app();
$_SESSION['addr'] = $_SERVER['REMOTE_ADDR'];
- if(x($user_record,'account_id')) {
+ 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 limit 1",
+ q("update account set account_lastlog = '%s' where account_id = %d",
dbesc(datetime_convert()),
intval($_SESSION['account_id'])
);
@@ -39,13 +51,19 @@ function authenticate_success($user_record, $login_initial = false, $interactive
// might want to log success here
}
- if($return || x($_SESSION,'workflow')) {
+ if($return || x($_SESSION, 'workflow')) {
unset($_SESSION['workflow']);
return;
}
if(($a->module !== 'home') && x($_SESSION,'login_return_url') && strlen($_SESSION['login_return_url'])) {
$return_url = $_SESSION['login_return_url'];
+
+ // don't let members get redirected to a raw ajax page update - this can happen
+ // if DHCP changes the IP address at an unfortunate time and paranoia is turned on
+ if(strstr($return_url,'update_'))
+ $return_url = '';
+
unset($_SESSION['login_return_url']);
goaway($a->get_baseurl() . '/' . $return_url);
}
@@ -53,7 +71,7 @@ function authenticate_success($user_record, $login_initial = false, $interactive
/* This account has never created a channel. Send them to new_channel by default */
if($a->module === 'login') {
- $r = q("select count(channel_id) as total from channel where channel_account_id = %d and not ( channel_pageflags & %d)",
+ $r = q("select count(channel_id) as total from channel where channel_account_id = %d and not ( channel_pageflags & %d)>0",
intval($a->account['account_id']),
intval(PAGE_REMOVED)
);
@@ -64,18 +82,37 @@ function authenticate_success($user_record, $login_initial = false, $interactive
/* else just return */
}
-
+/**
+ * @brief Change to another channel with current logged-in account.
+ *
+ * @param int $change_channel The channel_id of the channel you want to change to
+ *
+ * @return bool|array false or channel record of the new channel
+ */
function change_channel($change_channel) {
$ret = false;
if($change_channel) {
- $r = q("select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel_id = %d and channel_account_id = %d and not ( channel_pageflags & %d) limit 1",
+ $r = q("select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel_id = %d and channel_account_id = %d and not ( channel_pageflags & %d)>0 limit 1",
intval($change_channel),
intval(get_account_id()),
intval(PAGE_REMOVED)
);
+ // It's not there. Is this an administrator, and is this the sys channel?
+ if (is_developer()) {
+ if (! $r) {
+ if (is_site_admin()) {
+ $r = q("select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel_id = %d and ( channel_pageflags & %d) and not (channel_pageflags & %d )>0 limit 1",
+ intval($change_channel),
+ intval(PAGE_SYSTEM),
+ intval(PAGE_REMOVED)
+ );
+ }
+ }
+ }
+
if($r) {
$hash = $r[0]['channel_hash'];
$_SESSION['uid'] = intval($r[0]['channel_id']);
@@ -90,23 +127,28 @@ function change_channel($change_channel) {
);
if($x) {
$_SESSION['my_url'] = $x[0]['xchan_url'];
- $_SESSION['my_address'] = $r[0]['channel_address'] . '@' . substr(get_app()->get_baseurl(),strpos(get_app()->get_baseurl(),'://')+3);
+ $_SESSION['my_address'] = $r[0]['channel_address'] . '@' . substr(get_app()->get_baseurl(), strpos(get_app()->get_baseurl(), '://') + 3);
get_app()->set_observer($x[0]);
- get_app()->set_perms(get_all_perms(local_user(),$hash));
+ get_app()->set_perms(get_all_perms(local_user(), $hash));
}
if(! is_dir('store/' . $r[0]['channel_address']))
@os_mkdir('store/' . $r[0]['channel_address'], STORAGE_DEFAULT_PERMISSIONS,true);
-
}
return $ret;
-
}
-
-
-function permissions_sql($owner_id,$remote_verified = false,$groups = null) {
+/**
+ * @brief Creates an addiontal SQL where statement to check permissions.
+ *
+ * @param int $owner_id
+ * @param bool $remote_verified default false, not used at all
+ * @param string $groups this param is not used at all
+ *
+ * @return string additional SQL where statement
+ */
+function permissions_sql($owner_id, $remote_verified = false, $groups = null) {
if(defined('STATUSNET_PRIVACY_COMPATIBILITY'))
return '';
@@ -123,8 +165,7 @@ function permissions_sql($owner_id,$remote_verified = false,$groups = null) {
$sql = " AND allow_cid = ''
AND allow_gid = ''
AND deny_cid = ''
- AND deny_gid = ''
-
+ AND deny_gid = ''
";
/**
@@ -143,7 +184,6 @@ function permissions_sql($owner_id,$remote_verified = false,$groups = null) {
* done this and passed the groups into this function.
*/
-
else {
$observer = get_observer_hash();
if($observer) {
@@ -155,9 +195,10 @@ function permissions_sql($owner_id,$remote_verified = false,$groups = null) {
foreach($groups as $g)
$gs .= '|<' . $g . '>';
}
+ $regexop = db_getfunc('REGEXP');
$sql = sprintf(
- " AND ( NOT (deny_cid like '%s' OR deny_gid REGEXP '%s')
- AND ( allow_cid like '%s' OR allow_gid REGEXP '%s' OR ( allow_cid = '' AND allow_gid = '') )
+ " 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 = '') )
)
",
dbesc(protect_sprintf( '%<' . $observer . '>%')),
@@ -171,7 +212,16 @@ function permissions_sql($owner_id,$remote_verified = false,$groups = null) {
return $sql;
}
-function item_permissions_sql($owner_id,$remote_verified = false,$groups = null) {
+/**
+ * @brief Creates an addiontal SQL where statement to check permissions for an item.
+ *
+ * @param int $owner_id
+ * @param bool $remote_verified default false, not used at all
+ * @param string $groups this param is not used at all
+ *
+ * @return string additional SQL where statement
+ */
+function item_permissions_sql($owner_id, $remote_verified = false, $groups = null) {
if(defined('STATUSNET_PRIVACY_COMPATIBILITY'))
return '';
@@ -185,8 +235,7 @@ function item_permissions_sql($owner_id,$remote_verified = false,$groups = null)
* default permissions - anonymous user
*/
- $sql = " AND not item_private ";
-
+ $sql = " AND item_private=0 ";
/**
* Profile owner - everything is visible
@@ -204,7 +253,6 @@ function item_permissions_sql($owner_id,$remote_verified = false,$groups = null)
* done this and passed the groups into this function.
*/
-
else {
$observer = get_observer_hash();
@@ -216,10 +264,11 @@ function item_permissions_sql($owner_id,$remote_verified = false,$groups = null)
if(is_array($groups) && count($groups)) {
foreach($groups as $g)
$gs .= '|<' . $g . '>';
- }
+ }
+ $regexop = db_getfunc('REGEXP');
$sql = sprintf(
- " AND ( NOT (deny_cid like '%s' OR deny_gid REGEXP '%s')
- AND ( allow_cid like '%s' OR allow_gid REGEXP '%s' OR ( allow_cid = '' AND allow_gid = '') )
+ " 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 = '') )
)
",
dbesc(protect_sprintf( '%<' . $observer . '>%')),
@@ -229,12 +278,18 @@ function item_permissions_sql($owner_id,$remote_verified = false,$groups = null)
);
}
}
+
return $sql;
}
+/**
+ * @param string $observer_hash
+ *
+ * @return string additional SQL where statement
+ */
function public_permissions_sql($observer_hash) {
- $observer = get_app()->get_observer();
+ //$observer = get_app()->get_observer();
$groups = init_groups_visitor($observer_hash);
$gs = '<<>>'; // should be impossible to match
@@ -242,12 +297,13 @@ function public_permissions_sql($observer_hash) {
if(is_array($groups) && count($groups)) {
foreach($groups as $g)
$gs .= '|<' . $g . '>';
- }
+ }
$sql = '';
if($observer_hash) {
+ $regexop = db_getfunc('REGEXP');
$sql = sprintf(
- " OR (( NOT (deny_cid like '%s' OR deny_gid REGEXP '%s')
- AND ( allow_cid like '%s' OR allow_gid REGEXP '%s' OR ( allow_cid = '' AND allow_gid = '') )
+ " OR (( 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 = '') )
))
",
dbesc(protect_sprintf( '%<' . $observer_hash . '>%')),
@@ -319,11 +375,11 @@ function check_form_security_token_ForbiddenOnErr($typename = '', $formname = 'f
}
}
+
// Returns an array of group id's this contact is a member of.
// This array will only contain group id's related to the uid of this
// DFRN contact. They are *not* neccessarily unique across the entire site.
-
if(! function_exists('init_groups_visitor')) {
function init_groups_visitor($contact_id) {
$groups = array();
@@ -339,8 +395,6 @@ function init_groups_visitor($contact_id) {
-
-
// This is used to determine which uid have posts which are visible to the logged in user (from the API) for the
// public_timeline, and we can use this in a community page by making
// $perms = (PERMS_NETWORK|PERMS_PUBLIC) unless logged in.
@@ -356,23 +410,26 @@ function stream_perms_api_uids($perms = NULL ) {
$ret = array();
if(local_user())
$ret[] = local_user();
- $r = q("select channel_id from channel where channel_r_stream > 0 and (channel_r_stream & %d) and not (channel_pageflags & %d)",
+ $r = q("select channel_id from channel where channel_r_stream > 0 and (channel_r_stream & %d)>0 and not (channel_pageflags & %d)>0",
intval($perms),
- intval(PAGE_CENSORED|PAGE_SYSTEM|PAGE_REMOVED)
+ intval(PAGE_ADULT|PAGE_CENSORED|PAGE_SYSTEM|PAGE_REMOVED)
);
- if($r)
+ if($r) {
foreach($r as $rr)
- if(! in_array($rr['channel_id'],$ret))
+ if(! in_array($rr['channel_id'], $ret))
$ret[] = $rr['channel_id'];
+ }
$str = '';
- if($ret)
+ if($ret) {
foreach($ret as $rr) {
if($str)
$str .= ',';
$str .= intval($rr);
}
+ }
logger('stream_perms_api_uids: ' . $str, LOGGER_DEBUG);
+
return $str;
}
@@ -383,23 +440,25 @@ function stream_perms_xchans($perms = NULL ) {
if(local_user())
$ret[] = get_observer_hash();
- $r = q("select channel_hash from channel where channel_r_stream > 0 and (channel_r_stream & %d) and not (channel_pageflags & %d)",
+ $r = q("select channel_hash from channel where channel_r_stream > 0 and (channel_r_stream & %d)>0 and not (channel_pageflags & %d)>0",
intval($perms),
- intval(PAGE_CENSORED|PAGE_SYETEM|PAGE_REMOVED)
+ intval(PAGE_ADULT|PAGE_CENSORED|PAGE_SYSTEM|PAGE_REMOVED)
);
- if($r)
+ if($r) {
foreach($r as $rr)
- if(! in_array($rr['channel_hash'],$ret))
+ if(! in_array($rr['channel_hash'], $ret))
$ret[] = $rr['channel_hash'];
+ }
$str = '';
- if($ret)
+ if($ret) {
foreach($ret as $rr) {
if($str)
$str .= ',';
$str .= "'" . dbesc($rr) . "'";
}
+ }
logger('stream_perms_xchans: ' . $str, LOGGER_DEBUG);
+
return $str;
}
-
diff --git a/include/session.php b/include/session.php
index b531688e2..ed4dfdd16 100644
--- a/include/session.php
+++ b/include/session.php
@@ -60,11 +60,11 @@ function ref_session_write ($id,$data) {
if($session_exists)
$r = q("UPDATE `session`
SET `data` = '%s', `expire` = '%s'
- WHERE `sid` = '%s' LIMIT 1",
+ WHERE `sid` = '%s'",
dbesc($data), dbesc($expire), dbesc($id));
else
- $r = q("INSERT INTO `session`
- SET `sid` = '%s', `expire` = '%s', `data` = '%s'",
+ $r = q("INSERT INTO `session` (sid, expire, data) values ('%s', '%s', '%s')",
+ //SET `sid` = '%s', `expire` = '%s', `data` = '%s'",
dbesc($id), dbesc($default_expire), dbesc($data));
return true;
@@ -84,8 +84,9 @@ function ref_session_destroy ($id) {
function ref_session_gc($expire) {
q("DELETE FROM session WHERE expire < %d", dbesc(time()));
- q("OPTIMIZE TABLE session");
- return true;
+ if (! get_config('system','innodb'))
+ db_optimizetable('session');
+ return true;
}
$gc_probability = 50;
diff --git a/include/socgraph.php b/include/socgraph.php
index 504a6b2c0..740886b1c 100644
--- a/include/socgraph.php
+++ b/include/socgraph.php
@@ -77,7 +77,7 @@ function poco_load($xchan = '',$url = null) {
dbesc($xchan)
);
if($r) {
- q("update xchat set xchat_edited = '%s' where xchat_id = %d limit 1",
+ q("update xchat set xchat_edited = '%s' where xchat_id = %d",
dbesc(datetime_convert()),
intval($r[0]['xchat_id'])
);
@@ -93,7 +93,8 @@ function poco_load($xchan = '',$url = null) {
}
}
}
- q("delete from xchat where xchat_edited < UTC_TIMESTAMP() - INTERVAL 7 DAY and xchat_xchan = '%s' ",
+ q("delete from xchat where xchat_edited < %s - INTERVAL %s and xchat_xchan = '%s' ",
+ db_utcnow(), db_quoteinterval('7 DAY'),
dbesc($xchan)
);
}
@@ -195,7 +196,7 @@ function poco_load($xchan = '',$url = null) {
);
}
else {
- q("update xlink set xlink_updated = '%s', xlink_rating = %d where xlink_id = %d limit 1",
+ q("update xlink set xlink_updated = '%s', xlink_rating = %d where xlink_id = %d",
dbesc(datetime_convert()),
intval($rating),
intval($r[0]['xlink_id'])
@@ -204,8 +205,9 @@ function poco_load($xchan = '',$url = null) {
}
logger("poco_load: loaded $total entries",LOGGER_DEBUG);
- q("delete from xlink where xlink_xchan = '%s' and xlink_updated < UTC_TIMESTAMP() - INTERVAL 2 DAY",
- dbesc($xchan)
+ q("delete from xlink where xlink_xchan = '%s' and xlink_updated < %s - INTERVAL %s",
+ dbesc($xchan),
+ db_utcnow(), db_quoteinterval('2 DAY')
);
}
@@ -227,18 +229,19 @@ function count_common_friends($uid,$xchan) {
function common_friends($uid,$xchan,$start = 0,$limit=100000000,$shuffle = false) {
+ $rand = db_getfunc('rand');
if($shuffle)
- $sql_extra = " order by rand() ";
+ $sql_extra = " order by $rand ";
else
$sql_extra = " order by xchan_name asc ";
$r = q("SELECT * from xchan left join xlink on xlink_link = xchan_hash where xlink_xchan = '%s' and xlink_link in
- (select abook_xchan from abook where abook_xchan != '%s' and abook_channel = %d and abook_flags = 0 ) $sql_extra limit %d, %d",
+ (select abook_xchan from abook where abook_xchan != '%s' and abook_channel = %d and abook_flags = 0 ) $sql_extra limit %d offset %d",
dbesc($xchan),
dbesc($xchan),
intval($uid),
- intval($start),
- intval($limit)
+ intval($limit),
+ intval($start)
);
return $r;
@@ -273,11 +276,11 @@ function common_friends_zcid($uid,$zcid,$start = 0, $limit = 9999,$shuffle = fal
FROM `glink` left join `gcontact` on `glink`.`gcid` = `gcontact`.`id`
where `glink`.`zcid` = %d
and `gcontact`.`nurl` in (select nurl from contact where uid = %d and self = 0 and blocked = 0 and hidden = 0 )
- $sql_extra limit %d, %d",
+ $sql_extra limit %d offset %d",
intval($zcid),
intval($uid),
- intval($start),
- intval($limit)
+ intval($limit),
+ intval($start)
);
return $r;
@@ -306,11 +309,11 @@ function all_friends($uid,$cid,$start = 0, $limit = 80) {
$r = q("SELECT `gcontact`.*
FROM `glink` left join `gcontact` on `glink`.`gcid` = `gcontact`.`id`
where `glink`.`cid` = %d and `glink`.`uid` = %d
- order by `gcontact`.`name` asc LIMIT %d, %d ",
+ order by `gcontact`.`name` asc LIMIT %d OFFSET %d ",
intval($cid),
intval($uid),
- intval($start),
- intval($limit)
+ intval($limit),
+ intval($start)
);
return $r;
@@ -329,16 +332,16 @@ function suggestion_query($uid, $myxchan, $start = 0, $limit = 80) {
and not xlink_link in ( select abook_xchan from abook where abook_channel = %d )
and not xlink_link in ( select xchan from xign where uid = %d )
and xlink_xchan != ''
- and not ( xchan_flags & %d )
- and not ( xchan_flags & %d )
- group by xchan_hash order by total desc limit %d, %d ",
+ and not ( xchan_flags & %d )>0
+ and not ( xchan_flags & %d )>0
+ group by xchan_hash order by total desc limit %d offset %d ",
intval($uid),
intval($uid),
intval($uid),
intval(XCHAN_FLAGS_HIDDEN),
intval(XCHAN_FLAGS_DELETED),
- intval($start),
- intval($limit)
+ intval($limit),
+ intval($start)
);
if($r && count($r) >= ($limit -1))
@@ -349,15 +352,15 @@ function suggestion_query($uid, $myxchan, $start = 0, $limit = 80) {
where xlink_xchan = ''
and not xlink_link in ( select abook_xchan from abook where abook_channel = %d )
and not xlink_link in ( select xchan from xign where uid = %d )
- and not ( xchan_flags & %d )
- and not ( xchan_flags & %d )
- group by xchan_hash order by total desc limit %d, %d ",
+ and not ( xchan_flags & %d )>0
+ and not ( xchan_flags & %d )>0
+ group by xchan_hash order by total desc limit %d offset %d ",
intval($uid),
intval($uid),
intval(XCHAN_FLAGS_HIDDEN),
intval(XCHAN_FLAGS_DELETED),
- intval($start),
- intval($limit)
+ intval($limit),
+ intval($start)
);
if(is_array($r) && is_array($r2))
@@ -394,7 +397,9 @@ function update_suggestions() {
// the targets may have changed their preferences and don't want to be suggested - and they
// may have simply gone away.
- $r = q("delete from xlink where xlink_xchan = '' and xlink_updated < UTC_TIMESTAMP() - INTERVAL 7 DAY");
+ $r = q("delete from xlink where xlink_xchan = '' and xlink_updated < %s - INTERVAL %s",
+ db_utcnow(), db_quoteinterval('7 DAY')
+ );
$j = json_decode($ret['body'],true);
diff --git a/include/statistics_fns.php b/include/statistics_fns.php
new file mode 100644
index 000000000..288925a2c
--- /dev/null
+++ b/include/statistics_fns.php
@@ -0,0 +1,79 @@
+<?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 > %s - INTERVAL %s",
+ db_utcnow(), db_quoteinterval('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)>0 and created > %s - INTERVAL %s group by uid",
+ intval(ITEM_WALL),
+ db_utcnow(), db_quoteinterval('6 MONTH')
+ );
+ 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 > %s - INTERVAL %s",
+ db_utcnow(), db_quoteinterval('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 )>0 and created > %s - INTERVAL %s group by uid",
+ intval(ITEM_WALL),
+ db_utcnow(), db_quoteinterval('1 MONTH')
+ );
+ 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)>0 ",
+ 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/taxonomy.php b/include/taxonomy.php
index 92003328f..be80008df 100644
--- a/include/taxonomy.php
+++ b/include/taxonomy.php
@@ -154,7 +154,7 @@ function tagadelic($uid, $count = 0, $authors = '', $flags = 0, $restrict = 0, $
}
function tags_sort($a,$b) {
- if($a[0] == $b[0])
+ if(strtolower($a[0]) == strtolower($b[0]))
return 0;
return((strtolower($a[0]) < strtolower($b[0])) ? -1 : 1);
}
@@ -209,13 +209,55 @@ function tagblock($link,$uid,$count = 0,$authors = '',$flags = 0,$restrict = 0,$
if($r) {
$o = '<div class="tagblock widget"><h3>' . t('Tags') . '</h3><div class="tags" align="center">';
foreach($r as $rr) {
- $o .= '<a href="'.$link .'/' . '?f=&tag=' . urlencode($rr[0]).'" class="tag'.$rr[2].'">'.$rr[0].'</a> ' . "\r\n";
+ $o .= '<span class="tag'.$rr[2].'">#</span><a href="'.$link .'/' . '?f=&tag=' . urlencode($rr[0]).'" class="tag'.$rr[2].'">'.$rr[0].'</a> ' . "\r\n";
}
$o .= '</div></div>';
}
return $o;
}
+function wtagblock($uid,$count = 0,$authors = '',$flags = 0,$restrict = 0,$type = TERM_HASHTAG) {
+ $o = '';
+ $tab = 0;
+ $r = tagadelic($uid,$count,$authors,$flags,$restrict,$type);
+
+ if($r) {
+ $c = q("select channel_address from channel where channel_id = %d limit 1",
+ intval($uid)
+ );
+
+ $o = '<div class="tagblock widget"><h3>' . t('Tags') . '</h3><div class="tags" align="center">';
+ foreach($r as $rr) {
+ $o .= '<span class="tag' . $rr[2] . '">#</span><a href="channel/' . $c[0]['channel_address'] . '?f=&tag=' . urlencode($rr[0]).'" class="tag'.$rr[2].'">'.$rr[0].'</a> ' . "\r\n";
+ }
+ $o .= '</div></div>';
+ }
+ return $o;
+}
+
+
+function catblock($uid,$count = 0,$authors = '',$flags = 0,$restrict = 0,$type = TERM_CATEGORY) {
+ $o = '';
+ $tab = 0;
+
+ $r = tagadelic($uid,$count,$authors,$flags,$restrict,$type);
+
+ if($r) {
+ $c = q("select channel_address from channel where channel_id = %d limit 1",
+ intval($uid)
+ );
+
+ $o = '<div class="tagblock widget"><h3>' . t('Categories') . '</h3><div class="tags" align="center">';
+ foreach($r as $rr) {
+ $o .= '<a href="channel/' . $c[0]['channel_address']. '?f=&cat=' . urlencode($rr[0]).'" class="tag'.$rr[2].'">'.$rr[0].'</a> ' . "\r\n";
+ }
+ $o .= '</div></div>';
+ }
+ return $o;
+}
+
+
+
function dir_tagblock($link,$r) {
$o = '';
$tab = 0;
diff --git a/include/text.php b/include/text.php
index 50c68b6b2..6516d734d 100644
--- a/include/text.php
+++ b/include/text.php
@@ -181,34 +181,34 @@ function autoname($len) {
'kh', 'kl','kr','mn','pl','pr','rh','tr','qu','wh');
$start = mt_rand(0,2);
- if($start == 0)
- $table = $vowels;
- else
- $table = $cons;
+ if($start == 0)
+ $table = $vowels;
+ else
+ $table = $cons;
$word = '';
for ($x = 0; $x < $len; $x ++) {
- $r = mt_rand(0,count($table) - 1);
- $word .= $table[$r];
-
- if($table == $vowels)
- $table = array_merge($cons,$midcons);
- else
- $table = $vowels;
+ $r = mt_rand(0,count($table) - 1);
+ $word .= $table[$r];
+
+ if($table == $vowels)
+ $table = array_merge($cons,$midcons);
+ else
+ $table = $vowels;
}
$word = substr($word,0,$len);
foreach($noend as $noe) {
- if((strlen($word) > 2) && (substr($word,-2) == $noe)) {
- $word = substr($word,0,-1);
- break;
- }
+ if((strlen($word) > 2) && (substr($word,-2) == $noe)) {
+ $word = substr($word,0,-1);
+ break;
+ }
}
if(substr($word,-1) == 'q')
- $word = substr($word,0,-1);
+ $word = substr($word,0,-1);
return $word;
}
@@ -224,11 +224,11 @@ function autoname($len) {
*/
function xmlify($str) {
$buffer = '';
-
+
$len = mb_strlen($str);
for($x = 0; $x < $len; $x ++) {
$char = mb_substr($str,$x,1);
-
+
switch( $char ) {
case "\r" :
@@ -267,7 +267,7 @@ function xmlify($str) {
function unxmlify($s) {
$ret = str_replace('&amp;','&', $s);
$ret = str_replace(array('&lt;','&gt;','&quot;','&apos;'),array('<','>','"',"'"),$ret);
- return $ret;
+ return $ret;
}
// convenience wrapper, reverse the operation "bin2hex"
@@ -314,8 +314,7 @@ function paginate(&$a) {
$pagenum = $a->pager['page'];
$url = $a->get_baseurl() . '/' . $stripped;
-
- if($a->pager['total'] > $a->pager['itemspage']) {
+ if($a->pager['total'] > $a->pager['itemspage']) {
$o .= '<div class="pager">';
if($a->pager['page'] != 1)
$o .= '<span class="pager_prev">'."<a href=\"$url".'&page='.($a->pager['page'] - 1).'">' . t('prev') . '</a></span> ';
@@ -331,7 +330,7 @@ function paginate(&$a) {
$numstart = (($pagenum > 7) ? ($pagenum - 7) : 1);
$numstop = (($pagenum > ($numpages - 7)) ? $numpages : ($numstart + 14));
}
-
+
for($i = $numstart; $i <= $numstop; $i++){
if($i == $a->pager['page'])
$o .= '<span class="pager_current">'.(($i < 10) ? '&nbsp;'.$i : $i);
@@ -405,7 +404,7 @@ function expand_acl($s) {
}
}
return $ret;
-}
+}
// Used to wrap ACL elements in angle brackets for storage
@@ -420,7 +419,6 @@ function sanitise_acl(&$item) {
// Convert an ACL array to a storable string
-
function perms2str($p) {
$ret = '';
@@ -436,16 +434,17 @@ function perms2str($p) {
return $ret;
}
-// generate a guaranteed unique (for this domain) item ID for ATOM
-// safe from birthday paradox
-
-
+/**
+ * @brief Generate a guaranteed unique (for this domain) item ID for ATOM.
+ *
+ * Safe from birthday paradox.
+ *
+ * @return string a unique id
+ */
function item_message_id() {
-
do {
$dups = false;
$hash = random_string();
-
$mid = $hash . '@' . get_app()->get_hostname();
$r = q("SELECT id FROM item WHERE mid = '%s' LIMIT 1",
@@ -453,31 +452,33 @@ function item_message_id() {
if(count($r))
$dups = true;
} while($dups == true);
+
return $mid;
}
-// Generate a guaranteed unique photo ID.
-// safe from birthday paradox
-
-
+/**
+ * @brief Generate a guaranteed unique photo ID.
+ *
+ * Safe from birthday paradox.
+ *
+ * @return string a uniqe hash
+ */
function photo_new_resource() {
-
do {
$found = false;
- $resource = hash('md5',uniqid(mt_rand(),true));
+ $resource = hash('md5', uniqid(mt_rand(), true));
+
$r = q("SELECT id FROM photo WHERE resource_id = '%s' LIMIT 1",
- dbesc($resource)
- );
+ dbesc($resource));
if(count($r))
$found = true;
- } while($found == true);
+ } while($found === true);
+
return $resource;
}
-
-
// for html,xml parsing - let's say you've got
// an attribute foobar="class1 class2 class3"
// and you want to find out if it contains 'class3'.
@@ -487,52 +488,76 @@ function photo_new_resource() {
// pass the attribute string as $attr and the attribute you
// are looking for as $s - returns true if found, otherwise false
-function attribute_contains($attr,$s) {
+function attribute_contains($attr, $s) {
$a = explode(' ', $attr);
- if(count($a) && in_array($s,$a))
+ if(count($a) && in_array($s, $a))
return true;
+
return false;
}
-
-function logger($msg,$level = 0) {
+/**
+ * @brief Logging function for RedMatrix.
+ *
+ * Logging output is configured through RedMatrix's system config. The log file
+ * is set in system logfile, log level in system loglevel and to enable logging
+ * set system debugging.
+ *
+ * Available constants for log level are LOGGER_NORMAL, LOGGER_TRACE, LOGGER_DEBUG,
+ * LOGGER_DATA and LOGGER_ALL.
+ *
+ * Since PHP5.4 we get the file, function and line automatically where the logger
+ * was caleld, so no need to add it to the message anymore.
+ *
+ * @param string $msg Message to log
+ * @param int $level A log level.
+ */
+function logger($msg, $level = 0) {
// turn off logger in install mode
global $a;
global $db;
- if(($a->module == 'install') || (! ($db && $db->connected))) return;
+ if(($a->module == 'install') || (! ($db && $db->connected)))
+ return;
- $debugging = get_config('system','debugging');
- $loglevel = intval(get_config('system','loglevel'));
- $logfile = get_config('system','logfile');
+ $debugging = get_config('system', 'debugging');
+ $loglevel = intval(get_config('system', 'loglevel'));
+ $logfile = get_config('system', 'logfile');
if((! $debugging) || (! $logfile) || ($level > $loglevel))
return;
$where = '';
- if(version_compare(PHP_VERSION,'5.4.0') >= 0) {
- $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,2);
+ if(version_compare(PHP_VERSION, '5.4.0') >= 0) {
+ $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
$where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': ';
}
- @file_put_contents($logfile, datetime_convert() . ':' . session_id() . ' ' . $where . $msg . "\n", FILE_APPEND);
- return;
+ @file_put_contents($logfile, datetime_convert() . ':' . session_id() . ' ' . $where . $msg . PHP_EOL, FILE_APPEND);
}
-
-// This is a special logging facility for developers. It allows one to target specific things to trace/debug
-// and is identical to logger() with the exception of the log filename. This allows one to isolate specific
-// calls while allowing logger() to paint a bigger picture of overall activity and capture more detail.
-// If you find dlogger() calls in checked in code, you are free to remove them - so as to provide a noise-free
-// development environment which responds to events you are targetting personally.
-
-
-function dlogger($msg,$level = 0) {
+/**
+ * @brief This is a special logging facility for developers.
+ *
+ * It allows one to target specific things to trace/debug and is identical to
+ * logger() with the exception of the log filename. This allows one to isolate
+ * specific calls while allowing logger() to paint a bigger picture of overall
+ * activity and capture more detail.
+ *
+ * If you find dlogger() calls in checked in code, you are free to remove them -
+ * so as to provide a noise-free development environment which responds to events
+ * you are targetting personally.
+ *
+ * @param string $msg Message to log
+ * @param int $level A log level.
+ */
+function dlogger($msg, $level = 0) {
// turn off logger in install mode
global $a;
global $db;
- if(($a->module == 'install') || (! ($db && $db->connected))) return;
+ if(($a->module == 'install') || (! ($db && $db->connected)))
+ return;
$debugging = get_config('system','debugging');
$loglevel = intval(get_config('system','loglevel'));
@@ -540,19 +565,23 @@ function dlogger($msg,$level = 0) {
if((! $debugging) || (! $logfile) || ($level > $loglevel))
return;
-
- @file_put_contents($logfile, datetime_convert() . ':' . session_id() . ' ' . $msg . "\n", FILE_APPEND);
- return;
+
+ $where = '';
+ if(version_compare(PHP_VERSION, '5.4.0') >= 0) {
+ $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
+ $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': ';
+ }
+
+ @file_put_contents($logfile, datetime_convert() . ':' . session_id() . ' ' . $where . $msg . PHP_EOL, FILE_APPEND);
}
function profiler($t1,$t2,$label) {
if(file_exists('profiler.out') && $t1 && t2)
- @file_put_contents('profiler.out', sprintf('%01.4f %s',$t2 - $t1,$label) . "\n", FILE_APPEND);
+ @file_put_contents('profiler.out', sprintf('%01.4f %s',$t2 - $t1,$label) . PHP_EOL, FILE_APPEND);
}
-
function activity_match($haystack,$needle) {
if(($haystack === $needle) || ((basename($needle) === $haystack) && strstr($needle,NAMESPACE_ACTIVITY_SCHEMA)))
return true;
@@ -569,7 +598,6 @@ function activity_match($haystack,$needle) {
// Returns array of tags found, or empty array.
-
function get_tags($s) {
$ret = array();
@@ -592,9 +620,6 @@ function get_tags($s) {
// Match full names against @tags including the space between first and last
// We will look these up afterward to see if they are full names or not recognisable.
-
-
-
if(preg_match_all('/(@[^ \x0D\x0A,:?]+ [^ \x0D\x0A@,:?]+)([ \x0D\x0A@,:?]|$)/',$s,$match)) {
foreach($match[1] as $mtch) {
if(strstr($mtch,"]")) {
@@ -619,8 +644,8 @@ function get_tags($s) {
}
if(substr($mtch,-1,1) === '.')
$mtch = substr($mtch,0,-1);
- // ignore strictly numeric tags like #1
- if((strpos($mtch,'#') === 0) && ( ctype_digit(substr($mtch,1)) || substr($mtch,1,1) === '^'))
+ // ignore strictly numeric tags like #1 or #^ bookmarks or ## double hash
+ if((strpos($mtch,'#') === 0) && ( ctype_digit(substr($mtch,1)) || substr($mtch,1,1) === '^') || substr($mtch,1,1) === '#')
continue;
// try not to catch url fragments
if(strpos($s,$mtch) && preg_match('/[a-zA-z0-9\/]/',substr($s,strpos($s,$mtch)-1,1)))
@@ -648,7 +673,6 @@ function get_tags($s) {
usort($ret,'tag_sort_length');
-
// logger('get_tags: ' . print_r($ret,true));
return $ret;
@@ -657,13 +681,12 @@ function get_tags($s) {
function tag_sort_length($a,$b) {
if(mb_strlen($a) == mb_strlen($b))
return 0;
+
return((mb_strlen($b) < mb_strlen($a)) ? (-1) : 1);
}
-
-
function strip_zids($s) {
return preg_replace('/[\?&]zid=(.*?)(&|$)/ism','$2',$s);
}
@@ -673,12 +696,10 @@ function strip_zids($s) {
function qp($s) {
-return str_replace ("%","=",rawurlencode($s));
+ return str_replace ("%","=",rawurlencode($s));
}
-
-
function get_mentions($item,$tags) {
$o = '';
@@ -712,7 +733,6 @@ function contact_block() {
if($shown == 0)
return;
-
$is_owner = ((local_user() && local_user() == $a->profile['uid']) ? true : false);
$abook_flags = ABOOK_FLAG_PENDING|ABOOK_FLAG_SELF;
@@ -724,7 +744,7 @@ function contact_block() {
if((! is_array($a->profile)) || ($a->profile['hide_friends']))
return $o;
- $r = q("SELECT COUNT(abook_id) AS total FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and not ( abook_flags & %d ) and not (xchan_flags & %d)",
+ $r = q("SELECT COUNT(abook_id) AS total FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d and not ( abook_flags & %d )>0 and not (xchan_flags & %d)>0",
intval($a->profile['uid']),
intval($abook_flags),
intval($xchan_flags)
@@ -734,11 +754,14 @@ function contact_block() {
}
if(! $total) {
$contacts = t('No connections');
- $micropro = Null;
-
+ $micropro = null;
} else {
-
- $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash WHERE abook_channel = %d AND not ( abook_flags & %d) and not (xchan_flags & %d ) ORDER BY RAND() LIMIT %d",
+ if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
+ $randfunc = 'RANDOM()';
+ } else {
+ $randfunc = 'RAND()';
+ }
+ $r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash WHERE abook_channel = %d AND not ( abook_flags & %d)>0 and not (xchan_flags & %d )>0 ORDER BY $randfunc LIMIT %d",
intval($a->profile['uid']),
intval($abook_flags|ABOOK_FLAG_ARCHIVED),
intval($xchan_flags),
@@ -754,7 +777,7 @@ function contact_block() {
}
}
}
-
+
$tpl = get_markup_template('contact_block.tpl');
$o = replace_macros($tpl, array(
'$contacts' => $contacts,
@@ -767,7 +790,6 @@ function contact_block() {
call_hooks('contact_block_end', $arr);
return $o;
-
}
@@ -811,42 +833,38 @@ 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')
+ ));
}
function valid_email($x){
-
if(get_config('system','disable_email_validation'))
return true;
if(preg_match('/^[_a-zA-Z0-9\-\+]+(\.[_a-zA-Z0-9\-\+]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+$/',$x))
return true;
+
return false;
}
@@ -877,11 +895,10 @@ function linkify($s) {
*
* @returns string
*/
-
-
function sslify($s) {
if(strpos(z_root(),'https:') === false)
return $s;
+
$matches = null;
$cnt = preg_match_all("/\<(.*?)src=\"(http\:.*?)\"(.*?)\>/",$s,$matches,PREG_SET_ORDER);
if($cnt) {
@@ -895,7 +912,6 @@ function sslify($s) {
function get_poke_verbs() {
-
// index is present tense verb
// value is array containing past tense verb, translation of present, translation of past
@@ -907,12 +923,13 @@ function get_poke_verbs() {
'finger' => array( 'fingered', t('finger'), t('fingered')),
'rebuff' => array( 'rebuffed', t('rebuff'), t('rebuffed')),
);
+
call_hooks('poke_verbs', $arr);
return $arr;
}
function get_mood_verbs() {
-
+
$arr = array(
'happy' => t('happy'),
'sad' => t('sad'),
@@ -941,40 +958,10 @@ function get_mood_verbs() {
return $arr;
}
-
-/**
- *
- * Function: smilies
- *
- * Description:
- * Replaces text emoticons with graphical images
- *
- * @Parameter: string $s
- *
- * Returns string
- *
- * It is expected that this function will be called using HTML text.
- * We will escape text between HTML pre and code blocks, and HTML attributes
- * (such as urls) from being processed.
- *
- * At a higher level, the bbcode [nosmile] tag can be used to prevent this
- * function from being executed by the prepare_text() routine when preparing
- * bbcode source for HTML display
- *
- */
-
-
-function smilies($s, $sample = false) {
-
+// Function to list all smilies, both internal and from addons
+// Returns array with keys 'texts' and 'icons'
+function list_smilies() {
$a = get_app();
-
- if(intval(get_config('system','no_smilies'))
- || (local_user() && intval(get_pconfig(local_user(),'system','no_smilies'))))
- return $s;
-
- $s = preg_replace_callback('{<(pre|code)>.*?</\1>}ism','smile_shield',$s);
- $s = preg_replace_callback('/<[a-z]+ .*?>/ism','smile_shield',$s);
-
$texts = array(
'&lt;3',
'&lt;/3',
@@ -1046,14 +1033,48 @@ function smilies($s, $sample = false) {
'<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-facepalm.gif" alt=":facepalm" />',
'<img class="smiley" src="' . $a->get_baseurl() . '/images/like.gif" alt=":like" />',
'<img class="smiley" src="' . $a->get_baseurl() . '/images/dislike.gif" alt=":dislike" />',
- '<a href="http://getzot.com"><strong>red<img class="smiley" src="' . $a->get_baseurl() . '/images/rm-16.png" alt="red#matrix" />matrix</strong></a>',
- '<a href="http://getzot.com"><strong>red<img class="smiley" src="' . $a->get_baseurl() . '/images/rm-16.png" alt="red#" />matrix</strong></a>',
+ '<a href="http://getzot.com"><strong>red<img class="smiley" src="' . $a->get_baseurl() . '/images/rm-16.png" alt="' . urlencode('red#matrix') . '" />matrix</strong></a>',
+ '<a href="http://getzot.com"><strong>red<img class="smiley" src="' . $a->get_baseurl() . '/images/rm-16.png" alt="' . urlencode('red#') . '" />matrix</strong></a>',
'<a href="http://getzot.com"><strong>red<img class="smiley" src="' . $a->get_baseurl() . '/images/rm-16.png" alt="r#" />matrix</strong></a>'
);
- $params = array('texts' => $texts, 'icons' => $icons, 'string' => $s);
+ $params = array('texts' => $texts, 'icons' => $icons);
call_hooks('smilie', $params);
+ return $params;
+}
+/**
+ *
+ * Function: smilies
+ *
+ * Description:
+ * Replaces text emoticons with graphical images
+ *
+ * @Parameter: string $s
+ *
+ * Returns string
+ *
+ * It is expected that this function will be called using HTML text.
+ * We will escape text between HTML pre and code blocks, and HTML attributes
+ * (such as urls) from being processed.
+ *
+ * At a higher level, the bbcode [nosmile] tag can be used to prevent this
+ * function from being executed by the prepare_text() routine when preparing
+ * bbcode source for HTML display
+ *
+ */
+function smilies($s, $sample = false) {
+ $a = get_app();
+
+ if(intval(get_config('system','no_smilies'))
+ || (local_user() && intval(get_pconfig(local_user(),'system','no_smilies'))))
+ return $s;
+
+ $s = preg_replace_callback('{<(pre|code)>.*?</\1>}ism','smile_shield',$s);
+ $s = preg_replace_callback('/<[a-z]+ .*?>/ism','smile_shield',$s);
+
+ $params = list_smilies();
+ $params['string'] = $s;
if($sample) {
$s = '<div class="smiley-sample">';
@@ -1069,7 +1090,6 @@ function smilies($s, $sample = false) {
$s = preg_replace_callback('/<!--base64:(.*?)-->/ism', 'smile_unshield', $s);
return $s;
-
}
function smile_shield($m) {
@@ -1129,6 +1149,7 @@ function normalise_link($url) {
function link_compare($a,$b) {
if(strcasecmp(normalise_link($a),normalise_link($b)) === 0)
return true;
+
return false;
}
@@ -1144,7 +1165,6 @@ function unobscure(&$item) {
if($item['body'])
$item['body'] = crypto_unencapsulate(json_decode_plus($item['body']),$key);
}
-
}
function theme_attachments(&$item) {
@@ -1182,13 +1202,15 @@ function theme_attachments(&$item) {
$title = t('unknown.???');
$title .= ' ' . $r['length'] . ' ' . t('bytes');
- $url = z_root() . '/magic?f=&hash=' . $item['author_xchan'] . '&dest=' . $r['href'] . '/' . $r['revision'];
+ require_once('include/identity.php');
+ if(is_foreigner($item['author_xchan']))
+ $url = $r['href'];
+ else
+ $url = z_root() . '/magic?f=&hash=' . $item['author_xchan'] . '&dest=' . $r['href'] . '/' . $r['revision'];
+
$s .= '<a href="' . $url . '" title="' . $title . '" class="attachlink" >' . $icon . '</a>';
$attaches[] = array('title' => $title, 'url' => $url, 'icon' => $icon );
-
}
-
-
}
$s = replace_macros(get_markup_template('item_attach.tpl'), array(
@@ -1196,13 +1218,12 @@ function theme_attachments(&$item) {
));
return $s;
-
}
function format_categories(&$item,$writeable) {
-
$s = '';
+
$terms = get_terms_oftype($item['term'],TERM_CATEGORY);
if($terms) {
$categories = array();
@@ -1218,6 +1239,7 @@ function format_categories(&$item,$writeable) {
'$remove' => t('remove category'),
'$categories' => $categories
));
+
return $s;
}
@@ -1228,7 +1250,6 @@ function format_hashtags(&$item) {
$s = '';
$terms = get_terms_oftype($item['term'],TERM_HASHTAG);
if($terms) {
- $categories = array();
foreach($terms as $t) {
$term = htmlspecialchars($t['term'],ENT_COMPAT,'UTF-8',false) ;
if(! trim($term))
@@ -1248,11 +1269,10 @@ function format_hashtags(&$item) {
function format_mentions(&$item) {
-
$s = '';
+
$terms = get_terms_oftype($item['term'],TERM_MENTION);
if($terms) {
- $categories = array();
foreach($terms as $t) {
$term = htmlspecialchars($t['term'],ENT_COMPAT,'UTF-8',false) ;
if(! trim($term))
@@ -1271,8 +1291,8 @@ function format_mentions(&$item) {
function format_filer(&$item) {
-
$s = '';
+
$terms = get_terms_oftype($item['term'],TERM_FILE);
if($terms) {
$categories = array();
@@ -1288,18 +1308,28 @@ function format_filer(&$item) {
'$remove' => t('remove from file'),
'$categories' => $categories
));
+
return $s;
}
+function generate_map($coord) {
+ $coord = trim($coord);
+ $coord = str_replace(array(',','/',' '),array(' ',' ',' '),$coord);
+ $arr = array('lat' => trim(substr($coord,0,strpos($coord,' '))), 'lon' => trim(substr($coord,strpos($coord,' ')+1)), 'html' => '');
+ call_hooks('generate_map',$arr);
+ return $arr['html'];
+}
+function generate_named_map($location) {
+ $arr = array('location' => $location, 'html' => '');
+ call_hooks('generate_named_map',$arr);
+ return $arr['html'];
+}
-function prepare_body(&$item,$attach = false) {
-
- $a = get_app();
-
+function prepare_body(&$item,$attach = false) {
call_hooks('prepare_body_init', $item);
@@ -1315,28 +1345,29 @@ function prepare_body(&$item,$attach = false) {
return $s;
}
+ if(strpos($s,'<div class="map">') !== false && $item['coord']) {
+ $x = generate_map(trim($item['coord']));
+ if($x) {
+ $s = preg_replace('/\<div class\=\"map\"\>/','$0' . $x,$s);
+ }
+ }
$s .= theme_attachments($item);
-
- $writeable = ((get_observer_hash() == $item['owner_xchan']) ? true : false);
-
+ $writeable = ((get_observer_hash() == $item['owner_xchan']) ? true : false);
$s .= format_hashtags($item);
if($item['resource_type'])
$s .= format_mentions($item);
-
$s .= format_categories($item,$writeable);
if(local_user() == $item['uid'])
$s .= format_filer($item);
-
$s = sslify($s);
-
// Look for spoiler
$spoilersearch = '<blockquote class="spoiler">';
@@ -1351,7 +1382,7 @@ function prepare_body(&$item,$attach = false) {
$pos = strpos($s, $spoilersearch);
$rnd = random_string(8);
$spoilerreplace = '<br /> <span id="spoiler-wrap-'.$rnd.'" style="white-space:nowrap;" class="fakelink" onclick="openClose(\'spoiler-'.$rnd.'\');">'.sprintf(t('Click to open/close')).'</span>'.
- '<blockquote class="spoiler" id="spoiler-'.$rnd.'" style="display: none;">';
+ '<blockquote class="spoiler" id="spoiler-'.$rnd.'" style="display: none;">';
$s = substr($s, 0, $pos).$spoilerreplace.substr($s, $pos+strlen($spoilersearch));
}
@@ -1359,11 +1390,10 @@ function prepare_body(&$item,$attach = false) {
$authorsearch = '<blockquote class="author">';
while ((strpos($s, $authorsearch) !== false)) {
-
$pos = strpos($s, $authorsearch);
$rnd = random_string(8);
$authorreplace = '<br /> <span id="author-wrap-'.$rnd.'" style="white-space:nowrap;" class="fakelink" onclick="openClose(\'author-'.$rnd.'\');">'.sprintf(t('Click to open/close')).'</span>'.
- '<blockquote class="author" id="author-'.$rnd.'" style="display: block;">';
+ '<blockquote class="author" id="author-'.$rnd.'" style="display: block;">';
$s = substr($s, 0, $pos).$authorreplace.substr($s, $pos+strlen($authorsearch));
}
@@ -1379,10 +1409,7 @@ function prepare_body(&$item,$attach = false) {
function prepare_text($text,$content_type = 'text/bbcode') {
-
-
switch($content_type) {
-
case 'text/plain':
$s = escape_tags($text);
break;
@@ -1452,7 +1479,7 @@ function zidify_callback($match) {
function zidify_img_callback($match) {
$is_zid = ((feature_enabled(local_user(),'sendzid')) || (strpos($match[1],'zrl')) ? true : false);
$replace = '<img' . $match[1] . ' src="' . (($is_zid) ? zid($match[2]) : $match[2]) . '"';
-
+
$x = str_replace($match[0],$replace,$match[0]);
return $x;
}
@@ -1465,14 +1492,11 @@ function zidify_links($s) {
}
-
/**
* return atom link elements for all of our hubs
*/
-
function feed_hublinks() {
-
$hub = get_config('system','huburl');
$hubxml = '';
@@ -1487,14 +1511,13 @@ function feed_hublinks() {
}
}
}
+
return $hubxml;
}
/* return atom link elements for salmon endpoints */
-
function feed_salmonlinks($nick) {
-
$a = get_app();
$salmon = '<link rel="salmon" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ;
@@ -1503,23 +1526,23 @@ function feed_salmonlinks($nick) {
$salmon .= ' <link rel="http://salmon-protocol.org/ns/salmon-replies" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ;
$salmon .= ' <link rel="http://salmon-protocol.org/ns/salmon-mention" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ;
+
return $salmon;
}
function get_plink($item,$conversation_mode = true) {
- $a = get_app();
if($conversation_mode)
$key = 'plink';
else
$key = 'llink';
-
+
if(x($item,$key)) {
return array(
'href' => zid($item[$key]),
'title' => t('Link to Source'),
);
- }
+ }
else {
return false;
}
@@ -1531,7 +1554,7 @@ function unamp($s) {
}
function layout_select($channel_id, $current = '') {
- $r = q("select mid,sid from item left join item_id on iid = item.id where service = 'PDL' and item.uid = item_id.uid and item_id.uid = %d and (item_restrict & %d)",
+ $r = q("select mid,sid from item left join item_id on iid = item.id where service = 'PDL' and item.uid = item_id.uid and item_id.uid = %d and (item_restrict & %d)>0",
intval($channel_id),
intval(ITEM_PDL)
);
@@ -1551,9 +1574,6 @@ function layout_select($channel_id, $current = '') {
}
-
-
-
function mimetype_select($channel_id, $current = 'text/bbcode') {
$x = array(
@@ -1584,19 +1604,17 @@ function mimetype_select($channel_id, $current = 'text/bbcode') {
$o .= '</select>';
return $o;
-
}
-
function lang_selector() {
global $a;
-
+
$langs = glob('view/*/strings.php');
-
+
$lang_options = array();
$selected = "";
-
+
if(is_array($langs) && count($langs)) {
$langs[] = '';
if(! in_array('view/en/strings.php',$langs))
@@ -1614,25 +1632,24 @@ function lang_selector() {
}
}
- $tpl = get_markup_template("lang_selector.tpl");
+ $tpl = get_markup_template("lang_selector.tpl");
$o = replace_macros($tpl, array(
'$title' => t('Select an alternate language'),
'$langs' => array($lang_options, $selected),
));
+
return $o;
}
-
function return_bytes ($size_str) {
- switch (substr ($size_str, -1))
- {
- case 'M': case 'm': return (int)$size_str * 1048576;
- case 'K': case 'k': return (int)$size_str * 1024;
- case 'G': case 'g': return (int)$size_str * 1073741824;
- default: return $size_str;
- }
+ switch (substr ($size_str, -1)) {
+ case 'M': case 'm': return (int)$size_str * 1048576;
+ case 'K': case 'k': return (int)$size_str * 1024;
+ case 'G': case 'g': return (int)$size_str * 1073741824;
+ default: return $size_str;
+ }
}
@@ -1647,7 +1664,6 @@ function base64url_encode($s, $strip_padding = true) {
}
function base64url_decode($s) {
-
if(is_array($s)) {
logger('base64url_decode: illegal input: ' . print_r(debug_backtrace(), true));
return $s;
@@ -1655,7 +1671,11 @@ function base64url_decode($s) {
return base64_decode(strtr($s,'-_','+/'));
}
-
+/**
+ * @ Return a div to clear floats.
+ *
+ * @return string
+ */
function cleardiv() {
return '<div class="clear"></div>';
}
@@ -1673,7 +1693,7 @@ function bb_translate_video($s) {
$s = str_replace($mtch[0],'[vimeo]' . $mtch[1] . '[/vimeo]',$s);
}
}
- return $s;
+ return $s;
}
function html2bb_video($s) {
@@ -1693,61 +1713,57 @@ function html2bb_video($s) {
/**
* apply xmlify() to all values of array $val, recursively
*/
-function array_xmlify($val){
+function array_xmlify($val) {
if (is_bool($val)) return $val?"true":"false";
if (is_array($val)) return array_map('array_xmlify', $val);
return xmlify((string) $val);
}
-function reltoabs($text, $base)
-{
- if (empty($base))
- return $text;
+function reltoabs($text, $base) {
+ if (empty($base))
+ return $text;
- $base = rtrim($base,'/');
+ $base = rtrim($base,'/');
- $base2 = $base . "/";
-
- // Replace links
- $pattern = "/<a([^>]*) href=\"(?!http|https|\/)([^\"]*)\"/";
- $replace = "<a\${1} href=\"" . $base2 . "\${2}\"";
- $text = preg_replace($pattern, $replace, $text);
+ $base2 = $base . "/";
- $pattern = "/<a([^>]*) href=\"(?!http|https)([^\"]*)\"/";
- $replace = "<a\${1} href=\"" . $base . "\${2}\"";
- $text = preg_replace($pattern, $replace, $text);
+ // Replace links
+ $pattern = "/<a([^>]*) href=\"(?!http|https|\/)([^\"]*)\"/";
+ $replace = "<a\${1} href=\"" . $base2 . "\${2}\"";
+ $text = preg_replace($pattern, $replace, $text);
- // Replace images
- $pattern = "/<img([^>]*) src=\"(?!http|https|\/)([^\"]*)\"/";
- $replace = "<img\${1} src=\"" . $base2 . "\${2}\"";
- $text = preg_replace($pattern, $replace, $text);
+ $pattern = "/<a([^>]*) href=\"(?!http|https)([^\"]*)\"/";
+ $replace = "<a\${1} href=\"" . $base . "\${2}\"";
+ $text = preg_replace($pattern, $replace, $text);
- $pattern = "/<img([^>]*) src=\"(?!http|https)([^\"]*)\"/";
- $replace = "<img\${1} src=\"" . $base . "\${2}\"";
- $text = preg_replace($pattern, $replace, $text);
+ // Replace images
+ $pattern = "/<img([^>]*) src=\"(?!http|https|\/)([^\"]*)\"/";
+ $replace = "<img\${1} src=\"" . $base2 . "\${2}\"";
+ $text = preg_replace($pattern, $replace, $text);
+ $pattern = "/<img([^>]*) src=\"(?!http|https)([^\"]*)\"/";
+ $replace = "<img\${1} src=\"" . $base . "\${2}\"";
+ $text = preg_replace($pattern, $replace, $text);
- // Done
- return $text;
+ // Done
+ return $text;
}
function item_post_type($item) {
-
-
- switch($item['resource_type']) {
- case 'photo':
- $post_type = t('photo');
- break;
- case 'event':
- $post_type = t('event');
- break;
- default:
- $post_type = t('status');
- if($item['mid'] != $item['parent_mid'])
- $post_type = t('comment');
- break;
- }
+ switch($item['resource_type']) {
+ case 'photo':
+ $post_type = t('photo');
+ break;
+ case 'event':
+ $post_type = t('event');
+ break;
+ default:
+ $post_type = t('status');
+ if($item['mid'] != $item['parent_mid'])
+ $post_type = t('comment');
+ break;
+ }
if(strlen($item['verb']) && (! activity_match($item['verb'],ACTIVITY_POST)))
$post_type = t('activity');
@@ -1789,6 +1805,7 @@ function is_a_date_arg($s) {
return true;
}
}
+
return false;
}
@@ -1808,8 +1825,13 @@ function legal_webbie($s) {
function check_webbie($arr) {
+ $reservechan = get_config('system','reserved_channels');
+ if(strlen($reservechan))
+ $taken = explode(',', $reservechan);
+ else
+ $taken = array();
+
$str = '';
- $taken = array();
if(count($arr)) {
foreach($arr as $x) {
$y = legal_webbie($x);
@@ -1835,14 +1857,16 @@ function check_webbie($arr) {
}
}
}
+
return '';
}
-
+
function ids_to_querystr($arr,$idx = 'id') {
$t = array();
foreach($arr as $x)
$t[] = $x[$idx];
+
return(implode(',', $t));
}
@@ -1850,9 +1874,17 @@ function ids_to_querystr($arr,$idx = 'id') {
// author_xchan and owner_xchan. If $abook is true also include the abook info.
// This is needed in the API to save extra per item lookups there.
-function xchan_query(&$items,$abook = true) {
+function xchan_query(&$items,$abook = true,$effective_uid = 0) {
$arr = array();
if($items && count($items)) {
+
+ if($effective_uid) {
+ for($x = 0; $x < count($items); $x ++) {
+ $items[$x]['real_uid'] = $items[$x]['uid'];
+ $items[$x]['uid'] = $effective_uid;
+ }
+ }
+
foreach($items as $item) {
if($item['owner_xchan'] && (! in_array($item['owner_xchan'],$arr)))
$arr[] = "'" . dbesc($item['owner_xchan']) . "'";
@@ -1863,13 +1895,13 @@ function xchan_query(&$items,$abook = true) {
if(count($arr)) {
if($abook) {
$chans = q("select * from xchan left join hubloc on hubloc_hash = xchan_hash left join abook on abook_xchan = xchan_hash and abook_channel = %d
- where xchan_hash in (" . implode(',', $arr) . ") and ( hubloc_flags & " . intval(HUBLOC_FLAGS_PRIMARY) . " )",
+ where xchan_hash in (" . implode(',', $arr) . ") and ( hubloc_flags & " . intval(HUBLOC_FLAGS_PRIMARY) . " )>0",
intval($item['uid'])
);
}
else {
$chans = q("select xchan.*,hubloc.* from xchan left join hubloc on hubloc_hash = xchan_hash
- where xchan_hash in (" . implode(',', $arr) . ") and ( hubloc_flags & " . intval(HUBLOC_FLAGS_PRIMARY) . " )");
+ where xchan_hash in (" . implode(',', $arr) . ") and ( hubloc_flags & " . intval(HUBLOC_FLAGS_PRIMARY) . " )>0");
}
$xchans = q("select * from xchan where xchan_hash in (" . implode(',',$arr) . ") and xchan_network in ('rss','unknown')");
if(! $chans)
@@ -1883,7 +1915,6 @@ function xchan_query(&$items,$abook = true) {
$items[$x]['author'] = find_xchan_in_array($items[$x]['author_xchan'],$chans);
}
}
-
}
function xchan_mail_query(&$item) {
@@ -1898,7 +1929,7 @@ function xchan_mail_query(&$item) {
if(count($arr)) {
$chans = q("select xchan.*,hubloc.* from xchan left join hubloc on hubloc_hash = xchan_hash
- where xchan_hash in (" . implode(',', $arr) . ") and ( hubloc_flags & " . intval(HUBLOC_FLAGS_PRIMARY) . " )");
+ where xchan_hash in (" . implode(',', $arr) . ") and ( hubloc_flags & " . intval(HUBLOC_FLAGS_PRIMARY) . " )>0");
}
if($chans) {
$item['from'] = find_xchan_in_array($item['from_xchan'],$chans);
@@ -1923,6 +1954,7 @@ function get_rel_link($j,$rel) {
foreach($j as $l)
if($l['rel'] === $rel)
return $l['href'];
+
return '';
}
@@ -1932,7 +1964,7 @@ function get_rel_link($j,$rel) {
function magic_link($s) {
return $s;
}
-
+
// if $escape is true, dbesc() each element before adding quotes
function stringify_array_elms(&$arr,$escape = false) {
@@ -1949,23 +1981,22 @@ function stringify_array_elms(&$arr,$escape = false) {
*/
function jindent($json) {
- $result = '';
- $pos = 0;
- $strLen = strlen($json);
- $indentStr = ' ';
- $newLine = "\n";
- $prevChar = '';
+ $result = '';
+ $pos = 0;
+ $strLen = strlen($json);
+ $indentStr = ' ';
+ $newLine = "\n";
+ $prevChar = '';
$outOfQuotes = true;
for ($i=0; $i<=$strLen; $i++) {
-
// Grab the next character in the string.
$char = substr($json, $i, 1);
// Are we inside a quoted string?
if ($char == '"' && $prevChar != '\\') {
$outOfQuotes = !$outOfQuotes;
-
+
// If this character is the end of an element,
// output a new line and indent the next line.
} else if(($char == '}' || $char == ']') && $outOfQuotes) {
@@ -1975,7 +2006,7 @@ function jindent($json) {
$result .= $indentStr;
}
}
-
+
// Add the character to the result string.
$result .= $char;
@@ -1986,7 +2017,7 @@ function jindent($json) {
if ($char == '{' || $char == '[') {
$pos ++;
}
-
+
for ($j = 0; $j < $pos; $j++) {
$result .= $indentStr;
}
@@ -2000,22 +2031,31 @@ function jindent($json) {
function json_decode_plus($s) {
-
$x = json_decode($s,true);
if(! $x)
$x = json_decode(str_replace(array('\\"','\\\\'),array('"','\\'),$s),true);
- return $x;
+ return $x;
}
function design_tools() {
+
$channel = get_app()->get_channel();
+ $sys = false;
+
+ if(get_app()->is_sys && is_site_admin()) {
+ require_once('include/identity.php');
+ $channel = get_sys_channel();
+ $sys = true;
+ }
+
$who = $channel['channel_address'];
return replace_macros(get_markup_template('design_tools.tpl'), array(
'$title' => t('Design'),
'$who' => $who,
+ '$sys' => $sys,
'$blocks' => t('Blocks'),
'$menus' => t('Menus'),
'$layout' => t('Layouts'),
@@ -2033,4 +2073,279 @@ function normalise_openid($s) {
return trim(str_replace(array('http://','https://'),array('',''),$s),'/');
}
+// used in ajax endless scroll request to find out all the args that the master page was viewing.
+// This was using $_REQUEST, but $_REQUEST also contains all your cookies. So we're restricting it
+// to $_GET and $_POST.
+
+function extra_query_args() {
+ $s = '';
+ if(count($_GET)) {
+ foreach($_GET as $k => $v) {
+ // these are request vars we don't want to duplicate
+ if(! in_array($k, array('q','f','zid','page','PHPSESSID'))) {
+ $s .= '&' . $k . '=' . urlencode($v);
+ }
+ }
+ }
+ if(count($_POST)) {
+ foreach($_POST as $k => $v) {
+ // these are request vars we don't want to duplicate
+ if(! in_array($k, array('q','f','zid','page','PHPSESSID'))) {
+ $s .= '&' . $k . '=' . urlencode($v);
+ }
+ }
+ }
+ return $s;
+}
+
+/**
+ * This function removes the tag $tag from the text $body and replaces it with
+ * the appropiate link.
+ *
+ * @param unknown_type $body the text to replace the tag in
+ * @param unknown_type $access_tag - used to return tag ACL exclusions e.g. @!foo
+ * @param unknown_type $str_tags string to add the tag to
+ * @param unknown_type $profile_uid
+ * @param unknown_type $tag the tag to replace
+ *
+ * @return boolean true if replaced, false if not replaced
+ */
+function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag) {
+
+ $replaced = false;
+ $r = null;
+
+ $termtype = ((strpos($tag,'#') === 0) ? TERM_HASHTAG : TERM_UNKNOWN);
+ $termtype = ((strpos($tag,'@') === 0) ? TERM_MENTION : $termtype);
+ $termtype = ((strpos($tag,'#^[') === 0) ? TERM_BOOKMARK : $termtype);
+
+ //is it a hash tag?
+ if(strpos($tag,'#') === 0) {
+ if(strpos($tag,'#^[') === 0) {
+ if(preg_match('/#\^\[(url|zrl)(.*?)\](.*?)\[\/(url|zrl)\]/',$tag,$match)) {
+ $basetag = $match[3];
+ $url = ((substr($match[2],0,1) === '=') ? substr($match[2],1) : $match[3]);
+ $replaced = true;
+ }
+ }
+ // if the tag is already replaced...
+ elseif((strpos($tag,'[zrl=')) || (strpos($tag,'[url='))) {
+ //...do nothing
+ return $replaced;
+ }
+ if($tag == '#getzot') {
+ $basetag = 'getzot';
+ $url = 'https://redmatrix.me';
+ $newtag = '#[zrl=' . $url . ']' . $basetag . '[/zrl]';
+ $body = str_replace($tag,$newtag,$body);
+ $replaced = true;
+ }
+ if(! $replaced) {
+
+ //base tag has the tags name only
+
+ if((substr($tag,0,7) === '#&quot;') && (substr($tag,-6,6) === '&quot;')) {
+ $basetag = substr($tag,7);
+ $basetag = substr($basetag,0,-6);
+ }
+ else
+ $basetag = str_replace('_',' ',substr($tag,1));
+
+ //create text for link
+ $url = $a->get_baseurl() . '/search?tag=' . rawurlencode($basetag);
+ $newtag = '#[zrl=' . $a->get_baseurl() . '/search?tag=' . rawurlencode($basetag) . ']' . $basetag . '[/zrl]';
+ //replace tag by the link
+ $body = str_replace($tag, $newtag, $body);
+ $replaced = true;
+ }
+ //is the link already in str_tags?
+ if(! stristr($str_tags,$newtag)) {
+ //append or set str_tags
+ if(strlen($str_tags))
+ $str_tags .= ',';
+
+ $str_tags .= $newtag;
+ }
+ return array('replaced' => $replaced, 'termtype' => $termtype, 'term' => $basetag, 'url' => $url, 'contact' => $r[0]);
+ }
+
+ //is it a person tag?
+
+ if(strpos($tag,'@') === 0) {
+
+ // The @! tag will alter permissions
+ $exclusive = ((strpos($tag,'!') === 1) ? true : false);
+
+ //is it already replaced?
+ if(strpos($tag,'[zrl='))
+ return $replaced;
+
+ //get the person's name
+
+ $name = substr($tag,(($exclusive) ? 2 : 1)); // The name or name fragment we are going to replace
+ $newname = $name; // a copy that we can mess with
+ $tagcid = 0;
+
+ $r = null;
+
+ // is it some generated name?
+
+ $forum = false;
+ $trailing_plus_name = false;
+
+ // @channel+ is a forum or network delivery tag
+
+ if(substr($newname,-1,1) === '+') {
+ $forum = true;
+ $newname = substr($newname,0,-1);
+ }
+
+ // Here we're looking for an address book entry as provided by the auto-completer
+ // of the form something+nnn where nnn is an abook_id or the first chars of xchan_hash
+
+ if(strrpos($newname,'+')) {
+ //get the id
+
+ if(strrpos($tagcid,' '))
+ $tagcid = substr($tagcid,0,strrpos($tagcid,' '));
+
+ $tagcid = substr($newname,strrpos($newname,'+') + 1);
+
+ if(strlen($tagcid) < 16)
+ $abook_id = intval($tagcid);
+ //remove the next word from tag's name
+ if(strpos($name,' ')) {
+ $name = substr($name,0,strpos($name,' '));
+ }
+
+ if($abook_id) { // if there was an id
+ // select channel with that id from the logged in user's address book
+ $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash
+ WHERE abook_id = %d AND abook_channel = %d LIMIT 1",
+ intval($abook_id),
+ intval($profile_uid)
+ );
+ }
+ else {
+ $r = q("SELECT * FROM xchan
+ WHERE xchan_hash like '%s%%' LIMIT 1",
+ dbesc($tagcid)
+ );
+ }
+ }
+
+ if(! $r) {
+
+ // look for matching names in the address book
+
+ // Two ways to deal with spaces - double quote the name or use underscores
+ // we see this after input filtering so quotes have been html entity encoded
+
+ if((substr($name,0,6) === '&quot;') && (substr($name,-6,6) === '&quot;')) {
+ $newname = substr($name,6);
+ $newname = substr($newname,0,-6);
+ }
+ else
+ $newname = str_replace('_',' ',$name);
+
+ // do this bit over since we started over with $name
+
+ if(substr($newname,-1,1) === '+') {
+ $forum = true;
+ $newname = substr($newname,0,-1);
+ }
+
+ //select someone from this user's contacts by name
+ $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash
+ WHERE xchan_name = '%s' AND abook_channel = %d LIMIT 1",
+ dbesc($newname),
+ intval($profile_uid)
+ );
+ if(! $r) {
+ //select someone by attag or nick and the name passed in
+ $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash
+ WHERE xchan_addr like ('%s') AND abook_channel = %d LIMIT 1",
+ dbesc(((strpos($newname,'@')) ? $newname : $newname . '@%')),
+ intval($profile_uid)
+ );
+ }
+
+ if(! $r) {
+ // it's possible somebody has a name ending with '+', which we stripped off as a forum indicator
+ // This is very rare but we want to get it right.
+
+ $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash
+ WHERE xchan_name = '%s' AND abook_channel = %d LIMIT 1",
+ dbesc($newname . '+'),
+ intval($profile_uid)
+ );
+ if($r)
+ $trailing_plus_name = true;
+ }
+ }
+
+ // $r is set if we found something
+
+ $channel = get_app()->get_channel();
+
+ if($r) {
+ $profile = $r[0]['xchan_url'];
+ $newname = $r[0]['xchan_name'];
+ // add the channel's xchan_hash to $access_tag if exclusive
+ if($exclusive) {
+ $access_tag .= 'cid:' . $r[0]['xchan_hash'];
+ }
+ }
+ else {
+ // check for a group/collection exclusion tag
+
+ // note that we aren't setting $replaced even though we're replacing text.
+ // This tag isn't going to get a term attached to it. It's only used for
+ // access control. The link points to out own channel just so it doesn't look
+ // weird - as all the other tags are linked to something.
+
+ if(local_user() && local_user() == $profile_uid) {
+ require_once('include/group.php');
+ $grp = group_byname($profile_uid,$name);
+
+ if($grp) {
+ $g = q("select hash from groups where id = %d and visible = 1 limit 1",
+ intval($grp)
+ );
+ if($g && $exclusive) {
+ $access_tag .= 'gid:' . $g[0]['hash'];
+ }
+ $channel = get_app()->get_channel();
+ if($channel) {
+ $newtag = '@' . (($exclusive) ? '!' : '') . '[zrl=' . z_root() . '/channel/' . $channel['channel_address'] . ']' . $newname . '[/zrl]';
+ $body = str_replace('@' . (($exclusive) ? '!' : '') . $name, $newtag, $body);
+ }
+ }
+ }
+ }
+
+ if(($exclusive) && (! $access_tag)) {
+ $access_tag .= 'cid:' . $channel['channel_hash'];
+ }
+
+ // if there is an url for this channel
+
+ if(isset($profile)) {
+ $replaced = true;
+ //create profile link
+ $profile = str_replace(',','%2c',$profile);
+ $url = $profile;
+ $newtag = '@' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . (($forum && ! $trailing_plus_name) ? '+' : '') . '[/zrl]';
+ $body = str_replace('@' . (($exclusive) ? '!' : '') . $name, $newtag, $body);
+ //append tag to str_tags
+ if(! stristr($str_tags,$newtag)) {
+ if(strlen($str_tags))
+ $str_tags .= ',';
+ $str_tags .= $newtag;
+ }
+ }
+ }
+
+ return array('replaced' => $replaced, 'termtype' => $termtype, 'term' => $newname, 'url' => $url, 'contact' => $r[0]);
+}
diff --git a/include/widgets.php b/include/widgets.php
index 1aa018fb6..bb9890add 100644
--- a/include/widgets.php
+++ b/include/widgets.php
@@ -1,4 +1,9 @@
-<?php /** @file */
+<?php
+/**
+ * @file include/widgets.php
+ *
+ * @brief This file contains the widgets.
+ */
require_once('include/dir_fns.php');
require_once('include/contact_widgets.php');
@@ -6,7 +11,7 @@ require_once('include/contact_widgets.php');
function widget_profile($args) {
$a = get_app();
- $block = (((get_config('system','block_public')) && (! local_user()) && (! remote_user())) ? true : false);
+ $block = (((get_config('system', 'block_public')) && (! local_user()) && (! remote_user())) ? true : false);
return profile_sidebar($a->profile, $block, true);
}
@@ -16,14 +21,15 @@ function widget_profile($args) {
function widget_tagcloud($args) {
$o = '';
- $tab = 0;
+ //$tab = 0;
$a = get_app();
$uid = $a->profile_uid;
$count = ((x($args,'count')) ? intval($args['count']) : 24);
$flags = 0;
$type = TERM_CATEGORY;
- $r = tagadelic($uid,$count,$authors,$flags,ITEM_WEBPAGE,$type);
+ // FIXME there exists no $authors variable
+ $r = tagadelic($uid, $count, $authors, $flags, ITEM_WEBPAGE, $type);
if($r) {
$o = '<div class="tagblock widget"><h3>' . t('Categories') . '</h3><div class="tags" align="center">';
@@ -75,9 +81,8 @@ function widget_collections($args) {
return '';
break;
}
-
- return group_side($every, $each, $edit, $current, $abook_id, $wmode);
+ return group_side($every, $each, $edit, $current, $abook_id, $wmode);
}
@@ -93,7 +98,6 @@ function widget_appselect($arr) {
}
-
function widget_suggestions($arr) {
if((! local_user()) || (! feature_enabled(local_user(),'suggest')))
@@ -114,16 +118,13 @@ function widget_suggestions($arr) {
// This will throw some entropy intot he situation so you won't
// be looking at the same two mug shots every time the widget runs
-
$index = ((count($r) > 2) ? mt_rand(0,count($r) - 2) : 0);
-
for($x = $index; $x <= ($index+1); $x ++) {
-
$rr = $r[$x];
if(! $rr['xchan_url'])
break;
-
+
$connlnk = z_root() . '/follow/?url=' . $rr['xchan_addr'];
$arr[] = array(
@@ -138,7 +139,6 @@ function widget_suggestions($arr) {
);
}
-
$o = replace_macros(get_markup_template('suggest_widget.tpl'),array(
'$title' => t('Suggestions'),
'$more' => t('See more...'),
@@ -146,16 +146,16 @@ function widget_suggestions($arr) {
));
return $o;
-
}
function widget_follow($args) {
if(! local_user())
return '';
+
$a = get_app();
$uid =$a->channel['channel_id'];
- $r = q("select count(*) as total from abook where abook_channel = %d and not (abook_flags & %d) ",
+ $r = q("select count(*) as total from abook where abook_channel = %d and not (abook_flags & %d)>0 ",
intval($uid),
intval(ABOOK_FLAG_SELF)
);
@@ -163,10 +163,10 @@ function widget_follow($args) {
$total_channels = $r[0]['total'];
$limit = service_class_fetch($uid,'total_channels');
if($limit !== false) {
- $abook_usage_message = sprintf( t("You have %1$.0f of %2$.0f allowed connections."), $total_channels, $limit);
+ $abook_usage_message = sprintf( t("You have %1$.0f of %2$.0f allowed connections."), $total_channels, $limit);
}
else {
- $abook_usage_message = '';
+ $abook_usage_message = '';
}
return replace_macros(get_markup_template('follow.tpl'),array(
'$connect' => t('Add New Connection'),
@@ -175,7 +175,6 @@ function widget_follow($args) {
'$follow' => t('Connect'),
'$abook_usage_message' => $abook_usage_message
));
-
}
@@ -192,6 +191,7 @@ function widget_notes($arr) {
'$text' => $text,
'$save' => t('Save'),
));
+
return $o;
}
@@ -220,7 +220,7 @@ function widget_savedsearch($arr) {
}
if(x($_GET,'searchremove') && $search) {
- q("delete from `term` where `uid` = %d and `type` = %d and `term` = '%s' limit 1",
+ q("delete from `term` where `uid` = %d and `type` = %d and `term` = '%s'",
intval(local_user()),
intval(TERM_SAVEDSEARCH),
dbesc($search)
@@ -228,8 +228,6 @@ function widget_savedsearch($arr) {
$search = '';
}
-
-
$srchurl = $a->query_string;
$srchurl = rtrim(preg_replace('/searchsave\=[^\&].*?(\&|$)/is','',$srchurl),'&');
@@ -238,9 +236,13 @@ function widget_savedsearch($arr) {
$hasq = ((strpos($srchurl,'?') !== false) ? true : false);
$srchurl = rtrim(preg_replace('/search\=[^\&].*?(\&|$)/is','',$srchurl),'&');
+ $srchurl = rtrim(preg_replace('/submit\=[^\&].*?(\&|$)/is','',$srchurl),'&');
$srchurl = str_replace(array('?f=','&f='),array('',''),$srchurl);
+
+
$hasq = ((strpos($srchurl,'?') !== false) ? true : false);
-
+
+
$o = '';
$r = q("select `tid`,`term` from `term` WHERE `uid` = %d and `type` = %d ",
@@ -252,31 +254,28 @@ function widget_savedsearch($arr) {
if(count($r)) {
foreach($r as $rr) {
-
$saved[] = array(
'id' => $rr['tid'],
- 'term' => $rr['term'],
+ 'term' => $rr['term'],
'dellink' => z_root() . '/' . $srchurl . (($hasq) ? '' : '?f=') . '&amp;searchremove=1&amp;search=' . urlencode($rr['term']),
'srchlink' => z_root() . '/' . $srchurl . (($hasq) ? '' : '?f=') . '&amp;search=' . urlencode($rr['term']),
'displayterm' => htmlspecialchars($rr['term'], ENT_COMPAT,'UTF-8'),
- 'encodedterm' => urlencode($rr['term']),
- 'delete' => t('Remove term'),
- 'selected' => ($search==$rr['term']),
+ 'encodedterm' => urlencode($rr['term']),
+ 'delete' => t('Remove term'),
+ 'selected' => ($search==$rr['term']),
);
}
- }
+ }
-
$tpl = get_markup_template("saved_searches.tpl");
$o = replace_macros($tpl, array(
'$title' => t('Saved Searches'),
'$add' => t('add'),
- '$searchbox' => searchbox($search,'netsearch-box',$srchurl . (($hasq) ? '' : '?f='),true),
+ '$searchbox' => searchbox($search, 'netsearch-box', $srchurl . (($hasq) ? '' : '?f='), true),
'$saved' => $saved,
));
return $o;
-
}
@@ -306,7 +305,6 @@ function widget_filer($arr) {
'$all' => t('Everything'),
'$terms' => $terms,
'$base' => z_root() . '/' . $a->cmd
-
));
}
@@ -327,22 +325,32 @@ function widget_archive($arr) {
if(! perm_is_allowed($uid,get_observer_hash(),'view_stream'))
return '';
-
$wall = ((array_key_exists('wall', $arr)) ? intval($arr['wall']) : 0);
$style = ((array_key_exists('style', $arr)) ? $arr['style'] : 'select');
- $url = z_root() . '/' . $a->cmd;
+ $showend = ((get_pconfig($uid,'system','archive_show_end_date')) ? true : false);
+ $mindate = get_pconfig($uid,'system','archive_mindate');
+ $visible_years = get_pconfig($uid,'system','archive_visible_years');
+ if(! $visible_years)
+ $visible_years = 5;
+ $url = z_root() . '/' . $a->cmd;
- $ret = list_post_dates($uid,$wall);
+ $ret = list_post_dates($uid,$wall,$mindate);
if(! count($ret))
return '';
+ $cutoff_year = intval(datetime_convert('',date_default_timezone_get(),'now','Y')) - $visible_years;
+ $cutoff = ((array_key_exists($cutoff_year,$ret))? true : false);
+
$o = replace_macros(get_markup_template('posted_date_widget.tpl'),array(
'$title' => t('Archives'),
- '$size' => ((count($ret) > 6) ? 6 : count($ret)),
+ '$size' => $visible_years,
+ '$cutoff_year' => $cutoff_year,
+ '$cutoff' => $cutoff,
'$url' => $url,
'$style' => $style,
+ '$showend' => $showend,
'$dates' => $ret
));
return $o;
@@ -354,7 +362,7 @@ function widget_fullprofile($arr) {
if(! $a->profile['profile_uid'])
return;
- $block = (((get_config('system','block_public')) && (! local_user()) && (! remote_user())) ? true : false);
+ $block = (((get_config('system', 'block_public')) && (! local_user()) && (! remote_user())) ? true : false);
return profile_sidebar($a->profile, $block);
}
@@ -362,32 +370,45 @@ function widget_fullprofile($arr) {
function widget_categories($arr) {
$a = get_app();
-
if($a->profile['profile_uid'] && (! perm_is_allowed($a->profile['profile_uid'],get_observer_hash(),'view_stream')))
return '';
-
$cat = ((x($_REQUEST,'cat')) ? htmlspecialchars($_REQUEST['cat'],ENT_COMPAT,'UTF-8') : '');
$srchurl = $a->query_string;
$srchurl = rtrim(preg_replace('/cat\=[^\&].*?(\&|$)/is','',$srchurl),'&');
$srchurl = str_replace(array('?f=','&f='),array('',''),$srchurl);
- return categories_widget($srchurl,$cat);
+ return categories_widget($srchurl, $cat);
}
function widget_tagcloud_wall($arr) {
$a = get_app();
+
if((! $a->profile['profile_uid']) || (! $a->profile['channel_hash']))
return '';
- if(! perm_is_allowed($a->profile['profile_uid'],get_observer_hash(),'view_stream'))
+ if(! perm_is_allowed($a->profile['profile_uid'], get_observer_hash(), 'view_stream'))
return '';
- $limit = ((array_key_exists('limit',$arr)) ? intval($arr['limit']) : 50);
- if(feature_enabled($a->profile['profile_uid'],'tagadelic'))
- return tagblock('search',$a->profile['profile_uid'],$limit,$a->profile['channel_hash'],ITEM_WALL);
+ $limit = ((array_key_exists('limit', $arr)) ? intval($arr['limit']) : 50);
+ if(feature_enabled($a->profile['profile_uid'], 'tagadelic'))
+ return wtagblock($a->profile['profile_uid'], $limit, $a->profile['channel_hash'], ITEM_WALL);
+
return '';
}
+function widget_catcloud_wall($arr) {
+ $a = get_app();
+
+ if((! $a->profile['profile_uid']) || (! $a->profile['channel_hash']))
+ return '';
+ if(! perm_is_allowed($a->profile['profile_uid'], get_observer_hash(), 'view_stream'))
+ return '';
+
+ $limit = ((array_key_exists('limit',$arr)) ? intval($arr['limit']) : 50);
+
+ return catblock($a->profile['profile_uid'], $limit, $a->profile['channel_hash'], ITEM_WALL);
+}
+
function widget_affinity($arr) {
@@ -414,6 +435,7 @@ function widget_affinity($arr) {
call_hooks('main_slider',$arr);
return $arr['html'];
}
+
return '';
}
@@ -430,27 +452,28 @@ function widget_settings_menu($arr) {
// Retrieve the 'self' address book entry for use in the auto-permissions link
- $abk = q("select abook_id from abook where abook_channel = %d and ( abook_flags & %d ) limit 1",
+ $role = get_pconfig(local_user(),'system','permissions_role');
+
+ $abk = q("select abook_id from abook where abook_channel = %d and ( abook_flags & %d )>0 limit 1",
intval(local_user()),
intval(ABOOK_FLAG_SELF)
);
if($abk)
$abook_self_id = $abk[0]['abook_id'];
-
$tabs = array(
array(
'label' => t('Account settings'),
'url' => $a->get_baseurl(true).'/settings/account',
'selected' => ((argv(1) === 'account') ? 'active' : ''),
),
-
+
array(
'label' => t('Channel settings'),
'url' => $a->get_baseurl(true).'/settings/channel',
'selected' => ((argv(1) === 'channel') ? 'active' : ''),
),
-
+
array(
'label' => t('Additional features'),
'url' => $a->get_baseurl(true).'/settings/features',
@@ -468,7 +491,7 @@ function widget_settings_menu($arr) {
'url' => $a->get_baseurl(true).'/settings/display',
'selected' => ((argv(1) === 'display') ? 'active' : ''),
),
-
+
array(
'label' => t('Connected apps'),
'url' => $a->get_baseurl(true) . '/settings/oauth',
@@ -477,24 +500,19 @@ function widget_settings_menu($arr) {
array(
'label' => t('Export channel'),
- 'url' => $a->get_baseurl(true) . '/uexport/basic',
+ 'url' => $a->get_baseurl(true) . '/uexport',
'selected' => ''
),
-// array(
-// 'label' => t('Export account'),
-// 'url' => $a->get_baseurl(true) . '/uexport/complete',
-// 'selected' => ''
-// ),
+ );
- array(
- 'label' => t('Automatic Permissions (Advanced)'),
+ if($role === false || $role === 'custom') {
+ $tabs[] = array(
+ 'label' => t('Connection Default Permissions'),
'url' => $a->get_baseurl(true) . '/connedit/' . $abook_self_id,
'selected' => ''
- ),
-
-
- );
+ );
+ }
if(feature_enabled(local_user(),'premium_channel')) {
$tabs[] = array(
@@ -502,7 +520,6 @@ function widget_settings_menu($arr) {
'url' => $a->get_baseurl(true) . '/connect/' . $channel['channel_address'],
'selected' => ''
);
-
}
if(feature_enabled(local_user(),'channel_sources')) {
@@ -511,18 +528,14 @@ function widget_settings_menu($arr) {
'url' => $a->get_baseurl(true) . '/sources',
'selected' => ''
);
-
}
-
-
$tabtpl = get_markup_template("generic_links_widget.tpl");
return replace_macros($tabtpl, array(
'$title' => t('Settings'),
'$class' => 'settings-widget',
'$items' => $tabs,
));
-
}
@@ -532,8 +545,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(
'label' => t('Check Mail'),
'url' => $a->get_baseurl(true) . '/message',
@@ -544,9 +557,7 @@ function widget_mailmenu($arr) {
'url' => $a->get_baseurl(true) . '/mail/new',
'sel'=> (argv(1) == 'new'),
)
-
));
-
}
function widget_design_tools($arr) {
@@ -556,8 +567,8 @@ function widget_design_tools($arr) {
// otherwise local_user() is sufficient for permissions.
if($a->profile['profile_uid'])
- if($a->profile['profile_uid'] != local_user())
- return '';
+ if(($a->profile['profile_uid'] != local_user()) && (! $a->is_sys))
+ return '';
if(! local_user())
return '';
@@ -572,23 +583,25 @@ function widget_findpeople($arr) {
function widget_photo_albums($arr) {
$a = get_app();
+
if(! $a->profile['profile_uid'])
return '';
$channelx = channelx_by_n($a->profile['profile_uid']);
- if((! $channelx) || (! perm_is_allowed($a->profile['profile_uid'],get_observer_hash(),'view_photos')))
+ if((! $channelx) || (! perm_is_allowed($a->profile['profile_uid'], get_observer_hash(), 'view_photos')))
return '';
- return photos_album_widget($channelx,$a->get_observer());
+ require_once('include/photos.php');
+ return photos_album_widget($channelx, $a->get_observer());
}
function widget_vcard($arr) {
require_once ('include/Contact.php');
- return vcard_from_xchan('',get_app()->get_observer());
+ return vcard_from_xchan('', get_app()->get_observer());
}
-/**
+/*
* The following directory widgets are only useful on the directory page
*/
@@ -601,21 +614,23 @@ function widget_dirsort($arr) {
}
function widget_dirtags($arr) {
- return dir_tagblock(z_root() . '/directory',null);
+ return dir_tagblock(z_root() . '/directory', null);
}
function widget_menu_preview($arr) {
if(! get_app()->data['menu_item'])
return;
require_once('include/menu.php');
+
return menu_render(get_app()->data['menu_item']);
}
function widget_chatroom_list($arr) {
$a = get_app();
+
require_once("include/chat.php");
$r = chatroom_list($a->profile['profile_uid']);
- return replace_macros(get_markup_template('chatroomlist.tpl'),array(
+ return replace_macros(get_markup_template('chatroomlist.tpl'), array(
'$header' => t('Chat Rooms'),
'$baseurl' => z_root(),
'$nickname' => $a->profile['channel_address'],
@@ -628,11 +643,13 @@ function widget_bookmarkedchats($arr) {
if(! $h)
return;
$r = q("select * from xchat where xchat_xchan = '%s' group by xchat_url order by xchat_desc",
- dbesc($h)
+ dbesc($h)
);
-
- for($x = 0; $x < count($r); $x ++)
- $r[$x]['xchat_url'] = zid($r[$x]['xchat_url']);
+ if($r) {
+ for($x = 0; $x < count($r); $x ++) {
+ $r[$x]['xchat_url'] = zid($r[$x]['xchat_url']);
+ }
+ }
return replace_macros(get_markup_template('bookmarkedchats.tpl'),array(
'$header' => t('Bookmarked Chatrooms'),
'$rooms' => $r
@@ -648,9 +665,11 @@ function widget_suggestedchats($arr) {
if(! $h)
return;
$r = q("select *, count(xchat_url) as total from xchat group by xchat_url order by total desc, xchat_desc limit 24");
-
- for($x = 0; $x < count($r); $x ++)
- $r[$x]['xchat_url'] = zid($r[$x]['xchat_url']);
+ if($r) {
+ for($x = 0; $x < count($r); $x ++) {
+ $r[$x]['xchat_url'] = zid($r[$x]['xchat_url']);
+ }
+ }
return replace_macros(get_markup_template('bookmarkedchats.tpl'),array(
'$header' => t('Suggested Chatrooms'),
'$rooms' => $r
@@ -658,17 +677,17 @@ function widget_suggestedchats($arr) {
}
function widget_item($arr) {
+ // FIXME there is no $a here
$uid = $a->profile['profile_uid'];
if((! $uid) || (! $arr['mid']))
return '';
- if(! perm_is_allowed($uid,get_observer_hash(),'view_pages'))
+ if(! perm_is_allowed($uid, get_observer_hash(), 'view_pages'))
return '';
require_once('include/security.php');
$sql_extra = item_permissions_sql($uid);
-
$r = q("select * from item where mid = '%s' and uid = %d and item_restrict = " . intval(ITEM_WEBPAGE) . " $sql_extra limit 1",
dbesc($arr['mid']),
intval($uid)
@@ -678,11 +697,10 @@ function widget_item($arr) {
return '';
xchan_query($r);
- $r = fetch_post_tags($r,true);
+ $r = fetch_post_tags($r, true);
- $o .= prepare_page($r[0]);
+ $o = prepare_page($r[0]);
return $o;
-
}
function widget_clock($arr) {
@@ -752,18 +770,17 @@ return $o;
* URL must be an http or https URL
*/
-
function widget_photo($arr) {
$style = $zrl = false;
- $params = '';
- if(array_key_exists('src',$arr) && isset($arr['src']))
+
+ if(array_key_exists('src', $arr) && isset($arr['src']))
$url = $arr['src'];
if(strpos($url,'http') !== 0)
return '';
- if(array_key_exists('style',$arr) && isset($arr['style']))
+ if(array_key_exists('style', $arr) && isset($arr['style']))
$style = $arr['style'];
// ensure they can't sneak in an eval(js) function
@@ -771,7 +788,7 @@ function widget_photo($arr) {
if(strpos($style,'(') !== false)
return '';
- if(array_key_exists('zrl',$arr) && isset($arr['zrl']))
+ if(array_key_exists('zrl', $arr) && isset($arr['zrl']))
$zrl = (($arr['zrl']) ? true : false);
if($zrl)
@@ -781,9 +798,108 @@ function widget_photo($arr) {
$o .= '<img ' . (($zrl) ? ' class="zrl" ' : '')
. (($style) ? ' style="' . $style . '"' : '')
- . ' src="' . $url . '" />';
+ . ' src="' . $url . '" alt="' . t('photo/image') . '">';
$o .= '</div>';
return $o;
}
+
+
+function widget_photo_rand($arr) {
+
+ require_once('include/photos.php');
+ $style = false;
+
+ if(array_key_exists('album', $arr) && isset($arr['album']))
+ $album = $arr['album'];
+ else
+ $album = '';
+
+ $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 '';
+
+ $scale = ((array_key_exists('scale',$arr)) ? intval($arr['scale']) : 0);
+
+ $ret = photos_list_photos(array('channel_id' => $channel_id),get_app()->get_observer(),$album);
+
+ $filtered = array();
+ if($ret['success'] && $ret['photos'])
+ foreach($ret['photos'] as $p)
+ if($p['scale'] == $scale)
+ $filtered[] = $p['src'];
+
+ if($filtered) {
+ $e = mt_rand(0, count($filtered) - 1);
+ $url = $filtered[$e];
+ }
+
+ if(strpos($url, 'http') !== 0)
+ return '';
+
+ if(array_key_exists('style', $arr) && isset($arr['style']))
+ $style = $arr['style'];
+
+ // ensure they can't sneak in an eval(js) function
+
+ if(strpos($style,'(') !== false)
+ return '';
+
+ $url = zid($url);
+
+ $o = '<div class="widget">';
+
+ $o .= '<img class="zrl" '
+ . (($style) ? ' style="' . $style . '"' : '')
+ . ' src="' . $url . '" alt="' . t('photo/image') . '">';
+
+ $o .= '</div>';
+
+ return $o;
+}
+
+
+function widget_random_block($arr) {
+
+ $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(array_key_exists('contains',$arr))
+ $contains = $arr['contains'];
+
+ $o = '';
+
+ require_once('include/security.php');
+ $sql_options = item_permissions_sql($channel_id);
+
+ $randfunc = db_getfunc('RAND');
+
+ $r = q("select item.* from item left join item_id on item.id = item_id.iid
+ where item.uid = %d and sid like '%s' and service = 'BUILDBLOCK' and
+ item_restrict = %d $sql_options order by $randfunc limit 1",
+ intval($channel_id),
+ dbesc('%' . $contains . '%'),
+ intval(ITEM_BUILDBLOCK)
+ );
+
+ if($r) {
+ $o = '<div class="widget bblock">';
+ if($r[0]['title'])
+ $o .= '<h3>' . $r[0]['title'] . '</h3>';
+
+ $o .= prepare_text($r[0]['body'],$r[0]['mimetype']);
+ $o .= '</div>';
+ }
+
+ return $o;
+}
diff --git a/include/zot.php b/include/zot.php
index a53b39663..c88b2a369 100644
--- a/include/zot.php
+++ b/include/zot.php
@@ -2,9 +2,10 @@
require_once('include/crypto.php');
require_once('include/items.php');
+require_once('include/hubloc.php');
/**
- * Red implementation of zot protocol.
+ * Red implementation of zot protocol.
*
* https://github.com/friendica/red/wiki/zot
* https://github.com/friendica/red/wiki/Zot---A-High-Level-Overview
@@ -19,8 +20,8 @@ require_once('include/items.php');
* Generates a unique string for use as a zot guid using our DNS-based url, the channel nickname and some entropy.
* The entropy ensures uniqueness against re-installs where the same URL and nickname are chosen.
* NOTE: zot doesn't require this to be unique. Internally we use a whirlpool hash of this guid and the signature
- * of this guid signed with the channel private key. This can be verified and should make the probability of
- * collision of the verified result negligible within the constraints of our immediate universe.
+ * of this guid signed with the channel private key. This can be verified and should make the probability of
+ * collision of the verified result negligible within the constraints of our immediate universe.
*
* @param string channel_nickname = unique nickname of controlling entity
*
@@ -50,7 +51,7 @@ function make_xchan_hash($guid,$guid_sig) {
/**
* @function zot_get_hublocs($hash)
- * Given a zot hash, return all distinct hubs.
+ * Given a zot hash, return all distinct hubs.
* This function is used in building the zot discovery packet
* and therefore should only be used by channels which are defined
* on this hub
@@ -70,7 +71,7 @@ function make_xchan_hash($guid,$guid_sig) {
* hubloc_connect char(255)
* hubloc_sitekey text
* hubloc_updated datetime
- * hubloc_connected datetime
+ * hubloc_connected datetime
*
*/
@@ -78,32 +79,32 @@ function zot_get_hublocs($hash) {
/** Only search for active hublocs - e.g. those that haven't been marked deleted */
- $ret = q("select * from hubloc where hubloc_hash = '%s' and not ( hubloc_flags & %d ) group by hubloc_url ",
+ $ret = q("select * from hubloc where hubloc_hash = '%s' and not ( hubloc_flags & %d )>0 order by hubloc_url ",
dbesc($hash),
intval(HUBLOC_FLAGS_DELETED)
);
return $ret;
}
-
+
/**
*
* @function zot_build_packet($channel,$type = 'notify',$recipients = null, $remote_key = null, $secret = null)
* builds a zot notification packet that you can either
- * store in the queue with a message array or call zot_zot to immediately
+ * store in the queue with a message array or call zot_zot to immediately
* zot it to the other side
*
* @param array $channel => sender channel structure
* @param string $type => packet type: one of 'ping', 'pickup', 'purge', 'refresh', 'force_refresh', 'notify', 'auth_check'
* @param array $recipients => envelope information, array ( 'guid' => string, 'guid_sig' => string ); empty for public posts
* @param string $remote_key => optional public site key of target hub used to encrypt entire packet
- * NOTE: remote_key and encrypted packets are required for 'auth_check' packets, optional for all others
+ * NOTE: remote_key and encrypted packets are required for 'auth_check' packets, optional for all others
* @param string $secret => random string, required for packets which require verification/callback
- * e.g. 'pickup', 'purge', 'notify', 'auth_check'. Packet types 'ping', 'force_refresh', and 'refresh' do not require verification
+ * e.g. 'pickup', 'purge', 'notify', 'auth_check'. Packet types 'ping', 'force_refresh', and 'refresh' do not require verification
*
* @returns string json encoded zot packet
*/
-function zot_build_packet($channel,$type = 'notify',$recipients = null, $remote_key = null, $secret = null) {
+function zot_build_packet($channel,$type = 'notify',$recipients = null, $remote_key = null, $secret = null, $extra = null) {
$data = array(
'type' => $type,
@@ -128,6 +129,12 @@ function zot_build_packet($channel,$type = 'notify',$recipients = null, $remote_
$data['secret_sig'] = base64url_encode(rsa_sign($secret,$channel['channel_prvkey']));
}
+ if($extra) {
+ foreach($extra as $k => $v)
+ $data[$k] = $v;
+ }
+
+
logger('zot_build_packet: ' . print_r($data,true), LOGGER_DATA);
// Hush-hush ultra top-secret mode
@@ -187,14 +194,15 @@ function zot_finger($webbie,$channel,$autofallback = true) {
logger('zot_finger: no address :' . $webbie);
return array('success' => false);
}
-
+ logger('using xchan_addr: ' . $xchan_addr, LOGGER_DATA);
+
// potential issue here; the xchan_addr points to the primary hub.
// The webbie we were called with may not, so it might not be found
// unless we query for hubloc_addr instead of xchan_addr
$r = q("select xchan.*, hubloc.* from xchan
left join hubloc on xchan_hash = hubloc_hash
- where xchan_addr = '%s' and (hubloc_flags & %d) limit 1",
+ where xchan_addr = '%s' and (hubloc_flags & %d) > 0 limit 1",
dbesc($xchan_addr),
intval(HUBLOC_FLAGS_PRIMARY)
);
@@ -204,6 +212,7 @@ function zot_finger($webbie,$channel,$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);
return array('success' => false);
}
}
@@ -292,7 +301,7 @@ function zot_refresh($them,$channel = null, $force = false) {
if($them['hubloc_url'])
$url = $them['hubloc_url'];
else {
- $r = q("select hubloc_url from hubloc where hubloc_hash = '%s' and ( hubloc_flags & %d ) limit 1",
+ $r = q("select hubloc_url from hubloc where hubloc_hash = '%s' and ( hubloc_flags & %d ) > 0 limit 1",
dbesc($them['xchan_hash']),
intval(HUBLOC_FLAGS_PRIMARY)
);
@@ -374,7 +383,7 @@ function zot_refresh($them,$channel = null, $force = false) {
}
}
- $r = q("select * from abook where abook_xchan = '%s' and abook_channel = %d and not (abook_flags & %d) limit 1",
+ $r = q("select * from abook where abook_xchan = '%s' and abook_channel = %d and not (abook_flags & %d) > 0 limit 1",
dbesc($x['hash']),
intval($channel['channel_id']),
intval(ABOOK_FLAG_SELF)
@@ -384,7 +393,7 @@ function zot_refresh($them,$channel = null, $force = false) {
$next_birthday = datetime_convert('UTC','UTC',$j['profile']['next_birthday']);
}
else {
- $next_birthday = '0000-00-00 00:00:00';
+ $next_birthday = NULL_DATE;
}
if($r) {
@@ -400,7 +409,7 @@ function zot_refresh($them,$channel = null, $force = false) {
$y = q("update abook set abook_their_perms = %d, abook_dob = '%s'
where abook_xchan = '%s' and abook_channel = %d
- and not (abook_flags & %d) limit 1",
+ and not (abook_flags & %d) > 0 ",
intval($their_perms),
dbesc($next_birthday),
dbesc($x['hash']),
@@ -412,7 +421,8 @@ function zot_refresh($them,$channel = null, $force = false) {
// if they are in your address book but you aren't in theirs, and/or this does not
// match your current connected state setting, toggle it.
-
+ // FIXME: uncoverted to postgres
+ // FIXME: when this was enabled, all contacts became unconnected. Currently disabled intentionally
// $y1 = q("update abook set abook_flags = (abook_flags ^ %d)
// where abook_xchan = '%s' and abook_channel = %d
// and not (abook_flags & %d) limit 1",
@@ -432,15 +442,15 @@ function zot_refresh($them,$channel = null, $force = false) {
}
}
else {
- $default_perms = 0;
- // look for default permissions to apply in return - e.g. auto-friend
- $z = q("select * from abook where abook_channel = %d and (abook_flags & %d) limit 1",
- intval($channel['channel_id']),
- intval(ABOOK_FLAG_SELF)
- );
-
- if($z)
- $default_perms = intval($z[0]['abook_my_perms']);
+ $role = get_pconfig($channel['channel_id'],'system','permissions_role');
+ if($role) {
+ $xx = get_role_perms($role);
+ if($xx['perms_auto'])
+ $default_perms = $xx['perms_accept'];
+ }
+ if(! $default_perms)
+ $default_perms = intval(get_pconfig($channel['channel_id'],'system','autoperms'));
+
// Keep original perms to check if we need to notify them
$previous_perms = get_all_perms($channel['channel_id'],$x['hash']);
@@ -462,7 +472,7 @@ function zot_refresh($them,$channel = null, $force = false) {
$new_perms = get_all_perms($channel['channel_id'],$x['hash']);
if($new_perms != $previous_perms) {
// Send back a permissions update if permissions have changed
- $z = q("select * from abook where abook_xchan = '%s' and abook_channel = %d and not (abook_flags & %d) limit 1",
+ $z = q("select * from abook where abook_xchan = '%s' and abook_channel = %d and not (abook_flags & %d) > 0 limit 1",
dbesc($x['hash']),
intval($channel['channel_id']),
intval(ABOOK_FLAG_SELF)
@@ -661,6 +671,8 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
}
$xchan_hash = make_xchan_hash($arr['guid'],$arr['guid_sig']);
+ $arr['hash'] = $xchan_hash;
+
$import_photos = false;
if(! rsa_verify($arr['guid'],base64url_decode($arr['guid_sig']),$arr['key'])) {
@@ -690,10 +702,10 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
$dirmode = get_config('system','directory_mode');
- if((($arr['site']['directory_mode'] === 'standalone') || ($dirmode & DIRECTORY_MODE_STANDALONE))
-&& ($arr['site']['url'] != z_root()))
+ if((($arr['site']['directory_mode'] === 'standalone') || ($dirmode & DIRECTORY_MODE_STANDALONE)) && ($arr['site']['url'] != z_root()))
$arr['searchable'] = false;
+
$hidden = (1 - intval($arr['searchable']));
// Be careful - XCHAN_FLAGS_HIDDEN should evaluate to 1
@@ -712,6 +724,11 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
if($deleted_changed)
$new_flags = $new_flags ^ XCHAN_FLAGS_DELETED;
+ $public_forum = (($r[0]['xchan_flags'] & XCHAN_FLAGS_PUBFORUM) ? true : false);
+ $pubforum_changed = ((intval($public_forum) != intval($arr['public_forum'])) ? true : false);
+ if($pubforum_changed)
+ $new_flags = $r[0]['xchan_flags'] ^ XCHAN_FLAGS_PUBFORUM;
+
if(($r[0]['xchan_name_date'] != $arr['name_updated'])
|| ($r[0]['xchan_connurl'] != $arr['connections_url'])
|| ($r[0]['xchan_flags'] != $new_flags)
@@ -721,8 +738,8 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
|| ($r[0]['xchan_url'] != $arr['url'])) {
$r = q("update xchan set xchan_name = '%s', xchan_name_date = '%s', xchan_connurl = '%s', xchan_follow = '%s',
xchan_connpage = '%s', xchan_flags = %d,
- xchan_addr = '%s', xchan_url = '%s' where xchan_hash = '%s' limit 1",
- dbesc($arr['name']),
+ xchan_addr = '%s', xchan_url = '%s' where xchan_hash = '%s'",
+ dbesc(($arr['name']) ? $arr['name'] : '-'),
dbesc($arr['name_updated']),
dbesc($arr['connections_url']),
dbesc($arr['follow_url']),
@@ -772,10 +789,10 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
dbesc($arr['connections_url']),
dbesc($arr['follow_url']),
dbesc($arr['connect_url']),
- dbesc($arr['name']),
+ dbesc(($arr['name']) ? $arr['name'] : '-'),
dbesc('zot'),
- dbesc($arr['photo_updated']),
- dbesc($arr['name_updated']),
+ dbescdate($arr['photo_updated']),
+ dbescdate($arr['name_updated']),
intval($new_flags)
);
@@ -816,7 +833,7 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
// importing the photo failed somehow. Leave the photo_date alone so we can try again at a later date.
// This often happens when somebody joins the matrix with a bad cert.
$r = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s'
- where xchan_hash = '%s' limit 1",
+ where xchan_hash = '%s'",
dbesc($photos[0]),
dbesc($photos[1]),
dbesc($photos[2]),
@@ -826,8 +843,8 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
}
else {
$r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s'
- where xchan_hash = '%s' limit 1",
- dbesc(datetime_convert('UTC','UTC',$arr['photo_updated'])),
+ where xchan_hash = '%s'",
+ dbescdate(datetime_convert('UTC','UTC',$arr['photo_updated'])),
dbesc($photos[0]),
dbesc($photos[1]),
dbesc($photos[2]),
@@ -843,174 +860,16 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
// what we are missing for true hub independence is for any changes in the primary hub to
// get reflected not only in the hublocs, but also to update the URLs and addr in the appropriate xchan
- if($arr['locations']) {
-
- $xisting = q("select hubloc_id, hubloc_url, hubloc_sitekey from hubloc where hubloc_hash = '%s'",
- dbesc($xchan_hash)
- );
-
- // See if a primary is specified
-
- $has_primary = false;
- foreach($arr['locations'] as $location) {
- if($location['primary']) {
- $has_primary = true;
- break;
- }
- }
-
- foreach($arr['locations'] as $location) {
- if(! rsa_verify($location['url'],base64url_decode($location['url_sig']),$arr['key'])) {
- logger('import_xchan: Unable to verify site signature for ' . $location['url']);
- $ret['message'] .= sprintf( t('Unable to verify site signature for %s'), $location['url']) . EOL;
- continue;
- }
-
- // Ensure that they have one primary hub
-
- if(! $has_primary)
- $location['primary'] = true;
-
-
- for($x = 0; $x < count($xisting); $x ++) {
- if(($xisting[$x]['hubloc_url'] === $location['url']) && ($xisting[$x]['hubloc_sitekey'] === $location['sitekey'])) {
- $xisting[$x]['updated'] = true;
- }
- }
-
- if(! $location['sitekey']) {
- logger('import_xchan: empty hubloc sitekey. ' . print_r($location,true));
- continue;
- }
-
- // Catch some malformed entries from the past which still exist
-
- if(strpos($location['address'],'/') !== false)
- $location['address'] = substr($location['address'],0,strpos($location['address'],'/'));
-
- // match as many fields as possible in case anything at all changed.
-
- $r = q("select * from hubloc where hubloc_hash = '%s' and hubloc_guid = '%s' and hubloc_guid_sig = '%s' and hubloc_url = '%s' and hubloc_url_sig = '%s' and hubloc_host = '%s' and hubloc_addr = '%s' and hubloc_callback = '%s' and hubloc_sitekey = '%s' ",
- dbesc($xchan_hash),
- dbesc($arr['guid']),
- dbesc($arr['guid_sig']),
- dbesc($location['url']),
- dbesc($location['url_sig']),
- dbesc($location['host']),
- dbesc($location['address']),
- dbesc($location['callback']),
- dbesc($location['sitekey'])
- );
- if($r) {
- logger('import_xchan: hub exists: ' . $location['url']);
- // update connection timestamp if this is the site we're talking to
- if($location['url'] == $arr['site']['url']) {
- q("update hubloc set hubloc_connected = '%s', hubloc_updated = '%s' where hubloc_id = %d limit 1",
- dbesc(datetime_convert()),
- dbesc(datetime_convert()),
- intval($r[0]['hubloc_id'])
- );
- }
- if($r[0]['hubloc_status'] & HUBLOC_OFFLINE) {
- q("update hubloc set hubloc_status = (hubloc_status ^ %d) where hubloc_id = %d limit 1",
- intval(HUBLOC_OFFLINE),
- intval($r[0]['hubloc_id'])
- );
- if($r[0]['hubloc_flags'] & HUBLOC_FLAGS_ORPHANCHECK) {
- q("update hubloc set hubloc_flags = (hubloc_flags ^ %d) where hubloc_id = %d limit 1",
- intval(HUBLOC_FLAGS_ORPHANCHECK),
- intval($r[0]['hubloc_id'])
- );
- }
- q("update xchan set xchan_flags = (xchan_flags ^ %d) where (xchan_flags & %d) and xchan_hash = '%s' limit 1",
- intval(XCHAN_FLAGS_ORPHAN),
- intval(XCHAN_FLAGS_ORPHAN),
- dbesc($xchan_hash)
- );
-
- }
-
- // Remove pure duplicates
- if(count($r) > 1) {
- for($h = 1; $h < count($r); $h ++) {
- q("delete from hubloc where hubloc_id = %d limit 1",
- intval($r[$h]['hubloc_id'])
- );
- }
- }
-
- if((($r[0]['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY) && (! $location['primary']))
- || ((! ($r[0]['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY)) && ($location['primary']))) {
- $r = q("update hubloc set hubloc_flags = (hubloc_flags ^ %d), hubloc_updated = '%s' where hubloc_id = %d limit 1",
- intval(HUBLOC_FLAGS_PRIMARY),
- dbesc(datetime_convert()),
- intval($r[0]['hubloc_id'])
- );
- $what = 'primary_hub ';
- $changed = true;
- }
- if((($r[0]['hubloc_flags'] & HUBLOC_FLAGS_DELETED) && (! $location['deleted']))
- || ((! ($r[0]['hubloc_flags'] & HUBLOC_FLAGS_DELETED)) && ($location['deleted']))) {
- $r = q("update hubloc set hubloc_flags = (hubloc_flags ^ %d), hubloc_updated = '%s' where hubloc_id = %d limit 1",
- intval(HUBLOC_FLAGS_DELETED),
- dbesc(datetime_convert()),
- intval($r[0]['hubloc_id'])
- );
- $what = 'delete_hub ';
- $changed = true;
- }
- continue;
- }
-
- // new hub claiming to be primary. Make it so.
-
- if(intval($location['primary'])) {
- $r = q("update hubloc set hubloc_flags = (hubloc_flags ^ %d), hubloc_updated = '%s' where hubloc_hash = '%s' and (hubloc_flags & %d )",
- intval(HUBLOC_FLAGS_PRIMARY),
- dbesc(datetime_convert()),
- dbesc($xchan_hash),
- intval(HUBLOC_FLAGS_PRIMARY)
- );
- }
- logger('import_xchan: new hub: ' . $location['url']);
- $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_network, hubloc_flags, hubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey, hubloc_updated, hubloc_connected)
- values ( '%s','%s','%s','%s', '%s', %d ,'%s','%s','%s','%s','%s','%s','%s')",
- dbesc($arr['guid']),
- dbesc($arr['guid_sig']),
- dbesc($xchan_hash),
- dbesc($location['address']),
- dbesc('zot'),
- intval((intval($location['primary'])) ? HUBLOC_FLAGS_PRIMARY : 0),
- dbesc($location['url']),
- dbesc($location['url_sig']),
- dbesc($location['host']),
- dbesc($location['callback']),
- dbesc($location['sitekey']),
- dbesc(datetime_convert()),
- dbesc(datetime_convert())
- );
- $what .= 'newhub ';
- $changed = true;
-
- }
- // get rid of any hubs we have for this channel which weren't reported.
- // This was needed at one time to resolve complicated cross-site inconsistencies, but can cause sync conflict.
- // currently disabled.
-
-// if($xisting) {
-// foreach($xisting as $x) {
-// if(! array_key_exists('updated',$x)) {
-// logger('import_xchan: removing unreferenced hub location ' . $x['hubloc_url']);
-// $r = q("delete from hubloc where hubloc_id = %d limit 1",
-// intval($x['hubloc_id'])
-// );
-// $what .= 'removed_hub';
-// $changed = true;
-// }
-// }
-// }
+ $s = sync_locations($arr,$arr);
+ if($s) {
+ if($s['change_message'])
+ $what .= $s['change_message'];
+ if($s['changed'])
+ $changed = $s['changed'];
+ if($s['message'])
+ $ret['message'] .= $s['message'];
}
// Which entries in the update table are we interested in updating?
@@ -1045,10 +904,10 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
else {
logger('import_xchan: profile not available - hiding');
// they may have made it private
- $r = q("delete from xprof where xprof_hash = '%s' limit 1",
+ $r = q("delete from xprof where xprof_hash = '%s'",
dbesc($xchan_hash)
);
- $r = q("delete from xtag where xtag_hash = '%s' limit 1",
+ $r = q("delete from xtag where xtag_hash = '%s'",
dbesc($xchan_hash)
);
}
@@ -1070,7 +929,7 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
}
elseif(! $ud_flags) {
// nothing changed but we still need to update the updates record
- q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d) ",
+ q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d)>0 ",
intval(UPDATE_FLAGS_UPDATED),
dbesc($address),
intval(UPDATE_FLAGS_UPDATED)
@@ -1120,14 +979,14 @@ function zot_process_response($hub,$arr,$outq) {
// async messages remain in the queue until processed.
if(intval($outq['outq_async'])) {
- $r = q("update outq set outq_delivered = 1, outq_updated = '%s' where outq_hash = '%s' and outq_channel = %d limit 1",
+ $r = q("update outq set outq_delivered = 1, outq_updated = '%s' where outq_hash = '%s' and outq_channel = %d",
dbesc(datetime_convert()),
dbesc($outq['outq_hash']),
intval($outq['outq_channel'])
);
}
else {
- $r = q("delete from outq where outq_hash = '%s' and outq_channel = %d limit 1",
+ $r = q("delete from outq where outq_hash = '%s' and outq_channel = %d",
dbesc($outq['outq_hash']),
intval($outq['outq_channel'])
);
@@ -1189,7 +1048,8 @@ function zot_fetch($arr) {
* we will verify the sender and url in each returned message structure and also verify
* that all the messages returned match the site url that we are currently processing.
*
- * The message types handled here are 'activity' (e.g. posts), 'mail' , 'profile', and 'channel_sync'
+ * The message types handled here are 'activity' (e.g. posts), 'mail' , 'profile', 'location',
+ * and 'channel_sync'
*
* @returns array => array ( [0] => string $channel_hash, [1] => string $delivery_status, [2] => string $address )
* suitable for logging remotely, enumerating the processing results of each message/recipient combination.
@@ -1219,7 +1079,7 @@ function zot_import($arr, $sender_url) {
if(array_key_exists('iv',$i['notify'])) {
$i['notify'] = json_decode(crypto_unencapsulate($i['notify'],get_config('system','prvkey')),true);
- }
+ }
logger('zot_import: notify: ' . print_r($i['notify'],true), LOGGER_DATA);
@@ -1229,6 +1089,9 @@ function zot_import($arr, $sender_url) {
continue;
}
+ $message_request = ((array_key_exists('message_id',$i['notify'])) ? true : false);
+ if($message_request)
+ logger('processing message request');
$i['notify']['sender']['hash'] = make_xchan_hash($i['notify']['sender']['guid'],$i['notify']['sender']['guid_sig']);
$deliveries = null;
@@ -1241,7 +1104,7 @@ function zot_import($arr, $sender_url) {
}
stringify_array_elms($recip_arr);
$recips = implode(',',$recip_arr);
- $r = q("select channel_hash as hash from channel where channel_hash in ( " . $recips . " ) and not ( channel_pageflags & %d ) ",
+ $r = q("select channel_hash as hash from channel where channel_hash in ( " . $recips . " ) and not ( channel_pageflags & %d )>0 ",
intval(PAGE_REMOVED)
);
if(! $r) {
@@ -1252,7 +1115,7 @@ function zot_import($arr, $sender_url) {
// It's a specifically targetted post. If we were sent a public_scope hint (likely),
// get rid of it so that it doesn't get stored and cause trouble.
- if(array_key_exists('message',$i) && array_key_exists('public_scope',$i['message']))
+ if(($i) && is_array($i) && array_key_exists('message',$i) && is_array($i['message']) && array_key_exists('public_scope',$i['message']))
unset($i['message']['public_scope']);
$deliveries = $r;
@@ -1276,6 +1139,11 @@ function zot_import($arr, $sender_url) {
$deliveries = allowed_public_recips($i);
+ if($i['message'] && array_key_exists('type',$i['message']) && $i['message']['type'] === 'location') {
+ $sys = get_sys_channel();
+ $deliveries = array(array('hash' => $sys['xchan_hash']));
+ }
+
// if the scope is anything but 'public' we're going to store it as private regardless
// of the private flag on the post.
@@ -1325,7 +1193,7 @@ function zot_import($arr, $sender_url) {
logger('Activity recipients: ' . print_r($deliveries,true), LOGGER_DATA);
$relay = ((array_key_exists('flags',$i['message']) && in_array('relay',$i['message']['flags'])) ? true : false);
- $result = process_delivery($i['notify']['sender'],$arr,$deliveries,$relay);
+ $result = process_delivery($i['notify']['sender'],$arr,$deliveries,$relay,false,$message_request);
}
elseif($i['message']['type'] === 'mail') {
@@ -1348,7 +1216,7 @@ function zot_import($arr, $sender_url) {
}
elseif($i['message']['type'] === 'channel_sync') {
-// $arr = get_channelsync_elements($i['message']);
+ // $arr = get_channelsync_elements($i['message']);
$arr = $i['message'];
@@ -1357,6 +1225,15 @@ function zot_import($arr, $sender_url) {
$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);
+
+ $result = process_location_delivery($i['notify']['sender'],$arr,$deliveries);
+ }
+
}
if($result){
$return = array_merge($return,$result);
@@ -1379,8 +1256,14 @@ function zot_import($arr, $sender_url) {
function public_recips($msg) {
+
+ require_once('include/identity.php');
+
$check_mentions = false;
+ $include_sys = false;
+
if($msg['message']['type'] === 'activity') {
+ $include_sys = true;
$col = 'channel_w_stream';
$field = PERMS_W_STREAM;
if(array_key_exists('flags',$msg['message']) && in_array('thread_parent', $msg['message']['flags'])) {
@@ -1409,9 +1292,9 @@ function public_recips($msg) {
if($msg['notify']['sender']['url'] === z_root())
- $sql = " where (( " . $col . " & " . PERMS_NETWORK . " ) or ( " . $col . " & " . PERMS_SITE . " ) or ( " . $col . " & " . PERMS_PUBLIC . ")) ";
+ $sql = " where (( " . $col . " & " . PERMS_NETWORK . " )>0 or ( " . $col . " & " . PERMS_SITE . " )>0 or ( " . $col . " & " . PERMS_PUBLIC . ")>0) ";
else
- $sql = " where (( " . $col . " & " . PERMS_NETWORK . " ) or ( " . $col . " & " . PERMS_PUBLIC . ")) ";
+ $sql = " where (( " . $col . " & " . PERMS_NETWORK . " )>0 or ( " . $col . " & " . PERMS_PUBLIC . ")>0) ";
$r = q("select channel_hash as hash from channel $sql or channel_hash = '%s' ",
@@ -1421,7 +1304,7 @@ function public_recips($msg) {
if(! $r)
$r = array();
- $x = q("select channel_hash as hash from channel left join abook on abook_channel = channel_id where abook_xchan = '%s' and not ( channel_pageflags & " . PAGE_REMOVED . " ) and (( " . $col . " & " . PERMS_SPECIFIC . " ) and ( abook_my_perms & " . $field . " )) OR ( " . $col . " & " . PERMS_PENDING . " ) OR (( " . $col . " & " . PERMS_CONTACTS . " ) and not ( abook_flags & " . ABOOK_FLAG_PENDING . " )) ",
+ $x = q("select channel_hash as hash from channel left join abook on abook_channel = channel_id where abook_xchan = '%s' and not ( channel_pageflags & " . PAGE_REMOVED . " )>0 and (( " . $col . " & " . PERMS_SPECIFIC . " )>0 and ( abook_my_perms & " . $field . " )>0) OR ( " . $col . " & " . PERMS_PENDING . " )>0 OR (( " . $col . " & " . PERMS_CONTACTS . " )>0 and not ( abook_flags & " . ABOOK_FLAG_PENDING . " )>0) ",
dbesc($msg['notify']['sender']['hash'])
);
@@ -1430,6 +1313,14 @@ function public_recips($msg) {
$r = array_merge($r,$x);
+ //logger('message: ' . print_r($msg['message'],true));
+
+ if($include_sys && array_key_exists('public_scope',$msg['message']) && $msg['message']['public_scope'] === 'public') {
+ $sys = get_sys_channel();
+ if($sys)
+ $r[] = array('hash' => $sys['channel_hash']);
+ }
+
// look for any public mentions on this site
// They will get filtered by tgroup_check() so we don't need to check permissions now
@@ -1460,7 +1351,7 @@ function public_recips($msg) {
function allowed_public_recips($msg) {
- logger('allowed_public_recips: ' . print_r($msg,true));
+ logger('allowed_public_recips: ' . print_r($msg,true),LOGGER_DATA);
$recips = public_recips($msg);
@@ -1497,7 +1388,7 @@ function allowed_public_recips($msg) {
$condensed_recips[] = $rr['hash'];
$results = array();
- $r = q("select channel_hash as hash from channel left join abook on abook_channel = channel_id where abook_xchan = '%s' and not ( channel_pageflags & %d ) ",
+ $r = q("select channel_hash as hash from channel left join abook on abook_channel = channel_id where abook_xchan = '%s' and not ( channel_pageflags & %d )>0 ",
dbesc($hash),
intval(PAGE_REMOVED)
);
@@ -1513,7 +1404,7 @@ function allowed_public_recips($msg) {
}
-function process_delivery($sender,$arr,$deliveries,$relay,$public = false) {
+function process_delivery($sender,$arr,$deliveries,$relay,$public = false,$request = false) {
$result = array();
@@ -1526,8 +1417,9 @@ function process_delivery($sender,$arr,$deliveries,$relay,$public = false) {
return;
}
}
-
+
foreach($deliveries as $d) {
+ $local_public = $public;
$r = q("select * from channel where channel_hash = '%s' limit 1",
dbesc($d['hash'])
);
@@ -1537,12 +1429,28 @@ function process_delivery($sender,$arr,$deliveries,$relay,$public = false) {
continue;
}
+
$channel = $r[0];
+ // allow public postings to the sys channel regardless of permissions
+ if(($channel['channel_pageflags'] & PAGE_SYSTEM) && (! $arr['item_private'])) {
+ $local_public = true;
+
+ $r = q("select xchan_flags from xchan where xchan_hash = '%s' limit 1",
+ dbesc($sender['hash'])
+ );
+ // don't import sys channel posts from selfcensored authors
+ if($r && ($r[0]['xchan_flags'] & XCHAN_FLAGS_SELFCENSORED)) {
+ $local_public = false;
+ continue;
+ }
+ }
+
$tag_delivery = tgroup_check($channel['channel_id'],$arr);
$perm = (($arr['mid'] == $arr['parent_mid']) ? 'send_stream' : 'post_comments');
+
// This is our own post, possibly coming from a channel clone
if($arr['owner_xchan'] == $d['hash']) {
@@ -1555,12 +1463,68 @@ function process_delivery($sender,$arr,$deliveries,$relay,$public = false) {
}
}
- if((! perm_is_allowed($channel['channel_id'],$sender['hash'],$perm)) && (! $tag_delivery) && (! $public)) {
+ if((! perm_is_allowed($channel['channel_id'],$sender['hash'],$perm)) && (! $tag_delivery) && (! $local_public)) {
logger("permission denied for delivery to channel {$channel['channel_id']} {$channel['channel_address']}");
$result[] = array($d['hash'],'permission denied',$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>',$arr['mid']);
continue;
}
-
+
+ if($arr['mid'] != $arr['parent_mid']) {
+
+ // check source route.
+ // We are only going to accept comments from this sender if the comment has the same route as the top-level-post,
+ // this is so that permissions mismatches between senders apply to the entire conversation
+ // As a side effect we will also do a preliminary check that we have the top-level-post, otherwise
+ // processing it is pointless.
+
+ $r = q("select route from item where mid = '%s' and uid = %d limit 1",
+ dbesc($arr['parent_mid']),
+ intval($channel['channel_id'])
+ );
+ if(! $r) {
+ $result[] = array($d['hash'],'comment parent not found',$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>',$arr['mid']);
+
+ // We don't seem to have a copy of this conversation or at least the parent
+ // - so request a copy of the entire conversation to date.
+ // Don't do this if it's a relay post as we're the ones who are supposed to
+ // have the copy and we don't want the request to loop.
+ // Also don't do this if this comment came from a conversation request packet.
+ // It's possible that comments are allowed but posting isn't and that could
+ // cause a conversation fetch loop. We can detect these packets since they are
+ // delivered via a 'notify' packet type that has a message_id element in the
+ // initial zot packet (just like the corresponding 'request' packet type which
+ // makes the request).
+ // We'll also check the send_stream permission - because if it isn't allowed,
+ // the top level post is unlikely to be imported and
+ // this is just an exercise in futility.
+
+ if((! $relay) && (! $request) && (! $local_public)
+ && perm_is_allowed($channel['channel_id'],$sender['hash'],'send_stream')) {
+ proc_run('php', 'include/notifier.php', 'request', $channel['channel_id'], $sender['hash'], $arr['parent_mid']);
+ }
+ continue;
+ }
+ if($relay) {
+ // reset the route in case it travelled a great distance upstream
+ // use our parent's route so when we go back downstream we'll match
+ // with whatever route our parent has.
+ $arr['route'] = $r[0]['route'];
+ }
+ else {
+
+ // going downstream check that we have the same upstream provider that
+ // sent it to us originally. Ignore it if it came from another source
+ // (with potentially different permissions)
+
+ $current_route = (($arr['route']) ? $arr['route'] . ',' : '') . $sender['hash'];
+
+ if($r[0]['route'] != $current_route) {
+ $result[] = array($d['hash'],'comment route mismatch',$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>',$arr['mid']);
+ continue;
+ }
+ }
+ }
+
if($arr['item_restrict'] & ITEM_DELETED) {
// remove_community_tag is a no-op if this isn't a community tag activity
@@ -1578,18 +1542,29 @@ function process_delivery($sender,$arr,$deliveries,$relay,$public = false) {
continue;
}
- $r = q("select id, edited from item where mid = '%s' and uid = %d limit 1",
+ $r = q("select id, edited, item_flags, mid, parent_mid from item where mid = '%s' and uid = %d limit 1",
dbesc($arr['mid']),
intval($channel['channel_id'])
);
if($r) {
+ // We already have this post.
+ // Maybe it has been edited?
+ $item_id = $r[0]['id'];
if($arr['edited'] > $r[0]['edited']) {
$arr['id'] = $r[0]['id'];
$arr['uid'] = $channel['channel_id'];
update_imported_item($sender,$arr,$channel['channel_id']);
- }
- $result[] = array($d['hash'],'updated',$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>',$arr['mid']);
- $item_id = $r[0]['id'];
+ $result[] = array($d['hash'],'updated',$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>',$arr['mid']);
+ if(! $relay)
+ add_source_route($item_id,$sender['hash']);
+ }
+ else {
+ $result[] = array($d['hash'],'update ignored',$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>',$arr['mid']);
+ // We need this line to ensure wall-to-wall comments are relayed (by falling through to the relay bit),
+ // and at the same time not relay any other relayable posts more than once, because to do so is very wasteful.
+ if(! ($r[0]['item_flags'] & ITEM_ORIGIN))
+ continue;
+ }
}
else {
$arr['aid'] = $channel['channel_account_id'];
@@ -1600,7 +1575,9 @@ function process_delivery($sender,$arr,$deliveries,$relay,$public = false) {
$item_id = $item_result['item_id'];
$parr = array('item_id' => $item_id,'item' => $arr,'sender' => $sender,'channel' => $channel);
call_hooks('activity_received',$parr);
- add_source_route($item_id,$sender['hash']);
+ // don't add a source route if it's a relay or later recipients will get a route mismatch
+ if(! $relay)
+ add_source_route($item_id,$sender['hash']);
}
$result[] = array($d['hash'],(($item_id) ? 'posted' : 'storage failed:' . $item_result['message']),$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>',$arr['mid']);
}
@@ -1671,7 +1648,7 @@ function remove_community_tag($sender,$arr,$uid) {
return;
}
- $x = q("delete from term where uid = %d and oid = %d and otype = %d and type = %d and term = '%s' and url = '%s' limit 1",
+ $x = q("delete from term where uid = %d and oid = %d and otype = %d and type = %d and term = '%s' and url = '%s'",
intval($uid),
intval($r[0]['id']),
intval(TERM_OBJ_POST),
@@ -1765,7 +1742,7 @@ function process_mail_delivery($sender,$arr,$deliveries) {
);
if($r) {
if($arr['mail_flags'] & MAIL_RECALLED) {
- $x = q("delete from mail where id = %d and channel_id = %d limit 1",
+ $x = q("delete from mail where id = %d and channel_id = %d",
intval($r[0]['id']),
intval($channel['channel_id'])
);
@@ -1800,6 +1777,264 @@ function process_profile_delivery($sender,$arr,$deliveries) {
import_directory_profile($sender['hash'],$arr,$r[0]['xchan_addr'], UPDATE_FLAGS_UPDATED, 0);
}
+function process_location_delivery($sender,$arr,$deliveries) {
+
+ // deliveries is irrelevant
+ logger('process_location_delivery', LOGGER_DEBUG);
+
+ $r = q("select xchan_pubkey from xchan where xchan_hash = '%s' limit 1",
+ dbesc($sender['hash'])
+ );
+ if($r)
+ $sender['key'] = $r[0]['xchan_pubkey'];
+ if(array_key_exists('locations',$arr) && $arr['locations']) {
+ $x = sync_locations($sender,$arr,true);
+ logger('process_location_delivery: results: ' . print_r($x,true), LOGGER_DEBUG);
+ if($x['changed']) {
+ $guid = random_string() . '@' . get_app()->get_hostname();
+ update_modtime($sender['hash'],$sender['guid'],$arr['locations'][0]['address'],UPDATE_FLAGS_UPDATED);
+ }
+ }
+}
+
+
+function sync_locations($sender,$arr,$absolute = false) {
+
+ $ret = array();
+
+ if($arr['locations']) {
+
+ $xisting = q("select hubloc_id, hubloc_url, hubloc_sitekey from hubloc where hubloc_hash = '%s'",
+ dbesc($sender['hash'])
+ );
+
+ // See if a primary is specified
+
+ $has_primary = false;
+ foreach($arr['locations'] as $location) {
+ if($location['primary']) {
+ $has_primary = true;
+ break;
+ }
+ }
+
+ // Ensure that they have one primary hub
+
+ if(! $has_primary)
+ $arr['locations'][0]['primary'] = true;
+
+ foreach($arr['locations'] as $location) {
+ if(! rsa_verify($location['url'],base64url_decode($location['url_sig']),$sender['key'])) {
+ logger('sync_locations: Unable to verify site signature for ' . $location['url']);
+ $ret['message'] .= sprintf( t('Unable to verify site signature for %s'), $location['url']) . EOL;
+ continue;
+ }
+
+
+ for($x = 0; $x < count($xisting); $x ++) {
+ if(($xisting[$x]['hubloc_url'] === $location['url'])
+ && ($xisting[$x]['hubloc_sitekey'] === $location['sitekey'])) {
+ $xisting[$x]['updated'] = true;
+ }
+ }
+
+ if(! $location['sitekey']) {
+ logger('sync_locations: empty hubloc sitekey. ' . print_r($location,true));
+ continue;
+ }
+
+ // Catch some malformed entries from the past which still exist
+
+ if(strpos($location['address'],'/') !== false)
+ $location['address'] = substr($location['address'],0,strpos($location['address'],'/'));
+
+ // match as many fields as possible in case anything at all changed.
+
+ $r = q("select * from hubloc where hubloc_hash = '%s' and hubloc_guid = '%s' and hubloc_guid_sig = '%s' and hubloc_url = '%s' and hubloc_url_sig = '%s' and hubloc_host = '%s' and hubloc_addr = '%s' and hubloc_callback = '%s' and hubloc_sitekey = '%s' ",
+ dbesc($sender['hash']),
+ dbesc($sender['guid']),
+ dbesc($sender['guid_sig']),
+ dbesc($location['url']),
+ dbesc($location['url_sig']),
+ dbesc($location['host']),
+ dbesc($location['address']),
+ dbesc($location['callback']),
+ dbesc($location['sitekey'])
+ );
+ if($r) {
+ logger('sync_locations: hub exists: ' . $location['url'], LOGGER_DEBUG);
+
+ // update connection timestamp if this is the site we're talking to
+ // This only happens when called from import_xchan
+
+ if(array_key_exists('site',$arr) && $location['url'] == $arr['site']['url']) {
+ q("update hubloc set hubloc_connected = '%s', hubloc_updated = '%s' where hubloc_id = %d",
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ intval($r[0]['hubloc_id'])
+ );
+ }
+
+ // if it's marked offline/dead, bring it back
+ // Should we do this? It's basically saying that the channel knows better than
+ // the directory server if the site is alive.
+
+ if($r[0]['hubloc_status'] & HUBLOC_OFFLINE) {
+ q("update hubloc set hubloc_status = (hubloc_status & ~%d) where hubloc_id = %d",
+ intval(HUBLOC_OFFLINE),
+ intval($r[0]['hubloc_id'])
+ );
+ if($r[0]['hubloc_flags'] & HUBLOC_FLAGS_ORPHANCHECK) {
+ q("update hubloc set hubloc_flags = (hubloc_flags & ~%d) where hubloc_id = %d",
+ intval(HUBLOC_FLAGS_ORPHANCHECK),
+ intval($r[0]['hubloc_id'])
+ );
+ }
+ q("update xchan set xchan_flags = (xchan_flags & ~%d) where (xchan_flags & %d)>0 and xchan_hash = '%s'",
+ intval(XCHAN_FLAGS_ORPHAN),
+ intval(XCHAN_FLAGS_ORPHAN),
+ dbesc($sender['hash'])
+ );
+ }
+
+ // Remove pure duplicates
+ if(count($r) > 1) {
+ for($h = 1; $h < count($r); $h ++) {
+ q("delete from hubloc where hubloc_id = %d",
+ intval($r[$h]['hubloc_id'])
+ );
+ $what .= 'duplicate_hubloc_removed ';
+ $changed = true;
+ }
+ }
+
+ if((($r[0]['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY) && (! $location['primary']))
+ || ((! ($r[0]['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY)) && ($location['primary']))) {
+ $m = q("update hubloc set hubloc_flags = (hubloc_flags & ~%d), hubloc_updated = '%s' where hubloc_id = %d",
+ intval(HUBLOC_FLAGS_PRIMARY),
+ dbesc(datetime_convert()),
+ intval($r[0]['hubloc_id'])
+ );
+ // make sure hubloc_change_primary() has current data
+ $r[0]['hubloc_flags'] = $r[0]['hubloc_flags'] ^ HUBLOC_FLAGS_PRIMARY;
+ hubloc_change_primary($r[0]);
+ $what .= 'primary_hub ';
+ $changed = true;
+ }
+ elseif($absolute) {
+ // Absolute sync - make sure the current primary is correctly reflected in the xchan
+ $pr = hubloc_change_primary($r[0]);
+ if($pr) {
+ $what .= 'xchan_primary';
+ $changed = true;
+ }
+ }
+ if((($r[0]['hubloc_flags'] & HUBLOC_FLAGS_DELETED) && (! $location['deleted']))
+ || ((! ($r[0]['hubloc_flags'] & HUBLOC_FLAGS_DELETED)) && ($location['deleted']))) {
+ $n = q("update hubloc set hubloc_flags = (hubloc_flags & ~%d), hubloc_updated = '%s' where hubloc_id = %d",
+ intval(HUBLOC_FLAGS_DELETED),
+ dbesc(datetime_convert()),
+ intval($r[0]['hubloc_id'])
+ );
+ $what .= 'delete_hub ';
+ $changed = true;
+ }
+ continue;
+ }
+
+ // Existing hubs are dealt with. Now let's process any new ones.
+ // New hub claiming to be primary. Make it so by removing any existing primaries.
+
+ if(intval($location['primary'])) {
+ $r = q("update hubloc set hubloc_flags = (hubloc_flags & ~%d), hubloc_updated = '%s' where hubloc_hash = '%s' and (hubloc_flags & %d )>0",
+ intval(HUBLOC_FLAGS_PRIMARY),
+ dbesc(datetime_convert()),
+ dbesc($sender['hash']),
+ intval(HUBLOC_FLAGS_PRIMARY)
+ );
+ }
+ logger('sync_locations: new hub: ' . $location['url']);
+ $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_network, hubloc_flags, hubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey, hubloc_updated, hubloc_connected)
+ values ( '%s','%s','%s','%s', '%s', %d ,'%s','%s','%s','%s','%s','%s','%s')",
+ dbesc($sender['guid']),
+ dbesc($sender['guid_sig']),
+ dbesc($sender['hash']),
+ dbesc($location['address']),
+ dbesc('zot'),
+ intval((intval($location['primary'])) ? HUBLOC_FLAGS_PRIMARY : 0),
+ dbesc($location['url']),
+ dbesc($location['url_sig']),
+ dbesc($location['host']),
+ dbesc($location['callback']),
+ dbesc($location['sitekey']),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert())
+ );
+ $what .= 'newhub ';
+ $changed = true;
+
+ if($location['primary']) {
+ $r = q("select * from hubloc where hubloc_addr = '%s' and hubloc_sitekey = '%s' limit 1",
+ dbesc($location['address']),
+ dbesc($location['sitekey'])
+ );
+ if($r)
+ hubloc_change_primary($r[0]);
+ }
+ }
+
+ // get rid of any hubs we have for this channel which weren't reported.
+
+ if($absolute && $xisting) {
+ foreach($xisting as $x) {
+ if(! array_key_exists('updated',$x)) {
+ logger('sync_locations: deleting unreferenced hub location ' . $x['hubloc_url']);
+ $r = q("update hubloc set hubloc_flags = (hubloc_flags & ~%d), hubloc_updated = '%s' where hubloc_id = %d",
+ intval(HUBLOC_FLAGS_DELETED),
+ dbesc(datetime_convert()),
+ intval($x['hubloc_id'])
+ );
+ $what .= 'removed_hub ';
+ $changed = true;
+ }
+ }
+ }
+ }
+
+ $ret['change_message'] = $what;
+ $ret['changed'] = $changed;
+
+ return $ret;
+
+}
+
+
+function zot_encode_locations($channel) {
+ $ret = array();
+
+ $x = zot_get_hublocs($channel['channel_hash']);
+ if($x && count($x)) {
+ foreach($x as $hub) {
+ if(! ($hub['hubloc_flags'] & HUBLOC_FLAGS_UNVERIFIED)) {
+ $ret[] = array(
+ 'host' => $hub['hubloc_host'],
+ 'address' => $hub['hubloc_addr'],
+ 'primary' => (($hub['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY) ? true : false),
+ 'url' => $hub['hubloc_url'],
+ 'url_sig' => $hub['hubloc_url_sig'],
+ 'callback' => $hub['hubloc_callback'],
+ 'sitekey' => $hub['hubloc_sitekey'],
+ 'deleted' => (($hub['hubloc_flags'] & HUBLOC_FLAGS_DELETED) ? true : false)
+ );
+ }
+ }
+ }
+ return $ret;
+}
+
+
+
+
/*
* @function import_directory_profile
@@ -1848,7 +2083,7 @@ function import_directory_profile($hash,$profile,$addr,$ud_flags = UPDATE_FLAGS_
if(in_arrayi('nsfw',$clean) || in_arrayi('adult',$clean)) {
- q("update xchan set xchan_flags = (xchan_flags | %d) where xchan_hash = '%s' limit 1",
+ q("update xchan set xchan_flags = (xchan_flags | %d) where xchan_hash = '%s'",
intval(XCHAN_FLAGS_SELFCENSORED),
dbesc($hash)
);
@@ -1883,7 +2118,7 @@ function import_directory_profile($hash,$profile,$addr,$ud_flags = UPDATE_FLAGS_
xprof_homepage = '%s',
xprof_hometown = '%s',
xprof_keywords = '%s'
- where xprof_hash = '%s' limit 1",
+ where xprof_hash = '%s'",
dbesc($arr['xprof_desc']),
dbesc($arr['xprof_dob']),
intval($arr['xprof_age']),
@@ -1953,7 +2188,7 @@ function import_directory_keywords($hash,$keywords) {
foreach($existing as $x) {
if(! in_array($x,$clean))
- $r = q("delete from xtag where xtag_hash = '%s' and xtag_term = '%s' limit 1",
+ $r = q("delete from xtag where xtag_hash = '%s' and xtag_term = '%s'",
dbesc($hash),
dbesc($x)
);
@@ -1985,7 +2220,7 @@ function update_modtime($hash,$guid,$addr,$flags = 0) {
);
}
else {
- q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d) ",
+ q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d)>0 ",
intval(UPDATE_FLAGS_UPDATED),
dbesc($addr),
intval(UPDATE_FLAGS_UPDATED)
@@ -2076,7 +2311,7 @@ function import_site($arr,$pubkey) {
// logger('import_site: stored: ' . print_r($siterecord,true));
$r = q("update site set site_location = '%s', site_flags = %d, site_access = %d, site_directory = '%s', site_register = %d, site_update = '%s', site_sellpage = '%s', site_realm = '%s'
- where site_url = '%s' limit 1",
+ where site_url = '%s'",
dbesc($site_location),
intval($site_directory),
intval($access_policy),
@@ -2247,7 +2482,7 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) {
function process_channel_sync_delivery($sender,$arr,$deliveries) {
// FIXME - this will sync red structures (channel, pconfig and abook). Eventually we need to make this application agnostic.
-// TODO: missing group membership changes
+
$result = array();
@@ -2263,6 +2498,10 @@ function process_channel_sync_delivery($sender,$arr,$deliveries) {
$channel = $r[0];
+ $max_friends = service_class_fetch($channel['channel_id'],'total_channels');
+ $max_feeds = account_service_class_fetch($channel['channel_account_id'],'total_feeds');
+
+
if($channel['channel_hash'] != $sender['hash']) {
logger('process_channel_sync_delivery: possible forgery. Sender ' . $sender['hash'] . ' is not ' . $channel['channel_hash']);
$result[] = array($d['hash'],'channel mismatch',$channel['channel_name'],'');
@@ -2288,7 +2527,7 @@ function process_channel_sync_delivery($sender,$arr,$deliveries) {
if(count($clean)) {
foreach($clean as $k => $v) {
$r = dbq("UPDATE channel set " . dbesc($k) . " = '" . dbesc($v)
- . "' where channel_id = " . intval($channel['channel_id']) . " limit 1");
+ . "' where channel_id = " . intval($channel['channel_id']) );
}
}
}
@@ -2296,24 +2535,41 @@ function process_channel_sync_delivery($sender,$arr,$deliveries) {
if(array_key_exists('abook',$arr) && is_array($arr['abook']) && count($arr['abook'])) {
+ $total_friends = 0;
+ $total_feeds = 0;
+
+ $r = q("select abook_id, abook_flags from abook where abook_channel = %d",
+ intval($channel['channel_id'])
+ );
+ if($r) {
+ // don't count yourself
+ $total_friends = ((count($r) > 0) ? count($r) - 1 : 0);
+ foreach($r as $rr)
+ if($rr['abook_flags'] & ABOOK_FLAG_FEED)
+ $total_feeds ++;
+ }
$disallowed = array('abook_id','abook_account','abook_channel');
- $clean = array();
foreach($arr['abook'] as $abook) {
+ $clean = array();
if($abook['abook_xchan'] && $abook['entry_deleted']) {
logger('process_channel_sync_delivery: removing abook entry for ' . $abook['abook_xchan']);
require_once('include/Contact.php');
- $r = q("select abook_id from abook where abook_xchan = '%s' and abook_channel = %d and not ( abook_flags & %d ) limit 1",
+ $r = q("select abook_id, abook_flags from abook where abook_xchan = '%s' and abook_channel = %d and not ( abook_flags & %d )>0 limit 1",
dbesc($abook['abook_xchan']),
intval($channel['channel_id']),
intval(ABOOK_FLAG_SELF)
);
- if($r)
+ if($r) {
contact_remove($channel['channel_id'],$r[0]['abook_id']);
-
+ if($total_friends)
+ $total_friends --;
+ if($r[0]['abook_flags'] & ABOOK_FLAG_FEED)
+ $total_feeds --;
+ }
continue;
}
@@ -2329,7 +2585,7 @@ function process_channel_sync_delivery($sender,$arr,$deliveries) {
continue;
}
$j = json_decode($f['body'],true);
- if(! ($j['success'] && $j['guid'])) {
+ if(! ($j['success'] && $j['guid'])) {
logger('process_channel_sync_delivery: probe failed.');
continue;
}
@@ -2360,17 +2616,27 @@ function process_channel_sync_delivery($sender,$arr,$deliveries) {
// make sure we have an abook entry for this xchan on this system
if(! $r) {
+ if($max_friends !== false && $total_friends > $max_friends) {
+ logger('process_channel_sync_delivery: total_channels service class limit exceeded');
+ continue;
+ }
+ if($max_feeds !== false && ($clean['abook_flags'] & ABOOK_FLAG_FEED) && $total_feeds > $max_feeds) {
+ logger('process_channel_sync_delivery: total_feeds service class limit exceeded');
+ continue;
+ }
q("insert into abook ( abook_xchan, abook_channel ) values ('%s', %d ) ",
dbesc($clean['abook_xchan']),
intval($channel['channel_id'])
);
+ $total_friends ++;
+ if($clean['abook_flags'] & ABOOK_FLAG_FEED)
+ $total_feeds ++;
}
if(count($clean)) {
foreach($clean as $k => $v) {
$r = dbq("UPDATE abook set " . dbesc($k) . " = '" . dbesc($v)
- . "' where abook_xchan = '" . dbesc($clean['abook_xchan']) . "' and abook_channel = " . intval($channel['channel_id'])
- . " limit 1");
+ . "' where abook_xchan = '" . dbesc($clean['abook_xchan']) . "' and abook_channel = " . intval($channel['channel_id']));
}
}
}
@@ -2395,7 +2661,7 @@ function process_channel_sync_delivery($sender,$arr,$deliveries) {
if(($y['name'] != $cl['name'])
|| ($y['visible'] != $cl['visible'])
|| ($y['deleted'] != $cl['deleted'])) {
- q("update groups set name = '%s', visible = %d, deleted = %d where hash = '%s' and uid = %d limit 1",
+ q("update groups set name = '%s', visible = %d, deleted = %d where hash = '%s' and uid = %d",
dbesc($cl['name']),
intval($cl['visible']),
intval($cl['deleted']),
@@ -2417,8 +2683,8 @@ function process_channel_sync_delivery($sender,$arr,$deliveries) {
intval($channel['channel_id']),
intval($cl['visible']),
intval($cl['deleted']),
- dbesc($cl['name'])
- );
+ dbesc($cl['name'])
+ );
}
// now look for any collections locally which weren't in the list we just received.
@@ -2438,7 +2704,7 @@ function process_channel_sync_delivery($sender,$arr,$deliveries) {
q("delete from group_member where gid = %d",
intval($y['id'])
);
- q("update groups set deleted = 1 where id = %d and uid = %d limit 1",
+ q("update groups set deleted = 1 where id = %d and uid = %d",
intval($y['id']),
intval($channel['channel_id'])
);
@@ -2501,7 +2767,7 @@ function process_channel_sync_delivery($sender,$arr,$deliveries) {
foreach($m as $mm) {
// if the local existing member isn't in the list we just received - remove them
if(! in_array($mm['xchan'],$members[$y['hash']])) {
- q("delete from group_member where xchan = '%s' and gid = %d and uid = %d limit 1",
+ q("delete from group_member where xchan = '%s' and gid = %d and uid = %d",
dbesc($mm['xchan']),
intval($y['id']),
intval($channel['channel_id'])
@@ -2547,8 +2813,7 @@ function process_channel_sync_delivery($sender,$arr,$deliveries) {
if(count($clean)) {
foreach($clean as $k => $v) {
$r = dbq("UPDATE profile set " . dbesc($k) . " = '" . dbesc($v)
- . "' where profile_guid = '" . dbesc($profile['profile_guid']) . "' and uid = " . intval($channel['channel_id'])
- . " limit 1");
+ . "' where profile_guid = '" . dbesc($profile['profile_guid']) . "' and uid = " . intval($channel['channel_id']));
}
}
}
@@ -2573,7 +2838,7 @@ function get_rpost_path($observer) {
function import_author_zot($x) {
$hash = make_xchan_hash($x['guid'],$x['guid_sig']);
- $r = q("select hubloc_url from hubloc where hubloc_guid = '%s' and hubloc_guid_sig = '%s' and (hubloc_flags & %d) limit 1",
+ $r = q("select hubloc_url from hubloc where hubloc_guid = '%s' and hubloc_guid_sig = '%s' and (hubloc_flags & %d)>0 limit 1",
dbesc($x['guid']),
dbesc($x['guid_sig']),
intval(HUBLOC_FLAGS_PRIMARY)
@@ -2592,3 +2857,110 @@ function import_author_zot($x) {
return false;
}
+
+/**
+ * @function zot_process_message_request($data)
+ * If a site receives a comment to a post but finds they have no parent to attach it with, they
+ * may send a 'request' packet containing the message_id of the missing parent. This is the handler
+ * for that packet. We will create a message_list array of the entire conversation starting with
+ * the missing parent and invoke delivery to the sender of the packet.
+ *
+ * include/deliver.php (for local delivery) and mod/post.php (for web delivery) detect the existence of
+ * this 'message_list' at the destination and split it into individual messages which are
+ * processed/delivered in order.
+ *
+ * Called from mod/post.php
+ */
+
+
+function zot_process_message_request($data) {
+ $ret = array('success' => false);
+
+ if(! $data['message_id']) {
+ $ret['message'] = 'no message_id';
+ logger('no message_id');
+ return $ret;
+ }
+
+ $sender = $data['sender'];
+ $sender_hash = make_xchan_hash($sender['guid'],$sender['guid_sig']);
+
+ /*
+ * Find the local channel in charge of this post (the first and only recipient of the request packet)
+ */
+
+ $arr = $data['recipients'][0];
+ $recip_hash = make_xchan_hash($arr['guid'],$arr['guid_sig']);
+ $c = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_hash = '%s' limit 1",
+ dbesc($recip_hash)
+ );
+ if(! $c) {
+ logger('recipient channel not found.');
+ $ret['message'] .= 'recipient not found.' . EOL;
+ return $ret;
+ }
+
+ /*
+ * fetch the requested conversation
+ */
+
+ $messages = zot_feed($c[0]['channel_id'],$sender_hash,array('message_id' => $data['message_id']));
+
+ if($messages) {
+ $env_recips = null;
+
+ $r = q("select hubloc_guid, hubloc_url, hubloc_sitekey, hubloc_network, hubloc_flags, hubloc_callback, hubloc_host
+ from hubloc where hubloc_hash = '%s' and not (hubloc_flags & %d)>0
+ and not (hubloc_status & %d)>0 group by hubloc_sitekey",
+ dbesc($sender_hash),
+ intval(HUBLOC_FLAGS_DELETED),
+ intval(HUBLOC_OFFLINE)
+ );
+ if(! $r) {
+ logger('no hubs');
+ return $ret;
+ }
+ $hubs = $r;
+ $hublist = array();
+ $keys = array();
+
+ $private = ((array_key_exists('flags',$messages[0]) && in_array('private',$messages[0]['flags'])) ? true : false);
+ if($private)
+ $env_recips = array('guid' => $sender['guid'],'guid_sig' => $sender['guid_sig'],'hash' => $sender_hash);
+
+ $data_packet = json_encode(array('message_list' => $messages));
+
+ foreach($hubs as $hub) {
+ $hash = random_string();
+
+ /*
+ * create a notify packet and drop the actual message packet in the queue for pickup
+ */
+
+ $n = zot_build_packet($c[0],'notify',$env_recips,(($private) ? $hub['hubloc_sitekey'] : null),$hash,array('message_id' => $data['message_id']));
+ q("insert into outq ( outq_hash, outq_account, outq_channel, outq_driver, outq_posturl, outq_async,
+ outq_created, outq_updated, outq_notify, outq_msg )
+ values ( '%s', %d, %d, '%s', '%s', %d, '%s', '%s', '%s', '%s' )",
+ dbesc($hash),
+ intval($c[0]['channel_account_id']),
+ intval($c[0]['channel_id']),
+ dbesc('zot'),
+ dbesc($hub['hubloc_callback']),
+ intval(1),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ dbesc($n),
+ dbesc($data_packet)
+ );
+
+ /*
+ * invoke delivery to send out the notify packet
+ */
+
+ proc_run('php','include/deliver.php',$hash);
+ }
+
+ }
+ $ret['success'] = true;
+ return $ret;
+}