FetchRemotePlugin.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. <?php
  2. /*
  3. * GNU social - a federating social network
  4. * Copyright (C) 2013-2014, Free Software Foundation, Inc.
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. /**
  20. * Uses WebFinger to implement remote notice retrieval for GNU social.
  21. *
  22. * Depends on: WebFinger plugin
  23. *
  24. * @package GNUsocial
  25. * @author Mikael Nordfeldth <mmn@hethane.se>
  26. */
  27. if (!defined('GNUSOCIAL')) { exit(1); }
  28. class FetchRemotePlugin extends Plugin
  29. {
  30. const PLUGIN_VERSION = '2.0.0';
  31. static function fetchNoticeFromUrl($url)
  32. {
  33. if (!common_valid_http_url($url)) {
  34. throw new InvalidUrlException($url);
  35. }
  36. $host = parse_url($url, PHP_URL_HOST);
  37. // TODO: try to fetch directly, either by requesting Atom or
  38. // Link headers/<head> elements with rel=alternate and compare
  39. // the remote domain name with the notice URL's.
  40. if (!$stored instanceof Notice) {
  41. common_log(LOG_INFO, 'Could not fetch remote notice from URL: '._ve($url));
  42. throw new ServerException('Could not fetch remote notice.');
  43. }
  44. return $stored;
  45. }
  46. public function onFetchRemoteNoticeWithSource($uri, Profile $source, &$stored)
  47. {
  48. if (common_valid_http_url($uri) && !Event::handle('FetchRemoteNoticeFromUrl', array($url, &$stored))) {
  49. // Woopi, we got it straight from a URL-formatted URI!
  50. return false;
  51. }
  52. // Let's assume we can only do this over HTTPS and a proper
  53. // WebFinger (RFC7033) endpoint on /.well-known/webfinger
  54. try {
  55. $source_url = parse_url($source->getUrl());
  56. } catch (InvalidUrlException $e) {
  57. return true;
  58. }
  59. if ($source_url['scheme'] !== 'https') {
  60. common_debug('Will not try to fetch remote notice from non-HTTPS capable profile source');
  61. return true;
  62. }
  63. try {
  64. $port = isset($source_url['port']) ? ":{$source_url['port']}" : '';
  65. $rfc7033 = "https://{$source_url['host']}{$port}/.well-known/webfinger";
  66. $params = ['resource' => $uri];
  67. common_debug(__METHOD__ . ": getting json data about notice from: {$rfc7033}?resource=$uri");
  68. $json = HTTPClient::quickGetJson($rfc7033, $params);
  69. } catch (Exception $e) {
  70. // NOPE NOPE NOPE NOPE
  71. // couldn't get remote data about this notice's URI
  72. // FIXME: try later?
  73. return true;
  74. }
  75. if (!isset($json->aliases)) {
  76. // FIXME: malformed json for our current use, but maybe we could find rel="alternate" type="text/html"?
  77. return true;
  78. }
  79. common_debug(__METHOD__ . ": Found these aliases: "._ve($json->aliases));
  80. foreach ($json->aliases as $alias) {
  81. try {
  82. $stored = self::fetchNoticeFromUrl($url);
  83. if ($stored instanceof Notice) {
  84. // we're done here! all good, let's get back to business
  85. return false;
  86. }
  87. } catch (InvalidUrlException $e) {
  88. /// mmmmye, aliases might not always be HTTP(S) URLs.
  89. } catch (Exception $e) {
  90. // oh well, try the next one and see if it works better.
  91. common_debug(__METHOD__ . ": {$e->getMessage()}");
  92. }
  93. }
  94. // Nothing found, return true to continue processing the event
  95. return true;
  96. }
  97. public function onPluginVersion(array &$versions): bool
  98. {
  99. $versions[] = array('name' => 'FetchRemote',
  100. 'version' => self::PLUGIN_VERSION,
  101. 'author' => 'Mikael Nordfeldth',
  102. 'homepage' => GNUSOCIAL_ENGINE_URL,
  103. // TRANS: Plugin description.
  104. 'rawdescription' => _m('Retrieves remote notices (and serves local) via WebFinger'));
  105. return true;
  106. }
  107. }