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.

166 lines
5.0 KiB

  1. <?php
  2. namespace Telegraph;
  3. use ORM, Exception;
  4. use IndieWeb\MentionClient;
  5. class Webmention {
  6. private static $http = false;
  7. private static $unsupported = null;
  8. // Returns false if the target URL is known to not accept webmentions
  9. public static function isProbablySupported($targetURL) {
  10. // Reject links that are known to not accept webmentions
  11. $host = str_replace('www.','',parse_url($targetURL, PHP_URL_HOST));
  12. if(!$host) return false;
  13. if(self::$unsupported === null) {
  14. self::$unsupported = explode("\n", file_get_contents(__DIR__.'/../../data/unsupported-domains.txt'));
  15. }
  16. if(in_array($host, self::$unsupported))
  17. return false;
  18. if(preg_match('/.+\.amazonaws\.com/', $host))
  19. return false;
  20. return true;
  21. }
  22. private static function updateStatus($webmention, $http_code, $code, $raw=null) {
  23. $status = ORM::for_table('webmention_status')->create();
  24. $status->webmention_id = $webmention->id;
  25. $status->created_at = date('Y-m-d H:i:s');
  26. if($http_code)
  27. $status->http_code = $http_code;
  28. $status->status = $code;
  29. if($raw)
  30. $status->raw_response = $raw;
  31. $status->save();
  32. // Post to the callback URL if one is set
  33. if($webmention->callback) {
  34. $payload = [
  35. 'source' => $webmention->source,
  36. 'target' => $webmention->target,
  37. 'status' => $code,
  38. ];
  39. if($webmention->webmention_endpoint) {
  40. $payload['type'] = 'webmention';
  41. } elseif($webmention->pingback_endpoint) {
  42. $payload['type'] = 'pingback';
  43. }
  44. if($status->http_code) {
  45. $payload['http_code'] = $status->http_code;
  46. }
  47. if($raw) {
  48. $payload['http_body'] = $raw;
  49. }
  50. echo "Sending result to callback URL: $webmention->callback\n";
  51. return self::$http->post($webmention->callback, $payload);
  52. }
  53. }
  54. public static function send($id, $client=false, $http=false) {
  55. initdb();
  56. $webmention = ORM::for_table('webmentions')->where('id', $id)->find_one();
  57. if(!$webmention) {
  58. echo 'Webmention '.$id.' was not found'."\n";
  59. return;
  60. }
  61. echo "Processing webmention $id from ".$webmention->source."\n";
  62. if(!$client)
  63. $client = new MentionClient();
  64. if(!$http)
  65. $http = new HTTP();
  66. self::$http = $http;
  67. echo "Finding webmention endpoint for $webmention->target\n";
  68. // Discover the webmention or pingback endpoint
  69. $endpoint = $client->discoverWebmentionEndpoint($webmention->target);
  70. if(!$endpoint) {
  71. echo "No webmention endpoint, checking for pingback\n";
  72. // If no webmention endpoint found, try to send a pingback
  73. $pingbackEndpoint = $client->discoverPingbackEndpoint($webmention->target);
  74. // If no pingback endpoint was found, we can't do anything else
  75. if(!$pingbackEndpoint) {
  76. echo "No webmention or pingback endpoint found\n";
  77. return self::updateStatus($webmention, null, 'not_supported');
  78. }
  79. echo "Found pingback endpoint $pingbackEndpoint\n";
  80. $webmention->pingback_endpoint = $pingbackEndpoint;
  81. $webmention->save();
  82. echo "Sending pingback now...\n";
  83. $success = $client->sendPingbackToEndpoint($pingbackEndpoint, $webmention->source, $webmention->target);
  84. echo "Result: ".($success?'accepted':'error')."\n";
  85. return self::updateStatus($webmention, null, ($success ? 'accepted' : 'error'));
  86. }
  87. echo "Found webmention endpoint $endpoint\n";
  88. // There is a webmention endpoint, send the webmention now
  89. $webmention->webmention_endpoint = $endpoint;
  90. $webmention->save();
  91. $params = [];
  92. if($webmention->code) {
  93. $params['code'] = $webmention->code;
  94. if($webmention->realm)
  95. $params['realm'] = $webmention->realm;
  96. }
  97. if($webmention->vouch) {
  98. $params['vouch'] = $webmention->vouch;
  99. }
  100. echo "Sending webmention now...\n";
  101. $response = $client->sendWebmentionToEndpoint($endpoint, $webmention->source, $webmention->target, $params);
  102. echo "Response code: ".$response['code']."\n";
  103. if(in_array($response['code'], [200,201,202])) {
  104. $status = 'accepted';
  105. $webmention->complete = $response['code'] == 200 ? 1 : 0;
  106. // Check if the endpoint returned a status URL
  107. if(array_key_exists('Location', $response['headers'])) {
  108. $webmention->webmention_status_url = \Mf2\resolveUrl($endpoint, $response['headers']['Location']);
  109. echo "Endpoint returned a status URL: ".$webmention->webmention_status_url."\n";
  110. // TODO: queue a job to poll the endpoint for updates and deliver to the callback URL
  111. } else {
  112. // No status URL was returned, so we can't follow up with this later. Mark as complete.
  113. $webmention->complete = 1;
  114. }
  115. } else {
  116. $webmention->complete = 1;
  117. $status = 'error';
  118. }
  119. $webmention->save();
  120. $result = self::updateStatus($webmention, $response['code'], $status, $response['body']);
  121. echo "Done\n";
  122. $pdo = ORM::get_db();
  123. $pdo = null;
  124. return $result;
  125. }
  126. }