aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/Contact.php100
-rw-r--r--include/ConversationObject.php29
-rw-r--r--include/ItemObject.php11
-rw-r--r--include/account.php99
-rw-r--r--include/acl_selectors.php2
-rw-r--r--include/activities.php2
-rw-r--r--include/api.php86
-rw-r--r--include/apps.php14
-rw-r--r--include/auth.php13
-rw-r--r--include/bb2diaspora.php337
-rw-r--r--include/bbcode.php7
-rw-r--r--include/chat.php23
-rw-r--r--include/conversation.php34
-rw-r--r--include/crypto.php193
-rw-r--r--include/datetime.php2
-rwxr-xr-xinclude/dba/dba_driver.php8
-rw-r--r--include/deliver.php18
-rwxr-xr-xinclude/diaspora.php2760
-rw-r--r--include/dir_fns.php54
-rw-r--r--include/enotify.php16
-rw-r--r--include/event.php24
-rw-r--r--include/externals.php6
-rw-r--r--include/features.php7
-rw-r--r--include/follow.php98
-rw-r--r--include/group.php1
-rw-r--r--include/identity.php97
-rwxr-xr-xinclude/items.php1141
-rw-r--r--include/message.php2
-rw-r--r--include/nav.php15
-rw-r--r--include/network.php1134
-rw-r--r--include/notifier.php83
-rw-r--r--include/onepoll.php21
-rw-r--r--include/permissions.php80
-rw-r--r--include/photo/photo_driver.php22
-rw-r--r--include/photo/photo_gd.php2
-rw-r--r--include/photos.php88
-rwxr-xr-xinclude/plugin.php87
-rw-r--r--include/poller.php37
-rw-r--r--include/queue.php20
-rw-r--r--include/security.php6
-rw-r--r--include/socgraph.php31
-rw-r--r--[-rwxr-xr-x]include/text.php36
-rw-r--r--include/widgets.php7
-rw-r--r--include/zot.php143
44 files changed, 5668 insertions, 1328 deletions
diff --git a/include/Contact.php b/include/Contact.php
index 787612c83..8d50b1e5b 100644
--- a/include/Contact.php
+++ b/include/Contact.php
@@ -162,7 +162,7 @@ function user_remove($uid) {
}
-function account_remove($account_id,$local = true) {
+function account_remove($account_id,$local = true,$unset_session=true) {
logger('account_remove: ' . $account_id);
@@ -185,6 +185,7 @@ function account_remove($account_id,$local = true) {
$r = q("select * from account where account_id = %d limit 1",
intval($account_id)
);
+ $account_email=$r[0]['account_email'];
if(! $r) {
logger('account_remove: No account with id: ' . $account_id);
@@ -196,7 +197,7 @@ function account_remove($account_id,$local = true) {
);
if($x) {
foreach($x as $xx) {
- channel_remove($xx['channel_id'],$local);
+ channel_remove($xx['channel_id'],$local,false);
}
}
@@ -204,11 +205,17 @@ function account_remove($account_id,$local = true) {
intval($account_id)
);
+ if ($unset_session) {
+ unset($_SESSION['authenticated']);
+ unset($_SESSION['uid']);
+ notice( sprintf(t("User '%s' deleted"),$account_email) . EOL);
+ goaway(get_app()->get_baseurl());
+ }
return $r;
}
-function channel_remove($channel_id, $local = true) {
+function channel_remove($channel_id, $local = true, $unset_session=true) {
if(! $channel_id)
return;
@@ -238,12 +245,12 @@ function channel_remove($channel_id, $local = true) {
intval($channel_id)
);
- $r = q("update hubloc set hubloc_flags = hubloc_flags | %d where hubloc_hash = '%s'",
+ $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'",
+ $r = q("update xchan set xchan_flags = (xchan_flags | %d) where xchan_hash = '%s'",
intval(XCHAN_FLAGS_DELETED),
dbesc($channel['channel_hash'])
);
@@ -267,7 +274,7 @@ function channel_remove($channel_id, $local = 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) limit 1",
dbesc($channel['channel_hash']),
dbesc(ABOOK_FLAG_SELF)
);
@@ -278,13 +285,13 @@ function channel_remove($channel_id, $local = true) {
intval($channel_id)
);
- $r = q("update hubloc set hubloc_flags = hubloc_flags | %d where hubloc_hash = '%s' and hubloc_url = '%s' ",
+ $r = q("update hubloc set hubloc_flags = (hubloc_flags | %d) where hubloc_hash = '%s' and hubloc_url = '%s' ",
intval(HUBLOC_FLAGS_DELETED),
dbesc($channel['channel_hash']),
dbesc(z_root())
);
- $r = q("update xchan set xchan_flags = xchan_flags | %d where xchan_hash = '%s' ",
+ $r = q("update xchan set xchan_flags = (xchan_flags | %d) where xchan_hash = '%s' ",
intval(XCHAN_FLAGS_DELETED),
dbesc($channel['channel_hash'])
);
@@ -292,7 +299,7 @@ function channel_remove($channel_id, $local = true) {
proc_run('php','include/directory.php',$channel_id);
- if($channel_id == local_user()) {
+ if($channel_id == local_user() && $unset_session) {
unset($_SESSION['authenticated']);
unset($_SESSION['uid']);
goaway($a->get_baseurl());
@@ -316,7 +323,7 @@ function mark_orphan_hubsxchans() {
return;
$r = q("update hubloc set hubloc_status = (hubloc_status | %d) where not (hubloc_status & %d)
- and hubloc_connected < utc_timestamp() - interval 36 day",
+ and hubloc_network = 'zot' and hubloc_connected < utc_timestamp() - interval 36 day",
intval(HUBLOC_OFFLINE),
intval(HUBLOC_OFFLINE)
);
@@ -428,12 +435,12 @@ function remove_all_xchan_resources($xchan, $channel_id = 0) {
// directory servers need to keep the record around for sync purposes - mark it deleted
- $r = q("update hubloc set hubloc_flags = hubloc_flags | %d where hubloc_hash = '%s'",
+ $r = q("update hubloc set hubloc_flags = (hubloc_flags | %d) where hubloc_hash = '%s'",
intval(HUBLOC_FLAGS_DELETED),
dbesc($xchan)
);
- $r = q("update xchan set xchan_flags = xchan_flags | %d where xchan_hash = '%s'",
+ $r = q("update xchan set xchan_flags = (xchan_flags | %d) where xchan_hash = '%s'",
intval(XCHAN_FLAGS_DELETED),
dbesc($xchan)
);
@@ -477,7 +484,7 @@ function contact_remove($channel_id, $abook_id) {
);
if($r) {
foreach($r as $rr) {
- drop_item($rr,false);
+ drop_item($rr['id'],false);
}
}
@@ -506,73 +513,6 @@ function contact_remove($channel_id, $abook_id) {
}
-// sends an unfriend message. Does not remove the contact
-
-function terminate_friendship($user,$self,$contact) {
-
-
- $a = get_app();
-
- require_once('include/datetime.php');
-
- if($contact['network'] === NETWORK_DFRN) {
- require_once('include/items.php');
- dfrn_deliver($user,$contact,'placeholder', 1);
- }
-
-}
-
-
-// Contact has refused to recognise us as a friend. We will start a countdown.
-// If they still don't recognise us in 32 days, the relationship is over,
-// and we won't waste any more time trying to communicate with them.
-// This provides for the possibility that their database is temporarily messed
-// up or some other transient event and that there's a possibility we could recover from it.
-
-if(! function_exists('mark_for_death')) {
-function mark_for_death($contact) {
-
- if($contact['archive'])
- return;
-
- if($contact['term_date'] == '0000-00-00 00:00:00') {
- q("UPDATE `contact` SET `term_date` = '%s' WHERE `id` = %d LIMIT 1",
- dbesc(datetime_convert()),
- intval($contact['id'])
- );
- }
- else {
-
- // TODO: We really should send a notification to the owner after 2-3 weeks
- // so they won't be surprised when the contact vanishes and can take
- // remedial action if this was a serious mistake or glitch
-
- $expiry = $contact['term_date'] . ' + 32 days ';
- if(datetime_convert() > datetime_convert('UTC','UTC',$expiry)) {
-
- // relationship is really truly dead.
- // archive them rather than delete
- // though if the owner tries to unarchive them we'll start the whole process over again
-
- q("update contact set `archive` = 1 where id = %d limit 1",
- intval($contact['id'])
- );
-
- //contact_remove($contact['id']);
-
- }
- }
-
-}}
-
-if(! function_exists('unmark_for_death')) {
-function unmark_for_death($contact) {
- // It's a miracle. Our dead contact has inexplicably come back to life.
- q("UPDATE `contact` SET `term_date` = '%s' WHERE `id` = %d LIMIT 1",
- dbesc('0000-00-00 00:00:00'),
- intval($contact['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");
diff --git a/include/ConversationObject.php b/include/ConversationObject.php
index 9bf410358..767ef7360 100644
--- a/include/ConversationObject.php
+++ b/include/ConversationObject.php
@@ -159,28 +159,23 @@ class Conversation extends BaseObject {
return false;
}
-// if(local_user() && $item->get_data_value('uid') == local_user())
-// $this->commentable = true;
-
-// if($this->writable)
-// $this->commentable = true;
-
$item->set_commentable(false);
$ob_hash = (($this->observer) ? $this->observer['xchan_hash'] : '');
- if(($item->get_data_value('author_xchan') === $ob_hash) || ($item->get_data_value('owner_xchan') === $ob_hash))
- $item->set_commentable(true);
+ if(! comments_are_now_closed($item->get_data())) {
+ if(($item->get_data_value('author_xchan') === $ob_hash) || ($item->get_data_value('owner_xchan') === $ob_hash))
+ $item->set_commentable(true);
- if($item->get_data_value('item_flags') & ITEM_NOCOMMENT) {
- $item->set_commentable(false);
- }
- elseif(($this->observer) && (! $item->is_commentable())) {
- if((array_key_exists('owner',$item->data)) && ($item->data['owner']['abook_flags'] & ABOOK_FLAG_SELF))
- $item->set_commentable(perm_is_allowed($this->profile_owner,$this->observer['xchan_hash'],'post_comments'));
- else
- $item->set_commentable(can_comment_on_post($this->observer['xchan_hash'],$item->data));
+ if($item->get_data_value('item_flags') & ITEM_NOCOMMENT) {
+ $item->set_commentable(false);
+ }
+ elseif(($this->observer) && (! $item->is_commentable())) {
+ if((array_key_exists('owner',$item->data)) && ($item->data['owner']['abook_flags'] & ABOOK_FLAG_SELF))
+ $item->set_commentable(perm_is_allowed($this->profile_owner,$this->observer['xchan_hash'],'post_comments'));
+ else
+ $item->set_commentable(can_comment_on_post($this->observer['xchan_hash'],$item->data));
+ }
}
-
require_once('include/identity.php');
$sys = get_sys_channel();
diff --git a/include/ItemObject.php b/include/ItemObject.php
index a5870ef91..451e369b0 100644
--- a/include/ItemObject.php
+++ b/include/ItemObject.php
@@ -90,6 +90,10 @@ class Item extends BaseObject {
: false);
$shareable = ((($conv->get_profile_owner() == local_user()) && ($item['item_private'] != 1)) ? true : false);
+ // allow an exemption for sharing stuff from your private feeds
+ if($item['author']['xchan_network'] === 'rss')
+ $shareable = true;
+
$mode = $conv->get_mode();
if(local_user() && $observer['xchan_hash'] === $item['author_xchan'])
@@ -207,10 +211,11 @@ class Item extends BaseObject {
if($this->is_commentable()) {
$like = array( t("I like this \x28toggle\x29"), t("like"));
$dislike = array( t("I don't like this \x28toggle\x29"), t("dislike"));
- if ($shareable)
- $share = array( t('Share This'), t('share'));
}
+ if ($shareable)
+ $share = array( t('Share This'), t('share'));
+
if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0)
$indent .= ' shiny';
@@ -251,7 +256,7 @@ class Item extends BaseObject {
'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,
diff --git a/include/account.php b/include/account.php
index 138db3290..a3d6ef025 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');
@@ -423,7 +423,7 @@ function user_deny($hash) {
if(! count($register))
return false;
- $account = q("SELECT account_id FROM account WHERE account_id = %d LIMIT 1",
+ $account = q("SELECT account_id, account_email FROM account WHERE account_id = %d LIMIT 1",
intval($register[0]['uid'])
);
@@ -511,9 +511,10 @@ 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 != '%s'
and account_expires < UTC_TIMESTAMP() ",
- intval(ACCOUNT_EXPIRED)
+ intval(ACCOUNT_EXPIRED),
+ dbesc(NULL_DATE)
);
if(! $r)
@@ -528,7 +529,7 @@ function downgrade_accounts() {
$x = q("UPDATE account set account_service_class = '%s', account_expires = '%s'
where account_id = %d limit 1",
dbesc($basic),
- dbesc('0000-00-00 00:00:00'),
+ dbesc(NULL_DATE),
intval($rr['account_id'])
);
$ret = array('account' => $rr);
@@ -547,3 +548,91 @@ function downgrade_accounts() {
}
}
+
+
+// check service_class restrictions. If there are no service_classes defined, everything is allowed.
+// if $usage is supplied, we check against a maximum count and return true if the current usage is
+// less than the subscriber plan allows. Otherwise we return boolean true or false if the property
+// is allowed (or not) in this subscriber plan. An unset property for this service plan means
+// the property is allowed, so it is only necessary to provide negative properties for each plan,
+// or what the subscriber is not allowed to do.
+
+
+function service_class_allows($uid,$property,$usage = false) {
+ $a = get_app();
+ if($uid == local_user()) {
+ $service_class = $a->account['account_service_class'];
+ }
+ else {
+ $r = q("select account_service_class as service_class
+ from channel c, account a
+ where c.channel_account_id=a.account_id and c.channel_id= %d limit 1",
+ intval($uid)
+ );
+ if($r !== false and count($r)) {
+ $service_class = $r[0]['service_class'];
+ }
+ }
+ if(! x($service_class))
+ return true; // everything is allowed
+
+ $arr = get_config('service_class',$service_class);
+ if(! is_array($arr) || (! count($arr)))
+ return true;
+
+ if($usage === false)
+ return ((x($arr[$property])) ? (bool) $arr[$property] : true);
+ else {
+ if(! array_key_exists($property,$arr))
+ return true;
+ return (((intval($usage)) < intval($arr[$property])) ? true : false);
+ }
+}
+
+
+function service_class_fetch($uid,$property) {
+ $a = get_app();
+ if($uid == local_user()) {
+ $service_class = $a->account['account_service_class'];
+ }
+ else {
+ $r = q("select account_service_class as service_class
+ from channel c, account a
+ where c.channel_account_id=a.account_id and c.channel_id= %d limit 1",
+ intval($uid)
+ );
+ if($r !== false and 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)
+ return '';
+ if($bbcode)
+ $t = sprintf('[zrl=%s]' . t('Click here to upgrade.') . '[/zrl]', $l);
+ else
+ $t = sprintf('<a href="%s">' . t('Click here to upgrade.') . '</div>', $l);
+ return $t;
+}
+
+function upgrade_message($bbcode = false) {
+ $x = upgrade_link($bbcode);
+ return t('This action exceeds the limits set by your subscription plan.') . (($x) ? ' ' . $x : '') ;
+}
+
+function upgrade_bool_message($bbcode = false) {
+ $x = upgrade_link($bbcode);
+ return t('This action is not available under your subscription plan.') . (($x) ? ' ' . $x : '') ;
+}
diff --git a/include/acl_selectors.php b/include/acl_selectors.php
index 8d94264e4..0b68ba227 100644
--- a/include/acl_selectors.php
+++ b/include/acl_selectors.php
@@ -237,7 +237,7 @@ function populate_acl($defaults = null,$show_jotnets = true) {
$tpl = get_markup_template("acl_selector.tpl");
$o = replace_macros($tpl, array(
- '$showall'=> t("Visible to everybody"),
+ '$showall'=> t("Visible to your default audience"),
'$show' => t("Show"),
'$hide' => t("Don't show"),
'$allowcid' => json_encode($allow_cid),
diff --git a/include/activities.php b/include/activities.php
index 4502b758e..d978ebcd6 100644
--- a/include/activities.php
+++ b/include/activities.php
@@ -25,7 +25,7 @@ function profile_activity($changed, $value) {
$arr['verb'] = ACTIVITY_UPDATE;
$arr['obj_type'] = ACTIVITY_OBJ_PROFILE;
- $arr['$plink'] = z_root() . '/channel/' . $self['channel_address'] . '/?f=&mid=' . $arr['mid'];
+ $arr['plink'] = z_root() . '/channel/' . $self['channel_address'] . '/?f=&mid=' . $arr['mid'];
$A = '[url=' . z_root() . '/channel/' . $self['channel_address'] . ']' . $self['channel_name'] . '[/url]';
diff --git a/include/api.php b/include/api.php
index 57551a3b0..47de945db 100644
--- a/include/api.php
+++ b/include/api.php
@@ -119,16 +119,36 @@ require_once('include/items.php');
// process normal login request
require_once('include/auth.php');
+ $channel_login = 0;
$record = account_verify_password($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']);
if(! $record) {
- logger('API_login failure: ' . print_r($_SERVER,true), LOGGER_DEBUG);
- header('WWW-Authenticate: Basic realm="Red"');
- header('HTTP/1.0 401 Unauthorized');
- die('This api requires login');
+ $r = q("select * from channel where channel_address = '%s' limit 1",
+ dbesc($_SERVER['PHP_AUTH_USER'])
+ );
+ if ($r) {
+ $x = q("select * from account where account_id = %d limit 1",
+ intval($r[0]['channel_account_id'])
+ );
+ if ($x) {
+ $record = account_verify_password($x[0]['account_email'],$_SERVER['PHP_AUTH_PW']);
+ if($record)
+ $channel_login = $r[0]['channel_id'];
+ }
+ }
+ if(! $record) {
+ logger('API_login failure: ' . print_r($_SERVER,true), LOGGER_DEBUG);
+ header('WWW-Authenticate: Basic realm="Red"');
+ header('HTTP/1.0 401 Unauthorized');
+ die('This api requires login');
+ }
}
require_once('include/security.php');
authenticate_success($record);
+
+ if($channel_login)
+ change_channel($channel_login);
+
$_SESSION['allow_api'] = true;
}
@@ -562,7 +582,40 @@ 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);
@@ -1498,6 +1551,9 @@ require_once('include/items.php');
$a = get_app();
$ret = array();
+ if(! $r)
+ return $ret;
+
foreach($r as $item) {
localize_item($item);
@@ -1875,17 +1931,19 @@ require_once('include/items.php');
);
$ret = Array();
- foreach($r as $item) {
- if ($box == "inbox" || $item['from-url'] != $profile_url){
- $recipient = $user_info;
- $sender = api_get_user($a,$item['contact-id']);
- }
- elseif ($box == "sentbox" || $item['from-url'] != $profile_url){
- $recipient = api_get_user($a,$item['contact-id']);
- $sender = $user_info;
+ if($r) {
+ foreach($r as $item) {
+ if ($box == "inbox" || $item['from-url'] != $profile_url){
+ $recipient = $user_info;
+ $sender = api_get_user($a,$item['contact-id']);
+ }
+ elseif ($box == "sentbox" || $item['from-url'] != $profile_url){
+ $recipient = api_get_user($a,$item['contact-id']);
+ $sender = $user_info;
+ }
+
+ $ret[]=api_format_messages($item, $recipient, $sender);
}
-
- $ret[]=api_format_messages($item, $recipient, $sender);
}
diff --git a/include/apps.php b/include/apps.php
index 135eaa99a..91012b0ef 100644
--- a/include/apps.php
+++ b/include/apps.php
@@ -124,7 +124,7 @@ function translate_system_apps(&$arr) {
'Bookmarks' => t('Bookmarks'),
'Address Book' => t('Address Book'),
'Login' => t('Login'),
- 'Channel Select' => t('Channel Select'),
+ 'Channel Manager' => t('Channel Manager'),
'Matrix' => t('Matrix'),
'Settings' => t('Settings'),
'Files' => t('Files'),
@@ -141,7 +141,13 @@ function translate_system_apps(&$arr) {
'Chat' => t('Chat'),
'Search' => t('Search'),
'Probe' => t('Probe'),
- 'Suggest' => t('Suggest')
+ 'Suggest' => t('Suggest'),
+ 'Random Channel' => t('Random Channel'),
+ 'Invite' => t('Invite'),
+ 'Features' => t('Features'),
+ 'Language' => t('Language'),
+ 'Post' => t('Post'),
+ 'Profile Photo' => t('Profile Photo')
);
if(array_key_exists($arr['name'],$apps))
@@ -461,4 +467,6 @@ function app_encode($app,$embed = false) {
function papp_encode($papp) {
return chunk_split(base64_encode(json_encode($papp)),72,"\n");
-} \ No newline at end of file
+}
+
+
diff --git a/include/auth.php b/include/auth.php
index cc07917b7..f8188f443 100644
--- a/include/auth.php
+++ b/include/auth.php
@@ -60,6 +60,19 @@ function account_verify_password($email,$pass) {
}
$error = 'password failed for ' . $email;
logger($error);
+
+
+ if($record['account_flags'] & ACCOUNT_UNVERIFIED)
+ logger('Account is unverified. account_flags = ' . $record['account_flags']);
+ if($record['account_flags'] & ACCOUNT_BLOCKED)
+ 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']);
+
// Also log failed logins to a separate auth log to reduce overhead for server side intrusion prevention
$authlog = get_config('system', 'authlog');
if ($authlog)
diff --git a/include/bb2diaspora.php b/include/bb2diaspora.php
index f15a34e99..9c884926a 100644
--- a/include/bb2diaspora.php
+++ b/include/bb2diaspora.php
@@ -77,6 +77,36 @@ function share_unshield($m) {
}
+function diaspora_mention_callback($matches) {
+
+ $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];
+
+ 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]' ;
+
+}
+
+
// we don't want to support a bbcode specific markdown interpreter
// and the markdown library we have is pretty good, but provides HTML output.
@@ -97,7 +127,16 @@ function diaspora2bb($s,$use_zrl = false) {
$s = str_replace("<br/>","<br />",$s);
$s = str_replace("\n","<br />",$s);
- $s = preg_replace('/\@\{(.+?)\; (.+?)\@(.+?)\}/','@[url=https://$3/u/$2]$1[/url]',$s);
+
+// $s = preg_replace('/\@\{(.+?)\; (.+?)\@(.+?)\}/','@[url=https://$3/u/$2]$1[/url]',$s);
+
+ // first try plustags
+ // nope don't do it. This will cause mis-attributed messages and runaway delivery chains -
+ // Diaspora doesn't have sufficient delivery loop detection.
+ // Leave the next line commented and leave this description here so future readers will know why.
+ // $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
// $s = preg_replace('/\#([^\s\#])/','\\#$1',$s);
@@ -211,6 +250,106 @@ function diaspora_ol($s) {
return preg_replace("/\[\\\\\*\]( *)/", "1. ", $s[1]);
}
+function bb2dmention_callback($match) {
+
+ $r = q("select xchan_addr from xchan where xchan_url = '%s'",
+ dbesc($match[2])
+ );
+
+ if($r)
+ return '@{' . $match[3] . ' ; ' . $r[0]['xchan_addr'] . '}';
+ return '@' . $match[3];
+
+}
+
+
+function bb2diaspora_itemwallwall(&$item) {
+
+ if(($item['mid'] == $item['parent_mid']) && ($item['author_xchan'] != $item['owner_xchan']) && (is_array($item['author']))) {
+ 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']['photo']['src'] . '[/img]'
+ . '[url=' . $item['author']['url'] . ']' . $item['author']['name'] . '[/url]' . "\n\n"
+ . $item['body'];
+ }
+}
+
+
+function bb2diaspora_itembody($item) {
+
+ if($item['diaspora_meta']) {
+ $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'];
+ bb2diaspora_itemwallwall($newitem);
+ return $newitem['body'];
+ }
+ }
+ }
+
+ $newitem = $item;
+
+ if(array_key_exists('item_flags',$item) && ($item['item_flags'] & ITEM_OBSCURED)) {
+ $key = get_config('system','prvkey');
+ $newitem['title'] = (($item['title']) ? crypto_unencapsulate(json_decode($item['title'],true),$key) : '');
+ $newitem['body'] = (($item['body']) ? crypto_unencapsulate(json_decode($item['body'],true),$key) : '');
+ }
+
+ bb2diaspora_itemwallwall($newitem);
+
+ $body = preg_replace('/\#\^http/i', 'http', $newitem['body']);
+
+ // protect tags and mentions from hijacking
+
+ if(intval(get_pconfig($item['uid'],'system','prevent_tag_hijacking'))) {
+ $new_tag = html_entity_decode('&#x22d5;',ENT_COMPAT,'UTF-8');
+ $new_mention = html_entity_decode('&#xff20;',ENT_COMPAT,'UTF-8');
+
+ // #-tags
+ $body = preg_replace('/\#\[url/i', $new_tag . '[url', $body);
+ $body = preg_replace('/\#\[zrl/i', $new_tag . '[zrl', $body);
+ // @-mentions
+ $body = preg_replace('/\@\!?\[url/i', $new_mention . '[url', $body);
+ $body = preg_replace('/\@\!?\[zrl/i', $new_mention . '[zrl', $body);
+ }
+
+ // remove multiple newlines
+ do {
+ $oldbody = $body;
+ $body = str_replace("\n\n\n", "\n\n", $body);
+ } while ($oldbody != $body);
+
+ $body = bb2diaspora($body);
+
+ if(strlen($title))
+ $body = "## " . $title . "\n\n" . $body;
+
+ if($item['attach']) {
+ $cnt = preg_match_all('/href=\"(.*?)\"(.*?)title=\"(.*?)\"/ism',$item['attach'],$matches,PREG_SET_ORDER);
+ if(cnt) {
+ $body .= "\n" . t('Attachments:') . "\n";
+ foreach($matches as $mtch) {
+ $body .= '[' . $mtch[3] . '](' . $mtch[1] . ')' . "\n";
+ }
+ }
+ }
+
+ logger('bb2diaspora_itembody : ' . $body);
+
+ return html_entity_decode($body);
+
+}
function bb2diaspora($Text,$preserve_nl = false, $fordiaspora = true) {
@@ -221,10 +360,14 @@ function bb2diaspora($Text,$preserve_nl = false, $fordiaspora = true) {
/**
* Transform #tags, strip off the [url] and replace spaces with underscore
*/
- $Text = preg_replace_callback('/#\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', create_function('$match',
- 'return \'#\'. str_replace(\' \', \'_\', $match[2]);'
+ $Text = preg_replace_callback('/#\[([zu])rl\=(\w+.*?)\](\w+.*?)\[\/[(zu)]rl\]/i', create_function('$match',
+ 'return \'#\'. str_replace(\' \', \'_\', $match[3]);'
), $Text);
+ $Text = preg_replace('/#\^\[([zu])rl\=(\w+.*?)\](\w+.*?)\[\/([zu])rl\]/i', '[$1rl=$2]$3[/$4rl]', $Text);
+
+ $Text = preg_replace_callback('/\@\!?\[([zu])rl\=(\w+.*?)\](\w+.*?)\[\/([zu])rl\]/i', 'bb2dmention_callback', $Text);
+
// Converting images with size parameters to simple images. Markdown doesn't know it.
$Text = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $Text);
@@ -252,195 +395,9 @@ function bb2diaspora($Text,$preserve_nl = false, $fordiaspora = true) {
// Remove all unconverted tags
$Text = strip_tags($Text);
-
-/* Old routine
-
- $ev = bbtoevent($Text);
-
- // Replace any html brackets with HTML Entities to prevent executing HTML or script
- // Don't use strip_tags here because it breaks [url] search by replacing & with amp
-
- $Text = str_replace("<", "&lt;", $Text);
- $Text = str_replace(">", "&gt;", $Text);
-
- // If we find any event code, turn it into an event.
- // After we're finished processing the bbcode we'll
- // replace all of the event code with a reformatted version.
-
- if($preserve_nl)
- $Text = str_replace(array("\n","\r"), array('',''),$Text);
- else
- // Remove the "return" character, as Diaspora uses only the "newline"
- // character, so having the "return" character can cause signature
- // failures
- $Text = str_replace("\r", "", $Text);
-
-
- // Set up the parameters for a URL search string
- $URLSearchString = "^\[\]";
- // Set up the parameters for a MAIL search string
- $MAILSearchString = $URLSearchString;
-
- // Perform URL Search
-
- // [img]pathtoimage[/img]
-
- // the following was added on 10-January-2012 due to an inability of Diaspora's
- // new javascript markdown processor to handle links with images as the link "text"
- // It is not optimal and may be removed if this ability is restored in the future
-
- $Text = preg_replace("/\[url\=([$URLSearchString]*)\]\[img\](.*?)\[\/img\]\[\/url\]/ism",
- '![' . t('image/photo') . '](' . '$2' . ')' . "\n" . '[' . t('link') . '](' . '$1' . ')', $Text);
-
- $Text = preg_replace("/\[bookmark\]([$URLSearchString]*)\[\/bookmark\]/ism", '[$1]($1)', $Text);
- $Text = preg_replace("/\[bookmark\=([$URLSearchString]*)\](.*?)\[\/bookmark\]/ism", '[$2]($1)', $Text);
-
- $Text = preg_replace("/\[url\]([$URLSearchString]*)\[\/url\]/ism", '[$1]($1)', $Text);
- $Text = preg_replace("/\#\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '[#$2]($1)', $Text);
- $Text = preg_replace("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '[$2]($1)', $Text);
-
-
- $Text = preg_replace("/\[img\](.*?)\[\/img\]/", '![' . t('image/photo') . '](' . '$1' . ')', $Text);
- $Text = preg_replace("/\[img\=(.*?)\](.*?)\[\/img\]/", '![' . t('image/photo') . '](' . '$2' . ')', $Text);
-
- $Text = preg_replace("/\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '[$1]($1)', $Text);
- $Text = preg_replace("/\#\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '[#$2]($1)', $Text);
- $Text = preg_replace("/\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '[$2]($1)', $Text);
-
-
- $Text = preg_replace("/\[zmg\](.*?)\[\/zmg\]/", '![' . t('image/photo') . '](' . '$1' . ')', $Text);
- $Text = preg_replace("/\[zmg\=(.*?)\](.*?)\[\/zmg\]/", '![' . t('image/photo') . '](' . '$2' . ')', $Text);
-
- // Perform MAIL Search
- $Text = preg_replace("(\[mail\]([$MAILSearchString]*)\[/mail\])", '[$1](mailto:$1)', $Text);
- $Text = preg_replace("/\[mail\=([$MAILSearchString]*)\](.*?)\[\/mail\]/", '[$2](mailto:$1)', $Text);
-
- $Text = str_replace('*', '\\*', $Text);
- $Text = str_replace('_', '\\_', $Text);
-
- $Text = str_replace('`','\\`', $Text);
-
- // Check for bold text
- $Text = preg_replace("(\[b\](.*?)\[\/b\])is",'**$1**',$Text);
-
- // Check for italics text
- $Text = preg_replace("(\[i\](.*?)\[\/i\])is",'_$1_',$Text);
-
- // Check for underline text
- // Replace with italics since Diaspora doesn't have underline
- $Text = preg_replace("(\[u\](.*?)\[\/u\])is",'_$1_',$Text);
-
- // Check for strike-through text
- $Text = preg_replace("(\[s\](.*?)\[\/s\])is",'**[strike]**$1**[/strike]**',$Text);
-
- // Check for over-line text
-// $Text = preg_replace("(\[o\](.*?)\[\/o\])is",'<span class="overline">$1</span>',$Text);
-
- // Check for colored text
- // Remove color since Diaspora doesn't support it
- $Text = preg_replace("(\[color=(.*?)\](.*?)\[\/color\])is","$2",$Text);
-
- // Check for sized text
- // Remove it since Diaspora doesn't support sizes very well
- $Text = preg_replace("(\[size=(.*?)\](.*?)\[\/size\])is","$2",$Text);
-
- // Check for list text
- $endlessloop = 0;
- while ((((strpos($Text, "[/list]") !== false) && (strpos($Text, "[list") !== false)) ||
- ((strpos($Text, "[/ol]") !== false) && (strpos($Text, "[ol]") !== false)) ||
- ((strpos($Text, "[/ul]") !== false) && (strpos($Text, "[ul]") !== false)) ||
- ((strpos($Text, "[/li]") !== false) && (strpos($Text, "[li]") !== false))) && (++$endlessloop < 20)) {
- $Text = preg_replace_callback("/\[list\](.*?)\[\/list\]/is", 'diaspora_ul', $Text);
- $Text = preg_replace_callback("/\[list=1\](.*?)\[\/list\]/is", 'diaspora_ol', $Text);
- $Text = preg_replace_callback("/\[list=i\](.*?)\[\/list\]/s",'diaspora_ol', $Text);
- $Text = preg_replace_callback("/\[list=I\](.*?)\[\/list\]/s", 'diaspora_ol', $Text);
- $Text = preg_replace_callback("/\[list=a\](.*?)\[\/list\]/s", 'diaspora_ol', $Text);
- $Text = preg_replace_callback("/\[list=A\](.*?)\[\/list\]/s", 'diaspora_ol', $Text);
- $Text = preg_replace_callback("/\[ul\](.*?)\[\/ul\]/is", 'diaspora_ul', $Text);
- $Text = preg_replace_callback("/\[ol\](.*?)\[\/ol\]/is", 'diaspora_ol', $Text);
- $Text = preg_replace("/\[li\]( *)(.*?)\[\/li\]/s", '* $2' ,$Text);
- }
-
- // Just get rid of table tags since Diaspora doesn't support tables
- $Text = preg_replace("/\[th\](.*?)\[\/th\]/s", '$1' ,$Text);
- $Text = preg_replace("/\[td\](.*?)\[\/td\]/s", '$1' ,$Text);
- $Text = preg_replace("/\[tr\](.*?)\[\/tr\]/s", '$1' ,$Text);
- $Text = preg_replace("/\[table\](.*?)\[\/table\]/s", '$1' ,$Text);
-
- $Text = preg_replace("/\[table border=(.*?)\](.*?)\[\/table\]/s", '$2' ,$Text);
-// $Text = preg_replace("/\[table border=0\](.*?)\[\/table\]/s", '<table border="0" >$1</table>' ,$Text);
-
-
-// $Text = str_replace("[*]", "<li>", $Text);
-
- // Check for font change text
-// $Text = preg_replace("(\[font=(.*?)\](.*?)\[\/font\])","<span style=\"font-family: $1;\">$2</span>",$Text);
-
-
- $Text = preg_replace_callback("/\[code\](.*?)\[\/code\]/is",'stripdcode_br_cb',$Text);
-
- // Check for [code] text
- $Text = preg_replace("/(\[code\])+(.*?)(\[\/code\])+/is","\t$2\n", $Text);
-
-
-
-
- // Declare the format for [quote] layout
- // $QuoteLayout = '<blockquote>$1</blockquote>';
- // Check for [quote] text
- $Text = preg_replace("/\[quote\](.*?)\[\/quote\]/is",">$1\n\n", $Text);
- $Text = preg_replace("/\[quote=(.*?)\](.*?)\[\/quote\]/is",">$2\n\n", $Text);
-
- // Images
-
- // html5 video and audio
-
- $Text = preg_replace("/\[video\](.*?)\[\/video\]/", '$1', $Text);
-
- $Text = preg_replace("/\[audio\](.*?)\[\/audio\]/", '$1', $Text);
-
-// $Text = preg_replace("/\[iframe\](.*?)\[\/iframe\]/", '<iframe src="$1" width="425" height="350"><a href="$1">$1</a></iframe>', $Text);
-
- // [img=widthxheight]image source[/img]
-// $Text = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/", '<img src="$3" style="height:{$2}px; width:{$1}px;" >', $Text);
-
- $Text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/watch\?v\=(.*?)\[\/youtube\]/ism",'http://www.youtube.com/watch?v=$1',$Text);
- $Text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/embed\/(.*?)\[\/youtube\]/ism",'http://www.youtube.com/watch?v=$1',$Text);
- $Text = preg_replace("/\[youtube\]https?:\/\/youtu.be\/(.*?)\[\/youtube\]/ism",'http://www.youtube.com/watch?v=$1',$Text);
- $Text = preg_replace("/\[youtube\]([A-Za-z0-9\-_=]+)(.*?)\[\/youtube\]/ism", 'http://www.youtube.com/watch?v=$1', $Text);
-
- $Text = preg_replace("/\[vimeo\]https?:\/\/player.vimeo.com\/video\/([0-9]+)(.*?)\[\/vimeo\]/ism",'http://vimeo.com/$1',$Text);
- $Text = preg_replace("/\[vimeo\]https?:\/\/vimeo.com\/([0-9]+)(.*?)\[\/vimeo\]/ism",'http://vimeo.com/$1',$Text);
- $Text = preg_replace("/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism", 'http://vimeo.com/$1',$Text);
-
-
- $Text = str_replace('[nosmile]','',$Text);
-
- // oembed tag
- // $Text = oembed_bbcode2html($Text);
-
- // If we found an event earlier, strip out all the event code and replace with a reformatted version.
-
- if(x($ev,'start')) {
-
- $sub = format_event_diaspora($ev);
-
- $Text = preg_replace("/\[event\-summary\](.*?)\[\/event\-summary\]/is",'',$Text);
- $Text = preg_replace("/\[event\-description\](.*?)\[\/event\-description\]/is",'',$Text);
- $Text = preg_replace("/\[event\-start\](.*?)\[\/event\-start\]/is",$sub,$Text);
- $Text = preg_replace("/\[event\-finish\](.*?)\[\/event\-finish\]/is",'',$Text);
- $Text = preg_replace("/\[event\-location\](.*?)\[\/event\-location\]/is",'',$Text);
- $Text = preg_replace("/\[event\-adjust\](.*?)\[\/event\-adjust\]/is",'',$Text);
- }
-
- $Text = preg_replace("/\<(.*?)(src|href)=(.*?)\&amp\;(.*?)\>/ism",'<$1$2=$3&$4>',$Text);
-
- $Text = preg_replace_callback('/\[(.*?)\]\((.*?)\)/ism','unescape_underscores_in_links',$Text);
-
-*/
-
// Remove any leading or trailing whitespace, as this will mess up
// the Diaspora signature verification and cause the item to disappear
+
$Text = trim($Text);
call_hooks('bb2diaspora',$Text);
diff --git a/include/bbcode.php b/include/bbcode.php
index 195700729..a7055fc45 100644
--- a/include/bbcode.php
+++ b/include/bbcode.php
@@ -502,6 +502,13 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true) {
if (strpos($Text,'[o]') !== false) {
$Text = preg_replace("(\[o\](.*?)\[\/o\])ism",'<span class="overline">$1</span>',$Text);
}
+ if (strpos($Text,'[sup]') !== false) {
+ $Text = preg_replace("(\[sup\](.*?)\[\/sup\])ism",'<sup>$1</sup>',$Text);
+ }
+ if (strpos($Text,'[sub]') !== false) {
+ $Text = preg_replace("(\[sub\](.*?)\[\/sub\])ism",'<sub>$1</sub>',$Text);
+ }
+
// Check for colored text
if (strpos($Text,'[/color]') !== false) {
$Text = preg_replace("(\[color=(.*?)\](.*?)\[\/color\])ism","<span style=\"color: $1;\">$2</span>",$Text);
diff --git a/include/chat.php b/include/chat.php
index 5f69853e7..b8fb185df 100644
--- a/include/chat.php
+++ b/include/chat.php
@@ -128,8 +128,10 @@ function chatroom_enter($observer_xchan,$room_id,$status,$client) {
}
}
- if(intval($x[0]['cr_expire']))
- $r = q("delete from chat where created < UTC_TIMESTAMP() - INTERVAL " . intval($x[0]['cr_expire']) . " MINUTE and chat_room = " . intval($x[0]['cr_id']));
+ 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("select * from chatpresence where cp_xchan = '%s' and cp_room = %d limit 1",
dbesc($observer_xchan),
@@ -153,7 +155,6 @@ function chatroom_enter($observer_xchan,$room_id,$status,$client) {
dbesc($client)
);
- chatroom_flush($room_id,$xchan);
return $r;
}
@@ -226,21 +227,5 @@ function chat_message($uid,$room_id,$xchan,$text) {
);
$ret['success'] = true;
- chatroom_flush($room_id,$xchan);
return $ret;
}
-
-/**
- * Reduces the number of lines shown in chat by removing those older than MAX_CHATROOM_HOURS
- */
-
-function chatroom_flush($room_id,$xchan) {
-
-
- $date_limit = date('Y-m-d H:i:s', time() - 3600 * MAX_CHATROOM_HOURS);
- $d = q("delete from chat where chat_room = %d and chat_xchan = '%s' and created < '%s'",
- intval($room_id),
- dbesc($xchan),
- datetime_convert('','', $date_limit));
- return true;
-}
diff --git a/include/conversation.php b/include/conversation.php
index c0d0c1e7d..05ae72d61 100644
--- a/include/conversation.php
+++ b/include/conversation.php
@@ -262,6 +262,8 @@ function localize_item(&$item){
$item['body'] = sprintf($txt, $A, t($verb));
}
+
+
/*
// FIXME store parent item as object or target
// (and update to json storage)
@@ -363,6 +365,17 @@ function localize_item(&$item){
// if($sparkle)
// $item['plink'] = $y . '?f=&url=' . $item['plink'];
// }
+
+ // if item body was obscured and we changed it, re-obscure it
+ // FIXME - we need a better filter than just the string 'data'; try and
+ // match the fact that it's json encoded
+
+ if(($item['item_flags'] & ITEM_OBSCURED)
+ && strlen($item['body']) && (! strpos($item['body'],'data'))) {
+ $item['body'] = json_encode(crypto_encapsulate($item['body'],get_config('system','pubkey')));
+ }
+
+
}
/**
@@ -694,7 +707,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,
@@ -902,6 +915,7 @@ function item_photo_menu($item){
$contact_url="";
$pm_url="";
$vsrc_link = "";
+ $follow_url = "";
if(local_user()) {
$ssl_state = true;
@@ -923,6 +937,9 @@ function item_photo_menu($item){
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'])
+ $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'];
@@ -940,6 +957,7 @@ function item_photo_menu($item){
t("View Profile") => $profile_link,
t("View Photos") => $photos_link,
t("Matrix Activity") => $posts_link,
+ t("Connect") => $follow_url,
t("Edit Contact") => $contact_url,
t("Send PM") => $pm_url,
t("Poke") => $poke_link
@@ -1399,7 +1417,7 @@ function network_tabs() {
if(! get_config('system','disable_discover_tab')) {
$tabs[] = array(
'label' => t('Discover'),
- 'url'=>$a->get_baseurl(true) . '/' . $cmd . '?f=&fh=1' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : '') . ((x($_GET,'gid')) ? '&gid=' . $_GET['gid'] : ''),
+ 'url'=>z_root() . '/' . $cmd . '?f=&fh=1' ,
'sel'=> $public_active,
'title'=> t('Imported public streams'),
);
@@ -1407,14 +1425,14 @@ function network_tabs() {
$tabs[] = array(
'label' => t('Commented Order'),
- 'url'=>$a->get_baseurl(true) . '/' . $cmd . '?f=&order=comment' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : '') . ((x($_GET,'gid')) ? '&gid=' . $_GET['gid'] : ''),
+ 'url'=>z_root() . '/' . $cmd . '?f=&order=comment' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : '') . ((x($_GET,'gid')) ? '&gid=' . $_GET['gid'] : ''),
'sel'=>$all_active,
'title'=> t('Sort by Comment Date'),
);
$tabs[] = array(
'label' => t('Posted Order'),
- 'url'=>$a->get_baseurl(true) . '/' . $cmd . '?f=&order=post' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : '') . ((x($_GET,'gid')) ? '&gid=' . $_GET['gid'] : ''),
+ 'url'=>z_root() . '/' . $cmd . '?f=&order=post' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : '') . ((x($_GET,'gid')) ? '&gid=' . $_GET['gid'] : ''),
'sel'=>$postord_active,
'title' => t('Sort by Post Date'),
);
@@ -1422,7 +1440,7 @@ function network_tabs() {
if(feature_enabled(local_user(),'personal_tab')) {
$tabs[] = array(
'label' => t('Personal'),
- 'url' => $a->get_baseurl(true) . '/' . $cmd . '?f=' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : '') . '&conv=1',
+ 'url' => z_root() . '/' . $cmd . '?f=' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : '') . '&conv=1',
'sel' => $conv_active,
'title' => t('Posts that mention or involve you'),
);
@@ -1431,7 +1449,7 @@ function network_tabs() {
if(feature_enabled(local_user(),'new_tab')) {
$tabs[] = array(
'label' => t('New'),
- 'url' => $a->get_baseurl(true) . '/' . $cmd . '?f=' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : '') . '&new=1' . ((x($_GET,'gid')) ? '&gid=' . $_GET['gid'] : ''),
+ 'url' => z_root() . '/' . $cmd . '?f=' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : '') . '&new=1' . ((x($_GET,'gid')) ? '&gid=' . $_GET['gid'] : ''),
'sel' => $new_active,
'title' => t('Activity Stream - by date'),
);
@@ -1440,7 +1458,7 @@ function network_tabs() {
if(feature_enabled(local_user(),'star_posts')) {
$tabs[] = array(
'label' => t('Starred'),
- 'url'=>$a->get_baseurl(true) . '/' . $cmd . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '') . '&star=1',
+ 'url'=>z_root() . '/' . $cmd . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '') . '&star=1',
'sel'=>$starred_active,
'title' => t('Favourite Posts'),
);
@@ -1450,7 +1468,7 @@ function network_tabs() {
if(feature_enabled(local_user(),'spam_filter')) {
$tabs[] = array(
'label' => t('Spam'),
- 'url'=>$a->get_baseurl(true) . '/network?f=&spam=1',
+ 'url'=> z_root() . '/network?f=&spam=1',
'sel'=> $spam_active,
'title' => t('Posts flagged as SPAM'),
);
diff --git a/include/crypto.php b/include/crypto.php
index 33cdc10c0..07655e24f 100644
--- a/include/crypto.php
+++ b/include/crypto.php
@@ -1,5 +1,8 @@
<?php /** @file */
+require_once('library/ASNValue.class.php');
+require_once('library/asn1.php');
+
function rsa_sign($data,$key,$alg = 'sha256') {
if(! $key)
return 'no key';
@@ -127,3 +130,193 @@ function new_keypair($bits) {
}
+function pkcs1to8($oldkey,$len) {
+
+ if($len == 4096)
+ $c = 'g';
+ if($len == 2048)
+ $c = 'Q';
+
+ if(strstr($oldkey,'BEGIN PUBLIC'))
+ return $oldkey;
+
+ $oldkey = str_replace('-----BEGIN RSA PUBLIC KEY-----', '', $oldkey);
+ $oldkey = trim(str_replace('-----END RSA PUBLIC KEY-----', '', $oldkey));
+ $key = 'MIICIjANBgkqhkiG9w0BAQEFAAOCA' . $c . '8A' . str_replace("\n", '', $oldkey);
+ $key = "-----BEGIN PUBLIC KEY-----\n" . wordwrap($key, 64, "\n", true) . "\n-----END PUBLIC KEY-----";
+ return $key;
+}
+
+function pkcs8to1($oldkey,$len) {
+
+ if(strstr($oldkey,'BEGIN RSA'))
+ return $oldkey;
+
+ $oldkey = str_replace('-----BEGIN PUBLIC KEY-----', '', $oldkey);
+ $oldkey = trim(str_replace('-----END PUBLIC KEY-----', '', $oldkey));
+ $key = str_replace("\n",'',$oldkey);
+ $key = substr($key,32);
+ $key = "-----BEGIN RSA PUBLIC KEY-----\n" . wordwrap($key, 64, "\n", true) . "\n-----END RSA PUBLIC KEY-----";
+ return $key;
+}
+
+
+function DerToPem($Der, $Private=false)
+{
+ //Encode:
+ $Der = base64_encode($Der);
+ //Split lines:
+ $lines = str_split($Der, 65);
+ $body = implode("\n", $lines);
+ //Get title:
+ $title = $Private? 'RSA PRIVATE KEY' : 'PUBLIC KEY';
+ //Add wrapping:
+ $result = "-----BEGIN {$title}-----\n";
+ $result .= $body . "\n";
+ $result .= "-----END {$title}-----\n";
+
+ return $result;
+}
+
+function DerToRsa($Der)
+{
+ //Encode:
+ $Der = base64_encode($Der);
+ //Split lines:
+ $lines = str_split($Der, 64);
+ $body = implode("\n", $lines);
+ //Get title:
+ $title = 'RSA PUBLIC KEY';
+ //Add wrapping:
+ $result = "-----BEGIN {$title}-----\n";
+ $result .= $body . "\n";
+ $result .= "-----END {$title}-----\n";
+
+ return $result;
+}
+
+
+function pkcs8_encode($Modulus,$PublicExponent) {
+ //Encode key sequence
+ $modulus = new ASNValue(ASNValue::TAG_INTEGER);
+ $modulus->SetIntBuffer($Modulus);
+ $publicExponent = new ASNValue(ASNValue::TAG_INTEGER);
+ $publicExponent->SetIntBuffer($PublicExponent);
+ $keySequenceItems = array($modulus, $publicExponent);
+ $keySequence = new ASNValue(ASNValue::TAG_SEQUENCE);
+ $keySequence->SetSequence($keySequenceItems);
+ //Encode bit string
+ $bitStringValue = $keySequence->Encode();
+ $bitStringValue = chr(0x00) . $bitStringValue; //Add unused bits byte
+ $bitString = new ASNValue(ASNValue::TAG_BITSTRING);
+ $bitString->Value = $bitStringValue;
+ //Encode body
+ $bodyValue = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00" . $bitString->Encode();
+ $body = new ASNValue(ASNValue::TAG_SEQUENCE);
+ $body->Value = $bodyValue;
+ //Get DER encoded public key:
+ $PublicDER = $body->Encode();
+ return $PublicDER;
+}
+
+
+function pkcs1_encode($Modulus,$PublicExponent) {
+ //Encode key sequence
+ $modulus = new ASNValue(ASNValue::TAG_INTEGER);
+ $modulus->SetIntBuffer($Modulus);
+ $publicExponent = new ASNValue(ASNValue::TAG_INTEGER);
+ $publicExponent->SetIntBuffer($PublicExponent);
+ $keySequenceItems = array($modulus, $publicExponent);
+ $keySequence = new ASNValue(ASNValue::TAG_SEQUENCE);
+ $keySequence->SetSequence($keySequenceItems);
+ //Encode bit string
+ $bitStringValue = $keySequence->Encode();
+ return $bitStringValue;
+}
+
+
+function metopem($m,$e) {
+ $der = pkcs8_encode($m,$e);
+ $key = DerToPem($der,false);
+ return $key;
+}
+
+
+function pubrsatome($key,&$m,&$e) {
+ require_once('library/asn1.php');
+
+ $lines = explode("\n",$key);
+ unset($lines[0]);
+ unset($lines[count($lines)]);
+ $x = base64_decode(implode('',$lines));
+
+ $r = ASN_BASE::parseASNString($x);
+
+ $m = base64url_decode($r[0]->asnData[0]->asnData);
+ $e = base64url_decode($r[0]->asnData[1]->asnData);
+}
+
+
+function rsatopem($key) {
+ pubrsatome($key,$m,$e);
+ return(metopem($m,$e));
+}
+
+function pemtorsa($key) {
+ pemtome($key,$m,$e);
+ return(metorsa($m,$e));
+}
+
+function pemtome($key,&$m,&$e) {
+ $lines = explode("\n",$key);
+ unset($lines[0]);
+ unset($lines[count($lines)]);
+ $x = base64_decode(implode('',$lines));
+
+ $r = ASN_BASE::parseASNString($x);
+
+ $m = base64url_decode($r[0]->asnData[1]->asnData[0]->asnData[0]->asnData);
+ $e = base64url_decode($r[0]->asnData[1]->asnData[0]->asnData[1]->asnData);
+}
+
+function metorsa($m,$e) {
+ $der = pkcs1_encode($m,$e);
+ $key = DerToRsa($der);
+ return $key;
+}
+
+function salmon_key($pubkey) {
+ pemtome($pubkey,$m,$e);
+ return 'RSA' . '.' . base64url_encode($m,true) . '.' . base64url_encode($e,true) ;
+}
+
+// old function for providing mysql compatible encryption and is also
+// used in Friendica 'RINO'. This function is messy and should be retired.
+
+
+if(! function_exists('aes_decrypt')) {
+function aes_decrypt($val,$ky)
+{
+ $key="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+ for($a=0;$a<strlen($ky);$a++)
+ $key[$a%16]=chr(ord($key[$a%16]) ^ ord($ky[$a]));
+ $mode = MCRYPT_MODE_ECB;
+ $enc = MCRYPT_RIJNDAEL_128;
+ $dec = @mcrypt_decrypt($enc, $key, $val, $mode, @mcrypt_create_iv( @mcrypt_get_iv_size($enc, $mode), MCRYPT_DEV_URANDOM ) );
+ return rtrim($dec,(( ord(substr($dec,strlen($dec)-1,1))>=0 and ord(substr($dec, strlen($dec)-1,1))<=16)? chr(ord( substr($dec,strlen($dec)-1,1))):null));
+}}
+
+
+if(! function_exists('aes_encrypt')) {
+function aes_encrypt($val,$ky)
+{
+ $key="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+ for($a=0;$a<strlen($ky);$a++)
+ $key[$a%16]=chr(ord($key[$a%16]) ^ ord($ky[$a]));
+ $mode=MCRYPT_MODE_ECB;
+ $enc=MCRYPT_RIJNDAEL_128;
+ $val=str_pad($val, (16*(floor(strlen($val) / 16)+(strlen($val) % 16==0?2:1))), chr(16-(strlen($val) % 16)));
+ return mcrypt_encrypt($enc, $key, $val, $mode, mcrypt_create_iv( mcrypt_get_iv_size($enc, $mode), MCRYPT_DEV_URANDOM));
+}}
+
+
diff --git a/include/datetime.php b/include/datetime.php
index 0214b9e4c..84ab1e2fa 100644
--- a/include/datetime.php
+++ b/include/datetime.php
@@ -271,7 +271,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');
}
diff --git a/include/dba/dba_driver.php b/include/dba/dba_driver.php
index c829c3714..3e19b7aa4 100755
--- a/include/dba/dba_driver.php
+++ b/include/dba/dba_driver.php
@@ -112,8 +112,12 @@ function q($sql) {
if($db && $db->connected) {
$stmt = vsprintf($sql,$args);
- if($stmt === false)
- logger('dba: vsprintf error: ' . print_r(debug_backtrace(),true));
+ 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);
}
diff --git a/include/deliver.php b/include/deliver.php
index 0ad008c23..f4fae6061 100644
--- a/include/deliver.php
+++ b/include/deliver.php
@@ -20,6 +20,24 @@ function deliver_run($argv, $argc) {
dbesc($argv[$x])
);
if($r) {
+ if($r[0]['outq_driver'] === 'post') {
+ $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",
+ 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",
+ dbesc(datetime_convert()),
+ dbesc($argv[$x])
+ );
+ }
+ continue;
+ }
+
if($r[0]['outq_posturl'] === z_root() . '/post') {
logger('deliver: local delivery', LOGGER_DEBUG);
// local delivery
diff --git a/include/diaspora.php b/include/diaspora.php
new file mode 100755
index 000000000..8a3eaf873
--- /dev/null
+++ b/include/diaspora.php
@@ -0,0 +1,2760 @@
+<?php
+
+require_once('include/crypto.php');
+require_once('include/items.php');
+require_once('include/bb2diaspora.php');
+require_once('include/contact_selectors.php');
+//require_once('include/queue_fn.php');
+//require_once('include/lock.php');
+
+function diaspora_dispatch_public($msg) {
+
+ $enabled = intval(get_config('system','diaspora_enabled'));
+ if(! $enabled) {
+ logger('mod-diaspora: disabled');
+ return;
+ }
+
+ // find everybody following or allowing this author
+
+
+ $r = q("SELECT * from channel where channel_id in ( SELECT abook_channel from abook left join xchan on abook_xchan = xchan_hash WHERE xchan_network like '%%diaspora%%' and xchan_addr = '%s' )",
+ dbesc($msg['author'])
+ );
+
+ // also need to look for those following public streams
+
+ if($r) {
+ foreach($r as $rr) {
+ logger('diaspora_public: delivering to: ' . $rr['channel_name'] . ' (' . $rr['channel_address'] . ') ');
+ diaspora_dispatch($rr,$msg);
+ }
+ }
+ else
+ logger('diaspora_public: no subscribers');
+}
+
+
+
+function diaspora_dispatch($importer,$msg,$attempt=1) {
+
+ $ret = 0;
+
+ $enabled = intval(get_config('system','diaspora_enabled'));
+ if(! $enabled) {
+ logger('mod-diaspora: disabled');
+ return;
+ }
+
+ // php doesn't like dashes in variable names
+
+ $msg['message'] = str_replace(
+ array('<activity_streams-photo>','</activity_streams-photo>'),
+ array('<asphoto>','</asphoto>'),
+ $msg['message']);
+
+
+ $parsed_xml = parse_xml_string($msg['message'],false);
+
+ $xmlbase = $parsed_xml->post;
+
+ logger('diaspora_dispatch: ' . print_r($xmlbase,true), LOGGER_DEBUG);
+
+
+ if($xmlbase->request) {
+ $ret = diaspora_request($importer,$xmlbase->request);
+ }
+ elseif($xmlbase->status_message) {
+ $ret = diaspora_post($importer,$xmlbase->status_message,$msg);
+ }
+ elseif($xmlbase->profile) {
+ $ret = diaspora_profile($importer,$xmlbase->profile,$msg);
+ }
+ elseif($xmlbase->comment) {
+ $ret = diaspora_comment($importer,$xmlbase->comment,$msg);
+ }
+ elseif($xmlbase->like) {
+ $ret = diaspora_like($importer,$xmlbase->like,$msg);
+ }
+ elseif($xmlbase->asphoto) {
+ $ret = diaspora_asphoto($importer,$xmlbase->asphoto,$msg);
+ }
+ elseif($xmlbase->reshare) {
+ $ret = diaspora_reshare($importer,$xmlbase->reshare,$msg);
+ }
+ elseif($xmlbase->retraction) {
+ $ret = diaspora_retraction($importer,$xmlbase->retraction,$msg);
+ }
+ elseif($xmlbase->signed_retraction) {
+ $ret = diaspora_signed_retraction($importer,$xmlbase->signed_retraction,$msg);
+ }
+ elseif($xmlbase->relayable_retraction) {
+ $ret = diaspora_signed_retraction($importer,$xmlbase->relayable_retraction,$msg);
+ }
+ elseif($xmlbase->photo) {
+ $ret = diaspora_photo($importer,$xmlbase->photo,$msg,$attempt);
+ }
+ elseif($xmlbase->conversation) {
+ $ret = diaspora_conversation($importer,$xmlbase->conversation,$msg);
+ }
+ elseif($xmlbase->message) {
+ $ret = diaspora_message($importer,$xmlbase->message,$msg);
+ }
+ else {
+ logger('diaspora_dispatch: unknown message type: ' . print_r($xmlbase,true));
+ }
+ return $ret;
+}
+
+
+function diaspora_is_blacklisted($s) {
+
+ $bl1 = get_config('system','blacklisted_sites');
+ if(is_array($bl1) && $bl1) {
+ foreach($bl1 as $bl) {
+ if($bl && strpos($s,$bl) !== false) {
+ logger('diaspora_is_blacklisted: blacklisted ' . $s);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+function diaspora_process_outbound($arr) {
+
+/*
+
+ We are passed the following array from the notifier, providing everything we need to make delivery decisions.
+
+ diaspora_process_outbound(array(
+ 'channel' => $channel,
+ 'env_recips' => $env_recips,
+ 'recipients' => $recipients,
+ 'item' => $item,
+ 'target_item' => $target_item,
+ 'hub' => $hub,
+ 'top_level_post' => $top_level_post,
+ 'private' => $private,
+ 'followup' => $followup,
+ 'relay_to_owner' => $relay_to_owner,
+ 'uplink' => $uplink,
+ 'cmd' => $cmd,
+ 'expire' => $expire,
+ 'mail' => $mail,
+ 'fsuggest' => $fsuggest,
+ 'normal_mode' => $normal_mode,
+ 'packet_type' => $packet_type,
+ 'walltowall' => $walltowall,
+ ));
+*/
+
+
+ $target_item = $arr['target_item'];
+
+ if($target_item && array_key_exists('item_flags',$target_item) && ($target_item['item_flags'] & ITEM_OBSCURED)) {
+ $key = get_config('system','prvkey');
+ if($target_item['title'])
+ $target_item['title'] = crypto_unencapsulate(json_decode($target_item['title'],true),$key);
+ if($target_item['body'])
+ $target_item['body'] = crypto_unencapsulate(json_decode($target_item['body'],true),$key);
+ }
+
+
+ if($arr['env_recips']) {
+ $hashes = array();
+
+ // re-explode the recipients, but only for this hub/pod
+
+ foreach($arr['env_recips'] as $recip)
+ $hashes[] = "'" . $recip['hash'] . "'";
+
+ $r = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_url = '%s'
+ and xchan_hash in (" . implode(',', $hashes) . ") and xchan_network in ('diaspora', 'friendica-over-diaspora') ",
+ dbesc($arr['hub']['hubloc_url'])
+ );
+
+ if(! $r) {
+ logger('diaspora_process_outbound: no recipients');
+ return;
+ }
+
+ foreach($r as $contact) {
+
+ if($arr['mail']) {
+ diaspora_send_mail($arr['item'],$arr['channel'],$contact);
+ continue;
+ }
+
+ if(! $arr['normal_mode'])
+ continue;
+
+ // special handling for followup to public post
+ // all other public posts processed as public batches further below
+
+ if((! $arr['private']) && ($arr['followup'])) {
+ diaspora_send_followup($target_item,$arr['channel'],$contact, true);
+ continue;
+ }
+
+ if(! $contact['xchan_pubkey'])
+ continue;
+
+ if(activity_match($target_item['verb'],ACTIVITY_DISLIKE)) {
+ continue;
+ }
+ elseif(($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);
+ continue;
+ }
+ elseif($arr['followup']) {
+ // send comments and likes to owner to relay
+ diaspora_send_followup($target_item,$arr['channel'],$contact);
+ continue;
+ }
+
+ elseif($target_item['mid'] !== $target_item['parent_mid']) {
+ // we are the relay - send comments, likes and relayable_retractions
+ // (of comments and likes) to our conversants
+ diaspora_send_relay($target_item,$arr['channel'],$contact);
+ continue;
+ }
+ elseif($arr['top_level_post']) {
+ diaspora_send_status($target_item,$arr['channel'],$contact);
+ continue;
+ }
+ }
+ }
+ else {
+ // public message
+
+ $contact = $arr['hub'];
+
+ if($target_item['verb'] === ACTIVITY_DISLIKE) {
+ // unsupported
+ return;
+ }
+ elseif(($target_item['deleted'])
+ && ($target_item['mid'] === $target_item['parent_mod'])) {
+ // top-level retraction
+ logger('delivery: diaspora retract: ' . $loc);
+ diaspora_send_retraction($target_item,$arr['channel'],$contact,true);
+ return;
+ }
+ elseif($target_item['mid'] !== $target_item['parent_mid']) {
+ // we are the relay - send comments, likes and relayable_retractions to our conversants
+ logger('delivery: diaspora relay: ' . $loc);
+ diaspora_send_relay($target_item,$arr['channel'],$contact,true);
+ 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;
+ }
+
+ }
+
+}
+
+
+
+
+function diaspora_handle_from_contact($contact_hash) {
+
+ logger("diaspora_handle_from_contact: contact id is " . $contact_hash, LOGGER_DEBUG);
+
+ $r = q("SELECT xchan_addr from xchan where xchan_hash = '%s' limit 1",
+ dbesc($contact_hash)
+ );
+ if($r) {
+ return $r[0]['xchan_addr'];
+ }
+ return false;
+}
+
+function diaspora_get_contact_by_handle($uid,$handle) {
+
+ if(diaspora_is_blacklisted($handle))
+ return false;
+
+ $r = q("SELECT * FROM abook left join xchan on xchan_hash = abook_xchan where xchan_addr = '%s' and abook_channel = %d limit 1",
+ dbesc($handle),
+ intval($uid)
+ );
+ return (($r) ? $r[0] : false);
+}
+
+function find_diaspora_person_by_handle($handle) {
+
+ $person = false;
+
+ if(diaspora_is_blacklisted($handle))
+ return false;
+
+ $r = q("select * from xchan where xchan_addr = '%s' limit 1",
+ dbesc($handle)
+ );
+ if($r) {
+ $person = $r[0];
+ logger('find_diaspora_person_by handle: in cache ' . print_r($r,true), LOGGER_DATA);
+ }
+
+ if(! $person) {
+
+ // try webfinger. Make sure to distinguish between diaspora,
+ // redmatrix w/diaspora protocol and friendica w/diaspora protocol.
+
+ $result = discover_by_webbie($handle);
+ if($result) {
+ $r = q("select * from xchan where xchan_addr = '%s' limit 1",
+ dbesc($handle)
+ );
+ if($r) {
+ $person = $r[0];
+ logger('find_diaspora_person_by handle: discovered ' . print_r($r,true), LOGGER_DATA);
+ }
+ }
+ }
+
+ return $person;
+}
+
+
+function get_diaspora_key($handle) {
+ logger('Fetching diaspora key for: ' . $handle, LOGGER_DEBUG);
+ $r = find_diaspora_person_by_handle($handle);
+ return(($r) ? $r['xchan_pubkey'] : '');
+}
+
+
+function diaspora_pubmsg_build($msg,$channel,$contact,$prvkey,$pubkey) {
+
+ $a = get_app();
+
+ logger('diaspora_pubmsg_build: ' . $msg, LOGGER_DATA);
+
+ $handle = $channel['channel_address'] . '@' . get_app()->get_hostname();
+
+
+ $b64url_data = base64url_encode($msg,false);
+
+ $data = str_replace(array("\n","\r"," ","\t"),array('','','',''),$b64url_data);
+
+ $type = 'application/xml';
+ $encoding = 'base64url';
+ $alg = 'RSA-SHA256';
+
+ $signable_data = $data . '.' . base64url_encode($type,false) . '.'
+ . base64url_encode($encoding,false) . '.' . base64url_encode($alg,false) ;
+
+ $signature = rsa_sign($signable_data,$prvkey);
+ $sig = base64url_encode($signature,false);
+
+$magic_env = <<< EOT
+<?xml version='1.0' encoding='UTF-8'?>
+<diaspora xmlns="https://joindiaspora.com/protocol" xmlns:me="http://salmon-protocol.org/ns/magic-env" >
+ <header>
+ <author_id>$handle</author_id>
+ </header>
+ <me:env>
+ <me:encoding>base64url</me:encoding>
+ <me:alg>RSA-SHA256</me:alg>
+ <me:data type="application/xml">$data</me:data>
+ <me:sig>$sig</me:sig>
+ </me:env>
+</diaspora>
+EOT;
+
+ logger('diaspora_pubmsg_build: magic_env: ' . $magic_env, LOGGER_DATA);
+ return $magic_env;
+
+}
+
+
+
+
+function diaspora_msg_build($msg,$channel,$contact,$prvkey,$pubkey,$public = false) {
+ $a = get_app();
+
+ if($public)
+ return diaspora_pubmsg_build($msg,$channel,$contact,$prvkey,$pubkey);
+
+ logger('diaspora_msg_build: ' . $msg, LOGGER_DATA);
+
+ // without a public key nothing will work
+
+ if(! $pubkey) {
+ logger('diaspora_msg_build: pubkey missing: contact id: ' . $contact['abook_id']);
+ return '';
+ }
+
+ $inner_aes_key = random_string(32);
+ $b_inner_aes_key = base64_encode($inner_aes_key);
+ $inner_iv = random_string(16);
+ $b_inner_iv = base64_encode($inner_iv);
+
+ $outer_aes_key = random_string(32);
+ $b_outer_aes_key = base64_encode($outer_aes_key);
+ $outer_iv = random_string(16);
+ $b_outer_iv = base64_encode($outer_iv);
+
+ $handle = $channel['channel_address'] . '@' . get_app()->get_hostname();
+
+ $padded_data = pkcs5_pad($msg,16);
+ $inner_encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $inner_aes_key, $padded_data, MCRYPT_MODE_CBC, $inner_iv);
+
+ $b64_data = base64_encode($inner_encrypted);
+
+
+ $b64url_data = base64url_encode($b64_data,false);
+ $data = str_replace(array("\n","\r"," ","\t"),array('','','',''),$b64url_data);
+
+ $type = 'application/xml';
+ $encoding = 'base64url';
+ $alg = 'RSA-SHA256';
+
+ $signable_data = $data . '.' . base64url_encode($type,false) . '.'
+ . base64url_encode($encoding,false) . '.' . base64url_encode($alg,false) ;
+
+ logger('diaspora_msg_build: signable_data: ' . $signable_data, LOGGER_DATA);
+
+ $signature = rsa_sign($signable_data,$prvkey);
+ $sig = base64url_encode($signature,false);
+
+$decrypted_header = <<< EOT
+<decrypted_header>
+ <iv>$b_inner_iv</iv>
+ <aes_key>$b_inner_aes_key</aes_key>
+ <author_id>$handle</author_id>
+</decrypted_header>
+EOT;
+
+ $decrypted_header = pkcs5_pad($decrypted_header,16);
+
+ $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $outer_aes_key, $decrypted_header, MCRYPT_MODE_CBC, $outer_iv);
+
+ $outer_json = json_encode(array('iv' => $b_outer_iv,'key' => $b_outer_aes_key));
+
+ $encrypted_outer_key_bundle = '';
+ openssl_public_encrypt($outer_json,$encrypted_outer_key_bundle,$pubkey);
+
+ $b64_encrypted_outer_key_bundle = base64_encode($encrypted_outer_key_bundle);
+
+ logger('outer_bundle: ' . $b64_encrypted_outer_key_bundle . ' key: ' . $pubkey, LOGGER_DATA);
+
+ $encrypted_header_json_object = json_encode(array('aes_key' => base64_encode($encrypted_outer_key_bundle),
+ 'ciphertext' => base64_encode($ciphertext)));
+ $cipher_json = base64_encode($encrypted_header_json_object);
+
+ $encrypted_header = '<encrypted_header>' . $cipher_json . '</encrypted_header>';
+
+$magic_env = <<< EOT
+<?xml version='1.0' encoding='UTF-8'?>
+<diaspora xmlns="https://joindiaspora.com/protocol" xmlns:me="http://salmon-protocol.org/ns/magic-env" >
+ $encrypted_header
+ <me:env>
+ <me:encoding>base64url</me:encoding>
+ <me:alg>RSA-SHA256</me:alg>
+ <me:data type="application/xml">$data</me:data>
+ <me:sig>$sig</me:sig>
+ </me:env>
+</diaspora>
+EOT;
+
+ logger('diaspora_msg_build: magic_env: ' . $magic_env, LOGGER_DATA);
+ return $magic_env;
+
+}
+
+/**
+ *
+ * diaspora_decode($importer,$xml)
+ * array $importer -> from user table
+ * string $xml -> urldecoded Diaspora salmon
+ *
+ * Returns array
+ * 'message' -> decoded Diaspora XML message
+ * 'author' -> author diaspora handle
+ * 'key' -> author public key (converted to pkcs#8)
+ *
+ * Author and key are used elsewhere to save a lookup for verifying replies and likes
+ */
+
+
+function diaspora_decode($importer,$xml) {
+
+ $public = false;
+ $basedom = parse_xml_string($xml);
+
+ $children = $basedom->children('https://joindiaspora.com/protocol');
+
+ if($children->header) {
+ $public = true;
+ $author_link = str_replace('acct:','',$children->header->author_id);
+ }
+ else {
+
+ $encrypted_header = json_decode(base64_decode($children->encrypted_header));
+
+ $encrypted_aes_key_bundle = base64_decode($encrypted_header->aes_key);
+ $ciphertext = base64_decode($encrypted_header->ciphertext);
+
+ $outer_key_bundle = '';
+ openssl_private_decrypt($encrypted_aes_key_bundle,$outer_key_bundle,$importer['channel_prvkey']);
+
+ $j_outer_key_bundle = json_decode($outer_key_bundle);
+
+ $outer_iv = base64_decode($j_outer_key_bundle->iv);
+ $outer_key = base64_decode($j_outer_key_bundle->key);
+
+ $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $outer_key, $ciphertext, MCRYPT_MODE_CBC, $outer_iv);
+
+
+ $decrypted = pkcs5_unpad($decrypted);
+
+ /**
+ * $decrypted now contains something like
+ *
+ * <decrypted_header>
+ * <iv>8e+G2+ET8l5BPuW0sVTnQw==</iv>
+ * <aes_key>UvSMb4puPeB14STkcDWq+4QE302Edu15oaprAQSkLKU=</aes_key>
+
+***** OBSOLETE
+
+ * <author>
+ * <name>Ryan Hughes</name>
+ * <uri>acct:galaxor@diaspora.pirateship.org</uri>
+ * </author>
+
+***** CURRENT
+
+ * <author_id>galaxor@diaspora.priateship.org</author_id>
+
+***** END DIFFS
+
+ * </decrypted_header>
+ */
+
+ logger('decrypted: ' . $decrypted, LOGGER_DEBUG);
+ $idom = parse_xml_string($decrypted,false);
+
+ $inner_iv = base64_decode($idom->iv);
+ $inner_aes_key = base64_decode($idom->aes_key);
+
+ $author_link = str_replace('acct:','',$idom->author_id);
+
+ }
+
+ $dom = $basedom->children(NAMESPACE_SALMON_ME);
+
+ // figure out where in the DOM tree our data is hiding
+
+ if($dom->provenance->data)
+ $base = $dom->provenance;
+ elseif($dom->env->data)
+ $base = $dom->env;
+ elseif($dom->data)
+ $base = $dom;
+
+ if(! $base) {
+ logger('mod-diaspora: unable to locate salmon data in xml ');
+ http_status_exit(400);
+ }
+
+
+ // Stash the signature away for now. We have to find their key or it won't be good for anything.
+ $signature = base64url_decode($base->sig);
+
+ // unpack the data
+
+ // strip whitespace so our data element will return to one big base64 blob
+ $data = str_replace(array(" ","\t","\r","\n"),array("","","",""),$base->data);
+
+
+ // stash away some other stuff for later
+
+ $type = $base->data[0]->attributes()->type[0];
+ $keyhash = $base->sig[0]->attributes()->keyhash[0];
+ $encoding = $base->encoding;
+ $alg = $base->alg;
+
+ $signed_data = $data . '.' . base64url_encode($type,false) . '.' . base64url_encode($encoding,false) . '.' . base64url_encode($alg,false);
+
+
+ // decode the data
+ $data = base64url_decode($data);
+
+
+ if($public) {
+ $inner_decrypted = $data;
+ }
+ else {
+
+ // Decode the encrypted blob
+
+ $inner_encrypted = base64_decode($data);
+ $inner_decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $inner_aes_key, $inner_encrypted, MCRYPT_MODE_CBC, $inner_iv);
+ $inner_decrypted = pkcs5_unpad($inner_decrypted);
+ }
+
+ if(! $author_link) {
+ logger('mod-diaspora: Could not retrieve author URI.');
+ http_status_exit(400);
+ }
+
+ // Once we have the author URI, go to the web and try to find their public key
+ // (first this will look it up locally if it is in the fcontact cache)
+ // This will also convert diaspora public key from pkcs#1 to pkcs#8
+
+ logger('mod-diaspora: Fetching key for ' . $author_link );
+ $key = get_diaspora_key($author_link);
+
+ if(! $key) {
+ logger('mod-diaspora: Could not retrieve author key.');
+ http_status_exit(400);
+ }
+
+ $verify = rsa_verify($signed_data,$signature,$key);
+
+ if(! $verify) {
+ logger('mod-diaspora: Message did not verify. Discarding.');
+ http_status_exit(400);
+ }
+
+ logger('mod-diaspora: Message verified.');
+
+ return array('message' => $inner_decrypted, 'author' => $author_link, 'key' => $key);
+
+}
+
+
+/* sender is now sharing with recipient */
+
+function diaspora_request($importer,$xml) {
+
+ $a = get_app();
+
+ $sender_handle = unxmlify($xml->sender_handle);
+ $recipient_handle = unxmlify($xml->recipient_handle);
+
+ if(! $sender_handle || ! $recipient_handle)
+ return;
+
+
+ // Do we already have an abook record?
+
+ $contact = diaspora_get_contact_by_handle($importer['channel_id'],$sender_handle);
+
+ if($contact && $contact['abook_id']) {
+
+ // perhaps we were already sharing with this person. Now they're sharing with us.
+ // That makes us friends. Maybe.
+
+ // Please note some of these permissions such as PERMS_R_PAGES are impossible for Disapora.
+ // They cannot authenticate to our system.
+
+ $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",
+ intval($newperms),
+ intval($contact['abook_id']),
+ intval($importer['channel_id'])
+ );
+
+ return;
+ }
+
+ $ret = find_diaspora_person_by_handle($sender_handle);
+
+ if((! $ret) || (! strstr($ret['xchan_network'],'diaspora'))) {
+ logger('diaspora_request: Cannot resolve diaspora handle ' . $sender_handle . ' for ' . $recipient_handle);
+ 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']);
+
+ $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) 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']),
+ intval($default_perms),
+ intval($their_perms),
+ intval(99),
+ intval(0),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ dbesc(NULL_DATE),
+ intval(($default_perms) ? 0 : ABOOK_FLAG_PENDING)
+ );
+
+
+ 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 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'])
+ );
+ if($new_connection) {
+ require_once('include/enotify.php');
+ notification(array(
+ 'type' => NOTIFY_INTRO,
+ 'from_xchan' => $ret['xchan_hash'],
+ 'to_xchan' => $importer['channel_hash'],
+ 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id'],
+ ));
+
+ if($default_perms) {
+ // Send back a sharing notification to them
+ diaspora_share($importer,$new_connection[0]);
+
+ }
+ }
+ }
+
+ // find the abook record we just created
+
+ $contact_record = diaspora_get_contact_by_handle($importer['channel_id'],$sender_handle);
+
+ if(! $contact_record) {
+ logger('diaspora_request: unable to locate newly created contact record.');
+ return;
+ }
+
+ /** If there is a default group for this channel, add this member to it */
+
+ if($importer['channel_default_group']) {
+ require_once('include/group.php');
+ $g = group_rec_byhash($importer['channel_id'],$importer['channel_default_group']);
+ if($g)
+ group_add_member($importer['channel_id'],'',$contact_record['xchan_hash'],$g['id']);
+ }
+
+ return;
+}
+
+
+
+
+function diaspora_post($importer,$xml,$msg) {
+
+ $a = get_app();
+ $guid = notags(unxmlify($xml->guid));
+ $diaspora_handle = notags(unxmlify($xml->diaspora_handle));
+ $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;
+ }
+
+ $contact = diaspora_get_contact_by_handle($importer['channel_id'],$diaspora_handle);
+ if(! $contact)
+ return;
+
+
+
+ if(! $app) {
+ if(strstr($contact['xchan_network'],'friendica'))
+ $app = 'Friendica';
+ else
+ $app = 'Diaspora';
+ }
+
+
+ if(! perm_is_allowed($importer['channel_id'],$contact['xchan_hash'],'send_stream')) {
+ logger('diaspora_post: Ignoring this author.');
+ return 202;
+ }
+
+ $search_guid = ((strlen($guid) == 64) ? $guid . '%' : $guid);
+
+ $r = q("SELECT id FROM item WHERE uid = %d AND mid like '%s' LIMIT 1",
+ intval($importer['channel_id']),
+ dbesc($search_guid)
+ );
+
+ if($r) {
+ // check dates if post editing is implemented
+ logger('diaspora_post: message exists: ' . $guid);
+ return;
+ }
+
+ $created = unxmlify($xml->created_at);
+ $private = ((unxmlify($xml->public) == 'false') ? 1 : 0);
+
+ $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);
+
+ $datarray = array();
+
+
+ $tags = get_tags($body);
+
+
+ if(count($tags)) {
+
+ $datarray['term'] = array();
+
+ foreach($tags as $tag) {
+ if(strpos($tag,'#') === 0) {
+ if(strpos($tag,'[url='))
+ continue;
+
+ // don't link tags that are already embedded in links
+
+ if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$body))
+ continue;
+ if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$body))
+ continue;
+
+ $basetag = str_replace('_',' ',substr($tag,1));
+ $body = str_replace($tag,'#[url=' . $a->get_baseurl() . '/search?tag=' . rawurlencode($basetag) . ']' . $basetag . '[/url]',$body);
+
+ $datarray['term'][] = array(
+ 'uid' => $importer['channel_id'],
+ 'type' => TERM_HASHTAG,
+ 'otype' => TERM_OBJ_POST,
+ 'term' => $basetag,
+ 'url' => z_root() . '/search?tag=' . rawurlencode($basetag)
+ );
+ }
+ }
+ }
+
+ $cnt = preg_match_all('/@\[url=(.*?)\](.*?)\[\/url\]/ism',$body,$matches,PREG_SET_ORDER);
+ if($cnt) {
+ foreach($matches as $mtch) {
+ $datarray['term'][] = array(
+ 'uid' => $importer['channel_id'],
+ 'type' => TERM_MENTION,
+ 'otype' => TERM_OBJ_POST,
+ 'term' => $mtch[2],
+ 'url' => $mtch[1]
+ );
+ }
+ }
+
+ $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;
+
+ $datarray['uid'] = $importer['channel_id'];
+
+ $datarray['verb'] = ACTIVITY_POST;
+ $datarray['mid'] = $datarray['parent_mid'] = $guid;
+
+ $datarray['changed'] = $datarray['created'] = $datarray['edited'] = datetime_convert('UTC','UTC',$created);
+ $datarray['item_private'] = $private;
+
+ $datarray['plink'] = $plink;
+
+ $datarray['author_xchan'] = $contact['xchan_hash'];
+ $datarray['owner_xchan'] = $contact['xchan_hash'];
+
+ $datarray['body'] = $body;
+
+ $datarray['app'] = $app;
+
+ $datarray['item_flags'] = ITEM_UNSEEN|ITEM_THREAD_TOP;
+
+
+ $result = item_store($datarray);
+ return;
+
+}
+
+function diaspora_reshare($importer,$xml,$msg) {
+
+ logger('diaspora_reshare: init: ' . print_r($xml,true));
+
+ $a = get_app();
+ $guid = notags(unxmlify($xml->guid));
+ $diaspora_handle = notags(unxmlify($xml->diaspora_handle));
+
+
+ if($diaspora_handle != $msg['author']) {
+ logger('diaspora_post: Potential forgery. Message handle is not the same as envelope sender.');
+ return 202;
+ }
+
+ $contact = diaspora_get_contact_by_handle($importer['channel_id'],$diaspora_handle);
+ if(! $contact)
+ return;
+
+ if(! perm_is_allowed($importer['channel_id'],$contact['xchan_hash'],'send_stream')) {
+ logger('diaspora_reshare: Ignoring this author: ' . $diaspora_handle . ' ' . print_r($xml,true));
+ return 202;
+ }
+
+ $search_guid = ((strlen($guid) == 64) ? $guid . '%' : $guid);
+ $r = q("SELECT id FROM item WHERE uid = %d AND mid like '%s' LIMIT 1",
+ intval($importer['channel_id']),
+ dbesc($search_guid)
+ );
+ if($r) {
+ logger('diaspora_reshare: message exists: ' . $guid);
+ return;
+ }
+
+ $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';
+ $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']);
+
+ $source_xml = parse_xml_string($x['body'],false);
+
+ if($source_xml->post->status_message) {
+ $body = diaspora2bb($source_xml->post->status_message->raw_message);
+
+ // Checking for embedded pictures
+ 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));
+ $remote_photo_name = notags(unxmlify($source_xml->post->status_message->photo->remote_photo_name));
+
+ $body = '[img]'.$remote_photo_path.$remote_photo_name.'[/img]'."\n".$body;
+
+ logger('diaspora_reshare: embedded picture link found: '.$body, LOGGER_DEBUG);
+ }
+
+ $body = scale_external_images($body);
+
+ // Add OEmbed and other information to the body
+// $body = add_page_info_to_body($body, false, true);
+ }
+ else {
+ // Maybe it is a reshare of a photo that will be delivered at a later time (testing)
+ logger('diaspora_reshare: no reshare content found: ' . print_r($source_xml,true));
+ $body = "";
+ //return;
+ }
+
+ //if(! $body) {
+ // logger('diaspora_reshare: empty body: source= ' . $x);
+ // return;
+ //}
+
+ $person = find_diaspora_person_by_handle($orig_author);
+
+ if($person) {
+ $orig_author_name = $person['xchan_name'];
+ $orig_author_link = $person['xchan_url'];
+ $orig_author_photo = $person['xchan_photo_m'];
+ }
+
+ $newbody = "[share author='" . urlencode($orig_author_name)
+ . "' profile='" . $orig_author_link
+ . "' avatar='" . $orig_author_photo
+ . "' link='" . $orig_url
+ . "' posted='" . datetime_convert('UTC','UTC',unxmlify($sourcexml->post->status_message->created_at))
+ . "' message_id='" . unxmlify($source_xml->post->status_message->guid)
+ . "]" . $body . "[/share]";
+
+
+ $created = unxmlify($xml->created_at);
+ $private = ((unxmlify($xml->public) == 'false') ? 1 : 0);
+
+ $datarray = array();
+
+ $str_tags = '';
+
+ $tags = get_tags($newbody);
+
+
+ if(count($tags)) {
+
+ $datarray['term'] = array();
+
+ foreach($tags as $tag) {
+ if(strpos($tag,'#') === 0) {
+ if(strpos($tag,'[url='))
+ continue;
+
+ // don't link tags that are already embedded in links
+
+ if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$newbody))
+ continue;
+ if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$newbody))
+ continue;
+
+ $basetag = str_replace('_',' ',substr($tag,1));
+ $newbody = str_replace($tag,'#[url=' . $a->get_baseurl() . '/search?tag=' . rawurlencode($basetag) . ']' . $basetag . '[/url]',$newbody);
+
+ $datarray['term'][] = array(
+ 'uid' => $importer['channel_id'],
+ 'type' => TERM_HASHTAG,
+ 'otype' => TERM_OBJ_POST,
+ 'term' => $basetag,
+ 'url' => z_root() . '/search?tag=' . rawurlencode($basetag)
+ );
+ }
+ }
+ }
+
+ $cnt = preg_match_all('/@\[url=(.*?)\](.*?)\[\/url\]/ism',$newbody,$matches,PREG_SET_ORDER);
+ if($cnt) {
+ foreach($matches as $mtch) {
+ $datarray['term'][] = array(
+ 'uid' => $importer['channel_id'],
+ 'type' => TERM_MENTION,
+ 'otype' => TERM_OBJ_POST,
+ 'term' => $mtch[2],
+ 'url' => $mtch[1]
+ );
+ }
+ }
+
+ // This won't work
+ $plink = 'https://'.substr($diaspora_handle,strpos($diaspora_handle,'@')+1).'/posts/'.$guid;
+
+ $datarray['uid'] = $importer['channel_id'];
+ $datarray['mid'] = $datarray['parent_mid'] = $guid;
+ $datarray['changed'] = $datarray['created'] = $datarray['edited'] = datetime_convert('UTC','UTC',$created);
+ $datarray['item_private'] = $private;
+ $datarray['plink'] = $plink;
+ $datarray['owner_xchan'] = $contact['xchan_hash'];
+ $datarray['author_xchan'] = $contact['xchan_hash'];
+
+ $datarray['body'] = $newbody;
+ $datarray['app'] = 'Diaspora';
+
+
+ $result = item_store($datarray);
+
+ return;
+
+}
+
+
+function diaspora_asphoto($importer,$xml,$msg) {
+ logger('diaspora_asphoto called');
+
+ $a = get_app();
+ $guid = notags(unxmlify($xml->guid));
+ $diaspora_handle = notags(unxmlify($xml->diaspora_handle));
+
+ if($diaspora_handle != $msg['author']) {
+ logger('diaspora_post: Potential forgery. Message handle is not the same as envelope sender.');
+ return 202;
+ }
+
+ $contact = diaspora_get_contact_by_handle($importer['channel_id'],$diaspora_handle);
+ if(! $contact)
+ return;
+
+ if(! perm_is_allowed($importer['channel_id'],$contact['xchan_hash'],'send_stream')) {
+ logger('diaspora_asphoto: Ignoring this author.');
+ return 202;
+ }
+
+ $message_id = $diaspora_handle . ':' . $guid;
+ $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `guid` = '%s' LIMIT 1",
+ intval($importer['channel_id']),
+ dbesc($message_id),
+ dbesc($guid)
+ );
+ if(count($r)) {
+ logger('diaspora_asphoto: message exists: ' . $guid);
+ return;
+ }
+
+ // allocate a guid on our system - we aren't fixing any collisions.
+ // we're ignoring them
+
+ $g = q("select * from guid where guid = '%s' limit 1",
+ dbesc($guid)
+ );
+ if(! count($g)) {
+ q("insert into guid ( guid ) values ( '%s' )",
+ dbesc($guid)
+ );
+ }
+
+ $created = unxmlify($xml->created_at);
+ $private = ((unxmlify($xml->public) == 'false') ? 1 : 0);
+
+ if(strlen($xml->objectId) && ($xml->objectId != 0) && ($xml->image_url)) {
+ $body = '[url=' . notags(unxmlify($xml->image_url)) . '][img]' . notags(unxmlify($xml->objectId)) . '[/img][/url]' . "\n";
+ $body = scale_external_images($body,false);
+ }
+ elseif($xml->image_url) {
+ $body = '[img]' . notags(unxmlify($xml->image_url)) . '[/img]' . "\n";
+ $body = scale_external_images($body);
+ }
+ else {
+ logger('diaspora_asphoto: no photo url found.');
+ return;
+ }
+
+ $plink = 'https://'.substr($diaspora_handle,strpos($diaspora_handle,'@')+1).'/posts/'.$guid;
+
+ $datarray = array();
+
+ $datarray['uid'] = $importer['channel_id'];
+ $datarray['contact-id'] = $contact['id'];
+ $datarray['wall'] = 0;
+ $datarray['network'] = NETWORK_DIASPORA;
+ $datarray['guid'] = $guid;
+ $datarray['uri'] = $datarray['parent-uri'] = $message_id;
+ $datarray['changed'] = $datarray['created'] = $datarray['edited'] = datetime_convert('UTC','UTC',$created);
+ $datarray['private'] = $private;
+ $datarray['parent'] = 0;
+ $datarray['plink'] = $plink;
+ $datarray['owner-name'] = $contact['name'];
+ $datarray['owner-link'] = $contact['url'];
+ //$datarray['owner-avatar'] = $contact['thumb'];
+ $datarray['owner-avatar'] = ((x($contact,'thumb')) ? $contact['thumb'] : $contact['photo']);
+ $datarray['author-name'] = $contact['name'];
+ $datarray['author-link'] = $contact['url'];
+ $datarray['author-avatar'] = $contact['thumb'];
+ $datarray['body'] = $body;
+
+ $datarray['app'] = 'Diaspora/Cubbi.es';
+
+ $message_id = item_store($datarray);
+
+ //if($message_id) {
+ // q("update item set plink = '%s' where id = %d",
+ // dbesc($a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $message_id),
+ // intval($message_id)
+ // );
+ //}
+
+ return;
+
+}
+
+
+
+
+
+
+function diaspora_comment($importer,$xml,$msg) {
+
+ $a = get_app();
+ $guid = notags(unxmlify($xml->guid));
+ $parent_guid = notags(unxmlify($xml->parent_guid));
+ $diaspora_handle = notags(unxmlify($xml->diaspora_handle));
+ $target_type = notags(unxmlify($xml->target_type));
+ $text = unxmlify($xml->text);
+ $author_signature = notags(unxmlify($xml->author_signature));
+
+ $parent_author_signature = (($xml->parent_author_signature) ? notags(unxmlify($xml->parent_author_signature)) : '');
+
+ $contact = diaspora_get_contact_by_handle($importer['channel_id'],$msg['author']);
+ if(! $contact) {
+ logger('diaspora_comment: cannot find contact: ' . $msg['author']);
+ return;
+ }
+
+ if(! perm_is_allowed($importer['channel_id'],$contact['xchan_hash'],'post_comments')) {
+ logger('diaspora_comment: Ignoring this author.');
+ return 202;
+ }
+
+ // Friendica is currently truncating guids at 64 chars
+
+ $search_guid = $guid;
+ if(strlen($guid) == 64)
+ $search_guid = $guid . '%';
+
+ $r = q("SELECT * FROM item WHERE uid = %d AND mid like '%s' LIMIT 1",
+ intval($importer['channel_id']),
+ dbesc($search_guid)
+ );
+ if($r) {
+ logger('diaspora_comment: our comment just got relayed back to us (or there was a guid collision) : ' . $guid);
+ return;
+ }
+
+ $search_guid = $parent_guid;
+ if(strlen($parent_guid) == 64)
+ $search_guid = $parent_guid . '%';
+
+
+ $r = q("SELECT * FROM item WHERE uid = %d AND mid LIKE '%s' LIMIT 1",
+ intval($importer['channel_id']),
+ dbesc($search_guid)
+ );
+ if(! $r) {
+ logger('diaspora_comment: parent item not found: parent: ' . $parent_guid . ' item: ' . $guid);
+ return;
+ }
+ $parent_item = $r[0];
+
+
+ /* How Diaspora performs comment signature checking:
+
+ - If an item has been sent by the comment author to the top-level post owner to relay on
+ to the rest of the contacts on the top-level post, the top-level post owner should check
+ the author_signature, then create a parent_author_signature before relaying the comment on
+ - If an item has been relayed on by the top-level post owner, the contacts who receive it
+ check only the parent_author_signature. Basically, they trust that the top-level post
+ owner has already verified the authenticity of anything he/she sends out
+ - In either case, the signature that get checked is the signature created by the person
+ who sent the psuedo-salmon
+ */
+
+ $signed_data = $guid . ';' . $parent_guid . ';' . $text . ';' . $diaspora_handle;
+ $key = $msg['key'];
+
+ if($parent_author_signature) {
+ // If a parent_author_signature exists, then we've received the comment
+ // relayed from the top-level post owner. There's no need to check the
+ // author_signature if the parent_author_signature is valid
+
+ $parent_author_signature = base64_decode($parent_author_signature);
+
+ if(! rsa_verify($signed_data,$parent_author_signature,$key,'sha256')) {
+ logger('diaspora_comment: top-level owner verification failed.');
+ return;
+ }
+ }
+ else {
+ // If there's no parent_author_signature, then we've received the comment
+ // from the comment creator. In that case, the person is commenting on
+ // our post, so he/she must be a contact of ours and his/her public key
+ // should be in $msg['key']
+
+ $author_signature = base64_decode($author_signature);
+
+ if(! rsa_verify($signed_data,$author_signature,$key,'sha256')) {
+ logger('diaspora_comment: comment author verification failed.');
+ return;
+ }
+ }
+
+ // Phew! Everything checks out. Now create an item.
+
+ // Find the original comment author information.
+ // We need this to make sure we display the comment author
+ // information (name and avatar) correctly.
+
+ if(strcasecmp($diaspora_handle,$msg['author']) == 0)
+ $person = $contact;
+ else {
+ $person = find_diaspora_person_by_handle($diaspora_handle);
+
+ if(! is_array($person)) {
+ logger('diaspora_comment: unable to find author details');
+ return;
+ }
+ }
+
+
+ $body = diaspora2bb($text);
+
+ $datarray = array();
+
+ $tags = get_tags($body);
+
+ if(count($tags)) {
+
+ $datarray['term'] = array();
+
+ foreach($tags as $tag) {
+ if(strpos($tag,'#') === 0) {
+ if(strpos($tag,'[url='))
+ continue;
+
+ // don't link tags that are already embedded in links
+
+ if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$body))
+ continue;
+ if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$body))
+ continue;
+
+ $basetag = str_replace('_',' ',substr($tag,1));
+ $body = str_replace($tag,'#[url=' . $a->get_baseurl() . '/search?tag=' . rawurlencode($basetag) . ']' . $basetag . '[/url]',$body);
+
+ $datarray['term'][] = array(
+ 'uid' => $importer['channel_id'],
+ 'type' => TERM_HASHTAG,
+ 'otype' => TERM_OBJ_POST,
+ 'term' => $basetag,
+ 'url' => z_root() . '/search?tag=' . rawurlencode($basetag)
+ );
+ }
+ }
+ }
+
+ $cnt = preg_match_all('/@\[url=(.*?)\](.*?)\[\/url\]/ism',$body,$matches,PREG_SET_ORDER);
+ if($cnt) {
+ foreach($matches as $mtch) {
+ $datarray['term'][] = array(
+ 'uid' => $importer['channel_id'],
+ 'type' => TERM_MENTION,
+ 'otype' => TERM_OBJ_POST,
+ 'term' => $mtch[2],
+ 'url' => $mtch[1]
+ );
+ }
+ }
+
+ $datarray['uid'] = $importer['channel_id'];
+ $datarray['verb'] = ACTIVITY_POST;
+ $datarray['mid'] = $guid;
+ $datarray['parent_mid'] = $parent_item['mid'];
+
+
+ // No timestamps for comments? OK, we'll the use current time.
+ $datarray['changed'] = $datarray['created'] = $datarray['edited'] = datetime_convert();
+ $datarray['item_private'] = $parent_item['item_private'];
+
+ $datarray['owner_xchan'] = $parent_item['owner_xchan'];
+ $datarray['author_xchan'] = $person['xchan_hash'];
+
+ $datarray['body'] = $body;
+
+ $datarray['app'] = 'Diaspora';
+
+ if(! $parent_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);
+
+ if($result && $result['success'])
+ $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.
+
+ proc_run('php','include/notifier.php','comment-import',$message_id);
+ }
+
+ if($result['item_id']) {
+ $r = q("select * from item where id = %d limit 1",
+ intval($result['item_id'])
+ );
+ if($r)
+ send_status_notifications($result['item_id'],$r[0]);
+ }
+
+ return;
+}
+
+
+
+
+function diaspora_conversation($importer,$xml,$msg) {
+
+ $a = get_app();
+
+ $guid = notags(unxmlify($xml->guid));
+ $subject = notags(unxmlify($xml->subject));
+ $diaspora_handle = notags(unxmlify($xml->diaspora_handle));
+ $participant_handles = notags(unxmlify($xml->participant_handles));
+ $created_at = datetime_convert('UTC','UTC',notags(unxmlify($xml->created_at)));
+
+ $parent_uri = $diaspora_handle . ':' . $guid;
+
+ $messages = $xml->message;
+
+ if(! count($messages)) {
+ logger('diaspora_conversation: empty conversation');
+ return;
+ }
+
+ $contact = diaspora_get_contact_by_handle($importer['channel_id'],$msg['author']);
+ if(! $contact) {
+ logger('diaspora_conversation: cannot find contact: ' . $msg['author']);
+ return;
+ }
+
+ if(($contact['rel'] == CONTACT_IS_FOLLOWER) || ($contact['blocked']) || ($contact['readonly'])) {
+ logger('diaspora_conversation: Ignoring this author.');
+ return 202;
+ }
+
+ $conversation = null;
+
+ $c = q("select * from conv where uid = %d and guid = '%s' limit 1",
+ intval($importer['channel_id']),
+ dbesc($guid)
+ );
+ if(count($c))
+ $conversation = $c[0];
+ else {
+ $r = q("insert into conv (uid,guid,creator,created,updated,subject,recips) values(%d, '%s', '%s', '%s', '%s', '%s', '%s') ",
+ intval($importer['channel_id']),
+ dbesc($guid),
+ dbesc($diaspora_handle),
+ dbesc(datetime_convert('UTC','UTC',$created_at)),
+ dbesc(datetime_convert()),
+ dbesc($subject),
+ dbesc($participant_handles)
+ );
+ if($r)
+ $c = q("select * from conv where uid = %d and guid = '%s' limit 1",
+ intval($importer['channel_id']),
+ dbesc($guid)
+ );
+ if(count($c))
+ $conversation = $c[0];
+ }
+ if(! $conversation) {
+ logger('diaspora_conversation: unable to create conversation.');
+ return;
+ }
+
+ foreach($messages as $mesg) {
+
+ $reply = 0;
+
+ $msg_guid = notags(unxmlify($mesg->guid));
+ $msg_parent_guid = notags(unxmlify($mesg->parent_guid));
+ $msg_parent_author_signature = notags(unxmlify($mesg->parent_author_signature));
+ $msg_author_signature = notags(unxmlify($mesg->author_signature));
+ $msg_text = unxmlify($mesg->text);
+ $msg_created_at = datetime_convert('UTC','UTC',notags(unxmlify($mesg->created_at)));
+ $msg_diaspora_handle = notags(unxmlify($mesg->diaspora_handle));
+ $msg_conversation_guid = notags(unxmlify($mesg->conversation_guid));
+ if($msg_conversation_guid != $guid) {
+ logger('diaspora_conversation: message conversation guid does not belong to the current conversation. ' . $xml);
+ continue;
+ }
+
+ $body = diaspora2bb($msg_text);
+
+ $author_signed_data = $msg_guid . ';' . $msg_parent_guid . ';' . $msg_text . ';' . unxmlify($mesg->created_at) . ';' . $msg_diaspora_handle . ';' . $msg_conversation_guid;
+
+ $author_signature = base64_decode($msg_author_signature);
+
+ if(strcasecmp($msg_diaspora_handle,$msg['author']) == 0) {
+ $person = $contact;
+ $key = $msg['key'];
+ }
+ else {
+ $person = find_diaspora_person_by_handle($msg_diaspora_handle);
+
+ if(is_array($person) && x($person,'xchan_pubkey'))
+ $key = $person['xchan_pubkey'];
+ else {
+ logger('diaspora_conversation: unable to find author details');
+ continue;
+ }
+ }
+
+ if(! rsa_verify($author_signed_data,$author_signature,$key,'sha256')) {
+ logger('diaspora_conversation: verification failed.');
+ continue;
+ }
+
+ if($msg_parent_author_signature) {
+ $owner_signed_data = $msg_guid . ';' . $msg_parent_guid . ';' . $msg_text . ';' . unxmlify($mesg->created_at) . ';' . $msg_diaspora_handle . ';' . $msg_conversation_guid;
+
+ $parent_author_signature = base64_decode($msg_parent_author_signature);
+
+ $key = $msg['key'];
+
+ if(! rsa_verify($owner_signed_data,$parent_author_signature,$key,'sha256')) {
+ logger('diaspora_conversation: owner verification failed.');
+ continue;
+ }
+ }
+
+ $r = q("select id from mail where mid = '%s' limit 1",
+ dbesc($message_id)
+ );
+ if(count($r)) {
+ logger('diaspora_conversation: duplicate message already delivered.', LOGGER_DEBUG);
+ 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')",
+ intval($importer['channel_id']),
+ intval($conversation['id']),
+ dbesc($person['xchan_hash']),
+ dbesc($importer['channel_hash']),
+ dbesc($subject),
+ dbesc($body),
+ 0,
+ dbesc($msg_guid),
+ dbesc($parent_uri),
+ dbesc($msg_created_at)
+ );
+
+ q("update conv set updated = '%s' where id = %d",
+ dbesc(datetime_convert()),
+ intval($conversation['id'])
+ );
+
+ require_once('include/enotify.php');
+/******
+//FIXME
+
+ notification(array(
+ 'type' => NOTIFY_MAIL,
+ 'notify_flags' => $importer['notify-flags'],
+ 'language' => $importer['language'],
+ 'to_name' => $importer['username'],
+ 'to_email' => $importer['email'],
+ 'uid' =>$importer['importer_uid'],
+ 'item' => array('subject' => $subject, 'body' => $body),
+ 'source_name' => $person['name'],
+ 'source_link' => $person['url'],
+ 'source_photo' => $person['thumb'],
+ 'verb' => ACTIVITY_POST,
+ 'otype' => 'mail'
+ ));
+*******/
+
+ }
+
+ return;
+}
+
+function diaspora_message($importer,$xml,$msg) {
+
+ $a = get_app();
+
+ $msg_guid = notags(unxmlify($xml->guid));
+ $msg_parent_guid = notags(unxmlify($xml->parent_guid));
+ $msg_parent_author_signature = notags(unxmlify($xml->parent_author_signature));
+ $msg_author_signature = notags(unxmlify($xml->author_signature));
+ $msg_text = unxmlify($xml->text);
+ $msg_created_at = datetime_convert('UTC','UTC',notags(unxmlify($xml->created_at)));
+ $msg_diaspora_handle = notags(unxmlify($xml->diaspora_handle));
+ $msg_conversation_guid = notags(unxmlify($xml->conversation_guid));
+
+ $parent_uri = $diaspora_handle . ':' . $msg_parent_guid;
+
+ $contact = diaspora_get_contact_by_handle($importer['channel_id'],$msg_diaspora_handle);
+ if(! $contact) {
+ logger('diaspora_message: cannot find contact: ' . $msg_diaspora_handle);
+ return;
+ }
+
+ if(($contact['rel'] == CONTACT_IS_FOLLOWER) || ($contact['blocked']) || ($contact['readonly'])) {
+ logger('diaspora_message: Ignoring this author.');
+ return 202;
+ }
+
+ $conversation = null;
+
+ $c = q("select * from conv where uid = %d and guid = '%s' limit 1",
+ intval($importer['channel_id']),
+ dbesc($msg_conversation_guid)
+ );
+ if(count($c))
+ $conversation = $c[0];
+ else {
+ logger('diaspora_message: conversation not available.');
+ return;
+ }
+
+ $reply = 0;
+
+ $body = diaspora2bb($msg_text);
+ $message_id = $msg_diaspora_handle . ':' . $msg_guid;
+
+ $author_signed_data = $msg_guid . ';' . $msg_parent_guid . ';' . $msg_text . ';' . unxmlify($xml->created_at) . ';' . $msg_diaspora_handle . ';' . $msg_conversation_guid;
+
+
+ $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'];
+ else {
+ logger('diaspora_message: unable to find author details');
+ return;
+ }
+
+ if(! rsa_verify($author_signed_data,$author_signature,$key,'sha256')) {
+ logger('diaspora_message: verification failed.');
+ return;
+ }
+
+ $r = q("select id from mail where `uri` = '%s' and uid = %d limit 1",
+ dbesc($message_id),
+ intval($importer['channel_id'])
+ );
+ if(count($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')",
+ 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($body),
+ 0,
+ 1,
+ dbesc($message_id),
+ dbesc($parent_uri),
+ dbesc($msg_created_at)
+ );
+
+ q("update conv set updated = '%s' where id = %d",
+ dbesc(datetime_convert()),
+ intval($conversation['id'])
+ );
+
+ return;
+}
+
+
+function diaspora_photo($importer,$xml,$msg,$attempt=1) {
+
+ $a = get_app();
+
+ logger('diaspora_photo: init',LOGGER_DEBUG);
+
+ $remote_photo_path = notags(unxmlify($xml->remote_photo_path));
+
+ $remote_photo_name = notags(unxmlify($xml->remote_photo_name));
+
+ $status_message_guid = notags(unxmlify($xml->status_message_guid));
+
+ $guid = notags(unxmlify($xml->guid));
+
+ $diaspora_handle = notags(unxmlify($xml->diaspora_handle));
+
+ $public = notags(unxmlify($xml->public));
+
+ $created_at = notags(unxmlify($xml_created_at));
+
+ logger('diaspora_photo: status_message_guid: ' . $status_message_guid, LOGGER_DEBUG);
+
+ $contact = diaspora_get_contact_by_handle($importer['channel_id'],$msg['author']);
+ if(! $contact) {
+ logger('diaspora_photo: contact record not found: ' . $msg['author'] . ' handle: ' . $diaspora_handle);
+ return;
+ }
+
+ if(! perm_is_allowed($importer['channel_id'],$contact['xchan_hash'],'send_stream')) {
+ logger('diaspora_photo: Ignoring this author.');
+ return 202;
+ }
+
+ $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `mid` = '%s' LIMIT 1",
+ intval($importer['channel_id']),
+ dbesc($status_message_guid)
+ );
+ if(! $r) {
+ logger('diaspora_photo: attempt = ' . $attempt . '; status message not found: ' . $status_message_guid . ' for photo: ' . $guid);
+ return;
+ }
+
+// $parent_item = $r[0];
+
+// $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));
+
+// 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;
+}
+
+
+
+
+function diaspora_like($importer,$xml,$msg) {
+
+ $a = get_app();
+ $guid = notags(unxmlify($xml->guid));
+ $parent_guid = notags(unxmlify($xml->parent_guid));
+ $diaspora_handle = notags(unxmlify($xml->diaspora_handle));
+ $target_type = notags(unxmlify($xml->target_type));
+ $positive = notags(unxmlify($xml->positive));
+ $author_signature = notags(unxmlify($xml->author_signature));
+
+ $parent_author_signature = (($xml->parent_author_signature) ? notags(unxmlify($xml->parent_author_signature)) : '');
+
+ // likes on comments not supported here and likes on photos not supported by Diaspora
+
+// if($target_type !== 'Post')
+// return;
+
+ $contact = diaspora_get_contact_by_handle($importer['channel_id'],$msg['author']);
+ if(! $contact) {
+ logger('diaspora_like: cannot find contact: ' . $msg['author']);
+ return;
+ }
+
+
+ if(! perm_is_allowed($importer['channel_id'],$contact['xchan_hash'],'post_comments')) {
+ logger('diaspora_like: Ignoring this author.');
+ return 202;
+ }
+
+ $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `mid` = '%s' LIMIT 1",
+ intval($importer['channel_id']),
+ dbesc($parent_guid)
+ );
+ if(! count($r)) {
+ logger('diaspora_like: parent item not found: ' . $guid);
+ return;
+ }
+
+ $parent_item = $r[0];
+
+ $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `mid` = '%s' LIMIT 1",
+ intval($importer['channel_id']),
+ dbesc($guid)
+ );
+ if(count($r)) {
+ if($positive === 'true') {
+ logger('diaspora_like: duplicate like: ' . $guid);
+ return;
+ }
+ // 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') {
+ logger('diaspora_like: received a like with positive set to "false"...ignoring');
+ // 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') {
+ logger('diaspora_like: received a like with positive set to "false"');
+ logger('diaspora_like: unlike received with no corresponding like...ignoring');
+ return;
+ }
+
+
+ /* How Diaspora performs "like" signature checking:
+
+ - If an item has been sent by the like author to the top-level post owner to relay on
+ to the rest of the contacts on the top-level post, the top-level post owner should check
+ the author_signature, then create a parent_author_signature before relaying the like on
+ - If an item has been relayed on by the top-level post owner, the contacts who receive it
+ check only the parent_author_signature. Basically, they trust that the top-level post
+ owner has already verified the authenticity of anything he/she sends out
+ - In either case, the signature that get checked is the signature created by the person
+ who sent the salmon
+ */
+
+ // 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) {
+ // If a parent_author_signature exists, then we've received the like
+ // relayed from the top-level post owner. There's no need to check the
+ // author_signature if the parent_author_signature is valid
+
+ $parent_author_signature = base64_decode($parent_author_signature);
+
+ if(! rsa_verify($signed_data,$parent_author_signature,$key,'sha256')) {
+ if (intval(get_config('system','ignore_diaspora_like_signature')))
+ logger('diaspora_like: top-level owner verification failed. Proceeding anyway.');
+ else {
+ logger('diaspora_like: top-level owner verification failed.');
+ return;
+ }
+ }
+ }
+ else {
+ // If there's no parent_author_signature, then we've received the like
+ // from the like creator. In that case, the person is "like"ing
+ // our post, so he/she must be a contact of ours and his/her public key
+ // should be in $msg['key']
+
+ $author_signature = base64_decode($author_signature);
+
+ if(! rsa_verify($signed_data,$author_signature,$key,'sha256')) {
+ if (intval(get_config('system','ignore_diaspora_like_signature')))
+ logger('diaspora_like: like creator verification failed. Proceeding anyway');
+ else {
+ logger('diaspora_like: like creator verification failed.');
+ return;
+ }
+ }
+ }
+
+ logger('diaspora_like: signature check complete.',LOGGER_DEBUG);
+
+ // Phew! Everything checks out. Now create an item.
+
+ // Find the original comment author information.
+ // We need this to make sure we display the comment author
+ // information (name and avatar) correctly.
+ if(strcasecmp($diaspora_handle,$msg['author']) == 0)
+ $person = $contact;
+ else {
+ $person = find_diaspora_person_by_handle($diaspora_handle);
+
+ if(! is_array($person)) {
+ logger('diaspora_like: unable to find author details');
+ return;
+ }
+ }
+
+ $uri = $diaspora_handle . ':' . $guid;
+
+ $activity = ACTIVITY_LIKE;
+
+ $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'];
+
+
+ $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['uid'] = $importer['channel_id'];
+ $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='. z_root() .'/display/'.$guid.']'.$post_type.'[/url]';
+ $arr['body'] = sprintf( $bodyverb, $ulink, $alink, $plink );
+
+ $arr['app'] = 'Diaspora';
+
+ $arr['item_private'] = $parent_item['item_private'];
+ $arr['verb'] = $activity;
+ $arr['obj_type'] = $objtype;
+ $arr['object'] = $object;
+
+ if(! $parent_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);
+
+ 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['item_flags'] & ITEM_ORIGIN) && (! $parent_author_signature))
+ proc_run('php','include/notifier.php','comment-import',$message_id);
+
+ return;
+}
+
+function diaspora_retraction($importer,$xml) {
+
+
+ $guid = notags(unxmlify($xml->guid));
+ $diaspora_handle = notags(unxmlify($xml->diaspora_handle));
+ $type = notags(unxmlify($xml->type));
+
+ $contact = diaspora_get_contact_by_handle($importer['channel_id'],$diaspora_handle);
+ if(! $contact)
+ return;
+
+ if($type === 'Person') {
+ require_once('include/Contact.php');
+ contact_remove($contact['id']);
+ }
+ elseif($type === 'Post') {
+ $r = q("select * from item where guid = '%s' and uid = %d and not file like '%%[%%' 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'])
+ );
+ }
+ }
+ }
+
+ return 202;
+ // NOTREACHED
+}
+
+function diaspora_signed_retraction($importer,$xml,$msg) {
+
+
+ $guid = notags(unxmlify($xml->target_guid));
+ $diaspora_handle = notags(unxmlify($xml->sender_handle));
+ $type = notags(unxmlify($xml->target_type));
+ $sig = notags(unxmlify($xml->target_author_signature));
+
+ $parent_author_signature = (($xml->parent_author_signature) ? notags(unxmlify($xml->parent_author_signature)) : '');
+
+ $contact = diaspora_get_contact_by_handle($importer['channel_id'],$diaspora_handle);
+ if(! $contact) {
+ logger('diaspora_signed_retraction: no contact ' . $diaspora_handle . ' for ' . $importer['channel_id']);
+ return;
+ }
+
+
+ $signed_data = $guid . ';' . $type ;
+ $key = $msg['key'];
+
+ /* How Diaspora performs relayable_retraction signature checking:
+
+ - If an item has been sent by the item author to the top-level post owner to relay on
+ to the rest of the contacts on the top-level post, the top-level post owner checks
+ the author_signature, then creates a parent_author_signature before relaying the item on
+ - If an item has been relayed on by the top-level post owner, the contacts who receive it
+ check only the parent_author_signature. Basically, they trust that the top-level post
+ owner has already verified the authenticity of anything he/she sends out
+ - In either case, the signature that get checked is the signature created by the person
+ who sent the salmon
+ */
+
+ if($parent_author_signature) {
+
+ $parent_author_signature = base64_decode($parent_author_signature);
+
+ if(! rsa_verify($signed_data,$parent_author_signature,$key,'sha256')) {
+ logger('diaspora_signed_retraction: top-level post owner verification failed');
+ return;
+ }
+
+ }
+ else {
+
+ $sig_decode = base64_decode($sig);
+
+ if(! rsa_verify($signed_data,$sig_decode,$key,'sha256')) {
+ logger('diaspora_signed_retraction: retraction owner verification failed.' . print_r($msg,true));
+ return;
+ }
+ }
+
+ if($type === 'StatusMessage' || $type === 'Comment' || $type === 'Like') {
+ $r = q("select * from item where guid = '%s' and uid = %d and not file like '%%[%%' 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'])
+ );
+
+ // 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",
+ $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)
+ );
+
+ // the existence of parent_author_signature would have meant the parent_author or owner
+ // is already relaying.
+ logger('diaspora_signed_retraction: relaying relayable_retraction');
+
+ proc_run('php','include/notifier.php','drop',$r[0]['id']);
+ }
+ }
+ }
+ }
+ }
+ else
+ logger('diaspora_signed_retraction: unknown type: ' . $type);
+
+ return 202;
+ // NOTREACHED
+}
+
+function diaspora_profile($importer,$xml,$msg) {
+
+ $a = get_app();
+ $diaspora_handle = notags(unxmlify($xml->diaspora_handle));
+
+
+ if($diaspora_handle != $msg['author']) {
+ logger('diaspora_post: Potential forgery. Message handle is not the same as envelope sender.');
+ return 202;
+ }
+
+ $contact = diaspora_get_contact_by_handle($importer['channel_id'],$diaspora_handle);
+ if(! $contact)
+ return;
+
+ if($contact['blocked']) {
+ logger('diaspora_post: Ignoring this author.');
+ return 202;
+ }
+
+ $name = unxmlify($xml->first_name) . ((strlen($xml->last_name)) ? ' ' . unxmlify($xml->last_name) : '');
+ $image_url = unxmlify($xml->image_url);
+ $birthday = unxmlify($xml->birthday);
+
+
+ $handle_parts = explode("@", $diaspora_handle);
+ if($name === '') {
+ $name = $handle_parts[0];
+ }
+
+
+ if( preg_match("|^https?://|", $image_url) === 0) {
+ $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');
+
+ $images = import_profile_photo($image_url,$importer['channel_id'],$contact['id']);
+
+ // Generic birthday. We don't know the timezone. The year is irrelevant.
+
+ $birthday = str_replace('1000','1901',$birthday);
+
+ $birthday = datetime_convert('UTC','UTC',$birthday,'Y-m-d');
+
+ // this is to prevent multiple birthday notifications in a single year
+ // if we already have a stored birthday and the 'm-d' part hasn't changed, preserve the entry, which will preserve the notify year
+
+ if(substr($birthday,5) === substr($contact['bd'],5))
+ $birthday = $contact['bd'];
+
+ // 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",
+ dbesc($name),
+ dbesc(datetime_convert()),
+ dbesc($images[0]),
+ dbesc($images[1]),
+ dbesc($images[2]),
+ dbesc(datetime_convert()),
+ dbesc($birthday),
+ intval($contact['id']),
+ intval($importer['channel_id'])
+ );
+
+/* if($r) {
+ if($oldphotos) {
+ foreach($oldphotos as $ph) {
+ q("DELETE FROM `photo` WHERE `uid` = %d AND `contact-id` = %d AND `album` = 'Contact Photos' AND `resource-id` = '%s' ",
+ intval($importer['channel_id']),
+ intval($contact['id']),
+ dbesc($ph['resource-id'])
+ );
+ }
+ }
+ } */
+
+ return;
+
+}
+
+function diaspora_share($owner,$contact) {
+ $a = get_app();
+ $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",
+ dbesc($contact['hubloc_hash'])
+ );
+ if(! $c) {
+ logger('diaspora_share: ' . $contact['hubloc_hash'] . ' not found.');
+ return;
+ }
+ $contact = $c[0];
+ }
+
+ $theiraddr = $contact['xchan_addr'];
+
+ $tpl = get_markup_template('diaspora_share.tpl');
+ $msg = replace_macros($tpl, array(
+ '$sender' => $myaddr,
+ '$recipient' => $theiraddr
+ ));
+
+ $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($owner,$contact) {
+
+ $a = get_app();
+ $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' => $owner['channel_guid'],
+ '$type' => 'Person',
+ '$handle' => $myaddr
+ ));
+
+ $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_send_status($item,$owner,$contact,$public_batch = false) {
+
+ $a = get_app();
+ $myaddr = $owner['channel_address'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
+
+ $images = array();
+
+ $title = $item['title'];
+ $body = bb2diaspora_itembody($item);
+
+/*
+ // We're trying to match Diaspora's split message/photo protocol but
+ // all the photos are displayed on D* as links and not img's - even
+ // though we're sending pretty much precisely what they send us when
+ // doing the same operation.
+ // Commented out for now, we'll use bb2diaspora to convert photos to markdown
+ // which seems to get through intact.
+
+ $cnt = preg_match_all('|\[img\](.*?)\[\/img\]|',$body,$matches,PREG_SET_ORDER);
+ if($cnt) {
+ foreach($matches as $mtch) {
+ $detail = array();
+ $detail['str'] = $mtch[0];
+ $detail['path'] = dirname($mtch[1]) . '/';
+ $detail['file'] = basename($mtch[1]);
+ $detail['guid'] = $item['guid'];
+ $detail['handle'] = $myaddr;
+ $images[] = $detail;
+ $body = str_replace($detail['str'],$mtch[1],$body);
+ }
+ }
+*/
+
+
+ $public = (($item['item_private']) ? 'false' : 'true');
+
+ require_once('include/datetime.php');
+ $created = datetime_convert('UTC','UTC',$item['created'],'Y-m-d H:i:s \U\T\C');
+
+ // Detect a share element and do a reshare
+ // see: https://github.com/Raven24/diaspora-federation/blob/master/lib/diaspora-federation/entities/reshare.rb
+ if (!$item['item_private'] AND ($ret = diaspora_is_reshare($item["body"]))) {
+ $tpl = get_markup_template('diaspora_reshare.tpl');
+ $msg = replace_macros($tpl, array(
+ '$root_handle' => xmlify($ret['root_handle']),
+ '$root_guid' => $ret['root_guid'],
+ '$guid' => $item['mid'],
+ '$handle' => xmlify($myaddr),
+ '$public' => $public,
+ '$created' => $created,
+ '$provider' => (($item['app']) ? $item['app'] : 'redmatrix')
+ ));
+ } else {
+ $tpl = get_markup_template('diaspora_post.tpl');
+ $msg = replace_macros($tpl, array(
+ '$body' => xmlify($body),
+ '$guid' => $item['mid'],
+ '$handle' => xmlify($myaddr),
+ '$public' => $public,
+ '$created' => $created,
+ '$provider' => (($item['app']) ? $item['app'] : 'redmatrix')
+ ));
+ }
+
+ logger('diaspora_send_status: '.$owner['channel_name'].' -> '.$contact['xchan_name'].' base message: ' . $msg, LOGGER_DATA);
+
+ $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['channel_prvkey'],$contact['xchan_pubkey'],$public_batch)));
+
+ $return_code = diaspora_transmit($owner,$contact,$slap,$public_batch);
+
+// logger('diaspora_send_status: guid: '.$item['mid'].' result '.$return_code, LOGGER_DEBUG);
+
+ if(count($images)) {
+ diaspora_send_images($item,$owner,$contact,$images,$public_batch);
+ }
+
+ return $return_code;
+}
+
+function diaspora_is_reshare($body) {
+
+ $body = trim($body);
+
+ // Skip if it isn't a pure repeated messages
+ // Does it start with a share?
+ if(strpos($body, "[share") > 0)
+ return(false);
+
+ // Does it end with a share?
+ if(strlen($body) > (strrpos($body, "[/share]") + 8))
+ return(false);
+
+ $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
+ // Skip if there is no shared message in there
+ if ($body == $attributes)
+ return(false);
+
+ $profile = "";
+ preg_match("/profile='(.*?)'/ism", $attributes, $matches);
+ if ($matches[1] != "")
+ $profile = $matches[1];
+
+ preg_match('/profile="(.*?)"/ism', $attributes, $matches);
+ if ($matches[1] != "")
+ $profile = $matches[1];
+
+ $ret= array();
+
+ $ret["root_handle"] = preg_replace("=https?://(.*)/u/(.*)=ism", "$2@$1", $profile);
+ if (($ret["root_handle"] == $profile) OR ($ret["root_handle"] == ""))
+ return(false);
+
+ $link = "";
+ preg_match("/link='(.*?)'/ism", $attributes, $matches);
+ if ($matches[1] != "")
+ $link = $matches[1];
+
+ preg_match('/link="(.*?)"/ism', $attributes, $matches);
+ if ($matches[1] != "")
+ $link = $matches[1];
+
+ $ret["root_guid"] = preg_replace("=https?://(.*)/posts/(.*)=ism", "$2", $link);
+ if (($ret["root_guid"] == $link) OR ($ret["root_guid"] == ""))
+ return(false);
+
+ return($ret);
+}
+
+function diaspora_send_images($item,$owner,$contact,$images,$public_batch = false) {
+ $a = get_app();
+ if(! count($images))
+ return;
+ $mysite = substr($a->get_baseurl(),strpos($a->get_baseurl(),'://') + 3) . '/photo';
+
+ $tpl = get_markup_template('diaspora_photo.tpl');
+ foreach($images as $image) {
+ if(! stristr($image['path'],$mysite))
+ continue;
+ $resource = str_replace('.jpg','',$image['file']);
+ $resource = substr($resource,0,strpos($resource,'-'));
+
+ $r = q("select * from photo where `resource_id` = '%s' and `uid` = %d limit 1",
+ dbesc($resource),
+ intval($owner['uid'])
+ );
+ if(! $r)
+ continue;
+ $public = (($r[0]['allow_cid'] || $r[0]['allow_gid'] || $r[0]['deny_cid'] || $r[0]['deny_gid']) ? 'false' : 'true' );
+ $msg = replace_macros($tpl,array(
+ '$path' => xmlify($image['path']),
+ '$filename' => xmlify($image['file']),
+ '$msg_guid' => xmlify($image['guid']),
+ '$guid' => xmlify($r[0]['resource_id']),
+ '$handle' => xmlify($image['handle']),
+ '$public' => xmlify($public),
+ '$created_at' => xmlify(datetime_convert('UTC','UTC',$r[0]['created'],'Y-m-d H:i:s \U\T\C'))
+ ));
+
+
+ logger('diaspora_send_photo: base message: ' . $msg, LOGGER_DATA);
+ $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['channel_prvkey'],$contact['xchan_pubkey'],$public_batch)));
+
+ diaspora_transmit($owner,$contact,$slap,$public_batch);
+ }
+
+}
+
+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);
+ $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']) {
+ $p = q("select mid, parent_mid from item where mid = '%s' limit 1",
+ dbesc($item['thr_parent'])
+ );
+ }
+ else {
+ // 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 * from item where parent = %d and id = %d limit 1",
+ intval($item['parent']),
+ intval($item['parent'])
+ );
+ }
+ if($p)
+ $parent = $p[0];
+ else
+ return;
+
+ if($item['verb'] === ACTIVITY_LIKE) {
+ $tpl = get_markup_template('diaspora_like.tpl');
+ $like = true;
+ $target_type = ( $parent['mid'] === $parent['parent_mid'] ? 'Post' : 'Comment');
+ $positive = 'true';
+
+ if(($item_['item_restrict'] & ITEM_DELETED))
+ logger('diaspora_send_followup: received deleted "like". Those should go to diaspora_send_retraction');
+ }
+ else {
+ $tpl = get_markup_template('diaspora_comment.tpl');
+ $like = false;
+ }
+
+ if($item['diaspora_meta'] && ! $like) {
+ $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);
+
+ // sign it
+
+ if($like)
+ $signed_text = $item['mid'] . ';' . $target_type . ';' . $parent['mid'] . ';' . $positive . ';' . $myaddr;
+ else
+ $signed_text = $item['mid'] . ';' . $parent['mid'] . ';' . $text . ';' . $myaddr;
+
+ $authorsig = base64_encode(rsa_sign($signed_text,$owner['channel_prvkey'],'sha256'));
+
+ }
+
+ $msg = replace_macros($tpl,array(
+ '$guid' => xmlify($item['mid']),
+ '$parent_guid' => xmlify($parent['mid']),
+ '$target_type' =>xmlify($target_type),
+ '$authorsig' => xmlify($authorsig),
+ '$body' => xmlify($text),
+ '$positive' => xmlify($positive),
+ '$handle' => xmlify($myaddr)
+ ));
+
+ logger('diaspora_followup: base message: ' . $msg, LOGGER_DATA);
+
+ $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['channel_prvkey'],$contact['xchan_pubkey'],$public_batch)));
+
+
+ return(diaspora_transmit($owner,$contact,$slap,$public_batch));
+}
+
+
+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);
+
+ $text = bb2diaspora_itembody($item);
+
+ $body = $text;
+
+ // 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']) {
+ $p = q("select * from item where mid = '%s' limit 1",
+ dbesc($item['thr_parent'])
+ );
+ }
+ else {
+ // 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 * from item where parent = %d and id = %d limit 1",
+ intval($item['parent']),
+ intval($item['parent'])
+ );
+ }
+
+ if($p)
+ $parent = $p[0];
+ else {
+ logger('diaspora_send_relay: no parent');
+ return;
+ }
+
+ $like = false;
+ $relay_retract = false;
+ $sql_sign_id = 'iid';
+
+ if( $item['item_restrict'] & ITEM_DELETED) {
+ $relay_retract = true;
+
+ $target_type = ( ($item['verb'] === ACTIVITY_LIKE) ? 'Like' : 'Comment');
+
+ $sql_sign_id = 'retract_iid';
+ $tpl = get_markup_template('diaspora_relayable_retraction.tpl');
+ }
+ elseif($item['verb'] === ACTIVITY_LIKE) {
+ $like = true;
+
+ $target_type = ( $parent['mid'] === $parent['parent_mid'] ? 'Post' : 'Comment');
+// $positive = (($item['item_restrict'] & ITEM_DELETED) ? 'false' : 'true');
+ $positive = 'true';
+
+ $tpl = get_markup_template('diaspora_like_relay.tpl');
+ }
+ else { // item is a comment
+ $tpl = get_markup_template('diaspora_comment_relay.tpl');
+ }
+
+ $diaspora_meta = (($item['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;
+ $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');
+
+ /* Since the author signature is only checked by the parent, not by the relay recipients,
+ * I think it may not be necessary for us to do so much work to preserve all the original
+ * signatures. The important thing that Diaspora DOES need is the original creator's handle.
+ * Let's just generate that and forget about all the original author signature stuff.
+ *
+ * Note: this might be more of an problem if we want to support likes on comments for older
+ * versions of Diaspora (diaspora-pistos), but since there are a number of problems with
+ * doing that, let's ignore it for now.
+ *
+ *
+ */
+// bug - nomadic identity may/will affect diaspora_handle_from_contact
+ if(! $handle) {
+ if($item['author_xchan'] === $owner['channel_hash'])
+ $handle = $owner['channel_address'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
+ else
+ $handle = diaspora_handle_from_contact($item['author_xchan']);
+ }
+ if(! $handle) {
+ logger('diaspora_send_relay: no handle');
+ return;
+ }
+
+ if(! $sender_signed_text) {
+ if($relay_retract)
+ $sender_signed_text = $item['mid'] . ';' . $target_type;
+ elseif($like)
+ $sender_signed_text = $positive . ';' . $item['mid'] . ';' . $target_type . ';' . $parent['mid'] . ';' . $handle;
+ else
+ $sender_signed_text = $item['mid'] . ';' . $parent['mid'] . ';' . $text . ';' . $handle;
+ }
+
+ // Sign the relayable with the top-level owner's signature
+ //
+ // We'll use the $sender_signed_text that we just created, instead of the $signed_text
+ // stored in the database, because that provides the best chance that Diaspora will
+ // be able to reconstruct the signed text the same way we did. This is particularly a
+ // concern for the comment, whose signed text includes the text of the comment. The
+ // smallest change in the text of the comment, including removing whitespace, will
+ // make the signature verification fail. Since we translate from BB code to Diaspora's
+ // markup at the top of this function, which is AFTER we placed the original $signed_text
+ // in the database, it's hazardous to trust the original $signed_text.
+
+ $parentauthorsig = base64_encode(rsa_sign($sender_signed_text,$owner['channel_prvkey'],'sha256'));
+
+ $msg = replace_macros($tpl,array(
+ '$guid' => xmlify($item['mid']),
+ '$parent_guid' => xmlify($parent['mid']),
+ '$target_type' =>xmlify($target_type),
+ '$authorsig' => xmlify($authorsig),
+ '$parentsig' => xmlify($parentauthorsig),
+ '$body' => xmlify($text),
+ '$positive' => xmlify($positive),
+ '$handle' => xmlify($handle)
+ ));
+
+ logger('diaspora_send_relay: base message: ' . $msg, LOGGER_DATA);
+
+ $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['channel_prvkey'],$contact['xchan_pubkey'],$public_batch)));
+
+ return(diaspora_transmit($owner,$contact,$slap,$public_batch));
+
+}
+
+
+
+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);
+
+ // Check whether the retraction is for a top-level post or whether it's a relayable
+ if( $item['mid'] !== $item['parent_mid'] ) {
+
+ $tpl = get_markup_template('diaspora_relay_retraction.tpl');
+ $target_type = (($item['verb'] === ACTIVITY_LIKE) ? 'Like' : 'Comment');
+ }
+ else {
+
+ $tpl = get_markup_template('diaspora_signed_retract.tpl');
+ $target_type = 'StatusMessage';
+ }
+
+ $signed_text = $item['mid'] . ';' . $target_type;
+
+ $msg = replace_macros($tpl, array(
+ '$guid' => xmlify($item['mid']),
+ '$type' => xmlify($target_type),
+ '$handle' => xmlify($myaddr),
+ '$signature' => xmlify(base64_encode(rsa_sign($signed_text,$owner['channel_prvkey'],'sha256')))
+ ));
+
+ $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['channel_prvkey'],$contact['xchan_pubkey'],$public_batch)));
+
+ return(diaspora_transmit($owner,$contact,$slap,$public_batch));
+}
+
+function diaspora_send_mail($item,$owner,$contact) {
+
+ $a = get_app();
+ $myaddr = $owner['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
+
+ $r = q("select * from conv where id = %d and uid = %d limit 1",
+ intval($item['convid']),
+ intval($item['uid'])
+ );
+
+ if(! count($r)) {
+ logger('diaspora_send_mail: conversation not found.');
+ return;
+ }
+ $cnv = $r[0];
+
+ $conv = array(
+ 'guid' => xmlify($cnv['guid']),
+ 'subject' => xmlify($cnv['subject']),
+ 'created_at' => xmlify(datetime_convert('UTC','UTC',$cnv['created'],'Y-m-d H:i:s \U\T\C')),
+ 'diaspora_handle' => xmlify($cnv['creator']),
+ 'participant_handles' => xmlify($cnv['recips'])
+ );
+
+ $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 . ';'
+ . $created . ';' . $myaddr . ';' . $cnv['guid'];
+
+ $sig = base64_encode(rsa_sign($signed_text,$owner['channel_prvkey'],'sha256'));
+
+ $msg = array(
+ 'guid' => xmlify($item['guid']),
+ 'parent_guid' => xmlify($cnv['guid']),
+ 'parent_author_signature' => (($item['reply']) ? null : xmlify($sig)),
+ 'author_signature' => xmlify($sig),
+ 'text' => xmlify($body),
+ 'created_at' => xmlify($created),
+ 'diaspora_handle' => xmlify($myaddr),
+ 'conversation_guid' => xmlify($cnv['guid'])
+ );
+
+ if($item['reply']) {
+ $tpl = get_markup_template('diaspora_message.tpl');
+ $xmsg = replace_macros($tpl, array('$msg' => $msg));
+ }
+ else {
+ $conv['messages'] = array($msg);
+ $tpl = get_markup_template('diaspora_conversation.tpl');
+ $xmsg = replace_macros($tpl, array('$conv' => $conv));
+ }
+
+ logger('diaspora_conversation: ' . print_r($xmsg,true), LOGGER_DATA);
+
+ $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($xmsg,$owner,$contact,$owner['channel_prvkey'],$contact['xchan_pubkey'],false)));
+
+ return(diaspora_transmit($owner,$contact,$slap,false));
+
+
+}
+
+function diaspora_transmit($owner,$contact,$slap,$public_batch,$queue_run=false) {
+
+ $enabled = intval(get_config('system','diaspora_enabled'));
+ if(! $enabled) {
+ return 200;
+ }
+
+ if($public_batch)
+ $dest_url = $contact['hubloc_callback'] . '/public';
+ else
+ $dest_url = $contact['hubloc_callback'] . '/users/' . $contact['hubloc_guid'];
+
+ logger('diaspora_transmit: URL: ' . $dest_url, LOGGER_DEBUG);
+
+ if(intval(get_config('system','diaspora_test')))
+ return 200;
+
+ $a = get_app();
+ $logid = random_string(4);
+
+ logger('diaspora_transmit: ' . $logid . ' ' . $dest_url, LOGGER_DEBUG);
+
+ $hash = random_string();
+
+ $interval = ((get_config('system','delivery_interval') !== false)
+ ? intval(get_config('system','delivery_interval')) : 2 );
+
+ 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($owner['account_id']),
+ intval($owner['channel_id']),
+ dbesc('post'),
+ dbesc($dest_url),
+ intval(1),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ dbesc(''),
+ dbesc($slap)
+ );
+
+ proc_run('php','include/deliver.php',$hash);
+ if($interval)
+ @time_sleep_until(microtime(true) + (float) $interval);
+
+}
diff --git a/include/dir_fns.php b/include/dir_fns.php
index 14e4c0a1d..f58e7c307 100644
--- a/include/dir_fns.php
+++ b/include/dir_fns.php
@@ -30,6 +30,26 @@ function find_upstream_directory($dirmode) {
return array('url' => $preferred);
}
+function check_upstream_directory() {
+ /**
+ * Directories may come and go over time. We will need to check that our
+ * directory server is still valid occasionally, and reset to something that
+ * is if our directory has gone offline for any reason
+ */
+ $directory = get_config('system','directory_server');
+ if ($directory) {
+ $r = q("select * from site where site_url = '%s' and (site_flags & %d) ",
+ dbesc($directory),
+ intval(DIRECTORY_MODE_PRIMARY|DIRECTORY_MODE_SECONDARY|DIRECTORY_MODE_STANDALONE)
+ );
+ }
+ // If we've got something, it's still a directory. If we haven't, we need to reset and let find_upstream_directory() fix it
+ if (! $r) {
+ set_config('system','directory_server','');
+ }
+ return;
+}
+
function dir_sort_links() {
$o = replace_macros(get_markup_template('dir_sort_links.tpl'), array(
@@ -64,26 +84,40 @@ function sync_directories($dirmode) {
if($dirmode == DIRECTORY_MODE_STANDALONE || $dirmode == DIRECTORY_MODE_NORMAL)
return;
- $r = q("select * from site where (site_flags & %d) and site_url != '%s'",
- intval(DIRECTORY_MODE_PRIMARY|DIRECTORY_MODE_SECONDARY),
- dbesc(z_root())
- );
+ $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 = '') ",
+ 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' ",
+ intval(DIRECTORY_MODE_PRIMARY|DIRECTORY_MODE_SECONDARY),
+ dbesc(z_root()),
+ dbesc(protect_sprintf('%' . $realm . '%'))
+ );
+ }
// If there are no directory servers, setup the fallback master
+ // FIXME - what to do if we're in a different realm?
if((! $r) && (z_root() != DIRECTORY_FALLBACK_MASTER)) {
$r = array(
'site_url' => DIRECTORY_FALLBACK_MASTER,
'site_flags' => DIRECTORY_MODE_PRIMARY,
- 'site_update' => '0000-00-00 00:00:00',
- 'site_directory' => DIRECTORY_FALLBACK_MASTER . '/dirsearch'
+ 'site_update' => NULL_DATE,
+ 'site_directory' => DIRECTORY_FALLBACK_MASTER . '/dirsearch',
+ 'site_realm' => DIRECTORY_REALM
);
- $x = q("insert into site ( site_url, site_flags, site_update, site_directory )
- values ( '%s', %d', '%s', '%s' ) ",
+ $x = q("insert into site ( site_url, site_flags, site_update, site_directory, site_realm )
+ values ( '%s', %d', '%s', '%s', '%s' ) ",
dbesc($r[0]['site_url']),
intval($r[0]['site_flags']),
dbesc($r[0]['site_update']),
- dbesc($r[0]['site_directory'])
+ dbesc($r[0]['site_directory']),
+ dbesc($r[0]['site_realm'])
);
$r = q("select * from site where (site_flags & %d) and site_url != '%s'",
@@ -103,7 +137,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'])
diff --git a/include/enotify.php b/include/enotify.php
index 8baf5c09f..2503f9ab0 100644
--- a/include/enotify.php
+++ b/include/enotify.php
@@ -65,7 +65,7 @@ function notification($params) {
localize_item($i);
$title = $i['title'];
$body = $i['body'];
- $private = $i['item_private'];
+ $private = (($i['item_private']) || ($i['item_flags'] & ITEM_OBSCURED));
}
else {
$title = $params['item']['title'];
@@ -135,7 +135,7 @@ function notification($params) {
$item_post_type = item_post_type($p[0]);
- $private = $p[0]['item_private'];
+// $private = $p[0]['item_private'];
$parent_id = $p[0]['id'];
//$possess_desc = str_replace('<!item_type!>',$possess_desc);
@@ -411,7 +411,7 @@ function notification($params) {
$textversion = strip_tags(html_entity_decode(bbcode(stripslashes(str_replace(array("\\r", "\\n"), array( "", "\n"), $body))),ENT_QUOTES,'UTF-8'));
- $htmlversion = html_entity_decode(bbcode(stripslashes(str_replace(array("\\r","\\n"), array("","<br />\n"),$body))), ENT_QUOTES,'UTF-8');
+ $htmlversion = bbcode(stripslashes(str_replace(array("\\r","\\n"), array("","<br />\n"),$body)));
// use $_SESSION['zid_override'] to force zid() to use
@@ -461,6 +461,8 @@ function notification($params) {
// Might be interesting to use GPG,PGP,S/MIME encryption instead
// but we'll save that for a clever plugin developer to implement
+ $private_activity = false;
+
if(! $datarray['email_secure']) {
switch($params['type']) {
case NOTIFY_WALL:
@@ -469,14 +471,22 @@ function notification($params) {
case NOTIFY_COMMENT:
if(! $private)
break;
+ $private_activity = true;
case NOTIFY_MAIL:
$datarray['textversion'] = $datarray['htmlversion'] = $datarray['title'] = '';
+ $datarray['subject'] = preg_replace('/' . preg_quote(t('[Red:Notify]')) . '/','$0*',$datarray['subject']);
break;
default:
break;
}
}
+ if($private_activity
+ && intval(get_pconfig($datarray['uid'],'system','ignore_private_notifications'))) {
+ pop_lang();
+ return;
+ }
+
// load the template for private message notifications
$tpl = get_markup_template('email_notify_html.tpl');
$email_html_body = replace_macros($tpl,array(
diff --git a/include/event.php b/include/event.php
index 0c29d26f6..1fe6e6f7f 100644
--- a/include/event.php
+++ b/include/event.php
@@ -374,6 +374,26 @@ 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;
@@ -424,6 +444,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'];
diff --git a/include/externals.php b/include/externals.php
index a96bf7c97..acfaae3b6 100644
--- a/include/externals.php
+++ b/include/externals.php
@@ -41,7 +41,7 @@ function externals_run($argv, $argc){
$bl1 = get_config('system','blacklisted_sites');
if(is_array($bl1) && $bl1) {
foreach($bl1 as $bl) {
- if(strpos($url,$bl) !== false) {
+ if($bl && strpos($url,$bl) !== false) {
$blacklisted = true;
break;
}
@@ -60,8 +60,8 @@ function externals_run($argv, $argc){
}
if($url) {
- if($r[0]['site_pull'] !== '0000-00-00 00:00:00')
- $mindate = urlencode($r[0]['site_pull']);
+ if($r[0]['site_pull'] !== NULL_DATE)
+ $mindate = urlencode(datetime_convert('','',$r[0]['site_pull'] . ' - 1 day'));
else {
$days = get_config('externals','since_days');
if($days === false)
diff --git a/include/features.php b/include/features.php
index 383cb739f..7530158ec 100644
--- a/include/features.php
+++ b/include/features.php
@@ -24,13 +24,16 @@ function get_features() {
// This is per post, and different from fixed expiration 'expire' which isn't working yet
array('content_expire', t('Content Expiration'), t('Remove posts/comments and/or private messages at a future time')),
array('multi_profiles', t('Multiple Profiles'), t('Ability to create multiple profiles')),
- array('advanced_profiles', t('Advanced Profiles'), t('Additional profile sections and selections')),
-
+ array('advanced_profiles', t('Advanced Profiles'), t('Additional profile sections and selections')),
+ array('profile_export', t('Profile Import/Export'), t('Save and load profile details across sites/channels')),
array('webpages', t('Web Pages'), t('Provide managed web pages on your channel')),
array('private_notes', t('Private Notes'), t('Enables a tool to store notes and reminders')),
// prettyphoto has licensing issues and will no longer be provided in core -
// in any event this setting should probably be a theme option or plugin
// array('prettyphoto', t('Enhanced Photo Albums'), t('Enable photo album with enhanced features')),
+ array('nav_channel_select', t('Navigation Channel Select'), t('Change channels directly from within the navigation dropdown menu')),
+
+
//FIXME - needs a description, but how the hell do we explain this to normals?
array('sendzid', t('Extended Identity Sharing'), t('Share your identity with all websites on the internet. When disabled, identity is only shared with sites in the matrix.')),
array('expert', t('Expert Mode'), t('Enable Expert Mode to provide advanced configuration options')),
diff --git a/include/follow.php b/include/follow.php
index d98a58198..18a9e66ea 100644
--- a/include/follow.php
+++ b/include/follow.php
@@ -17,7 +17,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
$a = get_app();
$is_red = false;
-
+ $is_http = ((strpos($url,'://') !== false) ? true : false);
if(! allowed_url($url)) {
$result['message'] = t('Channel is blocked on this site.');
@@ -29,16 +29,32 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
return $result;
}
+
+ // check service class limits
+
+ $r = q("select count(*) as total from abook where abook_channel = %d and not (abook_flags & %d) ",
+ intval($uid),
+ intval(ABOOK_FLAG_SELF)
+ );
+ if($r)
+ $total_channels = $r[0]['total'];
+
+ if(! service_class_allows($uid,'total_channels',$total_channels)) {
+ $result['message'] = upgrade_message();
+ return $result;
+ }
+
+
$arr = array('url' => $url, 'channel' => array());
call_hooks('follow', $arr);
if($arr['channel']['success'])
$ret = $arr['channel'];
- else
+ elseif(! $is_http)
$ret = zot_finger($url,$channel);
- if($ret['success']) {
+ if($ret && $ret['success']) {
$is_red = true;
$j = json_decode($ret['body'],true);
}
@@ -61,20 +77,6 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
if(array_key_exists('connect_url',$j) && (! $confirm))
goaway(zid($j['connect_url']));
- // check service class limits
-
- $r = q("select count(*) as total from abook where abook_channel = %d and not (abook_flags & %d) ",
- intval($uid),
- intval(ABOOK_FLAG_SELF)
- );
- if($r)
- $total_channels = $r[0]['total'];
-
- if(! service_class_allows($uid,'total_channels',$total_channels)) {
- $result['message'] = upgrade_message();
- return $result;
- }
-
// do we have an xchan and hubloc?
// If not, create them.
@@ -116,16 +118,42 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
}
}
else {
+ if(! ($is_http)) {
+ if(! intval(get_config('system','diaspora_enabled'))) {
+ $result['message'] = t('Protocol disabled.');
+ return $result;
+ }
+ }
- // attempt network auto-discovery
-
$my_perms = 0;
$their_perms = 0;
$xchan_hash = '';
-
-
+ $r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' limit 1",
+ dbesc($url),
+ dbesc($url)
+ );
+ if(! $r) {
+ // attempt network auto-discovery
+ if(strpos($url,'@')) {
+ $r = discover_by_webbie($url);
+ }
+ elseif($is_http) {
+ $r = discover_by_url($url);
+ }
+ if($r) {
+ $r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' limit 1",
+ dbesc($url),
+ dbesc($url)
+ );
+ }
+ }
+ if($r) {
+ $xchan_hash = $r[0]['xchan_hash'];
+ $their_perms = 0;
+ $my_perms = PERMS_W_STREAM|PERMS_W_MAIL;
+ }
}
if(! $xchan_hash) {
@@ -139,7 +167,6 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
$hash = get_observer_hash();
$ch = $a->get_channel();
$default_group = $ch['channel_default_group'];
-
}
else {
$r = q("select * from channel where channel_id = %d limit 1",
@@ -153,7 +180,25 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
$hash = $r[0]['channel_hash'];
$default_group = $r[0]['channel_default_group'];
}
-
+
+ if($is_http) {
+ if(! intval(get_config('system','feed_contacts'))) {
+ $result['message'] = t('Protocol disabled.');
+ return $result;
+ }
+
+ $r = q("select count(*) as total from abook where abook_account = %d and ( abook_flags & ABOOK_FLAG_FEED )",
+ intval($aid)
+ );
+ if($r)
+ $total_feeds = $r[0]['total'];
+
+ if(! service_class_allows($uid,'total_feeds',$total_feeds)) {
+ $result['message'] = upgrade_message();
+ return $result;
+ }
+ }
+
if($hash == $xchan_hash) {
$result['message'] = t('Cannot connect to yourself.');
return $result;
@@ -170,12 +215,13 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
);
}
else {
- $r = q("insert into abook ( abook_account, abook_channel, abook_xchan, abook_their_perms, abook_my_perms, abook_created, abook_updated )
- values( %d, %d, '%s', %d, %d, '%s', '%s' ) ",
+ $r = q("insert into abook ( abook_account, abook_channel, abook_xchan, abook_flags, abook_their_perms, abook_my_perms, abook_created, abook_updated )
+ values( %d, %d, '%s', %d, %d, %d, '%s', '%s' ) ",
intval($aid),
intval($uid),
dbesc($xchan_hash),
- intval($their_perms),
+ intval(($is_http) ? ABOOK_FLAG_FEED : 0),
+ intval(($is_http) ? $their_perms|PERMS_R_STREAM|PERMS_A_REPUBLISH : $their_perms),
intval($my_perms),
dbesc(datetime_convert()),
dbesc(datetime_convert())
diff --git a/include/group.php b/include/group.php
index acb65df28..d4f08108f 100644
--- a/include/group.php
+++ b/include/group.php
@@ -280,6 +280,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/identity.php b/include/identity.php
index c4134d3e8..d8f59e56c 100644
--- a/include/identity.php
+++ b/include/identity.php
@@ -559,6 +559,37 @@ function profile_load(&$a, $nickname, $profile = '') {
return;
}
+ $q = q("select * from profext where hash = '%s' and channel_id = %d",
+ dbesc($p[0]['profile_guid']),
+ intval($p[0]['profile_uid'])
+ );
+ if($q) {
+
+ $extra_fields = array();
+
+ require_once('include/identity.php');
+ $profile_fields_basic = get_profile_fields_basic();
+ $profile_fields_advanced = get_profile_fields_advanced();
+
+ $advanced = ((feature_enabled(local_user(),'advanced_profiles')) ? true : false);
+ if($advanced)
+ $fields = $profile_fields_advanced;
+ else
+ $fields = $profile_fields_basic;
+
+ foreach($q as $qq) {
+ foreach($fields as $k => $f) {
+ if($k == $qq['k']) {
+ $p[0][$k] = $qq['v'];
+ $extra_fields[] = $k;
+ break;
+ }
+ }
+ }
+ }
+
+ $p[0]['extra_fields'] = $extra_fields;
+
$z = q("select xchan_photo_date from xchan where xchan_hash = '%s' limit 1",
dbesc($p[0]['channel_hash'])
);
@@ -739,9 +770,21 @@ logger('online: ' . $profile['online']);
$location = $pdesc = $gender = $marital = $homepage = $online = False;
}
- $firstname = ((strpos($profile['name'],' '))
- ? trim(substr($profile['name'],0,strpos($profile['name'],' '))) : $profile['name']);
- $lastname = (($firstname === $profile['name']) ? '' : trim(substr($profile['name'],strlen($firstname))));
+ $firstname = ((strpos($profile['channel_name'],' '))
+ ? trim(substr($profile['channel_name'],0,strpos($profile['channel_name'],' '))) : $profile['channel_name']);
+ $lastname = (($firstname === $profile['channel_name']) ? '' : trim(substr($profile['channel_name'],strlen($firstname))));
+
+ $diaspora = array(
+ 'podloc' => z_root(),
+ 'searchable' => (($block) ? 'false' : 'true'),
+ 'nickname' => $profile['channel_address'],
+ 'fullname' => $profile['channel_name'],
+ 'firstname' => $firstname,
+ 'lastname' => $lastname,
+ 'photo300' => z_root() . '/photo/profile/300/' . $profile['uid'] . '.jpg',
+ 'photo100' => z_root() . '/photo/profile/100/' . $profile['uid'] . '.jpg',
+ 'photo50' => z_root() . '/photo/profile/50/' . $profile['uid'] . '.jpg',
+ );
$contact_block = contact_block();
@@ -771,6 +814,7 @@ logger('online: ' . $profile['online']);
'$marital' => $marital,
'$homepage' => $homepage,
'$chanmenu' => $channel_menu,
+ '$diaspora' => $diaspora,
'$contact_block' => $contact_block,
));
@@ -998,7 +1042,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'));
}
@@ -1041,6 +1085,16 @@ function advanced_profile(&$a) {
if($txt = prepare_text($a->profile['education'])) $profile['education'] = array( t('School/education:'), $txt );
+ if($a->profile['extra_fields']) {
+ foreach($a->profile['extra_fields'] as $f) {
+ $x = q("select * from profdef where field_name = '%s' limit 1",
+ dbesc($f)
+ );
+ if($x && $txt = prepare_text($a->profile[$f]))
+ $profile[$f] = array( $x[0]['field_desc'] . ':',$txt);
+ }
+ $profile['extra_fields'] = $a->profile['extra_fields'];
+ }
$things = get_things($a->profile['profile_guid'],$a->profile['profile_uid']);
@@ -1312,3 +1366,38 @@ function is_public_profile() {
return true;
return false;
}
+
+function get_profile_fields_basic($filter = 0) {
+
+ $profile_fields_basic = (($filter == 0) ? get_config('system','profile_fields_basic') : null);
+ if(! $profile_fields_basic)
+ $profile_fields_basic = array('name','pdesc','chandesc','gender','dob','dob_tz','address','locality','region','postal_code','country_name','marital','sexual','homepage','hometown','keywords','about','contact');
+
+ $x = array();
+ if($profile_fields_basic)
+ foreach($profile_fields_basic as $f)
+ $x[$f] = 1;
+
+ return $x;
+
+}
+
+
+function get_profile_fields_advanced($filter = 0) {
+ $basic = get_profile_fields_basic($filter);
+ $profile_fields_advanced = (($filter == 0) ? get_config('system','profile_fields_advanced') : null);
+ if(! $profile_fields_advanced)
+ $profile_fields_advanced = array('with','howlong','politic','religion','likes','dislikes','interest','channels','music','book','film','tv','romance','work','education');
+
+ $x = array();
+ if($basic)
+ foreach($basic as $f => $v)
+ $x[$f] = $v;
+ if($profile_fields_advanced)
+ foreach($profile_fields_advanced as $f)
+ $x[$f] = 1;
+
+ return $x;
+}
+
+
diff --git a/include/items.php b/include/items.php
index 679c24dc1..166303cac 100755
--- a/include/items.php
+++ b/include/items.php
@@ -7,23 +7,21 @@ require_once('include/photo/photo_driver.php');
require_once('include/permissions.php');
-function collect_recipients($item,&$private) {
+function collect_recipients($item,&$private_envelope) {
require_once('include/group.php');
- $private = ((intval($item['item_private'])) ? true : false);
+ $private_envelope = ((intval($item['item_private'])) ? true : false);
$recipients = array();
- // if the post is marked private but there are no recipients, only add the author and owner
- // as recipients. The ACL for the post may live on the hub of a different clone. We need to
- // get the post to that hub.
-
if($item['allow_cid'] || $item['allow_gid'] || $item['deny_cid'] || $item['deny_gid']) {
// it is private
$allow_people = expand_acl($item['allow_cid']);
+
$allow_groups = expand_groups(expand_acl($item['allow_gid']));
+ $allow_groups = filter_insecure($item['uid'],$allow_groups);
$recipients = array_unique(array_merge($allow_people,$allow_groups));
@@ -48,11 +46,28 @@ function collect_recipients($item,&$private) {
$deny_groups = expand_groups(expand_acl($item['deny_gid']));
$deny = array_unique(array_merge($deny_people,$deny_groups));
- $recipients = array_diff($recipients,$deny);
- $private = true;
+
+ // Don't deny anybody if nobody was allowed (e.g. they were all filtered out)
+ // That would lead to array_diff doing the wrong thing.
+ // This will result in a private post that won't be delivered to anybody.
+
+ if($recipients && $deny)
+ $recipients = array_diff($recipients,$deny);
+ $private_envelope = true;
}
else {
- if(! $private) {
+
+ // if the post is marked private but there are no recipients and public_policy/scope = self,
+ // only add the author and owner as recipients. The ACL for the post may live on the hub of
+ // a different clone. We need to get the post to that hub.
+
+ // The post may be private by virtue of not being visible to anybody on the internet,
+ // but there are no envelope recipients, so set this to false. Delivery is controlled
+ // by the directives in $item['public_policy'].
+
+ $private_envelope = false;
+
+ 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) ",
intval($item['uid']),
intval(ABOOK_FLAG_SELF|ABOOK_FLAG_PENDING|ABOOK_FLAG_ARCHIVED)
@@ -88,11 +103,57 @@ function collect_recipients($item,&$private) {
$recipients[] = $item['author_xchan'];
if($item['owner_xchan'] != $item['author_xchan'])
$recipients[] = $item['owner_xchan'];
+
return $recipients;
}
/**
+ * If channel is configured to filter insecure members of privacy groups
+ * (those whose networks leak privacy via email notifications or other criteria)
+ * remove them from any privacy groups (collections) that were included in a post.
+ * They can still be addressed individually.
+ * Networks may need to be added or removed from this list as circumstances change.
+ *
+ * Update: this may need to be the default, which will force people to opt-in to sending stuff
+ * privately to insecure platforms.
+ */
+
+function filter_insecure($channel_id,$arr) {
+ $insecure_nets = " and not xchan_network in ('diaspora', 'friendica-over-diaspora') ";
+
+ $ret = array();
+
+ if((! intval(get_config($channel_id,'system','filter_insecure_collections'))) || (! $arr))
+ return $arr;
+
+ $str = '';
+ foreach($arr as $rr) {
+ if(strlen($str))
+ $str .= ',';
+ $str .= "'" . dbesc($rr) . "'";
+ }
+ $r = q("select xchan_hash from xchan where xchan_hash in ($str) $insecure_nets ");
+ if($r) {
+ foreach($r as $rr) {
+ $ret[] = $rr['xchan_hash'];
+ }
+ }
+ return $ret;
+}
+
+
+function comments_are_now_closed($item) {
+ if($item['comments_closed'] !== NULL_DATE) {
+ $d = datetime_convert();
+ if($d > $item['comments_closed'])
+ return true;
+ }
+ return false;
+}
+
+
+/**
* @function can_comment_on_post($observer_xchan,$item);
*
* This function examines the comment_policy attached to an item and decides if the current observer has
@@ -101,6 +162,7 @@ function collect_recipients($item,&$private) {
* Generally we should look at the item - in particular the author['book_flags'] and see if ABOOK_FLAG_SELF is set.
* If it is, you should be able to use perm_is_allowed( ... 'post_comments'), and if it isn't you need to call
* can_comment_on_post()
+ * We also check the comments_closed date/time on the item if this is set.
*/
function can_comment_on_post($observer_xchan,$item) {
@@ -109,8 +171,14 @@ function can_comment_on_post($observer_xchan,$item) {
if(! $observer_xchan)
return false;
+
+
if($item['comment_policy'] === 'none')
return false;
+
+ if(comments_are_now_closed($item))
+ return false;
+
if($observer_xchan === $item['author_xchan'] || $observer_xchan === $item['owner_xchan'])
return true;
switch($item['comment_policy']) {
@@ -125,6 +193,7 @@ function can_comment_on_post($observer_xchan,$item) {
// false.
return false;
break;
+ case 'any connections':
case 'contacts':
case '':
if(array_key_exists('owner',$item)) {
@@ -289,6 +358,9 @@ function post_activity_item($arr) {
return $ret;
}
+ $arr['public_policy'] = ((x($_REQUEST,'public_policy')) ? escape_tags($_REQUEST['public_policy']) : map_scope($channel['channel_r_stream'],true));
+ if($arr['public_policy'])
+ $arr['item_private'] = 1;
if(! array_key_exists('mimetype',$arr))
$arr['mimetype'] = 'text/bbcode';
@@ -308,9 +380,9 @@ function post_activity_item($arr) {
$key = get_config('system','pubkey');
$arr['item_flags'] = $arr['item_flags'] | ITEM_OBSCURED;
if($arr['title'])
- $arr['title'] = json_encode(aes_encapsulate($arr['title'],$key));
+ $arr['title'] = json_encode(crypto_encapsulate($arr['title'],$key));
if($arr['body'])
- $arr['body'] = json_encode(aes_encapsulate($arr['body'],$key));
+ $arr['body'] = json_encode(crypto_encapsulate($arr['body'],$key));
}
$arr['mid'] = ((x($arr,'mid')) ? $arr['mid'] : item_message_id());
@@ -376,7 +448,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;
@@ -387,12 +459,13 @@ 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);
$params['direction'] = ((x($params,'direction')) ? $params['direction'] : 'desc');
$params['pages'] = ((x($params,'pages')) ? intval($params['pages']) : 0);
+ $params['top'] = ((x($params,'top')) ? intval($params['top']) : 0);
switch($params['type']) {
case 'json':
@@ -433,7 +506,8 @@ function get_feed_for($channel, $observer_hash, $params) {
'records' => $params['records'], // FIXME
'direction' => $params['direction'], // FIXME
'pages' => $params['pages'],
- 'order' => 'post'
+ 'order' => 'post',
+ 'top' => $params['top']
), $channel, $observer_hash, CLIENT_MODE_NORMAL, get_app()->module);
@@ -444,13 +518,13 @@ function get_feed_for($channel, $observer_hash, $params) {
$atom .= replace_macros($feed_template, array(
'$version' => xmlify(RED_VERSION),
'$red' => xmlify(RED_PLATFORM),
- '$feed_id' => xmlify($channel['channel_url']),
+ '$feed_id' => xmlify($channel['xchan_url']),
'$feed_title' => xmlify($channel['channel_name']),
'$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', 'now' , ATOM_TIME)) ,
'$hub' => '', // feed_hublinks(),
'$salmon' => '', // feed_salmonlinks($channel['channel_address']),
'$name' => xmlify($channel['channel_name']),
- '$profile_page' => xmlify($channel['channel_url']),
+ '$profile_page' => xmlify($channel['xchan_url']),
'$mimephoto' => xmlify($channel['xchan_photo_mimetype']),
'$photo' => xmlify($channel['xchan_photo_l']),
'$thumb' => xmlify($channel['xchan_photo_m']),
@@ -664,10 +738,16 @@ function title_is_body($title, $body) {
function get_item_elements($x) {
-
$arr = array();
$arr['body'] = (($x['body']) ? htmlspecialchars($x['body'],ENT_COMPAT,'UTF-8',false) : '');
+ $maxlen = get_max_import_size();
+
+ if($maxlen && mb_strlen($arr['body']) > $maxlen) {
+ $arr['body'] = mb_substr($arr['body'],0,$maxlen,'UTF-8');
+ logger('get_item_elements: message length exceeds max_import_size: truncated');
+ }
+
$arr['created'] = datetime_convert('UTC','UTC',$x['created']);
$arr['edited'] = datetime_convert('UTC','UTC',$x['edited']);
@@ -678,11 +758,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'])
+ : NULL_DATE);
$arr['title'] = (($x['title']) ? htmlspecialchars($x['title'], ENT_COMPAT,'UTF-8',false) : '');
@@ -703,11 +786,16 @@ function get_item_elements($x) {
$arr['mimetype'] = (($x['mimetype']) ? htmlspecialchars($x['mimetype'], ENT_COMPAT,'UTF-8',false) : '');
$arr['obj_type'] = (($x['object_type']) ? htmlspecialchars($x['object_type'], ENT_COMPAT,'UTF-8',false) : '');
$arr['tgt_type'] = (($x['target_type']) ? htmlspecialchars($x['target_type'], ENT_COMPAT,'UTF-8',false) : '');
+
+ $arr['public_policy'] = (($x['public_scope']) ? htmlspecialchars($x['public_scope'], ENT_COMPAT,'UTF-8',false) : '');
+ if($arr['public_policy'] === 'public')
+ $arr['public_policy'] = '';
+
$arr['comment_policy'] = (($x['comment_scope']) ? htmlspecialchars($x['comment_scope'], ENT_COMPAT,'UTF-8',false) : 'contacts');
$arr['sig'] = (($x['signature']) ? htmlspecialchars($x['signature'], ENT_COMPAT,'UTF-8',false) : '');
-
+ $arr['diaspora_meta'] = (($x['diaspora_meta']) ? $x['diaspora_meta'] : '');
$arr['object'] = activity_sanitise($x['object']);
$arr['target'] = activity_sanitise($x['target']);
@@ -783,14 +871,35 @@ function import_author_xchan($x) {
if((! array_key_exists('network', $x)) || ($x['network'] === 'zot')) {
$y = import_author_zot($x);
}
+ if(! $y)
+ $y = import_author_diaspora($x);
if($x['network'] === 'rss') {
$y = import_author_rss($x);
}
+ if($x['network'] === 'unknown') {
+ $y = import_author_unknown($x);
+ }
+
return(($y) ? $y : false);
}
+
+function import_author_diaspora($x) {
+ if(! $x['address'])
+ return false;
+ if(discover_by_webbie($x['address'])) {
+ $r = q("select xchan_hash from xchan where xchan_addr = '%s' limit 1",
+ dbesc($x['address'])
+ );
+ if($r)
+ return $r[0]['xchan_hash'];
+ }
+ return false;
+}
+
+
function import_author_rss($x) {
if(! $x['url'])
@@ -805,16 +914,17 @@ function import_author_rss($x) {
}
$name = trim($x['name']);
- $r = q("insert into xchan ( xchan_hash, xchan_url, xchan_name, xchan_network )
- values ( '%s', '%s', '%s', '%s' )",
- dbesc($x['url']),
+ $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_url, xchan_name, xchan_network )
+ values ( '%s', '%s', '%s', '%s', '%s' )",
+ dbesc($x['guid']),
+ dbesc($x['guid']),
dbesc($x['url']),
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",
@@ -834,6 +944,51 @@ 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']);
+
+ $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' limit 1",
+ 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) {
$x = array();
@@ -846,16 +1001,15 @@ function encode_item($item) {
intval($item['uid'])
);
- if($r) {
- $public_scope = $r[0]['channel_r_stream'];
+ if($r)
$comment_scope = $r[0]['channel_w_comment'];
- }
- else {
- $public_scope = 0;
+ else
$comment_scope = 0;
- }
- $scope = map_scope($public_scope);
+ $scope = $item['public_policy'];
+ if(! $scope)
+ $scope = 'public';
+
$c_scope = map_scope($comment_scope);
if(array_key_exists('item_flags',$item) && ($item['item_flags'] & ITEM_OBSCURED)) {
@@ -867,39 +1021,41 @@ function encode_item($item) {
}
- $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'];
- $x['commented'] = $item['commented'];
- $x['mimetype'] = $item['mimetype'];
- $x['title'] = $item['title'];
- $x['body'] = $item['body'];
- $x['app'] = $item['app'];
- $x['verb'] = $item['verb'];
- $x['object_type'] = $item['obj_type'];
- $x['target_type'] = $item['tgt_type'];
- $x['permalink'] = $item['plink'];
- $x['location'] = $item['location'];
- $x['longlat'] = $item['coord'];
- $x['signature'] = $item['sig'];
- $x['route'] = $item['route'];
-
- $x['owner'] = encode_item_xchan($item['owner']);
- $x['author'] = encode_item_xchan($item['author']);
+ $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'];
+ $x['commented'] = $item['commented'];
+ $x['mimetype'] = $item['mimetype'];
+ $x['title'] = $item['title'];
+ $x['body'] = $item['body'];
+ $x['app'] = $item['app'];
+ $x['verb'] = $item['verb'];
+ $x['object_type'] = $item['obj_type'];
+ $x['target_type'] = $item['tgt_type'];
+ $x['permalink'] = $item['plink'];
+ $x['location'] = $item['location'];
+ $x['longlat'] = $item['coord'];
+ $x['signature'] = $item['sig'];
+ $x['route'] = $item['route'];
+
+ $x['owner'] = encode_item_xchan($item['owner']);
+ $x['author'] = encode_item_xchan($item['author']);
if($item['object'])
- $x['object'] = json_decode_plus($item['object']);
+ $x['object'] = json_decode_plus($item['object']);
if($item['target'])
- $x['target'] = json_decode_plus($item['target']);
+ $x['target'] = json_decode_plus($item['target']);
if($item['attach'])
- $x['attach'] = json_decode_plus($item['attach']);
+ $x['attach'] = json_decode_plus($item['attach']);
if($y = encode_item_flags($item))
- $x['flags'] = $y;
+ $x['flags'] = $y;
- if(! in_array('private',$y))
- $x['public_scope'] = $scope;
+ if($item['comments_closed'] !== NULL_DATE)
+ $x['comments_closed'] = $item['comments_closed'];
+
+ $x['public_scope'] = $scope;
if($item['item_flags'] & ITEM_NOCOMMENT)
$x['comment_scope'] = 'none';
@@ -907,7 +1063,7 @@ function encode_item($item) {
$x['comment_scope'] = $c_scope;
if($item['term'])
- $x['tags'] = encode_item_terms($item['term']);
+ $x['tags'] = encode_item_terms($item['term']);
logger('encode_item: ' . print_r($x,true), LOGGER_DATA);
@@ -916,14 +1072,18 @@ function encode_item($item) {
}
-function map_scope($scope) {
+function map_scope($scope,$strip = false) {
switch($scope) {
case 0:
return 'self';
case PERMS_PUBLIC:
+ if($strip)
+ return '';
return 'public';
case PERMS_NETWORK:
return 'network: red';
+ case PERMS_AUTHED:
+ return 'authenticated';
case PERMS_SITE:
return 'site: ' . get_app()->get_hostname();
case PERMS_PENDING:
@@ -934,14 +1094,31 @@ function map_scope($scope) {
}
}
-
+function translate_scope($scope) {
+ if(! $scope || $scope === 'public')
+ return t('Visible to anybody on the internet.');
+ if(strpos($scope,'self') === 0)
+ return t('Visible to you only.');
+ if(strpos($scope,'network:') === 0)
+ return t('Visible to anybody in this network.');
+ if(strpos($scope,'authenticated') === 0)
+ return t('Visible to anybody authenticated.');
+ if(strpos($scope,'site:') === 0)
+ return sprintf( t('Visible to anybody on %s.'), strip_tags(substr($scope,6)));
+ if(strpos($scope,'any connections') === 0)
+ return t('Visible to all connections.');
+ if(strpos($scope,'contacts') === 0)
+ return t('Visible to approved connections.');
+ if(strpos($scope,'specific') === 0)
+ return t('Visible to specific connections.');
+}
function encode_item_xchan($xchan) {
$ret = array();
$ret['name'] = $xchan['xchan_name'];
$ret['address'] = $xchan['xchan_addr'];
- $ret['url'] = $xchan['hubloc_url'];
+ $ret['url'] = (($xchan['hubloc_url']) ? $xchan['hubloc_url'] : $xchan['xchan_url']);
$ret['network'] = $xchan['xchan_network'];
$ret['photo'] = array('mimetype' => $xchan['xchan_photo_mimetype'], 'src' => $xchan['xchan_photo_m']);
$ret['guid'] = $xchan['xchan_guid'];
@@ -1084,6 +1261,7 @@ function encode_mail($item) {
$x['message_parent'] = $item['parent_mid'];
$x['created'] = $item['created'];
$x['expires'] = $item['expires'];
+ $x['diaspora_meta'] = $item['diaspora_meta'];
$x['title'] = $item['title'];
$x['body'] = $item['body'];
$x['from'] = encode_item_xchan($item['from']);
@@ -1113,8 +1291,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']);
@@ -1189,23 +1367,24 @@ function get_profile_elements($x) {
-function get_atom_elements($feed,$item) {
+function get_atom_elements($feed,$item,&$author) {
$best_photo = array();
$res = array();
- $author = $item->get_author();
- if($author) {
- $res['author-name'] = unxmlify($author->get_name());
- $res['author-link'] = unxmlify($author->get_link());
+ $found_author = $item->get_author();
+ if($found_author) {
+ $author['author_name'] = unxmlify($found_author->get_name());
+ $author['author_link'] = unxmlify($found_author->get_link());
}
else {
- $res['author-name'] = unxmlify($feed->get_title());
- $res['author-link'] = unxmlify($feed->get_permalink());
+ $author['author_name'] = unxmlify($feed->get_title());
+ $author['author_link'] = unxmlify($feed->get_permalink());
}
- $res['mid'] = unxmlify($item->get_id());
+
+ $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));
@@ -1229,9 +1408,9 @@ function get_atom_elements($feed,$item) {
if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
$base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
foreach($base as $link) {
- if(!x($res, 'author-avatar') || !$res['author-avatar']) {
+ if(!x($author, 'author_photo') || ! $author['author_photo']) {
if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
- $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
+ $author['author_photo'] = unxmlify($link['attribs']['']['href']);
}
}
}
@@ -1242,28 +1421,38 @@ function get_atom_elements($feed,$item) {
$base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
if($base && count($base)) {
foreach($base as $link) {
- if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
- $res['author-link'] = unxmlify($link['attribs']['']['href']);
- if(!x($res, 'author-avatar') || !$res['author-avatar']) {
+ if($link['attribs']['']['rel'] === 'alternate' && (! $res['author_link']))
+ $author['author_link'] = unxmlify($link['attribs']['']['href']);
+ if(!x($author, 'author_photo') || ! $author['author_photo']) {
if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo')
- $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
+ $author['author_photo'] = unxmlify($link['attribs']['']['href']);
}
}
}
}
+ // 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($res,'author-link'))) || (! (x($res,'author-avatar')))) {
+ if((! (x($author,'author_link'))) || (! (x($author,'author_photo')))) {
$rawauthor = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'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' && (! $res['author-link']))
- $res['author-link'] = unxmlify($link['attribs']['']['href']);
- if(! $res['author-avatar']) {
+ if($link['attribs']['']['rel'] === 'alternate' && (! $author['author_link']))
+ $author['author_link'] = unxmlify($link['attribs']['']['href']);
+ if(! $author['author_photo']) {
if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
- $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
+ $author['author_photo'] = unxmlify($link['attribs']['']['href']);
}
}
}
@@ -1275,11 +1464,11 @@ function get_atom_elements($feed,$item) {
if($base && count($base)) {
foreach($base as $link) {
- if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
- $res['author-link'] = unxmlify($link['attribs']['']['href']);
- if(! (x($res,'author-avatar'))) {
+ if($link['attribs']['']['rel'] === 'alternate' && (! $res['author_link']))
+ $author['author_link'] = unxmlify($link['attribs']['']['href']);
+ if(! (x($author,'author_photo'))) {
if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo')
- $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
+ $author['author_photo'] = unxmlify($link['attribs']['']['href']);
}
}
}
@@ -1304,6 +1493,12 @@ function get_atom_elements($feed,$item) {
$res['body'] = str_replace(array(' ',"\t","\r","\n"), array('','','',''),$res['body']);
// make sure nobody is trying to sneak some html tags by us
$res['body'] = notags(base64url_decode($res['body']));
+
+ // 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']);
+
}
@@ -1343,9 +1538,9 @@ function get_atom_elements($feed,$item) {
$private = $item->get_item_tags(NAMESPACE_DFRN,'private');
if($private && intval($private[0]['data']) > 0)
- $res['private'] = intval($private[0]['data']);
+ $res['item_private'] = ((intval($private[0]['data'])) ? 1 : 0);
else
- $res['private'] = 0;
+ $res['item_private'] = 0;
$rawlocation = $item->get_item_tags(NAMESPACE_DFRN, 'location');
if($rawlocation)
@@ -1382,23 +1577,29 @@ function get_atom_elements($feed,$item) {
if($d2 > $d3)
$res['edited'] = datetime_convert();
+ $res['created'] = datetime_convert('UTC','UTC',$res['created']);
+ $res['edited'] = datetime_convert('UTC','UTC',$res['edited']);
+
$rawowner = $item->get_item_tags(NAMESPACE_DFRN, 'owner');
+ if(! $rawowner)
+ $rawowner = $item->get_item_tags(NAMESPACE_ZOT,'owner');
+
if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])
- $res['owner-name'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']);
+ $author['owner_name'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']);
elseif($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data'])
- $res['owner-name'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data']);
+ $author['owner_name'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data']);
if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])
- $res['owner-link'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']);
+ $author['owner_link'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']);
elseif($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data'])
- $res['owner-link'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data']);
+ $author['owner_link'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data']);
if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
$base = $rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
foreach($base as $link) {
- if(!x($res, 'owner-avatar') || !$res['owner-avatar']) {
+ if(!x($author, 'owner_photo') || ! $author['owner_photo']) {
if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
- $res['owner-avatar'] = unxmlify($link['attribs']['']['href']);
+ $author['owner_photo'] = unxmlify($link['attribs']['']['href']);
}
}
}
@@ -1435,12 +1636,12 @@ function get_atom_elements($feed,$item) {
$termurl = unxmlify(substr($scheme,9));
}
else {
- $termtype = TERM_UNKNOWN;
+ $termtype = TERM_CATEGORY;
}
$termterm = notags(trim(unxmlify($term)));
if($termterm) {
- $terms = array(
+ $terms[] = array(
'otype' => TERM_OBJ_POST,
'type' => $termtype,
'url' => $termurl,
@@ -1448,11 +1649,12 @@ function get_atom_elements($feed,$item) {
);
}
}
- $res['term'] = implode(',', $tag_arr);
+ $res['term'] = $terms;
}
$attach = $item->get_enclosures();
if($attach) {
+ $res['attach'] = array();
$att_arr = array();
foreach($attach as $att) {
$len = intval($att->get_length());
@@ -1468,33 +1670,32 @@ function get_atom_elements($feed,$item) {
$title = ' ';
if(! $type)
$type = 'application/octet-stream';
-
- $att_arr[] = '[attach]href="' . $link . '" length="' . $len . '" type="' . $type . '" title="' . $title . '"[/attach]';
+ $res['attach'][] = array('href' => $link, 'length' => $len, 'type' => $type, 'title' => $title );
}
- $res['attach'] = implode(',', $att_arr);
}
$rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'object');
if($rawobj) {
- $res['object'] = '<object>' . "\n";
+ $obj = array();
+
$child = $rawobj[0]['child'];
if($child[NAMESPACE_ACTIVITY]['obj_type'][0]['data']) {
$res['obj_type'] = $child[NAMESPACE_ACTIVITY]['obj_type'][0]['data'];
- $res['object'] .= '<type>' . $child[NAMESPACE_ACTIVITY]['obj_type'][0]['data'] . '</type>' . "\n";
+ $obj['type'] = $child[NAMESPACE_ACTIVITY]['obj_type'][0]['data'];
}
if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'id') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'])
- $res['object'] .= '<id>' . $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'] . '</id>' . "\n";
+ $obj['id'] = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'];
if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'link') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['link'])
- $res['object'] .= '<link>' . encode_rel_links($child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']) . '</link>' . "\n";
+ $obj['link'] = encode_rel_links($child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']);
if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'title') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'])
- $res['object'] .= '<title>' . $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'] . '</title>' . "\n";
+ $obj['title'] = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'];
if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'content') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) {
$body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data'];
if(! $body)
$body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data'];
// preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events
- $res['object'] .= '<orig>' . xmlify($body) . '</orig>' . "\n";
+ $obj['orig'] = xmlify($body);
if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) {
$body = purify_html($body);
@@ -1502,74 +1703,55 @@ function get_atom_elements($feed,$item) {
}
- $res['object'] .= '<content>' . $body . '</content>' . "\n";
+ $obj['content'] = $body;
}
- $res['object'] .= '</object>' . "\n";
+ $res['object'] = $obj;
}
$rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'target');
if($rawobj) {
- $res['target'] = '<target>' . "\n";
+ $obj = array();
+
$child = $rawobj[0]['child'];
if($child[NAMESPACE_ACTIVITY]['obj_type'][0]['data']) {
- $res['target'] .= '<type>' . $child[NAMESPACE_ACTIVITY]['obj_type'][0]['data'] . '</type>' . "\n";
+ $res['tgt_type'] = $child[NAMESPACE_ACTIVITY]['obj_type'][0]['data'];
+ $obj['type'] = $child[NAMESPACE_ACTIVITY]['obj_type'][0]['data'];
}
if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'id') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'])
- $res['target'] .= '<id>' . $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'] . '</id>' . "\n";
+ $obj['id'] = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'];
if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'link') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['link'])
- $res['target'] .= '<link>' . encode_rel_links($child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']) . '</link>' . "\n";
- if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'data') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'])
- $res['target'] .= '<title>' . $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'] . '</title>' . "\n";
- if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'data') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) {
+ $obj['link'] = encode_rel_links($child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']);
+ if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'title') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'])
+ $obj['title'] = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'];
+ if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'content') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) {
$body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data'];
if(! $body)
$body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data'];
// preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events
- $res['target'] .= '<orig>' . xmlify($body) . '</orig>' . "\n";
+ $obj['orig'] = xmlify($body);
if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) {
$body = purify_html($body);
$body = html2bbcode($body);
+
}
- $res['target'] .= '<content>' . $body . '</content>' . "\n";
+ $obj['content'] = $body;
}
- $res['target'] .= '</target>' . "\n";
+ $res['target'] = $obj;
}
- // This is some experimental stuff. By now retweets are shown with "RT:"
- // But: There is data so that the message could be shown similar to native retweets
- // There is some better way to parse this array - but it didn't worked for me.
-
- $child = $item->feed->data["child"][SIMPLEPIE_NAMESPACE_ATOM_10]["feed"][0]["child"][SIMPLEPIE_NAMESPACE_ATOM_10]["entry"][0]["child"]["http://activitystrea.ms/spec/1.0/"][object][0]["child"];
- if (is_array($child)) {
- $message = $child["http://activitystrea.ms/spec/1.0/"]["object"][0]["child"][SIMPLEPIE_NAMESPACE_ATOM_10]["content"][0]["data"];
- $author = $child[SIMPLEPIE_NAMESPACE_ATOM_10]["author"][0]["child"][SIMPLEPIE_NAMESPACE_ATOM_10];
- $uri = $author["uri"][0]["data"];
- $name = $author["name"][0]["data"];
- $avatar = @array_shift($author["link"][2]["attribs"]);
- $avatar = $avatar["href"];
-
- if (($name != "") and ($uri != "") and ($avatar != "") and ($message != "")) {
- $res["owner-name"] = $res["author-name"];
- $res["owner-link"] = $res["author-link"];
- $res["owner-avatar"] = $res["author-avatar"];
-
- $res["author-name"] = $name;
- $res["author-link"] = $uri;
- $res["author-avatar"] = $avatar;
-
- $res["body"] = html2bbcode($message);
- }
- }
+ $res['public_policy'] = 'specific';
+ $res['comment_policy'] = 'none';
$arr = array('feed' => $feed, 'item' => $item, 'result' => $res);
call_hooks('parse_atom', $arr);
- logger('get_atom_elements: ' . print_r($res,true));
+ logger('get_atom_elements: author: ' . print_r($author,true),LOGGER_DATA);
+ logger('get_atom_elements: ' . print_r($res,true),LOGGER_DATA);
return $res;
}
@@ -1578,6 +1760,7 @@ function encode_rel_links($links) {
$o = '';
if(! ((is_array($links)) && (count($links))))
return $o;
+
foreach($links as $link) {
$o .= '<link ';
if($link['attribs']['']['rel'])
@@ -1608,7 +1791,7 @@ function item_store($arr,$allow_exec = false) {
if(! $arr['uid']) {
logger('item_store: no uid');
$ret['message'] = 'No uid.';
- return ret;
+ return $ret;
}
$uplinked_comment = false;
@@ -1640,9 +1823,10 @@ function item_store($arr,$allow_exec = false) {
}
- $arr['title'] = ((x($arr,'title')) ? trim($arr['title']) : '');
- $arr['body'] = ((x($arr,'body')) ? trim($arr['body']) : '');
+ $arr['title'] = ((array_key_exists('title',$arr) && strlen($arr['title'])) ? trim($arr['title']) : '');
+ $arr['body'] = ((array_key_exists('body',$arr) && strlen($arr['body'])) ? trim($arr['body']) : '');
+ $arr['diaspora_meta'] = ((x($arr,'diaspora_meta')) ? $arr['diaspora_meta'] : '');
$arr['allow_cid'] = ((x($arr,'allow_cid')) ? trim($arr['allow_cid']) : '');
$arr['allow_gid'] = ((x($arr,'allow_gid')) ? trim($arr['allow_gid']) : '');
$arr['deny_cid'] = ((x($arr,'deny_cid')) ? trim($arr['deny_cid']) : '');
@@ -1714,8 +1898,10 @@ 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']) : NULL_DATE);
+
$arr['received'] = datetime_convert();
$arr['changed'] = datetime_convert();
$arr['location'] = ((x($arr,'location')) ? notags(trim($arr['location'])) : '');
@@ -1732,8 +1918,9 @@ function item_store($arr,$allow_exec = false) {
$arr['app'] = ((x($arr,'app')) ? notags(trim($arr['app'])) : '');
$arr['item_restrict'] = ((x($arr,'item_restrict')) ? intval($arr['item_restrict']) : 0 );
- $arr['comment_policy'] = ((x($arr,'comment_policy')) ? notags(trim($arr['comment_policy'])) : 'contacts' );
+ $arr['public_policy'] = ((x($arr,'public_policy')) ? notags(trim($arr['public_policy'])) : '' );
+ $arr['comment_policy'] = ((x($arr,'comment_policy')) ? notags(trim($arr['comment_policy'])) : 'contacts' );
$arr['item_flags'] = $arr['item_flags'] | ITEM_UNSEEN;
@@ -1764,6 +1951,8 @@ function item_store($arr,$allow_exec = false) {
$allow_gid = $arr['allow_gid'];
$deny_cid = $arr['deny_cid'];
$deny_gid = $arr['deny_gid'];
+ $public_policy = $arr['public_policy'];
+ $comments_closed = $arr['comments_closed'];
$arr['item_flags'] = $arr['item_flags'] | ITEM_THREAD_TOP;
}
else {
@@ -1778,6 +1967,12 @@ function item_store($arr,$allow_exec = false) {
if($r) {
+ if(comments_are_now_closed($r[0])) {
+ logger('item_store: comments closed');
+ $ret['message'] = 'Comments closed.';
+ 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.
@@ -1794,12 +1989,14 @@ function item_store($arr,$allow_exec = false) {
$r = $z;
}
- $parent_id = $r[0]['id'];
- $parent_deleted = $r[0]['item_restrict'] & ITEM_DELETED;
- $allow_cid = $r[0]['allow_cid'];
- $allow_gid = $r[0]['allow_gid'];
- $deny_cid = $r[0]['deny_cid'];
- $deny_gid = $r[0]['deny_gid'];
+ $parent_id = $r[0]['id'];
+ $parent_deleted = $r[0]['item_restrict'] & ITEM_DELETED;
+ $allow_cid = $r[0]['allow_cid'];
+ $allow_gid = $r[0]['allow_gid'];
+ $deny_cid = $r[0]['deny_cid'];
+ $deny_gid = $r[0]['deny_gid'];
+ $public_policy = $r[0]['public_policy'];
+ $comments_closed = $r[0]['comments_closed'];
if($r[0]['item_flags'] & ITEM_WALL)
$arr['item_flags'] = $arr['item_flags'] | ITEM_WALL;
@@ -1813,10 +2010,7 @@ function item_store($arr,$allow_exec = false) {
$uplinked_comment = true;
}
-
// if the parent is private, force privacy for the entire conversation
- // This differs from the above settings as it subtly allows comments from
- // email correspondents to be private even if the overall thread is not.
if($r[0]['item_private'])
$arr['item_private'] = $r[0]['item_private'];
@@ -1908,7 +2102,7 @@ function item_store($arr,$allow_exec = false) {
if((! $parent_id) || ($arr['parent_mid'] === $arr['mid']))
$parent_id = $current_post;
- if(strlen($allow_cid) || strlen($allow_gid) || strlen($deny_cid) || strlen($deny_gid))
+ if(strlen($allow_cid) || strlen($allow_gid) || strlen($deny_cid) || strlen($deny_gid) || strlen($public_policy))
$private = 1;
else
$private = $arr['item_private'];
@@ -1916,13 +2110,16 @@ function item_store($arr,$allow_exec = false) {
// Set parent id - and also make sure to inherit the parent's ACL's.
$r = q("UPDATE item SET parent = %d, allow_cid = '%s', allow_gid = '%s',
- deny_cid = '%s', deny_gid = '%s', item_private = %d WHERE id = %d LIMIT 1",
+ deny_cid = '%s', deny_gid = '%s', public_policy = '%s', item_private = %d, comments_closed = '%s'
+ WHERE id = %d LIMIT 1",
intval($parent_id),
dbesc($allow_cid),
dbesc($allow_gid),
dbesc($deny_cid),
dbesc($deny_gid),
+ dbesc($public_policy),
intval($private),
+ dbesc($comments_closed),
intval($current_post)
);
@@ -1933,8 +2130,10 @@ function item_store($arr,$allow_exec = false) {
$arr['allow_gid'] = $allow_gid;
$arr['deny_cid'] = $deny_cid;
$arr['deny_gid'] = $deny_gid;
+ $arr['public_policy'] = $public_policy;
$arr['item_private'] = $private;
-
+ $arr['comments_closed'] = $comments_closed;
+
// Store taxonomy
if(($terms) && (is_array($terms))) {
@@ -1957,9 +2156,10 @@ 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 ",
+ $z = q("select max(created) as commented from item where parent_mid = '%s' and uid = %d and not ( item_restrict & %d ) ",
dbesc($arr['parent_mid']),
- intval($arr['uid'])
+ intval($arr['uid']),
+ intval(ITEM_DELAYED_PUBLISH)
);
q("UPDATE item set commented = '%s', changed = '%s' WHERE id = %d LIMIT 1",
@@ -2106,10 +2306,17 @@ 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'] != NULL_DATE)
+ $arr['comments_closed'] = datetime_convert('UTC','UTC',$arr['comments_closed']);
+ else
+ $arr['comments_closed'] = $orig[0]['comments_closed'];
+
$arr['commented'] = $orig[0]['commented'];
$arr['received'] = datetime_convert();
$arr['changed'] = datetime_convert();
- $arr['title'] = ((x($arr,'title')) ? notags(trim($arr['title'])) : '');
+
+ $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']);
$arr['verb'] = ((x($arr,'verb')) ? notags(trim($arr['verb'])) : $orig[0]['verb']);
@@ -2125,7 +2332,8 @@ function item_store_update($arr,$allow_exec = false) {
$arr['deny_gid'] = ((array_key_exists('deny_gid',$arr)) ? trim($arr['deny_gid']) : $orig[0]['deny_gid']);
$arr['item_private'] = ((array_key_exists('item_private',$arr)) ? intval($arr['item_private']) : $orig[0]['item_private']);
- $arr['body'] = ((x($arr,'body')) ? trim($arr['body']) : '');
+ $arr['title'] = ((array_key_exists('title',$arr) && strlen($arr['title'])) ? trim($arr['title']) : '');
+ $arr['body'] = ((array_key_exists('body',$arr) && strlen($arr['body'])) ? trim($arr['body']) : '');
$arr['attach'] = ((x($arr,'attach')) ? notags(trim($arr['attach'])) : $orig[0]['attach']);
$arr['app'] = ((x($arr,'app')) ? notags(trim($arr['app'])) : $orig[0]['app']);
// $arr['item_restrict'] = ((x($arr,'item_restrict')) ? intval($arr['item_restrict']) : $orig[0]['item_restrict'] );
@@ -2134,6 +2342,11 @@ function item_store_update($arr,$allow_exec = false) {
$arr['sig'] = ((x($arr,'sig')) ? $arr['sig'] : '');
$arr['layout_mid'] = ((array_key_exists('layout_mid',$arr)) ? dbesc($arr['layout_mid']) : $orig[0]['layout_mid'] );
+ $arr['public_policy'] = ((x($arr,'public_policy')) ? notags(trim($arr['public_policy'])) : $orig[0]['public_policy'] );
+ $arr['comment_policy'] = ((x($arr,'comment_policy')) ? notags(trim($arr['comment_policy'])) : $orig[0]['comment_policy'] );
+
+
+
call_hooks('post_remote_update',$arr);
if(x($arr,'cancel')) {
@@ -2203,6 +2416,62 @@ function item_store_update($arr,$allow_exec = false) {
return $ret;
}
+function store_diaspora_comment_sig($datarray, $channel, $parent_item, $post_id) {
+
+ // 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;
+ }
+
+ $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);
+ }
+
+ logger('mod_item: storing diaspora comment signature',LOGGER_DEBUG);
+
+ require_once('include/bb2diaspora.php');
+
+ $signed_body = html_entity_decode(bb2diaspora($body));
+
+ $diaspora_handle = $channel['channel_address'] . '@' . get_app()->get_hostname();
+
+ $signed_text = $datarray['mid'] . ';' . $parent_item['mid'] . ';' . $signed_body . ';' . $diaspora_handle;
+
+ if( $uprvkey !== false )
+ $authorsig = base64_encode(rsa_sign($signed_text,$channel['channel_prvkey'],'sha256'));
+ else
+ $authorsig = '';
+
+ $x = array('signer' => $diaspora_handle, 'body' => $signed_body, 'signed_text' => $signed_text, 'signature' => base64_encode($authorsig));
+
+ $key = get_config('system','pubkey');
+ $y = crypto_encapsulate(json_encode($x),$key);
+
+ $r = q("update item set diaspora_meta = '%s' where id = %d limit 1",
+ 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');
+
+ return;
+}
@@ -2299,6 +2568,10 @@ function tag_deliver($uid,$item_id) {
$mention = false;
+ /**
+ * Fetch stuff we need - a channel and an item
+ */
+
$u = q("select * from channel where channel_id = %d limit 1",
intval($uid)
);
@@ -2316,14 +2589,19 @@ function tag_deliver($uid,$item_id) {
$item = $i[0];
- if(($item['source_xchan']) && ($item['item_flags'] & ITEM_UPLINK) && ($item['item_flags'] & ITEM_THREAD_TOP) && ($item['edited'] != $item['created'])) {
- // this is an update to a post which was already processed by us and has a second delivery chain
+ if(($item['source_xchan']) && ($item['item_flags'] & ITEM_UPLINK)
+ && ($item['item_flags'] & ITEM_THREAD_TOP) && ($item['edited'] != $item['created'])) {
+ // this is an update (edit) to a post which was already processed by us and has a second delivery chain
// Just start the second delivery chain to deliver the updated post
proc_run('php','include/notifier.php','tgroup',$item['id']);
return;
}
+ /**
+ * Seems like a good place to plug in a poke notification.
+ */
+
if (stristr($item['verb'],ACTIVITY_POKE)) {
$poke_notify = true;
@@ -2352,6 +2630,10 @@ function tag_deliver($uid,$item_id) {
}
}
+ /**
+ * Do community tagging
+ */
+
if($item['obj_type'] === ACTIVITY_OBJ_TAGTERM) {
// We received a community tag activity for a post.
@@ -2392,6 +2674,11 @@ function tag_deliver($uid,$item_id) {
logger('tag_deliver: tag permission denied for ' . $u[0]['channel_address']);
}
+ /**
+ * A "union" is a message which our channel has sourced from another channel.
+ * This sets up a second delivery chain just like forum tags do.
+ * Find out if this is a source-able post.
+ */
$union = check_item_source($uid,$item);
if($union)
@@ -2401,51 +2688,22 @@ function tag_deliver($uid,$item_id) {
// This might be a followup (e.g. comment) by the original post author to a tagged forum
// If so setup a second delivery chain
- $r = null;
-
if( ! ($item['item_flags'] & ITEM_THREAD_TOP)) {
$x = q("select * from item where id = parent and parent = %d and uid = %d limit 1",
intval($item['parent']),
intval($uid)
);
-
if(($x) && ($x[0]['item_flags'] & ITEM_UPLINK)) {
+ start_delivery_chain($u[0],$item,$item_id,$x[0]);
+ }
+ }
- logger('tag_deliver: creating second delivery chain for comment to tagged post.');
-
- // now change this copy of the post to a forum head message and deliver to all the tgroup members
- // also reset all the privacy bits to the forum default permissions
-
- $private = (($u[0]['channel_allow_cid'] || $u[0]['channel_allow_gid'] || $u[0]['channel_deny_cid'] || $u[0]['channel_deny_gid']) ? 1 : 0);
-
- $flag_bits = ITEM_WALL|ITEM_ORIGIN;
-
- // maintain the original source, which will be the original item owner and was stored in source_xchan
- // when we created the delivery fork
- $r = q("update item set source_xchan = '%s' where id = %d limit 1",
- dbesc($x[0]['source_xchan']),
- intval($item_id)
- );
+ /**
+ * Now we've got those out of the way. Let's see if this is a post that's tagged for re-delivery
+ */
- $r = q("update item set item_flags = ( item_flags | %d ), owner_xchan = '%s', allow_cid = '%s', allow_gid = '%s',
- deny_cid = '%s', deny_gid = '%s', item_private = %d where id = %d limit 1",
- intval($flag_bits),
- dbesc($u[0]['channel_hash']),
- dbesc($u[0]['channel_allow_cid']),
- dbesc($u[0]['channel_allow_gid']),
- dbesc($u[0]['channel_deny_cid']),
- dbesc($u[0]['channel_deny_gid']),
- intval($private),
- intval($item_id)
- );
- if($r)
- proc_run('php','include/notifier.php','tgroup',$item_id);
- else
- logger('tag_deliver: failed to update item');
- }
- }
$terms = get_terms_oftype($item['term'],TERM_MENTION);
@@ -2471,8 +2729,6 @@ function tag_deliver($uid,$item_id) {
intval($item_id)
);
-
-
// At this point we've determined that the person receiving this post was mentioned in it or it is a union.
// Now let's check if this mention was inside a reshare so we don't spam a forum
// If it's private we may have to unobscure it momentarily so that we can parse it.
@@ -2508,7 +2764,9 @@ function tag_deliver($uid,$item_id) {
$arr = array('channel_id' => $uid, 'item' => $item, 'body' => $body);
call_hooks('tagged',$arr);
- // Valid tag. Send a notification
+ /**
+ * Kill two birds with one stone. As long as we're here, send a mention notification.
+ */
require_once('include/enotify.php');
notification(array(
@@ -2542,49 +2800,33 @@ function tag_deliver($uid,$item_id) {
return;
}
-
// tgroup delivery - setup a second delivery chain
// prevent delivery looping - only proceed
// if the message originated elsewhere and is a top-level post
- if(($item['item_flags'] & ITEM_WALL) || ($item['item_flags'] & ITEM_ORIGIN) || (!($item['item_flags'] & ITEM_THREAD_TOP)) || ($item['id'] != $item['parent'])) {
+ if(($item['item_flags'] & ITEM_WALL)
+ || ($item['item_flags'] & ITEM_ORIGIN)
+ || (!($item['item_flags'] & ITEM_THREAD_TOP))
+ || ($item['id'] != $item['parent'])) {
logger('tag_deliver: item was local or a comment. rejected.');
return;
}
logger('tag_deliver: creating second delivery chain.');
-
- // now change this copy of the post to a forum head message and deliver to all the tgroup members
- // also reset all the privacy bits to the forum default permissions
-
- $private = (($u[0]['channel_allow_cid'] || $u[0]['channel_allow_gid'] || $u[0]['channel_deny_cid'] || $u[0]['channel_deny_gid']) ? 1 : 0);
-
- $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",
- intval($item_id)
- );
-
- $r = q("update item set item_flags = ( item_flags | %d ), owner_xchan = '%s', allow_cid = '%s', allow_gid = '%s',
- deny_cid = '%s', deny_gid = '%s', item_private = %d where id = %d limit 1",
- intval($flag_bits),
- dbesc($u[0]['channel_hash']),
- dbesc($u[0]['channel_allow_cid']),
- dbesc($u[0]['channel_allow_gid']),
- dbesc($u[0]['channel_deny_cid']),
- dbesc($u[0]['channel_deny_gid']),
- intval($private),
- intval($item_id)
- );
- if($r)
- proc_run('php','include/notifier.php','tgroup',$item_id);
- else
- logger('tag_deliver: failed to update item');
+ start_delivery_chain($u[0],$item,$item_id,null);
}
+/**
+ * @function tgroup_check($uid,$item)
+ *
+ * This function is called pre-deliver to see if a post matches the criteria to be tag delivered.
+ * We don't actually do anything except check that it matches the criteria.
+ * This is so that the channel with tag_delivery enabled can receive the post even if they turn off
+ * permissions for the sender to send their stream. tag_deliver() can't be called until the post is actually stored.
+ * By then it would be too late to reject it.
+ */
+
function tgroup_check($uid,$item) {
@@ -2655,6 +2897,99 @@ function tgroup_check($uid,$item) {
}
+/**
+ * Sourced and tag-delivered posts are re-targetted for delivery to the connections of the channel
+ * receiving the post. This starts the second delivery chain, by resetting permissions and ensuring
+ * that ITEM_UPLINK is set on the parent post, and storing the current owner_xchan as the source_xchan.
+ * We'll become the new owner. If called without $parent, this *is* the parent post.
+ */
+
+function start_delivery_chain($channel,$item,$item_id,$parent) {
+
+
+ // Change this copy of the post to a forum head message and deliver to all the tgroup members
+ // also reset all the privacy bits to the forum default permissions
+
+ $private = (($channel['channel_allow_cid'] || $channel['channel_allow_gid']
+ || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 1 : 0);
+
+ $new_public_policy = map_scope($channel['channel_r_stream'],true);
+
+ if((! $private) && $new_public_policy)
+ $private = 1;
+
+ $flag_bits = $item['item_flags'] | ITEM_WALL|ITEM_ORIGIN;
+
+ // unset the nocomment bit if it's there.
+
+ if($flag_bits & ITEM_NOCOMMENT)
+ $flag_bits = $flag_bits ^ ITEM_NOCOMMENT;
+
+ // maintain the original source, which will be the original item owner and was stored in source_xchan
+ // when we created the delivery fork
+
+ if($parent) {
+ $r = q("update item set source_xchan = '%s' where id = %d limit 1",
+ 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",
+ intval($item_id)
+ );
+ }
+
+ $title = $item['title'];
+ $body = $item['body'];
+
+ if($private) {
+ if(!($flag_bits & ITEM_OBSCURED)) {
+ $key = get_config('system','pubkey');
+ $flag_bits = $flag_bits|ITEM_OBSCURED;
+ if($title)
+ $title = json_encode(crypto_encapsulate($title,$key));
+ if($body)
+ $body = json_encode(crypto_encapsulate($body,$key));
+ }
+ }
+ else {
+ if($flag_bits & ITEM_OBSCURED) {
+ $key = get_config('system','prvkey');
+ $flag_bits = $flag_bits ^ ITEM_OBSCURED;
+ if($title)
+ $title = crypto_unencapsulate(json_decode($title,true),$key);
+ if($body)
+ $body = crypto_unencapsulate(json_decode($body,true),$key);
+ }
+ }
+
+ $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",
+ intval($flag_bits),
+ dbesc($channel['channel_hash']),
+ dbesc($channel['channel_allow_cid']),
+ dbesc($channel['channel_allow_gid']),
+ dbesc($channel['channel_deny_cid']),
+ dbesc($channel['channel_deny_gid']),
+ intval($private),
+ dbesc($new_public_policy),
+ dbesc(map_scope($channel['channel_w_comment'])),
+ dbesc($title),
+ dbesc($body),
+ intval($item_id)
+ );
+
+ if($r)
+ proc_run('php','include/notifier.php','tgroup',$item_id);
+ else
+ logger('start_delivery_chain: failed to update item');
+
+ return;
+}
+
+
/**
* @function check_item_source($uid,$item)
@@ -2670,29 +3005,33 @@ function tgroup_check($uid,$item) {
function check_item_source($uid,$item) {
- if($item['item_private'])
- return false;
-
$r = q("select * from source where src_channel_id = %d and ( src_xchan = '%s' || src_xchan = '*' ) limit 1",
intval($uid),
dbesc(($item['source_xchan']) ? $item['source_xchan'] : $item['owner_xchan'])
);
+
if(! $r)
return false;
- $x = q("select abook_their_perms from abook where abook_channel = %d and abook_xchan = '%s' limit 1",
+ $x = q("select abook_their_perms, abook_flags from abook where abook_channel = %d and abook_xchan = '%s' limit 1",
intval($uid),
dbesc($item['owner_xchan'])
);
+
if(! $x)
return false;
+
if(! ($x[0]['abook_their_perms'] & PERMS_A_REPUBLISH))
return false;
+ if($item['item_private'] && (! ($x[0]['abook_flags'] & ABOOK_FLAG_FEED)))
+ return false;
+
+
if($r[0]['src_channel_xchan'] === $item['owner_xchan'])
return false;
@@ -2741,7 +3080,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']) : '');
@@ -2852,7 +3191,7 @@ function mail_store($arr) {
* recursion.
*/
-function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) {
+function consume_feed($xml,$importer,&$contact,$pass = 0) {
require_once('library/simplepie/simplepie.inc');
@@ -2861,15 +3200,8 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0)
return;
}
- // Want to see this work as a content source for the matrix?
- // Read this: https://github.com/friendica/red/wiki/Service_Federation
-
$feed = new SimplePie();
$feed->set_raw_data($xml);
- if($datedir)
- $feed->enable_order_by_date(true);
- else
- $feed->enable_order_by_date(false);
$feed->init();
if($feed->error())
@@ -2879,7 +3211,6 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0)
// Check at the feed level for updated contact name and/or photo
-
// process any deleted entries
$del_entries = $feed->get_feed_tags(NAMESPACE_TOMB, 'deleted-entry');
@@ -2896,41 +3227,21 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0)
else
$when = datetime_convert('UTC','UTC','now','Y-m-d H:i:s');
}
+
+
if($deleted && is_array($contact)) {
-/* $r = q("SELECT `item`.*, `contact`.`self` FROM `item` left join `contact` on `item`.`contact-id` = `contact`.`id`
- WHERE `mid` = '%s' AND `item`.`uid` = %d AND `contact-id` = %d AND NOT `item`.`file` LIKE '%%[%%' LIMIT 1",
- dbesc($mid),
- intval($importer['channel_id']),
- intval($contact['id'])
+ $r = q("SELECT * from item where mid = '%s' and author_xchan = '%s' and uid = %d limit 1",
+ dbesc(base64url_encode($mid)),
+ dbesc($contact['xchan_hash']),
+ intval($importer['channel_id'])
);
-*/
- if(count($r)) {
- $item = $r[0];
- if(! $item['deleted'])
- logger('consume_feed: deleting item ' . $item['id'] . ' mid=' . $item['mid'], LOGGER_DEBUG);
+ if($r) {
+ $item = $r[0];
- if($item['mid'] == $item['parent_mid']) {
- $r = q("UPDATE `item` SET item_restrict = (item_restrict | %d), `edited` = '%s', `changed` = '%s',
- `body` = '', `title` = ''
- WHERE `parent_mid` = '%s' AND `uid` = %d",
- intval(ITEM_DELETED),
- dbesc($when),
- dbesc(datetime_convert()),
- dbesc($item['mid']),
- intval($importer['channel_id'])
- );
- }
- else {
- $r = q("UPDATE `item` SET item_restrict = ( item_restrict | %d ), `edited` = '%s', `changed` = '%s',
- `body` = '', `title` = ''
- WHERE `mid` = '%s' AND `uid` = %d LIMIT 1",
- intval(ITEM_DELETED),
- dbesc($when),
- dbesc(datetime_convert()),
- dbesc($mid),
- intval($importer['channel_id'])
- );
+ if(! ($item['item_restrict'] & ITEM_DELETED)) {
+ logger('consume_feed: deleting item ' . $item['id'] . ' mid=' . base64url_decode($item['mid']), LOGGER_DEBUG);
+ drop_item($item['id'],false);
}
}
}
@@ -2941,26 +3252,21 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0)
if($feed->get_item_quantity()) {
- logger('consume_feed: feed item count = ' . $feed->get_item_quantity());
-
- // in inverse date order
- if ($datedir)
- $items = array_reverse($feed->get_items());
- else
- $items = $feed->get_items();
+ logger('consume_feed: feed item count = ' . $feed->get_item_quantity(), LOGGER_DEBUG);
+ $items = $feed->get_items();
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('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) {
@@ -2971,23 +3277,33 @@ logger('consume_feed: processing ' . $item_id);
// Have we seen it? If not, import it.
- $item_id = $item->get_id();
- $datarray = get_atom_elements($feed,$item);
+ $item_id = base64url_encode($item->get_id());
+ $author = array();
+ $datarray = get_atom_elements($feed,$item,$author);
- if((! x($datarray,'author-name')) && ($contact['network'] != NETWORK_DFRN))
- $datarray['author-name'] = $contact['name'];
- if((! x($datarray,'author-link')) && ($contact['network'] != NETWORK_DFRN))
- $datarray['author-link'] = $contact['url'];
- if((! x($datarray,'author-avatar')) && ($contact['network'] != NETWORK_DFRN))
- $datarray['author-avatar'] = $contact['thumb'];
+ if(! x($author,'author_name'))
+ $author['author_name'] = $contact['xchan_name'];
+ if(! x($author,'author_link'))
+ $author['author_link'] = $contact['xchan_url'];
+ if(! x($author,'author_photo'))
+ $author['author_photo'] = $contact['xchan_photo_m'];
- if((! x($datarray,'author-name')) || (! x($datarray,'author-link'))) {
- logger('consume_feed: no author information! ' . print_r($datarray,true));
- continue;
+ $datarray['author_xchan'] = '';
+
+ if($author['author_link'] != $contact['xchan_url']) {
+ $x = import_author_unkown(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 `uid`, `edited`, `body` FROM `item` WHERE `mid` = '%s' AND `uid` = %d LIMIT 1",
+
+ $r = q("SELECT edited FROM item WHERE mid = '%s' AND uid = %d LIMIT 1",
dbesc($item_id),
intval($importer['channel_id'])
);
@@ -2995,71 +3311,26 @@ logger('consume_feed: processing ' . $item_id);
// Update content if 'updated' changes
if($r) {
- if((x($datarray,'edited') !== false) && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) {
+ if((x($datarray,'edited') !== false)
+ && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) {
// do not accept (ignore) an earlier edit than one we currently have.
if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited'])
continue;
- $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `edited` = '%s' WHERE `mid` = '%s' AND `uid` = %d LIMIT 1",
- dbesc($datarray['title']),
- dbesc($datarray['body']),
- dbesc(datetime_convert('UTC','UTC',$datarray['edited'])),
- dbesc($item_id),
- intval($importer['channel_id'])
- );
+ update_feed_item($importer['channel_id'],$datarray);
}
-
continue;
}
-
$datarray['parent_mid'] = $parent_mid;
$datarray['uid'] = $importer['channel_id'];
- $datarray['contact-id'] = $contact['id'];
- if((activity_match($datarray['verb'],ACTIVITY_LIKE)) || (activity_match($datarray['verb'],ACTIVITY_DISLIKE))) {
- $datarray['type'] = 'activity';
- $datarray['gravity'] = GRAVITY_LIKE;
- // only one like or dislike per person
- $r = q("select id from item where uid = %d and `contact-id` = %d and verb ='%s' and deleted = 0 and (`parent_mid` = '%s' OR `thr_parent` = '%s') limit 1",
- intval($datarray['uid']),
- intval($datarray['contact-id']),
- dbesc($datarray['verb']),
- dbesc($parent_mid),
- dbesc($parent_mid)
- );
- if($r && count($r))
- continue;
- }
- if(($datarray['verb'] === ACTIVITY_TAG) && ($datarray['obj_type'] === ACTIVITY_OBJ_TAGTERM)) {
- $xo = parse_xml_string($datarray['object'],false);
- $xt = parse_xml_string($datarray['target'],false);
- if($xt->type == ACTIVITY_OBJ_NOTE) {
- $r = q("select * from item where `mid` = '%s' AND `uid` = %d limit 1",
- dbesc($xt->id),
- intval($importer['channel_id'])
- );
- if(! count($r))
- continue;
- // extract tag, if not duplicate, add to parent item
- if($xo->id && $xo->content) {
- $newtag = '#[zrl=' . $xo->id . ']'. $xo->content . '[/zrl]';
- if(! (stristr($r[0]['tag'],$newtag))) {
- q("UPDATE item SET tag = '%s' WHERE id = %d LIMIT 1",
- dbesc($r[0]['tag'] . (strlen($r[0]['tag']) ? ',' : '') . $newtag),
- intval($r[0]['id'])
- );
- }
- }
- }
- }
+ logger('consume_feed: ' . print_r($datarray,true),LOGGER_DATA);
-logger('consume_feed: ' . print_r($datarray,true));
-
-// $xx = item_store($datarray);
+ $xx = item_store($datarray);
$r = $xx['item_id'];
continue;
}
@@ -3068,48 +3339,40 @@ logger('consume_feed: ' . print_r($datarray,true));
// Head post of a conversation. Have we seen it? If not, import it.
- $item_id = $item->get_id();
-
- $datarray = get_atom_elements($feed,$item);
+ $item_id = base64url_encode($item->get_id());
+ $author = array();
+ $datarray = get_atom_elements($feed,$item,$author);
if(is_array($contact)) {
- if((! x($datarray,'author-name')) && ($contact['network'] != NETWORK_DFRN))
- $datarray['author-name'] = $contact['name'];
- if((! x($datarray,'author-link')) && ($contact['network'] != NETWORK_DFRN))
- $datarray['author-link'] = $contact['url'];
- if((! x($datarray,'author-avatar')) && ($contact['network'] != NETWORK_DFRN))
- $datarray['author-avatar'] = $contact['thumb'];
+ if(! x($author,'author_name'))
+ $author['author_name'] = $contact['xchan_name'];
+ if(! x($author,'author_link'))
+ $author['author_link'] = $contact['xchan_url'];
+ if(! x($author,'author_photo'))
+ $author['author_photo'] = $contact['xchan_photo_m'];
}
- if((! x($datarray,'author-name')) || (! x($datarray,'author-link'))) {
- logger('consume_feed: no author information! ' . print_r($datarray,true));
+ if((! x($author,'author_name')) || (! x($author,'author_link'))) {
+ logger('consume_feed: no author information! ' . print_r($author,true));
continue;
}
- // special handling for events
-
- if((x($datarray,'obj_type')) && ($datarray['obj_type'] === ACTIVITY_OBJ_EVENT)) {
- $ev = bbtoevent($datarray['body']);
- if(x($ev,'desc') && x($ev,'start')) {
- $ev['uid'] = $importer['channel_id'];
- $ev['mid'] = $item_id;
- $ev['edited'] = $datarray['edited'];
- $ev['private'] = $datarray['private'];
-
- if(is_array($contact))
- $ev['cid'] = $contact['id'];
- $r = q("SELECT * FROM `event` WHERE `mid` = '%s' AND `uid` = %d LIMIT 1",
- dbesc($item_id),
- intval($importer['channel_id'])
- );
- if(count($r))
- $ev['id'] = $r[0]['id'];
-// $xyz = event_store($ev);
- 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'];
+
- $r = q("SELECT `uid`, `edited`, `body` FROM `item` WHERE `mid` = '%s' AND `uid` = %d LIMIT 1",
+ $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'])
);
@@ -3117,81 +3380,36 @@ logger('consume_feed: ' . print_r($datarray,true));
// Update content if 'updated' changes
if($r) {
- if((x($datarray,'edited') !== false) && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) {
+ if((x($datarray,'edited') !== false)
+ && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) {
// do not accept (ignore) an earlier edit than one we currently have.
if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited'])
continue;
- $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `edited` = '%s' WHERE `mid` = '%s' AND `uid` = %d LIMIT 1",
- dbesc($datarray['title']),
- dbesc($datarray['body']),
- dbesc(datetime_convert('UTC','UTC',$datarray['edited'])),
- dbesc($item_id),
- intval($importer['channel_id'])
- );
+ update_feed_item($importer['channel_id'],$datarray);
}
continue;
}
- if(activity_match($datarray['verb'],ACTIVITY_FOLLOW)) {
- logger('consume-feed: New follower');
- new_follower($importer,$contact,$datarray,$item);
- return;
- }
- if(activity_match($datarray['verb'],ACTIVITY_UNFOLLOW)) {
- lose_follower($importer,$contact,$datarray,$item);
- return;
- }
-
- if(activity_match($datarray['verb'],ACTIVITY_REQ_FRIEND)) {
- logger('consume-feed: New friend request');
- new_follower($importer,$contact,$datarray,$item,true);
- return;
- }
- if(activity_match($datarray['verb'],ACTIVITY_UNFRIEND)) {
- lose_sharer($importer,$contact,$datarray,$item);
- return;
- }
-
-
-// if(! is_array($contact))
-// return;
-
-
- // This is my contact on another system, but it's really me.
- // Turn this into a wall post.
-
- if($contact['remote_self']) {
- $datarray['wall'] = 1;
- }
$datarray['parent_mid'] = $item_id;
$datarray['uid'] = $importer['channel_id'];
- $datarray['contact-id'] = $contact['id'];
- if(! link_compare($datarray['owner-link'],$contact['url'])) {
- // The item owner info is not our contact. It's OK and is to be expected if this is a tgroup delivery,
- // but otherwise there's a possible data mixup on the sender's system.
- // the tgroup delivery code called from item_store will correct it if it's a forum,
- // but we're going to unconditionally correct it here so that the post will always be owned by our contact.
+ if(! link_compare($author['owner_link'],$contact['xchan_url'])) {
logger('consume_feed: Correcting item owner.', LOGGER_DEBUG);
- $datarray['owner-name'] = $contact['name'];
- $datarray['owner-link'] = $contact['url'];
- $datarray['owner-avatar'] = $contact['thumb'];
+ $author['owner_name'] = $contact['name'];
+ $author['owner_link'] = $contact['url'];
+ $author['owner_avatar'] = $contact['thumb'];
}
- // We've allowed "followers" to reach this point so we can decide if they are
- // posting an @-tag delivery, which followers are allowed to do for certain
- // page types. Now that we've parsed the post, let's check if it is legit. Otherwise ignore it.
+ logger('consume_feed: author ' . print_r($author,true),LOGGER_DEBUG);
- if(($contact['rel'] == CONTACT_IS_FOLLOWER) && (! tgroup_check($importer['channel_id'],$datarray)))
- continue;
-logger('consume_feed: ' . print_r($datarray,true));
+ logger('consume_feed: ' . print_r($datarray,true),LOGGER_DATA);
-// $xx = item_store($datarray);
+ $xx = item_store($datarray);
$r = $xx['item_id'];
continue;
@@ -3202,6 +3420,34 @@ logger('consume_feed: ' . print_r($datarray,true));
}
+function update_feed_item($uid,$datarray) {
+
+ logger('update_feed_item: not implemented! ' . $uid . ' ' . print_r($datarray,true), LOGGER_DATA);
+
+}
+
+
+function handle_feed($uid,$abook_id,$url) {
+
+ require_once('include/Contact.php');
+ $channel = channelx_by_n($uid);
+ if(! $channel)
+ return;
+ $x = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_channel = %d limit 1",
+ dbesc($abook_id),
+ intval($uid)
+ );
+
+ $recurse = 0;
+ $z = z_fetch_url($url,false,$recurse,array('novalidate' => true));
+
+//logger('handle_feed:' . print_r($z,true));
+
+ if($z['success']) {
+ consume_feed($z['body'],$channel,$x[0],0);
+ consume_feed($z['body'],$channel,$x[0],1);
+ }
+}
function atom_author($tag,$name,$uri,$h,$w,$type,$photo) {
@@ -3914,7 +4160,7 @@ function zot_feed($uid,$observer_xchan,$mindate) {
$result = array();
$mindate = datetime_convert('UTC','UTC',$mindate);
if(! $mindate)
- $mindate = '0000-00-00 00:00:00';
+ $mindate = NULL_DATE;
$mindate = dbesc($mindate);
@@ -3930,8 +4176,8 @@ function zot_feed($uid,$observer_xchan,$mindate) {
$sql_extra = item_permissions_sql($uid);
}
- if($mindate != '0000-00-00 00:00:00') {
- $sql_extra .= " and created > '$mindate' ";
+ if($mindate != NULL_DATE) {
+ $sql_extra .= " and ( created > '$mindate' or edited > '$mindate' ) ";
$limit = "";
}
else
@@ -4215,6 +4461,9 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C
$parents_str = ids_to_querystr($r,'item_id');
+ if($arr['top'])
+ $sql_extra = ' and id = parent ' . $sql_extra;
+
$items = q("SELECT item.*, item.id AS item_id FROM item
WHERE $item_uids $item_restrict
AND item.parent IN ( %s )
diff --git a/include/message.php b/include/message.php
index 607166ec9..96c3532c1 100644
--- a/include/message.php
+++ b/include/message.php
@@ -23,7 +23,7 @@ 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);
diff --git a/include/nav.php b/include/nav.php
index 8133ecf67..799faf5ce 100644
--- a/include/nav.php
+++ b/include/nav.php
@@ -38,6 +38,14 @@ 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 ",
+ intval(get_account_id()),
+ intval(PAGE_REMOVED)
+ );
+
+
+
+
}
elseif(remote_user())
$observer = $a->get_observer();
@@ -78,6 +86,11 @@ EOT;
$userinfo = null;
if(local_user()) {
+
+
+ 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'));
// user menu
@@ -193,7 +206,7 @@ EOT;
$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 Select'), "", t('Manage Your Channels'));
+ $nav['manage'] = array('manage', t('Channel Manager'), "", t('Manage Your Channels'));
$nav['settings'] = array('settings', t('Settings'),"", t('Account/Channel Settings'));
diff --git a/include/network.php b/include/network.php
index 66bba5b38..0191f203d 100644
--- a/include/network.php
+++ b/include/network.php
@@ -132,6 +132,29 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) {
}
+/**
+ * @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()) {
@@ -280,7 +303,7 @@ function xml_status($st, $message = '') {
function http_status_exit($val,$msg = '') {
- $err = '';
+ $err = '';
if($val >= 400)
$msg = (($msg) ? $msg : 'Error');
if($val >= 200 && $val < 300)
@@ -298,138 +321,43 @@ function http_status_exit($val,$msg = '') {
function convert_xml_element_to_array($xml_element, &$recursion_depth=0) {
- // If we're getting too deep, bail out
- if ($recursion_depth > 512) {
- return(null);
- }
-
- if (!is_string($xml_element) &&
- !is_array($xml_element) &&
- (get_class($xml_element) == 'SimpleXMLElement')) {
- $xml_element_copy = $xml_element;
- $xml_element = get_object_vars($xml_element);
- }
-
- if (is_array($xml_element)) {
- $result_array = array();
- if (count($xml_element) <= 0) {
- return (trim(strval($xml_element_copy)));
- }
-
- foreach($xml_element as $key=>$value) {
-
- $recursion_depth++;
- $result_array[strtolower($key)] =
- convert_xml_element_to_array($value, $recursion_depth);
- $recursion_depth--;
- }
- if ($recursion_depth == 0) {
- $temp_array = $result_array;
- $result_array = array(
- strtolower($xml_element_copy->getName()) => $temp_array,
- );
- }
-
- return ($result_array);
-
- } else {
- return (trim(strval($xml_element)));
- }
-}
-
-// Given an email style address, perform webfinger lookup and
-// return the resulting DFRN profile URL, or if no DFRN profile URL
-// is located, returns an OStatus subscription template (prefixed
-// with the string 'stat:' to identify it as on OStatus template).
-// If this isn't an email style address just return $s.
-// Return an empty string if email-style addresses but webfinger fails,
-// or if the resultant personal XRD doesn't contain a supported
-// subscription/friend-request attribute.
-
-// amended 7/9/2011 to return an hcard which could save potentially loading
-// a lengthy content page to scrape dfrn attributes
-
-
-function webfinger_dfrn($s,&$hcard) {
- if(! strstr($s,'@')) {
- return $s;
- }
- $profile_link = '';
-
- $links = webfinger($s);
- logger('webfinger_dfrn: ' . $s . ':' . print_r($links,true), LOGGER_DATA);
- if(count($links)) {
- foreach($links as $link) {
- if($link['@attributes']['rel'] === NAMESPACE_DFRN)
- $profile_link = $link['@attributes']['href'];
- if($link['@attributes']['rel'] === NAMESPACE_OSTATUSSUB)
- $profile_link = 'stat:' . $link['@attributes']['template'];
- if($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard')
- $hcard = $link['@attributes']['href'];
+ // If we're getting too deep, bail out
+ if ($recursion_depth > 512) {
+ return(null);
}
- }
- return $profile_link;
-}
-// Given an email style address, perform webfinger lookup and
-// return the array of link attributes from the personal XRD file.
-// On error/failure return an empty array.
-
-
-
-function webfinger($s, $debug = false) {
- $host = '';
- if(strstr($s,'@')) {
- $host = substr($s,strpos($s,'@') + 1);
- }
- if(strlen($host)) {
- $tpl = fetch_lrdd_template($host);
- logger('webfinger: lrdd template: ' . $tpl);
- if(strlen($tpl)) {
- $pxrd = str_replace('{uri}', urlencode('acct:' . $s), $tpl);
- logger('webfinger: pxrd: ' . $pxrd);
- $links = fetch_xrd_links($pxrd);
- if(! count($links)) {
- // try with double slashes
- $pxrd = str_replace('{uri}', urlencode('acct://' . $s), $tpl);
- logger('webfinger: pxrd: ' . $pxrd);
- $links = fetch_xrd_links($pxrd);
- }
- return $links;
+ if (!is_string($xml_element) &&
+ !is_array($xml_element) &&
+ (get_class($xml_element) == 'SimpleXMLElement')) {
+ $xml_element_copy = $xml_element;
+ $xml_element = get_object_vars($xml_element);
}
- }
- return array();
-}
-
+ if (is_array($xml_element)) {
+ $result_array = array();
+ if (count($xml_element) <= 0) {
+ return (trim(strval($xml_element_copy)));
+ }
+ foreach($xml_element as $key=>$value) {
-// Given a host name, locate the LRDD template from that
-// host. Returns the LRDD template or an empty string on
-// error/failure.
+ $recursion_depth++;
+ $result_array[strtolower($key)] =
+ convert_xml_element_to_array($value, $recursion_depth);
+ $recursion_depth--;
+ }
+ if ($recursion_depth == 0) {
+ $temp_array = $result_array;
+ $result_array = array(
+ strtolower($xml_element_copy->getName()) => $temp_array,
+ );
+ }
+ return ($result_array);
-function fetch_lrdd_template($host) {
- $tpl = '';
-
- $url1 = 'https://' . $host . '/.well-known/host-meta' ;
- $url2 = 'http://' . $host . '/.well-known/host-meta' ;
- $links = fetch_xrd_links($url1);
- logger('fetch_lrdd_template from: ' . $url1);
- logger('template (https): ' . print_r($links,true));
- if(! count($links)) {
- logger('fetch_lrdd_template from: ' . $url2);
- $links = fetch_xrd_links($url2);
- logger('template (http): ' . print_r($links,true));
- }
- if(count($links)) {
- foreach($links as $link)
- if($link['@attributes']['rel'] && $link['@attributes']['rel'] === 'lrdd')
- $tpl = $link['@attributes']['template'];
- }
- if(! strpos($tpl,'{uri}'))
- $tpl = '';
- return $tpl;
+ } else {
+ return (trim(strval($xml_element)));
+ }
}
// Take a URL from the wild, prepend http:// if necessary
@@ -679,35 +607,35 @@ function scale_external_images($s, $include_link = true, $scale_replace = false)
*/
function xml2array($contents, $namespaces = true, $get_attributes=1, $priority = 'attribute') {
- if(!$contents) return array();
+ if(!$contents) return array();
- if(!function_exists('xml_parser_create')) {
- logger('xml2array: parser function missing');
- return array();
- }
+ if(!function_exists('xml_parser_create')) {
+ logger('xml2array: parser function missing');
+ return array();
+ }
libxml_use_internal_errors(true);
libxml_clear_errors();
if($namespaces)
- $parser = @xml_parser_create_ns("UTF-8",':');
+ $parser = @xml_parser_create_ns("UTF-8",':');
else
- $parser = @xml_parser_create();
+ $parser = @xml_parser_create();
if(! $parser) {
logger('xml2array: xml_parser_create: no resource');
return array();
}
- xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8");
+ xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8");
// http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss
- xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
- xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
- @xml_parse_into_struct($parser, trim($contents), $xml_values);
- @xml_parser_free($parser);
+ xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
+ xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
+ @xml_parse_into_struct($parser, trim($contents), $xml_values);
+ @xml_parser_free($parser);
- if(! $xml_values) {
+ if(! $xml_values) {
logger('xml2array: libxml: parse error: ' . $contents, LOGGER_DATA);
foreach(libxml_get_errors() as $err)
logger('libxml: parse: ' . $err->code . " at " . $err->line . ":" . $err->column . " : " . $err->message, LOGGER_DATA);
@@ -715,40 +643,40 @@ function xml2array($contents, $namespaces = true, $get_attributes=1, $priority =
return;
}
- //Initializations
- $xml_array = array();
- $parents = array();
- $opened_tags = array();
- $arr = array();
-
- $current = &$xml_array; // Reference
-
- // Go through the tags.
- $repeated_tag_index = array(); // Multiple tags with same name will be turned into an array
- foreach($xml_values as $data) {
- unset($attributes,$value); // Remove existing values, or there will be trouble
-
- // This command will extract these variables into the foreach scope
- // tag(string), type(string), level(int), attributes(array).
- extract($data); // We could use the array by itself, but this cooler.
-
- $result = array();
- $attributes_data = array();
-
- if(isset($value)) {
- if($priority == 'tag') $result = $value;
- else $result['value'] = $value; // Put the value in a assoc array if we are in the 'Attribute' mode
- }
-
- //Set the attributes too.
- if(isset($attributes) and $get_attributes) {
- foreach($attributes as $attr => $val) {
- if($priority == 'tag') $attributes_data[$attr] = $val;
- else $result['@attributes'][$attr] = $val; // Set all the attributes in a array called 'attr'
- }
- }
-
- // See tag status and do the needed.
+ //Initializations
+ $xml_array = array();
+ $parents = array();
+ $opened_tags = array();
+ $arr = array();
+
+ $current = &$xml_array; // Reference
+
+ // Go through the tags.
+ $repeated_tag_index = array(); // Multiple tags with same name will be turned into an array
+ foreach($xml_values as $data) {
+ unset($attributes,$value); // Remove existing values, or there will be trouble
+
+ // This command will extract these variables into the foreach scope
+ // tag(string), type(string), level(int), attributes(array).
+ extract($data); // We could use the array by itself, but this cooler.
+
+ $result = array();
+ $attributes_data = array();
+
+ if(isset($value)) {
+ if($priority == 'tag') $result = $value;
+ else $result['value'] = $value; // Put the value in a assoc array if we are in the 'Attribute' mode
+ }
+
+ //Set the attributes too.
+ if(isset($attributes) and $get_attributes) {
+ foreach($attributes as $attr => $val) {
+ if($priority == 'tag') $attributes_data[$attr] = $val;
+ else $result['@attributes'][$attr] = $val; // Set all the attributes in a array called 'attr'
+ }
+ }
+
+ // See tag status and do the needed.
if($namespaces && strpos($tag,':')) {
$namespc = substr($tag,0,strrpos($tag,':'));
$tag = strtolower(substr($tag,strlen($namespc)+1));
@@ -757,80 +685,80 @@ function xml2array($contents, $namespaces = true, $get_attributes=1, $priority =
$tag = strtolower($tag);
if($type == "open") { // The starting of the tag '<tag>'
- $parent[$level-1] = &$current;
- if(!is_array($current) or (!in_array($tag, array_keys($current)))) { // Insert New tag
- $current[$tag] = $result;
- if($attributes_data) $current[$tag. '_attr'] = $attributes_data;
- $repeated_tag_index[$tag.'_'.$level] = 1;
-
- $current = &$current[$tag];
-
- } else { // There was another element with the same tag name
-
- if(isset($current[$tag][0])) { // If there is a 0th element it is already an array
- $current[$tag][$repeated_tag_index[$tag.'_'.$level]] = $result;
- $repeated_tag_index[$tag.'_'.$level]++;
- } else { // This section will make the value an array if multiple tags with the same name appear together
- $current[$tag] = array($current[$tag],$result); // This will combine the existing item and the new item together to make an array
- $repeated_tag_index[$tag.'_'.$level] = 2;
-
- if(isset($current[$tag.'_attr'])) { // The attribute of the last(0th) tag must be moved as well
- $current[$tag]['0_attr'] = $current[$tag.'_attr'];
- unset($current[$tag.'_attr']);
- }
-
- }
- $last_item_index = $repeated_tag_index[$tag.'_'.$level]-1;
- $current = &$current[$tag][$last_item_index];
- }
-
- } elseif($type == "complete") { // Tags that ends in 1 line '<tag />'
- //See if the key is already taken.
- if(!isset($current[$tag])) { //New Key
- $current[$tag] = $result;
- $repeated_tag_index[$tag.'_'.$level] = 1;
- if($priority == 'tag' and $attributes_data) $current[$tag. '_attr'] = $attributes_data;
-
- } else { // If taken, put all things inside a list(array)
- if(isset($current[$tag][0]) and is_array($current[$tag])) { // If it is already an array...
-
- // ...push the new element into that array.
- $current[$tag][$repeated_tag_index[$tag.'_'.$level]] = $result;
-
- if($priority == 'tag' and $get_attributes and $attributes_data) {
- $current[$tag][$repeated_tag_index[$tag.'_'.$level] . '_attr'] = $attributes_data;
- }
- $repeated_tag_index[$tag.'_'.$level]++;
-
- } else { // If it is not an array...
- $current[$tag] = array($current[$tag],$result); //...Make it an array using using the existing value and the new value
- $repeated_tag_index[$tag.'_'.$level] = 1;
- if($priority == 'tag' and $get_attributes) {
- if(isset($current[$tag.'_attr'])) { // The attribute of the last(0th) tag must be moved as well
-
- $current[$tag]['0_attr'] = $current[$tag.'_attr'];
- unset($current[$tag.'_attr']);
- }
-
- if($attributes_data) {
- $current[$tag][$repeated_tag_index[$tag.'_'.$level] . '_attr'] = $attributes_data;
- }
- }
- $repeated_tag_index[$tag.'_'.$level]++; // 0 and 1 indexes are already taken
- }
- }
-
- } elseif($type == 'close') { // End of tag '</tag>'
- $current = &$parent[$level-1];
- }
- }
-
- return($xml_array);
+ $parent[$level-1] = &$current;
+ if(!is_array($current) or (!in_array($tag, array_keys($current)))) { // Insert New tag
+ $current[$tag] = $result;
+ if($attributes_data) $current[$tag. '_attr'] = $attributes_data;
+ $repeated_tag_index[$tag.'_'.$level] = 1;
+
+ $current = &$current[$tag];
+
+ } else { // There was another element with the same tag name
+
+ if(isset($current[$tag][0])) { // If there is a 0th element it is already an array
+ $current[$tag][$repeated_tag_index[$tag.'_'.$level]] = $result;
+ $repeated_tag_index[$tag.'_'.$level]++;
+ } else { // This section will make the value an array if multiple tags with the same name appear together
+ $current[$tag] = array($current[$tag],$result); // This will combine the existing item and the new item together to make an array
+ $repeated_tag_index[$tag.'_'.$level] = 2;
+
+ if(isset($current[$tag.'_attr'])) { // The attribute of the last(0th) tag must be moved as well
+ $current[$tag]['0_attr'] = $current[$tag.'_attr'];
+ unset($current[$tag.'_attr']);
+ }
+
+ }
+ $last_item_index = $repeated_tag_index[$tag.'_'.$level]-1;
+ $current = &$current[$tag][$last_item_index];
+ }
+
+ } elseif($type == "complete") { // Tags that ends in 1 line '<tag />'
+ //See if the key is already taken.
+ if(!isset($current[$tag])) { //New Key
+ $current[$tag] = $result;
+ $repeated_tag_index[$tag.'_'.$level] = 1;
+ if($priority == 'tag' and $attributes_data) $current[$tag. '_attr'] = $attributes_data;
+
+ } else { // If taken, put all things inside a list(array)
+ if(isset($current[$tag][0]) and is_array($current[$tag])) { // If it is already an array...
+
+ // ...push the new element into that array.
+ $current[$tag][$repeated_tag_index[$tag.'_'.$level]] = $result;
+
+ if($priority == 'tag' and $get_attributes and $attributes_data) {
+ $current[$tag][$repeated_tag_index[$tag.'_'.$level] . '_attr'] = $attributes_data;
+ }
+ $repeated_tag_index[$tag.'_'.$level]++;
+
+ } else { // If it is not an array...
+ $current[$tag] = array($current[$tag],$result); //...Make it an array using using the existing value and the new value
+ $repeated_tag_index[$tag.'_'.$level] = 1;
+ if($priority == 'tag' and $get_attributes) {
+ if(isset($current[$tag.'_attr'])) { // The attribute of the last(0th) tag must be moved as well
+
+ $current[$tag]['0_attr'] = $current[$tag.'_attr'];
+ unset($current[$tag.'_attr']);
+ }
+
+ if($attributes_data) {
+ $current[$tag][$repeated_tag_index[$tag.'_'.$level] . '_attr'] = $attributes_data;
+ }
+ }
+ $repeated_tag_index[$tag.'_'.$level]++; // 0 and 1 indexes are already taken
+ }
+ }
+
+ } elseif($type == 'close') { // End of tag '</tag>'
+ $current = &$parent[$level-1];
+ }
+ }
+
+ return($xml_array);
}
function email_header_encode($in_str, $charset = 'UTF-8') {
- $out_str = $in_str;
+ $out_str = $in_str;
$need_to_convert = false;
for($x = 0; $x < strlen($in_str); $x ++) {
@@ -842,42 +770,42 @@ function email_header_encode($in_str, $charset = 'UTF-8') {
if(! $need_to_convert)
return $in_str;
- if ($out_str && $charset) {
-
- // define start delimimter, end delimiter and spacer
- $end = "?=";
- $start = "=?" . $charset . "?B?";
- $spacer = $end . "\r\n " . $start;
-
- // determine length of encoded text within chunks
- // and ensure length is even
- $length = 75 - strlen($start) - strlen($end);
-
- /*
- [EDIT BY danbrown AT php DOT net: The following
- is a bugfix provided by (gardan AT gmx DOT de)
- on 31-MAR-2005 with the following note:
- "This means: $length should not be even,
- but divisible by 4. The reason is that in
- base64-encoding 3 8-bit-chars are represented
- by 4 6-bit-chars. These 4 chars must not be
- split between two encoded words, according
- to RFC-2047.
- */
- $length = $length - ($length % 4);
-
- // encode the string and split it into chunks
- // with spacers after each chunk
- $out_str = base64_encode($out_str);
- $out_str = chunk_split($out_str, $length, $spacer);
-
- // remove trailing spacer and
- // add start and end delimiters
- $spacer = preg_quote($spacer,'/');
- $out_str = preg_replace("/" . $spacer . "$/", "", $out_str);
- $out_str = $start . $out_str . $end;
- }
- return $out_str;
+ if ($out_str && $charset) {
+
+ // define start delimimter, end delimiter and spacer
+ $end = "?=";
+ $start = "=?" . $charset . "?B?";
+ $spacer = $end . "\r\n " . $start;
+
+ // determine length of encoded text within chunks
+ // and ensure length is even
+ $length = 75 - strlen($start) - strlen($end);
+
+ /*
+ [EDIT BY danbrown AT php DOT net: The following
+ is a bugfix provided by (gardan AT gmx DOT de)
+ on 31-MAR-2005 with the following note:
+ "This means: $length should not be even,
+ but divisible by 4. The reason is that in
+ base64-encoding 3 8-bit-chars are represented
+ by 4 6-bit-chars. These 4 chars must not be
+ split between two encoded words, according
+ to RFC-2047.
+ */
+ $length = $length - ($length % 4);
+
+ // encode the string and split it into chunks
+ // with spacers after each chunk
+ $out_str = base64_encode($out_str);
+ $out_str = chunk_split($out_str, $length, $spacer);
+
+ // remove trailing spacer and
+ // add start and end delimiters
+ $spacer = preg_quote($spacer,'/');
+ $out_str = preg_replace("/" . $spacer . "$/", "", $out_str);
+ $out_str = $start . $out_str . $end;
+ }
+ return $out_str;
}
function email_send($addr, $subject, $headers, $item) {
@@ -888,7 +816,7 @@ function email_send($addr, $subject, $headers, $item) {
$part = uniqid("", true);
- $html = prepare_body($item);
+ $html = prepare_body($item);
$headers .= "Mime-Version: 1.0\n";
$headers .= 'Content-Type: multipart/alternative; boundary="=_'.$part.'"'."\n\n";
@@ -912,3 +840,651 @@ function email_send($addr, $subject, $headers, $item) {
logger('notifier: email delivery to ' . $addr);
mail($addr, $subject, $body, $headers);
}
+
+
+
+function discover_by_url($url,$arr = null) {
+ require_once('library/HTML5/Parser.php');
+
+ $x = scrape_feed($url);
+ if(! $x) {
+ if(! $arr)
+ return false;
+ $network = (($arr['network']) ? $arr['network'] : 'unknown');
+ $name = (($arr['name']) ? $arr['name'] : 'unknown');
+ $photo = (($arr['photo']) ? $arr['photo'] : '');
+ $addr = (($arr['addr']) ? $arr['addr'] : '');
+ $guid = $url;
+ }
+
+ $profile = $url;
+
+ logger('scrape_feed results: ' . print_r($x,true));
+
+ if($x['feed_atom'])
+ $guid = $x['feed_atom'];
+ if($x['feed_rss'])
+ $guid = $x['feed_rss'];
+
+ if(! $guid)
+ return false;
+
+
+ // try and discover stuff from the feeed
+
+ require_once('library/simplepie/simplepie.inc');
+ $feed = new SimplePie();
+ $level = 0;
+ $x = z_fetch_url($guid,false,$level,array('novalidate' => true));
+ if(! $x['success']) {
+ logger('probe_url: feed fetch failed for ' . $poll);
+ return false;
+ }
+ $xml = $x['body'];
+ logger('probe_url: fetch feed: ' . $guid . ' returns: ' . $xml, LOGGER_DATA);
+ logger('probe_url: scrape_feed: headers: ' . $x['header'], LOGGER_DATA);
+
+ // Don't try and parse an empty string
+ $feed->set_raw_data(($xml) ? $xml : '<?xml version="1.0" encoding="utf-8" ?><xml></xml>');
+
+ $feed->init();
+ if($feed->error())
+ logger('probe_url: scrape_feed: Error parsing XML: ' . $feed->error());
+
+ $name = unxmlify(trim($feed->get_title()));
+ $photo = $feed->get_image_url();
+ $author = $feed->get_author();
+
+ if($author) {
+ if(! $name)
+ $name = unxmlify(trim($author->get_name()));
+ if(! $name) {
+ $name = trim(unxmlify($author->get_email()));
+ if(strpos($name,'@') !== false)
+ $name = substr($name,0,strpos($name,'@'));
+ }
+ if(! $profile && $author->get_link())
+ $profile = trim(unxmlify($author->get_link()));
+ if(! $photo) {
+ $rawtags = $feed->get_feed_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author');
+ if($rawtags) {
+ $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10];
+ if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo'))
+ $photo = $elems['link'][0]['attribs']['']['href'];
+ }
+ }
+ }
+ else {
+ $item = $feed->get_item(0);
+ if($item) {
+ $author = $item->get_author();
+ if($author) {
+ if(! $name) {
+ $name = trim(unxmlify($author->get_name()));
+ if(! $name)
+ $name = trim(unxmlify($author->get_email()));
+ if(strpos($name,'@') !== false)
+ $name = substr($name,0,strpos($name,'@'));
+ }
+ if(! $profile && $author->get_link())
+ $profile = trim(unxmlify($author->get_link()));
+ }
+ if(! $photo) {
+ $rawmedia = $item->get_item_tags('http://search.yahoo.com/mrss/','thumbnail');
+ if($rawmedia && $rawmedia[0]['attribs']['']['url'])
+ $photo = unxmlify($rawmedia[0]['attribs']['']['url']);
+ }
+ if(! $photo) {
+ $rawtags = $item->get_item_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author');
+ if($rawtags) {
+ $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10];
+ if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo'))
+ $photo = $elems['link'][0]['attribs']['']['href'];
+ }
+ }
+ }
+ }
+ if($poll === $profile)
+ $lnk = $feed->get_permalink();
+ if(isset($lnk) && strlen($lnk))
+ $profile = $lnk;
+
+ if(! $network) {
+ $network = 'rss';
+ }
+
+ if(! $name)
+ $name = notags($feed->get_description());
+
+ if(! $guid)
+ return false;
+
+ $r = q("select * from xchan where xchan_hash = '%s' limit 1",
+ dbesc($guid)
+ );
+ if($r)
+ return true;
+
+ if(! $photo)
+ $photo = z_root() . '/images/rss_icon.png';
+
+ $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($guid),
+ dbesc($guid),
+ dbesc($pubkey),
+ dbesc($addr),
+ dbesc($profile),
+ dbesc($name),
+ dbesc($network),
+ dbesc(z_root()),
+ dbesc(datetime_convert())
+ );
+
+ $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",
+ dbesc(datetime_convert()),
+ dbesc($photos[0]),
+ dbesc($photos[1]),
+ dbesc($photos[2]),
+ dbesc($photos[3]),
+ dbesc($guid)
+ );
+ return true;
+
+}
+
+function discover_by_webbie($webbie) {
+ require_once('library/HTML5/Parser.php');
+
+ $webbie = strtolower($webbie);
+
+ $x = webfinger_rfc7033($webbie);
+ if($x && array_key_exists('links',$x) && $x['links']) {
+ foreach($x['links'] as $link) {
+ if(array_key_exists('rel',$link) && $link['rel'] == 'http://purl.org/zot/protocol') {
+ logger('discover_by_webbie: zot found for ' . $webbie, LOGGER_DEBUG);
+ $z = z_fetch_url($link['href']);
+ if($z['success']) {
+ $j = json_decode($z['body'],true);
+ $i = import_xchan($j);
+ return true;
+ }
+ }
+ }
+ }
+
+ $result = array();
+ $network = null;
+ $diaspora = false;
+
+ $diaspora_base = '';
+ $diaspora_guid = '';
+ $diaspora_key = '';
+ $dfrn = false;
+
+ $x = old_webfinger($webbie);
+ if($x) {
+ logger('old_webfinger: ' . print_r($x,true));
+ foreach($x as $link) {
+ if($link['@attributes']['rel'] === NAMESPACE_DFRN)
+ $dfrn = unamp($link['@attributes']['href']);
+ if($link['@attributes']['rel'] === 'salmon')
+ $notify = unamp($link['@attributes']['href']);
+ if($link['@attributes']['rel'] === NAMESPACE_FEED)
+ $poll = unamp($link['@attributes']['href']);
+ if($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard')
+ $hcard = unamp($link['@attributes']['href']);
+ if($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page')
+ $profile = unamp($link['@attributes']['href']);
+ if($link['@attributes']['rel'] === 'http://portablecontacts.net/spec/1.0')
+ $poco = unamp($link['@attributes']['href']);
+ if($link['@attributes']['rel'] === 'http://joindiaspora.com/seed_location') {
+ $diaspora_base = unamp($link['@attributes']['href']);
+ $diaspora = true;
+ }
+ if($link['@attributes']['rel'] === 'http://joindiaspora.com/guid') {
+ $diaspora_guid = unamp($link['@attributes']['href']);
+ $diaspora = true;
+ }
+ if($link['@attributes']['rel'] === 'diaspora-public-key') {
+ $diaspora_key = base64_decode(unamp($link['@attributes']['href']));
+ if(strstr($diaspora_key,'RSA '))
+ $pubkey = rsatopem($diaspora_key);
+ else
+ $pubkey = $diaspora_key;
+ $diaspora = true;
+ }
+ }
+
+ if($diaspora && $diaspora_base && $diaspora_guid) {
+ $guid = $diaspora_guid;
+ $diaspora_base = trim($diaspora_base,'/');
+
+ $notify = $diaspora_base . '/receive';
+
+ if(strpos($webbie,'@')) {
+ $addr = str_replace('acct:', '', $webbie);
+ $hostname = substr($webbie,strpos($webbie,'@')+1);
+ }
+ $network = 'diaspora';
+ // until we get a dfrn layer, we'll use diaspora protocols for Friendica,
+ // but give it a different network so we can go back and fix these when we get proper support.
+ // It really should be just 'friendica' but we also want to distinguish
+ // between Friendica sites that we can use D* protocols with and those we can't.
+ // Some Friendica sites will have Diaspora disabled.
+ if($dfrn)
+ $network = 'friendica-over-diaspora';
+ if($hcard) {
+ $vcard = scrape_vcard($hcard);
+ $vcard['nick'] = substr($webbie,0,strpos($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())
+ );
+
+ $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),
+ dbesc($addr),
+ dbesc($network),
+ dbesc(trim($diaspora_base,'/')),
+ dbesc($hostname),
+ dbesc($notify),
+ dbesc(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'])),
+ dbesc($photos[0]),
+ dbesc($photos[1]),
+ dbesc($photos[2]),
+ dbesc($photos[3]),
+ dbesc($addr)
+ );
+ return true;
+
+ }
+
+ return false;
+
+/*
+ $vcard['fn'] = notags($vcard['fn']);
+ $vcard['nick'] = str_replace(' ','',notags($vcard['nick']));
+
+ $result['name'] = $vcard['fn'];
+ $result['nick'] = $vcard['nick'];
+ $result['guid'] = $guid;
+ $result['url'] = $profile;
+ $result['hostname'] = $hostname;
+ $result['addr'] = $addr;
+ $result['batch'] = $batch;
+ $result['notify'] = $notify;
+ $result['poll'] = $poll;
+ $result['request'] = $request;
+ $result['confirm'] = $confirm;
+ $result['poco'] = $poco;
+ $result['photo'] = $vcard['photo'];
+ $result['priority'] = $priority;
+ $result['network'] = $network;
+ $result['alias'] = $alias;
+ $result['pubkey'] = $pubkey;
+
+ logger('probe_url: ' . print_r($result,true), LOGGER_DEBUG);
+
+ return $result;
+
+*/
+
+/* Sample Diaspora result.
+
+Array
+(
+ [name] => Mike Macgirvin
+ [nick] => macgirvin
+ [guid] => a9174a618f8d269a
+ [url] => https://joindiaspora.com/u/macgirvin
+ [hostname] => joindiaspora.com
+ [addr] => macgirvin@joindiaspora.com
+ [batch] =>
+ [notify] => https://joindiaspora.com/receive
+ [poll] => https://joindiaspora.com/public/macgirvin.atom
+ [request] =>
+ [confirm] =>
+ [poco] =>
+ [photo] => https://joindiaspora.s3.amazonaws.com/uploads/images/thumb_large_fec4e6eef13ae5e56207.jpg
+ [priority] =>
+ [network] => diaspora
+ [alias] =>
+ [pubkey] => -----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtihtyIuRDWkDpCA+I1UaQ
+jI4S7k625+A7EEJm+pL2ZVSJxeCKiFeEgHBQENjLMNNm8l8F6blxgQqE6ZJ9Spa7f
+tlaXYTRCrfxKzh02L3hR7sNA+JS/nXJaUAIo+IwpIEspmcIRbD9GB7Wv/rr+M28uH
+31EeYyDz8QL6InU/bJmnCdFvmEMBQxJOw1ih9tQp7UNJAbUMCje0WYFzBz7sfcaHL
+OyYcCOqOCBLdGucUoJzTQ9iDBVzB8j1r1JkIHoEb2moUoKUp+tkCylNfd/3IVELF9
+7w1Qjmit3m50OrJk2DQOXvCW9KQxaQNdpRPSwhvemIt98zXSeyZ1q/YjjOwG0DWDq
+AF8aLj3/oQaZndTPy/6tMiZogKaijoxj8xFLuPYDTw5VpKquriVC0z8oxyRbv4t9v
+8JZZ9BXqzmayvY3xZGGp8NulrfjW+me2bKh0/df1aHaBwpZdDTXQ6kqAiS2FfsuPN
+vg57fhfHbL1yJ4oDbNNNeI0kJTGchXqerr8C20khU/cQ2Xt31VyEZtnTB665Ceugv
+kp3t2qd8UpAVKl430S5Quqx2ymfUIdxdW08CEjnoRNEL3aOWOXfbf4gSVaXmPCR4i
+LSIeXnd14lQYK/uxW/8cTFjcmddsKxeXysoQxbSa9VdDK+KkpZdgYXYrTTofXs6v+
+4afAEhRaaY+MCAwEAAQ==
+-----END PUBLIC KEY-----
+
+)
+*/
+
+
+
+
+ }
+}
+
+
+function webfinger_rfc7033($webbie) {
+
+
+ if(! strpos($webbie,'@'))
+ return false;
+ $lhs = substr($webbie,0,strpos($webbie,'@'));
+ $rhs = substr($webbie,strpos($webbie,'@')+1);
+
+ $resource = 'acct:' . $webbie;
+
+ $s = z_fetch_url('https://' . $rhs . '/.well-known/webfinger?resource=' . $resource);
+
+ if($s['success'])
+ $j = json_decode($s['body'],true);
+ else
+ return false;
+ return($j);
+}
+
+
+function old_webfinger($webbie) {
+
+ $host = '';
+ if(strstr($webbie,'@'))
+ $host = substr($webbie,strpos($webbie,'@') + 1);
+
+ if(strlen($host)) {
+ $tpl = fetch_lrdd_template($host);
+ logger('old_webfinger: lrdd template: ' . $tpl,LOGGER_DATA);
+ if(strlen($tpl)) {
+ $pxrd = str_replace('{uri}', urlencode('acct:' . $webbie), $tpl);
+ logger('old_webfinger: pxrd: ' . $pxrd,LOGGER_DATA);
+ $links = fetch_xrd_links($pxrd);
+ if(! count($links)) {
+ // try with double slashes
+ $pxrd = str_replace('{uri}', urlencode('acct://' . $webbie), $tpl);
+ logger('old_webfinger: pxrd: ' . $pxrd,LOGGER_DATA);
+ $links = fetch_xrd_links($pxrd);
+ }
+ return $links;
+ }
+ }
+ return array();
+}
+
+
+function fetch_lrdd_template($host) {
+ $tpl = '';
+
+ $url1 = 'https://' . $host . '/.well-known/host-meta' ;
+ $url2 = 'http://' . $host . '/.well-known/host-meta' ;
+ $links = fetch_xrd_links($url1);
+ logger('fetch_lrdd_template from: ' . $url1, LOGGER_DEBUG);
+ logger('template (https): ' . print_r($links,true),LOGGER_DEBUG);
+ if(! count($links)) {
+ logger('fetch_lrdd_template from: ' . $url2);
+ $links = fetch_xrd_links($url2);
+ logger('template (http): ' . print_r($links,true),LOGGER_DEBUG);
+ }
+ if(count($links)) {
+ foreach($links as $link)
+ if($link['@attributes']['rel'] && $link['@attributes']['rel'] === 'lrdd' && (!$link['@attributes']['type'] || $link['@attributes']['type'] === 'application/xrd+xml'))
+ $tpl = $link['@attributes']['template'];
+ }
+ if(! strpos($tpl,'{uri}'))
+ $tpl = '';
+ return $tpl;
+
+}
+
+
+function fetch_xrd_links($url) {
+
+logger('fetch_xrd_links: ' . $url);
+
+ $redirects = 0;
+ $x = z_fetch_url($url,false,$redirects,array('timeout' => 20));
+
+ if(! $x['success'])
+ return array();
+
+ $xml = $x['body'];
+ logger('fetch_xrd_links: ' . $xml, LOGGER_DATA);
+
+ if ((! $xml) || (! stristr($xml,'<xrd')))
+ return array();
+
+ // fix diaspora's bad xml
+ $xml = str_replace(array('href=&quot;','&quot;/>'),array('href="','"/>'),$xml);
+
+ $h = parse_xml_string($xml);
+ if(! $h)
+ return array();
+
+ $arr = convert_xml_element_to_array($h);
+
+ $links = array();
+
+ if(isset($arr['xrd']['link'])) {
+ $link = $arr['xrd']['link'];
+
+ if(! isset($link[0]))
+ $links = array($link);
+ else
+ $links = $link;
+ }
+ if(isset($arr['xrd']['alias'])) {
+ $alias = $arr['xrd']['alias'];
+ if(! isset($alias[0]))
+ $aliases = array($alias);
+ else
+ $aliases = $alias;
+ if(is_array($aliases) && count($aliases)) {
+ foreach($aliases as $alias) {
+ $links[]['@attributes'] = array('rel' => 'alias' , 'href' => $alias);
+ }
+ }
+ }
+
+ logger('fetch_xrd_links: ' . print_r($links,true), LOGGER_DATA);
+
+ return $links;
+}
+
+
+function scrape_vcard($url) {
+
+ $a = get_app();
+
+ $ret = array();
+
+ logger('scrape_vcard: url=' . $url);
+
+ $x = z_fetch_url($url);
+ if(! $x['success'])
+ return $ret;
+
+ $s = $x['body'];
+
+ if(! $s)
+ return $ret;
+
+ $headers = $x['header'];
+ $lines = explode("\n",$headers);
+ if(count($lines)) {
+ foreach($lines as $line) {
+ // don't try and run feeds through the html5 parser
+ if(stristr($line,'content-type:') && ((stristr($line,'application/atom+xml')) || (stristr($line,'application/rss+xml'))))
+ return ret;
+ }
+ }
+
+ try {
+ $dom = HTML5_Parser::parse($s);
+ } catch (DOMException $e) {
+ logger('scrape_vcard: parse error: ' . $e);
+ }
+
+ if(! $dom)
+ return $ret;
+
+ // Pull out hCard profile elements
+
+ $largest_photo = 0;
+
+ $items = $dom->getElementsByTagName('*');
+ foreach($items as $item) {
+ if(attribute_contains($item->getAttribute('class'), 'vcard')) {
+ $level2 = $item->getElementsByTagName('*');
+ foreach($level2 as $x) {
+ if(attribute_contains($x->getAttribute('class'),'fn'))
+ $ret['fn'] = $x->textContent;
+ if((attribute_contains($x->getAttribute('class'),'photo'))
+ || (attribute_contains($x->getAttribute('class'),'avatar'))) {
+ $size = intval($x->getAttribute('width'));
+ if(($size > $largest_photo) || (! $largest_photo)) {
+ $ret['photo'] = $x->getAttribute('src');
+ $largest_photo = $size;
+ }
+ }
+ if((attribute_contains($x->getAttribute('class'),'nickname'))
+ || (attribute_contains($x->getAttribute('class'),'uid'))) {
+ $ret['nick'] = $x->textContent;
+ }
+ }
+ }
+ }
+
+ return $ret;
+}
+
+
+
+function scrape_feed($url) {
+
+ $a = get_app();
+
+ $ret = array();
+ $level = 0;
+ $x = z_fetch_url($url,false,$level,array('novalidate' => true));
+
+ if(! $x['success'])
+ return $ret;
+
+ $headers = $x['header'];
+ $code = $x['return_code'];
+ $s = $x['body'];
+
+ logger('scrape_feed: returns: ' . $code . ' headers=' . $headers, LOGGER_DEBUG);
+
+ if(! $s) {
+ logger('scrape_feed: no data returned for ' . $url);
+ return $ret;
+ }
+
+
+ $lines = explode("\n",$headers);
+ if(count($lines)) {
+ foreach($lines as $line) {
+ if(stristr($line,'content-type:')) {
+ if(stristr($line,'application/atom+xml') || stristr($s,'<feed')) {
+ $ret['feed_atom'] = $url;
+ return $ret;
+ }
+ if(stristr($line,'application/rss+xml') || stristr($s,'<rss')) {
+ $ret['feed_rss'] = $url;
+ return $ret;
+ }
+ }
+ }
+ // perhaps an RSS version 1 feed with a generic or incorrect content-type?
+ if(stristr($s,'</item>')) {
+ $ret['feed_rss'] = $url;
+ return $ret;
+ }
+ }
+
+ try {
+ $dom = HTML5_Parser::parse($s);
+ } catch (DOMException $e) {
+ logger('scrape_feed: parse error: ' . $e);
+ }
+
+ if(! $dom) {
+ logger('scrape_feed: failed to parse.');
+ return $ret;
+ }
+
+
+ $head = $dom->getElementsByTagName('base');
+ if($head) {
+ foreach($head as $head0) {
+ $basename = $head0->getAttribute('href');
+ break;
+ }
+ }
+ if(! $basename)
+ $basename = implode('/', array_slice(explode('/',$url),0,3)) . '/';
+
+ $items = $dom->getElementsByTagName('link');
+
+ // get Atom/RSS link elements, take the first one of either.
+
+ if($items) {
+ foreach($items as $item) {
+ $x = $item->getAttribute('rel');
+ if(($x === 'alternate') && ($item->getAttribute('type') === 'application/atom+xml')) {
+ if(! x($ret,'feed_atom'))
+ $ret['feed_atom'] = $item->getAttribute('href');
+ }
+ if(($x === 'alternate') && ($item->getAttribute('type') === 'application/rss+xml')) {
+ if(! x($ret,'feed_rss'))
+ $ret['feed_rss'] = $item->getAttribute('href');
+ }
+ }
+ }
+
+ // Drupal and perhaps others only provide relative URL's. Turn them into absolute.
+
+ if(x($ret,'feed_atom') && (! strstr($ret['feed_atom'],'://')))
+ $ret['feed_atom'] = $basename . $ret['feed_atom'];
+ if(x($ret,'feed_rss') && (! strstr($ret['feed_rss'],'://')))
+ $ret['feed_rss'] = $basename . $ret['feed_rss'];
+
+ return $ret;
+}
+
diff --git a/include/notifier.php b/include/notifier.php
index 9d5c7cb8e..0b5744b29 100644
--- a/include/notifier.php
+++ b/include/notifier.php
@@ -103,28 +103,33 @@ function notifier_run($argv, $argc){
);
if($r) {
// Get the sender
- $s = q("select * from channel where channel_id = %d limit 1",
+ $s = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1",
intval($r[0]['abook_channel'])
);
if($s) {
-
- // send a refresh message to each hub they have registered here
- $h = q("select * from hubloc where hubloc_hash = '%s'",
- dbesc($r[0]['hubloc_hash'])
- );
- if($h) {
- foreach($h as $hh) {
- $data = zot_build_packet($s[0],'refresh',array(array(
- 'guid' => $hh['hubloc_guid'],
- 'guid_sig' => $hh['hubloc_guid_sig'],
- 'url' => $hh['hubloc_url'])
- ));
- if($data) {
- $result = zot_zot($hh['hubloc_callback'],$data);
+ if($r[0]['hubloc_network'] === 'diaspora' || $r[0]['hubloc_network'] === 'friendica-over-diaspora') {
+ require_once('include/diaspora.php');
+ diaspora_share($s[0],$r[0]);
+ }
+ else {
+ // send a refresh message to each hub they have registered here
+ $h = q("select * from hubloc where hubloc_hash = '%s'",
+ dbesc($r[0]['hubloc_hash'])
+ );
+ if($h) {
+ foreach($h as $hh) {
+ $data = zot_build_packet($s[0],'refresh',array(array(
+ 'guid' => $hh['hubloc_guid'],
+ 'guid_sig' => $hh['hubloc_guid_sig'],
+ 'url' => $hh['hubloc_url'])
+ ));
+ if($data) {
+ $result = zot_zot($hh['hubloc_callback'],$data);
// zot_queue_item is not yet written
// if(! $result['success'])
// zot_queue_item();
+ }
}
}
}
@@ -366,6 +371,8 @@ function notifier_run($argv, $argc){
}
+ $walltowall = (($top_level_post && $channel['xchan_hash'] === $target_item['author_xchan']) ? true : false);
+
// Generic delivery section, we have an encoded item and recipients
// Now start the delivery process
@@ -382,7 +389,7 @@ function notifier_run($argv, $argc){
$env_recips = (($private) ? array() : null);
- $details = q("select xchan_hash, xchan_instance_url, xchan_addr, xchan_guid, xchan_guid_sig from xchan where xchan_hash in (" . implode(',',$recipients) . ")");
+ $details = q("select xchan_hash, xchan_instance_url, xchan_network, xchan_addr, xchan_guid, xchan_guid_sig from xchan where xchan_hash in (" . implode(',',$recipients) . ")");
$recip_list = array();
@@ -405,7 +412,7 @@ function notifier_run($argv, $argc){
$recip_list[] = $d['xchan_addr'] . ' (' . $d['xchan_hash'] . ')';
if($private)
- $env_recips[] = array('guid' => $d['xchan_guid'],'guid_sig' => $d['xchan_guid_sig']);
+ $env_recips[] = array('guid' => $d['xchan_guid'],'guid_sig' => $d['xchan_guid_sig'],'hash' => $d['xchan_hash']);
}
}
@@ -425,6 +432,8 @@ function notifier_run($argv, $argc){
$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')) {
// If sending a followup to the post owner, only send it to one channel clone - to avoid race conditions.
@@ -438,11 +447,11 @@ function notifier_run($argv, $argc){
// aren't the owner or author.
- $r = q("select hubloc_sitekey, hubloc_flags, hubloc_callback, hubloc_host from hubloc
+ $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");
}
else {
- $r = q("select hubloc_sitekey, hubloc_flags, hubloc_callback, hubloc_host from hubloc
+ $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");
}
@@ -485,6 +494,42 @@ function notifier_run($argv, $argc){
}
}
+
+ if($hub['hubloc_network'] === 'diaspora' || $hub['hubloc_network'] === 'friendica-over-diaspora') {
+ if(! get_config('system','diaspora_enabled'))
+ continue;
+
+ require_once('include/diaspora.php');
+
+ diaspora_process_outbound(array(
+ 'channel' => $channel,
+ 'env_recips' => $env_recips,
+ 'recipients' => $recipients,
+ 'item' => $item,
+ 'target_item' => $target_item,
+ 'hub' => $hub,
+ 'top_level_post' => $top_level_post,
+ 'private' => $private,
+ 'followup' => $followup,
+ 'relay_to_owner' => $relay_to_owner,
+ 'uplink' => $uplink,
+ 'cmd' => $cmd,
+ 'expire' => $expire,
+ 'mail' => $mail,
+ 'fsuggest' => $fsuggest,
+ 'normal_mode' => $normal_mode,
+ 'packet_type' => $packet_type,
+ 'walltowall' => $walltowall
+ ));
+
+ continue;
+
+ }
+
+
+ // default: zot protocol
+
+
$hash = random_string();
if($packet_type === 'refresh' || $packet_type === 'purge') {
$n = zot_build_packet($channel,$packet_type);
diff --git a/include/onepoll.php b/include/onepoll.php
index e81d8bcf7..1f28852e9 100644
--- a/include/onepoll.php
+++ b/include/onepoll.php
@@ -35,10 +35,12 @@ function onepoll_run($argv, $argc){
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 (( account_flags = %d ) OR ( account_flags = %d )) limit 1",
intval($contact_id),
- intval(ABOOK_FLAG_HIDDEN|ABOOK_FLAG_PENDING|ABOOK_FLAG_UNCONNECTED),
+ intval(ABOOK_FLAG_HIDDEN|ABOOK_FLAG_PENDING|ABOOK_FLAG_UNCONNECTED|ABOOK_FLAG_FEED),
intval(0),
+ intval(ABOOK_FLAG_ARCHIVED|ABOOK_FLAG_BLOCKED|ABOOK_FLAG_IGNORED),
intval(ACCOUNT_OK),
intval(ACCOUNT_UNVERIFIED)
);
@@ -65,11 +67,24 @@ 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')
);
+ 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",
+ dbesc(datetime_convert()),
+ intval($contact['abook_id'])
+ );
+ return;
+ }
+
+ if($contact['xchan_network'] !== 'zot')
+ return;
+
// update permissions
$x = zot_refresh($contact,$importer);
@@ -128,11 +143,9 @@ function onepoll_run($argv, $argc){
}
}
-
// fetch some items
// set last updated timestamp
-
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",
diff --git a/include/permissions.php b/include/permissions.php
index facba037f..8e4676f51 100644
--- a/include/permissions.php
+++ b/include/permissions.php
@@ -10,12 +10,12 @@ function get_perms() {
$global_perms = array(
// Read only permissions
- 'view_stream' => array('channel_r_stream', intval(PERMS_R_STREAM), true, t('Can view my "public" stream and posts'), ''),
- 'view_profile' => array('channel_r_profile', intval(PERMS_R_PROFILE), true, t('Can view my "public" channel profile'), ''),
- 'view_photos' => array('channel_r_photos', intval(PERMS_R_PHOTOS), true, t('Can view my "public" photo albums'), ''),
- 'view_contacts' => array('channel_r_abook', intval(PERMS_R_ABOOK), true, t('Can view my "public" address book'), ''),
- 'view_storage' => array('channel_r_storage', intval(PERMS_R_STORAGE), true, t('Can view my "public" file storage'), ''),
- 'view_pages' => array('channel_r_pages', intval(PERMS_R_PAGES), true, t('Can view my "public" pages'), ''),
+ 'view_stream' => array('channel_r_stream', intval(PERMS_R_STREAM), true, t('Can view my normal stream and posts'), ''),
+ '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'), ''),
// Write permissions
'send_stream' => array('channel_w_stream', intval(PERMS_W_STREAM), false, t('Can send me their channel stream and posts'), ''),
@@ -23,14 +23,14 @@ 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'), '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 "public" file storage'), ''),
- 'write_pages' => array('channel_w_pages', intval(PERMS_W_PAGES), false, t('Can edit my "public" pages'), ''),
+ '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')),
);
@@ -411,3 +411,63 @@ function site_default_perms() {
}
return $ret;
}
+
+
+/**
+ * @function get_role_perms($role)
+ * @param string $role
+ *
+ * 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
+ * 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)
+ *
+ * Any attributes may be extended (new roles defined) and modified (specific permissions altered) by plugins
+ *
+ */
+
+function get_role_perms($role) {
+
+ $ret = array();
+
+ $ret['role'] = $role;
+
+ switch($role) {
+ case 'social':
+ $ret['perms_auto'] = 0;
+ $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;
+ $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;
+ $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_r_pages'] = PERMS_PUBLIC;
+ $ret['channel_w_pages'] = 0;
+ $ret['channel_a_republish'] = PERMS_SPECIFIC;
+ $ret['channel_w_like'] = PERMS_NETWORK;
+
+ break;
+
+ }
+
+ call_hooks('get_role_perms',$ret);
+
+ return $ret;
+}
+
+
diff --git a/include/photo/photo_driver.php b/include/photo/photo_driver.php
index f5e915402..daf1bfc25 100644
--- a/include/photo/photo_driver.php
+++ b/include/photo/photo_driver.php
@@ -287,6 +287,10 @@ abstract class photo_driver {
$p['allow_gid'] = (($arr['allow_gid']) ? $arr['allow_gid'] : '');
$p['deny_cid'] = (($arr['deny_cid']) ? $arr['deny_cid'] : '');
$p['deny_gid'] = (($arr['deny_gid']) ? $arr['deny_gid'] : '');
+ $p['created'] = (($arr['created']) ? $arr['created'] : datetime_convert());
+ $p['edited'] = (($arr['edited']) ? $arr['edited'] : $p['created']);
+ $p['title'] = (($arr['title']) ? $arr['title'] : '');
+ $p['description'] = (($arr['description']) ? $arr['description'] : '');
// temporary until we get rid of photo['profile'] and just use photo['photo_flags']
// but this will require updating all existing photos in the DB.
@@ -318,6 +322,8 @@ abstract class photo_driver {
`scale` = %d,
`profile` = %d,
`photo_flags` = %d,
+ `title` = '%s',
+ `description` = '%s',
`allow_cid` = '%s',
`allow_gid` = '%s',
`deny_cid` = '%s',
@@ -328,8 +334,8 @@ abstract class photo_driver {
intval($p['uid']),
dbesc($p['xchan']),
dbesc($p['resource_id']),
- dbesc(datetime_convert()),
- dbesc(datetime_convert()),
+ dbesc($p['created']),
+ dbesc($p['edited']),
dbesc(basename($p['filename'])),
dbesc($this->getType()),
dbesc($p['album']),
@@ -340,6 +346,8 @@ abstract class photo_driver {
intval($p['scale']),
intval($p['profile']),
intval($p['photo_flags']),
+ dbesc($p['title']),
+ dbesc($p['description']),
dbesc($p['allow_cid']),
dbesc($p['allow_gid']),
dbesc($p['deny_cid']),
@@ -349,14 +357,14 @@ abstract class photo_driver {
}
else {
$r = q("INSERT INTO `photo`
- ( `aid`, `uid`, `xchan`, `resource_id`, `created`, `edited`, `filename`, type, `album`, `height`, `width`, `data`, `size`, `scale`, `profile`, `photo_flags`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid` )
- VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s' )",
+ ( `aid`, `uid`, `xchan`, `resource_id`, `created`, `edited`, `filename`, type, `album`, `height`, `width`, `data`, `size`, `scale`, `profile`, `photo_flags`, `title`, `description`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid` )
+ VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s' )",
intval($p['aid']),
intval($p['uid']),
dbesc($p['xchan']),
dbesc($p['resource_id']),
- dbesc(datetime_convert()),
- dbesc(datetime_convert()),
+ dbesc($p['created']),
+ dbesc($p['edited']),
dbesc(basename($p['filename'])),
dbesc($this->getType()),
dbesc($p['album']),
@@ -367,6 +375,8 @@ abstract class photo_driver {
intval($p['scale']),
intval($p['profile']),
intval($p['photo_flags']),
+ dbesc($p['title']),
+ dbesc($p['description']),
dbesc($p['allow_cid']),
dbesc($p['allow_gid']),
dbesc($p['deny_cid']),
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 9819c7ef2..06a99457a 100644
--- a/include/photos.php
+++ b/include/photos.php
@@ -61,47 +61,64 @@ function photo_upload($channel, $observer, $args) {
$str_group_deny = perms2str(((is_array($args['group_deny'])) ? $args['group_deny'] : explode(',',$args['group_deny'])));
$str_contact_deny = perms2str(((is_array($args['contact_deny'])) ? $args['contact_deny'] : explode(',',$args['contact_deny'])));
- $f = array('src' => '', 'filename' => '', 'filesize' => 0, 'type' => '');
- call_hooks('photo_upload_file',$f);
+ if($args['data']) {
- if(x($f,'src') && x($f,'filesize')) {
- $src = $f['src'];
- $filename = $f['filename'];
- $filesize = $f['filesize'];
- $type = $f['type'];
+ // allow an import from a binary string representing the image.
+ // This bypasses the upload step and max size limit checking
+
+ $imagedata = $args['data'];
+ $filename = $args['filename'];
+ $filesize = strlen($imagedata);
+ // this is going to be deleted if it exists
+ $src = '/tmp/deletemenow';
+ $type = $args['type'];
}
else {
- $src = $_FILES['userfile']['tmp_name'];
- $filename = basename($_FILES['userfile']['name']);
- $filesize = intval($_FILES['userfile']['size']);
- $type = $_FILES['userfile']['type'];
- }
+ $f = array('src' => '', 'filename' => '', 'filesize' => 0, 'type' => '');
- if (! $type)
- $type=guess_image_type($filename);
+ call_hooks('photo_upload_file',$f);
- logger('photo_upload: received file: ' . $filename . ' as ' . $src . ' ('. $type . ') ' . $filesize . ' bytes', LOGGER_DEBUG);
+ if(x($f,'src') && x($f,'filesize')) {
+ $src = $f['src'];
+ $filename = $f['filename'];
+ $filesize = $f['filesize'];
+ $type = $f['type'];
+ }
+ else {
+ $src = $_FILES['userfile']['tmp_name'];
+ $filename = basename($_FILES['userfile']['name']);
+ $filesize = intval($_FILES['userfile']['size']);
+ $type = $_FILES['userfile']['type'];
+ }
- $maximagesize = get_config('system','maximagesize');
+ if (! $type)
+ $type=guess_image_type($filename);
- if(($maximagesize) && ($filesize > $maximagesize)) {
- $ret['message'] = sprintf ( t('Image exceeds website size limit of %lu bytes'), $maximagesize);
- @unlink($src);
- call_hooks('photo_upload_end',$ret);
- return $ret;
- }
+ logger('photo_upload: received file: ' . $filename . ' as ' . $src . ' ('. $type . ') ' . $filesize . ' bytes', LOGGER_DEBUG);
- if(! $filesize) {
- $ret['message'] = t('Image file is empty.');
- @unlink($src);
- call_hooks('photo_post_end',$ret);
- return $ret;
- }
+
+ $maximagesize = get_config('system','maximagesize');
+
+ if(($maximagesize) && ($filesize > $maximagesize)) {
+ $ret['message'] = sprintf ( t('Image exceeds website size limit of %lu bytes'), $maximagesize);
+ @unlink($src);
+ call_hooks('photo_upload_end',$ret);
+ return $ret;
+ }
- logger('photo_upload: loading the contents of ' . $src , LOGGER_DEBUG);
+ if(! $filesize) {
+ $ret['message'] = t('Image file is empty.');
+ @unlink($src);
+ call_hooks('photo_post_end',$ret);
+ return $ret;
+ }
+
+ logger('photo_upload: loading the contents of ' . $src , LOGGER_DEBUG);
+
+ $imagedata = @file_get_contents($src);
+ }
- $imagedata = @file_get_contents($src);
$r = q("select sum(size) as total from photo where aid = %d and scale = 0 ",
intval($account_id)
@@ -141,7 +158,7 @@ function photo_upload($channel, $observer, $args) {
$smallest = 0;
- $photo_hash = photo_new_resource();
+ $photo_hash = (($args['resource_id']) ? $args['resource_id'] : photo_new_resource());
$visitor = '';
if($channel['channel_hash'] !== $observer['xchan_hash'])
@@ -154,6 +171,15 @@ function photo_upload($channel, $observer, $args) {
'allow_cid' => $str_contact_allow, 'allow_gid' => $str_group_allow,
'deny_cid' => $str_contact_deny, 'deny_gid' => $str_group_deny
);
+ if($args['created'])
+ $p['created'] = $args['created'];
+ if($args['edited'])
+ $p['edited'] = $args['edited'];
+ if($args['title'])
+ $p['title'] = $args['title'];
+ if($args['description'])
+ $p['desciprion'] = $args['description'];
+
$r1 = $ph->save($p);
if(! $r1)
diff --git a/include/plugin.php b/include/plugin.php
index c5f57829d..c2e08a989 100755
--- a/include/plugin.php
+++ b/include/plugin.php
@@ -430,93 +430,6 @@ function get_theme_screenshot($theme) {
}
-// check service_class restrictions. If there are no service_classes defined, everything is allowed.
-// if $usage is supplied, we check against a maximum count and return true if the current usage is
-// less than the subscriber plan allows. Otherwise we return boolean true or false if the property
-// is allowed (or not) in this subscriber plan. An unset property for this service plan means
-// the property is allowed, so it is only necessary to provide negative properties for each plan,
-// or what the subscriber is not allowed to do.
-
-
-function service_class_allows($uid,$property,$usage = false) {
- $a = get_app();
- if($uid == local_user()) {
- $service_class = $a->account['account_service_class'];
- }
- else {
- $r = q("select account_service_class as service_class
- from channel c, account a
- where c.channel_account_id=a.account_id and c.channel_id= %d limit 1",
- intval($uid)
- );
- if($r !== false and count($r)) {
- $service_class = $r[0]['service_class'];
- }
- }
- if(! x($service_class))
- return true; // everything is allowed
-
- $arr = get_config('service_class',$service_class);
- if(! is_array($arr) || (! count($arr)))
- return true;
-
- if($usage === false)
- return ((x($arr[$property])) ? (bool) $arr['property'] : true);
- else {
- if(! array_key_exists($property,$arr))
- return true;
- return (((intval($usage)) < intval($arr[$property])) ? true : false);
- }
-}
-
-
-function service_class_fetch($uid,$property) {
- $a = get_app();
- if($uid == local_user()) {
- $service_class = $a->account['account_service_class'];
- }
- else {
- $r = q("select account_service_class as service_class
- from channel c, account a
- where c.channel_account_id=a.account_id and c.channel_id= %d limit 1",
- intval($uid)
- );
- if($r !== false and 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)
- return '';
- if($bbcode)
- $t = sprintf('[zrl=%s]' . t('Click here to upgrade.') . '[/zrl]', $l);
- else
- $t = sprintf('<a href="%s">' . t('Click here to upgrade.') . '</div>', $l);
- return $t;
-}
-
-function upgrade_message($bbcode = false) {
- $x = upgrade_link($bbcode);
- return t('This action exceeds the limits set by your subscription plan.') . (($x) ? ' ' . $x : '') ;
-}
-
-function upgrade_bool_message($bbcode = false) {
- $x = upgrade_link($bbcode);
- return t('This action is not available under your subscription plan.') . (($x) ? ' ' . $x : '') ;
-}
-
/**
* @brief add CSS to <head>
*
diff --git a/include/poller.php b/include/poller.php
index 546a2d6d1..c90e48d6b 100644
--- a/include/poller.php
+++ b/include/poller.php
@@ -35,12 +35,15 @@ 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 < UTC_TIMESTAMP() ",
+ dbesc(NULL_DATE)
+ );
// expire any expired items
- $r = q("select id from item where expires != '0000-00-00 00:00:00' and expires < UTC_TIMESTAMP()
+ $r = q("select id from item where expires != '%s' and expires < UTC_TIMESTAMP()
and not ( item_restrict & %d ) ",
+ dbesc(NULL_DATE),
intval(ITEM_DELETED)
);
if($r) {
@@ -111,6 +114,9 @@ function poller_run($argv, $argc){
if(($d2 != $d1) && ($h1 == $h2)) {
+ require_once('include/dir_fns.php');
+ check_upstream_directory();
+
call_hooks('cron_daily',datetime_convert());
@@ -148,7 +154,6 @@ function poller_run($argv, $argc){
q("delete from notify where seen = 1 and date < UTC_TIMESTAMP() - INTERVAL 30 DAY");
// expire any expired accounts
- require_once('include/account.php');
downgrade_accounts();
// If this is a directory server, request a sync with an upstream
@@ -244,7 +249,7 @@ function poller_run($argv, $argc){
$sql_extra
AND (( abook_flags & %d ) OR ( abook_flags = %d ))
AND (( account_flags = %d ) OR ( account_flags = %d )) $abandon_sql ORDER BY RAND()",
- intval(ABOOK_FLAG_HIDDEN|ABOOK_FLAG_PENDING|ABOOK_FLAG_UNCONNECTED),
+ intval(ABOOK_FLAG_HIDDEN|ABOOK_FLAG_PENDING|ABOOK_FLAG_UNCONNECTED|ABOOK_FLAG_FEED),
intval(0),
intval(ACCOUNT_OK),
intval(ACCOUNT_UNVERIFIED) // FIXME
@@ -260,6 +265,21 @@ function poller_run($argv, $argc){
$t = $contact['abook_updated'];
$c = $contact['abook_connected'];
+ if($contact['abook_flags'] & ABOOK_FLAG_FEED) {
+ $min = service_class_fetch($contact['abook_channel'],'minimum_feedcheck_minutes');
+ if(! $min)
+ $min = intval(get_config('system','minimum_feedcheck_minutes'));
+ if(! $min)
+ $min = 60;
+ $x = datetime_convert('UTC','UTC',"now - $min minutes");
+ if($c < $x) {
+ proc_run('php','include/onepoll.php',$contact['abook_id']);
+ if($interval)
+ @time_sleep_until(microtime(true) + (float) $interval);
+ }
+ continue;
+ }
+
if($c == $t) {
if(datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 day"))
@@ -268,7 +288,7 @@ function poller_run($argv, $argc){
else {
// if we've never connected with them, start the mark for death countdown from now
- if($c == '0000-00-00 00:00:00') {
+ if($c == NULL_DATE) {
$r = q("update abook set abook_connected = '%s' where abook_id = %d limit 1",
dbesc(datetime_convert()),
intval($contact['abook_id'])
@@ -321,8 +341,9 @@ 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 not ( ud_flags & %d ) and ud_addr != '' and ( ud_last = '%s' OR ud_last > UTC_TIMESTAMP() - INTERVAL 7 DAY ) group by ud_addr ",
+ intval(UPDATE_FLAGS_UPDATED),
+ dbesc(NULL_DATE)
);
if($r) {
foreach($r as $rr) {
@@ -330,7 +351,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']);
diff --git a/include/queue.php b/include/queue.php
index 239d61fc0..222ebada4 100644
--- a/include/queue.php
+++ b/include/queue.php
@@ -38,7 +38,7 @@ function queue_run($argv, $argc){
// 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 )) and outq_driver in ('','zot') group by outq_posturl");
+ $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(! $r)
return;
@@ -46,6 +46,24 @@ function queue_run($argv, $argc){
foreach($r as $rr) {
if(in_array($rr['outq_posturl'],$deadguys))
continue;
+
+ if($rr['outq_driver'] === 'post') {
+ $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",
+ 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",
+ dbesc(datetime_convert()),
+ dbesc($rr['outq_hash'])
+ );
+ }
+ continue;
+ }
$result = zot_zot($rr['outq_posturl'],$rr['outq_notify']);
if($result['success']) {
zot_process_response($rr['outq_posturl'],$result, $rr);
diff --git a/include/security.php b/include/security.php
index 2ccfc6973..0f2edc708 100644
--- a/include/security.php
+++ b/include/security.php
@@ -46,6 +46,12 @@ function authenticate_success($user_record, $login_initial = false, $interactive
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);
}
diff --git a/include/socgraph.php b/include/socgraph.php
index e12da5862..504a6b2c0 100644
--- a/include/socgraph.php
+++ b/include/socgraph.php
@@ -123,7 +123,8 @@ function poco_load($xchan = '',$url = null) {
$profile_url = $url['value'];
continue;
}
- if($url['type'] == 'zot') {
+ if($url['type'] == 'zot' || $url['type'] == 'diaspora' || $url['type'] == 'friendica') {
+ $network = $url['type'];
$address = str_replace('acct:' , '', $url['value']);
continue;
}
@@ -151,17 +152,25 @@ function poco_load($xchan = '',$url = null) {
if(($x !== false) && (! count($x))) {
if($address) {
- $z = zot_finger($address,null);
- if($z['success']) {
- $j = json_decode($z['body'],true);
- if($j)
- import_xchan($j);
+ if($network === 'zot') {
+ $z = zot_finger($address,null);
+ if($z['success']) {
+ $j = json_decode($z['body'],true);
+ if($j)
+ import_xchan($j);
+ }
+ $x = q("select xchan_hash from xchan where xchan_hash = '%s' limit 1",
+ dbesc($hash)
+ );
+ if(! $x) {
+ continue;
+ }
}
- $x = q("select xchan_hash from xchan where xchan_hash = '%s' limit 1",
- dbesc($hash)
- );
- if(! $x) {
- continue;
+ else {
+ $x = import_author_diaspora(array('address' => $address));
+ if(! $x) {
+ continue;
+ }
}
}
else {
diff --git a/include/text.php b/include/text.php
index 1c5a78d4e..599c5d445 100755..100644
--- a/include/text.php
+++ b/include/text.php
@@ -508,8 +508,14 @@ function logger($msg,$level = 0) {
if((! $debugging) || (! $logfile) || ($level > $loglevel))
return;
-
- @file_put_contents($logfile, datetime_convert() . ':' . session_id() . ' ' . $msg . "\n", FILE_APPEND);
+
+ $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 . "\n", FILE_APPEND);
return;
}
@@ -966,6 +972,7 @@ function smilies($s, $sample = false) {
|| (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);
@@ -1002,6 +1009,7 @@ function smilies($s, $sample = false) {
':facepalm',
':like',
':dislike',
+ 'red#matrix',
'red#',
'r#'
);
@@ -1039,7 +1047,8 @@ 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</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>'
);
@@ -1863,6 +1872,11 @@ function xchan_query(&$items,$abook = true) {
$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) . " )");
}
+ $xchans = q("select * from xchan where xchan_hash in (" . implode(',',$arr) . ") and xchan_network in ('rss','unknown')");
+ if(! $chans)
+ $chans = $xchans;
+ else
+ $chans = array_merge($xchans,$chans);
}
if($items && count($items) && $chans && count($chans)) {
for($x = 0; $x < count($items); $x ++) {
@@ -2020,3 +2034,19 @@ 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. If this is used in a post handler, that decision may need to be considered.
+
+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 . '=' . $v;
+ }
+ }
+ }
+ return $s;
+} \ No newline at end of file
diff --git a/include/widgets.php b/include/widgets.php
index 96bced87f..1aa018fb6 100644
--- a/include/widgets.php
+++ b/include/widgets.php
@@ -48,6 +48,13 @@ function widget_collections($args) {
$abook_id = 0;
$wmode = 0;
break;
+ case 'connections':
+ $every = 'connections';
+ $each = 'group';
+ $edit = true;
+ $current = $_REQUEST['gid'];
+ $abook_id = 0;
+ $wmode = 0;
case 'groups':
$every = 'connections';
$each = argv(0);
diff --git a/include/zot.php b/include/zot.php
index 5df217c03..644d20ec2 100644
--- a/include/zot.php
+++ b/include/zot.php
@@ -117,8 +117,11 @@ function zot_build_packet($channel,$type = 'notify',$recipients = null, $remote_
'version' => ZOT_REVISION
);
- if($recipients)
+ if($recipients) {
+ for($x = 0; $x < count($recipients); $x ++)
+ unset($recipients[$x]['hash']);
$data['recipients'] = $recipients;
+ }
if($secret) {
$data['secret'] = $secret;
@@ -198,11 +201,17 @@ function zot_finger($webbie,$channel,$autofallback = true) {
if($r) {
$url = $r[0]['hubloc_url'];
+
+ if($r[0]['hubloc_network'] && $r[0]['hubloc_network'] !== 'zot') {
+ logger('zot_finger: alternate network: ' . $webbie);
+ return array('success' => false);
+ }
}
else {
$url = 'https://' . $host;
}
-
+
+
$rhs = '/.well-known/zot-info';
$https = ((strpos($url,'https://') === 0) ? true : false);
@@ -271,6 +280,11 @@ function zot_finger($webbie,$channel,$autofallback = true) {
function zot_refresh($them,$channel = null, $force = false) {
+ if(array_key_exists('xchan_network',$them) && ($them['xchan_network'] !== 'zot')) {
+ logger('zot_refresh: not got zot. ' . $them['xchan_name']);
+ return true;
+ }
+
logger('zot_refresh: them: ' . print_r($them,true), LOGGER_DATA);
if($channel)
logger('zot_refresh: channel: ' . print_r($channel,true), LOGGER_DATA);
@@ -370,7 +384,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) {
@@ -507,6 +521,22 @@ function zot_refresh($them,$channel = null, $force = false) {
function zot_gethub($arr) {
if($arr['guid'] && $arr['guid_sig'] && $arr['url'] && $arr['url_sig']) {
+
+ $blacklisted = false;
+ $bl1 = get_config('system','blacklisted_sites');
+ if(is_array($bl1) && $bl1) {
+ foreach($bl1 as $bl) {
+ if($bl && strpos($arr['url'],$bl) !== false) {
+ $blacklisted = true;
+ break;
+ }
+ }
+ }
+ if($blacklisted) {
+ logger('zot_gethub: blacklisted site: ' . $arr['url']);
+ return null;
+ }
+
$r = q("select * from hubloc
where hubloc_guid = '%s' and hubloc_guid_sig = '%s'
and hubloc_url = '%s' and hubloc_url_sig = '%s'
@@ -625,6 +655,10 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
return $ret;
}
+ if(! ($arr['guid'] && $arr['guid_sig'])) {
+ logger('import_xchan: no identity information provided. ' . print_r($arr,true));
+ return $ret;
+ }
$xchan_hash = make_xchan_hash($arr['guid'],$arr['guid_sig']);
$import_photos = false;
@@ -688,7 +722,7 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
$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']),
+ dbesc(($arr['name']) ? $arr['name'] : '-'),
dbesc($arr['name_updated']),
dbesc($arr['connections_url']),
dbesc($arr['follow_url']),
@@ -738,7 +772,7 @@ 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']),
@@ -939,12 +973,13 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
);
}
logger('import_xchan: new hub: ' . $location['url']);
- $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, hubloc_updated, hubloc_connected)
- values ( '%s','%s','%s','%s', %d ,'%s','%s','%s','%s','%s','%s','%s')",
+ $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']),
@@ -985,8 +1020,22 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) {
// Are we a directory server of some kind?
+ $other_realm = false;
+ $realm = get_directory_realm();
+ if(array_key_exists('site',$arr)
+ && array_key_exists('realm',$arr['site'])
+ && (strpos($arr['site']['realm'],$realm) === false))
+ $other_realm = true;
+
if($dirmode != DIRECTORY_MODE_NORMAL) {
- if(array_key_exists('profile',$arr) && is_array($arr['profile'])) {
+
+ // We're some kind of directory server. However we can only add directory information
+ // if the entry is in the same realm (or is a sub-realm). Sub-realms are denoted by
+ // including the parent realm in the name. e.g. 'RED_GLOBAL:foo' would allow an entry to
+ // be in directories for the local realm (foo) and also the RED_GLOBAL realm.
+
+
+ if(array_key_exists('profile',$arr) && is_array($arr['profile']) && (! $other_realm)) {
$profile_changed = import_directory_profile($xchan_hash,$arr['profile'],$address,$ud_flags, 1);
if($profile_changed) {
$what .= 'profile ';
@@ -1200,6 +1249,12 @@ function zot_import($arr, $sender_url) {
continue;
}
+ // 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']))
+ unset($i['message']['public_scope']);
+
$deliveries = $r;
// We found somebody on this site that's in the recipient list.
@@ -1207,18 +1262,32 @@ function zot_import($arr, $sender_url) {
}
else {
if(($i['message']) && (array_key_exists('flags',$i['message'])) && (in_array('private',$i['message']['flags']))) {
- // This should not happen but until we can stop it...
- logger('private message was delivered with no recipients.');
- continue;
+ if(array_key_exists('public_scope',$i['message']) && $i['message']['public_scope'] === 'public') {
+ // This should not happen but until we can stop it...
+ logger('private message was delivered with no recipients.');
+ continue;
+ }
}
- logger('public post');
+ logger('public post');
// Public post. look for any site members who are or may be accepting posts from this sender
// and who are allowed to see them based on the sender's permissions
$deliveries = allowed_public_recips($i);
+ // if the scope is anything but 'public' we're going to store it as private regardless
+ // of the private flag on the post.
+
+ if($i['message'] && array_key_exists('public_scope',$i['message'])
+ && $i['message']['public_scope'] !== 'public') {
+
+ if(! array_key_exists('flags',$i['message']))
+ $i['message']['flags'] = array();
+ if(! in_array('private',$i['message']['flags']))
+ $i['message']['flags'][] = 'private';
+
+ }
}
// Go through the hash array and remove duplicates. array_unique() won't do this because the array is more than one level.
@@ -1406,7 +1475,7 @@ function allowed_public_recips($msg) {
$hash = make_xchan_hash($msg['notify']['sender']['guid'],$msg['notify']['sender']['guid_sig']);
- if($scope === 'public' || $scope === 'network: red')
+ if($scope === 'public' || $scope === 'network: red' || $scope === 'authenticated')
return $recips;
if(strpos($scope,'site:') === 0) {
@@ -1526,12 +1595,13 @@ function process_delivery($sender,$arr,$deliveries,$relay,$public = false) {
$arr['aid'] = $channel['channel_account_id'];
$arr['uid'] = $channel['channel_id'];
$item_result = item_store($arr);
- $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']);
-
+ $item_id = 0;
+ if($item_result['success']) {
+ $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']);
+ }
$result[] = array($d['hash'],(($item_id) ? 'posted' : 'storage failed:' . $item_result['message']),$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>',$arr['mid']);
}
@@ -1721,8 +1791,6 @@ function process_mail_delivery($sender,$arr,$deliveries) {
function process_profile_delivery($sender,$arr,$deliveries) {
- // deliveries is irrelevant, what to do about birthday notification....?
-
logger('process_profile_delivery', LOGGER_DEBUG);
$r = q("select xchan_addr from xchan where xchan_hash = '%s' limit 1",
@@ -1794,7 +1862,7 @@ function import_directory_profile($hash,$profile,$addr,$ud_flags = UPDATE_FLAGS_
$update = false;
foreach($r[0] as $k => $v) {
if((array_key_exists($k,$arr)) && ($arr[$k] != $v)) {
- logger('import_directory_profile: update' . $k . ' => ' . $arr[$k]);
+ logger('import_directory_profile: update ' . $k . ' => ' . $arr[$k]);
$update = true;
break;
}
@@ -1836,7 +1904,7 @@ function import_directory_profile($hash,$profile,$addr,$ud_flags = UPDATE_FLAGS_
}
else {
$update = true;
- logger('import_directory_profile: new profile');
+ logger('import_directory_profile: new profile ');
$x = q("insert into xprof (xprof_hash, xprof_desc, xprof_dob, xprof_age, xprof_gender, xprof_marital, xprof_sexual, xprof_locale, xprof_region, xprof_postcode, xprof_country, xprof_about, xprof_homepage, xprof_hometown, xprof_keywords) values ('%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ",
dbesc($arr['xprof_hash']),
dbesc($arr['xprof_desc']),
@@ -1992,6 +2060,7 @@ function import_site($arr,$pubkey) {
$url = htmlspecialchars($arr['url'],ENT_COMPAT,'UTF-8',false);
$sellpage = htmlspecialchars($arr['sellpage'],ENT_COMPAT,'UTF-8',false);
$site_location = htmlspecialchars($arr['location'],ENT_COMPAT,'UTF-8',false);
+ $site_realm = htmlspecialchars($arr['realm'],ENT_COMPAT,'UTF-8',false);
if($exists) {
if(($siterecord['site_flags'] != $site_directory)
@@ -1999,13 +2068,14 @@ function import_site($arr,$pubkey) {
|| ($siterecord['site_directory'] != $directory_url)
|| ($siterecord['site_sellpage'] != $sellpage)
|| ($siterecord['site_location'] != $site_location)
- || ($siterecord['site_register'] != $register_policy)) {
+ || ($siterecord['site_register'] != $register_policy)
+ || ($siterecord['site_realm'] != $site_realm)) {
$update = true;
// logger('import_site: input: ' . print_r($arr,true));
// 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'
+ $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",
dbesc($site_location),
intval($site_directory),
@@ -2014,6 +2084,7 @@ function import_site($arr,$pubkey) {
intval($register_policy),
dbesc(datetime_convert()),
dbesc($sellpage),
+ dbesc($site_realm),
dbesc($url)
);
if(! $r) {
@@ -2023,8 +2094,8 @@ function import_site($arr,$pubkey) {
}
else {
$update = true;
- $r = q("insert into site ( site_location, site_url, site_access, site_flags, site_update, site_directory, site_register, site_sellpage )
- values ( '%s', '%s', %d, %d, '%s', '%s', %d, '%s' )",
+ $r = q("insert into site ( site_location, site_url, site_access, site_flags, site_update, site_directory, site_register, site_sellpage, site_realm )
+ values ( '%s', '%s', %d, %d, '%s', '%s', %d, '%s', '%s' )",
dbesc($site_location),
dbesc($url),
intval($access_policy),
@@ -2032,7 +2103,8 @@ function import_site($arr,$pubkey) {
dbesc(datetime_convert()),
dbesc($directory_url),
intval($register_policy),
- dbesc($sellpage)
+ dbesc($sellpage),
+ dbesc($site_realm)
);
if(! $r) {
logger('import_site: record create failed. ' . print_r($arr,true));
@@ -2230,6 +2302,21 @@ function process_channel_sync_delivery($sender,$arr,$deliveries) {
$clean = array();
foreach($arr['abook'] as $abook) {
+ 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",
+ dbesc($abook['abook_xchan']),
+ intval($channel['channel_id']),
+ intval(ABOOK_FLAG_SELF)
+ );
+ if($r)
+ contact_remove($channel['channel_id'],$r[0]['abook_id']);
+
+ continue;
+ }
+
// Perform discovery if the referenced xchan hasn't ever been seen on this hub.
// This relies on the undocumented behaviour that red sites send xchan info with the abook