private function webmention($params) { $request = new Request($params); $response = new Response(); $response = $this->api->webmention($request, $response); $webmention = ORM::for_table('webmentions')->where(['source' => $params['source'], 'target' => $params['target']])->find_one(); $client = new IndieWeb\MentionClientTest(); $client::$dataDir = dirname(__FILE__) . '/data/'; if (!is_object($webmention)) { throw new Exception("No webmention was queued for this test"); } $callback = Telegraph\Webmention::send($webmention->id, $client, $this->http); return [$webmention, $callback]; }
public function webmention(Request $request, Response $response) { # Require the token parameter if (!($token = $request->get('token'))) { return $this->respond($response, 401, ['error' => 'authentication_required', 'error_description' => 'A token is required to use the API']); } # Require source and target or target_domain parameters $target = $target_domain = null; if (!($source = $request->get('source')) || !($target = $request->get('target')) && !($target_domain = $request->get('target_domain'))) { return $this->respond($response, 400, ['error' => 'missing_parameters', 'error_description' => 'The source or target or target_domain parameters were missing']); } if ($target && $target_domain) { return $this->respond($response, 400, ['error' => 'invalid_parameter', 'error_description' => 'Can\'t provide both target and target_domain together']); } $urlregex = '/^https?:\\/\\/[^ ]+\\.[^ ]+$/'; $domainregex = '/^[^ ]+$/'; # Verify source, target, and callback are URLs $callback = $request->get('callback'); if (!preg_match($urlregex, $source) || !preg_match($urlregex, $target) && !preg_match($domainregex, $target_domain) || $callback && !preg_match($urlregex, $callback)) { return $this->respond($response, 400, ['error' => 'invalid_parameter', 'error_description' => 'The source, target, or callback parameters were invalid']); } # Don't send anything if the source domain matches the target domain # The problem is someone pushing to Superfeedr who is also subscribed, will cause a # request to be sent with the source of one of their posts, and their own target domain. # This causes a whole slew of webmentions to be queued up, almost all of which are not needed. if ($target_domain) { $source_domain = parse_url($source, PHP_URL_HOST); if ($target_domain == $source_domain) { # Return 200 so Superfeedr doesn't think something is broken return $this->respond($response, 200, ['error' => 'not_supported', 'error_description' => 'You cannot use the target_domain feature to send webmentions to the same domain as the source URL']); } } # Verify the token is valid $role = ORM::for_table('roles')->where('token', $token)->find_one(); if (!$role) { return $this->respond($response, 401, ['error' => 'invalid_token', 'error_description' => 'The token provided is not valid']); } # Check the blacklist of domains that are known to not accept webmentions if ($target && !Telegraph\Webmention::isProbablySupported($target)) { return $this->respond($response, 400, ['error' => 'not_supported', 'error_description' => 'The target domain is known to not accept webmentions. If you believe this is in error, please file an issue at https://github.com/aaronpk/Telegraph/issues']); } # If there is no code given, # Synchronously check the source URL and verify that it actually contains # a link to the target. This way we prevent this API from sending known invalid mentions. if ($request->get('code')) { # target URL is required if (!$target) { return $this->respond($response, 400, ['error' => 'not_supported', 'error_description' => 'The target_domain parameter is not supported for sending private webmentions']); } $found[$target] = null; } else { $sourceData = $this->http->get($source); $doc = new DOMDocument(); libxml_use_internal_errors(true); # suppress parse errors and warnings @$doc->loadHTML(self::toHtmlEntities($sourceData['body']), LIBXML_NOWARNING | LIBXML_NOERROR); libxml_clear_errors(); if (!$doc) { return $this->respond($response, 400, ['error' => 'source_not_html', 'error_description' => 'The source document could not be parsed as HTML']); } $xpath = new DOMXPath($doc); $found = []; foreach ($xpath->query('//a[@href]') as $href) { $url = $href->getAttribute('href'); if ($target) { # target parameter was provided if ($url == $target) { $found[$url] = null; } } elseif ($target_domain) { # target_domain parameter was provided $domain = parse_url($url, PHP_URL_HOST); if ($domain && ($domain == $target_domain || str_ends_with($domain, '.' . $target_domain))) { $found[$url] = null; } } } if (!$found) { return $this->respond($response, 400, ['error' => 'no_link_found', 'error_description' => 'The source document does not have a link to the target URL or domain']); } } # Write the webmention to the database and queue a job to start sending $statusURLs = []; foreach ($found as $url => $_) { $w = ORM::for_table('webmentions')->create(); $w->site_id = $role->site_id; $w->created_by = $role->user_id; $w->created_at = date('Y-m-d H:i:s'); $w->token = self::generateStatusToken(); $w->source = $source; $w->target = $url; $w->vouch = $request->get('vouch'); $w->code = $request->get('code'); $w->realm = $request->get('realm'); $w->callback = $callback; $w->save(); q()->queue('Telegraph\\Webmention', 'send', [$w->id]); $statusURLs[] = Config::$base . 'webmention/' . $w->token; } if ($target) { $body = ['status' => 'queued', 'location' => $statusURLs[0]]; $headers = ['Location' => $statusURLs[0]]; } else { $body = ['status' => 'queued', 'location' => $statusURLs]; $headers = []; } return $this->respond($response, 201, $body, $headers); }
public function discover_endpoint(Request $request, Response $response) { if (!$this->_is_logged_in($request, $response)) { return $response; } $targetURL = $request->get('target'); if (!Telegraph\Webmention::isProbablySupported($targetURL)) { $status = 'none'; $cached = -1; } else { // Cache the discovered result $cacheKey = 'telegraph:discover_endpoint:' . $targetURL; if ($request->get('ignore_cache') == 'true' || !($status = redis()->get($cacheKey))) { $client = new IndieWeb\MentionClient(); $endpoint = $client->discoverWebmentionEndpoint($targetURL); if ($endpoint) { $status = 'webmention'; } else { $endpoint = $client->discoverPingbackEndpoint($targetURL); if ($endpoint) { $status = 'pingback'; } else { $status = 'none'; } } $cached = false; redis()->setex($cacheKey, 600, $status); } else { $cached = true; } } $response->headers->set('Content-Type', 'application/json'); $response->setContent(json_encode(['status' => $status, 'cached' => $cached])); return $response; }