aboutsummaryrefslogtreecommitdiffstats
path: root/Zotlabs
diff options
context:
space:
mode:
Diffstat (limited to 'Zotlabs')
-rw-r--r--Zotlabs/Daemon/Convo.php1
-rw-r--r--Zotlabs/Daemon/Externals.php1
-rw-r--r--Zotlabs/Daemon/Onepoll.php1
-rw-r--r--Zotlabs/Daemon/Queue.php77
-rw-r--r--Zotlabs/Lib/Activity.php64
-rw-r--r--Zotlabs/Lib/ActivityStreams.php62
-rw-r--r--Zotlabs/Lib/DReport.php19
-rw-r--r--Zotlabs/Lib/Enotify.php6
-rw-r--r--Zotlabs/Lib/Libsync.php19
-rw-r--r--Zotlabs/Lib/Libzot.php155
-rw-r--r--Zotlabs/Lib/MessageFilter.php4
-rw-r--r--Zotlabs/Lib/Queue.php63
-rw-r--r--Zotlabs/Lib/QueueWorker.php24
-rw-r--r--Zotlabs/Module/Acl.php1
-rw-r--r--Zotlabs/Module/Cal.php6
-rw-r--r--Zotlabs/Module/Cdav.php12
-rw-r--r--Zotlabs/Module/Channel.php17
-rw-r--r--Zotlabs/Module/Chanview.php16
-rw-r--r--Zotlabs/Module/Connections.php14
-rw-r--r--Zotlabs/Module/Dirsearch.php2
-rw-r--r--Zotlabs/Module/Hq.php59
-rw-r--r--Zotlabs/Module/Like.php1
-rw-r--r--Zotlabs/Module/Magic.php6
-rw-r--r--Zotlabs/Module/Notes.php2
-rw-r--r--Zotlabs/Module/Settings/Account.php1
-rw-r--r--Zotlabs/Module/Settings/Multifactor.php90
-rw-r--r--Zotlabs/Module/Totp_check.php86
-rw-r--r--Zotlabs/Widget/Channel_activities.php4
-rw-r--r--Zotlabs/Widget/Messages.php29
-rw-r--r--Zotlabs/Widget/Pinned.php10
30 files changed, 500 insertions, 352 deletions
diff --git a/Zotlabs/Daemon/Convo.php b/Zotlabs/Daemon/Convo.php
index d1a7e4f4d..e05f7539e 100644
--- a/Zotlabs/Daemon/Convo.php
+++ b/Zotlabs/Daemon/Convo.php
@@ -55,6 +55,7 @@ class Convo {
$AS = new ActivityStreams($message);
if ($AS->is_valid() && is_array($AS->obj)) {
$item = Activity::decode_note($AS);
+ $item['item_fetched'] = true;
Activity::store($channel, $contact['abook_xchan'], $AS, $item);
}
}
diff --git a/Zotlabs/Daemon/Externals.php b/Zotlabs/Daemon/Externals.php
index 2c7c7c172..b67a0e286 100644
--- a/Zotlabs/Daemon/Externals.php
+++ b/Zotlabs/Daemon/Externals.php
@@ -143,6 +143,7 @@ class Externals {
$AS = new ActivityStreams($message);
if ($AS->is_valid() && is_array($AS->obj)) {
$item = Activity::decode_note($AS);
+ $item['item_fetched'] = true;
Activity::store($importer, $contact['hubloc_hash'], $AS, $item);
$total++;
}
diff --git a/Zotlabs/Daemon/Onepoll.php b/Zotlabs/Daemon/Onepoll.php
index 0a30a0c7d..bde39007e 100644
--- a/Zotlabs/Daemon/Onepoll.php
+++ b/Zotlabs/Daemon/Onepoll.php
@@ -172,6 +172,7 @@ class Onepoll {
$AS = new ActivityStreams($message);
if ($AS->is_valid() && is_array($AS->obj)) {
$item = Activity::decode_note($AS);
+ $item['item_fetched'] = true;
Activity::store($importer, $contact['abook_xchan'], $AS, $item);
}
}
diff --git a/Zotlabs/Daemon/Queue.php b/Zotlabs/Daemon/Queue.php
index 3eb7d9d23..b07fe369c 100644
--- a/Zotlabs/Daemon/Queue.php
+++ b/Zotlabs/Daemon/Queue.php
@@ -7,79 +7,56 @@ use Zotlabs\Lib\Queue as LibQueue;
class Queue {
static public function run($argc, $argv) {
-
- require_once('include/items.php');
- require_once('include/bbcode.php');
-
- if ($argc > 1)
- $queue_id = $argv[1];
- else
- $queue_id = EMPTY_STR;
+ $queue_id = ($argc > 1) ? $argv[1] : '';
logger('queue: start');
// delete all queue items more than 3 days old
// but first mark these sites dead if we haven't heard from them in a month
- $r = q("select outq_posturl from outq where outq_created < %s - INTERVAL %s",
- db_utcnow(), db_quoteinterval('3 DAY')
+ $oldqItems = q("select outq_posturl from outq where outq_created < %s - INTERVAL %s",
+ db_utcnow(),
+ db_quoteinterval('3 DAY')
);
- if ($r) {
- foreach ($r as $rr) {
- $h = parse_url($rr['outq_posturl']);
- $desturl = $h['scheme'] . '://' . $h['host'] . (isset($h['port']) ? ':' . $h['port'] : '');
+
+ if ($oldqItems) {
+ foreach ($oldqItems as $qItem) {
+ $h = parse_url($qItem['outq_posturl']);
+ $site_url = $h['scheme'] . '://' . $h['host'] . ((!empty($h['port'])) ? ':' . $h['port'] : '');
q("update site set site_dead = 1 where site_dead = 0 and site_url = '%s' and site_update < %s - INTERVAL %s",
- dbesc($desturl),
- db_utcnow(), db_quoteinterval('1 MONTH')
+ dbesc($site_url),
+ db_utcnow(),
+ db_quoteinterval('1 MONTH')
);
}
}
+ logger('Removing ' . count($oldqItems) . ' old queue entries');
q("DELETE FROM outq WHERE outq_created < %s - INTERVAL %s",
- db_utcnow(), db_quoteinterval('3 DAY')
+ db_utcnow(),
+ db_quoteinterval('3 DAY')
);
+ $deliveries = [];
+
if ($queue_id) {
- $r = q("SELECT * FROM outq WHERE outq_hash = '%s' LIMIT 1",
+ $qItems = q("SELECT * FROM outq WHERE outq_hash = '%s' LIMIT 1",
dbesc($queue_id)
);
+ logger('queue deliver: ' . $qItems[0]['outq_hash'] . ' to ' . $qItems[0]['outq_posturl'], LOGGER_DEBUG);
+ LibQueue::deliver($qItems[0]);
}
else {
-
- // For the first 12 hours we'll try to deliver every 15 minutes
- // After that, we'll only attempt delivery once per hour.
- // This currently only handles the default queue drivers ('zot' or '') which we will group by posturl
- // so that we don't start off a thousand deliveries for a couple of dead hubs.
- // The zot driver will deliver everything destined for a single hub once contact is made (*if* contact is made).
- // Other drivers will have to do something different here and may need their own query.
-
- // Note: this requires some tweaking as new posts to long dead hubs once a day will keep them in the
- // "every 15 minutes" category. We probably need to prioritise them when inserted into the queue
- // or just prior to this query based on recent and long-term delivery history. If we have good reason to believe
- // the site is permanently down, there's no reason to attempt delivery at all, or at most not more than once
- // or twice a day.
-
- $sqlrandfunc = db_getfunc('rand');
-
- $r = q("SELECT *,$sqlrandfunc as rn FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s order by rn limit 1",
+ $qItems = q("SELECT * FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s ",
db_utcnow()
);
- while ($r) {
- foreach ($r as $rv) {
- LibQueue::deliver($rv);
+ if ($qItems) {
+ foreach ($qItems as $qItem) {
+ $deliveries[] = $qItem['outq_hash'];
}
- $r = q("SELECT *,$sqlrandfunc as rn FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s order by rn limit 1",
- db_utcnow()
- );
+ do_delivery($deliveries, true);
}
- }
- if (!$r)
- return;
-
- foreach ($r as $rv) {
- LibQueue::deliver($rv);
- }
-
- return;
+ }
}
+
}
diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php
index 1a1031909..226f50636 100644
--- a/Zotlabs/Lib/Activity.php
+++ b/Zotlabs/Lib/Activity.php
@@ -595,9 +595,9 @@ class Activity {
foreach ($item['term'] as $t) {
switch ($t['ttype']) {
case TERM_HASHTAG:
- // href is required so if we don't have a url in the taxonomy, ignore it and keep going.
+ // id is required so if we don't have a url in the taxonomy, ignore it and keep going.
if ($t['url']) {
- $ret[] = ['type' => 'Hashtag', 'href' => $t['url'], 'name' => '#' . $t['term']];
+ $ret[] = ['type' => 'Hashtag', 'id' => $t['url'], 'name' => '#' . $t['term']];
}
break;
@@ -700,6 +700,13 @@ class Activity {
if (array_key_exists('name', $att) && $att['name']) {
$entry['name'] = html2plain(purify_html($att['name']), 256);
}
+ // Friendica attachments don't match the URL in the body.
+ // This makes it more difficult to detect image duplication in bb_attach()
+ // which adds images to plaintext microblog software. For these we need to examine both the
+ // url and image properties.
+ if (isset($att['image']) && is_string($att['image']) && isset($att['url']) && $att['image'] !== $att['url']) {
+ $entry['image'] = $att['image'];
+ }
if ($entry) {
$ret[] = $entry;
}
@@ -822,7 +829,8 @@ class Activity {
];
}
- $ret['published'] = ((isset($i['created'])) ? datetime_convert('UTC', 'UTC', $i['created'], ATOM_TIME) : datetime_convert());
+ $ret['published'] = datetime_convert('UTC', 'UTC', $i['created'], ATOM_TIME);
+
if (isset($i['created'], $i['edited']) && $i['created'] !== $i['edited']) {
$ret['updated'] = datetime_convert('UTC', 'UTC', $i['edited'], ATOM_TIME);
if ($ret['type'] === 'Create') {
@@ -1690,6 +1698,8 @@ class Activity {
}
}
+ $group_actor = ($person_obj['type'] === 'Group');
+
$r = q("select * from xchan join hubloc on xchan_hash = hubloc_hash where xchan_hash = '%s'",
dbesc($url)
);
@@ -1708,11 +1718,12 @@ class Activity {
);
// update existing xchan record
- q("update xchan set xchan_name = '%s', xchan_pubkey = '%s', xchan_addr = '%s', xchan_network = 'activitypub', xchan_name_date = '%s' where xchan_hash = '%s'",
+ q("update xchan set xchan_name = '%s', xchan_pubkey = '%s', xchan_addr = '%s', xchan_network = 'activitypub', xchan_name_date = '%s', xchan_pubforum = %d where xchan_hash = '%s'",
dbesc(escape_tags($name)),
dbesc(escape_tags($pubkey)),
dbesc(escape_tags($webfinger_addr)),
dbescdate(datetime_convert()),
+ intval($group_actor),
dbesc($url)
);
@@ -1739,7 +1750,8 @@ class Activity {
'xchan_url' => $profile,
'xchan_name' => escape_tags($name),
'xchan_name_date' => datetime_convert(),
- 'xchan_network' => 'activitypub'
+ 'xchan_network' => 'activitypub',
+ 'xchan_pubforum' => intval($group_actor)
]
);
@@ -1768,6 +1780,10 @@ class Activity {
dbesc($url)
);
if (!$zx) {
+ // FIXME: we might need to fetch and store this url immediately
+ // otherwise at least the first post of a yet unknown author might
+ // be stored with the activitypub url instead of the portable id.
+ // Another solution could be to fix the items after Gprobe has done its work.
Master::Summon(['Gprobe', bin2hex($url)]);
}
}
@@ -2218,7 +2234,6 @@ class Activity {
) {
return false;
}
-
// Within our family of projects, Follow/Unfollow of a thread is an internal activity which should not be transmitted,
// hence if we receive it - ignore or reject it.
// Unfollow is not defined by ActivityStreams, which prefers Undo->Follow.
@@ -2514,7 +2529,11 @@ class Activity {
if ($act->type === 'Announce') {
$s['author_xchan'] = self::get_attributed_to_actor_url($act);
$s['mid'] = $act->obj['id'];
- $s['parent_mid'] = $act->obj['id'];
+
+ // Do not force new thread if the announce is from a group actor
+ if ($act->actor['type'] !== 'Group') {
+ $s['parent_mid'] = $act->obj['id'];
+ }
}
// we will need a hook here to extract magnet links e.g. peertube
@@ -2589,13 +2608,13 @@ class Activity {
if ($mps) {
usort($mps,[ '\Zotlabs\Lib\Activity', 'vid_sort' ]);
foreach ($mps as $m) {
- if (intval($m['height']) < 500 && Activity::media_not_in_body($m['href'],$s['body'])) {
+ if (intval($m['height']) < 500 && self::media_not_in_body($m['href'],$s['body'])) {
$s['body'] = $tag . $m['href'] . '[/video]' . "\r\n" . $s['body'];
break;
}
}
}
- elseif (is_string($act->obj['url']) && Activity::media_not_in_body($act->obj['url'],$s['body'])) {
+ elseif (is_string($act->obj['url']) && self::media_not_in_body($act->obj['url'],$s['body'])) {
$s['body'] = $tag . $act->obj['url'] . '[/video]' . "\r\n" . $s['body'];
}
@@ -2843,6 +2862,10 @@ class Activity {
$is_child_node = true;
}
+ if (empty($item['item_fetched'])) {
+ $item['owner_xchan'] = $observer_hash;
+ }
+
$allowed = false;
// TODO: not implemented
@@ -2919,7 +2942,7 @@ class Activity {
// The $item['item_fetched'] flag is set in fetch_and_store_parents().
// In this case we should check against author permissions because sender is not owner.
- if (perm_is_allowed($channel['channel_id'], ((isset($item['item_fetched']) && $item['item_fetched']) ? $item['author_xchan'] : $observer_hash), 'send_stream') || $is_sys_channel) {
+ if (perm_is_allowed($channel['channel_id'], ((!empty($item['item_fetched'])) ? $item['author_xchan'] : $observer_hash), 'send_stream') || $is_sys_channel) {
$allowed = true;
}
// TODO: not implemented
@@ -3228,7 +3251,7 @@ class Activity {
$item = $hookinfo['item'];
if ($item) {
- $item['item_fetched'] = 1;
+ $item['item_fetched'] = true;
if (intval($channel['channel_system']) && intval($item['item_private'])) {
$p = [];
@@ -3342,7 +3365,7 @@ class Activity {
return false;
}
- */
+
static public function fetch_and_store_replies($channel, $arr) {
logger('fetching replies');
@@ -3399,6 +3422,7 @@ class Activity {
}
}
+*/
/* this is deprecated and not used anymore
static function announce_note($channel, $observer_hash, $act) {
@@ -3674,7 +3698,21 @@ class Activity {
if ($a['type'] === 'image/svg+xml' && strpos($item['body'], '[/svg]')) {
continue;
}
- if (self::media_not_in_body($a['href'], $item['body'])) {
+ // Friendica attachment weirdness
+ // Check both the attachment image and href since they can be different and the one in the href is a different link with different resolution.
+ // Otheriwse you'll get duplicated images
+ if (isset($a['image'])) {
+ if (self::media_not_in_body($a['image'], $item['body']) && self::media_not_in_body($a['href'], $item['body'])) {
+ if (isset($a['name']) && $a['name']) {
+ $alt = htmlspecialchars($a['name'], ENT_QUOTES);
+ $item['body'] = '[img=' . $a['href'] . ']' . $alt . '[/img]' . "\r\n" . $item['body'];
+ } else {
+ $item['body'] = '[img]' . $a['href'] . '[/img]' . "\r\n" . $item['body'];
+ }
+ }
+ continue;
+ }
+ elseif (self::media_not_in_body($a['href'], $item['body'])) {
if (isset($a['name']) && $a['name']) {
$alt = htmlspecialchars($a['name'], ENT_QUOTES);
$item['body'] = '[img=' . $a['href'] . ']' . $alt . '[/img]' . "\r\n" . $item['body'];
diff --git a/Zotlabs/Lib/ActivityStreams.php b/Zotlabs/Lib/ActivityStreams.php
index a07fdacb7..cfed53b3c 100644
--- a/Zotlabs/Lib/ActivityStreams.php
+++ b/Zotlabs/Lib/ActivityStreams.php
@@ -113,7 +113,7 @@ class ActivityStreams {
// fetch recursive or embedded activities
if ($this->obj && is_array($this->obj) && array_key_exists('object', $this->obj)) {
- $this->obj['object'] = $this->get_compound_property($this->obj['object']);
+ $this->obj['object'] = $this->get_compound_property('object', $this->obj);
}
if ($this->obj && is_array($this->obj) && isset($this->obj['actor']))
@@ -146,35 +146,58 @@ class ActivityStreams {
}
/**
+ * @brief get single property from Activity object
+ *
+ * @param string $property
+ * @param mixed $default return value if property or object not set
+ * or object is a string id which could not be fetched.
+ * @return mixed
+ */
+ public function objprop(string $property, mixed $default = false): mixed {
+ $x = $this->get_property_obj($property,$this->obj);
+ return (isset($x)) ? $x : $default;
+ }
+
+ /**
* @brief Collects all recipients.
*
- * @param string $base
+ * @param mixed $base
* @param string $namespace (optional) default empty
* @return array
*/
- function collect_recips($base = '', $namespace = '') {
- $x = [];
+ public function collect_recips(mixed $base = '', string $namespace = ''): array {
+ $result = [];
+ $tmp = [];
$fields = ['to', 'cc', 'bto', 'bcc', 'audience'];
- foreach ($fields as $f) {
- $y = $this->get_compound_property($f, $base, $namespace);
- if ($y) {
- if (!is_array($this->raw_recips)) {
- $this->raw_recips = [];
- }
-
- if (!is_array($y)) {
- $y = [$y];
- }
- $this->raw_recips[$f] = $y;
- $x = array_merge($x, $y);
+ foreach ($fields as $field) {
+ // don't expand these yet
+ $values = $this->get_property_obj($field, $base, $namespace);
+ if ($values) {
+ $values = force_array($values);
+ $tmp[$field] = $values;
+ $result = array_values(array_unique(array_merge($result, $values)));
+ }
+ // Merge the object recipients if they exist.
+ $values = $this->objprop($field);
+ if ($values) {
+ $values = force_array($values);
+ $tmp[$field] = ((isset($tmp[$field])) ? array_merge($tmp[$field], $values) : $values);
+ $result = array_values(array_unique(array_merge($result, $values)));
+ }
+ // remove duplicates
+ if (isset($tmp[$field])) {
+ $tmp[$field] = array_values(array_unique($tmp[$field]));
}
}
-// not yet ready for prime time
-// $x = $this->expand($x,$base,$namespace);
- return $x;
+ $this->raw_recips = $tmp;
+
+ // not yet ready for prime time
+ // $result = $this->expand($result,$base,$namespace);
+ return $result;
}
+
function expand($arr, $base = '', $namespace = '') {
$ret = [];
@@ -341,6 +364,7 @@ class ActivityStreams {
*/
function get_compound_property($property, $base = '', $namespace = '', $first = false) {
$x = $this->get_property_obj($property, $base, $namespace);
+
if ($this->is_url($x)) {
$y = $this->fetch_property($x);
if (is_array($y)) {
diff --git a/Zotlabs/Lib/DReport.php b/Zotlabs/Lib/DReport.php
index 2263529b2..e22ed65be 100644
--- a/Zotlabs/Lib/DReport.php
+++ b/Zotlabs/Lib/DReport.php
@@ -94,19 +94,6 @@ class DReport {
if(! $c)
return false;
- // legacy zot recipients add a space and their name to the xchan. remove it if true.
-
- $legacy_recipient = strpos($dr['recipient'], ' ');
- if($legacy_recipient !== false) {
- $legacy_recipient_parts = explode(' ', $dr['recipient'], 2);
- $rxchan = $legacy_recipient_parts[0];
- }
- else {
- $rxchan = $dr['recipient'];
- }
-
-
-
// is the recipient one of our connections, or do we want to store every report?
$pcf = get_pconfig($c[0]['channel_id'],'system','dreport_store_all');
@@ -117,7 +104,7 @@ class DReport {
// So if a remote site says they can't find us, that's no big surprise
// and just creates a lot of extra report noise
- if(($dr['location'] !== z_root()) && ($dr['sender'] === $rxchan) && ($dr['status'] === 'recipient not found'))
+ if(($dr['location'] !== z_root()) && ($dr['sender'] === $dr['recipient']) && ($dr['status'] === 'recipient not found'))
return false;
// If you have a private post with a recipient list, every single site is going to report
@@ -126,14 +113,14 @@ class DReport {
// have a channel on that site.
$r = q("select hubloc_id from hubloc where hubloc_hash = '%s' and hubloc_url = '%s'",
- dbesc($rxchan),
+ dbesc($dr['recipient']),
dbesc($dr['location'])
);
if((! $r) && ($dr['status'] === 'recipient_not_found'))
return false;
$r = q("select abook_id from abook where abook_xchan = '%s' and abook_channel = %d limit 1",
- dbesc($rxchan),
+ dbesc($dr['recipient']),
intval($c[0]['channel_id'])
);
if($r)
diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php
index 2015b260d..585761cc4 100644
--- a/Zotlabs/Lib/Enotify.php
+++ b/Zotlabs/Lib/Enotify.php
@@ -828,7 +828,7 @@ class Enotify {
: (($item['obj_type'] === 'Answer') ? sprintf( t('voted on %s\'s poll'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]') : sprintf( t('commented on %s\'s post'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]'))
);
- if($item['verb'] === ACTIVITY_SHARE) {
+ if($item['verb'] === ACTIVITY_SHARE && empty($item['owner']['xchan_pubforum'])) {
$itemem_text = sprintf( t('repeated %s\'s post'), '[bdi]' . $item['author']['xchan_name'] . '[/bdi]');
}
@@ -860,7 +860,7 @@ class Enotify {
// convert this logic into a json array just like the system notifications
- $who = (($item['verb'] === ACTIVITY_SHARE) ? 'owner' : 'author');
+ $who = (($item['verb'] === ACTIVITY_SHARE && empty($item['owner']['xchan_pubforum'])) ? 'owner' : 'author');
$body = html2plain(bbcode($item['body'], ['drop_media' => true, 'tryoembed' => false]), 75, true);
if ($body) {
$body = htmlentities($body, ENT_QUOTES, 'UTF-8', false);
@@ -869,7 +869,7 @@ class Enotify {
$x = array(
'notify_link' => $item['llink'],
'name' => $item[$who]['xchan_name'],
- 'addr' => $item[$who]['xchan_addr'] ?? $item[$who]['xchan_url'],
+ 'addr' => $item[$who]['xchan_addr'] ? $item[$who]['xchan_addr'] : $item[$who]['xchan_url'],
'url' => $item[$who]['xchan_url'],
'photo' => $item[$who]['xchan_photo_s'],
'when' => (($edit) ? datetime_convert('UTC', date_default_timezone_get(), $item['edited']) : datetime_convert('UTC', date_default_timezone_get(), $item['created'])),
diff --git a/Zotlabs/Lib/Libsync.php b/Zotlabs/Lib/Libsync.php
index 19361c4ae..d52b501e4 100644
--- a/Zotlabs/Lib/Libsync.php
+++ b/Zotlabs/Lib/Libsync.php
@@ -766,11 +766,10 @@ class Libsync {
*
* @param array $sender
* @param array $arr
- * @param boolean $absolute (optional) default false
* @return array
*/
- static function sync_locations($sender, $arr, $absolute = false) {
+ static function sync_locations($sender, $arr) {
$ret = [];
$what = '';
@@ -787,9 +786,6 @@ class Libsync {
if (isset($arr['locations']) && $arr['locations']) {
- if ($absolute)
- Libzot::check_location_move($sender['hash'], $arr['locations']);
-
$xisting = q("select * from hubloc where hubloc_hash = '%s'",
dbesc($sender['hash'])
);
@@ -933,14 +929,7 @@ class Libsync {
$what .= 'primary_hub ';
$changed = true;
}
- elseif ($absolute) {
- // Absolute sync - make sure the current primary is correctly reflected in the xchan
- $pr = hubloc_change_primary($r[0]);
- if ($pr) {
- $what .= 'xchan_primary ';
- $changed = true;
- }
- }
+
if (intval($r[0]['hubloc_deleted']) && (!intval($location['deleted']))) {
q("update hubloc set hubloc_deleted = 0, hubloc_updated = '%s' where hubloc_id_url = '%s'",
dbesc(datetime_convert()),
@@ -1009,9 +998,9 @@ class Libsync {
}
}
- // get rid of any hubs we have for this channel which weren't reported.
+ // get rid of any hublocs we have for this channel which weren't reported.
- if ($absolute && $xisting) {
+ if ($xisting) {
foreach ($xisting as $x) {
if (!array_key_exists('updated', $x)) {
logger('Deleting unreferenced hub location ' . $x['hubloc_addr']);
diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php
index c635fdb17..93d8a39c0 100644
--- a/Zotlabs/Lib/Libzot.php
+++ b/Zotlabs/Lib/Libzot.php
@@ -101,7 +101,6 @@ class Libzot {
static function build_packet($channel, $type = 'activity', $recipients = null, $msg = [], $encoding = 'activitystreams', $remote_key = null, $methods = '') {
$sig_method = get_config('system', 'signature_algorithm', 'sha256');
-
$data = [
'type' => $type,
'encoding' => $encoding,
@@ -115,9 +114,9 @@ class Libzot {
}
if ($msg) {
- $actor = channel_url($channel);
- if ($encoding === 'activitystreams' && array_key_exists('actor', $msg) && is_string($msg['actor']) && $actor === $msg['actor']) {
- $msg = JSalmon::sign($msg, $actor, $channel['channel_prvkey']);
+ $actors = get_hubloc_id_urls_by_x($channel['channel_hash']);
+ if ($encoding === 'activitystreams' && array_key_exists('actor', $msg) && is_string($msg['actor']) && in_array($msg['actor'], $actors)) {
+ $msg = JSalmon::sign($msg, $actors[0], $channel['channel_prvkey']);
}
$data['data'] = $msg;
}
@@ -1051,19 +1050,9 @@ class Libzot {
}
if (is_array($x) && array_key_exists('delivery_report', $x) && is_array($x['delivery_report'])) {
-
foreach ($x['delivery_report'] as $xx) {
call_hooks('dreport_process', $xx);
if (is_array($xx) && array_key_exists('message_id', $xx) && DReport::is_storable($xx)) {
-
- // legacy recipients add a space and their name to the xchan. split those if true.
- $legacy_recipient = strpos($xx['recipient'], ' ');
- if ($legacy_recipient !== false) {
- $legacy_recipient_parts = explode(' ', $xx['recipient'], 2);
- $xx['recipient'] = $legacy_recipient_parts[0];
- $xx['name'] = $legacy_recipient_parts[1];
- }
-
q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan ) values ( '%s', '%s', '%s','%s','%s','%s','%s' ) ",
dbesc($xx['message_id']),
dbesc($xx['location']),
@@ -1254,7 +1243,7 @@ class Libzot {
return;
}
- $r = q("select hubloc_hash, hubloc_network, hubloc_url from hubloc where hubloc_id_url = '%s' order by hubloc_id desc",
+ $r = q("select hubloc_hash, hubloc_network, hubloc_url from hubloc where hubloc_id_url = '%s' and hubloc_deleted = 0 order by hubloc_id desc",
dbesc($AS->actor['id'])
);
@@ -1262,7 +1251,7 @@ class Libzot {
// Author is unknown to this site. Perform channel discovery and try again.
$z = discover_by_webbie($AS->actor['id']);
if ($z) {
- $r = q("select hubloc_hash, hubloc_network, hubloc_url from hubloc where hubloc_id_url = '%s' order by hubloc_id desc",
+ $r = q("select hubloc_hash, hubloc_network, hubloc_url from hubloc where hubloc_id_url = '%s' and hubloc_deleted = 0 order by hubloc_id desc",
dbesc($AS->actor['id'])
);
}
@@ -1377,7 +1366,7 @@ class Libzot {
return false;
}
$x = self::find_parent($env, $act);
- if ($x === $act->id || $x === $act->obj['id']) {
+ if ($x === $act->id || (is_array($act->obj) && array_key_exists('id', $act->obj) && $x === $act->obj['id'])) {
return true;
}
}
@@ -1435,7 +1424,9 @@ class Libzot {
$r = [];
- $c = q("select channel_id, channel_hash from channel where channel_removed = 0");
+ $c = q("select channel_id, channel_hash from channel where channel_hash != '%s' and channel_removed = 0",
+ dbesc($msg['sender'])
+ );
if ($c) {
foreach ($c as $cc) {
@@ -1463,9 +1454,10 @@ class Libzot {
if ($tag['type'] === 'Mention' && (strpos($tag['href'], z_root()) !== false)) {
$address = basename($tag['href']);
if ($address) {
- $z = q("select channel_hash as hash from channel where channel_address = '%s'
+ $z = q("select channel_hash as hash from channel where channel_address = '%s' and channel_hash != '%s'
and channel_removed = 0 limit 1",
- dbesc($address)
+ dbesc($address),
+ dbesc($msg['sender'])
);
if ($z) {
$r[] = $z[0]['hash'];
@@ -1484,9 +1476,10 @@ class Libzot {
$thread_parent = self::find_parent($msg, $act);
if ($thread_parent) {
- $z = q("select channel_hash as hash from channel left join item on channel.channel_id = item.uid where ( item.thr_parent = '%s' OR item.parent_mid = '%s' ) ",
+ $z = q("select channel_hash as hash from channel left join item on channel.channel_id = item.uid where ( item.thr_parent = '%s' OR item.parent_mid = '%s' ) and channel_hash != '%s'",
+ dbesc($thread_parent),
dbesc($thread_parent),
- dbesc($thread_parent)
+ dbesc($msg['sender'])
);
if ($z) {
foreach ($z as $zv) {
@@ -1980,7 +1973,7 @@ class Libzot {
$ret = [];
- $signer = q("select hubloc_hash, hubloc_url from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
+ $signer = q("select hubloc_hash, hubloc_url from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' order by hubloc_id desc limit 1",
dbesc($a['signature']['signer'])
);
@@ -2008,7 +2001,7 @@ class Libzot {
continue;
}
- $r = q("select hubloc_hash, hubloc_network from hubloc where hubloc_id_url = '%s'",
+ $r = q("select hubloc_hash, hubloc_network from hubloc where hubloc_id_url = '%s' order by hubloc_id desc",
dbesc($AS->actor['id'])
);
@@ -2313,120 +2306,6 @@ class Libzot {
/**
- * @brief Processes delivery of profile.
- *
- * @param string $sender
- * @param array $arr
- * @param array $deliveries (unused)
- * @return void
- * @see import_directory_profile()
- *
- */
- static function process_profile_delivery($sender, $arr, $deliveries) {
-
- logger('process_profile_delivery', LOGGER_DEBUG);
-
- $r = q("select xchan_addr from xchan where xchan_hash = '%s' limit 1",
- dbesc($sender)
- );
- if ($r) {
- Libzotdir::import_directory_profile($sender, $arr, $r[0]['xchan_addr'], UPDATE_FLAGS_UPDATED, 0);
- }
- }
-
-
- /**
- * @brief
- *
- * @param string $sender
- * @param array $arr
- * @param array $deliveries (unused) deliveries is irrelevant
- * @return void
- */
- static function process_location_delivery($sender, $arr, $deliveries) {
-
- // deliveries is irrelevant
- logger('process_location_delivery', LOGGER_DEBUG);
-
- $r = q("select * from xchan where xchan_hash = '%s' limit 1",
- dbesc($sender)
- );
- if ($r) {
- $xchan = ['id' => $r[0]['xchan_guid'], 'id_sig' => $r[0]['xchan_guid_sig'],
- 'hash' => $r[0]['xchan_hash'], 'public_key' => $r[0]['xchan_pubkey']];
- }
- if (array_key_exists('locations', $arr) && $arr['locations']) {
- $x = Libsync::sync_locations($xchan, $arr, true);
- logger('results: ' . print_r($x, true), LOGGER_DEBUG);
- if ($x['changed']) {
- //$guid = random_string() . '@' . App::get_hostname();
- Libzotdir::update_modtime($sender, $r[0]['xchan_guid'], $arr['locations'][0]['address'], UPDATE_FLAGS_UPDATED);
- }
- }
- }
-
- /**
- * @brief Checks for a moved channel and sets the channel_moved flag.
- *
- * Currently the effect of this flag is to turn the channel into 'read-only' mode.
- * New content will not be processed (there was still an issue with blocking the
- * ability to post comments as of 10-Mar-2016).
- * We do not physically remove the channel at this time. The hub admin may choose
- * to do so, but is encouraged to allow a grace period of several days in case there
- * are any issues migrating content. This packet will generally be received by the
- * original site when the basic channel import has been processed.
- *
- * This will only be executed on the old location
- * if a new location is reported and there is only one location record.
- * The rest of the hubloc syncronisation will be handled within
- * sync_locations
- *
- * @param string $sender_hash A channel hash
- * @param array $locations
- * @return void
- */
- static function check_location_move($sender_hash, $locations) {
-
- if (!$locations)
- return;
-
- if (count($locations) != 1)
- return;
-
- $loc = $locations[0];
-
- $r = q("select * from channel where channel_hash = '%s' limit 1",
- dbesc($sender_hash)
- );
-
- if (!$r)
- return;
-
- if ($loc['url'] !== z_root()) {
- $x = q("update channel set channel_moved = '%s' where channel_hash = '%s' limit 1",
- dbesc($loc['url']),
- dbesc($sender_hash)
- );
-
- // federation plugins may wish to notify connections
- // of the move on singleton networks
-
- $arr = [
- 'channel' => $r[0],
- 'locations' => $locations
- ];
- /**
- * @hooks location_move
- * Called when a new location has been provided to a UNO channel (indicating a move rather than a clone).
- * * \e array \b channel
- * * \e array \b locations
- */
- call_hooks('location_move', $arr);
- }
- }
-
-
- /**
* @brief Returns an array with all known distinct hubs for this channel.
*
* @param array $channel an associative array which must contain
diff --git a/Zotlabs/Lib/MessageFilter.php b/Zotlabs/Lib/MessageFilter.php
index 7d6dcbe8e..e7382c0d5 100644
--- a/Zotlabs/Lib/MessageFilter.php
+++ b/Zotlabs/Lib/MessageFilter.php
@@ -25,7 +25,7 @@ class MessageFilter {
if ($exclude) {
foreach ($exclude as $word) {
- $word = trim($word);
+ $word = html_entity_decode(trim($word));
if (! $word) {
continue;
}
@@ -73,7 +73,7 @@ class MessageFilter {
if ($include) {
foreach ($include as $word) {
- $word = trim($word);
+ $word = html_entity_decode(trim($word));
if (! $word) {
continue;
}
diff --git a/Zotlabs/Lib/Queue.php b/Zotlabs/Lib/Queue.php
index c3f9cda20..348a2a079 100644
--- a/Zotlabs/Lib/Queue.php
+++ b/Zotlabs/Lib/Queue.php
@@ -65,16 +65,51 @@ class Queue {
);
}
-
- static function remove($id,$channel_id = 0) {
- logger('queue: remove queue item ' . $id,LOGGER_DEBUG);
+ public static function remove($id, $channel_id = 0) {
+ logger('queue: remove queue item ' . $id, LOGGER_DEBUG);
$sql_extra = (($channel_id) ? " and outq_channel = " . intval($channel_id) . " " : '');
- q("DELETE FROM outq WHERE outq_hash = '%s' $sql_extra",
+ // figure out what endpoint it is going to.
+ $record = q("select outq_posturl from outq where outq_hash = '%s' $sql_extra",
dbesc($id)
);
- }
+ if ($record) {
+ q("DELETE FROM outq WHERE outq_hash = '%s' $sql_extra",
+ dbesc($id)
+ );
+
+ // If there's anything remaining in the queue for this site, move one of them to the next active
+ // queue run by setting outq_scheduled back to the present. We may be attempting to deliver it
+ // as a 'piled_up' delivery, but this ensures the site has an active queue entry as long as queued
+ // entries still exist for it. This fixes an issue where one immediate delivery left everything
+ // else for that site undeliverable since all the other entries had been pushed far into the future.
+
+ $x = null;
+ $sql_quirks = ((get_config('system', 'db_skip_locked_supported')) ? 'SKIP LOCKED' : 'NOWAIT');
+
+ q("START TRANSACTION");
+
+ $r = q("SELECT outq_hash FROM outq WHERE outq_posturl = '%s' LIMIT 1 FOR UPDATE $sql_quirks",
+ dbesc($record[0]['outq_posturl'])
+ );
+
+ if ($r) {
+ $x = q("UPDATE outq SET outq_scheduled = '%s' WHERE outq_hash = '%s'",
+ dbesc(datetime_convert()),
+ dbesc($r[0]['outq_hash'])
+ );
+ }
+
+ if ($x) {
+ q("COMMIT");
+ }
+ else {
+ q("ROLLBACK");
+ }
+
+ }
+ }
static function remove_by_posturl($posturl) {
logger('queue: remove queue posturl ' . $posturl,LOGGER_DEBUG);
@@ -84,8 +119,6 @@ class Queue {
);
}
-
-
static function set_delivered($id,$channel = 0) {
logger('queue: set delivered ' . $id,LOGGER_DEBUG);
$sql_extra = (($channel['channel_id']) ? " and outq_channel = " . intval($channel['channel_id']) . " " : '');
@@ -152,17 +185,19 @@ class Queue {
$y = q("select site_update, site_dead from site where site_url = '%s' ",
dbesc($base)
);
- if($y) {
- if(intval($y[0]['site_dead'])) {
+
+ if ($y) {
+ // Don't bother delivering if the site is dead.
+ // And if we haven't heard from the site in over a month - let them through but 3 strikes you're out.
+ if (intval($y[0]['site_dead']) || ($y[0]['site_update'] < datetime_convert('UTC', 'UTC', 'now - 1 month') && $outq['outq_priority'] > 20)) {
+ q("update dreport set dreport_result = '%s' where dreport_queue = '%s'",
+ dbesc('site dead'),
+ dbesc($outq['outq_hash'])
+ );
self::remove_by_posturl($outq['outq_posturl']);
logger('dead site ignored ' . $base);
return;
}
- if($y[0]['site_update'] < datetime_convert('UTC','UTC','now - 1 month')) {
- self::update($outq['outq_hash'], 10);
- logger('immediate delivery deferred for site ' . $base);
- return;
- }
}
else {
diff --git a/Zotlabs/Lib/QueueWorker.php b/Zotlabs/Lib/QueueWorker.php
index fd2ebd7e1..696fb79fc 100644
--- a/Zotlabs/Lib/QueueWorker.php
+++ b/Zotlabs/Lib/QueueWorker.php
@@ -137,10 +137,26 @@ class QueueWorker {
self::$workermaxage = self::$workermaxage > 120 ? self::$workermaxage : 300;
}
- q("update workerq set workerq_reservationid = null where workerq_reservationid is not null and workerq_processtimeout < %s",
+ self::qstart();
+
+ // skip locked is preferred but is not supported by mariadb < 10.6 which is still used a lot - hence make it optional
+ $sql_quirks = ((get_config('system', 'db_skip_locked_supported')) ? 'SKIP LOCKED' : 'NOWAIT');
+
+ $r = q("SELECT workerq_id FROM workerq WHERE workerq_reservationid IS NOT NULL AND workerq_processtimeout < %s FOR UPDATE $sql_quirks",
db_utcnow()
);
+ if ($r) {
+ $ids = ids_to_querystr($r, 'workerq_id');
+ $u = dbq("update workerq set workerq_reservationid = null where workerq_id in ($ids)");
+ }
+
+ self::qcommit();
+
+ //q("update workerq set workerq_reservationid = null where workerq_reservationid is not null and workerq_processtimeout < %s",
+ //db_utcnow()
+ //);
+
//usleep(self::$workersleep);
$workers = dbq("select count(distinct workerq_reservationid) as total from workerq where workerq_reservationid is not null");
@@ -175,10 +191,10 @@ class QueueWorker {
self::qstart();
- // This is probably the better solution but is not supported by mariadb < 10.6 which is still used a lot.
- // $work = dbq("SELECT workerq_id FROM workerq WHERE workerq_reservationid IS NULL ORDER BY workerq_priority DESC, workerq_id ASC LIMIT 1 FOR UPDATE SKIP LOCKED;");
+ // skip locked is preferred but is not supported by mariadb < 10.6 which is still used a lot - hence make it optional
+ $sql_quirks = ((get_config('system', 'db_skip_locked_supported')) ? 'SKIP LOCKED' : 'NOWAIT');
- $work = dbq("SELECT workerq_id, workerq_cmd FROM workerq WHERE workerq_reservationid IS NULL ORDER BY workerq_priority DESC, workerq_id ASC LIMIT 1 FOR UPDATE;");
+ $work = dbq("SELECT workerq_id, workerq_cmd FROM workerq WHERE workerq_reservationid IS NULL ORDER BY workerq_priority DESC, workerq_id ASC LIMIT 1 FOR UPDATE $sql_quirks");
if (!$work) {
self::qcommit();
diff --git a/Zotlabs/Module/Acl.php b/Zotlabs/Module/Acl.php
index bcd79c873..b98f5621e 100644
--- a/Zotlabs/Module/Acl.php
+++ b/Zotlabs/Module/Acl.php
@@ -378,6 +378,7 @@ class Acl extends \Zotlabs\Web\Controller {
"name" => $g['name'],
"id" => urlencode($g['id']),
"xid" => $g['hash'],
+ "url" => $g['url'],
"link" => $clink,
"nick" => ((strpos($g['nick'],'@')) ? substr($g['nick'],0,strpos($g['nick'],'@')) : $g['nick']),
"self" => (intval($g['abook_self']) ? 'abook-self' : ''),
diff --git a/Zotlabs/Module/Cal.php b/Zotlabs/Module/Cal.php
index 9049fe7d0..70d8aa755 100644
--- a/Zotlabs/Module/Cal.php
+++ b/Zotlabs/Module/Cal.php
@@ -65,13 +65,9 @@ class Cal extends Controller {
nav_set_selected('Calendar');
- head_add_css('/library/fullcalendar/packages/core/main.min.css');
- head_add_css('/library/fullcalendar/packages/daygrid/main.min.css');
+ head_add_js('/library/fullcalendar/dist/index.global.js');
head_add_css('cdav_calendar.css');
- head_add_js('/library/fullcalendar/packages/core/main.min.js');
- head_add_js('/library/fullcalendar/packages/daygrid/main.min.js');
-
$sql_extra = permissions_sql($channel['channel_id'], get_observer_hash(), 'event');
if(! perm_is_allowed($channel['channel_id'], get_observer_hash(), 'view_contacts') || App::$profile['hide_friends'])
diff --git a/Zotlabs/Module/Cdav.php b/Zotlabs/Module/Cdav.php
index 8e77515ce..b6d7ff5a3 100644
--- a/Zotlabs/Module/Cdav.php
+++ b/Zotlabs/Module/Cdav.php
@@ -901,19 +901,9 @@ class Cdav extends Controller {
//Display calendar(s) here
if(argc() <= 3 && argv(1) === 'calendar') {
-
- head_add_css('/library/fullcalendar/packages/core/main.min.css');
- head_add_css('/library/fullcalendar/packages/daygrid/main.min.css');
- head_add_css('/library/fullcalendar/packages/timegrid/main.min.css');
- head_add_css('/library/fullcalendar/packages/list/main.min.css');
+ head_add_js('/library/fullcalendar/dist/index.global.js');
head_add_css('cdav_calendar.css');
- head_add_js('/library/fullcalendar/packages/core/main.min.js');
- head_add_js('/library/fullcalendar/packages/interaction/main.min.js');
- head_add_js('/library/fullcalendar/packages/daygrid/main.min.js');
- head_add_js('/library/fullcalendar/packages/timegrid/main.min.js');
- head_add_js('/library/fullcalendar/packages/list/main.min.js');
-
$o = '';
$sources = '';
$resource_id = '';
diff --git a/Zotlabs/Module/Channel.php b/Zotlabs/Module/Channel.php
index d3d72ca10..748d990cb 100644
--- a/Zotlabs/Module/Channel.php
+++ b/Zotlabs/Module/Channel.php
@@ -247,9 +247,7 @@ class Channel extends Controller {
$channel_acl = ['allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => ''];
}
-
if ($perms['post_wall']) {
-
$x = [
'is_owner' => $is_owner,
'allow_location' => ((($is_owner || $observer) && (intval(get_pconfig(App::$profile['profile_uid'], 'system', 'use_browser_location')))) ? true : false),
@@ -273,6 +271,12 @@ class Channel extends Controller {
$o .= status_editor($a, $x, false, 'Channel');
}
+ // Add pinned content
+ if (!x($_REQUEST, 'mid') && !$search) {
+ $pinned = new \Zotlabs\Widget\Pinned;
+ $r = $pinned->widget(intval(App::$profile['profile_uid']), [ITEM_TYPE_POST]);
+ $o .= $r['html'];
+ }
}
@@ -423,17 +427,14 @@ class Channel extends Controller {
$items = [];
}
- // Add pinned content
- if (!x($_REQUEST, 'mid') && !$search) {
- $pinned = new \Zotlabs\Widget\Pinned;
- $r = $pinned->widget(intval(App::$profile['profile_uid']), [ITEM_TYPE_POST]);
- $o .= $r['html'];
- }
+
$mode = (($search) ? 'search' : 'channel');
if ((!$update) && (!$load)) {
+
+
//if we got a decoded hash we must encode it again before handing to javascript
$mid = gen_link_id($mid);
diff --git a/Zotlabs/Module/Chanview.php b/Zotlabs/Module/Chanview.php
index f43432376..e178b27df 100644
--- a/Zotlabs/Module/Chanview.php
+++ b/Zotlabs/Module/Chanview.php
@@ -16,17 +16,17 @@ class Chanview extends \Zotlabs\Web\Controller {
$r = null;
- if(isset($_REQUEST['hash']) && $_REQUEST['hash']) {
+ if(!empty($_REQUEST['hash'])) {
$r = q("select * from xchan where xchan_hash = '%s' and xchan_deleted = 0",
dbesc($_REQUEST['hash'])
);
}
- if(isset($_REQUEST['address']) && $_REQUEST['address']) {
+ if(!empty($_REQUEST['address'])) {
$r = q("select * from xchan where xchan_addr = '%s' and xchan_deleted = 0",
dbesc(punify($_REQUEST['address']))
);
}
- elseif(local_channel() && isset($_REQUEST['cid']) && $_REQUEST['cid']) {
+ elseif(local_channel() && !empty($_REQUEST['cid'])) {
$r = q("SELECT abook.*, xchan.*
FROM abook left join xchan on abook_xchan = xchan_hash
WHERE abook_channel = %d and abook_id = %d and xchan_deleted = 0",
@@ -34,8 +34,7 @@ class Chanview extends \Zotlabs\Web\Controller {
intval($_REQUEST['cid'])
);
}
- elseif(isset($_REQUEST['url']) && $_REQUEST['url']) {
-
+ elseif(!empty($_REQUEST['url'])) {
// if somebody re-installed they will have more than one xchan, use the most recent name date as this is
// the most useful consistently ascending table item we have.
@@ -56,7 +55,7 @@ class Chanview extends \Zotlabs\Web\Controller {
if(! App::$poi) {
logger('mod_chanview: fallback');
- if(isset($_REQUEST['address']) && $_REQUEST['address']) {
+ if(!empty($_REQUEST['address'])) {
$href = Webfinger::zot_url(punify($_REQUEST['address']));
if($href) {
$_REQUEST['url'] = $href;
@@ -65,9 +64,9 @@ class Chanview extends \Zotlabs\Web\Controller {
$r = null;
- if(isset($_REQUEST['url']) && $_REQUEST['url']) {
+ if(!empty($_REQUEST['url'])) {
- $zf = Zotfinger::exec($_REQUEST['url'], null);
+ $zf = Zotfinger::exec($_REQUEST['url']);
if(array_path_exists('signature/signer',$zf) && $zf['signature']['signer'] === $_REQUEST['url'] && intval($zf['signature']['header_valid'])) {
Libzot::import_xchan($zf['data']);
@@ -79,6 +78,7 @@ class Chanview extends \Zotlabs\Web\Controller {
}
}
if(! $r) {
+
if(discover_by_webbie($_REQUEST['url'])) {
$r = q("select * from xchan where xchan_url = '%s' and xchan_deleted = 0",
dbesc($_REQUEST['url'])
diff --git a/Zotlabs/Module/Connections.php b/Zotlabs/Module/Connections.php
index ea30b9b9e..e0f463c76 100644
--- a/Zotlabs/Module/Connections.php
+++ b/Zotlabs/Module/Connections.php
@@ -145,6 +145,7 @@ class Connections extends \Zotlabs\Web\Controller {
}
$search = ((x($_REQUEST,'search')) ? notags(trim($_REQUEST['search'])) : '');
+ $search_xchan = ((x($_REQUEST,'search_xchan')) ? notags(trim($_REQUEST['search_xchan'])) : '');
$tabs = array(
/*
@@ -233,10 +234,15 @@ class Connections extends \Zotlabs\Web\Controller {
if($search) {
$search_hdr = $search;
- $search_txt = dbesc(protect_sprintf(preg_quote($search)));
- $searching = true;
+ $search_txt = (($search_xchan) ? urldecode($search_xchan) : preg_quote($search));
+
+ if ($search_xchan) {
+ $sql_extra .= " AND xchan_hash = '" . protect_sprintf(dbesc($search_txt)) . "' ";
+ }
+ else {
+ $sql_extra .= " AND xchan_name LIKE '%%" . protect_sprintf(dbesc($search_txt)) . "%%' ";
+ }
}
- $sql_extra .= (($searching) ? protect_sprintf(" AND xchan_name like '%$search_txt%' ") : "");
if(isset($_REQUEST['gid']) && $_REQUEST['gid']) {
$sql_extra .= " and xchan_hash in ( select xchan from pgrp_member where gid = " . intval($_REQUEST['gid']) . " and uid = " . intval(local_channel()) . " ) ";
@@ -396,7 +402,7 @@ class Connections extends \Zotlabs\Web\Controller {
'$search' => $search_hdr,
'$label' => t('Search'),
'$role_label' => t('Contact role'),
- '$desc' => t('Search your connections'),
+ '$desc' => $search ?? t('Search your connections'),
'$finding' => (($searching) ? t('Contact search') . ": '" . $search . "'" : ""),
'$submit' => t('Find'),
'$edit' => t('Edit'),
diff --git a/Zotlabs/Module/Dirsearch.php b/Zotlabs/Module/Dirsearch.php
index 34678af57..c0df8a1e3 100644
--- a/Zotlabs/Module/Dirsearch.php
+++ b/Zotlabs/Module/Dirsearch.php
@@ -263,7 +263,7 @@ class Dirsearch extends Controller {
xprof.xprof_homepage as homepage,
xprof.xprof_hometown as hometown,
xprof.xprof_keywords as keywords
- from xchan left join xprof on xchan_hash = xprof_hash left join hubloc on hubloc_hash = xchan_hash
+ from xchan left join xprof on xchan_hash = xprof_hash left join hubloc on (hubloc_id_url = xchan_url and hubloc_hash = xchan_hash)
where hubloc_primary = 1 and hubloc_updated > %s - INTERVAL %s and ( $logic $sql_extra ) $hub_query and xchan_network = 'zot6' and xchan_system = 0 and xchan_hidden = 0 and xchan_orphan = 0 and xchan_deleted = 0
$safesql $order $qlimit",
db_utcnow(),
diff --git a/Zotlabs/Module/Hq.php b/Zotlabs/Module/Hq.php
index 0b2d0ea7d..77d56e9a2 100644
--- a/Zotlabs/Module/Hq.php
+++ b/Zotlabs/Module/Hq.php
@@ -51,13 +51,20 @@ class Hq extends \Zotlabs\Web\Controller {
$o = '';
if($item_hash) {
+ // select the target item with a bias to our own item
+ $sql_order = ((local_channel() > $sys['channel_id']) ? 'DESC' : 'ASC');
- $r = q("select id, uid, mid, parent_mid, thr_parent, verb, item_type, item_deleted, item_blocked from item where mid = '%s' limit 1",
+ $r = q("select id, uid, mid, parent_mid, thr_parent, verb, item_type, item_deleted, item_blocked from item where uid in (%d, %d) and mid = '%s' order by uid $sql_order limit 2",
+ intval(local_channel()),
+ intval($sys['channel_id']),
dbesc($item_hash)
);
if($r) {
$target_item = $r[0];
+ if (intval($target_item['uid']) === intval($sys['channel_id'])) {
+ $sys_item = true;
+ }
}
//if the item is to be moderated redirect to /moderate
@@ -158,19 +165,18 @@ class Hq extends \Zotlabs\Web\Controller {
}
if($load && $target_item) {
- $r = null;
-
- $r = q("SELECT item.id AS item_id FROM item
- WHERE uid = %d
- AND mid = '%s'
- $item_normal
- LIMIT 1",
- intval(local_channel()),
- dbesc($target_item['parent_mid'])
- );
- if(!$r) {
- $sys_item = true;
+ if (!$sys_item) {
+ $r = q("SELECT item.id AS item_id FROM item
+ WHERE uid = %d
+ AND mid = '%s'
+ $item_normal
+ LIMIT 1",
+ intval(local_channel()),
+ dbesc($target_item['parent_mid'])
+ );
+ }
+ else {
$sql_extra = item_permissions_sql($sys['channel_id']);
$r = q("SELECT item.id AS item_id FROM item
@@ -184,20 +190,18 @@ class Hq extends \Zotlabs\Web\Controller {
}
}
elseif($update && $target_item) {
- $r = null;
-
- $r = q("SELECT item.parent AS item_id FROM item
- WHERE uid = %d
- AND parent_mid = '%s'
- $item_normal_update
- $simple_update
- LIMIT 1",
- intval(local_channel()),
- dbesc($target_item['parent_mid'])
- );
-
- if(!$r) {
- $sys_item = true;
+ if (!$sys_item) {
+ $r = q("SELECT item.parent AS item_id FROM item
+ WHERE uid = %d
+ AND parent_mid = '%s'
+ $item_normal_update
+ $simple_update
+ LIMIT 1",
+ intval(local_channel()),
+ dbesc($target_item['parent_mid'])
+ );
+ }
+ else {
$sql_extra = item_permissions_sql($sys['channel_id']);
$r = q("SELECT item.parent AS item_id FROM item
@@ -245,6 +249,7 @@ class Hq extends \Zotlabs\Web\Controller {
$options['offset'] = $_REQUEST['offset'] ?? 0;
$options['type'] = $_REQUEST['type'] ?? '';
+ $options['author'] = ((isset($_REQUEST['author'])) ? urldecode($_REQUEST['author']) : '');
$ret = Messages::get_messages_page($options);
diff --git a/Zotlabs/Module/Like.php b/Zotlabs/Module/Like.php
index 98fa7db5a..ef41faedd 100644
--- a/Zotlabs/Module/Like.php
+++ b/Zotlabs/Module/Like.php
@@ -546,6 +546,7 @@ class Like extends Controller {
$arr['deny_cid'] = $deny_cid;
$arr['deny_gid'] = $deny_gid;
$arr['item_private'] = $private;
+ $arr['created'] = datetime_convert();
call_hooks('post_local', $arr);
diff --git a/Zotlabs/Module/Magic.php b/Zotlabs/Module/Magic.php
index bfd38d2fa..6d997c662 100644
--- a/Zotlabs/Module/Magic.php
+++ b/Zotlabs/Module/Magic.php
@@ -16,7 +16,7 @@ class Magic extends Controller {
'url' => '',
'message' => ''
];
-
+
logger('mod_magic: invoked', LOGGER_DEBUG);
logger('args: ' . print_r($_REQUEST,true),LOGGER_DATA);
@@ -43,12 +43,12 @@ class Magic extends Controller {
$basepath = $parsed['scheme'] . '://' . $parsed['host'] . (isset($parsed['port']) ? ':' . $parsed['port'] : '');
$owapath = SConfig::get($basepath,'system','openwebauth', $basepath . '/owa');
- // This is ready-made for a plugin that provides a blacklist or "ask me" before blindly authenticating.
+ // This is ready-made for a plugin that provides a blacklist or "ask me" before blindly authenticating.
// By default, we'll proceed without asking.
$arr = [
'channel_id' => local_channel(),
- 'destination' => $dest,
+ 'destination' => $dest,
'proceed' => true
];
diff --git a/Zotlabs/Module/Notes.php b/Zotlabs/Module/Notes.php
index 57b8f30db..2fd719f25 100644
--- a/Zotlabs/Module/Notes.php
+++ b/Zotlabs/Module/Notes.php
@@ -39,7 +39,7 @@ class Notes extends Controller {
}
set_pconfig(local_channel(),'notes','text',$body);
- $ret['html'] = bbcode($body);
+ $ret['html'] = bbcode($body, ['tryoembed' => false]);
$ret['success'] = true;
}
diff --git a/Zotlabs/Module/Settings/Account.php b/Zotlabs/Module/Settings/Account.php
index 5e1fb176e..85e7c793f 100644
--- a/Zotlabs/Module/Settings/Account.php
+++ b/Zotlabs/Module/Settings/Account.php
@@ -105,6 +105,7 @@ class Account {
'$password1' => array('npassword', t('Enter New Password'), '', ''),
'$password2' => array('confirm', t('Confirm New Password'), '', t('Leave password fields blank unless changing')),
'$submit' => t('Submit'),
+ '$mfa' => t('Multi-Factor Authentication'),
'$email' => array('email', t('DId2 or Email Address:'), $email, '', '', $attremail),
'$email_hidden' => (($attremail) ? $email : ''),
'$removeme' => t('Remove Account'),
diff --git a/Zotlabs/Module/Settings/Multifactor.php b/Zotlabs/Module/Settings/Multifactor.php
new file mode 100644
index 000000000..8b91bc87a
--- /dev/null
+++ b/Zotlabs/Module/Settings/Multifactor.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace Zotlabs\Module\Settings;
+
+use App;
+use chillerlan\QRCode\QRCode;
+use Zotlabs\Lib\AConfig;
+use Zotlabs\Lib\System;
+use OTPHP\TOTP;
+use ParagonIE\ConstantTime\Base32;
+
+
+class Multifactor {
+ public function post() {
+ check_form_security_token_redirectOnErr('/settings/multifactor', 'settings_mfa');
+
+ $account = App::get_account();
+ if (!$account) {
+ return;
+ }
+
+ if (empty($_POST['password'])) {
+ notice(t('Password is required') . EOL);
+ return;
+ }
+
+ $password = trim($_POST['password']);
+ if(!account_verify_password($account['account_email'], $password)) {
+ notice(t('The provided password is not correct') . EOL);
+ return;
+ }
+
+ $enable_mfa = isset($_POST['enable_mfa']) ? (int) $_POST['enable_mfa'] : false;
+ AConfig::Set($account['account_id'], 'system', 'mfa_enabled', $enable_mfa);
+ if ($enable_mfa) {
+ $_SESSION['2FA_VERIFIED'] = true;
+ }
+ }
+
+ public function get() {
+ $account = App::get_account();
+ if (!$account) {
+ return '';
+ }
+
+ if (!$account['account_external']) {
+ $otp = TOTP::create();
+ $otp->setLabel($account['account_email']);
+ // $otp->setLabel(rawurlencode(System::get_platform_name()));
+ $otp->setIssuer(rawurlencode(System::get_platform_name()));
+
+ $mySecret = trim(Base32::encodeUpper(random_bytes(32)), '=');
+ $otp = TOTP::create($mySecret);
+ q("UPDATE account set account_external = '%s' where account_id = %d",
+ dbesc($otp->getSecret()),
+ intval($account['account_id'])
+ );
+ $account['account_external'] = $otp->getSecret();
+ }
+
+ $otp = TOTP::create($account['account_external']);
+ $otp->setLabel($account['account_email']);
+ $otp->setIssuer(rawurlencode(System::get_platform_name()));
+ $uri = $otp->getProvisioningUri();
+ return replace_macros(get_markup_template('totp_setup.tpl'),
+ [
+ '$form_security_token' => get_form_security_token("settings_mfa"),
+ '$title' => t('Account Multi-Factor Authentication'),
+ '$secret_text' => t('This is your generated secret. It may be used in some cases if the QR image cannot be read. Please store it in a safe place.'),
+ '$test_title' => t('Please enter the code from your authenticator app'),
+ '$test_title_sub' => t('You will only be able to enable MFA if the test passes'),
+ '$qrcode' => (new QRCode())->render($uri),
+ '$uri' => $uri,
+ '$secret' => ($account['account_external'] ?? ''),
+ '$test_pass' => t("Congratulations, the provided code was correct"),
+ '$test_fail' => t("Incorrect code"),
+ '$enable_mfa' => [
+ 'enable_mfa',
+ t('Enable Multi-Factor Authentication'),
+ AConfig::Get($account['account_id'], 'system', 'mfa_enabled'),
+ t('Logging in will require you to be in possession of your smartphone'),
+ [t('No'), t('Yes')]
+ ],
+ '$password' => ['password', t('Your account password'), '', t('Required')],
+ '$submit' => t('Submit'),
+ '$test' => t('Test')
+ ]
+ );
+ }
+}
diff --git a/Zotlabs/Module/Totp_check.php b/Zotlabs/Module/Totp_check.php
new file mode 100644
index 000000000..8212d3716
--- /dev/null
+++ b/Zotlabs/Module/Totp_check.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Zotlabs\Module;
+
+use App;
+use Zotlabs\Web\Controller;
+use OTPHP\TOTP;
+
+class Totp_check extends Controller {
+
+ public function post() {
+ $retval = ['status' => false];
+ $static = $_POST['totp_code_static'] ?? false;
+
+ if (!local_channel()) {
+ if ($static) {
+ goaway(z_root());
+ }
+
+ json_return_and_die($retval);
+ }
+
+ $account = App::get_account();
+ if (!$account) {
+ json_return_and_die($retval);
+ }
+
+ $secret = $account['account_external'];
+ $input = (isset($_POST['totp_code'])) ? trim($_POST['totp_code']) : '';
+
+ if ($secret && $input) {
+ $otp = TOTP::create($secret); // create TOTP object from the secret.
+ if ($otp->verify($_POST['totp_code']) || $input === $secret ) {
+ logger('otp_success');
+ $_SESSION['2FA_VERIFIED'] = true;
+
+ if ($static) {
+ goaway(z_root());
+ }
+
+ $retval['status'] = true;
+ json_return_and_die($retval);
+ }
+ logger('otp_fail');
+ }
+
+ if ($static) {
+ if(empty($_SESSION['totp_try_count'])) {
+ $_SESSION['totp_try_count'] = 1;
+ }
+
+ if ($_SESSION['totp_try_count'] > 2) {
+ goaway('logout');
+ }
+
+ $_SESSION['totp_try_count']++;
+ goaway(z_root());
+ }
+
+ json_return_and_die($retval);
+ }
+
+ public function get() {
+
+ if (!local_channel() || App::$module === 'totp_check') {
+ goaway(z_root());
+ }
+
+ $account = App::get_account();
+ if (!$account) {
+ return t('Account not found.');
+ }
+
+ $id = $account['account_email'];
+
+ return replace_macros(get_markup_template('totp.tpl'),
+ [
+ '$header' => t('Multifactor Verification'),
+ '$id' => $id,
+ '$desc' => t('Please enter the verification key from your authenticator app'),
+ '$submit' => t('Verify')
+ ]
+ );
+ }
+}
+
diff --git a/Zotlabs/Widget/Channel_activities.php b/Zotlabs/Widget/Channel_activities.php
index 06080f8c8..a799ea81e 100644
--- a/Zotlabs/Widget/Channel_activities.php
+++ b/Zotlabs/Widget/Channel_activities.php
@@ -179,7 +179,7 @@ class Channel_activities {
$account = App::get_account();
- $r = q("SELECT channel_id, channel_name, xchan_photo_s FROM channel
+ $r = q("SELECT channel_id, channel_name, xchan_addr, xchan_photo_s FROM channel
LEFT JOIN xchan ON channel_hash = xchan_hash
WHERE channel_account_id = %d
AND channel_id != %d AND channel_removed = 0",
@@ -223,7 +223,7 @@ class Channel_activities {
$i[] = [
'url' => z_root() . '/manage/' . $rr['channel_id'],
'title' => '',
- 'summary' => '<img src="' . $rr['xchan_photo_s'] . '" class="menu-img-2">' . $rr['channel_name'],
+ 'summary' => '<div class="text-truncate lh-sm"><img src="' . $rr['xchan_photo_s'] . '" class="menu-img-2">' . '<strong>' . $rr['channel_name'] . '</strong><br><small class="opacity-75">' . $rr['xchan_addr'] . '</small></div>',
'footer' => $footer
];
diff --git a/Zotlabs/Widget/Messages.php b/Zotlabs/Widget/Messages.php
index 8c413e16e..0a8900c4f 100644
--- a/Zotlabs/Widget/Messages.php
+++ b/Zotlabs/Widget/Messages.php
@@ -35,7 +35,8 @@ class Messages {
'notice_messages_title' => t('Notices'),
'loading' => t('Loading'),
'empty' => t('No messages'),
- 'unseen_count' => t('Unseen')
+ 'unseen_count' => t('Unseen'),
+ 'filter' => t('Filter by name or address')
]
]);
@@ -47,7 +48,8 @@ class Messages {
return;
$offset = $options['offset'] ?? 0;
- $type = $options['type'] ?? 'default';
+ $type = $options['type'] ?? '';
+ $author = $options['author'] ?? '';
if ($offset == -1) {
return;
@@ -63,10 +65,12 @@ class Messages {
$entries = [];
$limit = 30;
$dummy_order_sql = '';
+ $author_sql = '';
$loadtime = (($offset) ? $_SESSION['messages_loadtime'] : datetime_convert());
$vnotify = get_pconfig(local_channel(), 'system', 'vnotify', -1);
- $vnotify_sql = '';
+ $vnotify_sql_c = '';
+ $vnotify_sql_i = '';
if (!($vnotify & VNOTIFY_LIKE)) {
$vnotify_sql_c = " AND c.verb NOT IN ('" . dbesc(ACTIVITY_LIKE) . "', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
@@ -77,6 +81,10 @@ class Messages {
$vnotify_sql_i = " AND i.verb NOT IN ('" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
+ if($author) {
+ $author_sql = " AND i.owner_xchan = '" . protect_sprintf(dbesc($author)) . "' ";
+ }
+
switch($type) {
case 'direct':
$type_sql = ' AND i.item_private = 2 ';
@@ -100,6 +108,7 @@ class Messages {
AND i.created <= '%s'
$type_sql
AND i.item_thread_top = 1
+ $author_sql
$item_normal_i
ORDER BY i.created DESC $dummy_order_sql
LIMIT $limit OFFSET $offset",
@@ -153,7 +162,7 @@ class Messages {
$summary = '...';
}
else {
- $summary = substr_words($summary, 68);
+ $summary = substr_words($summary, 140);
}
switch(intval($item['item_private'])) {
@@ -169,6 +178,7 @@ class Messages {
$entries[$i]['author_name'] = $item['author']['xchan_name'];
$entries[$i]['author_addr'] = (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url']);
+ $entries[$i]['author_img'] = $item['author']['xchan_photo_s'];
$entries[$i]['info'] = $info;
$entries[$i]['created'] = datetime_convert('UTC', date_default_timezone_get(), $item['created']);
$entries[$i]['summary'] = $summary;
@@ -240,6 +250,7 @@ class Messages {
if (!local_channel())
return;
+
$limit = 30;
$offset = 0;
@@ -247,7 +258,14 @@ class Messages {
$offset = intval($options['offset']);
}
- $notices = q("SELECT * FROM notify WHERE uid = %d
+ $author_url = $options['author'] ?? '';
+ $author_sql = '';
+
+ if($author_url) {
+ $author_sql = " AND url = '" . protect_sprintf(dbesc($author_url)) . "' ";
+ }
+
+ $notices = q("SELECT * FROM notify WHERE uid = %d $author_sql
ORDER BY created DESC LIMIT $limit OFFSET $offset",
intval(local_channel())
);
@@ -265,6 +283,7 @@ class Messages {
$entries[$i]['author_name'] = $notice['xname'];
$entries[$i]['author_addr'] = $notice['url'];
+ $entries[$i]['author_img'] = $notice['photo'];// $item['author']['xchan_photo_s'];
$entries[$i]['info'] = '';
$entries[$i]['created'] = datetime_convert('UTC', date_default_timezone_get(), $notice['created']);
$entries[$i]['summary'] = $summary;
diff --git a/Zotlabs/Widget/Pinned.php b/Zotlabs/Widget/Pinned.php
index 83036e98c..6d2618deb 100644
--- a/Zotlabs/Widget/Pinned.php
+++ b/Zotlabs/Widget/Pinned.php
@@ -42,6 +42,8 @@ class Pinned {
$observer = \App::get_observer();
+ xchan_query($items);
+
foreach($items as $item) {
$midb64 = gen_link_id($item['mid']);
@@ -49,8 +51,8 @@ class Pinned {
if(isset($observer['xchan_hash']) && in_array($observer['xchan_hash'], get_pconfig($item['uid'], 'pinned_hide', $midb64, [])))
continue;
- $author = channelx_by_hash($item['author_xchan']);
- $owner = channelx_by_hash($item['owner_xchan']);
+ $author = $item['author'];
+ $owner = $item['owner'];
$profile_avatar = $author['xchan_photo_m'];
$profile_link = chanlink_hash($item['author_xchan']);
@@ -159,7 +161,9 @@ class Pinned {
'hide' => (! $is_new && isset($observer['xchan_hash']) && $observer['xchan_hash'] != $owner['xchan_hash'] ? t("Don't show") : ''),
// end toolbar buttons
'modal_dismiss' => t('Close'),
- 'responses' => $conv_responses
+ 'responses' => $conv_responses,
+ 'author_id' => (($author['xchan_addr']) ? $author['xchan_addr'] : $author['xchan_url'])
+
];
$tpl = get_markup_template('pinned_item.tpl');