PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 All 508 releases
← All changes | _inc/lib/class-jetpack-podcast-feed-locator.php +124 -0 16.2-beta → 16.3 View file →
@@ -1,0 +1,124 @@
1 +<?php
2 +/**
3 + * Extension of the SimplePie\Locator class, to detect podcast feeds
4 + *
5 + * @package automattic/jetpack
6 + */
7 +
8 +if ( ! defined( 'ABSPATH' ) ) {
9 + exit( 0 );
10 +}
11 +
12 +/**
13 + * Class Jetpack_Podcast_Feed_Locator
14 + */
15 +class Jetpack_Podcast_Feed_Locator extends SimplePie\Locator {
16 +
17 + /**
18 + * Overrides the locator is_feed function to check for
19 + * appropriate podcast elements.
20 + *
21 + * @param SimplePie\HTTP\Response $file The file being checked.
22 + * @param boolean $check_html Adds text/html to the mimetypes checked.
23 + */
24 + public function is_feed( $file, $check_html = false ) {
25 + return parent::is_feed( $file, $check_html ) &&
26 + $file instanceof SimplePie\File &&
27 + $this->is_podcast_feed( $file );
28 + }
29 +
30 + /**
31 + * Checks the contents of the file for elements that make
32 + * it a podcast feed.
33 + *
34 + * @param SimplePie\File $file The file being checked.
35 + */
36 + private function is_podcast_feed( $file ) {
37 + // If we can't read the DOM assume it's a podcast feed, we'll work
38 + // it out later.
39 + if ( ! class_exists( 'DOMDocument' ) ) {
40 + return true;
41 + }
42 +
43 + // @todo Drop is_callable check once WordPress.com Simple gets the SimplePie update that came with WordPress 6.9.
44 + if ( is_callable( array( $file, 'get_body_content' ) ) ) {
45 + $feed_dom = $this->safely_load_xml( $file->get_body_content() );
46 + } else {
47 + // @phan-suppress-next-line PhanDeprecatedProperty -- For compatibility only.
48 + $feed_dom = $this->safely_load_xml( (string) $file->body );
49 + }
50 +
51 + // Do this as either/or but prioritise the itunes namespace. It's pretty likely
52 + // that it's a podcast feed we've found if that namespace is present.
53 + return $feed_dom && $this->has_itunes_ns( $feed_dom ) && $this->has_audio_enclosures( $feed_dom );
54 + }
55 +
56 + /**
57 + * Safely loads an XML file
58 + *
59 + * @param string $xml A string of XML to load.
60 + * @return DOMDocument|false A resulting DOM document or `false` if there is an error.
61 + */
62 + private function safely_load_xml( $xml ) {
63 + if ( empty( $xml ) ) {
64 + return false;
65 + }
66 +
67 + $disable_entity_loader = PHP_VERSION_ID < 80000;
68 +
69 + if ( $disable_entity_loader ) {
70 + // This function has been deprecated in PHP 8.0 because in libxml 2.9.0, external entity loading
71 + // is disabled by default, so this function is no longer needed to protect against XXE attacks.
72 + // phpcs:ignore Generic.PHP.DeprecatedFunctions.Deprecated, PHPCompatibility.FunctionUse.RemovedFunctions.libxml_disable_entity_loaderDeprecated
73 + $loader = libxml_disable_entity_loader( true );
74 + }
75 +
76 + $errors = libxml_use_internal_errors( true );
77 +
78 + $return = new DOMDocument();
79 + if ( ! $return->loadXML( $xml ) ) {
80 + return false;
81 + }
82 +
83 + libxml_use_internal_errors( $errors );
84 +
85 + if ( $disable_entity_loader && isset( $loader ) ) {
86 + // phpcs:ignore Generic.PHP.DeprecatedFunctions.Deprecated, PHPCompatibility.FunctionUse.RemovedFunctions.libxml_disable_entity_loaderDeprecated
87 + libxml_disable_entity_loader( $loader );
88 + }
89 +
90 + return $return;
91 + }
92 +
93 + /**
94 + * Checks the RSS feed for the presence of the itunes podcast namespace.
95 + * It's pretty loose and just checks the URI for itunes.com
96 + *
97 + * @param DOMDocument $dom The XML document to check.
98 + * @return boolean Whether the itunes namespace is defined.
99 + */
100 + private function has_itunes_ns( $dom ) {
101 + $xpath = new DOMXPath( $dom );
102 + foreach ( $xpath->query( 'namespace::*' ) as $node ) {
103 + // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
104 + // nodeValue is not valid, but it's part of the DOM API that we don't control.
105 + if ( strstr( $node->nodeValue, 'itunes.com' ) ) {
106 + return true;
107 + }
108 + // phpcs:enable
109 + }
110 + return false;
111 + }
112 +
113 + /**
114 + * Checks the RSS feed for the presence of enclosures with an audio mimetype.
115 + *
116 + * @param DOMDocument $dom The XML document to check.
117 + * @return boolean Whether enclosures were found.
118 + */
119 + private function has_audio_enclosures( $dom ) {
120 + $xpath = new DOMXPath( $dom );
121 + $enclosures = $xpath->query( "//enclosure[starts-with(@type,'audio/')]" );
122 + return ! $enclosures ? false : $enclosures->length > 0;
123 + }
124 +}