VaultPress_Hotfixes   F
last analyzed

Complexity

Total Complexity 208

Size/Duplication

Total Lines 819
Duplicated Lines 1.71 %

Coupling/Cohesion

Components 0
Dependencies 1

Importance

Changes 0
Metric Value
dl 14
loc 819
rs 1.781
c 0
b 0
f 0
wmc 208
lcom 0
cbo 1

39 Methods

Rating   Name   Duplication   Size   Complexity  
F __construct() 0 133 31
A protect_rest_type_juggling() 0 7 2
A protect_youtube_embeds() 0 8 2
A safe_embed_handler_youtube() 0 4 1
A protect_jetpack_402_from_oembed_xss() 0 5 2
B needs_jetpack_402_fix() 0 54 6
A disable_jetpack_oembed() 0 3 1
A filter_long_comment_xss() 0 6 2
A wp_check_filetype_and_ext() 0 17 4
A disable_jetpack_xmlrpc_methods_293() 0 5 3
A disable_xmlrpc_methods_293() 0 5 2
A needs_jetpack_293_fix() 0 21 3
A r21138_xmlrpc_edit_posts() 0 9 6
B r21152_unfiltered_html() 0 12 7
A get_pagenum_link() 0 3 1
A r20486_comment_post_redirect() 0 6 1
B r20493_make_clickable() 0 51 8
A r20493_make_url_clickable_cb() 0 24 5
A r20493_split_str_by_whitespace() 0 26 5
A r16625() 0 31 3
F filter_xmlrpc_methods() 14 107 32
B r17172_esc_url() 0 29 7
A r17172_wp_kses() 0 3 1
B r17990() 0 27 6
A r18368() 0 10 4
B r17994() 0 18 11
A r17994_sanitize_mime_type() 0 4 1
A r17826_send_frame_options_header() 0 3 1
A r18346_sanitize_admin_email_on_save() 0 9 3
A r18346_sanitize_admin_email() 0 3 1
A r18346_sanitize_lang_on_save() 0 6 2
A r18346_sanitize_lang() 0 7 3
B protect_aioseo_ajax() 0 18 7
C protect_wysija_newsletters_verify_capability() 0 37 12
B protect_revslider_lfi() 0 14 7
B protect_woocommerce_paypal_object_injection() 0 18 8
A patch_user_email() 0 7 3
A filtered_caption_shortcode() 0 7 3
A protect_akismet_comment_xss() 0 3 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like VaultPress_Hotfixes often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use VaultPress_Hotfixes, and based on these observations, apply Extract Interface, too.

1
<?php
2
// don't call the file directly
3
defined( 'ABSPATH' ) or die();
4
5
class VaultPress_Hotfixes {
6
	function __construct() {
7
		global $wp_version;
8
9
		if ( version_compare( $wp_version, '3.0.2', '<' ) )
10
			add_filter( 'query', array( $this, 'r16625' ) );
11
12
		if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST && version_compare( $wp_version, '4.3.1', '<' ) )
13
			add_action( 'xmlrpc_call', array( $this, 'filter_xmlrpc_methods' ) );
14
15
		if ( version_compare( $wp_version, '3.3.2', '<' ) ) {
16
			add_filter( 'pre_kses', array( $this, 'r17172_wp_kses' ), 1, 3 );
17
			add_filter( 'clean_url', array( $this, 'r17172_esc_url' ), 1, 3 );
18
		}
19
20
		if ( version_compare( $wp_version, '3.1.3', '<' ) ) {
21
			add_filter( 'sanitize_file_name', array( $this, 'r17990' ) );
22
23
			if ( ! empty( $_POST ) ) {
24
				$this->r17994( $_POST );
25
			}
26
			// Protect add_meta, update_meta used by the XML-RPC API.
27
			add_filter( 'wp_xmlrpc_server_class', 'r17994_xmlrpc_server' );
28
29
			// clean post_mime_type and guid (r17994)
30
			add_filter( 'pre_post_mime_type', array( $this, 'r17994_sanitize_mime_type' ) );
31
			add_filter( 'post_mime_type', array( $this, 'r17994_sanitize_mime_type' ) );
32
			add_filter( 'pre_post_guid', 'esc_url_raw' );
33
			add_filter( 'post_guid', 'esc_url' );
34
		}
35
36
		if ( version_compare( $wp_version, '3.1.4', '<' ) ) {
37
			add_filter( 'wp_insert_post_data', array( $this, 'r18368' ), 1, 2 );
38
39
			// Add click jacking protection
40
			// login_init does not exist before 17826.
41
			$action = isset( $_REQUEST['action'] ) ? $_REQUEST['action'] : 'login';
42
			add_action( 'login_form_' . $action, array( $this, 'r17826_send_frame_options_header' ), 10, 0 );
43
			add_action( 'admin_init', array( $this, 'r17826_send_frame_options_header' ), 10, 0 );
44
45
			add_filter( 'sanitize_option_WPLANG', array( $this, 'r18346_sanitize_lang_on_save' ) );
46
			add_filter( 'sanitize_option_new_admin_email', array( $this, 'r18346_sanitize_admin_email_on_save' ) );
47
		}
48
		add_filter( 'option_new_admin_email', array( $this, 'r18346_sanitize_admin_email' ) );
49
50
		if ( version_compare( $wp_version, '3.3.2', '<' ) ) {
51
			remove_filter( 'comment_text', 'make_clickable' );
52
			add_filter( 'comment_text', array( $this, 'r20493_make_clickable' ), 9 );
53
54
			add_filter( 'comment_post_redirect', array( $this, 'r20486_comment_post_redirect' ) );
55
		}
56
57
		// WooThemes < 3.8.3, foxypress, asset-manager, wordpress-member-private-conversation.
58
		$end_execution = false;
59
		if ( isset( $_SERVER['SCRIPT_FILENAME'] ) )
60
			foreach ( array( 'preview-shortcode-external.php', 'uploadify.php', 'doupload.php', 'cef-upload.php', 'upload.php' ) as $vulnerable_script ) {
61
				if ( $vulnerable_script == basename( $_SERVER['SCRIPT_FILENAME'] ) ) {
62
					switch ( $vulnerable_script ) {
63
						case 'upload.php':
64
							$pma_config_file = realpath( dirname( $_SERVER['SCRIPT_FILENAME'] ) . DIRECTORY_SEPARATOR . 'paam-config-ajax.php' );
65
							if ( false === $pma_config_file || ! in_array( $pma_config_file, get_included_files() ) ) {
66
								break;
67
							}
68
						default:
69
							$end_execution = true;
70
							break 2;
71
					}
72
				}
73
			}
74
		if ( $end_execution )
75
			die( 'Disabled for security reasons' );
76
77
		if ( version_compare(  $wp_version, '3.3.2', '>') && version_compare( $wp_version, '3.4.1', '<' ) ) {
78
			add_filter( 'map_meta_cap', array( $this, 'r21138_xmlrpc_edit_posts' ), 10, 4 );
79
			add_action( 'map_meta_cap', array( $this, 'r21152_unfiltered_html' ), 10, 4 );
80
		}
81
82
		// https://core.trac.wordpress.org/changeset/21083
83
		if ( version_compare( $wp_version, '3.3', '>=') && version_compare( $wp_version, '3.3.3', '<' ) )
84
			add_filter( 'editable_slug', 'esc_textarea' );
85
86
		if ( version_compare( $wp_version, '4.1', '>=' ) && version_compare( $wp_version, '4.1.2', '<' ) )
87
			add_filter( 'wp_check_filetype_and_ext', array( $this, 'wp_check_filetype_and_ext' ), 20, 4 );
88
89
		if ( version_compare( $wp_version, '4.2', '<=' ) )
90
			add_filter( 'preprocess_comment', array( $this, 'filter_long_comment_xss' ), 10, 1 );
91
92
		add_filter( 'get_pagenum_link', array( $this, 'get_pagenum_link' ) );
93
94
		add_filter( 'jetpack_xmlrpc_methods', array( $this, 'disable_jetpack_xmlrpc_methods_293' ), 20, 3 );
95
		add_filter( 'xmlrpc_methods', array( $this, 'disable_xmlrpc_methods_293' ), 20 );
96
97
		// Protect All-in-one SEO from non-authorized users making changes, and script injection attacks.
98
		add_action( 'wp_ajax_aioseop_ajax_save_meta', array( $this, 'protect_aioseo_ajax' ), 1 );
99
100
		// Protect The MailPoet plugin (wysija-newsletters) from remote file upload. Affects versions <= 2.6.6
101
		add_action( 'admin_init', array( $this , 'protect_wysija_newsletters_verify_capability' ), 1 );
102
103
		// Protect the Revolution Slider plugin (revslider) from local file inclusion. Affects versions < 4.2
104
		add_action( 'init', array( $this , 'protect_revslider_lfi' ), 1 );
105
106
		// Protect WooCommerce from object injection via PayPal IPN notifications. Affects 2.0.20 -> 2.3.10
107
		add_action( 'init', array( $this , 'protect_woocommerce_paypal_object_injection' ), 1 );
108
109
		// Protect Jetpack from comments-based XSS attack
110
		add_action( 'plugins_loaded', array( $this, 'protect_jetpack_402_from_oembed_xss' ), 1 );
111
112
		if ( version_compare(  $wp_version, '3.1', '>=') && version_compare( $wp_version, '4.3', '<=' ) ) {
113
			if ( is_admin() ) {
114
				add_filter( 'user_email', array( $this, 'patch_user_email' ), 10 , 3 );
115
			}
116
117
			remove_shortcode( 'wp_caption' );
118
			remove_shortcode( 'caption' );
119
			add_shortcode( 'wp_caption', array( $this, 'filtered_caption_shortcode' ) );
120
			add_shortcode( 'caption', array( $this, 'filtered_caption_shortcode' ) );
121
		}
122
123
		// Protect Akismet < 3.1.5 from stored XSS in admin page
124
		add_filter( 'init', array( $this, 'protect_akismet_comment_xss' ), 50 );
125
126
		if ( version_compare( $wp_version, '4.7.1', '<=' ) ) {
127
			// Protect WordPress 4.4 - 4.7.1 against WP REST type abuse
128
			if ( version_compare( $wp_version, '4.4', '>=' ) ) {
129
				add_filter( 'rest_pre_dispatch', array( $this, 'protect_rest_type_juggling' ), 10, 3 );
130
			}
131
132
			//	Protect WordPress 4.0 - 4.7.1 against faulty youtube embeds
133
			if ( version_compare( $wp_version, '4.0', '>=' ) ) {
134
				$this->protect_youtube_embeds();
135
			}
136
		}
137
138
	}
139
140
	function protect_rest_type_juggling( $replace, $server, $request ) {
141
		if ( isset( $request['id'] ) ) {
142
			$request['id'] = intval( $request['id'] );
143
		}
144
145
		return $replace;
146
	}
147
148
	function protect_youtube_embeds() {
149
		if ( ! apply_filters( 'load_default_embeds', true ) ) {
150
			return;
151
		}
152
153
		wp_embed_unregister_handler( 'youtube_embed_url' );
154
		wp_embed_register_handler( 'youtube_embed_url', '#https?://(www.)?youtube\.com/(?:v|embed)/([^/]+)#i', array( $this, 'safe_embed_handler_youtube' ), 9, 4 );
155
	}
156
157
	function safe_embed_handler_youtube( $matches, $attr, $url, $rawattr ) {
158
		$matches[2] = urlencode( $matches[2] );
159
		return( wp_embed_handler_youtube( $matches, $attr, $url, $rawattr ) );
160
	}
161
162
	function protect_jetpack_402_from_oembed_xss() {
163
		if ( $this->needs_jetpack_402_fix() ) {
164
			add_filter( 'jetpack_comments_allow_oembed', array( $this, 'disable_jetpack_oembed' ) );
165
		}
166
	}
167
168
	function needs_jetpack_402_fix() {
169
		if ( ! defined( 'JETPACK__VERSION' ) ) {
170
			return false;
171
		}
172
173
		if ( version_compare( JETPACK__VERSION, '2.0.7', '<' ) ) {
174
			return true;
175
		}
176
177
		if ( version_compare( JETPACK__VERSION, '4.0.2', '>' ) ) {
178
			return false;
179
		}
180
181
		$secure_jetpacks = array(
182
			'2.1' => '2.1.5',
183
			'2.2' => '2.2.8',
184
			'2.3' => '2.3.8',
185
			'2.4' => '2.4.5',
186
			'2.5' => '2.5.3',
187
			'2.6' => '2.6.4',
188
			'2.7' => '2.7.3',
189
			'2.8' => '2.8.3',
190
			'2.9' => '2.9.4',
191
			'3.0' => '3.0.4',
192
			'3.1' => '3.1.3',
193
			'3.2' => '3.2.3',
194
			'3.3' => '3.3.4',
195
			'3.4' => '3.4.4',
196
			'3.5' => '3.5.4',
197
			'3.6' => '3.6.2',
198
			'3.7' => '3.7.3',
199
			'3.8' => '3.8.3',
200
			'3.9' => '3.9.7',
201
			'4.0' => '4.0.3',
202
		);
203
204
		$parts = explode( '.', JETPACK__VERSION, 3 );
205
		if ( count( $parts ) < 2 ) {
206
			// no/not enough periods in the version;
207
			return false;
208
		}
209
210
		// pull out the first two components, cast to int to get rid of weird 'beta2' junk
211
		$int_parts = array();
212
		$int_parts[0] = intval( $parts[0] );
213
		$int_parts[1] = intval( $parts[1] );
214
215
		// and find the secure version for this branch
216
		$branch = sprintf( '%d.%d', $int_parts[0], $int_parts[1] );
217
		if ( ! isset( $secure_jetpacks[ $branch ] ) ) {
218
			return false;
219
		}
220
		return version_compare( JETPACK__VERSION, $secure_jetpacks[ $branch ], '<' );
221
	}
222
223
	function disable_jetpack_oembed( $enabled ) {
224
		return false;
225
	}
226
227
	function filter_long_comment_xss( $commentdata ) {
228
		if ( strlen( $commentdata['comment_content'] ) > 65500 )
229
			wp_die( 'Comment too long', 'Invalid comment' );
230
231
		return $commentdata;
232
	}
233
234
	function wp_check_filetype_and_ext( $filetype, $file, $filename, $mimes ) {
235
		if ( empty( $mimes ) )
236
			$mimes = get_allowed_mime_types();
237
		$type = false;
238
		$ext = false;
239
		foreach ( $mimes as $ext_preg => $mime_match ) {
240
			$ext_preg = '!\.(' . $ext_preg . ')$!i';
241
			if ( preg_match( $ext_preg, $filename, $ext_matches ) ) {
242
				$type = $mime_match;
243
				$ext = $ext_matches[1];
244
				break;
245
			}
246
		}
247
		$filetype['ext'] = $ext;
248
		$filetype['type'] = $type;
249
		return $filetype;
250
	}
251
252
	function disable_jetpack_xmlrpc_methods_293( $jetpack_methods, $core_methods, $user = false ) {
253
		if ( $this->needs_jetpack_293_fix() && !$user )
254
			unset( $jetpack_methods['jetpack.jsonAPI'], $jetpack_methods['jetpack.verifyAction'] );
255
		return $jetpack_methods;
256
	}
257
258
	function disable_xmlrpc_methods_293( $core_methods ) {
259
		if ( $this->needs_jetpack_293_fix() )
260
			unset( $core_methods['jetpack.verifyAction'] );
261
		return $core_methods;
262
	}
263
264
	function needs_jetpack_293_fix() {
265
		if ( ! defined( 'JETPACK__VERSION' ) )
266
			return false;
267
		$secure_jetpacks = array(
268
			'1.9' => '1.9.3',
269
			'2.0' => '2.0.5',
270
			'2.1' => '2.1.3',
271
			'2.2' => '2.2.6',
272
			'2.3' => '2.3.6',
273
			'2.4' => '2.4.3',
274
			'2.5' => '2.5.1',
275
			'2.6' => '2.6.2',
276
			'2.7' => '2.7.1',
277
			'2.8' => '2.8.1',
278
			'2.9' => '2.9.3',
279
		);
280
		$float_version = (string) floatval( JETPACK__VERSION );
281
		if ( ! isset( $secure_jetpacks[ $float_version ] ) )
282
			return false;
283
		return version_compare( JETPACK__VERSION, $secure_jetpacks[ $float_version ], '<' );
284
	}
285
286
	function r21138_xmlrpc_edit_posts( $caps, $cap, $user_id, $args ) {
287
		if ( ! isset( $args[0] ) || isset( $args[1] ) && $args[1] === 'hotfixed' )
288
			return $caps;
289
		foreach ( get_post_types( array(), 'objects' ) as $post_type_object ) {
290
			if ( $cap === $post_type_object->cap->edit_posts )
291
				return map_meta_cap( $post_type_object->cap->edit_post, $user_id, $args[0], 'hotfixed' );
292
		}
293
		return $caps;
294
	}
295
296
	function r21152_unfiltered_html( $caps, $cap, $user_id, $args ) {
297
		if ( $cap !== 'unfiltered_html' )
298
			return $caps;
299
		if ( defined( 'DISALLOW_UNFILTERED_HTML' ) && DISALLOW_UNFILTERED_HTML )
300
			return $caps;
301
		$key = array_search( 'do_not_allow', $caps );
302
		if ( false !== $key )
303
			return $caps;
304
		if ( is_multisite() && ! is_super_admin( $user_id ) )
305
			$caps[$key] = 'do_not_allow';
306
		return $caps;
307
	}
308
309
	function get_pagenum_link( $url ) {
310
		return esc_url_raw( $url );
311
	}
312
313
	function r20486_comment_post_redirect( $location ) {
314
		$location = wp_sanitize_redirect( $location );
315
		$location = wp_validate_redirect( $location, admin_url() );
316
317
		return $location;
318
	}
319
320
	function r20493_make_clickable( $text ) {
321
		$r = '';
322
		$textarr = preg_split( '/(<[^<>]+>)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE ); // split out HTML tags
323
		foreach ( $textarr as $piece ) {
324
			if ( empty( $piece ) || ( $piece[0] == '<' && ! preg_match('|^<\s*[\w]{1,20}+://|', $piece) ) ) {
325
				$r .= $piece;
326
				continue;
327
			}
328
329
			// Long strings might contain expensive edge cases ...
330
			if ( 10000 < strlen( $piece ) ) {
331
				// ... break it up
332
				foreach ( $this->r20493_split_str_by_whitespace( $piece, 2100 ) as $chunk ) { // 2100: Extra room for scheme and leading and trailing paretheses
333
					if ( 2101 < strlen( $chunk ) ) {
334
						$r .= $chunk; // Too big, no whitespace: bail.
335
					} else {
336
						$r .= $this->r20493_make_clickable( $chunk );
337
					}
338
				}
339
			} else {
340
				$ret = " $piece "; // Pad with whitespace to simplify the regexes
341
342
				$url_clickable = '~
343
					([\\s(<.,;:!?])                                        # 1: Leading whitespace, or punctuation
344
					(                                                      # 2: URL
345
						[\\w]{1,20}+://                                # Scheme and hier-part prefix
346
						(?=\S{1,2000}\s)                               # Limit to URLs less than about 2000 characters long
347
						[\\w\\x80-\\xff#%\\~/@\\[\\]*(+=&$-]*+         # Non-punctuation URL character
348
						(?:                                            # Unroll the Loop: Only allow puctuation URL character if followed by a non-punctuation URL character
349
							[\'.,;:!?)]                            # Punctuation URL character
350
							[\\w\\x80-\\xff#%\\~/@\\[\\]*(+=&$-]++ # Non-punctuation URL character
351
						)*
352
					)
353
					(\)?)                                                  # 3: Trailing closing parenthesis (for parethesis balancing post processing)
354
				~xS'; // The regex is a non-anchored pattern and does not have a single fixed starting character.
355
				      // Tell PCRE to spend more time optimizing since, when used on a page load, it will probably be used several times.
356
357
				$ret = preg_replace_callback( $url_clickable, array( $this, 'r20493_make_url_clickable_cb') , $ret );
358
359
				$ret = preg_replace_callback( '#([\s>])((www|ftp)\.[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]+)#is', '_make_web_ftp_clickable_cb', $ret );
360
				$ret = preg_replace_callback( '#([\s>])([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})#i', '_make_email_clickable_cb', $ret );
361
362
				$ret = substr( $ret, 1, -1 ); // Remove our whitespace padding.
363
				$r .= $ret;
364
			}
365
		}
366
367
		// Cleanup of accidental links within links
368
		$r = preg_replace( '#(<a( [^>]+?>|>))<a [^>]+?>([^>]+?)</a></a>#i', "$1$3</a>", $r );
369
		return $r;
370
	}
371
372
	function r20493_make_url_clickable_cb($matches) {
373
		$url = $matches[2];
374
375
		if ( ')' == $matches[3] && strpos( $url, '(' ) ) {
376
			// If the trailing character is a closing parethesis, and the URL has an opening parenthesis in it, add the closing parenthesis to the URL.
377
			// Then we can let the parenthesis balancer do its thing below.
378
			$url .= $matches[3];
379
			$suffix = '';
380
		} else {
381
			$suffix = $matches[3];
382
		}
383
384
		// Include parentheses in the URL only if paired
385
		while ( substr_count( $url, '(' ) < substr_count( $url, ')' ) ) {
386
			$suffix = strrchr( $url, ')' ) . $suffix;
387
			$url = substr( $url, 0, strrpos( $url, ')' ) );
388
		}
389
390
		$url = esc_url($url);
391
		if ( empty($url) )
392
			return $matches[0];
393
394
		return $matches[1] . "<a href=\"$url\" rel=\"nofollow\">$url</a>" . $suffix;
395
	}
396
397
	function r20493_split_str_by_whitespace( $string, $goal ) {
398
		$chunks = array();
399
400
		$string_nullspace = strtr( $string, "\r\n\t\v\f ", "\000\000\000\000\000\000" );
401
402
		while ( $goal < strlen( $string_nullspace ) ) {
403
			$pos = strrpos( substr( $string_nullspace, 0, $goal + 1 ), "\000" );
404
405
			if ( false === $pos ) {
406
				$pos = strpos( $string_nullspace, "\000", $goal + 1 );
407
				if ( false === $pos ) {
408
					break;
409
				}
410
			}
411
412
			$chunks[] = substr( $string, 0, $pos + 1 );
413
			$string = substr( $string, $pos + 1 );
414
			$string_nullspace = substr( $string_nullspace, $pos + 1 );
415
		}
416
417
		if ( $string ) {
418
			$chunks[] = $string;
419
		}
420
421
		return $chunks;
422
	}
423
424
	function r16625( $query ) {
425
		// Hotfixes: http://core.trac.wordpress.org/changeset/16625
426
427
		// Punt as fast as possible if this isn't an UPDATE
428
		if ( substr( $query, 0, 6 ) != "UPDATE" )
429
			return $query;
430
		global $wpdb;
431
432
		// Determine what the prefix of the bad query would look like and punt if this query doesn't match
433
		$badstring = "UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, '";
434
		if ( substr( $query, 0, strlen( $badstring ) ) != $badstring )
435
			return $query;
436
437
		// Pull the post_id which is the last thing in the origin query, after a space, no quotes
438
		$query_parts = explode( " ", $query );
439
		$post_id = array_pop( $query_parts );
440
441
		// Chop off the beginning and end of the original query to get our unsanitized $tb_ping
442
		$tb_ping = substr(
443
			$query,
444
			strlen( $badstring ),
445
			(
446
				strlen( $query ) - (
447
					strlen( $badstring ) + strlen( sprintf( "', '')) WHERE ID = %d", $post_id ) )
448
				)
449
			)
450
		);
451
452
		// Return the fixed query
453
		return $wpdb->prepare( "UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, %s, '')) WHERE ID = %d", $tb_ping, $post_id );
454
	}
455
456
	function filter_xmlrpc_methods( $xmlrpc_method ) {
457
		// Hotfixes: http://core.trac.wordpress.org/changeset/16803
458
		global $wp_xmlrpc_server;
459
		// Pretend that we are an xmlrpc method, freshly called
460
		$args = $wp_xmlrpc_server->message->params;
461
		$error_code = 401;
462
		switch( $xmlrpc_method ) {
463
				case 'metaWeblog.newPost':
464
						$content_struct = $args[3];
465
						$publish = isset( $args[4] ) ? $args[4] : 0;
466
						if ( !empty( $content_struct['post_type'] ) ) {
467
								if ( $content_struct['post_type'] == 'page' ) {
468
										if ( $publish || 'publish' == $content_struct['page_status'] )
469
												$cap  = 'publish_pages';
470
										else
471
												$cap = 'edit_pages';
472
										$error_message = __( 'Sorry, you are not allowed to publish pages on this site.' );
473 View Code Duplication
								} elseif ( $content_struct['post_type'] == 'post' ) {
474
										if ( $publish || 'publish' == $content_struct['post_status'] )
475
												$cap  = 'publish_posts';
476
										else
477
												$cap = 'edit_posts';
478
										$error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );
479
								} else {
480
										$error_message = __( 'Invalid post type.' );
481
								}
482 View Code Duplication
						} else {
483
								if ( $publish || 'publish' == $content_struct['post_status'] )
484
										$cap  = 'publish_posts';
485
								else
486
										$cap = 'edit_posts';
487
								$error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );
488
						}
489
						if ( current_user_can( $cap ) )
0 ignored issues
show
Bug introduced by
The variable $cap does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
490
								return true;
491
						break;
492
				case 'metaWeblog.editPost':
493
						$post_ID = (int) $args[0];
0 ignored issues
show
Unused Code introduced by
$post_ID is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
494
						$content_struct = $args[3];
495
						$publish = $args[4] || ( isset( $content_struct['post_status'] ) && in_array( $content_struct['post_status'], array( 'publish', 'private' ) ) );
496
						$cap = ( $publish ) ? 'publish_posts' : 'edit_posts';
497
						$error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );
498
						if ( !empty( $content_struct['post_type'] ) ) {
499
							if ( $content_struct['post_type'] == 'page' ) {
500
								$error_message = __( 'Sorry, you are not allowed to publish pages on this site.' );
501
							} elseif ( $content_struct['post_type'] == 'post' ) {
502
								$error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );
503
							} else {
504
								$error_message = __( 'Invalid post type.' );
505
							}
506
						}
507
						if ( current_user_can( $cap ) )
508
								return true;
509
						break;
510
				case 'mt.publishPost':
511
						$post_ID = (int) $args[0];
512
						if ( current_user_can( 'publish_posts' ) && current_user_can( 'edit_post', $post_ID ) )
513
								return true;
514
						$error_message = __( 'Sorry, you cannot edit this post.' );
515
						break;
516
				case 'blogger.deletePost':
517
						$post_ID = (int) $args[1];
518
						if ( current_user_can( 'delete_post', $post_ID ) )
519
								return true;
520
						$error_message = __( 'Sorry, you do not have the right to delete this post.' );
521
						break;
522
				case 'wp.getPageStatusList':
523
						if ( current_user_can( 'edit_pages' ) )
524
								return true;
525
						$error_code = 403;
526
						$error_message = __( 'You are not allowed access to details about this site.' );
527
						break;
528
				case 'wp.deleteComment':
529
				case 'wp.editComment':
530
						$comment_ID = (int) $args[3];
531
						if ( !$comment = get_comment( $comment_ID ) )
532
								return true; // This will be handled in the calling function explicitly
533
						if ( current_user_can( 'edit_post', $comment->comment_post_ID ) )
534
								return true;
535
						$error_code = 403;
536
						$error_message = __( 'You are not allowed to moderate comments on this site.' );
537
						break;
538
				default:
539
						return true;
540
		}
541
		// If we are here then this was a handlable xmlrpc call and the capability checks above all failed
542
		// ( otherwise they would have returned to the do_action from the switch statement above ) so it's
543
		// time to exit with whatever error we've determined is the problem (thus short circuiting the
544
		// original XMLRPC method call, and enforcing the above capability checks -- with an ax.  We'll
545
		// mimic the behavior from the end of IXR_Server::serve()
546
		$r = new IXR_Error( $error_code, $error_message );
547
		$resultxml = $r->getXml();
548
		$xml = <<<EOD
549
<methodResponse>
550
  <params>
551
	<param>
552
	  <value>
553
		$resultxml
554
	  </value>
555
	</param>
556
  </params>
557
</methodResponse>
558
EOD;
559
		$wp_xmlrpc_server->output( $xml );
560
		// For good measure...
561
		die();
562
	}
563
564
	function r17172_esc_url( $url, $original_url, $_context ) {
565
		$url = $original_url;
566
567
		if ( '' == $url )
568
			return $url;
569
		$url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\\x80-\\xff]|i', '', $url);
570
		$strip = array('%0d', '%0a', '%0D', '%0A');
571
		$url = _deep_replace($strip, $url);
572
		$url = str_replace(';//', '://', $url);
573
		/* If the URL doesn't appear to contain a scheme, we
574
		 * presume it needs http:// appended (unless a relative
575
		 * link starting with /, # or ? or a php file).
576
		 */
577
		if ( strpos($url, ':') === false && ! in_array( $url[0], array( '/', '#', '?' ) ) &&
578
			! preg_match('/^[a-z0-9-]+?\.php/i', $url) )
579
			$url = 'http://' . $url;
580
581
		// Replace ampersands and single quotes only when displaying.
582
		if ( 'display' == $_context ) {
583
			$url = wp_kses_normalize_entities( $url );
584
			$url = str_replace( '&amp;', '&#038;', $url );
585
			$url = str_replace( "'", '&#039;', $url );
586
		}
587
588
		$protocols = array ('http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet', 'mms', 'rtsp', 'svn');
589
		if ( VaultPress_kses::wp_kses_bad_protocol( $url, $protocols ) != $url )
590
			return '';
591
		return $url;
592
	}
593
594
	// http://core.trac.wordpress.org/changeset/17172
595
	// http://core.trac.wordpress.org/changeset/20541
596
	function r17172_wp_kses( $string, $html, $protocols ) {
597
		return VaultPress_kses::wp_kses( $string, $html, $protocols );
598
	}
599
600
	// http://core.trac.wordpress.org/changeset/17990
601
	function r17990( $filename ) {
602
		$parts = explode('.', $filename);
603
		$filename = array_shift($parts);
604
		$extension = array_pop($parts);
605
		$mimes = get_allowed_mime_types();
606
607
		// Loop over any intermediate extensions.  Munge them with a trailing underscore if they are a 2 - 5 character
608
		// long alpha string not in the extension whitelist.
609
		foreach ( (array) $parts as $part) {
610
			$filename .= '.' . $part;
611
612
			if ( preg_match("/^[a-zA-Z]{2,5}\d?$/", $part) ) {
613
				$allowed = false;
614
				foreach ( $mimes as $ext_preg => $mime_match ) {
615
					$ext_preg = '!^(' . $ext_preg . ')$!i';
616
					if ( preg_match( $ext_preg, $part ) ) {
617
						$allowed = true;
618
						break;
619
					}
620
				}
621
				if ( !$allowed )
622
					$filename .= '_';
623
			}
624
		}
625
		$filename .= '.' . $extension;
626
		return $filename;
627
	}
628
629
	/*
630
	 * Hotfixes: http://core.trac.wordpress.org/changeset/18368
631
	 */
632
	function r18368( $post, $raw_post ) {
633
		if ( isset( $post['filter'] ) || isset ( $raw_post['filter'] ) ) {
634
			unset( $post['filter'], $raw_post['filter'] ); // to ensure the post is properly sanitized
635
			$post = sanitize_post($post, 'db');
636
		}
637
		if ( empty( $post['ID'] ) )
638
			unset( $post['ID'] ); // sanitize_post
639
		unset( $post['filter'] ); // sanitize_post
640
		return $post;
641
	}
642
643
	/**
644
	 * Protect WordPress internal metadata.
645
	 *
646
	 * The post data is passed as a parameter to (unit) test this method.
647
	 * @param $post_data|array the $_POST array.
648
	 */
649
	function r17994( &$post_data ) {
650
		// Protect admin-ajax add_meta
651
		$metakeyselect = isset( $post_data['metakeyselect'] ) ? stripslashes( trim( $post_data['metakeyselect'] ) ) : '';
652
		$metakeyinput = isset( $post_data['metakeyinput'] ) ? stripslashes( trim( $post_data['metakeyinput'] ) ) : '';
653
654
		if ( ( $metakeyselect && '_' == $metakeyselect[0] ) || ( $metakeyinput && '_' == $metakeyinput[0] ) ) {
655
			unset( $_POST['metakeyselect'], $_POST['metakeyinput'] );
656
		}
657
658
		// Protect admin-ajax update_meta
659
		if ( isset( $post_data['meta'] ) ) {
660
			foreach ( (array)$post_data['meta'] as $mid => $value ) {
661
				$key = stripslashes( $post_data['meta'][$mid]['key'] );
662
				if ( $key && '_' == $key[0] )
663
					unset( $post_data['meta'][$mid] );
664
			}
665
		}
666
	}
667
668
	function r17994_sanitize_mime_type( $mime_type ) {
669
		$sani_mime_type = preg_replace( '/[^\-*.a-zA-Z0-9\/+]/', '', $mime_type );
670
		return apply_filters( 'sanitize_mime_type', $sani_mime_type, $mime_type );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $mime_type.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
671
	}
672
673
	function r17826_send_frame_options_header() {
674
 		@header( 'X-Frame-Options: SAMEORIGIN' );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
675
 	}
676
677
	function r18346_sanitize_admin_email_on_save($value) {
678
		$value = sanitize_email( $value );
679
		if ( !is_email( $value ) ) {
680
			$value = get_option( 'new_admin_email' ); // Resets option to stored value in the case of failed sanitization
681
			if ( function_exists( 'add_settings_error' ) )
682
				add_settings_error( 'new_admin_email', 'invalid_admin_email', __( 'The email address entered did not appear to be a valid email address. Please enter a valid email address.' ) );
683
		}
684
		return $value;
685
	}
686
687
	function r18346_sanitize_admin_email( $value ) {
688
		return sanitize_email( $value ); // Is it enough ?
689
	}
690
691
	function r18346_sanitize_lang_on_save( $value ) {
692
		$value = $this->r18346_sanitize_lang( $value ); // sanitize the new value.
693
		if ( empty( $value ) )
694
			$value = get_option( 'WPLANG' );
695
		return $value;
696
	}
697
698
	function r18346_sanitize_lang( $value ) {
699
		$allowed = apply_filters( 'available_languages', get_available_languages() ); // add a filter to unit test
700
		if ( !empty( $value ) && !in_array( $value, $allowed ) )
701
			return false;
702
		else
703
			return $value;
704
	}
705
706
	// Protect All-in-one SEO AJAX calls from script injection and changes without privileges. Affects versions <= 2.1.5
707
	function protect_aioseo_ajax() {
708
		if ( defined( 'AIOSEOP_VERSION' ) && version_compare( AIOSEOP_VERSION, '2.1.5', '>' ) )
709
			return;
710
711
		if ( ! isset( $_POST['post_id'] ) || ! isset( $_POST['target_meta'] ) )
712
			die();
713
714
		// Ensure the current user has permission to write to the post.
715
		if ( ! current_user_can( 'edit_post', intval( $_POST['post_id'] ) ) )
716
			die();
717
718
		// Limit the fields that can be written to
719
		if ( ! in_array( $_POST['target_meta'], array( 'title', 'description', 'keywords' ) ) )
720
			die();
721
722
		// Strip tags from the metadata value.
723
		$_POST['new_meta'] = strip_tags( $_POST['new_meta'] );
724
	}
725
726
	// Protect The MailPoet plugin (wysija-newsletters) from remote file upload. Affects versions <= 2.6.6
727
	function protect_wysija_newsletters_verify_capability() {
728
		if ( !class_exists( 'WYSIJA_object' ) )
729
			return true;
730
		if ( version_compare( WYSIJA::get_version(), '2.6.7', '>=' ) )
731
			return true;
732
		if ( !defined( 'DOING_AJAX' ) && !defined( 'WYSIJA_ITF' ) )
733
			return true;
734
        if( isset( $_REQUEST['page'] ) && substr( $_REQUEST['page'] ,0 ,7 ) == 'wysija_' ){
735
736
            switch( $_REQUEST['page'] ){
737
                case 'wysija_campaigns':
738
                    $role_needed = 'wysija_newsletters';
739
                    break;
740
                case 'wysija_subscribers':
741
                    $role_needed = 'wysija_subscribers';
742
                    break;
743
                case 'wysija_config':
744
                    $role_needed = 'wysija_config';
745
                    break;
746
                case 'wysija_statistics':
747
                    $role_needed = 'wysija_stats_dashboard';
748
                    break;
749
                default:
750
                    $role_needed = 'switch_themes';
751
            }
752
753
            if( current_user_can( $role_needed ) ){
754
                return true;
755
            } else{
756
                die( 'You are not allowed here.' );
757
            }
758
759
        }else{
760
            // this is not a wysija interface/action we can let it pass
761
            return true;
762
        }
763
    }
764
765
	// Protect the Revolution Slider plugin (revslider) from local file inclusion. Affects versions < 4.2
766
	function protect_revslider_lfi() {
767
		if ( isset( $_GET['action'] ) && 'revslider_show_image' == $_GET['action'] ) {
768
			$img = '';
769
			if ( isset( $_GET['img'] ) )
770
				$img = $_GET['img'];
771
			if ( is_numeric( $img ) )
772
				return;
773
			$validate = validate_file( $img );
774
			if ( 0 !== $validate )
775
				die( 'invalid file' );
776
			if ( !file_exists( $img ) )
777
				die( 'file does not exist' );
778
		}
779
	}
780
781
	// Protect WooCommerce 2.0.20 - 2.3.10 from PayPal IPN object injection attack.
782
	function protect_woocommerce_paypal_object_injection() {
783
		global $woocommerce;
784
		if ( ! isset( $woocommerce ) )
785
			return;
786
787
		$wc_version = $woocommerce->version;
788
		if ( version_compare( $wc_version, '2.0.20', '<' ) || version_compare( $wc_version, '2.3.11', '>=' ) )
789
			return;
790
791
		if ( isset( $_REQUEST['paypalListener'] ) ) {
792
			$check_fields = array( 'custom', 'cm' );
793
			foreach ( $check_fields as $field ) {
794
				if ( isset( $_REQUEST[ $field ] ) && preg_match( '/[CO]:\+?[0-9]+:/', $_REQUEST[ $field ] ) ) {
795
					die();
796
				}
797
			}
798
		}
799
	}
800
801
	// Protect WordPress 3.1.0 -> WordPress 4.3.0 from code injection via user email
802
	function patch_user_email( $value, $user_id, $context ) {
803
		if ( 'display' === $context && class_exists( 'WP_Users_List_Table' ) ) {
804
			return esc_attr( $value );
805
		}
806
807
		return $value;
808
	}
809
810
	// Protect WordPress < 4.3.1 from evil tags inside caption shortcodes
811
	function filtered_caption_shortcode( $attr, $content = null ) {
812
		if ( isset( $attr['caption'] ) && strpos( $attr['caption'], '<' ) !== false ) {
813
			$attr['caption'] = wp_kses( $attr['caption'], 'post' );
814
		}
815
816
		return img_caption_shortcode( $attr, $content );
817
	}
818
819
	// Protect Akismet < 3.1.5 from stored XSS in admin page
820
	function protect_akismet_comment_xss() {
821
		remove_filter( 'comment_text', array( 'Akismet_Admin', 'text_add_link_class' ) );
822
	}
823
}
824
825
global $wp_version;
826
$needs_class_fix = version_compare( $wp_version, '3.1', '>=') && version_compare( $wp_version, '3.1.3', '<' );
827
if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST && $needs_class_fix ) {
828
	include_once( ABSPATH . WPINC . '/class-IXR.php' );
829
	include_once( ABSPATH . WPINC . '/class-wp-xmlrpc-server.php' );
830
831
	class VaultPress_XMLRPC_Server_r17994 extends wp_xmlrpc_server {
832
		function set_custom_fields( $post_id, $fields ) {
833
			foreach( $fields as $k => $meta ) {
834
				$key = stripslashes( trim( $meta['key'] ) );
835
				if ( $key && '_' ==  $key[0] )
836
					unset( $fields[$k] );
837
			}
838
			parent::set_custom_fields( $post_id, $fields );
839
		}
840
	}
841
842
	function r17994_xmlrpc_server() {
843
		return 'VaultPress_XMLRPC_Server_r17994';
844
	}
845
}
846
847
class VaultPress_kses {
848
	static function wp_kses($string, $allowed_html, $allowed_protocols = array ()) {
849
		$string = wp_kses_no_null($string);
850
		$string = wp_kses_js_entities($string);
851
		$string = wp_kses_normalize_entities($string);
852
		return VaultPress_kses::wp_kses_split($string, $allowed_html, $allowed_protocols);
853
	}
854
855
	static function wp_kses_split($string, $allowed_html, $allowed_protocols) {
856
		global $pass_allowed_html, $pass_allowed_protocols;
857
		$pass_allowed_html = $allowed_html;
858
		$pass_allowed_protocols = $allowed_protocols;
859
		return preg_replace_callback( '%(<!--.*?(-->|$))|(<[^>]*(>|$)|>)%', 'VaultPress_kses::_vp_kses_split_callback', $string );
860
	}
861
862
	static function _vp_kses_split_callback( $match ) {
863
		global $pass_allowed_html, $pass_allowed_protocols;
864
		return VaultPress_kses::wp_kses_split2( $match[0], $pass_allowed_html, $pass_allowed_protocols );
865
	}
866
867
	static function wp_kses_split2($string, $allowed_html, $allowed_protocols) {
868
		$string = wp_kses_stripslashes($string);
869
870
		if (substr($string, 0, 1) != '<')
871
			return '&gt;';
872
		# It matched a ">" character
873
874
		if ( '<!--' == substr( $string, 0, 4 ) ) {
875
			$string = str_replace( array('<!--', '-->'), '', $string );
876
			while ( $string != ($newstring = VaultPress_kses::wp_kses($string, $allowed_html, $allowed_protocols)) )
877
				$string = $newstring;
878
			if ( $string == '' )
879
				return '';
880
			// prevent multiple dashes in comments
881
			$string = preg_replace('/--+/', '-', $string);
882
			// prevent three dashes closing a comment
883
			$string = preg_replace('/-$/', '', $string);
884
			return "<!--{$string}-->";
885
		}
886
		# Allow HTML comments
887
888
		if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $string, $matches))
889
			return '';
890
		# It's seriously malformed
891
892
		$slash = trim($matches[1]);
893
		$elem = $matches[2];
894
		$attrlist = $matches[3];
895
896
		if ( ! isset($allowed_html[strtolower($elem)]) )
897
			return '';
898
		# They are using a not allowed HTML element
899
900
		if ($slash != '')
901
			return "</$elem>";
902
		# No attributes are allowed for closing elements
903
904
		return VaultPress_kses::wp_kses_attr( $elem, $attrlist, $allowed_html, $allowed_protocols );
905
	}
906
907
	static function wp_kses_attr($element, $attr, $allowed_html, $allowed_protocols) {
908
		# Is there a closing XHTML slash at the end of the attributes?
909
910
		$xhtml_slash = '';
911
		if (preg_match('%\s*/\s*$%', $attr))
912
			$xhtml_slash = ' /';
913
914
		# Are any attributes allowed at all for this element?
915
		if ( ! isset($allowed_html[strtolower($element)]) || count($allowed_html[strtolower($element)]) == 0 )
916
			return "<$element$xhtml_slash>";
917
918
		# Split it
919
		$attrarr = VaultPress_kses::wp_kses_hair($attr, $allowed_protocols);
920
921
		# Go through $attrarr, and save the allowed attributes for this element
922
		# in $attr2
923
		$attr2 = '';
924
925
		$allowed_attr = $allowed_html[strtolower($element)];
926
		foreach ($attrarr as $arreach) {
927
			if ( ! isset( $allowed_attr[strtolower($arreach['name'])] ) )
928
				continue; # the attribute is not allowed
929
930
			$current = $allowed_attr[strtolower($arreach['name'])];
931
			if ( $current == '' )
932
				continue; # the attribute is not allowed
933
934
			if ( strtolower( $arreach['name'] ) == 'style' ) {
935
				$orig_value = $arreach['value'];
936
				$value = safecss_filter_attr( $orig_value );
937
938
				if ( empty( $value ) )
939
					continue;
940
941
				$arreach['value'] = $value;
942
				$arreach['whole'] = str_replace( $orig_value, $value, $arreach['whole'] );
943
			}
944
945
			if ( ! is_array($current) ) {
946
				$attr2 .= ' '.$arreach['whole'];
947
			# there are no checks
948
949
			} else {
950
				# there are some checks
951
				$ok = true;
952
				foreach ($current as $currkey => $currval) {
953
					if ( ! wp_kses_check_attr_val($arreach['value'], $arreach['vless'], $currkey, $currval) ) {
954
						$ok = false;
955
						break;
956
					}
957
				}
958
959
				if ( $ok )
960
					$attr2 .= ' '.$arreach['whole']; # it passed them
961
			} # if !is_array($current)
962
		} # foreach
963
964
		# Remove any "<" or ">" characters
965
		$attr2 = preg_replace('/[<>]/', '', $attr2);
966
967
		return "<$element$attr2$xhtml_slash>";
968
	}
969
970
	static function wp_kses_hair($attr, $allowed_protocols) {
971
		$attrarr = array ();
972
		$mode = 0;
973
		$attrname = '';
974
		$uris = array('xmlns', 'profile', 'href', 'src', 'cite', 'classid', 'codebase', 'data', 'usemap', 'longdesc', 'action');
975
976
		# Loop through the whole attribute list
977
978
		while (strlen($attr) != 0) {
979
			$working = 0; # Was the last operation successful?
980
981
			switch ($mode) {
982
				case 0 : # attribute name, href for instance
983
984
					if (preg_match('/^([-a-zA-Z]+)/', $attr, $match)) {
985
						$attrname = $match[1];
986
						$working = $mode = 1;
987
						$attr = preg_replace('/^[-a-zA-Z]+/', '', $attr);
988
					}
989
990
					break;
991
992
				case 1 : # equals sign or valueless ("selected")
993
994
					if (preg_match('/^\s*=\s*/', $attr)) # equals sign
995
						{
996
						$working = 1;
997
						$mode = 2;
998
						$attr = preg_replace('/^\s*=\s*/', '', $attr);
999
						break;
1000
					}
1001
1002
					if (preg_match('/^\s+/', $attr)) # valueless
1003
						{
1004
						$working = 1;
1005
						$mode = 0;
1006
						if(false === array_key_exists($attrname, $attrarr)) {
1007
							$attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
1008
						}
1009
						$attr = preg_replace('/^\s+/', '', $attr);
1010
					}
1011
1012
					break;
1013
1014
				case 2 : # attribute value, a URL after href= for instance
1015
1016 View Code Duplication
					if (preg_match('%^"([^"]*)"(\s+|/?$)%', $attr, $match))
1017
						# "value"
1018
						{
1019
						$thisval = $match[1];
1020
						if ( in_array(strtolower($attrname), $uris) )
1021
							$thisval = VaultPress_kses::wp_kses_bad_protocol($thisval, $allowed_protocols);
1022
1023
						if(false === array_key_exists($attrname, $attrarr)) {
1024
							$attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
1025
						}
1026
						$working = 1;
1027
						$mode = 0;
1028
						$attr = preg_replace('/^"[^"]*"(\s+|$)/', '', $attr);
1029
						break;
1030
					}
1031
1032 View Code Duplication
					if (preg_match("%^'([^']*)'(\s+|/?$)%", $attr, $match))
1033
						# 'value'
1034
						{
1035
						$thisval = $match[1];
1036
						if ( in_array(strtolower($attrname), $uris) )
1037
							$thisval = VaultPress_kses::wp_kses_bad_protocol($thisval, $allowed_protocols);
1038
1039
						if(false === array_key_exists($attrname, $attrarr)) {
1040
							$attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname='$thisval'", 'vless' => 'n');
1041
						}
1042
						$working = 1;
1043
						$mode = 0;
1044
						$attr = preg_replace("/^'[^']*'(\s+|$)/", '', $attr);
1045
						break;
1046
					}
1047
1048 View Code Duplication
					if (preg_match("%^([^\s\"']+)(\s+|/?$)%", $attr, $match))
1049
						# value
1050
						{
1051
						$thisval = $match[1];
1052
						if ( in_array(strtolower($attrname), $uris) )
1053
							$thisval = VaultPress_kses::wp_kses_bad_protocol($thisval, $allowed_protocols);
1054
1055
						if(false === array_key_exists($attrname, $attrarr)) {
1056
							$attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
1057
						}
1058
						# We add quotes to conform to W3C's HTML spec.
1059
						$working = 1;
1060
						$mode = 0;
1061
						$attr = preg_replace("%^[^\s\"']+(\s+|$)%", '', $attr);
1062
					}
1063
1064
					break;
1065
			} # switch
1066
1067
			if ($working == 0) # not well formed, remove and try again
1068
			{
1069
				$attr = wp_kses_html_error($attr);
1070
				$mode = 0;
1071
			}
1072
		} # while
1073
1074
		if ($mode == 1 && false === array_key_exists($attrname, $attrarr))
1075
			# special case, for when the attribute list ends with a valueless
1076
			# attribute like "selected"
1077
			$attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
1078
1079
		return $attrarr;
1080
	}
1081
1082
	static function wp_kses_bad_protocol($string, $allowed_protocols) {
1083
		$string = wp_kses_no_null($string);
1084
		$iterations = 0;
1085
1086
		do {
1087
			$original_string = $string;
1088
			$string = VaultPress_kses::wp_kses_bad_protocol_once($string, $allowed_protocols);
1089
		} while ( $original_string != $string && ++$iterations < 6 );
1090
1091
		if ( $original_string != $string )
1092
			return '';
1093
1094
		return $string;
1095
	}
1096
1097
	static function wp_kses_bad_protocol_once($string, $allowed_protocols, $count = 1) {
1098
		$string2 = preg_split( '/:|&#0*58;|&#x0*3a;/i', $string, 2 );
1099
		if ( isset($string2[1]) && ! preg_match('%/\?%', $string2[0]) ) {
1100
			$string = trim( $string2[1] );
1101
			$protocol = VaultPress_kses::wp_kses_bad_protocol_once2( $string2[0], $allowed_protocols );
1102
			if ( 'feed:' == $protocol ) {
1103
				if ( $count > 2 )
1104
					return '';
1105
				$string = VaultPress_kses::wp_kses_bad_protocol_once( $string, $allowed_protocols, ++$count );
1106
				if ( empty( $string ) )
1107
					return $string;
1108
			}
1109
			$string = $protocol . $string;
1110
		}
1111
1112
		return $string;
1113
	}
1114
1115
	static function wp_kses_bad_protocol_once2( $string, $allowed_protocols ) {
1116
		$string2 = wp_kses_decode_entities($string);
1117
		$string2 = preg_replace('/\s/', '', $string2);
1118
		$string2 = wp_kses_no_null($string2);
1119
		$string2 = strtolower($string2);
1120
1121
		$allowed = false;
1122
		foreach ( (array) $allowed_protocols as $one_protocol ) {
1123
			if ( strtolower( $one_protocol ) == $string2 ) {
1124
				$allowed = true;
1125
				break;
1126
			}
1127
		}
1128
1129
		if ($allowed)
1130
			return "$string2:";
1131
		else
1132
			return '';
1133
	}
1134
1135
}
1136
1137
if ( !function_exists( 'get_available_languages' ) ) {
1138
	function get_available_languages( $dir = null ) {
1139
		$languages = array();
1140
		foreach( glob( ( is_null( $dir) ? WP_LANG_DIR : $dir ) . '/*.mo' ) as $lang_file )
1141
			if ( false === strpos( $lang_file, 'continents-cities' ) )
1142
				$languages[] = basename($lang_file, '.mo');
1143
		return $languages;
1144
	}
1145
}
1146