Example #1
0
 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];
 }
Example #2
0
 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);
 }
Example #3
0
 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;
 }