| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  | <?php | 
					
						
							| 
									
										
										
										
											2018-07-18 05:31:24 +01:00
										 |  |  | // This file is part of GNU social - https://www.gnu.org/software/social
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // GNU social is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // GNU social is distributed in the hope that it will be useful,
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with GNU social.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * OembedPlugin implementation for GNU social | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2018-07-18 05:31:24 +01:00
										 |  |  |  * @package   GNUsocial | 
					
						
							|  |  |  |  * @author    Mikael Nordfeldth | 
					
						
							|  |  |  |  * @author    hannes | 
					
						
							|  |  |  |  * @author    Diogo Cordeiro <diogo@fc.up.pt> | 
					
						
							|  |  |  |  * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org | 
					
						
							|  |  |  |  * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-18 05:31:24 +01:00
										 |  |  | defined('GNUSOCIAL') || die(); | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2019-07-06 17:26:15 +01:00
										 |  |  |  * Utility class to wrap basic embed lookups. | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Blacklisted hosts will use an alternate lookup method: | 
					
						
							|  |  |  |  *  - Twitpic | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2019-07-06 17:26:15 +01:00
										 |  |  |  * Whitelisted hosts will use known embed API endpoints: | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  |  *  - Flickr, YFrog | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Sites that provide discovery links will use them directly; a bug | 
					
						
							|  |  |  |  * in use of discovery links with query strings is worked around. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Others will fall back to oohembed (unless disabled). | 
					
						
							|  |  |  |  * The API endpoint can be configured or disabled through config | 
					
						
							|  |  |  |  * as 'oohembed'/'endpoint'. | 
					
						
							| 
									
										
										
										
											2018-07-18 05:31:24 +01:00
										 |  |  |  * | 
					
						
							|  |  |  |  * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org | 
					
						
							|  |  |  |  * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-07-06 17:26:15 +01:00
										 |  |  | class EmbedHelper | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Perform or fake an oEmbed lookup for the given resource. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Some known hosts are whitelisted with API endpoints where we | 
					
						
							|  |  |  |      * know they exist but autodiscovery data isn't available. | 
					
						
							|  |  |  |      * If autodiscovery links are missing and we don't recognize the | 
					
						
							| 
									
										
										
										
											2011-09-30 19:51:23 +00:00
										 |  |  |      * host, we'll pass it to noembed.com's public service which | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  |      * will either proxy or fake info on a lot of sites. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * A few hosts are blacklisted due to known problems with oohembed, | 
					
						
							|  |  |  |      * in which case we'll look up the info another way and return | 
					
						
							|  |  |  |      * equivalent data. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Throws exceptions on failure. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param string $url | 
					
						
							|  |  |  |      * @return object | 
					
						
							| 
									
										
										
										
											2019-07-12 03:13:40 +01:00
										 |  |  |      * @throws EmbedHelper_BadHtmlException | 
					
						
							|  |  |  |      * @throws HTTP_Request2_Exception | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-07-12 03:13:40 +01:00
										 |  |  |     public static function getObject($url) | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2015-11-30 02:06:04 +01:00
										 |  |  |         common_log(LOG_INFO, 'Checking for remote URL metadata for ' . $url); | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-30 02:06:04 +01:00
										 |  |  |         // TODO: Make this class something like UrlMetadata, or use a dataobject?
 | 
					
						
							|  |  |  |         $metadata = new stdClass(); | 
					
						
							| 
									
										
										
										
											2011-04-06 17:57:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-30 02:06:04 +01:00
										 |  |  |         if (Event::handle('GetRemoteUrlMetadata', array($url, &$metadata))) { | 
					
						
							|  |  |  |             // If that event didn't return anything, try downloading the body and parse it
 | 
					
						
							| 
									
										
										
										
											2016-01-26 13:37:52 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             // don't use quickGet since we want to check Content-Type header for utf-8
 | 
					
						
							|  |  |  |             $client = new HTTPClient(); | 
					
						
							|  |  |  |             $response = $client->get($url); | 
					
						
							|  |  |  |             if (!$response->isOk()) { | 
					
						
							|  |  |  |                 // TRANS: Exception. %s is the URL we tried to GET.
 | 
					
						
							|  |  |  |                 throw new Exception(sprintf(_m('Could not GET URL %s.'), $url), $response->getStatus()); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             $body = $response->getBody(); | 
					
						
							| 
									
										
										
										
											2011-04-06 17:57:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-30 02:06:04 +01:00
										 |  |  |             // DOMDocument::loadHTML may throw warnings on unrecognized elements,
 | 
					
						
							|  |  |  |             // and notices on unrecognized namespaces.
 | 
					
						
							|  |  |  |             $old = error_reporting(error_reporting() & ~(E_WARNING | E_NOTICE)); | 
					
						
							| 
									
										
										
										
											2018-07-18 05:31:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 13:37:52 +00:00
										 |  |  |             // DOMDocument assumes ISO-8859-1 per HTML spec
 | 
					
						
							|  |  |  |             // use UTF-8 if we find any evidence of that encoding
 | 
					
						
							|  |  |  |             $utf8_evidence = false; | 
					
						
							|  |  |  |             $unicode_check_dom = new DOMDocument(); | 
					
						
							|  |  |  |             $ok = $unicode_check_dom->loadHTML($body); | 
					
						
							| 
									
										
										
										
											2018-07-18 05:31:24 +01:00
										 |  |  |             if (!$ok) { | 
					
						
							| 
									
										
										
										
											2019-07-07 13:26:10 +01:00
										 |  |  |                 throw new EmbedHelper_BadHtmlException(); | 
					
						
							| 
									
										
										
										
											2018-07-18 05:31:24 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2016-01-26 13:37:52 +00:00
										 |  |  |             $metaNodes = $unicode_check_dom->getElementsByTagName('meta'); | 
					
						
							| 
									
										
										
										
											2018-07-18 05:31:24 +01:00
										 |  |  |             foreach ($metaNodes as $metaNode) { | 
					
						
							| 
									
										
										
										
											2016-01-26 13:37:52 +00:00
										 |  |  |                 // case in-sensitive since Content-type and utf-8 can be written in many ways
 | 
					
						
							| 
									
										
										
										
											2018-07-18 05:31:24 +01:00
										 |  |  |                 if (stristr($metaNode->getAttribute('http-equiv'), 'content-type') | 
					
						
							|  |  |  |                 && stristr($metaNode->getAttribute('content'), 'utf-8')) { | 
					
						
							|  |  |  |                     $utf8_evidence = true; | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 } elseif (stristr($metaNode->getAttribute('charset'), 'utf-8')) { | 
					
						
							|  |  |  |                     $utf8_evidence = true; | 
					
						
							| 
									
										
										
										
											2016-01-26 13:37:52 +00:00
										 |  |  |                     break; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             unset($unicode_check_dom); | 
					
						
							| 
									
										
										
										
											2018-07-18 05:31:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 13:37:52 +00:00
										 |  |  |             // The Content-Type HTTP response header overrides encoding metatags in DOM
 | 
					
						
							| 
									
										
										
										
											2018-07-18 05:31:24 +01:00
										 |  |  |             if (stristr($response->getHeader('Content-Type'), 'utf-8')) { | 
					
						
							|  |  |  |                 $utf8_evidence = true; | 
					
						
							| 
									
										
										
										
											2016-01-26 13:37:52 +00:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2018-07-18 05:31:24 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             // add utf-8 encoding prolog if we have reason to believe this is utf-8 content
 | 
					
						
							|  |  |  |             // DOMDocument('1.0', 'UTF-8') does not work!
 | 
					
						
							|  |  |  |             $utf8_tag = $utf8_evidence ? '<?xml encoding="utf-8" ?>' : ''; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-30 02:06:04 +01:00
										 |  |  |             $dom = new DOMDocument(); | 
					
						
							| 
									
										
										
										
											2016-01-26 13:37:52 +00:00
										 |  |  |             $ok = $dom->loadHTML($utf8_tag.$body); | 
					
						
							| 
									
										
										
										
											2015-11-30 02:06:04 +01:00
										 |  |  |             unset($body);   // storing the DOM in memory is enough...
 | 
					
						
							|  |  |  |             error_reporting($old); | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-30 02:06:04 +01:00
										 |  |  |             if (!$ok) { | 
					
						
							| 
									
										
										
										
											2019-07-07 13:26:10 +01:00
										 |  |  |                 throw new EmbedHelper_BadHtmlException(); | 
					
						
							| 
									
										
										
										
											2010-11-15 11:25:38 -08:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2018-07-18 05:31:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-30 02:06:04 +01:00
										 |  |  |             Event::handle('GetRemoteUrlMetadataFromDom', array($url, $dom, &$metadata)); | 
					
						
							| 
									
										
										
										
											2010-11-15 11:25:38 -08:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-30 02:06:04 +01:00
										 |  |  |         return self::normalize($metadata); | 
					
						
							| 
									
										
										
										
											2015-11-30 01:28:18 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-15 11:25:38 -08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Normalize oEmbed format. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2019-07-12 03:13:40 +01:00
										 |  |  |      * @param stdClass $data | 
					
						
							| 
									
										
										
										
											2010-11-15 11:25:38 -08:00
										 |  |  |      * @return object | 
					
						
							| 
									
										
										
										
											2019-07-12 03:13:40 +01:00
										 |  |  |      * @throws Exception | 
					
						
							| 
									
										
										
										
											2010-11-15 11:25:38 -08:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2018-07-18 05:31:24 +01:00
										 |  |  |     public static function normalize(stdClass $data) | 
					
						
							| 
									
										
										
										
											2010-11-15 11:25:38 -08:00
										 |  |  |     { | 
					
						
							|  |  |  |         if (empty($data->type)) { | 
					
						
							|  |  |  |             throw new Exception('Invalid oEmbed data: no type field.'); | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2010-11-15 11:25:38 -08:00
										 |  |  |         if ($data->type == 'image') { | 
					
						
							|  |  |  |             // YFrog does this.
 | 
					
						
							|  |  |  |             $data->type = 'photo'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (isset($data->thumbnail_url)) { | 
					
						
							|  |  |  |             if (!isset($data->thumbnail_width)) { | 
					
						
							|  |  |  |                 // !?!?!
 | 
					
						
							| 
									
										
										
										
											2014-04-21 20:39:28 +02:00
										 |  |  |                 $data->thumbnail_width = common_config('thumbnail', 'width'); | 
					
						
							|  |  |  |                 $data->thumbnail_height = common_config('thumbnail', 'height'); | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-15 11:25:38 -08:00
										 |  |  |         return $data; | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-07 13:26:10 +01:00
										 |  |  | class EmbedHelper_Exception extends Exception | 
					
						
							| 
									
										
										
										
											2010-11-15 11:25:38 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2010-12-20 18:28:21 -05:00
										 |  |  |     public function __construct($message = "", $code = 0, $previous = null) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-07-12 03:13:40 +01:00
										 |  |  |         parent::__construct($message, $code, $previous); | 
					
						
							| 
									
										
										
										
											2010-12-20 18:28:21 -05:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2010-11-15 11:25:38 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-07 13:26:10 +01:00
										 |  |  | class EmbedHelper_BadHtmlException extends EmbedHelper_Exception | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-07-18 05:31:24 +01:00
										 |  |  |     public function __construct($previous=null) | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2010-11-15 11:25:38 -08:00
										 |  |  |         return parent::__construct('Bad HTML in discovery data.', 0, $previous); | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2010-11-15 11:25:38 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-07 13:26:10 +01:00
										 |  |  | class EmbedHelper_DiscoveryException extends EmbedHelper_Exception | 
					
						
							| 
									
										
										
										
											2010-11-15 11:25:38 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-07-18 05:31:24 +01:00
										 |  |  |     public function __construct($previous=null) | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2010-11-15 11:25:38 -08:00
										 |  |  |         return parent::__construct('No oEmbed discovery data.', 0, $previous); | 
					
						
							| 
									
										
										
										
											2010-11-12 17:41:35 -08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-01-28 18:57:36 +01:00
										 |  |  | } |