Issues (4967)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/wp-includes/rss.php (18 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * MagpieRSS: a simple RSS integration tool
4
 *
5
 * A compiled file for RSS syndication
6
 *
7
 * @author Kellan Elliott-McCrea <[email protected]>
8
 * @version 0.51
9
 * @license GPL
10
 *
11
 * @package External
12
 * @subpackage MagpieRSS
13
 * @deprecated 3.0.0 Use SimplePie instead.
14
 */
15
16
/**
17
 * Deprecated. Use SimplePie (class-simplepie.php) instead.
18
 */
19
_deprecated_file( basename( __FILE__ ), '3.0.0', WPINC . '/class-simplepie.php' );
20
21
/**
22
 * Fires before MagpieRSS is loaded, to optionally replace it.
23
 *
24
 * @since 2.3.0
25
 * @deprecated 3.0.0
26
 */
27
do_action( 'load_feed_engine' );
28
29
/** RSS feed constant. */
30
define('RSS', 'RSS');
31
define('ATOM', 'Atom');
32
define('MAGPIE_USER_AGENT', 'WordPress/' . $GLOBALS['wp_version']);
33
34
class MagpieRSS {
35
	var $parser;
36
	var $current_item	= array();	// item currently being parsed
37
	var $items			= array();	// collection of parsed items
38
	var $channel		= array();	// hash of channel fields
39
	var $textinput		= array();
40
	var $image			= array();
41
	var $feed_type;
42
	var $feed_version;
43
44
	// parser variables
45
	var $stack				= array(); // parser stack
46
	var $inchannel			= false;
47
	var $initem 			= false;
48
	var $incontent			= false; // if in Atom <content mode="xml"> field
49
	var $intextinput		= false;
50
	var $inimage 			= false;
51
	var $current_field		= '';
52
	var $current_namespace	= false;
53
54
	//var $ERROR = "";
55
56
	var $_CONTENT_CONSTRUCTS = array('content', 'summary', 'info', 'title', 'tagline', 'copyright');
57
58
	/**
59
	 * PHP5 constructor.
60
	 */
61
	function __construct( $source ) {
62
63
		# Check if PHP xml isn't compiled
64
		#
65
		if ( ! function_exists('xml_parser_create') ) {
66
			return trigger_error( "PHP's XML extension is not available. Please contact your hosting provider to enable PHP's XML extension." );
0 ignored issues
show
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
67
		}
68
69
		$parser = xml_parser_create();
70
71
		$this->parser = $parser;
72
73
		# pass in parser, and a reference to this object
74
		# set up handlers
75
		#
76
		xml_set_object( $this->parser, $this );
77
		xml_set_element_handler($this->parser,
78
				'feed_start_element', 'feed_end_element' );
79
80
		xml_set_character_data_handler( $this->parser, 'feed_cdata' );
81
82
		$status = xml_parse( $this->parser, $source );
83
84
		if (! $status ) {
85
			$errorcode = xml_get_error_code( $this->parser );
86
			if ( $errorcode != XML_ERROR_NONE ) {
87
				$xml_error = xml_error_string( $errorcode );
88
				$error_line = xml_get_current_line_number($this->parser);
89
				$error_col = xml_get_current_column_number($this->parser);
90
				$errormsg = "$xml_error at line $error_line, column $error_col";
91
92
				$this->error( $errormsg );
93
			}
94
		}
95
96
		xml_parser_free( $this->parser );
97
98
		$this->normalize();
99
	}
100
101
	/**
102
	 * PHP4 constructor.
103
	 */
104
	public function MagpieRSS( $source ) {
105
		self::__construct( $source );
106
	}
107
108
	function feed_start_element($p, $element, &$attrs) {
0 ignored issues
show
The parameter $p is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
109
		$el = $element = strtolower($element);
110
		$attrs = array_change_key_case($attrs, CASE_LOWER);
111
112
		// check for a namespace, and split if found
113
		$ns	= false;
114
		if ( strpos( $element, ':' ) ) {
115
			list($ns, $el) = explode( ':', $element, 2);
116
		}
117
		if ( $ns and $ns != 'rdf' ) {
118
			$this->current_namespace = $ns;
119
		}
120
121
		# if feed type isn't set, then this is first element of feed
122
		# identify feed from root element
123
		#
124
		if (!isset($this->feed_type) ) {
125
			if ( $el == 'rdf' ) {
126
				$this->feed_type = RSS;
127
				$this->feed_version = '1.0';
128
			}
129
			elseif ( $el == 'rss' ) {
130
				$this->feed_type = RSS;
131
				$this->feed_version = $attrs['version'];
132
			}
133
			elseif ( $el == 'feed' ) {
134
				$this->feed_type = ATOM;
135
				$this->feed_version = $attrs['version'];
136
				$this->inchannel = true;
137
			}
138
			return;
139
		}
140
141
		if ( $el == 'channel' )
142
		{
143
			$this->inchannel = true;
144
		}
145
		elseif ($el == 'item' or $el == 'entry' )
146
		{
147
			$this->initem = true;
148
			if ( isset($attrs['rdf:about']) ) {
149
				$this->current_item['about'] = $attrs['rdf:about'];
150
			}
151
		}
152
153
		// if we're in the default namespace of an RSS feed,
154
		//  record textinput or image fields
155 View Code Duplication
		elseif (
156
			$this->feed_type == RSS and
157
			$this->current_namespace == '' and
158
			$el == 'textinput' )
159
		{
160
			$this->intextinput = true;
161
		}
162
163 View Code Duplication
		elseif (
164
			$this->feed_type == RSS and
165
			$this->current_namespace == '' and
166
			$el == 'image' )
167
		{
168
			$this->inimage = true;
169
		}
170
171
		# handle atom content constructs
172
		elseif ( $this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) )
173
		{
174
			// avoid clashing w/ RSS mod_content
175
			if ($el == 'content' ) {
176
				$el = 'atom_content';
177
			}
178
179
			$this->incontent = $el;
180
181
		}
182
183
		// if inside an Atom content construct (e.g. content or summary) field treat tags as text
184
		elseif ($this->feed_type == ATOM and $this->incontent )
185
		{
186
			// if tags are inlined, then flatten
187
			$attrs_str = join(' ',
188
					array_map(array('MagpieRSS', 'map_attrs'),
189
					array_keys($attrs),
190
					array_values($attrs) ) );
191
192
			$this->append_content( "<$element $attrs_str>"  );
193
194
			array_unshift( $this->stack, $el );
195
		}
196
197
		// Atom support many links per containging element.
198
		// Magpie treats link elements of type rel='alternate'
199
		// as being equivalent to RSS's simple link element.
200
		//
201
		elseif ($this->feed_type == ATOM and $el == 'link' )
202
		{
203
			if ( isset($attrs['rel']) and $attrs['rel'] == 'alternate' )
204
			{
205
				$link_el = 'link';
206
			}
207
			else {
208
				$link_el = 'link_' . $attrs['rel'];
209
			}
210
211
			$this->append($link_el, $attrs['href']);
212
		}
213
		// set stack[0] to current element
214
		else {
215
			array_unshift($this->stack, $el);
216
		}
217
	}
218
219
	function feed_cdata ($p, $text) {
0 ignored issues
show
The parameter $p is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
220
221
		if ($this->feed_type == ATOM and $this->incontent)
222
		{
223
			$this->append_content( $text );
224
		}
225
		else {
226
			$current_el = join('_', array_reverse($this->stack));
227
			$this->append($current_el, $text);
228
		}
229
	}
230
231
	function feed_end_element ($p, $el) {
0 ignored issues
show
The parameter $p is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
232
		$el = strtolower($el);
233
234
		if ( $el == 'item' or $el == 'entry' )
235
		{
236
			$this->items[] = $this->current_item;
237
			$this->current_item = array();
238
			$this->initem = false;
239
		}
240 View Code Duplication
		elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'textinput' )
241
		{
242
			$this->intextinput = false;
243
		}
244 View Code Duplication
		elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'image' )
245
		{
246
			$this->inimage = false;
247
		}
248
		elseif ($this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) )
249
		{
250
			$this->incontent = false;
251
		}
252
		elseif ($el == 'channel' or $el == 'feed' )
253
		{
254
			$this->inchannel = false;
255
		}
256
		elseif ($this->feed_type == ATOM and $this->incontent  ) {
257
			// balance tags properly
258
			// note: This may not actually be necessary
259
			if ( $this->stack[0] == $el )
260
			{
261
				$this->append_content("</$el>");
262
			}
263
			else {
264
				$this->append_content("<$el />");
265
			}
266
267
			array_shift( $this->stack );
268
		}
269
		else {
270
			array_shift( $this->stack );
271
		}
272
273
		$this->current_namespace = false;
274
	}
275
276
	function concat (&$str1, $str2="") {
277
		if (!isset($str1) ) {
278
			$str1="";
279
		}
280
		$str1 .= $str2;
281
	}
282
283
	function append_content($text) {
284
		if ( $this->initem ) {
285
			$this->concat( $this->current_item[ $this->incontent ], $text );
286
		}
287
		elseif ( $this->inchannel ) {
288
			$this->concat( $this->channel[ $this->incontent ], $text );
289
		}
290
	}
291
292
	// smart append - field and namespace aware
293
	function append($el, $text) {
294
		if (!$el) {
295
			return;
296
		}
297
		if ( $this->current_namespace )
298
		{
299
			if ( $this->initem ) {
300
				$this->concat(
301
					$this->current_item[ $this->current_namespace ][ $el ], $text);
302
			}
303
			elseif ($this->inchannel) {
304
				$this->concat(
305
					$this->channel[ $this->current_namespace][ $el ], $text );
306
			}
307
			elseif ($this->intextinput) {
308
				$this->concat(
309
					$this->textinput[ $this->current_namespace][ $el ], $text );
310
			}
311
			elseif ($this->inimage) {
312
				$this->concat(
313
					$this->image[ $this->current_namespace ][ $el ], $text );
314
			}
315
		}
316
		else {
317
			if ( $this->initem ) {
318
				$this->concat(
319
					$this->current_item[ $el ], $text);
320
			}
321
			elseif ($this->intextinput) {
322
				$this->concat(
323
					$this->textinput[ $el ], $text );
324
			}
325
			elseif ($this->inimage) {
326
				$this->concat(
327
					$this->image[ $el ], $text );
328
			}
329
			elseif ($this->inchannel) {
330
				$this->concat(
331
					$this->channel[ $el ], $text );
332
			}
333
334
		}
335
	}
336
337
	function normalize () {
338
		// if atom populate rss fields
339
		if ( $this->is_atom() ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->is_atom() of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
340
			$this->channel['descripton'] = $this->channel['tagline'];
341 View Code Duplication
			for ( $i = 0; $i < count($this->items); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
342
				$item = $this->items[$i];
343
				if ( isset($item['summary']) )
344
					$item['description'] = $item['summary'];
345
				if ( isset($item['atom_content']))
346
					$item['content']['encoded'] = $item['atom_content'];
347
348
				$this->items[$i] = $item;
349
			}
350
		}
351
		elseif ( $this->is_rss() ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->is_rss() of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
352
			$this->channel['tagline'] = $this->channel['description'];
353 View Code Duplication
			for ( $i = 0; $i < count($this->items); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
354
				$item = $this->items[$i];
355
				if ( isset($item['description']))
356
					$item['summary'] = $item['description'];
357
				if ( isset($item['content']['encoded'] ) )
358
					$item['atom_content'] = $item['content']['encoded'];
359
360
				$this->items[$i] = $item;
361
			}
362
		}
363
	}
364
365
	function is_rss () {
366
		if ( $this->feed_type == RSS ) {
367
			return $this->feed_version;
368
		}
369
		else {
370
			return false;
371
		}
372
	}
373
374
	function is_atom() {
375
		if ( $this->feed_type == ATOM ) {
376
			return $this->feed_version;
377
		}
378
		else {
379
			return false;
380
		}
381
	}
382
383
	function map_attrs($k, $v) {
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
384
		return "$k=\"$v\"";
385
	}
386
387 View Code Duplication
	function error( $errormsg, $lvl = E_USER_WARNING ) {
388
		// append PHP's error message if track_errors enabled
389
		if ( isset($php_errormsg) ) {
390
			$errormsg .= " ($php_errormsg)";
391
		}
392
		if ( MAGPIE_DEBUG ) {
393
			trigger_error( $errormsg, $lvl);
394
		} else {
395
			error_log( $errormsg, 0);
396
		}
397
	}
398
399
}
400
401
if ( !function_exists('fetch_rss') ) :
402
/**
403
 * Build Magpie object based on RSS from URL.
404
 *
405
 * @since 1.5.0
406
 * @package External
407
 * @subpackage MagpieRSS
408
 *
409
 * @param string $url URL to retrieve feed
410
 * @return bool|MagpieRSS false on failure or MagpieRSS object on success.
411
 */
412
function fetch_rss ($url) {
413
	// initialize constants
414
	init();
415
416
	if ( !isset($url) ) {
417
		// error("fetch_rss called without a url");
418
		return false;
419
	}
420
421
	// if cache is disabled
422
	if ( !MAGPIE_CACHE_ON ) {
423
		// fetch file, and parse it
424
		$resp = _fetch_remote_file( $url );
425
		if ( is_success( $resp->status ) ) {
426
			return _response_to_rss( $resp );
427
		}
428
		else {
429
			// error("Failed to fetch $url and cache is off");
430
			return false;
431
		}
432
	}
433
	// else cache is ON
434
	else {
435
		// Flow
436
		// 1. check cache
437
		// 2. if there is a hit, make sure it's fresh
438
		// 3. if cached obj fails freshness check, fetch remote
439
		// 4. if remote fails, return stale object, or error
440
441
		$cache = new RSSCache( MAGPIE_CACHE_DIR, MAGPIE_CACHE_AGE );
442
443
		if (MAGPIE_DEBUG and $cache->ERROR) {
444
			debug($cache->ERROR, E_USER_WARNING);
445
		}
446
447
		$cache_status 	 = 0;		// response of check_cache
448
		$request_headers = array(); // HTTP headers to send with fetch
449
		$rss 			 = 0;		// parsed RSS object
450
		$errormsg		 = 0;		// errors, if any
451
452
		if (!$cache->ERROR) {
453
			// return cache HIT, MISS, or STALE
454
			$cache_status = $cache->check_cache( $url );
455
		}
456
457
		// if object cached, and cache is fresh, return cached obj
458
		if ( $cache_status == 'HIT' ) {
459
			$rss = $cache->get( $url );
460
			if ( isset($rss) and $rss ) {
461
				$rss->from_cache = 1;
462
				if ( MAGPIE_DEBUG > 1) {
463
				debug("MagpieRSS: Cache HIT", E_USER_NOTICE);
464
			}
465
				return $rss;
466
			}
467
		}
468
469
		// else attempt a conditional get
470
471
		// set up headers
472
		if ( $cache_status == 'STALE' ) {
473
			$rss = $cache->get( $url );
474
			if ( isset($rss->etag) and $rss->last_modified ) {
475
				$request_headers['If-None-Match'] = $rss->etag;
476
				$request_headers['If-Last-Modified'] = $rss->last_modified;
477
			}
478
		}
479
480
		$resp = _fetch_remote_file( $url, $request_headers );
481
482
		if (isset($resp) and $resp) {
483
			if ($resp->status == '304' ) {
484
				// we have the most current copy
485
				if ( MAGPIE_DEBUG > 1) {
486
					debug("Got 304 for $url");
487
				}
488
				// reset cache on 304 (at minutillo insistent prodding)
489
				$cache->set($url, $rss);
490
				return $rss;
491
			}
492
			elseif ( is_success( $resp->status ) ) {
493
				$rss = _response_to_rss( $resp );
494
				if ( $rss ) {
495
					if (MAGPIE_DEBUG > 1) {
496
						debug("Fetch successful");
497
					}
498
					// add object to cache
499
					$cache->set( $url, $rss );
500
					return $rss;
501
				}
502
			}
503
			else {
504
				$errormsg = "Failed to fetch $url. ";
505
				if ( $resp->error ) {
506
					# compensate for Snoopy's annoying habbit to tacking
507
					# on '\n'
508
					$http_error = substr($resp->error, 0, -2);
509
					$errormsg .= "(HTTP Error: $http_error)";
510
				}
511
				else {
512
					$errormsg .=  "(HTTP Response: " . $resp->response_code .')';
513
				}
514
			}
515
		}
516
		else {
517
			$errormsg = "Unable to retrieve RSS file for unknown reasons.";
518
		}
519
520
		// else fetch failed
521
522
		// attempt to return cached object
523
		if ($rss) {
524
			if ( MAGPIE_DEBUG ) {
525
				debug("Returning STALE object for $url");
526
			}
527
			return $rss;
528
		}
529
530
		// else we totally failed
531
		// error( $errormsg );
532
533
		return false;
534
535
	} // end if ( !MAGPIE_CACHE_ON ) {
536
} // end fetch_rss()
537
endif;
538
539
/**
540
 * Retrieve URL headers and content using WP HTTP Request API.
541
 *
542
 * @since 1.5.0
543
 * @package External
544
 * @subpackage MagpieRSS
545
 *
546
 * @param string $url URL to retrieve
547
 * @param array $headers Optional. Headers to send to the URL.
0 ignored issues
show
Should the type for parameter $headers not be string|array? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
548
 * @return Snoopy style response
0 ignored issues
show
Should the return type not be stdClass?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
549
 */
550
function _fetch_remote_file($url, $headers = "" ) {
551
	$resp = wp_safe_remote_request( $url, array( 'headers' => $headers, 'timeout' => MAGPIE_FETCH_TIME_OUT ) );
552
	if ( is_wp_error($resp) ) {
553
		$error = array_shift($resp->errors);
554
555
		$resp = new stdClass;
556
		$resp->status = 500;
557
		$resp->response_code = 500;
558
		$resp->error = $error[0] . "\n"; //\n = Snoopy compatibility
559
		return $resp;
560
	}
561
562
	// Snoopy returns headers unprocessed.
563
	// Also note, WP_HTTP lowercases all keys, Snoopy did not.
564
	$return_headers = array();
565
	foreach ( wp_remote_retrieve_headers( $resp ) as $key => $value ) {
566
		if ( !is_array($value) ) {
567
			$return_headers[] = "$key: $value";
568
		} else {
569
			foreach ( $value as $v )
570
				$return_headers[] = "$key: $v";
571
		}
572
	}
573
574
	$response = new stdClass;
575
	$response->status = wp_remote_retrieve_response_code( $resp );
576
	$response->response_code = wp_remote_retrieve_response_code( $resp );
577
	$response->headers = $return_headers;
578
	$response->results = wp_remote_retrieve_body( $resp );
579
580
	return $response;
581
}
582
583
/**
584
 * Retrieve
585
 *
586
 * @since 1.5.0
587
 * @package External
588
 * @subpackage MagpieRSS
589
 *
590
 * @param array $resp
591
 * @return MagpieRSS|bool
0 ignored issues
show
Consider making the return type a bit more specific; maybe use MagpieRSS|false.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
592
 */
593
function _response_to_rss ($resp) {
594
	$rss = new MagpieRSS( $resp->results );
595
596
	// if RSS parsed successfully
597
	if ( $rss && (!isset($rss->ERROR) || !$rss->ERROR) ) {
598
599
		// find Etag, and Last-Modified
600
		foreach ( (array) $resp->headers as $h) {
601
			// 2003-03-02 - Nicola Asuni (www.tecnick.com) - fixed bug "Undefined offset: 1"
602
			if (strpos($h, ": ")) {
603
				list($field, $val) = explode(": ", $h, 2);
604
			}
605
			else {
606
				$field = $h;
607
				$val = "";
608
			}
609
610
			if ( $field == 'etag' ) {
611
				$rss->etag = $val;
0 ignored issues
show
The property etag does not seem to exist in MagpieRSS.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
612
			}
613
614
			if ( $field == 'last-modified' ) {
615
				$rss->last_modified = $val;
0 ignored issues
show
The property last_modified does not seem to exist in MagpieRSS.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
616
			}
617
		}
618
619
		return $rss;
620
	} // else construct error message
621
	else {
622
		$errormsg = "Failed to parse RSS file.";
623
624
		if ($rss) {
625
			$errormsg .= " (" . $rss->ERROR . ")";
0 ignored issues
show
The property ERROR does not seem to exist in MagpieRSS.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
626
		}
627
		// error($errormsg);
628
629
		return false;
630
	} // end if ($rss and !$rss->error)
631
}
632
633
/**
634
 * Set up constants with default values, unless user overrides.
635
 *
636
 * @since 1.5.0
637
 * @package External
638
 * @subpackage MagpieRSS
639
 */
640
function init () {
641
	if ( defined('MAGPIE_INITALIZED') ) {
642
		return;
643
	}
644
	else {
645
		define('MAGPIE_INITALIZED', 1);
646
	}
647
648
	if ( !defined('MAGPIE_CACHE_ON') ) {
649
		define('MAGPIE_CACHE_ON', 1);
650
	}
651
652
	if ( !defined('MAGPIE_CACHE_DIR') ) {
653
		define('MAGPIE_CACHE_DIR', './cache');
654
	}
655
656
	if ( !defined('MAGPIE_CACHE_AGE') ) {
657
		define('MAGPIE_CACHE_AGE', 60*60); // one hour
658
	}
659
660
	if ( !defined('MAGPIE_CACHE_FRESH_ONLY') ) {
661
		define('MAGPIE_CACHE_FRESH_ONLY', 0);
662
	}
663
664
		if ( !defined('MAGPIE_DEBUG') ) {
665
		define('MAGPIE_DEBUG', 0);
666
	}
667
668
	if ( !defined('MAGPIE_USER_AGENT') ) {
669
		$ua = 'WordPress/' . $GLOBALS['wp_version'];
670
671
		if ( MAGPIE_CACHE_ON ) {
672
			$ua = $ua . ')';
673
		}
674
		else {
675
			$ua = $ua . '; No cache)';
676
		}
677
678
		define('MAGPIE_USER_AGENT', $ua);
679
	}
680
681
	if ( !defined('MAGPIE_FETCH_TIME_OUT') ) {
682
		define('MAGPIE_FETCH_TIME_OUT', 2);	// 2 second timeout
683
	}
684
685
	// use gzip encoding to fetch rss files if supported?
686
	if ( !defined('MAGPIE_USE_GZIP') ) {
687
		define('MAGPIE_USE_GZIP', true);
688
	}
689
}
690
691
function is_info ($sc) {
692
	return $sc >= 100 && $sc < 200;
693
}
694
695
function is_success ($sc) {
696
	return $sc >= 200 && $sc < 300;
697
}
698
699
function is_redirect ($sc) {
700
	return $sc >= 300 && $sc < 400;
701
}
702
703
function is_error ($sc) {
704
	return $sc >= 400 && $sc < 600;
705
}
706
707
function is_client_error ($sc) {
708
	return $sc >= 400 && $sc < 500;
709
}
710
711
function is_server_error ($sc) {
712
	return $sc >= 500 && $sc < 600;
713
}
714
715
class RSSCache {
716
	var $BASE_CACHE;	// where the cache files are stored
717
	var $MAX_AGE	= 43200;  		// when are files stale, default twelve hours
718
	var $ERROR 		= '';			// accumulate error messages
719
720
	/**
721
	 * PHP5 constructor.
722
	 */
723
	function __construct( $base = '', $age = '' ) {
724
		$this->BASE_CACHE = WP_CONTENT_DIR . '/cache';
725
		if ( $base ) {
726
			$this->BASE_CACHE = $base;
727
		}
728
		if ( $age ) {
729
			$this->MAX_AGE = $age;
0 ignored issues
show
Documentation Bug introduced by
The property $MAX_AGE was declared of type integer, but $age is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
730
		}
731
732
	}
733
734
	/**
735
	 * PHP4 constructor.
736
	 */
737
	public function RSSCache( $base = '', $age = '' ) {
738
		self::__construct( $base, $age );
739
	}
740
741
/*=======================================================================*\
742
	Function:	set
743
	Purpose:	add an item to the cache, keyed on url
744
	Input:		url from which the rss file was fetched
745
	Output:		true on success
746
\*=======================================================================*/
747
	function set ($url, $rss) {
748
		$cache_option = 'rss_' . $this->file_name( $url );
749
750
		set_transient($cache_option, $rss, $this->MAX_AGE);
751
752
		return $cache_option;
753
	}
754
755
/*=======================================================================*\
756
	Function:	get
757
	Purpose:	fetch an item from the cache
758
	Input:		url from which the rss file was fetched
759
	Output:		cached object on HIT, false on MISS
760
\*=======================================================================*/
761
	function get ($url) {
762
		$this->ERROR = "";
763
		$cache_option = 'rss_' . $this->file_name( $url );
764
765
		if ( ! $rss = get_transient( $cache_option ) ) {
766
			$this->debug(
767
				"Cache doesn't contain: $url (cache option: $cache_option)"
768
			);
769
			return 0;
770
		}
771
772
		return $rss;
773
	}
774
775
/*=======================================================================*\
776
	Function:	check_cache
777
	Purpose:	check a url for membership in the cache
778
				and whether the object is older then MAX_AGE (ie. STALE)
779
	Input:		url from which the rss file was fetched
780
	Output:		cached object on HIT, false on MISS
781
\*=======================================================================*/
782
	function check_cache ( $url ) {
783
		$this->ERROR = "";
784
		$cache_option = 'rss_' . $this->file_name( $url );
785
786
		if ( get_transient($cache_option) ) {
787
			// object exists and is current
788
				return 'HIT';
789
		} else {
790
			// object does not exist
791
			return 'MISS';
792
		}
793
	}
794
795
/*=======================================================================*\
796
	Function:	serialize
797
\*=======================================================================*/
798
	function serialize ( $rss ) {
799
		return serialize( $rss );
800
	}
801
802
/*=======================================================================*\
803
	Function:	unserialize
804
\*=======================================================================*/
805
	function unserialize ( $data ) {
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
806
		return unserialize( $data );
807
	}
808
809
/*=======================================================================*\
810
	Function:	file_name
811
	Purpose:	map url to location in cache
812
	Input:		url from which the rss file was fetched
813
	Output:		a file name
814
\*=======================================================================*/
815
	function file_name ($url) {
816
		return md5( $url );
817
	}
818
819
/*=======================================================================*\
820
	Function:	error
821
	Purpose:	register error
822
\*=======================================================================*/
823 View Code Duplication
	function error ($errormsg, $lvl=E_USER_WARNING) {
824
		// append PHP's error message if track_errors enabled
825
		if ( isset($php_errormsg) ) {
826
			$errormsg .= " ($php_errormsg)";
827
		}
828
		$this->ERROR = $errormsg;
829
		if ( MAGPIE_DEBUG ) {
830
			trigger_error( $errormsg, $lvl);
831
		}
832
		else {
833
			error_log( $errormsg, 0);
834
		}
835
	}
836
			function debug ($debugmsg, $lvl=E_USER_NOTICE) {
837
		if ( MAGPIE_DEBUG ) {
838
			$this->error("MagpieRSS [debug] $debugmsg", $lvl);
839
		}
840
	}
841
}
842
843
if ( !function_exists('parse_w3cdtf') ) :
844
function parse_w3cdtf ( $date_str ) {
845
846
	# regex to match wc3dtf
847
	$pat = "/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(:(\d{2}))?(?:([-+])(\d{2}):?(\d{2})|(Z))?/";
848
849
	if ( preg_match( $pat, $date_str, $match ) ) {
850
		list( $year, $month, $day, $hours, $minutes, $seconds) =
851
			array( $match[1], $match[2], $match[3], $match[4], $match[5], $match[7]);
852
853
		# calc epoch for current date assuming GMT
854
		$epoch = gmmktime( $hours, $minutes, $seconds, $month, $day, $year);
855
856
		$offset = 0;
857
		if ( $match[11] == 'Z' ) {
858
			# zulu time, aka GMT
859
		}
860
		else {
861
			list( $tz_mod, $tz_hour, $tz_min ) =
862
				array( $match[8], $match[9], $match[10]);
863
864
			# zero out the variables
865
			if ( ! $tz_hour ) { $tz_hour = 0; }
866
			if ( ! $tz_min ) { $tz_min = 0; }
867
868
			$offset_secs = (($tz_hour*60)+$tz_min)*60;
869
870
			# is timezone ahead of GMT?  then subtract offset
871
			#
872
			if ( $tz_mod == '+' ) {
873
				$offset_secs = $offset_secs * -1;
874
			}
875
876
			$offset = $offset_secs;
877
		}
878
		$epoch = $epoch + $offset;
879
		return $epoch;
880
	}
881
	else {
882
		return -1;
883
	}
884
}
885
endif;
886
887
if ( !function_exists('wp_rss') ) :
888
/**
889
 * Display all RSS items in a HTML ordered list.
890
 *
891
 * @since 1.5.0
892
 * @package External
893
 * @subpackage MagpieRSS
894
 *
895
 * @param string $url URL of feed to display. Will not auto sense feed URL.
896
 * @param int $num_items Optional. Number of items to display, default is all.
897
 */
898
function wp_rss( $url, $num_items = -1 ) {
899
	if ( $rss = fetch_rss( $url ) ) {
900
		echo '<ul>';
901
902
		if ( $num_items !== -1 ) {
903
			$rss->items = array_slice( $rss->items, 0, $num_items );
904
		}
905
906
		foreach ( (array) $rss->items as $item ) {
907
			printf(
908
				'<li><a href="%1$s" title="%2$s">%3$s</a></li>',
909
				esc_url( $item['link'] ),
910
				esc_attr( strip_tags( $item['description'] ) ),
911
				esc_html( $item['title'] )
912
			);
913
		}
914
915
		echo '</ul>';
916
	} else {
917
		_e( 'An error has occurred, which probably means the feed is down. Try again later.' );
918
	}
919
}
920
endif;
921
922
if ( !function_exists('get_rss') ) :
923
/**
924
 * Display RSS items in HTML list items.
925
 *
926
 * You have to specify which HTML list you want, either ordered or unordered
927
 * before using the function. You also have to specify how many items you wish
928
 * to display. You can't display all of them like you can with wp_rss()
929
 * function.
930
 *
931
 * @since 1.5.0
932
 * @package External
933
 * @subpackage MagpieRSS
934
 *
935
 * @param string $url URL of feed to display. Will not auto sense feed URL.
936
 * @param int $num_items Optional. Number of items to display, default is all.
937
 * @return bool False on failure.
0 ignored issues
show
Should the return type not be null|false?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
938
 */
939
function get_rss ($url, $num_items = 5) { // Like get posts, but for RSS
940
	$rss = fetch_rss($url);
941
	if ( $rss ) {
942
		$rss->items = array_slice($rss->items, 0, $num_items);
943
		foreach ( (array) $rss->items as $item ) {
944
			echo "<li>\n";
945
			echo "<a href='$item[link]' title='$item[description]'>";
946
			echo esc_html($item['title']);
947
			echo "</a><br />\n";
948
			echo "</li>\n";
949
		}
950
	} else {
951
		return false;
952
	}
953
}
954
endif;
955