2009-11-20 13:55:38 +09:00
< ? php
2019-09-11 15:46:30 +09:00
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
2009-11-20 13:55:38 +09:00
/**
2013-10-01 00:13:03 +09:00
* OStatusPlugin implementation for GNU Social
* Depends on : WebFinger plugin
*
2019-09-11 15:46:30 +09:00
* @ package OStatusPlugin
* @ author Brion Vibber < brion @ status . net >
* @ copyright 2009 - 2010 StatusNet , Inc .
* @ license https :// www . gnu . org / licenses / agpl . html GNU AGPL v3 or later
2009-11-20 13:55:38 +09:00
*/
2019-09-11 15:46:30 +09:00
defined ( 'GNUSOCIAL' ) || die ();
2009-11-20 13:55:38 +09:00
2019-08-25 11:14:50 +09:00
require_once __DIR__ . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'util.php' ;
2010-02-09 04:06:03 +09:00
class OStatusPlugin extends Plugin
2009-11-20 13:55:38 +09:00
{
2019-08-25 11:14:50 +09:00
const PLUGIN_VERSION = '2.1.0' ;
2019-06-03 09:56:52 +09:00
2009-11-20 13:55:38 +09:00
/**
* Hook for RouterInitialized event .
*
2014-11-07 23:24:05 +09:00
* @ param URLMapper $m path - to - action mapper
2009-11-20 13:55:38 +09:00
* @ return boolean hook return
*/
2014-11-07 23:24:05 +09:00
public function onRouterInitialized ( URLMapper $m )
2009-11-20 13:55:38 +09:00
{
2010-02-10 05:37:37 +09:00
// Discovery actions
2019-09-11 15:46:30 +09:00
$m -> connect (
'main/ostatustag' ,
[ 'action' => 'ostatustag' ]
);
$m -> connect (
'main/ostatustag?nickname=:nickname' ,
[ 'action' => 'ostatustag' ],
[ 'nickname' => '[A-Za-z0-9_-]+' ]
);
$m -> connect (
'main/ostatus/group/:group' ,
[ 'action' => 'ostatusinit' ],
[ 'group' => '[A-Za-z0-9_-]+' ]
);
$m -> connect (
'main/ostatus/peopletag/:peopletag/tagger/:tagger' ,
[ 'action' => 'ostatusinit' ],
[
'tagger' => '[A-Za-z0-9_-]+' ,
'peopletag' => '[A-Za-z0-9_-]+' ,
]
);
$m -> connect (
'main/ostatus' ,
[ 'action' => 'ostatusinit' ]
);
2011-03-07 04:15:34 +09:00
// Remote subscription actions
2019-09-11 15:46:30 +09:00
$m -> connect (
'main/ostatusgroup' ,
[ 'action' => 'ostatusgroup' ]
);
$m -> connect (
'main/ostatuspeopletag' ,
[ 'action' => 'ostatuspeopletag' ]
);
2010-02-10 05:37:37 +09:00
2017-05-01 18:04:27 +09:00
// WebSub actions
2019-07-12 03:14:03 +09:00
$m -> connect ( 'main/push/hub' , [ 'action' => 'pushhub' ]);
2010-02-09 04:06:03 +09:00
2019-09-11 15:46:30 +09:00
$m -> connect (
'main/push/callback/:feed' ,
[ 'action' => 'pushcallback' ],
[ 'feed' => '[0-9]+' ]
);
2010-02-10 05:37:37 +09:00
// Salmon endpoint
2019-09-11 15:46:30 +09:00
$m -> connect (
'main/salmon/user/:id' ,
[ 'action' => 'usersalmon' ],
[ 'id' => '[0-9]+' ]
);
$m -> connect (
'main/salmon/group/:id' ,
[ 'action' => 'groupsalmon' ],
[ 'id' => '[0-9]+' ]
);
$m -> connect (
'main/salmon/peopletag/:id' ,
[ 'action' => 'peopletagsalmon' ],
[ 'id' => '[0-9]+' ]
);
2009-11-20 13:55:38 +09:00
return true ;
}
2010-02-09 04:06:03 +09:00
/**
* Set up queue handlers for outgoing hub pushes
* @ param QueueManager $qm
* @ return boolean hook return
*/
2019-09-11 15:46:30 +09:00
public function onEndInitializeQueueManager ( QueueManager $qm )
2010-02-09 04:06:03 +09:00
{
2010-02-24 11:19:13 +09:00
// Prepare outgoing distributions after notice save.
$qm -> connect ( 'ostatus' , 'OStatusQueueHandler' );
2017-05-01 18:04:27 +09:00
// Outgoing from our internal WebSub hub
2010-02-25 05:36:36 +09:00
$qm -> connect ( 'hubconf' , 'HubConfQueueHandler' );
2010-06-05 03:48:54 +09:00
$qm -> connect ( 'hubprep' , 'HubPrepQueueHandler' );
2010-02-09 04:06:03 +09:00
$qm -> connect ( 'hubout' , 'HubOutQueueHandler' );
2010-02-22 06:40:59 +09:00
2010-02-24 11:19:13 +09:00
// Outgoing Salmon replies (when we don't need a return value)
2010-02-25 05:36:36 +09:00
$qm -> connect ( 'salmon' , 'SalmonQueueHandler' );
2010-02-24 11:19:13 +09:00
2017-05-01 18:04:27 +09:00
// Incoming from a foreign WebSub hub
2010-02-25 05:36:36 +09:00
$qm -> connect ( 'pushin' , 'PushInQueueHandler' );
2015-10-21 10:50:03 +09:00
// Re-subscribe feeds that need renewal
$qm -> connect ( 'pushrenew' , 'PushRenewQueueHandler' );
2010-02-09 04:06:03 +09:00
return true ;
}
/**
* Put saved notices into the queue for pubsub distribution .
*/
2019-09-11 15:46:30 +09:00
public function onStartEnqueueNotice ( $notice , & $transports )
2010-02-09 04:06:03 +09:00
{
2016-03-02 20:42:09 +09:00
if ( $notice -> inScope ( null ) && $notice -> getProfile () -> hasRight ( Right :: PUBLICNOTICE )) {
2019-08-07 05:34:02 +09:00
$transports [] = 'ostatus' ;
2016-03-24 01:52:02 +09:00
$this -> log ( LOG_INFO , " OSTATUS [ { $notice -> getID () } ]: queued for OStatus processing " );
2011-06-30 02:20:18 +09:00
} else {
2012-07-30 07:17:16 +09:00
// FIXME: we don't do privacy-controlled OStatus updates yet.
// once that happens, finer grain of control here.
2016-03-24 01:52:02 +09:00
$this -> log ( LOG_NOTICE , " OSTATUS [ { $notice -> getID () } ]: Not queueing because of privacy; scope = { $notice -> scope } " );
2010-06-04 08:58:45 +09:00
}
2010-02-09 04:06:03 +09:00
return true ;
}
/**
2017-05-01 18:04:27 +09:00
* Set up a WebSub hub link to our internal link for canonical timeline
2010-02-10 11:32:52 +09:00
* Atom feeds for users and groups .
2010-02-09 04:06:03 +09:00
*/
2019-09-11 15:46:30 +09:00
public function onStartApiAtom ( $feed )
2010-02-09 04:06:03 +09:00
{
2010-02-13 12:00:35 +09:00
$id = null ;
if ( $feed instanceof AtomUserNoticeFeed ) {
2010-02-22 03:11:00 +09:00
$salmonAction = 'usersalmon' ;
$user = $feed -> getUser ();
$id = $user -> id ;
$profile = $user -> getProfile ();
2019-09-11 15:46:30 +09:00
} elseif ( $feed instanceof AtomGroupNoticeFeed ) {
2010-02-22 03:11:00 +09:00
$salmonAction = 'groupsalmon' ;
$group = $feed -> getGroup ();
$id = $group -> id ;
2019-09-11 15:46:30 +09:00
} elseif ( $feed instanceof AtomListNoticeFeed ) {
2011-03-07 04:15:34 +09:00
$salmonAction = 'peopletagsalmon' ;
$peopletag = $feed -> getList ();
$id = $peopletag -> id ;
2010-02-12 05:02:17 +09:00
} else {
2010-02-22 03:11:00 +09:00
return true ;
2010-02-12 05:02:17 +09:00
}
2010-02-11 07:58:39 +09:00
2010-02-22 03:11:00 +09:00
if ( ! empty ( $id )) {
2010-02-12 05:02:17 +09:00
$hub = common_config ( 'ostatus' , 'hub' );
if ( empty ( $hub )) {
2017-05-01 18:04:27 +09:00
// Updates will be handled through our internal WebSub hub.
2010-02-12 05:02:17 +09:00
$hub = common_local_url ( 'pushhub' );
2010-02-09 04:06:03 +09:00
}
2010-02-13 12:00:35 +09:00
$feed -> addLink ( $hub , array ( 'rel' => 'hub' ));
2010-02-12 05:02:17 +09:00
// Also, we'll add in the salmon link
$salmon = common_local_url ( $salmonAction , array ( 'id' => $id ));
2010-08-03 02:23:55 +09:00
$feed -> addLink ( $salmon , array ( 'rel' => Salmon :: REL_SALMON ));
2013-11-02 01:00:12 +09:00
// XXX: these are deprecated, but StatusNet only looks for NS_REPLIES
2010-02-27 03:17:24 +09:00
$feed -> addLink ( $salmon , array ( 'rel' => Salmon :: NS_REPLIES ));
$feed -> addLink ( $salmon , array ( 'rel' => Salmon :: NS_MENTIONS ));
2010-02-09 04:06:03 +09:00
}
2010-02-22 03:11:00 +09:00
return true ;
2010-02-09 04:06:03 +09:00
}
2010-02-13 12:00:35 +09:00
2010-02-09 15:37:45 +09:00
/**
2019-08-25 11:14:50 +09:00
* Add in an OStatus list button
*
* @ param HTMLOutputter $output
* @ param Profile $profile
* @ return bool hook return value
2010-02-09 15:37:45 +09:00
*/
2019-09-11 15:46:30 +09:00
public function onStartProfileRemoteSubscribe ( HTMLOutputter $output , Profile $profile ) : bool
2011-03-07 04:15:34 +09:00
{
2019-08-25 11:14:50 +09:00
$this -> onStartProfileListItemActionElements ( $output );
return true ;
2011-03-07 04:15:34 +09:00
}
2019-09-11 15:46:30 +09:00
public function onStartGroupSubscribe ( $widget , $group )
2010-02-09 15:37:45 +09:00
{
$cur = common_current_user ();
if ( empty ( $cur )) {
2011-09-18 06:28:01 +09:00
$widget -> out -> elementStart ( 'li' , 'entity_subscribe' );
2011-09-19 01:44:16 +09:00
2019-09-11 15:46:30 +09:00
$url = common_local_url (
'ostatusinit' ,
[ 'group' => $group -> nickname ]
);
$widget -> out -> element (
'a' ,
[
'href' => $url ,
'class' => 'entity_remote_subscribe' ,
],
// TRANS: Link to subscribe to a remote entity.
_m ( 'Subscribe' )
);
2010-02-13 12:00:35 +09:00
2011-09-18 06:28:01 +09:00
$widget -> out -> elementEnd ( 'li' );
2011-03-22 11:26:25 +09:00
return false ;
2010-02-09 15:37:45 +09:00
}
2010-02-14 02:42:00 +09:00
2011-03-07 04:15:34 +09:00
return true ;
2010-02-09 15:37:45 +09:00
}
2019-09-11 15:46:30 +09:00
public function onStartSubscribePeopletagForm ( $output , $peopletag )
2010-03-04 02:02:10 +09:00
{
$cur = common_current_user ();
if ( empty ( $cur )) {
2011-03-07 04:15:34 +09:00
$output -> elementStart ( 'li' , 'entity_subscribe' );
$profile = $peopletag -> getTagger ();
2019-09-11 15:46:30 +09:00
$url = common_local_url (
'ostatusinit' ,
[ 'tagger' => $profile -> nickname , 'peopletag' => $peopletag -> tag ]
);
$output -> element (
'a' ,
[
'href' => $url ,
'class' => 'entity_remote_subscribe' ,
],
// TRANS: Link to subscribe to a remote entity.
_m ( 'Subscribe' )
);
2011-03-07 04:15:34 +09:00
$output -> elementEnd ( 'li' );
return false ;
}
return true ;
}
/*
* If the field being looked for is URI look for the profile
*/
2019-09-11 15:46:30 +09:00
public function onStartProfileCompletionSearch ( $action , $profile , $search_engine )
{
2011-03-07 04:15:34 +09:00
if ( $action -> field == 'uri' ) {
2019-09-11 15:46:30 +09:00
$profile -> joinAdd ([ 'id' , 'user:id' ]);
$profile -> whereAdd ( " uri LIKE '% " . $profile -> escape ( $q ) . " %' " );
2011-03-07 04:15:34 +09:00
$profile -> query ();
2015-11-08 18:33:41 +09:00
$validate = new Validate ();
2011-03-07 04:15:34 +09:00
if ( $profile -> N == 0 ) {
try {
2015-11-08 18:33:41 +09:00
if ( $validate -> email ( $q )) {
2011-03-07 04:15:34 +09:00
$oprofile = Ostatus_profile :: ensureWebfinger ( $q );
2019-09-11 15:46:30 +09:00
} elseif ( $validate -> uri ( $q )) {
2011-03-07 04:15:34 +09:00
$oprofile = Ostatus_profile :: ensureProfileURL ( $q );
} else {
2011-04-11 07:39:27 +09:00
// TRANS: Exception in OStatus when invalid URI was entered.
throw new Exception ( _m ( 'Invalid URI.' ));
2011-03-07 04:15:34 +09:00
}
return $this -> filter ( array ( $oprofile -> localProfile ()));
} catch ( Exception $e ) {
2019-09-11 15:46:30 +09:00
// TRANS: Error message in OStatus plugin. Do not translate the domain names example.com
// TRANS: and example.net, as these are official standard domain names for use in examples.
2011-04-11 07:39:27 +09:00
$this -> msg = _m ( " Sorry, we could not reach that address. Please make sure that the OStatus address is like nickname@example.com or http://example.net/nickname. " );
2011-03-07 04:15:34 +09:00
return array ();
}
}
return false ;
}
2010-03-04 06:26:02 +09:00
return true ;
2010-03-04 02:02:10 +09:00
}
2017-04-22 17:58:14 +09:00
/**
* Webfinger matches : @ user @ example . com or even @ user -- one . george_orwell @ 1984. biz
2017-08-10 18:06:52 +09:00
* @ param string $text The text from which to extract webfinger IDs
* @ param string $preMention Character ( s ) that signals a mention ( '@' , '!' ... )
2017-04-22 17:58:14 +09:00
*
2017-08-10 19:30:11 +09:00
* @ return array The matching IDs ( without $preMention ) and each respective position in the given string .
2017-04-22 17:58:14 +09:00
*/
2019-09-11 15:46:30 +09:00
public static function extractWebfingerIds ( $text , $preMention = '@' )
2017-04-22 17:58:14 +09:00
{
$wmatches = array ();
2019-09-11 15:46:30 +09:00
$result = preg_match_all (
'/(?<!\S)' . preg_quote ( $preMention , '/' ) . '(' . Nickname :: WEBFINGER_FMT . ')/' ,
$text ,
$wmatches ,
PREG_OFFSET_CAPTURE
);
2017-04-22 17:58:14 +09:00
if ( $result === false ) {
common_log ( LOG_ERR , __METHOD__ . ': Error parsing webfinger IDs from text (preg_last_error==' . preg_last_error () . ').' );
2019-07-10 08:10:18 +09:00
return [];
2017-05-01 17:34:51 +09:00
} elseif ( count ( $wmatches )) {
common_debug ( sprintf ( 'Found %d matches for WebFinger IDs: %s' , count ( $wmatches ), _ve ( $wmatches )));
2017-04-22 17:58:14 +09:00
}
return $wmatches [ 1 ];
}
/**
* Profile URL matches : @ example . com / mublog / user
2017-08-10 18:06:52 +09:00
* @ param string $text The text from which to extract URL mentions
* @ param string $preMention Character ( s ) that signals a mention ( '@' , '!' ... )
2017-04-22 17:58:14 +09:00
*
* @ return array The matching URLs ( without @ or acct : ) and each respective position in the given string .
*/
2019-09-11 15:46:30 +09:00
public static function extractUrlMentions ( $text , $preMention = '@' )
2017-04-22 17:58:14 +09:00
{
$wmatches = array ();
2017-04-22 18:45:24 +09:00
// In the regexp below we need to match / _before_ URL_REGEX_VALID_PATH_CHARS because it otherwise gets merged
// with the TLD before (but / is in URL_REGEX_VALID_PATH_CHARS anyway, it's just its positioning that is important)
2019-09-11 15:46:30 +09:00
$result = preg_match_all (
'/(?:^|\s+)' . preg_quote ( $preMention , '/' ) . '(' . URL_REGEX_DOMAIN_NAME . '(?:\/[' . URL_REGEX_VALID_PATH_CHARS . ']*)*)/' ,
$text ,
$wmatches ,
PREG_OFFSET_CAPTURE
);
2017-04-22 17:58:14 +09:00
if ( $result === false ) {
common_log ( LOG_ERR , __METHOD__ . ': Error parsing profile URL mentions from text (preg_last_error==' . preg_last_error () . ').' );
2019-07-10 08:10:18 +09:00
return [];
2017-05-01 17:34:51 +09:00
} elseif ( count ( $wmatches )) {
common_debug ( sprintf ( 'Found %d matches for profile URL mentions: %s' , count ( $wmatches ), _ve ( $wmatches )));
2017-04-22 17:58:14 +09:00
}
return $wmatches [ 1 ];
}
2010-02-11 09:09:20 +09:00
/**
2010-03-02 09:36:33 +09:00
* Find any explicit remote mentions . Accepted forms :
* Webfinger : @ user @ example . com
* Profile link : @ example . com / mublog / user
2014-02-24 05:49:55 +09:00
* @ param Profile $sender
2010-03-02 09:36:33 +09:00
* @ param string $text input markup text
* @ param array & $mention in / out param : set of found mentions
* @ return boolean hook return value
2010-02-11 09:09:20 +09:00
*/
2019-09-11 15:46:30 +09:00
public function onEndFindMentions ( Profile $sender , $text , & $mentions )
2010-02-11 09:09:20 +09:00
{
2010-03-04 02:32:25 +09:00
$matches = array ();
2017-08-10 18:06:52 +09:00
foreach ( self :: extractWebfingerIds ( $text , '@' ) as $wmatch ) {
2017-04-22 17:58:14 +09:00
list ( $target , $pos ) = $wmatch ;
2017-08-10 19:30:11 +09:00
$this -> log ( LOG_INFO , " Checking webfinger person ' $target ' " );
2017-04-22 17:58:14 +09:00
$profile = null ;
try {
$oprofile = Ostatus_profile :: ensureWebfinger ( $target );
if ( ! $oprofile instanceof Ostatus_profile || ! $oprofile -> isPerson ()) {
2016-01-30 00:06:16 +09:00
continue ;
2010-03-02 09:36:33 +09:00
}
2017-04-22 17:58:14 +09:00
$profile = $oprofile -> localProfile ();
} catch ( OStatusShadowException $e ) {
// This means we got a local user in the webfinger lookup
$profile = $e -> profile ;
} catch ( Exception $e ) {
$this -> log ( LOG_ERR , " Webfinger check failed: " . $e -> getMessage ());
continue ;
}
2016-01-30 00:06:16 +09:00
2017-04-22 17:58:14 +09:00
assert ( $profile instanceof Profile );
2016-01-30 00:06:16 +09:00
2017-08-10 19:30:11 +09:00
$displayName = ! empty ( $profile -> nickname ) && mb_strlen ( $profile -> nickname ) < mb_strlen ( $target )
2017-04-22 17:58:14 +09:00
? $profile -> getNickname () // TODO: we could do getBestName() or getFullname() here
: $target ;
$url = $profile -> getUri ();
if ( ! common_valid_http_url ( $url )) {
$url = $profile -> getUrl ();
2010-03-04 02:32:25 +09:00
}
2017-04-22 17:58:14 +09:00
$matches [ $pos ] = array ( 'mentioned' => array ( $profile ),
'type' => 'mention' ,
2017-08-10 19:30:11 +09:00
'text' => $displayName ,
'position' => $pos ,
'length' => mb_strlen ( $target ),
'url' => $url );
}
// Doing groups in a separate routine because webfinger lookups don't work
// remotely until everyone updates etc. etc.
foreach ( self :: extractWebfingerIds ( $text , '!' ) as $wmatch ) {
list ( $target , $pos ) = $wmatch ;
list ( $target_nickname , $target_hostname ) = explode ( '@' , parse_url ( $target , PHP_URL_PATH ));
$this -> log ( LOG_INFO , sprintf ( 'Checking webfinger group %s as user %s on server %s' , $target , $target_nickname , $target_hostname ));
$profile = null ;
if ( $target_hostname === mb_strtolower ( common_config ( 'site' , 'server' ))) {
try {
$profile = Local_group :: getKV ( 'nickname' , $target_nickname ) -> getProfile ();
} catch ( NoSuchGroupException $e ) {
// referenced a local group which does not exist, so not returning it as a mention
$this -> log ( LOG_ERR , " Local group lookup failed: " . _ve ( $e -> getMessage ()));
continue ;
}
} else {
// XXX: Superhacky. Domain name can be incorrectly matched
// here. But since users are only members of groups
// they trust (of course they are!), the likelihood of
// a mention-hijacking is very very low... for now.
$possible_groups = new User_group ();
$possible_groups -> nickname = $target_nickname ;
if ( ! $possible_groups -> find ()) {
common_debug ( 'No groups at all found with nickname: ' . _ve ( $target_nickname ));
continue ;
}
while ( $possible_groups -> fetch ()) {
if ( ! $sender -> isMember ( $possible_groups )) {
continue ;
}
$group_hostname = mb_strtolower ( parse_url ( $possible_groups -> mainpage , PHP_URL_HOST ));
if ( $target_hostname === $group_hostname ) {
common_debug ( sprintf ( 'Found group with nick@host (%s@%s) matching %s' , _ve ( $possible_groups -> nickname ), _ve ( $group_hostname ), _ve ( $target )));
$profile = $possible_groups -> getProfile ();
break ;
}
}
$possible_groups -> free ();
if ( ! $profile instanceof Profile ) {
common_debug ( 'Found groups with correct nickname but not hostname for: ' . _ve ( $target ));
continue ;
}
}
assert ( $profile instanceof Profile );
$displayName = ! empty ( $profile -> nickname ) && mb_strlen ( $profile -> nickname ) < mb_strlen ( $target )
? $profile -> getNickname () // TODO: we could do getBestName() or getFullname() here
: $target ;
$url = $profile -> getUri ();
if ( ! common_valid_http_url ( $url )) {
$url = $profile -> getUrl ();
}
$matches [ $pos ] = array ( 'mentioned' => array ( $profile ),
'type' => 'group' ,
'text' => $displayName ,
2017-04-22 17:58:14 +09:00
'position' => $pos ,
'length' => mb_strlen ( $target ),
'url' => $url );
2010-03-04 02:32:25 +09:00
}
2017-04-22 17:58:14 +09:00
foreach ( self :: extractUrlMentions ( $text ) as $wmatch ) {
list ( $target , $pos ) = $wmatch ;
2017-04-22 19:29:53 +09:00
$schemes = array ( 'https' , 'http' );
2017-04-22 17:58:14 +09:00
foreach ( $schemes as $scheme ) {
$url = " $scheme :// $target " ;
$this -> log ( LOG_INFO , " Checking profile address ' $url ' " );
try {
$oprofile = Ostatus_profile :: ensureProfileURL ( $url );
if ( $oprofile instanceof Ostatus_profile && ! $oprofile -> isGroup ()) {
$profile = $oprofile -> localProfile ();
2017-08-10 19:30:11 +09:00
$displayName = ! empty ( $profile -> nickname ) && mb_strlen ( $profile -> nickname ) < mb_strlen ( $target ) ?
2017-04-22 17:58:14 +09:00
$profile -> nickname : $target ;
$matches [ $pos ] = array ( 'mentioned' => array ( $profile ),
'type' => 'mention' ,
2017-08-10 19:30:11 +09:00
'text' => $displayName ,
2017-04-22 17:58:14 +09:00
'position' => $pos ,
'length' => mb_strlen ( $target ),
'url' => $profile -> getUrl ());
break ;
2010-03-02 09:36:33 +09:00
}
2017-04-22 17:58:14 +09:00
} catch ( Exception $e ) {
$this -> log ( LOG_ERR , " Profile check failed: " . $e -> getMessage ());
2010-03-02 09:36:33 +09:00
}
}
2010-03-04 02:32:25 +09:00
}
2010-02-22 00:53:32 +09:00
2010-03-04 02:32:25 +09:00
foreach ( $mentions as $i => $other ) {
// If we share a common prefix with a local user, override it!
$pos = $other [ 'position' ];
if ( isset ( $matches [ $pos ])) {
$mentions [ $i ] = $matches [ $pos ];
unset ( $matches [ $pos ]);
2010-02-10 05:37:37 +09:00
}
}
2010-03-04 02:32:25 +09:00
foreach ( $matches as $mention ) {
$mentions [] = $mention ;
}
2010-02-22 00:53:32 +09:00
2010-02-11 09:09:20 +09:00
return true ;
}
2010-03-09 07:01:43 +09:00
/**
* Allow remote profile references to be used in commands :
* sub update @ status . net
* whois evan @ identi . ca
* reply http :// identi . ca / evan hey what ' s up
*
* @ param Command $command
* @ param string $arg
* @ param Profile & $profile
* @ return hook return code
*/
2019-09-11 15:46:30 +09:00
public function onStartCommandGetProfile ( $command , $arg , & $profile )
2010-03-09 07:01:43 +09:00
{
$oprofile = $this -> pullRemoteProfile ( $arg );
2014-05-06 21:36:52 +09:00
if ( $oprofile instanceof Ostatus_profile && ! $oprofile -> isGroup ()) {
2014-05-20 00:58:05 +09:00
try {
$profile = $oprofile -> localProfile ();
} catch ( NoProfileException $e ) {
// No locally stored profile found for remote profile
return true ;
}
2010-03-09 07:01:43 +09:00
return false ;
} else {
return true ;
}
}
/**
* Allow remote group references to be used in commands :
* join group + statusnet @ identi . ca
* join http :// identi . ca / group / statusnet
* drop identi . ca / group / statusnet
*
* @ param Command $command
* @ param string $arg
* @ param User_group & $group
* @ return hook return code
*/
2019-09-11 15:46:30 +09:00
public function onStartCommandGetGroup ( $command , $arg , & $group )
2010-03-09 07:01:43 +09:00
{
$oprofile = $this -> pullRemoteProfile ( $arg );
2014-05-06 21:36:52 +09:00
if ( $oprofile instanceof Ostatus_profile && $oprofile -> isGroup ()) {
2010-03-09 07:01:43 +09:00
$group = $oprofile -> localGroup ();
return false ;
} else {
return true ;
}
}
protected function pullRemoteProfile ( $arg )
{
$oprofile = null ;
if ( preg_match ( '!^((?:\w+\.)*\w+@(?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+)$!' , $arg )) {
// webfinger lookup
try {
return Ostatus_profile :: ensureWebfinger ( $arg );
} catch ( Exception $e ) {
common_log ( LOG_ERR , 'Webfinger lookup failed for ' .
$arg . ': ' . $e -> getMessage ());
}
}
// Look for profile URLs, with or without scheme:
$urls = array ();
if ( preg_match ( '!^https?://((?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+(?:/\w+)+)$!' , $arg )) {
$urls [] = $arg ;
}
if ( preg_match ( '!^((?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+(?:/\w+)+)$!' , $arg )) {
$schemes = array ( 'http' , 'https' );
foreach ( $schemes as $scheme ) {
$urls [] = " $scheme :// $arg " ;
}
}
foreach ( $urls as $url ) {
try {
2010-03-19 05:21:26 +09:00
return Ostatus_profile :: ensureProfileURL ( $url );
2010-03-09 07:01:43 +09:00
} catch ( Exception $e ) {
common_log ( LOG_ERR , 'Profile lookup failed for ' .
$arg . ': ' . $e -> getMessage ());
}
}
return null ;
}
2019-09-11 15:46:30 +09:00
public function onEndProfileSettingsActions ( $out ) {
2015-10-28 10:10:28 +09:00
$siteName = common_config ( 'site' , 'name' );
2019-08-25 11:14:50 +09:00
$js = 'navigator.registerContentHandler("application/vnd.mozilla.maybe.feed", "' . addslashes ( common_local_url ( 'RemoteFollowSub' , null , array ( 'profile' => '%s' ))) . '", "' . addslashes ( $siteName ) . '")' ;
2015-10-28 10:10:28 +09:00
$out -> elementStart ( 'li' );
2019-09-11 15:46:30 +09:00
$out -> element (
'a' ,
[ 'href' => 'javascript:' . $js ],
// TRANS: Option in profile settings to add this instance to Firefox as a feedreader
_ ( 'Add to Firefox as feedreader' )
);
2015-10-28 10:10:28 +09:00
$out -> elementEnd ( 'li' );
}
2010-02-12 05:02:17 +09:00
/**
* Make sure necessary tables are filled out .
*/
2019-09-11 15:46:30 +09:00
public function onCheckSchema ()
{
2009-11-20 13:55:38 +09:00
$schema = Schema :: get ();
2010-02-12 09:22:16 +09:00
$schema -> ensureTable ( 'ostatus_profile' , Ostatus_profile :: schemaDef ());
2010-02-19 06:22:21 +09:00
$schema -> ensureTable ( 'feedsub' , FeedSub :: schemaDef ());
2010-02-09 04:06:03 +09:00
$schema -> ensureTable ( 'hubsub' , HubSub :: schemaDef ());
2010-02-23 13:11:40 +09:00
$schema -> ensureTable ( 'magicsig' , Magicsig :: schemaDef ());
2009-11-20 13:55:38 +09:00
return true ;
2010-02-11 09:09:20 +09:00
}
2010-02-14 03:07:21 +09:00
2019-09-11 15:46:30 +09:00
public function onEndShowStylesheets ( Action $action )
{
2011-02-04 01:09:26 +09:00
$action -> cssLink ( $this -> path ( 'theme/base/css/ostatus.css' ));
2010-02-14 03:07:21 +09:00
return true ;
}
2010-02-14 04:28:05 +09:00
2019-09-11 15:46:30 +09:00
public function onEndShowStatusNetScripts ( $action )
{
2011-02-04 01:09:26 +09:00
$action -> script ( $this -> path ( 'js/ostatus.js' ));
2010-02-14 04:28:05 +09:00
return true ;
}
2010-02-17 11:16:03 +09:00
2010-02-19 03:20:48 +09:00
/**
* Override the " from ostatus " bit in notice lists to link to the
* original post and show the domain it came from .
*
* @ param Notice in $notice
* @ param string out & $name
* @ param string out & $url
* @ param string out & $title
* @ return mixed hook return code
*/
2019-09-11 15:46:30 +09:00
public function onStartNoticeSourceLink ( $notice , & $name , & $url , & $title )
2010-02-17 11:16:03 +09:00
{
2014-04-20 05:18:36 +09:00
// If we don't handle this, keep the event handler going
2015-10-14 08:30:29 +09:00
if ( ! in_array ( $notice -> source , array ( 'ostatus' , 'share' ))) {
2014-04-20 05:18:36 +09:00
return true ;
}
2010-02-17 11:16:03 +09:00
2014-04-20 05:18:36 +09:00
try {
$url = $notice -> getUrl ();
// If getUrl() throws exception, $url is never set
2019-07-10 08:10:18 +09:00
2014-04-20 05:18:36 +09:00
$bits = parse_url ( $url );
$domain = $bits [ 'host' ];
if ( substr ( $domain , 0 , 4 ) == 'www.' ) {
$name = substr ( $domain , 4 );
} else {
$name = $domain ;
2010-02-24 06:47:14 +09:00
}
2014-04-20 05:18:36 +09:00
// TRANS: Title. %s is a domain name.
$title = sprintf ( _m ( 'Sent from %s via OStatus' ), $domain );
// Abort event handler, we have a name and URL!
return false ;
} catch ( InvalidUrlException $e ) {
// This just means we don't have the notice source data
return true ;
2010-02-17 11:16:03 +09:00
}
}
2010-02-19 06:22:21 +09:00
/**
2017-05-01 18:04:27 +09:00
* Send incoming WebSub feeds for OStatus endpoints in for processing .
2010-02-19 06:22:21 +09:00
*
* @ param FeedSub $feedsub
* @ param DOMDocument $feed
* @ return mixed hook return code
*/
2019-09-11 15:46:30 +09:00
public function onStartFeedSubReceive ( $feedsub , $feed )
2010-02-19 06:22:21 +09:00
{
2013-08-18 20:04:58 +09:00
$oprofile = Ostatus_profile :: getKV ( 'feeduri' , $feedsub -> uri );
2013-11-01 21:20:23 +09:00
if ( $oprofile instanceof Ostatus_profile ) {
2010-02-24 10:09:52 +09:00
$oprofile -> processFeed ( $feed , 'push' );
2010-02-20 09:21:17 +09:00
} else {
common_log ( LOG_DEBUG , " No ostatus profile for incoming feed $feedsub->uri " );
2010-02-19 06:22:21 +09:00
}
}
2010-02-21 01:48:42 +09:00
2010-08-07 02:56:18 +09:00
/**
* Tell the FeedSub infrastructure whether we have any active OStatus
* usage for the feed ; if not it ' ll be able to garbage - collect the
* feed subscription .
2010-09-02 03:21:24 +09:00
*
2010-08-07 02:56:18 +09:00
* @ param FeedSub $feedsub
* @ param integer $count in / out
* @ return mixed hook return code
*/
2019-09-11 15:46:30 +09:00
public function onFeedSubSubscriberCount ( $feedsub , & $count )
2010-08-07 02:56:18 +09:00
{
2013-08-18 20:04:58 +09:00
$oprofile = Ostatus_profile :: getKV ( 'feeduri' , $feedsub -> uri );
2014-05-06 21:36:52 +09:00
if ( $oprofile instanceof Ostatus_profile ) {
2010-08-07 02:56:18 +09:00
$count += $oprofile -> subscriberCount ();
}
return true ;
}
2010-02-24 05:44:27 +09:00
/**
* When about to subscribe to a remote user , start a server - to - server
2017-05-01 18:04:27 +09:00
* WebSub subscription if needed . If we can ' t establish that , abort .
2010-02-24 05:44:27 +09:00
*
* @ fixme If something else aborts later , we could end up with a stray
2017-05-01 18:04:27 +09:00
* WebSub subscription . This is relatively harmless , though .
2010-02-24 05:44:27 +09:00
*
2013-09-10 04:35:16 +09:00
* @ param Profile $profile subscriber
* @ param Profile $other subscribee
2010-02-24 05:44:27 +09:00
*
* @ return hook return code
*
* @ throws Exception
*/
2019-09-11 15:46:30 +09:00
public function onStartSubscribe ( Profile $profile , Profile $other )
2010-02-24 05:44:27 +09:00
{
2013-09-10 04:35:16 +09:00
if ( ! $profile -> isLocal ()) {
2010-02-24 05:44:27 +09:00
return true ;
}
2013-08-18 20:04:58 +09:00
$oprofile = Ostatus_profile :: getKV ( 'profile_id' , $other -> id );
2014-05-06 21:36:52 +09:00
if ( ! $oprofile instanceof Ostatus_profile ) {
2010-02-24 05:44:27 +09:00
return true ;
}
2014-05-06 22:40:57 +09:00
$oprofile -> subscribe ();
2010-02-24 05:44:27 +09:00
}
/**
* Having established a remote subscription , send a notification to the
* remote OStatus profile ' s endpoint .
*
2013-09-10 04:35:16 +09:00
* @ param Profile $profile subscriber
* @ param Profile $other subscribee
2010-02-24 05:44:27 +09:00
*
* @ return hook return code
*
* @ throws Exception
*/
2019-09-11 15:46:30 +09:00
public function onEndSubscribe ( Profile $profile , Profile $other )
2010-02-21 01:48:42 +09:00
{
2013-09-10 04:35:16 +09:00
if ( ! $profile -> isLocal ()) {
2010-02-21 01:48:42 +09:00
return true ;
}
2013-08-18 20:04:58 +09:00
$oprofile = Ostatus_profile :: getKV ( 'profile_id' , $other -> id );
2014-05-06 21:36:52 +09:00
if ( ! $oprofile instanceof Ostatus_profile ) {
2010-02-21 01:48:42 +09:00
return true ;
}
2013-09-10 04:35:16 +09:00
$sub = Subscription :: pkeyGet ( array ( 'subscriber' => $profile -> id ,
2010-08-05 04:07:49 +09:00
'subscribed' => $other -> id ));
2010-02-22 00:53:32 +09:00
2010-08-05 04:07:49 +09:00
$act = $sub -> asActivity ();
2010-02-22 00:53:32 +09:00
2013-09-10 04:35:16 +09:00
$oprofile -> notifyActivity ( $act , $profile );
2010-02-21 01:48:42 +09:00
return true ;
}
2010-02-24 05:44:27 +09:00
/**
* Notify remote server and garbage collect unused feeds on unsubscribe .
2011-04-11 07:39:27 +09:00
* @ todo FIXME : Send these operations to background queues
2010-02-24 05:44:27 +09:00
*
* @ param User $user
* @ param Profile $other
* @ return hook return value
*/
2019-09-11 15:46:30 +09:00
public function onEndUnsubscribe ( Profile $profile , Profile $other )
2010-02-24 05:44:27 +09:00
{
2013-09-10 04:35:16 +09:00
if ( ! $profile -> isLocal ()) {
2010-02-24 05:44:27 +09:00
return true ;
}
2013-08-18 20:04:58 +09:00
$oprofile = Ostatus_profile :: getKV ( 'profile_id' , $other -> id );
2014-05-06 21:36:52 +09:00
if ( ! $oprofile instanceof Ostatus_profile ) {
2010-02-24 05:44:27 +09:00
return true ;
}
2017-05-01 18:04:27 +09:00
// Drop the WebSub subscription if there are no other subscribers.
2010-02-24 05:44:27 +09:00
$oprofile -> garbageCollect ();
$act = new Activity ();
$act -> verb = ActivityVerb :: UNFOLLOW ;
2019-09-11 15:46:30 +09:00
$act -> id = TagURI :: mint (
'unfollow:%d:%d:%s' ,
$profile -> id ,
$other -> id ,
common_date_iso8601 ( time ())
);
2010-02-24 05:44:27 +09:00
$act -> time = time ();
2011-04-11 07:39:27 +09:00
// TRANS: Title for unfollowing a remote profile.
2019-09-11 15:46:30 +09:00
$act -> title = _m ( 'TITLE' , 'Unfollow' );
2010-09-03 08:35:04 +09:00
// TRANS: Success message for unsubscribe from user attempt through OStatus.
// TRANS: %1$s is the unsubscriber's name, %2$s is the unsubscribed user's name.
2019-09-11 15:46:30 +09:00
$act -> content = sprintf (
_m ( '%1$s stopped following %2$s.' ),
$profile -> getBestName (),
$other -> getBestName ()
);
2010-02-24 05:44:27 +09:00
2014-07-03 01:50:28 +09:00
$act -> actor = $profile -> asActivityObject ();
2016-02-24 07:50:57 +09:00
$act -> objects [] = $other -> asActivityObject ();
2010-02-24 05:44:27 +09:00
2010-02-27 04:21:21 +09:00
$oprofile -> notifyActivity ( $act , $profile );
2010-02-24 05:44:27 +09:00
return true ;
}
2010-02-23 02:43:27 +09:00
/**
* When one of our local users tries to join a remote group ,
* notify the remote server . If the notification is rejected ,
* deny the join .
*
* @ param User_group $group
2011-09-19 03:06:35 +09:00
* @ param Profile $profile
2010-02-23 02:43:27 +09:00
*
* @ return mixed hook return value
2014-05-06 22:40:57 +09:00
* @ throws Exception of various kinds , some from $oprofile -> subscribe ();
2010-02-23 02:43:27 +09:00
*/
2019-09-11 15:46:30 +09:00
public function onStartJoinGroup ( $group , $profile )
2010-02-23 02:43:27 +09:00
{
2013-08-18 20:04:58 +09:00
$oprofile = Ostatus_profile :: getKV ( 'group_id' , $group -> id );
2014-05-06 21:36:52 +09:00
if ( ! $oprofile instanceof Ostatus_profile ) {
return true ;
}
2010-02-24 05:44:27 +09:00
2014-05-06 22:40:57 +09:00
$oprofile -> subscribe ();
2014-05-06 21:36:52 +09:00
// NOTE: we don't use Group_member::asActivity() since that record
// has not yet been created.
$act = new Activity ();
2019-09-11 15:46:30 +09:00
$act -> id = TagURI :: mint (
'join:%d:%d:%s' ,
$profile -> id ,
$group -> id ,
common_date_iso8601 ( time ())
);
2014-05-06 21:36:52 +09:00
2014-07-03 01:50:28 +09:00
$act -> actor = $profile -> asActivityObject ();
2014-05-06 21:36:52 +09:00
$act -> verb = ActivityVerb :: JOIN ;
2016-02-24 07:50:57 +09:00
$act -> objects [] = $oprofile -> asActivityObject ();
2014-05-06 21:36:52 +09:00
$act -> time = time ();
// TRANS: Title for joining a remote groep.
2019-09-11 15:46:30 +09:00
$act -> title = _m ( 'TITLE' , 'Join' );
2014-05-06 21:36:52 +09:00
// TRANS: Success message for subscribe to group attempt through OStatus.
// TRANS: %1$s is the member name, %2$s is the subscribed group's name.
2019-09-11 15:46:30 +09:00
$act -> content = sprintf (
_m ( '%1$s has joined group %2$s.' ),
$profile -> getBestName (),
$oprofile -> getBestName ()
);
2014-05-06 21:36:52 +09:00
if ( $oprofile -> notifyActivity ( $act , $profile )) {
return true ;
} else {
$oprofile -> garbageCollect ();
// TRANS: Exception thrown when joining a remote group fails.
throw new Exception ( _m ( 'Failed joining remote group.' ));
2010-02-23 02:43:27 +09:00
}
}
/**
* When one of our local users leaves a remote group , notify the remote
* server .
*
* @ fixme Might be good to schedule a resend of the leave notification
* if it failed due to a transitory error . We ' ve canceled the local
* membership already anyway , but if the remote server comes back up
* it ' ll be left with a stray membership record .
*
* @ param User_group $group
2011-09-19 03:06:35 +09:00
* @ param Profile $profile
2010-02-23 02:43:27 +09:00
*
* @ return mixed hook return value
*/
2019-09-11 15:46:30 +09:00
public function onEndLeaveGroup ( $group , $profile )
2010-02-23 02:43:27 +09:00
{
2013-08-18 20:04:58 +09:00
$oprofile = Ostatus_profile :: getKV ( 'group_id' , $group -> id );
2014-05-06 21:36:52 +09:00
if ( ! $oprofile instanceof Ostatus_profile ) {
return true ;
}
2010-02-24 05:59:10 +09:00
2017-05-01 18:04:27 +09:00
// Drop the WebSub subscription if there are no other subscribers.
2014-05-06 21:36:52 +09:00
$oprofile -> garbageCollect ();
2010-02-23 02:43:27 +09:00
2014-05-06 21:36:52 +09:00
$member = $profile ;
2010-02-23 02:43:27 +09:00
2014-05-06 21:36:52 +09:00
$act = new Activity ();
2019-09-11 15:46:30 +09:00
$act -> id = TagURI :: mint (
'leave:%d:%d:%s' ,
$member -> id ,
$group -> id ,
common_date_iso8601 ( time ())
);
2010-02-23 02:43:27 +09:00
2014-07-03 01:50:28 +09:00
$act -> actor = $member -> asActivityObject ();
2014-05-06 21:36:52 +09:00
$act -> verb = ActivityVerb :: LEAVE ;
2016-02-24 07:50:57 +09:00
$act -> objects [] = $oprofile -> asActivityObject ();
2010-02-23 02:43:27 +09:00
2014-05-06 21:36:52 +09:00
$act -> time = time ();
// TRANS: Title for leaving a remote group.
2019-09-11 15:46:30 +09:00
$act -> title = _m ( 'TITLE' , 'Leave' );
2014-05-06 21:36:52 +09:00
// TRANS: Success message for unsubscribe from group attempt through OStatus.
// TRANS: %1$s is the member name, %2$s is the unsubscribed group's name.
2019-09-11 15:46:30 +09:00
$act -> content = sprintf (
_m ( '%1$s has left group %2$s.' ),
$member -> getBestName (),
$oprofile -> getBestName ()
);
2014-05-06 21:36:52 +09:00
$oprofile -> notifyActivity ( $act , $member );
2010-02-23 02:43:27 +09:00
}
2011-03-07 04:15:34 +09:00
/**
* When one of our local users tries to subscribe to a remote peopletag ,
* notify the remote server . If the notification is rejected ,
* deny the subscription .
*
* @ param Profile_list $peopletag
* @ param User $user
*
* @ return mixed hook return value
2014-05-06 22:40:57 +09:00
* @ throws Exception of various kinds , some from $oprofile -> subscribe ();
2011-03-07 04:15:34 +09:00
*/
2019-09-11 15:46:30 +09:00
public function onStartSubscribePeopletag ( $peopletag , $user )
2011-03-07 04:15:34 +09:00
{
2013-08-18 20:04:58 +09:00
$oprofile = Ostatus_profile :: getKV ( 'peopletag_id' , $peopletag -> id );
2014-05-06 21:36:52 +09:00
if ( ! $oprofile instanceof Ostatus_profile ) {
return true ;
}
2011-03-07 04:15:34 +09:00
2014-05-06 22:40:57 +09:00
$oprofile -> subscribe ();
2014-05-06 21:36:52 +09:00
$sub = $user -> getProfile ();
$tagger = Profile :: getKV ( $peopletag -> tagger );
$act = new Activity ();
2019-09-11 15:46:30 +09:00
$act -> id = TagURI :: mint (
'subscribe_peopletag:%d:%d:%s' ,
$sub -> id ,
$peopletag -> id ,
common_date_iso8601 ( time ())
);
2014-05-06 21:36:52 +09:00
2014-07-03 01:50:28 +09:00
$act -> actor = $sub -> asActivityObject ();
2014-05-06 21:36:52 +09:00
$act -> verb = ActivityVerb :: FOLLOW ;
2016-02-24 07:50:57 +09:00
$act -> objects [] = $oprofile -> asActivityObject ();
2014-05-06 21:36:52 +09:00
$act -> time = time ();
// TRANS: Title for following a remote list.
2019-09-11 15:46:30 +09:00
$act -> title = _m ( 'TITLE' , 'Follow list' );
2014-05-06 21:36:52 +09:00
// TRANS: Success message for remote list follow through OStatus.
// TRANS: %1$s is the subscriber name, %2$s is the list, %3$s is the lister's name.
2019-09-11 15:46:30 +09:00
$act -> content = sprintf (
_m ( '%1$s is now following people listed in %2$s by %3$s.' ),
$sub -> getBestName (),
$oprofile -> getBestName (),
$tagger -> getBestName ()
);
2014-05-06 21:36:52 +09:00
if ( $oprofile -> notifyActivity ( $act , $sub )) {
return true ;
} else {
$oprofile -> garbageCollect ();
// TRANS: Exception thrown when subscription to remote list fails.
throw new Exception ( _m ( 'Failed subscribing to remote list.' ));
2011-03-07 04:15:34 +09:00
}
}
/**
* When one of our local users unsubscribes to a remote peopletag , notify the remote
* server .
*
* @ param Profile_list $peopletag
* @ param User $user
*
* @ return mixed hook return value
*/
2019-09-11 15:46:30 +09:00
public function onEndUnsubscribePeopletag ( $peopletag , $user )
2011-03-07 04:15:34 +09:00
{
2013-08-18 20:04:58 +09:00
$oprofile = Ostatus_profile :: getKV ( 'peopletag_id' , $peopletag -> id );
2014-05-06 21:36:52 +09:00
if ( ! $oprofile instanceof Ostatus_profile ) {
return true ;
2011-03-07 04:15:34 +09:00
}
2014-05-06 21:36:52 +09:00
2017-05-01 18:04:27 +09:00
// Drop the WebSub subscription if there are no other subscribers.
2014-05-06 21:36:52 +09:00
$oprofile -> garbageCollect ();
$sub = Profile :: getKV ( $user -> id );
$tagger = Profile :: getKV ( $peopletag -> tagger );
$act = new Activity ();
2019-09-11 15:46:30 +09:00
$act -> id = TagURI :: mint (
'unsubscribe_peopletag:%d:%d:%s' ,
$sub -> id ,
$peopletag -> id ,
common_date_iso8601 ( time ())
);
2014-05-06 21:36:52 +09:00
2014-07-03 01:50:28 +09:00
$act -> actor = $member -> asActivityObject ();
2014-05-06 21:36:52 +09:00
$act -> verb = ActivityVerb :: UNFOLLOW ;
2016-02-24 07:50:57 +09:00
$act -> objects [] = $oprofile -> asActivityObject ();
2014-05-06 21:36:52 +09:00
$act -> time = time ();
// TRANS: Title for unfollowing a remote list.
$act -> title = _m ( 'Unfollow list' );
// TRANS: Success message for remote list unfollow through OStatus.
// TRANS: %1$s is the subscriber name, %2$s is the list, %3$s is the lister's name.
2019-09-11 15:46:30 +09:00
$act -> content = sprintf (
_m ( '%1$s stopped following the list %2$s by %3$s.' ),
$sub -> getBestName (),
$oprofile -> getBestName (),
$tagger -> getBestName ()
);
2014-05-06 21:36:52 +09:00
$oprofile -> notifyActivity ( $act , $user );
2011-03-07 04:15:34 +09:00
}
2015-02-04 01:50:21 +09:00
/**
* Notify remote users when their notices get favorited .
*
* @ param Profile or User $profile of local user doing the faving
* @ param Notice $notice being favored
* @ return hook return value
*/
2019-09-11 15:46:30 +09:00
public function onEndFavorNotice ( Profile $profile , Notice $notice )
2015-02-04 01:50:21 +09:00
{
// Only distribute local users' favor actions, remote users
// will have already distributed theirs.
if ( ! $profile -> isLocal ()) {
return true ;
}
$oprofile = Ostatus_profile :: getKV ( 'profile_id' , $notice -> profile_id );
if ( ! $oprofile instanceof Ostatus_profile ) {
return true ;
}
$fav = Fave :: pkeyGet ( array ( 'user_id' => $profile -> id ,
'notice_id' => $notice -> id ));
if ( ! $fav instanceof Fave ) {
// That's weird.
// TODO: Make pkeyGet throw exception, since this is a critical failure.
return true ;
}
$act = $fav -> asActivity ();
$oprofile -> notifyActivity ( $act , $profile );
return true ;
}
2011-04-09 20:47:32 +09:00
/**
* Notify remote user it has got a new people tag
* - tag verb is queued
* - the subscription is done immediately if not present
*
* @ param Profile_tag $ptag the people tag that was created
* @ return hook return value
2014-05-06 22:40:57 +09:00
* @ throws Exception of various kinds , some from $oprofile -> subscribe ();
2011-04-09 20:47:32 +09:00
*/
2019-09-11 15:46:30 +09:00
public function onEndTagProfile ( $ptag )
2011-03-07 04:15:34 +09:00
{
2013-08-18 20:04:58 +09:00
$oprofile = Ostatus_profile :: getKV ( 'profile_id' , $ptag -> tagged );
2014-05-06 21:36:52 +09:00
if ( ! $oprofile instanceof Ostatus_profile ) {
2011-03-07 04:15:34 +09:00
return true ;
}
$plist = $ptag -> getMeta ();
if ( $plist -> private ) {
return true ;
}
$act = new Activity ();
$tagger = $plist -> getTagger ();
2013-08-18 20:04:58 +09:00
$tagged = Profile :: getKV ( 'id' , $ptag -> tagged );
2011-03-07 04:15:34 +09:00
$act -> verb = ActivityVerb :: TAG ;
2019-09-11 15:46:30 +09:00
$act -> id = TagURI :: mint (
'tag_profile:%d:%d:%s' ,
$plist -> tagger ,
$plist -> id ,
common_date_iso8601 ( time ())
);
2011-03-07 04:15:34 +09:00
$act -> time = time ();
2011-04-30 01:59:47 +09:00
// TRANS: Title for listing a remote profile.
2019-09-11 15:46:30 +09:00
$act -> title = _m ( 'TITLE' , 'List' );
2011-04-30 01:59:47 +09:00
// TRANS: Success message for remote list addition through OStatus.
// TRANS: %1$s is the list creator's name, %2$s is the added list member, %3$s is the list name.
2019-09-11 15:46:30 +09:00
$act -> content = sprintf (
_m ( '%1$s listed %2$s in the list %3$s.' ),
$tagger -> getBestName (),
$tagged -> getBestName (),
$plist -> getBestName ()
);
2011-03-07 04:15:34 +09:00
2014-07-03 01:50:28 +09:00
$act -> actor = $tagger -> asActivityObject ();
$act -> objects = array ( $tagged -> asActivityObject ());
2011-03-07 04:15:34 +09:00
$act -> target = ActivityObject :: fromPeopletag ( $plist );
2011-04-09 20:47:32 +09:00
$oprofile -> notifyDeferred ( $act , $tagger );
2011-03-07 04:15:34 +09:00
2017-05-01 18:04:27 +09:00
// initiate a WebSub subscription for the person being tagged
2014-05-06 22:40:57 +09:00
$oprofile -> subscribe ();
2011-03-07 04:15:34 +09:00
return true ;
}
2011-04-09 20:47:32 +09:00
/**
* Notify remote user that a people tag has been removed
* - untag verb is queued
* - the subscription is undone immediately if not required
* i . e garbageCollect () ' d
*
* @ param Profile_tag $ptag the people tag that was deleted
* @ return hook return value
*/
2019-09-11 15:46:30 +09:00
public function onEndUntagProfile ( $ptag )
2011-03-07 04:15:34 +09:00
{
2013-08-18 20:04:58 +09:00
$oprofile = Ostatus_profile :: getKV ( 'profile_id' , $ptag -> tagged );
2014-05-06 21:36:52 +09:00
if ( ! $oprofile instanceof Ostatus_profile ) {
2011-03-07 04:15:34 +09:00
return true ;
}
$plist = $ptag -> getMeta ();
if ( $plist -> private ) {
return true ;
}
$act = new Activity ();
$tagger = $plist -> getTagger ();
2013-08-18 20:04:58 +09:00
$tagged = Profile :: getKV ( 'id' , $ptag -> tagged );
2011-03-07 04:15:34 +09:00
$act -> verb = ActivityVerb :: UNTAG ;
2019-09-11 15:46:30 +09:00
$act -> id = TagURI :: mint (
'untag_profile:%d:%d:%s' ,
$plist -> tagger ,
$plist -> id ,
common_date_iso8601 ( time ())
);
2011-03-07 04:15:34 +09:00
$act -> time = time ();
2011-04-30 01:59:47 +09:00
// TRANS: Title for unlisting a remote profile.
2019-09-11 15:46:30 +09:00
$act -> title = _m ( 'TITLE' , 'Unlist' );
2011-04-30 01:59:47 +09:00
// TRANS: Success message for remote list removal through OStatus.
// TRANS: %1$s is the list creator's name, %2$s is the removed list member, %3$s is the list name.
2019-09-11 15:46:30 +09:00
$act -> content = sprintf (
_m ( '%1$s removed %2$s from the list %3$s.' ),
$tagger -> getBestName (),
$tagged -> getBestName (),
$plist -> getBestName ()
);
2011-03-07 04:15:34 +09:00
2014-07-03 01:50:28 +09:00
$act -> actor = $tagger -> asActivityObject ();
$act -> objects = array ( $tagged -> asActivityObject ());
2011-03-07 04:15:34 +09:00
$act -> target = ActivityObject :: fromPeopletag ( $plist );
2011-04-09 20:47:32 +09:00
$oprofile -> notifyDeferred ( $act , $tagger );
2011-03-07 04:15:34 +09:00
2017-05-01 18:04:27 +09:00
// unsubscribe to WebSub feed if no more required
2011-03-07 04:15:34 +09:00
$oprofile -> garbageCollect ();
return true ;
}
2010-02-21 08:56:36 +09:00
/**
* Notify remote users when their notices get de - favorited .
*
2010-02-22 03:32:24 +09:00
* @ param Profile $profile Profile person doing the de - faving
* @ param Notice $notice Notice being favored
*
2010-02-21 08:56:36 +09:00
* @ return hook return value
*/
2019-09-11 15:46:30 +09:00
public function onEndDisfavorNotice ( Profile $profile , Notice $notice )
2010-02-21 09:58:20 +09:00
{
2014-05-06 21:36:52 +09:00
// Only distribute local users' disfavor actions, remote users
// will have already distributed theirs.
if ( ! $profile -> isLocal ()) {
2010-02-21 10:34:29 +09:00
return true ;
}
2013-08-18 20:04:58 +09:00
$oprofile = Ostatus_profile :: getKV ( 'profile_id' , $notice -> profile_id );
2014-05-06 21:36:52 +09:00
if ( ! $oprofile instanceof Ostatus_profile ) {
2010-02-22 00:53:32 +09:00
return true ;
2010-02-21 09:58:20 +09:00
}
2010-02-21 08:56:36 +09:00
2010-02-22 00:53:32 +09:00
$act = new Activity ();
$act -> verb = ActivityVerb :: UNFAVORITE ;
2019-09-11 15:46:30 +09:00
$act -> id = TagURI :: mint (
'disfavor:%d:%d:%s' ,
$profile -> id ,
$notice -> id ,
common_date_iso8601 ( time ())
);
2010-02-22 00:53:32 +09:00
$act -> time = time ();
2011-04-30 01:59:47 +09:00
// TRANS: Title for unliking a remote notice.
$act -> title = _m ( 'Unlike' );
2010-09-03 08:35:04 +09:00
// TRANS: Success message for remove a favorite notice through OStatus.
// TRANS: %1$s is the unfavoring user's name, %2$s is URI to the no longer favored notice.
2019-09-11 15:46:30 +09:00
$act -> content = sprintf (
_m ( '%1$s no longer likes %2$s.' ),
$profile -> getBestName (),
$notice -> getUrl ()
);
2010-02-22 00:53:32 +09:00
2014-07-03 01:50:28 +09:00
$act -> actor = $profile -> asActivityObject ();
2016-02-24 07:50:57 +09:00
$act -> objects [] = $notice -> asActivityObject ();
2010-02-22 00:53:32 +09:00
2010-02-27 04:21:21 +09:00
$oprofile -> notifyActivity ( $act , $profile );
2010-02-22 00:53:32 +09:00
2010-02-21 10:34:29 +09:00
return true ;
}
2010-02-22 12:52:52 +09:00
2019-09-11 15:46:30 +09:00
public function onStartGetProfileUri ( $profile , & $uri )
2010-02-22 12:52:52 +09:00
{
2013-08-18 20:04:58 +09:00
$oprofile = Ostatus_profile :: getKV ( 'profile_id' , $profile -> id );
2013-10-30 22:43:40 +09:00
if ( $oprofile instanceof Ostatus_profile ) {
2010-02-22 12:52:52 +09:00
$uri = $oprofile -> uri ;
return false ;
}
return true ;
}
2010-02-23 01:07:48 +09:00
2019-09-11 15:46:30 +09:00
public function onStartUserGroupHomeUrl ( $group , & $url )
2010-02-23 09:44:45 +09:00
{
2010-02-26 04:59:15 +09:00
return $this -> onStartUserGroupPermalink ( $group , $url );
2010-02-23 09:44:45 +09:00
}
2019-09-11 15:46:30 +09:00
public function onStartUserGroupPermalink ( $group , & $url )
2010-02-23 09:44:45 +09:00
{
2013-08-18 20:04:58 +09:00
$oprofile = Ostatus_profile :: getKV ( 'group_id' , $group -> id );
2014-05-06 21:36:52 +09:00
if ( $oprofile instanceof Ostatus_profile ) {
2010-02-23 09:44:45 +09:00
// @fixme this should probably be in the user_group table
// @fixme this uri not guaranteed to be a profile page
$url = $oprofile -> uri ;
return false ;
}
}
2019-09-11 15:46:30 +09:00
public function onStartShowUserGroupsContent ( $action )
2010-03-03 02:41:18 +09:00
{
$this -> showEntityRemoteSubscribe ( $action );
return true ;
}
2019-09-11 15:46:30 +09:00
public function onEndShowGroupsMiniList ( $action )
2010-03-04 02:56:19 +09:00
{
2019-08-25 11:14:50 +09:00
$this -> showEntityRemoteSubscribe ( $action );
2010-03-04 02:56:19 +09:00
return true ;
}
2019-09-11 15:46:30 +09:00
public function showEntityRemoteSubscribe ( $action )
2010-02-23 01:07:48 +09:00
{
2015-07-10 20:44:47 +09:00
if ( ! $action -> getScoped () instanceof Profile ) {
// early return if we're not logged in
return true ;
}
if ( $action -> getScoped () -> sameAs ( $action -> getTarget ())) {
2010-02-23 01:07:48 +09:00
$action -> elementStart ( 'div' , 'entity_actions' );
$action -> elementStart ( 'p' , array ( 'id' => 'entity_remote_subscribe' ,
'class' => 'entity_subscribe' ));
2019-09-11 15:46:30 +09:00
$action -> element (
'a' ,
[
'href' => common_local_url ( 'ostatusgroup' ),
'class' => 'entity_remote_subscribe' ,
],
// TRANS: Link text for link to remote subscribe.
_m ( 'Remote' )
);
2010-02-23 01:07:48 +09:00
$action -> elementEnd ( 'p' );
$action -> elementEnd ( 'div' );
}
}
2010-02-25 05:36:36 +09:00
/**
* Ping remote profiles with updates to this profile .
* Salmon pings are queued for background processing .
*/
2019-09-11 15:46:30 +09:00
public function onEndBroadcastProfile ( Profile $profile )
2010-02-25 05:36:36 +09:00
{
2013-08-18 20:04:58 +09:00
$user = User :: getKV ( 'id' , $profile -> id );
2010-02-25 05:36:36 +09:00
// Find foreign accounts I'm subscribed to that support Salmon pings.
//
2017-05-01 18:04:27 +09:00
// @fixme we could run updates through the WebSub feed too,
2010-02-25 05:36:36 +09:00
// in which case we can skip Salmon pings to folks who
// are also subscribed to me.
$sql = " SELECT * FROM ostatus_profile " .
" WHERE profile_id IN " .
" (SELECT subscribed FROM subscription WHERE subscriber=%d) " .
" OR group_id IN " .
" (SELECT group_id FROM group_member WHERE profile_id=%d) " ;
$oprofile = new Ostatus_profile ();
$oprofile -> query ( sprintf ( $sql , $profile -> id , $profile -> id ));
if ( $oprofile -> N == 0 ) {
common_log ( LOG_DEBUG , " No OStatus remote subscribees for $profile->nickname " );
return true ;
}
$act = new Activity ();
$act -> verb = ActivityVerb :: UPDATE_PROFILE ;
2019-09-11 15:46:30 +09:00
$act -> id = TagURI :: mint (
'update-profile:%d:%s' ,
$profile -> id ,
common_date_iso8601 ( time ())
);
2010-02-25 05:36:36 +09:00
$act -> time = time ();
2010-09-19 22:17:36 +09:00
// TRANS: Title for activity.
2011-04-30 01:59:47 +09:00
$act -> title = _m ( 'Profile update' );
2010-09-03 08:35:04 +09:00
// TRANS: Ping text for remote profile update through OStatus.
// TRANS: %s is user that updated their profile.
2019-09-11 15:46:30 +09:00
$act -> content = sprintf (
_m ( '%s has updated their profile page.' ),
$profile -> getBestName ()
);
2010-02-25 05:36:36 +09:00
2014-07-03 01:50:28 +09:00
$act -> actor = $profile -> asActivityObject ();
2016-02-24 07:50:57 +09:00
$act -> objects [] = $act -> actor ;
2010-02-25 05:36:36 +09:00
while ( $oprofile -> fetch ()) {
2010-02-27 04:21:21 +09:00
$oprofile -> notifyDeferred ( $act , $profile );
2010-02-25 05:36:36 +09:00
}
return true ;
}
2010-03-04 04:32:03 +09:00
2019-09-11 15:46:30 +09:00
public function onEndShowAccountProfileBlock ( HTMLOutputter $out , Profile $profile )
2017-05-02 04:18:04 +09:00
{
if ( $profile -> isLocal ()) {
return true ;
}
2017-05-02 16:14:30 +09:00
try {
$oprofile = Ostatus_profile :: fromProfile ( $profile );
2019-06-12 10:20:25 +09:00
} catch ( Exception $e ) {
2017-05-02 16:14:30 +09:00
// Not a remote Ostatus_profile! Maybe some other network
// that has imported a non-local user?
return true ;
}
try {
$feedsub = $oprofile -> getFeedSub ();
} catch ( NoResultException $e ) {
// No WebSub subscription has been attempted or exists for this profile
// which is the case, say for remote profiles that are only included
// via mentions or repeat/share.
return true ;
}
2017-05-02 04:18:04 +09:00
$websub_states = [
2019-09-12 20:19:21 +09:00
'subscribe' => _m ( 'Pending' ),
'active' => _m ( 'Active' ),
'unsubscribe' => _m ( 'Unsubscribing' ),
'nohub' => _m ( 'Polling' ),
'inactive' => _m ( 'Inactive' ),
];
2017-05-02 04:18:04 +09:00
$out -> elementStart ( 'dl' , 'entity_tags ostatus_profile' );
$out -> element ( 'dt' , null , _m ( 'WebSub' ));
$out -> element ( 'dd' , null , $websub_states [ $feedsub -> sub_state ]);
$out -> elementEnd ( 'dl' );
}
2015-07-10 20:44:47 +09:00
// FIXME: This one can accept both an Action and a Widget. Confusing! Refactor to (HTMLOutputter $out, Profile $target)!
2019-09-11 15:46:30 +09:00
public function onStartProfileListItemActionElements ( $item )
2010-03-04 04:32:03 +09:00
{
2015-07-10 20:44:47 +09:00
if ( common_logged_in ()) {
// only non-logged in users get to see the "remote subscribe" form
return true ;
2019-08-25 11:14:50 +09:00
}
$target = $item -> getTarget ();
if ( ! $target -> isLocal ()) {
2015-07-10 20:44:47 +09:00
// we can (for now) only provide remote subscribe forms for local users
return true ;
}
2010-03-04 04:32:03 +09:00
2015-07-10 20:44:47 +09:00
if ( $item instanceof ProfileAction ) {
$output = $item ;
} elseif ( $item instanceof Widget ) {
$output = $item -> out ;
} else {
// Bad $item class, don't know how to use this for outputting!
throw new ServerException ( 'Bad item type for onStartProfileListItemActionElements' );
2010-03-04 04:32:03 +09:00
}
2015-07-10 20:44:47 +09:00
$output -> elementStart ( 'li' , 'entity_tag' );
2019-08-25 11:14:50 +09:00
$url = common_local_url ( 'ostatustag' , [ 'nickname' => $target -> getNickname ()]);
$output -> element ( 'a' ,
2019-09-11 15:46:30 +09:00
[
'href' => $url ,
'class' => 'entity_remote_tag' ,
],
2019-08-25 11:14:50 +09:00
// TRANS: Link text for a user to list an OStatus user.
2015-07-10 20:44:47 +09:00
_m ( 'List' ));
$output -> elementEnd ( 'li' );
2010-03-04 04:32:03 +09:00
return true ;
}
2010-03-05 02:02:01 +09:00
2019-04-22 15:27:50 +09:00
/**
* Plugin Nodeinfo information
*
* @ param array $protocols
* @ return bool hook true
*/
public function onNodeInfoProtocols ( array & $protocols )
{
$protocols [] = " ostatus " ;
return true ;
}
2019-08-12 23:03:30 +09:00
public function onPluginVersion ( array & $versions ) : bool
2010-03-05 02:02:01 +09:00
{
$versions [] = array ( 'name' => 'OStatus' ,
2019-06-03 09:56:52 +09:00
'version' => self :: PLUGIN_VERSION ,
2010-03-05 02:02:01 +09:00
'author' => 'Evan Prodromou, James Walker, Brion Vibber, Zach Copley' ,
2016-01-23 01:38:42 +09:00
'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/OStatus' ,
2010-09-19 22:17:36 +09:00
// TRANS: Plugin description.
'rawdescription' => _m ( 'Follow people across social networks that implement ' .
'<a href="http://ostatus.org/">OStatus</a>.' ));
2010-03-05 02:02:01 +09:00
return true ;
}
2010-03-11 10:00:05 +09:00
/**
2010-09-28 03:29:54 +09:00
* Utility function to check if the given URI is a canonical group profile
2010-03-11 10:00:05 +09:00
* page , and if so return the ID number .
*
* @ param string $url
* @ return mixed int or false
*/
public static function localGroupFromUrl ( $url )
{
2013-08-18 20:04:58 +09:00
$group = User_group :: getKV ( 'uri' , $url );
2014-05-19 21:46:54 +09:00
if ( $group instanceof User_group ) {
if ( $group -> isLocal ()) {
2010-09-28 03:29:54 +09:00
return $group -> id ;
}
} else {
// To find local groups which haven't had their uri fields filled out...
// If the domain has changed since a subscriber got the URI, it'll
// be broken.
$template = common_local_url ( 'groupbyid' , array ( 'id' => '31337' ));
$template = preg_quote ( $template , '/' );
$template = str_replace ( '31337' , '(\d+)' , $template );
if ( preg_match ( " / $template / " , $url , $matches )) {
return intval ( $matches [ 1 ]);
}
2010-03-11 10:00:05 +09:00
}
return false ;
}
2010-08-04 07:50:21 +09:00
public function onStartProfileGetAtomFeed ( $profile , & $feed )
{
2013-08-18 20:04:58 +09:00
$oprofile = Ostatus_profile :: getKV ( 'profile_id' , $profile -> id );
2010-08-04 07:50:21 +09:00
2014-05-06 21:36:52 +09:00
if ( ! $oprofile instanceof Ostatus_profile ) {
2010-08-04 07:50:21 +09:00
return true ;
}
$feed = $oprofile -> feeduri ;
return false ;
}
2010-09-02 05:16:38 +09:00
2019-09-11 15:46:30 +09:00
public function onStartGetProfileFromURI ( $uri , & $profile )
2011-01-18 05:45:03 +09:00
{
// Don't want to do Web-based discovery on our own server,
2016-01-09 22:36:47 +09:00
// so we check locally first. This duplicates the functionality
// in the Profile class, since the plugin always runs before
// that local lookup, but since we return false it won't run double.
2010-09-02 05:16:38 +09:00
2013-08-18 20:04:58 +09:00
$user = User :: getKV ( 'uri' , $uri );
2016-01-09 22:36:47 +09:00
if ( $user instanceof User ) {
2011-01-18 05:45:03 +09:00
$profile = $user -> getProfile ();
return false ;
2016-01-09 22:36:47 +09:00
} else {
$group = User_group :: getKV ( 'uri' , $uri );
if ( $group instanceof User_group ) {
$profile = $group -> getProfile ();
return false ;
}
2011-01-18 05:45:03 +09:00
}
2010-09-02 05:16:38 +09:00
2011-01-18 05:45:03 +09:00
// Now, check remotely
2013-10-30 22:43:40 +09:00
try {
$oprofile = Ostatus_profile :: ensureProfileURI ( $uri );
2010-09-02 05:16:38 +09:00
$profile = $oprofile -> localProfile ();
2013-10-30 22:43:40 +09:00
return ! ( $profile instanceof Profile ); // localProfile won't throw exception but can return null
} catch ( Exception $e ) {
return true ; // It's not an OStatus profile as far as we know, continue event handling
2010-09-02 05:16:38 +09:00
}
}
2010-09-06 06:43:29 +09:00
2019-09-11 15:46:30 +09:00
public function onEndWebFingerNoticeLinks ( XML_XRD $xrd , Notice $target )
2013-11-02 00:54:59 +09:00
{
2016-03-27 21:56:27 +09:00
$salmon_url = null ;
$actor = $target -> getProfile ();
if ( $actor -> isLocal ()) {
$profiletype = $this -> profileTypeString ( $actor );
$salmon_url = common_local_url ( " { $profiletype } salmon " , array ( 'id' => $actor -> getID ()));
} else {
try {
$oprofile = Ostatus_profile :: fromProfile ( $actor );
$salmon_url = $oprofile -> salmonuri ;
} catch ( Exception $e ) {
// Even though it's not a local user, we couldn't get an Ostatus_profile?!
}
}
// Ostatus_profile salmon URL may be empty
if ( ! empty ( $salmon_url )) {
$xrd -> links [] = new XML_XRD_Element_Link ( Salmon :: REL_SALMON , $salmon_url );
}
2013-11-02 00:54:59 +09:00
return true ;
}
2019-09-11 15:46:30 +09:00
public function onEndWebFingerProfileLinks ( XML_XRD $xrd , Profile $target )
2010-11-27 11:38:38 +09:00
{
2015-01-26 20:10:27 +09:00
if ( $target -> getObjectType () === ActivityObject :: PERSON ) {
$this -> addWebFingerPersonLinks ( $xrd , $target );
2016-06-26 03:13:19 +09:00
} elseif ( $target -> getObjectType () === ActivityObject :: GROUP ) {
2019-09-11 15:46:30 +09:00
$xrd -> links [] = new XML_XRD_Element_Link (
Discovery :: UPDATESFROM ,
common_local_url (
'ApiTimelineGroup' ,
[ 'id' => $target -> getGroup () -> getID (), 'format' => 'atom' ]
),
'application/atom+xml'
);
2015-01-26 20:10:27 +09:00
}
2011-04-11 07:39:27 +09:00
2015-01-26 20:10:27 +09:00
// Salmon
$profiletype = $this -> profileTypeString ( $target );
$salmon_url = common_local_url ( " { $profiletype } salmon " , array ( 'id' => $target -> id ));
2010-11-27 11:38:38 +09:00
2013-10-01 00:13:03 +09:00
$xrd -> links [] = new XML_XRD_Element_Link ( Salmon :: REL_SALMON , $salmon_url );
2013-11-02 01:00:12 +09:00
// XXX: these are deprecated, but StatusNet only looks for NS_REPLIES
2013-10-01 00:13:03 +09:00
$xrd -> links [] = new XML_XRD_Element_Link ( Salmon :: NS_REPLIES , $salmon_url );
$xrd -> links [] = new XML_XRD_Element_Link ( Salmon :: NS_MENTIONS , $salmon_url );
2010-11-27 11:38:38 +09:00
2015-01-26 20:10:27 +09:00
return true ;
}
protected function profileTypeString ( Profile $target )
{
// This is just used to have a definitive string response to "USERsalmon" or "GROUPsalmon"
switch ( $target -> getObjectType ()) {
case ActivityObject :: PERSON :
return 'user' ;
case ActivityObject :: GROUP :
return 'group' ;
default :
throw new ServerException ( 'Unknown profile type for WebFinger profile links' );
}
}
protected function addWebFingerPersonLinks ( XML_XRD $xrd , Profile $target )
{
2019-09-11 15:46:30 +09:00
$xrd -> links [] = new XML_XRD_Element_Link (
Discovery :: UPDATESFROM ,
common_local_url (
'ApiTimelineUser' ,
[ 'id' => $target -> id , 'format' => 'atom' ]
),
'application/atom+xml'
);
2015-01-26 20:10:27 +09:00
2014-06-03 02:33:09 +09:00
// Get this profile's keypair
$magicsig = Magicsig :: getKV ( 'user_id' , $target -> id );
if ( ! $magicsig instanceof Magicsig && $target -> isLocal ()) {
2014-06-03 04:50:40 +09:00
$magicsig = Magicsig :: generate ( $target -> getUser ());
2010-11-27 11:38:38 +09:00
}
2015-06-06 20:49:27 +09:00
if ( ! $magicsig instanceof Magicsig ) {
return false ; // value doesn't mean anything, just figured I'd indicate this function didn't do anything
}
if ( Event :: handle ( 'StartAttachPubkeyToUserXRD' , array ( $magicsig , $xrd , $target ))) {
2019-09-11 15:46:30 +09:00
$xrd -> links [] = new XML_XRD_Element_Link (
Magicsig :: PUBLICKEYREL ,
'data:application/magic-public-key,' . $magicsig -> toString ()
);
2015-06-06 20:49:27 +09:00
// The following event handles plugins like Diaspora which add their own version of the Magicsig pubkey
Event :: handle ( 'EndAttachPubkeyToUserXRD' , array ( $magicsig , $xrd , $target ));
2014-06-03 02:33:09 +09:00
}
2010-09-06 06:43:29 +09:00
}
2014-07-01 18:42:08 +09:00
public function onGetLocalAttentions ( Profile $actor , array $attention_uris , array & $mentions , array & $groups )
{
2015-11-09 07:31:23 +09:00
list ( $groups , $mentions ) = Ostatus_profile :: filterAttention ( $actor , $attention_uris );
2014-07-01 18:42:08 +09:00
}
2014-07-01 22:48:34 +09:00
// FIXME: Maybe this shouldn't be so authoritative that it breaks other remote profile lookups?
2019-09-11 15:46:30 +09:00
public static function onCheckActivityAuthorship ( Activity $activity , Profile & $profile )
2014-07-01 22:48:34 +09:00
{
try {
2014-12-31 13:56:33 +09:00
$oprofile = Ostatus_profile :: ensureProfileURL ( $profile -> getUrl ());
2014-11-25 07:40:06 +09:00
$profile = $oprofile -> checkAuthorship ( $activity );
2014-07-01 22:48:34 +09:00
} catch ( Exception $e ) {
2014-10-25 21:23:15 +09:00
common_log ( LOG_ERR , 'Could not get a profile or check authorship (' . get_class ( $e ) . ': "' . $e -> getMessage () . '") for activity ID: ' . $activity -> id );
2014-07-01 22:48:34 +09:00
$profile = null ;
return false ;
}
return true ;
}
2014-07-04 18:45:42 +09:00
public function onProfileDeleteRelated ( $profile , & $related )
{
// Ostatus_profile has a 'profile_id' property, which will be used to find the object
$related [] = 'Ostatus_profile' ;
2015-01-24 20:47:39 +09:00
// Magicsig has a "user_id" column instead, so we have to delete it more manually:
$magicsig = Magicsig :: getKV ( 'user_id' , $profile -> id );
if ( $magicsig instanceof Magicsig ) {
$magicsig -> delete ();
}
2014-07-04 18:45:42 +09:00
return true ;
}
2015-10-04 07:17:07 +09:00
2015-10-04 19:06:48 +09:00
public function onSalmonSlap ( $endpoint_uri , MagicEnvelope $magic_env , Profile $target = null )
2015-10-04 07:17:07 +09:00
{
2016-06-24 06:27:18 +09:00
try {
$envxml = $magic_env -> toXML ( $target );
} catch ( Exception $e ) {
common_log ( LOG_ERR , sprintf ( 'Could not generate Magic Envelope XML for profile id==' . $target -> getID () . ': ' . $e -> getMessage ()));
return false ;
}
2015-10-04 07:17:07 +09:00
$headers = array ( 'Content-Type: application/magic-envelope+xml' );
try {
$client = new HTTPClient ();
$client -> setBody ( $envxml );
$response = $client -> post ( $endpoint_uri , $headers );
2016-01-13 22:17:49 +09:00
} catch ( Exception $e ) {
2015-10-04 07:17:07 +09:00
common_log ( LOG_ERR , " Salmon post to $endpoint_uri failed: " . $e -> getMessage ());
return false ;
}
if ( $response -> getStatus () === 422 ) {
2015-10-05 00:26:35 +09:00
common_debug ( sprintf ( 'Salmon (from profile %d) endpoint %s returned status %s. We assume it is a Diaspora seed; will adapt and try again if that plugin is enabled!' , $magic_env -> getActor () -> getID (), $endpoint_uri , $response -> getStatus ()));
2015-10-04 07:17:07 +09:00
return true ;
}
2017-08-05 23:21:34 +09:00
// The different kinds of accepted responses...
// 200 OK means it's all ok
// 201 Created is what Mastodon returns when it's ok
// 202 Accepted is what we get from Diaspora, also good
if ( ! in_array ( $response -> getStatus (), array ( 200 , 201 , 202 ))) {
2019-09-11 15:46:30 +09:00
common_log ( LOG_ERR , sprintf (
'Salmon (from profile %d) endpoint %s returned status %s: %s' ,
$magic_env -> getActor () -> getID (),
$endpoint_uri ,
$response -> getStatus (),
$response -> getBody ()
));
2015-10-04 07:17:07 +09:00
return true ;
}
// Since we completed the salmon slap, we discontinue the event
return false ;
}
2015-11-06 00:16:02 +09:00
2015-10-21 10:10:48 +09:00
public function onCronDaily ()
{
try {
$sub = FeedSub :: renewalCheck ();
} catch ( NoResultException $e ) {
common_log ( LOG_INFO , " There were no expiring feeds. " );
return ;
}
2015-10-21 10:50:03 +09:00
$qm = QueueManager :: get ();
2015-10-21 10:10:48 +09:00
while ( $sub -> fetch ()) {
2015-10-21 10:50:03 +09:00
$item = array ( 'feedsub_id' => $sub -> id );
$qm -> enqueue ( $item , 'pushrenew' );
2015-10-21 10:10:48 +09:00
}
}
2019-08-25 11:14:50 +09:00
/**
* Try to grab and store the remote profile by the given uri
*
* @ param string $uri
* @ param Profile & $profile
* @ return bool
*/
public function onRemoteFollowPullProfile ( string $uri , ? Profile & $profile ) : bool
{
$oprofile = pullRemoteProfile ( $uri );
if ( $oprofile instanceof Ostatus_profile ) {
// validation
if ( $oprofile -> isGroup ()) {
$target = common_local_url ( 'ostatusgroup' , [], [ 'profile' => $uri ]);
common_redirect ( $target , 303 );
} else if ( $oprofile -> isPeopletag ()) {
$target = common_local_url ( 'ostatuspeopletag' , [], [ 'profile' => $uri ]);
common_redirect ( $target , 303 );
}
$profile = $oprofile -> localProfile ();
}
return is_null ( $profile );
}
2019-10-24 10:08:54 +09:00
/**
* To be consistent with the ActivityPub protocol , we need to make sure that
* all ostatus profiles are unique and use non - fancy URIs . This method executes
* an external script to ensure that conditions .
*
* @ return bool hook flag
* @ author Bruno Casteleiro < brunoccast @ fc . up . pt >
*/
public function onEndUpgrade () : bool
{
$flag = __DIR__ . DIRECTORY_SEPARATOR . '.ostatus_version.txt' ;
$script = __DIR__ . DIRECTORY_SEPARATOR . 'scripts' . DIRECTORY_SEPARATOR . 'updateuris.php' ;
if ( ! file_exists ( $flag )) {
// If our flag-file isn't set then we know OStatus hasn't upgraded yet,
// run external script and plant the flag
define ( 'OSTATUS_UPGRADE' , true );
require_once $script ;
$file = fopen ( $flag , " w " );
if ( $file === false ) {
$this -> log ( LOG_ERR , " Failed to create the file: { $flag } \n " );
} else {
fwrite ( $file , self :: PLUGIN_VERSION . PHP_EOL );
fclose ( $file );
}
}
return true ;
}
2009-11-20 13:55:38 +09:00
}