2018-06-04 00:49:48 +00:00
< ? php
2016-05-20 02:42:45 +00:00
namespace Zotlabs\Daemon ;
2018-06-01 02:42:13 +00:00
use Zotlabs\Lib\Libzot ;
2018-07-03 05:43:41 +00:00
use Zotlabs\Lib\Queue ;
2018-08-23 06:04:37 +00:00
use Zotlabs\Lib\Activity ;
use Zotlabs\Lib\ActivityStreams ;
2018-08-23 23:50:00 +00:00
use Zotlabs\Lib\ActivityPub ;
2018-08-23 06:04:37 +00:00
use Zotlabs\Lib\LDSignatures ;
2018-06-01 02:42:13 +00:00
2016-05-20 02:42:45 +00:00
require_once ( 'include/html2plain.php' );
2016-10-26 03:43:24 +00:00
require_once ( 'include/conversation.php' );
2017-04-19 00:26:50 +00:00
require_once ( 'include/bbcode.php' );
2016-05-20 02:42:45 +00:00
/*
2018-06-28 01:43:39 +00:00
* Notifier - message dispatch and preparation for delivery
2016-05-20 02:42:45 +00:00
*
* The basic flow is :
* Identify the type of message
* Collect any information that needs to be sent
* Convert it into a suitable generic format for sending
* Figure out who the recipients are and if we need to relay
* through a conversation owner
* Once we know what recipients are involved , collect a list of
* destination sites
* Build and store a queue item for each unique site and invoke
* a delivery process for each site or a small number of sites ( 1 - 3 )
* and add a slight delay between each delivery invocation if desired ( usually )
*
*/
/*
* The notifier is typically called with :
*
2020-06-15 07:29:56 +00:00
* Zotlabs\Daemon\Run :: Summon ( [ 'Notifier' , COMMAND , ITEM_ID ] );
2016-05-20 02:42:45 +00:00
*
* where COMMAND is one of the following :
*
* activity ( in diaspora . php , dfrn_confirm . php , profiles . php )
* comment - import ( in diaspora . php , items . php )
* comment - new ( in item . php )
* drop ( in diaspora . php , items . php , photos . php )
* edit_post ( in item . php )
* event ( in events . php )
* expire ( in items . php )
* like ( in like . php , poke . php )
* mail ( in message . php )
* tag ( in photos . php , poke . php , tagger . php )
* tgroup ( in items . php )
* wall - new ( in photos . php , item . php )
*
* and ITEM_ID is the id of the item in the database that needs to be sent to others .
*
* ZOT
2018-06-28 01:43:39 +00:00
* permissions_create abook_id
* permissions_accept abook_id
* permissions_reject abook_id
* permissions_update abook_id
* refresh_all channel_id
2019-05-07 03:22:08 +00:00
* purge xchan_hash
2018-06-28 01:43:39 +00:00
* purge_all channel_id
* expire channel_id
* relay item_id ( item was relayed to owner , we will deliver it as owner )
* single_activity item_id ( deliver to a singleton network from the appropriate clone )
* single_mail mail_id ( deliver to a singleton network from the appropriate clone )
* location channel_id
* request channel_id xchan_hash message_id
* rating xlink_id
* keychange channel_id
2016-05-20 02:42:45 +00:00
*
*/
class Notifier {
2018-06-28 01:43:39 +00:00
static public $deliveries = [];
static public $recipients = [];
static public $env_recips = [];
static public $packet_type = 'activity' ;
static public $encoding = 'activitystreams' ;
static public $encoded_item = null ;
static public $channel = null ;
static public $private = false ;
2019-09-10 04:03:33 +00:00
static public function run ( $argc , $argv ) {
2016-05-20 02:42:45 +00:00
2019-09-10 04:03:33 +00:00
if ( $argc < 3 ) {
2016-05-20 02:42:45 +00:00
return ;
2019-09-10 04:03:33 +00:00
}
2016-05-20 02:42:45 +00:00
2019-09-10 04:03:33 +00:00
logger ( 'notifier: invoked: ' . print_r ( $argv , true ), LOGGER_DEBUG , LOG_INFO );
2016-05-20 02:42:45 +00:00
$cmd = $argv [ 1 ];
$item_id = $argv [ 2 ];
2019-09-10 04:03:33 +00:00
if ( ! $item_id ) {
2016-05-20 02:42:45 +00:00
return ;
2019-09-10 04:03:33 +00:00
}
2016-05-20 02:42:45 +00:00
2019-08-11 22:10:12 +00:00
self :: $deliveries = [];
self :: $recipients = [];
self :: $env_recips = [];
self :: $packet_type = 'activity' ;
self :: $encoding = 'activitystreams' ;
self :: $encoded_item = null ;
self :: $channel = null ;
self :: $private = false ;
2016-05-20 02:42:45 +00:00
$sys = get_sys_channel ();
$top_level = false ;
2018-06-28 01:43:39 +00:00
2016-05-20 02:42:45 +00:00
$url_recipients = array ();
$normal_mode = true ;
2018-06-28 01:43:39 +00:00
2019-09-10 04:03:33 +00:00
if ( $cmd === 'request' ) {
2018-06-28 01:43:39 +00:00
2016-05-20 02:42:45 +00:00
$xchan = $argv [ 3 ];
2019-09-10 04:03:33 +00:00
if ( $argc < 5 ) {
2018-06-28 01:43:39 +00:00
return ;
}
2016-05-20 02:42:45 +00:00
2018-06-28 01:43:39 +00:00
self :: $channel = channelx_by_n ( $item_id );
2016-05-20 02:42:45 +00:00
2018-06-28 01:43:39 +00:00
self :: $private = true ;
self :: $recipients [] = $xchan ;
self :: $packet_type = 'request' ;
2018-07-04 05:18:07 +00:00
self :: $encoded_item = [ 'message_id' => $argv [ 4 ] ];
self :: $encoding = 'zot' ;
2016-05-20 02:42:45 +00:00
$normal_mode = false ;
}
2019-09-10 04:03:33 +00:00
elseif ( $cmd === 'keychange' ) {
2018-06-28 01:43:39 +00:00
self :: $channel = channelx_by_n ( $item_id );
2017-07-25 03:23:00 +00:00
$r = q ( " select abook_xchan from abook where abook_channel = %d " ,
intval ( $item_id )
);
2019-09-10 04:03:33 +00:00
if ( $r ) {
foreach ( $r as $rr ) {
2018-06-28 01:43:39 +00:00
self :: $recipients [] = $rr [ 'abook_xchan' ];
2017-07-25 03:23:00 +00:00
}
}
2018-06-28 01:43:39 +00:00
self :: $private = false ;
self :: $packet_type = 'keychange' ;
2018-07-04 05:18:07 +00:00
self :: $encoded_item = get_pconfig ( self :: $channel [ 'channel_id' ], 'system' , 'keychange' );
self :: $encoding = 'zot' ;
2017-07-25 03:23:00 +00:00
$normal_mode = false ;
}
2019-09-10 04:03:33 +00:00
elseif ( in_array ( $cmd , [ 'permissions_update' , 'permissions_reject' , 'permissions_accept' , 'permissions_create' ])) {
2018-09-20 06:53:26 +00:00
2016-05-20 02:42:45 +00:00
// Get the (single) recipient
2018-09-20 06:53:26 +00:00
2016-05-20 02:42:45 +00:00
$r = q ( " select * from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_self = 0 " ,
intval ( $item_id )
);
2018-09-20 06:53:26 +00:00
2019-09-10 04:03:33 +00:00
if ( $r ) {
$recip = array_shift ( $r );
$uid = $recip [ 'abook_channel' ];
2016-05-20 02:42:45 +00:00
// Get the sender
2018-06-28 01:43:39 +00:00
self :: $channel = channelx_by_n ( $uid );
2019-09-10 04:03:33 +00:00
if ( self :: $channel ) {
2018-09-20 06:53:26 +00:00
2019-09-10 04:03:33 +00:00
$perm_update = [ 'sender' => self :: $channel , 'recipient' => $recip , 'success' => false , 'deliveries' => '' ];
2018-08-23 06:04:37 +00:00
2019-09-10 04:03:33 +00:00
switch ( $cmd ) {
2018-08-23 06:04:37 +00:00
case 'permissions_create' :
2018-08-23 23:50:00 +00:00
ActivityPub :: permissions_create ( $perm_update );
2018-08-23 06:04:37 +00:00
break ;
case 'permissions_accept' :
2018-08-23 23:50:00 +00:00
ActivityPub :: permissions_accept ( $perm_update );
2018-08-23 06:04:37 +00:00
break ;
2019-11-14 22:45:55 +00:00
case 'permissions_update' :
ActivityPub :: permissions_update ( $perm_update );
break ;
2018-08-23 06:04:37 +00:00
default :
break ;
}
2019-09-10 04:03:33 +00:00
if ( ! $perm_update [ 'success' ]) {
2018-08-23 06:04:37 +00:00
call_hooks ( $cmd , $perm_update );
}
2016-05-20 02:42:45 +00:00
2019-09-10 04:03:33 +00:00
if ( $perm_update [ 'success' ]) {
if ( $perm_update [ 'deliveries' ]) {
2018-06-28 01:43:39 +00:00
self :: $deliveries [] = $perm_update [ 'deliveries' ];
do_delivery ( self :: $deliveries );
2016-05-20 02:42:45 +00:00
}
return ;
}
else {
2019-09-10 04:03:33 +00:00
self :: $recipients [] = $recip [ 'abook_xchan' ];
2018-06-28 01:43:39 +00:00
self :: $private = false ;
self :: $packet_type = 'refresh' ;
2019-11-14 22:47:15 +00:00
self :: $env_recips = [ $recip [ 'xchan_hash' ] ];
2016-05-20 02:42:45 +00:00
}
}
}
}
2019-09-10 04:03:33 +00:00
elseif ( $cmd === 'refresh_all' ) {
2016-05-20 02:42:45 +00:00
logger ( 'notifier: refresh_all: ' . $item_id );
2018-06-28 01:43:39 +00:00
self :: $channel = channelx_by_n ( $item_id );
2016-05-20 02:42:45 +00:00
$r = q ( " select abook_xchan from abook where abook_channel = %d " ,
intval ( $item_id )
);
2019-09-10 04:03:33 +00:00
if ( $r ) {
foreach ( $r as $rr ) {
2018-06-28 01:43:39 +00:00
self :: $recipients [] = $rr [ 'abook_xchan' ];
2016-05-20 02:42:45 +00:00
}
}
2018-06-28 01:43:39 +00:00
self :: $private = false ;
self :: $packet_type = 'refresh' ;
2016-05-20 02:42:45 +00:00
}
2019-09-10 04:03:33 +00:00
elseif ( $cmd === 'purge' ) {
2019-06-05 00:53:21 +00:00
$xchan = $argv [ 3 ];
2019-05-07 03:22:08 +00:00
logger ( 'notifier: purge: ' . $item_id . ' => ' . $xchan );
if ( ! $xchan ) {
return ;
}
self :: $channel = channelx_by_n ( $item_id );
self :: $recipients = [ $xchan ];
self :: $private = true ;
self :: $packet_type = 'purge' ;
}
2019-09-10 04:03:33 +00:00
elseif ( $cmd === 'purge_all' ) {
2016-05-20 02:42:45 +00:00
logger ( 'notifier: purge_all: ' . $item_id );
2019-05-07 03:22:08 +00:00
self :: $channel = channelx_by_n ( $item_id );
2018-06-28 01:43:39 +00:00
2019-06-06 06:08:17 +00:00
self :: $recipients = [];
2016-09-04 00:32:48 +00:00
$r = q ( " select abook_xchan from abook where abook_channel = %d and abook_self = 0 " ,
2016-05-20 02:42:45 +00:00
intval ( $item_id )
);
2019-06-06 06:08:17 +00:00
if ( ! $r ) {
return ;
}
foreach ( $r as $rr ) {
self :: $recipients [] = $rr [ 'abook_xchan' ];
2016-05-20 02:42:45 +00:00
}
2019-06-06 06:08:17 +00:00
2018-06-28 01:43:39 +00:00
self :: $private = false ;
self :: $packet_type = 'purge' ;
2016-05-20 02:42:45 +00:00
}
else {
// Normal items
// Fetch the target item
$r = q ( " SELECT * FROM item WHERE id = %d and parent != 0 LIMIT 1 " ,
intval ( $item_id )
);
2019-09-10 04:03:33 +00:00
if ( ! $r ) {
2016-05-20 02:42:45 +00:00
return ;
2019-09-10 04:03:33 +00:00
}
2016-05-20 02:42:45 +00:00
xchan_query ( $r );
$r = fetch_post_tags ( $r );
2019-09-10 04:03:33 +00:00
$target_item = array_shift ( $r );
2020-10-07 03:53:44 +00:00
if ( $target_item [ 'author' ][ 'xchan_network' ] === 'anon' ) {
logger ( 'notifier: target item author is not a fetchable actor' , LOGGER_DEBUG );
return ;
}
2016-05-20 02:42:45 +00:00
$deleted_item = false ;
2019-09-10 04:03:33 +00:00
if ( intval ( $target_item [ 'item_deleted' ])) {
2016-05-20 02:42:45 +00:00
logger ( 'notifier: target item ITEM_DELETED' , LOGGER_DEBUG );
$deleted_item = true ;
}
2019-09-22 16:16:07 +00:00
if ( ! in_array ( intval ( $target_item [ 'item_type' ]), [ ITEM_TYPE_POST , ITEM_TYPE_MAIL ] )) {
if ( intval ( $target_item [ 'item_type' ] == ITEM_TYPE_CUSTOM )) {
$hookinfo = [
2019-10-11 23:45:04 +00:00
'targetitem' => $target_item ,
'deliver' => false
2019-09-22 16:16:07 +00:00
];
call_hooks ( 'customitem_deliver' , $hookinfo );
}
2019-10-11 23:45:04 +00:00
if ( ! $hookinfo [ 'deliver' ]) {
2019-09-22 16:16:07 +00:00
logger ( 'notifier: target item not forwardable: type ' . $target_item [ 'item_type' ], LOGGER_DEBUG );
return ;
}
2016-05-20 02:42:45 +00:00
}
2016-06-09 23:11:58 +00:00
// Check for non published items, but allow an exclusion for transmitting hidden file activities
2019-09-10 04:03:33 +00:00
if ( intval ( $target_item [ 'item_unpublished' ]) || intval ( $target_item [ 'item_delayed' ]) ||
2017-06-20 19:45:05 +00:00
intval ( $target_item [ 'item_blocked' ]) ||
2016-06-09 23:11:58 +00:00
( intval ( $target_item [ 'item_hidden' ]) && ( $target_item [ 'obj_type' ] !== ACTIVITY_OBJ_FILE ))) {
2016-05-20 02:42:45 +00:00
logger ( 'notifier: target item not published, so not forwardable' , LOGGER_DEBUG );
return ;
}
$s = q ( " select * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1 " ,
intval ( $target_item [ 'uid' ])
);
2019-09-10 04:03:33 +00:00
if ( $s ) {
self :: $channel = array_shift ( $s );
}
2016-05-20 02:42:45 +00:00
2019-09-10 04:03:33 +00:00
if ( self :: $channel [ 'channel_hash' ] !== $target_item [ 'author_xchan' ] && self :: $channel [ 'channel_hash' ] !== $target_item [ 'owner_xchan' ]) {
2018-06-28 01:43:39 +00:00
logger ( " notifier: Sending channel is not owner { $target_item [ 'owner_xchan' ] } or author { $target_item [ 'author_xchan' ] } " , LOGGER_NORMAL , LOG_WARNING );
2016-05-20 02:42:45 +00:00
return ;
}
2018-09-11 10:36:31 +00:00
$thread_is_public = false ;
2016-05-20 02:42:45 +00:00
2019-09-10 04:03:33 +00:00
if ( $target_item [ 'mid' ] === $target_item [ 'parent_mid' ]) {
2016-05-20 02:42:45 +00:00
$parent_item = $target_item ;
$top_level_post = true ;
}
else {
2018-06-28 01:43:39 +00:00
2016-05-20 02:42:45 +00:00
// fetch the parent item
$r = q ( " SELECT * from item where id = %d order by id asc " ,
intval ( $target_item [ 'parent' ])
);
2019-09-10 04:03:33 +00:00
if ( ! $r ) {
2016-05-20 02:42:45 +00:00
return ;
2019-09-10 04:03:33 +00:00
}
2016-05-20 02:42:45 +00:00
xchan_query ( $r );
$r = fetch_post_tags ( $r );
2019-09-10 04:03:33 +00:00
$parent_item = array_shift ( $r );
2016-05-20 02:42:45 +00:00
$top_level_post = false ;
2019-11-18 22:02:54 +00:00
$thread_is_public = (( intval ( $parent_item [ 'item_private' ])) ? false : true ) ;
2016-05-20 02:42:45 +00:00
}
// avoid looping of discover items 12/4/2014
2019-09-10 04:03:33 +00:00
if ( $sys && $parent_item [ 'uid' ] == $sys [ 'channel_id' ]) {
2016-05-20 02:42:45 +00:00
return ;
2019-09-10 04:03:33 +00:00
}
2018-05-30 06:30:18 +00:00
2018-11-22 03:04:42 +00:00
$m = get_iconfig ( $target_item , 'activitypub' , 'signed_data' );
2020-08-20 02:49:37 +00:00
// Re-use existing signature unless the activity type changed to a Tombstone, which won't verify.
if ( $m && ( ! intval ( $target_item [ 'item_deleted' ]))) {
2018-07-12 01:02:25 +00:00
self :: $encoded_item = json_decode ( $m , true );
2019-09-10 04:03:33 +00:00
}
2018-11-22 02:24:45 +00:00
else {
self :: $encoded_item = array_merge ([ '@context' => [
ACTIVITYSTREAMS_JSONLD_REV ,
'https://w3id.org/security/v1' ,
z_root () . ZOT_APSCHEMA_REV
2020-04-18 01:41:12 +00:00
]], Activity :: encode_activity ( $target_item , true )
2018-11-22 02:24:45 +00:00
);
}
2018-07-12 01:02:25 +00:00
2019-11-19 03:08:46 +00:00
logger ( 'target_item: ' . print_r ( $target_item , true ), LOGGER_DEBUG );
logger ( 'encoded: ' . print_r ( self :: $encoded_item , true ), LOGGER_DEBUG );
2016-05-20 02:42:45 +00:00
// Send comments to the owner to re-deliver to everybody in the conversation
// We only do this if the item in question originated on this site. This prevents looping.
// To clarify, a site accepting a new comment is responsible for sending it to the owner for relay.
// Relaying should never be initiated on a post that arrived from elsewhere.
// We should normally be able to rely on ITEM_ORIGIN, but start_delivery_chain() incorrectly set this
// flag on comments for an extended period. So we'll also call comment_local_origin() which looks at
// the hostname in the message_id and provides a second (fallback) opinion.
2018-09-11 10:36:31 +00:00
$relay_to_owner = ((( ! $top_level_post ) && ( intval ( $target_item [ 'item_origin' ])) && comment_local_origin ( $target_item ) && $cmd !== 'hyper' ) ? true : false );
2016-05-20 02:42:45 +00:00
$uplink = false ;
// $cmd === 'relay' indicates the owner is sending it to the original recipients
// don't allow the item in the relay command to relay to owner under any circumstances, it will loop
logger ( 'notifier: relay_to_owner: ' . (( $relay_to_owner ) ? 'true' : 'false' ), LOGGER_DATA , LOG_DEBUG );
logger ( 'notifier: top_level_post: ' . (( $top_level_post ) ? 'true' : 'false' ), LOGGER_DATA , LOG_DEBUG );
// tag_deliver'd post which needs to be sent back to the original author
2019-09-10 04:03:33 +00:00
if (( $cmd === 'uplink' ) && intval ( $parent_item [ 'item_uplink' ]) && ( ! $top_level_post )) {
2016-05-20 02:42:45 +00:00
logger ( 'notifier: uplink' );
$uplink = true ;
2018-07-10 23:42:16 +00:00
self :: $packet_type = 'response' ;
2016-05-20 02:42:45 +00:00
}
2019-09-10 04:03:33 +00:00
if (( $relay_to_owner || $uplink ) && ( $cmd !== 'relay' )) {
2018-07-18 00:26:26 +00:00
logger ( 'followup relay (upstream delivery)' , LOGGER_DEBUG );
2019-02-28 23:14:22 +00:00
$sendto = ( $uplink ) ? $parent_item [ 'source_xchan' ] : $parent_item [ 'owner_xchan' ];
self :: $recipients = [ $sendto ];
2018-06-28 01:43:39 +00:00
self :: $private = true ;
2016-10-26 09:46:14 +00:00
$upstream = true ;
2018-07-10 23:42:16 +00:00
self :: $packet_type = 'response' ;
2019-02-28 23:14:22 +00:00
$is_moderated = their_perms_contains ( $parent_item [ 'uid' ], $sendto , 'moderated' );
2019-07-22 02:24:14 +00:00
if ( $relay_to_owner && $thread_is_public && ( ! $is_moderated )) {
if ( get_pconfig ( $target_item [ 'uid' ], 'system' , 'hyperdrive' , false )) {
2020-06-15 07:29:56 +00:00
Run :: Summon ([ 'Notifier' , 'hyper' , $item_id ]);
2019-07-22 02:24:14 +00:00
}
2018-09-11 10:36:31 +00:00
}
2016-05-20 02:42:45 +00:00
}
else {
2019-09-10 04:03:33 +00:00
if ( $cmd === 'relay' ) {
2018-07-18 00:26:26 +00:00
logger ( 'owner relay (downstream delivery)' );
}
else {
logger ( 'normal (downstream) distribution' , LOGGER_DEBUG );
}
2016-10-26 09:46:14 +00:00
$upstream = false ;
2016-05-20 02:42:45 +00:00
// if our parent is a tag_delivery recipient, uplink to the original author causing
// a delivery fork.
2019-09-10 04:03:33 +00:00
if (( $parent_item ) && intval ( $parent_item [ 'item_uplink' ]) && ( ! $top_level_post ) && ( $cmd !== 'uplink' )) {
2016-05-20 02:42:45 +00:00
// don't uplink a relayed post to the relay owner
2019-09-10 04:03:33 +00:00
if ( $parent_item [ 'source_xchan' ] !== $parent_item [ 'owner_xchan' ]) {
2016-05-20 02:42:45 +00:00
logger ( 'notifier: uplinking this item' );
2020-06-15 07:29:56 +00:00
Run :: Summon ( array ( 'Notifier' , 'uplink' , $item_id ));
2016-05-20 02:42:45 +00:00
}
}
2019-09-10 04:03:33 +00:00
if ( $thread_is_public && $cmd === 'hyper' ) {
self :: $recipients = [];
2018-10-17 05:33:45 +00:00
$r = q ( " select abook_xchan, xchan_network from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 and abook_pending = 0 and abook_archived = 0 and not abook_xchan in ( '%s', '%s', '%s' ) " ,
2018-09-11 23:58:26 +00:00
intval ( $target_item [ 'uid' ]),
dbesc ( $target_item [ 'author_xchan' ]),
2018-10-17 05:33:45 +00:00
dbesc ( $target_item [ 'owner_xchan' ]),
dbesc ( $target_item [ 'source_xchan' ])
2018-09-11 10:36:31 +00:00
);
2019-09-10 04:03:33 +00:00
if ( $r ) {
foreach ( $r as $rv ) {
self :: $recipients [] = $rv [ 'abook_xchan' ];
2018-09-11 10:36:31 +00:00
}
}
self :: $private = false ;
}
else {
self :: $private = false ;
self :: $recipients = collect_recipients ( $parent_item , self :: $private );
}
2016-05-20 02:42:45 +00:00
// FIXME add any additional recipients such as mentions, etc.
2019-03-04 23:39:03 +00:00
if ( $top_level_post ) {
// remove clones who will receive the post via sync
2020-04-30 00:35:55 +00:00
self :: $recipients = array_values ( array_diff ( self :: $recipients , [ $target_item [ 'owner_xchan' ] ]));
2019-03-04 23:39:03 +00:00
}
2016-05-20 02:42:45 +00:00
// don't send deletions onward for other people's stuff
2019-09-10 04:03:33 +00:00
if ( intval ( $target_item [ 'item_deleted' ]) && ( ! intval ( $target_item [ 'item_wall' ]))) {
2016-05-20 02:42:45 +00:00
logger ( 'notifier: ignoring delete notification for non-wall item' , LOGGER_NORMAL , LOG_NOTICE );
return ;
}
}
}
// Generic delivery section, we have an encoded item and recipients
// Now start the delivery process
2018-07-18 00:26:26 +00:00
logger ( 'encoded item: ' . print_r ( self :: $encoded_item , true ), LOGGER_DATA , LOG_DEBUG );
2016-05-20 02:42:45 +00:00
2018-06-28 01:43:39 +00:00
stringify_array_elms ( self :: $recipients );
2019-09-10 04:03:33 +00:00
if ( ! self :: $recipients ) {
2017-11-27 02:29:24 +00:00
logger ( 'no recipients' );
2016-05-20 02:42:45 +00:00
return ;
2017-11-27 02:29:24 +00:00
}
2016-05-20 02:42:45 +00:00
2018-07-18 00:26:26 +00:00
// logger('recipients: ' . print_r(self::$recipients,true), LOGGER_NORMAL, LOG_DEBUG);
2016-05-20 02:42:45 +00:00
2019-09-10 04:03:33 +00:00
if ( ! count ( self :: $env_recips )) {
2018-06-28 01:43:39 +00:00
self :: $env_recips = (( self :: $private ) ? [] : null );
2019-09-10 04:03:33 +00:00
}
2016-05-20 02:42:45 +00:00
2019-09-10 04:03:33 +00:00
$recip_list = [];
2016-05-20 02:42:45 +00:00
2019-09-10 04:03:33 +00:00
$details = q ( " select xchan_hash, xchan_network, xchan_addr, xchan_guid, xchan_guid_sig from xchan
where xchan_hash in ( " . protect_sprintf(implode(',',self:: $recipients )) . " ) " );
2016-05-20 02:42:45 +00:00
2019-09-10 04:03:33 +00:00
if ( $details ) {
foreach ( $details as $d ) {
2016-05-20 02:42:45 +00:00
$recip_list [] = $d [ 'xchan_addr' ] . ' (' . $d [ 'xchan_hash' ] . ')' ;
2019-09-10 04:03:33 +00:00
if ( self :: $private ) {
2018-06-28 01:43:39 +00:00
self :: $env_recips [] = $d [ 'xchan_hash' ];
2017-04-26 02:42:06 +00:00
}
2016-05-20 02:42:45 +00:00
}
}
2017-04-26 02:42:06 +00:00
$narr = [
2018-06-28 01:43:39 +00:00
'channel' => self :: $channel ,
2017-04-26 02:42:06 +00:00
'upstream' => $upstream ,
2018-06-28 01:43:39 +00:00
'env_recips' => self :: $env_recips ,
'recipients' => self :: $recipients ,
2017-04-26 02:42:06 +00:00
'item' => $item ,
'target_item' => $target_item ,
2017-08-12 22:42:42 +00:00
'parent_item' => $parent_item ,
2016-05-20 02:42:45 +00:00
'top_level_post' => $top_level_post ,
2018-06-28 01:43:39 +00:00
'private' => self :: $private ,
2016-05-20 02:42:45 +00:00
'relay_to_owner' => $relay_to_owner ,
2017-04-26 02:42:06 +00:00
'uplink' => $uplink ,
'cmd' => $cmd ,
2019-09-24 03:57:55 +00:00
'single' => (( $cmd === 'single_activity' ) ? true : false ),
2017-04-26 02:42:06 +00:00
'request' => $request ,
'normal_mode' => $normal_mode ,
2018-06-28 01:43:39 +00:00
'packet_type' => self :: $packet_type ,
2017-04-26 02:42:06 +00:00
'queued' => []
];
2016-05-20 02:42:45 +00:00
call_hooks ( 'notifier_process' , $narr );
2019-09-10 04:03:33 +00:00
if ( $narr [ 'queued' ]) {
foreach ( $narr [ 'queued' ] as $pq ) {
2018-06-28 01:43:39 +00:00
self :: $deliveries [] = $pq ;
2019-09-10 04:03:33 +00:00
}
2016-05-20 02:42:45 +00:00
}
// notifier_process can alter the recipient list
2018-06-28 01:43:39 +00:00
self :: $recipients = $narr [ 'recipients' ];
self :: $env_recips = $narr [ 'env_recips' ];
2016-05-20 02:42:45 +00:00
2019-09-10 04:03:33 +00:00
if (( self :: $private ) && ( ! self :: $env_recips )) {
2016-05-20 02:42:45 +00:00
// shouldn't happen
2018-07-18 00:26:26 +00:00
logger ( 'private message with no envelope recipients.' . print_r ( $argv , true ), LOGGER_NORMAL , LOG_NOTICE );
return ;
2016-05-20 02:42:45 +00:00
}
logger ( 'notifier: recipients (may be delivered to more if public): ' . print_r ( $recip_list , true ), LOGGER_DEBUG );
2019-09-10 04:03:33 +00:00
// Now we have collected recipients (except for external mentions, @FIXME)
2017-04-19 00:26:50 +00:00
// Let's reduce this to a set of hubs; checking that the site is not dead.
2016-05-20 02:42:45 +00:00
2019-09-10 04:03:33 +00:00
$hubs = q ( " select hubloc.*, site.site_crypto, site.site_flags from hubloc left join site on site_url = hubloc_url
where hubloc_hash in ( " . protect_sprintf(implode(',',self:: $recipients )) . " )
2020-11-26 22:51:22 +00:00
and hubloc_error = 0 and hubloc_deleted = 0 "
2016-05-20 02:42:45 +00:00
);
2020-07-15 01:47:23 +00:00
// public posts won't make it to the local public stream unless there's a recipient on this site.
// This code block sees if it's a public post and localhost is missing, and if so adds an entry for the local sys channel to the $hubs list
if ( ! self :: $private ) {
$found_localhost = false ;
if ( $hubs ) {
foreach ( $hubs as $h ) {
if ( $h [ 'hubloc_url' ] === z_root ()) {
$found_localhost = true ;
break ;
}
}
}
if ( ! $found_localhost ) {
2020-10-13 02:56:07 +00:00
$localhub = q ( " select hubloc.*, site.site_crypto, site.site_flags, site.site_dead from hubloc
left join site on site_url = hubloc_url where hubloc_id_url = '%s' and hubloc_error = 0 and hubloc_deleted = 0 " ,
2020-07-15 01:47:23 +00:00
dbesc ( z_root () . '/channel/sys' )
);
if ( $localhub ) {
$hubs = array_merge ( $hubs , $localhub );
}
}
}
2019-09-10 04:03:33 +00:00
if ( ! $hubs ) {
2016-05-20 02:42:45 +00:00
logger ( 'notifier: no hubs' , LOGGER_NORMAL , LOG_NOTICE );
return ;
}
/**
2017-04-19 00:26:50 +00:00
* Reduce the hubs to those that are unique . For zot hubs , we need to verify uniqueness by the sitekey ,
* since it may have been a re - install which has not yet been detected and pruned .
2016-05-20 02:42:45 +00:00
* For other networks which don 't have or require sitekeys, we' ll have to use the URL
*/
2017-04-19 00:26:50 +00:00
$hublist = []; // this provides an easily printable list for the logs
$dhubs = []; // delivery hubs where we store our resulting unique array
$keys = []; // array of keys to check uniquness for zot hubs
$urls = []; // array of urls to check uniqueness of hubs from other networks
$hub_env = []; // per-hub envelope so we don't broadcast the entire envelope to all
2020-10-13 02:56:07 +00:00
$dead = []; // known dead hubs - report them as undeliverable
2020-12-10 02:55:14 +00:00
2019-09-10 04:03:33 +00:00
foreach ( $hubs as $hub ) {
2020-10-13 02:56:07 +00:00
if ( intval ( $hub [ 'site_dead' ])) {
$dead [] = $hub ;
continue ;
}
2017-04-19 00:26:50 +00:00
2019-09-10 04:03:33 +00:00
if ( self :: $env_recips ) {
foreach ( self :: $env_recips as $er ) {
if ( $hub [ 'hubloc_hash' ] === $er ) {
if ( ! array_key_exists ( $hub [ 'hubloc_site_id' ], $hub_env )) {
2018-07-04 05:18:07 +00:00
$hub_env [ $hub [ 'hubloc_site_id' ]] = [];
2017-04-19 00:26:50 +00:00
}
2018-07-04 05:18:07 +00:00
$hub_env [ $hub [ 'hubloc_site_id' ]][] = $er ;
2017-04-19 00:26:50 +00:00
}
}
2016-05-20 02:42:45 +00:00
}
2017-04-19 00:26:50 +00:00
2016-05-20 02:42:45 +00:00
2019-09-10 04:03:33 +00:00
if ( $hub [ 'hubloc_network' ] === 'zot6' ) {
if ( ! in_array ( $hub [ 'hubloc_sitekey' ], $keys )) {
2017-08-10 05:45:52 +00:00
$hublist [] = $hub [ 'hubloc_host' ] . ' ' . $hub [ 'hubloc_network' ];
2017-04-26 02:42:06 +00:00
$dhubs [] = $hub ;
$keys [] = $hub [ 'hubloc_sitekey' ];
2016-05-20 02:42:45 +00:00
}
}
else {
2019-09-10 04:03:33 +00:00
if ( ! in_array ( $hub [ 'hubloc_url' ], $urls )) {
2017-08-10 05:45:52 +00:00
$hublist [] = $hub [ 'hubloc_host' ] . ' ' . $hub [ 'hubloc_network' ];
2017-04-26 02:42:06 +00:00
$dhubs [] = $hub ;
$urls [] = $hub [ 'hubloc_url' ];
2016-05-20 02:42:45 +00:00
}
}
}
logger ( 'notifier: will notify/deliver to these hubs: ' . print_r ( $hublist , true ), LOGGER_DEBUG , LOG_DEBUG );
2019-09-10 04:03:33 +00:00
foreach ( $dhubs as $hub ) {
logger ( 'notifier_hub: ' . $hub [ 'hubloc_url' ], LOGGER_DEBUG , LOG_DEBUG );
2016-05-20 02:42:45 +00:00
2019-09-10 04:03:33 +00:00
// deliver to any non-zot networks
2018-08-29 05:43:37 +00:00
2019-09-10 04:03:33 +00:00
if ( $hub [ 'hubloc_network' ] !== 'zot6' ) {
2017-04-26 02:42:06 +00:00
$narr = [
2018-06-28 01:43:39 +00:00
'channel' => self :: $channel ,
2017-04-26 02:42:06 +00:00
'upstream' => $upstream ,
2018-06-28 01:43:39 +00:00
'env_recips' => self :: $env_recips ,
'recipients' => self :: $recipients ,
2017-04-26 02:42:06 +00:00
'item' => $item ,
'target_item' => $target_item ,
2017-08-12 22:42:42 +00:00
'parent_item' => $parent_item ,
2017-04-26 02:42:06 +00:00
'hub' => $hub ,
2016-05-20 02:42:45 +00:00
'top_level_post' => $top_level_post ,
2018-06-28 01:43:39 +00:00
'private' => self :: $private ,
2016-05-20 02:42:45 +00:00
'relay_to_owner' => $relay_to_owner ,
2017-04-26 02:42:06 +00:00
'uplink' => $uplink ,
'cmd' => $cmd ,
2019-09-24 03:57:55 +00:00
'single' => (( $cmd === 'single_activity' ) ? true : false ),
2017-04-26 02:42:06 +00:00
'request' => $request ,
'normal_mode' => $normal_mode ,
2018-06-28 01:43:39 +00:00
'packet_type' => self :: $packet_type ,
2017-04-26 02:42:06 +00:00
'queued' => []
];
2016-05-20 02:42:45 +00:00
2018-08-23 23:50:00 +00:00
ActivityPub :: notifier_process ( $narr );
2018-08-23 06:04:37 +00:00
2016-05-20 02:42:45 +00:00
call_hooks ( 'notifier_hub' , $narr );
if ( $narr [ 'queued' ]) {
foreach ( $narr [ 'queued' ] as $pq )
2018-06-28 01:43:39 +00:00
self :: $deliveries [] = $pq ;
2016-05-20 02:42:45 +00:00
}
continue ;
}
2020-04-18 01:41:12 +00:00
// Single deliveries are for non-nomadic federated networks and we're essentially
2017-10-11 01:40:02 +00:00
// delivering only to those that have this site url in their abook_instance
// and only from within a sync operation. This means if you post from a clone,
// and a connection is connected to one of your other clones; assuming that hub
// is running it will receive a sync packet. On receipt of this sync packet it
// will invoke a delivery to those connections which are connected to just that
// hub instance.
2019-09-24 03:57:55 +00:00
if ( $cmd === 'single_activity' ) {
2017-10-11 01:40:02 +00:00
continue ;
}
// default: zot protocol
2019-09-10 04:03:33 +00:00
$hash = new_uuid ();
2016-05-20 02:42:45 +00:00
2018-07-04 05:18:07 +00:00
$env = (( $hub_env && $hub_env [ $hub [ 'hubloc_site_id' ]]) ? $hub_env [ $hub [ 'hubloc_site_id' ]] : '' );
2019-09-10 04:03:33 +00:00
if (( self :: $private ) && ( ! $env )) {
2018-07-04 05:18:07 +00:00
continue ;
2016-05-20 02:42:45 +00:00
}
2019-09-10 04:03:33 +00:00
$packet = Libzot :: build_packet ( self :: $channel , self :: $packet_type , $env , self :: $encoded_item , self :: $encoding ,
(( self :: $private ) ? $hub [ 'hubloc_sitekey' ] : null ), $hub [ 'site_crypto' ]);
2018-07-04 05:18:07 +00:00
Queue :: insert (
[
2016-05-20 02:42:45 +00:00
'hash' => $hash ,
2018-06-28 01:43:39 +00:00
'account_id' => self :: $channel [ 'channel_account_id' ],
'channel_id' => self :: $channel [ 'channel_id' ],
2016-05-20 02:42:45 +00:00
'posturl' => $hub [ 'hubloc_callback' ],
2017-07-25 03:23:00 +00:00
'notify' => $packet ,
2018-06-25 01:54:29 +00:00
'msg' => EMPTY_STR
2018-07-04 05:18:07 +00:00
]
);
2016-05-20 02:42:45 +00:00
2018-07-04 05:18:07 +00:00
// only create delivery reports for normal undeleted items
2019-09-10 04:03:33 +00:00
if ( is_array ( $target_item ) && ( ! $target_item [ 'item_deleted' ]) && ( ! get_config ( 'system' , 'disable_dreport' ))) {
q ( " insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan, dreport_queue )
values ( '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' ) " ,
2018-07-04 05:18:07 +00:00
dbesc ( $target_item [ 'mid' ]),
dbesc ( $hub [ 'hubloc_host' ]),
dbesc ( $hub [ 'hubloc_host' ]),
2018-07-19 03:15:46 +00:00
dbesc ( $hub [ 'hubloc_host' ]),
2018-07-04 05:18:07 +00:00
dbesc ( 'queued' ),
dbesc ( datetime_convert ()),
dbesc ( self :: $channel [ 'channel_hash' ]),
dbesc ( $hash )
);
2016-05-20 02:42:45 +00:00
}
2018-06-28 01:43:39 +00:00
self :: $deliveries [] = $hash ;
2016-05-20 02:42:45 +00:00
}
2019-09-10 04:03:33 +00:00
if ( $normal_mode ) {
// This wastes a process if there are no delivery hooks configured, so check this before launching the new process
2016-05-20 02:42:45 +00:00
$x = q ( " select * from hook where hook = 'notifier_normal' " );
2019-09-10 04:03:33 +00:00
if ( $x ) {
2020-06-15 07:29:56 +00:00
Run :: Summon ( [ 'Deliver_hooks' , $target_item [ 'id' ] ] );
2017-04-26 02:42:06 +00:00
}
2016-05-20 02:42:45 +00:00
}
2019-09-10 04:03:33 +00:00
if ( self :: $deliveries ) {
2018-06-28 01:43:39 +00:00
do_delivery ( self :: $deliveries );
2019-09-10 04:03:33 +00:00
}
2016-05-20 02:42:45 +00:00
2020-10-13 02:56:07 +00:00
if ( $dead ) {
foreach ( $dead as $deceased ) {
if ( is_array ( $target_item ) && ( ! $target_item [ 'item_deleted' ]) && ( ! get_config ( 'system' , 'disable_dreport' ))) {
q ( " insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan, dreport_queue )
values ( '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' ) " ,
dbesc ( $target_item [ 'mid' ]),
dbesc ( $deceased [ 'hubloc_host' ]),
dbesc ( $deceased [ 'hubloc_host' ]),
dbesc ( $deceased [ 'hubloc_host' ]),
dbesc ( 'undeliverable/unresponsive site' ),
dbesc ( datetime_convert ()),
dbesc ( self :: $channel [ 'channel_hash' ]),
dbesc ( new_uuid ())
);
}
}
}
2016-05-20 02:42:45 +00:00
call_hooks ( 'notifier_end' , $target_item );
logger ( 'notifer: complete.' );
2020-10-13 02:56:07 +00:00
2016-05-20 02:42:45 +00:00
return ;
}
2018-08-23 06:04:37 +00:00
2016-05-20 02:42:45 +00:00
}