diff --git a/classes/Activitypub_profile.php b/classes/Activitypub_profile.php index da4831c..6cdcc32 100755 --- a/classes/Activitypub_profile.php +++ b/classes/Activitypub_profile.php @@ -413,4 +413,57 @@ class Activitypub_profile extends Managed_DataObject // TRANS: Exception. %s is a webfinger address. throw new Exception(sprintf(_m('Could not find a valid profile for "%s".'), $addr)); } + + /** + * Update remote user profile in local instance + * Depends on do_update + * + * @author Diogo Cordeiro + * @param array $res remote response + * @return Profile remote Profile object + */ + public static function update_profile($aprofile, $res) + { + // ActivityPub Profile + $aprofile->uri = $res['id']; + $aprofile->nickname = $res['preferredUsername']; + $aprofile->fullname = isset($res['name']) ? $res['name'] : null; + $aprofile->bio = isset($res['summary']) ? substr(strip_tags($res['summary']), 0, 1000) : null; + $aprofile->inboxuri = $res['inbox']; + $aprofile->sharedInboxuri = isset($res['endpoints']['sharedInbox']) ? $res['endpoints']['sharedInbox'] : $res['inbox']; + + $profile = $aprofile->local_profile(); + + $profile->modified = $aprofile->modified = common_sql_now(); + + $fields = [ + 'uri' => 'profileurl', + 'nickname' => 'nickname', + 'fullname' => 'fullname', + 'bio' => 'bio' + ]; + + foreach ($fields as $af => $pf) { + $profile->$pf = $aprofile->$af; + } + + // Profile + $profile->update(); + $aprofile->update(); + + // Public Key + Activitypub_rsa::update_public_key($profile, $res['publicKey']['publicKeyPem']); + + // Avatar + if (isset($res['icon']['url'])) { + try { + Activitypub_explorer::update_avatar($profile, $res['icon']['url']); + } catch (Exception $e) { + // Let the exception go, it isn't a serious issue + common_debug('An error ocurred while grabbing remote avatar'.$e->getMessage()); + } + } + + return $profile; + } } diff --git a/classes/Activitypub_rsa.php b/classes/Activitypub_rsa.php index 9c6893e..817ace7 100755 --- a/classes/Activitypub_rsa.php +++ b/classes/Activitypub_rsa.php @@ -83,7 +83,6 @@ class Activitypub_rsa extends Managed_DataObject return $apRSA->private_key; } - /** * Guarantees a Public Key for a given profile. * @@ -104,7 +103,8 @@ class Activitypub_rsa extends Managed_DataObject } else { // This should never happen, but try to recover! if ($fetch) { - // TODO: Call profile updater + $res = Activitypub_explorer::get_remote_user_activity(ActivityPubPlugin::actor_uri($profile)); + Activitypub_rsa::update_public_key($profile, $res['publicKey']['publicKeyPem']); return ensure_public_key($profile, false); } else { throw new ServerException('Activitypub_rsa: Failed to find keys for given profile. That should have not happened!'); @@ -156,4 +156,23 @@ class Activitypub_rsa extends Managed_DataObject $public_key = $pubKey["key"]; unset($pubKey); } + + /** + * Update public key. + * + * @author Diogo Cordeiro + * @param Profile $profile + * @param string $public_key + */ + public static function update_public_key($profile, $public_key) + { + // Public Key + $apRSA = new Activitypub_rsa(); + $apRSA->profile_id = $profile->getID(); + $apRSA->public_key = $public_key; + $apRSA->modified = common_sql_now(); + if(!$apRSA->update()) { + $apRSA->insert(); + } + } } diff --git a/daemons/profile_updater.php b/daemons/profile_updater.php deleted file mode 100755 index c0e9bbe..0000000 --- a/daemons/profile_updater.php +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env php -. - * - * @category Plugin - * @package GNUsocial - * @author Diogo Cordeiro - * @copyright 2018 Free Software Foundation http://fsf.org - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link https://www.gnu.org/software/social/ - */ - -define('INSTALLDIR', realpath(__DIR__ . '/../../..')); - -$shortoptions = 'u:af'; -$longoptions = ['uri=', 'all', 'force']; - -$helptext = <<find(); -if (!empty($cnt)) { - if (!$quiet) { - echo "Found {$cnt} ActivityPub profiles:\n"; - } -} else { - if (have_option('u', 'uri')) { - if (!$quiet) { - echo "Couldn't find an existing ActivityPub profile with that URI.\n"; - } - } else { - if (!$quiet) { - echo "Couldn't find any existing ActivityPub profiles.\n"; - } - } - exit(0); -} -while ($user->fetch()) { - try { - $res = grab_remote_user($user->uri); - } catch (Exception $e) { - // let it go - } - if (!$quiet) { - echo "Updated ".update_profile($user, $res)->getBestName()."\n"; - } -} - -/** - * Update Profile and ActivityPub Profile objects - * - * @author Diogo Cordeiro - * @access public - */ -function do_update($aprofile) -{ - $profile = $aprofile->local_profile(); - - $profile->modified = $aprofile->modified = common_sql_now(); - - $fields = [ - 'uri' => 'profileurl', - 'nickname' => 'nickname', - 'fullname' => 'fullname', - 'bio' => 'bio' - ]; - - foreach ($fields as $af => $pf) { - $profile->$pf = $aprofile->$af; - } - - $profile->update(); - $aprofile->update(); -} - -/** - * Save remote user profile in local instance - * - * @author Diogo Cordeiro - * @param array $res remote response - * @return Profile remote Profile object - */ -function update_profile($aprofile, $res) -{ - // ActivityPub Profile - $aprofile->uri = $res['id']; - $aprofile->nickname = $res['preferredUsername']; - $aprofile->fullname = isset($res['name']) ? $res['name'] : null; - $aprofile->bio = isset($res['summary']) ? substr(strip_tags($res['summary']), 0, 1000) : null; - $aprofile->inboxuri = $res['inbox']; - $aprofile->sharedInboxuri = isset($res['endpoints']['sharedInbox']) ? $res['endpoints']['sharedInbox'] : $res['inbox']; - - do_update($aprofile); - $profile = $aprofile->local_profile(); - - // Public Key - $apRSA = new Activitypub_rsa(); - $apRSA->profile_id = $profile->getID(); - $apRSA->public_key = $res['publicKey']['publicKeyPem']; - $apRSA->modified = common_sql_now(); - if(!$apRSA->update()) - $apRSA->insert(); - - // Avatar - if (isset($res['icon']['url'])) { - try { - Activitypub_explorer::update_avatar($profile, $res['icon']['url']); - } catch (Exception $e) { - // Let the exception go, it isn't a serious issue - common_debug('An error ocurred while grabbing remote avatar'.$e->getMessage()); - } - } - - return $profile; -} - -/** - * Get a remote user(s) profile(s) from its URL - * - * @author Diogo Cordeiro - * @param string $url User's url - * @return boolean success state - */ -function grab_remote_user($url) -{ - $client = new HTTPClient(); - $headers = []; - $headers[] = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"'; - $headers[] = 'User-Agent: GNUSocialBot v0.1 - https://gnu.io/social'; - $response = $client->get($url, $headers); - $res = json_decode($response->getBody(), true); - if (Activitypub_explorer::validate_remote_response($res)) { - common_debug('ActivityPub Explorer: Found a valid remote actor for '.$url); - return $res; - } - throw new Exception('Failed to grab.'); -} diff --git a/daemons/update_activitypub_profiles.php b/daemons/update_activitypub_profiles.php new file mode 100755 index 0000000..c41c83b --- /dev/null +++ b/daemons/update_activitypub_profiles.php @@ -0,0 +1,104 @@ +#!/usr/bin/env php +. + * + * @category Plugin + * @package GNUsocial + * @author Diogo Cordeiro + * @copyright 2018 Free Software Foundation http://fsf.org + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link https://www.gnu.org/software/social/ + */ + +define('INSTALLDIR', realpath(__DIR__ . '/../../..')); + +$shortoptions = 'u:af'; +$longoptions = ['uri=', 'all', 'force']; + +$helptext = <<lookup($uri); + if (empty($discovery)) { + echo "Bad URI\n"; + exit(1); + } + $user = $discovery->lookup($uri)[0]; + try { + $res = Activitypub_explorer::get_remote_user_activity($uri); + } catch (Exception $e) { + echo $e->getMessage()."\n"; + exit(1); + } + if (!$quiet) { + echo "Updated ".Activitypub_profile::update_profile($user, $res)->getBestName()."\n"; + } +} else if (!have_option('a', 'all')) { + show_help(); + exit(1); +} + +$user = new Activitypub_profile(); +$cnt = $user->find(); +if (!empty($cnt)) { + if (!$quiet) { + echo "Found {$cnt} ActivityPub profiles:\n"; + } +} else { + if (have_option('u', 'uri')) { + if (!$quiet) { + echo "Couldn't find an existing ActivityPub profile with that URI.\n"; + } + } else { + if (!$quiet) { + echo "Couldn't find any existing ActivityPub profiles.\n"; + } + } + exit(0); +} +while ($user->fetch()) { + try { + $res = Activitypub_explorer::get_remote_user_activity($user->uri); + if (!$quiet) { + echo "Updated ".Activitypub_profile::update_profile($user, $res)->getBestName()."\n"; + } + } catch (Exception $e) { + // let it go + } +} diff --git a/utils/explorer.php b/utils/explorer.php index 234094a..dc8d600 100755 --- a/utils/explorer.php +++ b/utils/explorer.php @@ -438,4 +438,27 @@ class Activitypub_explorer return true; } + + /** + * Get a remote user array from its URL (this function is only used for + * profile updating and shall not be used for anything else) + * + * @author Diogo Cordeiro + * @param string $url User's url + * @throws Exception + */ + public static function get_remote_user_activity($url) + { + $client = new HTTPClient(); + $headers = []; + $headers[] = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"'; + $headers[] = 'User-Agent: GNUSocialBot v0.1 - https://gnu.io/social'; + $response = $client->get($url, $headers); + $res = json_decode($response->getBody(), true); + if (Activitypub_explorer::validate_remote_response($res)) { + common_debug('ActivityPub Explorer: Found a valid remote actor for '.$url); + return $res; + } + throw new Exception('ActivityPub Explorer: Failed to get activity.'); + } }