Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

199 lines
5.0 KiB

  1. <?php
  2. namespace Friendica\Module;
  3. use Friendica\BaseModule;
  4. use Friendica\Core\Hook;
  5. use Friendica\Core\Renderer;
  6. use Friendica\Database\DBA;
  7. use Friendica\Model\Photo;
  8. use Friendica\Model\User;
  9. use Friendica\Protocol\Activity\ANamespace;
  10. use Friendica\Protocol\Salmon;
  11. use Friendica\Util\Strings;
  12. /**
  13. * Prints responses to /.well-known/webfinger or /xrd requests
  14. */
  15. class Xrd extends BaseModule
  16. {
  17. public static function rawContent()
  18. {
  19. $app = self::getApp();
  20. // @TODO: Replace with parameter from router
  21. if ($app->argv[0] == 'xrd') {
  22. if (empty($_GET['uri'])) {
  23. return;
  24. }
  25. $uri = urldecode(Strings::escapeTags(trim($_GET['uri'])));
  26. if (($_SERVER['HTTP_ACCEPT'] ?? '') == 'application/jrd+json') {
  27. $mode = 'json';
  28. } else {
  29. $mode = 'xml';
  30. }
  31. } else {
  32. if (empty($_GET['resource'])) {
  33. return;
  34. }
  35. $uri = urldecode(Strings::escapeTags(trim($_GET['resource'])));
  36. if (($_SERVER['HTTP_ACCEPT'] ?? '') == 'application/xrd+xml') {
  37. $mode = 'xml';
  38. } else {
  39. $mode = 'json';
  40. }
  41. }
  42. if (substr($uri, 0, 4) === 'http') {
  43. $name = ltrim(basename($uri), '~');
  44. } else {
  45. $local = str_replace('acct:', '', $uri);
  46. if (substr($local, 0, 2) == '//') {
  47. $local = substr($local, 2);
  48. }
  49. $name = substr($local, 0, strpos($local, '@'));
  50. }
  51. $user = User::getByNickname($name);
  52. if (empty($user)) {
  53. throw new \Friendica\Network\HTTPException\NotFoundException();
  54. }
  55. $owner = User::getOwnerDataById($user['uid']);
  56. $alias = str_replace('/profile/', '/~', $owner['url']);
  57. $avatar = Photo::selectFirst(['type'], ['uid' => $owner['uid'], 'profile' => true]);
  58. if (!DBA::isResult($avatar)) {
  59. $avatar = ['type' => 'image/jpeg'];
  60. }
  61. if ($mode == 'xml') {
  62. self::printXML($alias, $app->getBaseURL(), $user, $owner, $avatar);
  63. } else {
  64. self::printJSON($alias, $app->getBaseURL(), $owner, $avatar);
  65. }
  66. }
  67. private static function printJSON($alias, $baseURL, $owner, $avatar)
  68. {
  69. $salmon_key = Salmon::salmonKey($owner['spubkey']);
  70. header('Access-Control-Allow-Origin: *');
  71. header('Content-type: application/json; charset=utf-8');
  72. $json = [
  73. 'subject' => 'acct:' . $owner['addr'],
  74. 'aliases' => [
  75. $alias,
  76. $owner['url'],
  77. ],
  78. 'links' => [
  79. [
  80. 'rel' => ANamespace::DFRN ,
  81. 'href' => $owner['url'],
  82. ],
  83. [
  84. 'rel' => ANamespace::FEED,
  85. 'type' => 'application/atom+xml',
  86. 'href' => $owner['poll'],
  87. ],
  88. [
  89. 'rel' => 'http://webfinger.net/rel/profile-page',
  90. 'type' => 'text/html',
  91. 'href' => $owner['url'],
  92. ],
  93. [
  94. 'rel' => 'self',
  95. 'type' => 'application/activity+json',
  96. 'href' => $owner['url'],
  97. ],
  98. [
  99. 'rel' => 'http://microformats.org/profile/hcard',
  100. 'type' => 'text/html',
  101. 'href' => $baseURL . '/hcard/' . $owner['nickname'],
  102. ],
  103. [
  104. 'rel' => ANamespace::POCO,
  105. 'href' => $owner['poco'],
  106. ],
  107. [
  108. 'rel' => 'http://webfinger.net/rel/avatar',
  109. 'type' => $avatar['type'],
  110. 'href' => $owner['photo'],
  111. ],
  112. [
  113. 'rel' => 'http://joindiaspora.com/seed_location',
  114. 'type' => 'text/html',
  115. 'href' => $baseURL,
  116. ],
  117. [
  118. 'rel' => 'salmon',
  119. 'href' => $baseURL . '/salmon/' . $owner['nickname'],
  120. ],
  121. [
  122. 'rel' => 'http://salmon-protocol.org/ns/salmon-replies',
  123. 'href' => $baseURL . '/salmon/' . $owner['nickname'],
  124. ],
  125. [
  126. 'rel' => 'http://salmon-protocol.org/ns/salmon-mention',
  127. 'href' => $baseURL . '/salmon/' . $owner['nickname'] . '/mention',
  128. ],
  129. [
  130. 'rel' => 'http://ostatus.org/schema/1.0/subscribe',
  131. 'template' => $baseURL . '/follow?url={uri}',
  132. ],
  133. [
  134. 'rel' => 'magic-public-key',
  135. 'href' => 'data:application/magic-public-key,' . $salmon_key,
  136. ],
  137. [
  138. 'rel' => 'http://purl.org/openwebauth/v1',
  139. 'type' => 'application/x-zot+json',
  140. 'href' => $baseURL . '/owa',
  141. ],
  142. ],
  143. ];
  144. echo json_encode($json);
  145. exit();
  146. }
  147. private static function printXML($alias, $baseURL, $user, $owner, $avatar)
  148. {
  149. $salmon_key = Salmon::salmonKey($owner['spubkey']);
  150. header('Access-Control-Allow-Origin: *');
  151. header('Content-type: text/xml');
  152. $tpl = Renderer::getMarkupTemplate('xrd_person.tpl');
  153. $o = Renderer::replaceMacros($tpl, [
  154. '$nick' => $owner['nickname'],
  155. '$accturi' => 'acct:' . $owner['addr'],
  156. '$alias' => $alias,
  157. '$profile_url' => $owner['url'],
  158. '$hcard_url' => $baseURL . '/hcard/' . $owner['nickname'],
  159. '$atom' => $owner['poll'],
  160. '$poco_url' => $owner['poco'],
  161. '$photo' => $owner['photo'],
  162. '$type' => $avatar['type'],
  163. '$salmon' => $baseURL . '/salmon/' . $owner['nickname'],
  164. '$salmen' => $baseURL . '/salmon/' . $owner['nickname'] . '/mention',
  165. '$subscribe' => $baseURL . '/follow?url={uri}',
  166. '$openwebauth' => $baseURL . '/owa',
  167. '$modexp' => 'data:application/magic-public-key,' . $salmon_key
  168. ]);
  169. $arr = ['user' => $user, 'xml' => $o];
  170. Hook::callAll('personal_xrd', $arr);
  171. echo $arr['xml'];
  172. exit();
  173. }
  174. }