Wordpress 3.6
[autoinstalls/wordpress.git] / wp-includes / functions.php
1 <?php
2 /**
3  * Main WordPress API
4  *
5  * @package WordPress
6  */
7
8 require( ABSPATH . WPINC . '/option.php' );
9
10 /**
11  * Converts given date string into a different format.
12  *
13  * $format should be either a PHP date format string, e.g. 'U' for a Unix
14  * timestamp, or 'G' for a Unix timestamp assuming that $date is GMT.
15  *
16  * If $translate is true then the given date and format string will
17  * be passed to date_i18n() for translation.
18  *
19  * @since 0.71
20  *
21  * @param string $format Format of the date to return.
22  * @param string $date Date string to convert.
23  * @param bool $translate Whether the return date should be translated. Default is true.
24  * @return string|int Formatted date string, or Unix timestamp.
25  */
26 function mysql2date( $format, $date, $translate = true ) {
27         if ( empty( $date ) )
28                 return false;
29
30         if ( 'G' == $format )
31                 return strtotime( $date . ' +0000' );
32
33         $i = strtotime( $date );
34
35         if ( 'U' == $format )
36                 return $i;
37
38         if ( $translate )
39                 return date_i18n( $format, $i );
40         else
41                 return date( $format, $i );
42 }
43
44 /**
45  * Retrieve the current time based on specified type.
46  *
47  * The 'mysql' type will return the time in the format for MySQL DATETIME field.
48  * The 'timestamp' type will return the current timestamp.
49  *
50  * If $gmt is set to either '1' or 'true', then both types will use GMT time.
51  * if $gmt is false, the output is adjusted with the GMT offset in the WordPress option.
52  *
53  * @since 1.0.0
54  *
55  * @param string $type Either 'mysql' or 'timestamp'.
56  * @param int|bool $gmt Optional. Whether to use GMT timezone. Default is false.
57  * @return int|string String if $type is 'gmt', int if $type is 'timestamp'.
58  */
59 function current_time( $type, $gmt = 0 ) {
60         switch ( $type ) {
61                 case 'mysql':
62                         return ( $gmt ) ? gmdate( 'Y-m-d H:i:s' ) : gmdate( 'Y-m-d H:i:s', ( time() + ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ) ) );
63                         break;
64                 case 'timestamp':
65                         return ( $gmt ) ? time() : time() + ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS );
66                         break;
67         }
68 }
69
70 /**
71  * Retrieve the date in localized format, based on timestamp.
72  *
73  * If the locale specifies the locale month and weekday, then the locale will
74  * take over the format for the date. If it isn't, then the date format string
75  * will be used instead.
76  *
77  * @since 0.71
78  *
79  * @param string $dateformatstring Format to display the date.
80  * @param int $unixtimestamp Optional. Unix timestamp.
81  * @param bool $gmt Optional, default is false. Whether to convert to GMT for time.
82  * @return string The date, translated if locale specifies it.
83  */
84 function date_i18n( $dateformatstring, $unixtimestamp = false, $gmt = false ) {
85         global $wp_locale;
86         $i = $unixtimestamp;
87
88         if ( false === $i ) {
89                 if ( ! $gmt )
90                         $i = current_time( 'timestamp' );
91                 else
92                         $i = time();
93                 // we should not let date() interfere with our
94                 // specially computed timestamp
95                 $gmt = true;
96         }
97
98         // store original value for language with untypical grammars
99         // see http://core.trac.wordpress.org/ticket/9396
100         $req_format = $dateformatstring;
101
102         $datefunc = $gmt? 'gmdate' : 'date';
103
104         if ( ( !empty( $wp_locale->month ) ) && ( !empty( $wp_locale->weekday ) ) ) {
105                 $datemonth = $wp_locale->get_month( $datefunc( 'm', $i ) );
106                 $datemonth_abbrev = $wp_locale->get_month_abbrev( $datemonth );
107                 $dateweekday = $wp_locale->get_weekday( $datefunc( 'w', $i ) );
108                 $dateweekday_abbrev = $wp_locale->get_weekday_abbrev( $dateweekday );
109                 $datemeridiem = $wp_locale->get_meridiem( $datefunc( 'a', $i ) );
110                 $datemeridiem_capital = $wp_locale->get_meridiem( $datefunc( 'A', $i ) );
111                 $dateformatstring = ' '.$dateformatstring;
112                 $dateformatstring = preg_replace( "/([^\\\])D/", "\\1" . backslashit( $dateweekday_abbrev ), $dateformatstring );
113                 $dateformatstring = preg_replace( "/([^\\\])F/", "\\1" . backslashit( $datemonth ), $dateformatstring );
114                 $dateformatstring = preg_replace( "/([^\\\])l/", "\\1" . backslashit( $dateweekday ), $dateformatstring );
115                 $dateformatstring = preg_replace( "/([^\\\])M/", "\\1" . backslashit( $datemonth_abbrev ), $dateformatstring );
116                 $dateformatstring = preg_replace( "/([^\\\])a/", "\\1" . backslashit( $datemeridiem ), $dateformatstring );
117                 $dateformatstring = preg_replace( "/([^\\\])A/", "\\1" . backslashit( $datemeridiem_capital ), $dateformatstring );
118
119                 $dateformatstring = substr( $dateformatstring, 1, strlen( $dateformatstring ) -1 );
120         }
121         $timezone_formats = array( 'P', 'I', 'O', 'T', 'Z', 'e' );
122         $timezone_formats_re = implode( '|', $timezone_formats );
123         if ( preg_match( "/$timezone_formats_re/", $dateformatstring ) ) {
124                 $timezone_string = get_option( 'timezone_string' );
125                 if ( $timezone_string ) {
126                         $timezone_object = timezone_open( $timezone_string );
127                         $date_object = date_create( null, $timezone_object );
128                         foreach( $timezone_formats as $timezone_format ) {
129                                 if ( false !== strpos( $dateformatstring, $timezone_format ) ) {
130                                         $formatted = date_format( $date_object, $timezone_format );
131                                         $dateformatstring = ' '.$dateformatstring;
132                                         $dateformatstring = preg_replace( "/([^\\\])$timezone_format/", "\\1" . backslashit( $formatted ), $dateformatstring );
133                                         $dateformatstring = substr( $dateformatstring, 1, strlen( $dateformatstring ) -1 );
134                                 }
135                         }
136                 }
137         }
138         $j = @$datefunc( $dateformatstring, $i );
139         // allow plugins to redo this entirely for languages with untypical grammars
140         $j = apply_filters('date_i18n', $j, $req_format, $i, $gmt);
141         return $j;
142 }
143
144 /**
145  * Convert integer number to format based on the locale.
146  *
147  * @since 2.3.0
148  *
149  * @param int $number The number to convert based on locale.
150  * @param int $decimals Precision of the number of decimal places.
151  * @return string Converted number in string format.
152  */
153 function number_format_i18n( $number, $decimals = 0 ) {
154         global $wp_locale;
155         $formatted = number_format( $number, absint( $decimals ), $wp_locale->number_format['decimal_point'], $wp_locale->number_format['thousands_sep'] );
156         return apply_filters( 'number_format_i18n', $formatted );
157 }
158
159 /**
160  * Convert number of bytes largest unit bytes will fit into.
161  *
162  * It is easier to read 1kB than 1024 bytes and 1MB than 1048576 bytes. Converts
163  * number of bytes to human readable number by taking the number of that unit
164  * that the bytes will go into it. Supports TB value.
165  *
166  * Please note that integers in PHP are limited to 32 bits, unless they are on
167  * 64 bit architecture, then they have 64 bit size. If you need to place the
168  * larger size then what PHP integer type will hold, then use a string. It will
169  * be converted to a double, which should always have 64 bit length.
170  *
171  * Technically the correct unit names for powers of 1024 are KiB, MiB etc.
172  * @link http://en.wikipedia.org/wiki/Byte
173  *
174  * @since 2.3.0
175  *
176  * @param int|string $bytes Number of bytes. Note max integer size for integers.
177  * @param int $decimals Precision of number of decimal places. Deprecated.
178  * @return bool|string False on failure. Number string on success.
179  */
180 function size_format( $bytes, $decimals = 0 ) {
181         $quant = array(
182                 // ========================= Origin ====
183                 'TB' => 1099511627776,  // pow( 1024, 4)
184                 'GB' => 1073741824,     // pow( 1024, 3)
185                 'MB' => 1048576,        // pow( 1024, 2)
186                 'kB' => 1024,           // pow( 1024, 1)
187                 'B ' => 1,              // pow( 1024, 0)
188         );
189         foreach ( $quant as $unit => $mag )
190                 if ( doubleval($bytes) >= $mag )
191                         return number_format_i18n( $bytes / $mag, $decimals ) . ' ' . $unit;
192
193         return false;
194 }
195
196 /**
197  * Get the week start and end from the datetime or date string from mysql.
198  *
199  * @since 0.71
200  *
201  * @param string $mysqlstring Date or datetime field type from mysql.
202  * @param int $start_of_week Optional. Start of the week as an integer.
203  * @return array Keys are 'start' and 'end'.
204  */
205 function get_weekstartend( $mysqlstring, $start_of_week = '' ) {
206         $my = substr( $mysqlstring, 0, 4 ); // Mysql string Year
207         $mm = substr( $mysqlstring, 8, 2 ); // Mysql string Month
208         $md = substr( $mysqlstring, 5, 2 ); // Mysql string day
209         $day = mktime( 0, 0, 0, $md, $mm, $my ); // The timestamp for mysqlstring day.
210         $weekday = date( 'w', $day ); // The day of the week from the timestamp
211         if ( !is_numeric($start_of_week) )
212                 $start_of_week = get_option( 'start_of_week' );
213
214         if ( $weekday < $start_of_week )
215                 $weekday += 7;
216
217         $start = $day - DAY_IN_SECONDS * ( $weekday - $start_of_week ); // The most recent week start day on or before $day
218         $end = $start + 7 * DAY_IN_SECONDS - 1; // $start + 7 days - 1 second
219         return compact( 'start', 'end' );
220 }
221
222 /**
223  * Unserialize value only if it was serialized.
224  *
225  * @since 2.0.0
226  *
227  * @param string $original Maybe unserialized original, if is needed.
228  * @return mixed Unserialized data can be any type.
229  */
230 function maybe_unserialize( $original ) {
231         if ( is_serialized( $original ) ) // don't attempt to unserialize data that wasn't serialized going in
232                 return @unserialize( $original );
233         return $original;
234 }
235
236 /**
237  * Check value to find if it was serialized.
238  *
239  * If $data is not an string, then returned value will always be false.
240  * Serialized data is always a string.
241  *
242  * @since 2.0.5
243  *
244  * @param mixed $data Value to check to see if was serialized.
245  * @return bool False if not serialized and true if it was.
246  */
247 function is_serialized( $data ) {
248         // if it isn't a string, it isn't serialized
249         if ( ! is_string( $data ) )
250                 return false;
251         $data = trim( $data );
252         if ( 'N;' == $data )
253                 return true;
254         $length = strlen( $data );
255         if ( $length < 4 )
256                 return false;
257         if ( ':' !== $data[1] )
258                 return false;
259         $lastc = $data[$length-1];
260         if ( ';' !== $lastc && '}' !== $lastc )
261                 return false;
262         $token = $data[0];
263         switch ( $token ) {
264                 case 's' :
265                         if ( '"' !== $data[$length-2] )
266                                 return false;
267                 case 'a' :
268                 case 'O' :
269                         return (bool) preg_match( "/^{$token}:[0-9]+:/s", $data );
270                 case 'b' :
271                 case 'i' :
272                 case 'd' :
273                         return (bool) preg_match( "/^{$token}:[0-9.E-]+;\$/", $data );
274         }
275         return false;
276 }
277
278 /**
279  * Check whether serialized data is of string type.
280  *
281  * @since 2.0.5
282  *
283  * @param mixed $data Serialized data
284  * @return bool False if not a serialized string, true if it is.
285  */
286 function is_serialized_string( $data ) {
287         // if it isn't a string, it isn't a serialized string
288         if ( !is_string( $data ) )
289                 return false;
290         $data = trim( $data );
291         $length = strlen( $data );
292         if ( $length < 4 )
293                 return false;
294         elseif ( ':' !== $data[1] )
295                 return false;
296         elseif ( ';' !== $data[$length-1] )
297                 return false;
298         elseif ( $data[0] !== 's' )
299                 return false;
300         elseif ( '"' !== $data[$length-2] )
301                 return false;
302         else
303                 return true;
304 }
305
306 /**
307  * Serialize data, if needed.
308  *
309  * @since 2.0.5
310  *
311  * @param mixed $data Data that might be serialized.
312  * @return mixed A scalar data
313  */
314 function maybe_serialize( $data ) {
315         if ( is_array( $data ) || is_object( $data ) )
316                 return serialize( $data );
317
318         // Double serialization is required for backward compatibility.
319         // See http://core.trac.wordpress.org/ticket/12930
320         if ( is_serialized( $data ) )
321                 return serialize( $data );
322
323         return $data;
324 }
325
326 /**
327  * Retrieve post title from XMLRPC XML.
328  *
329  * If the title element is not part of the XML, then the default post title from
330  * the $post_default_title will be used instead.
331  *
332  * @package WordPress
333  * @subpackage XMLRPC
334  * @since 0.71
335  *
336  * @global string $post_default_title Default XMLRPC post title.
337  *
338  * @param string $content XMLRPC XML Request content
339  * @return string Post title
340  */
341 function xmlrpc_getposttitle( $content ) {
342         global $post_default_title;
343         if ( preg_match( '/<title>(.+?)<\/title>/is', $content, $matchtitle ) ) {
344                 $post_title = $matchtitle[1];
345         } else {
346                 $post_title = $post_default_title;
347         }
348         return $post_title;
349 }
350
351 /**
352  * Retrieve the post category or categories from XMLRPC XML.
353  *
354  * If the category element is not found, then the default post category will be
355  * used. The return type then would be what $post_default_category. If the
356  * category is found, then it will always be an array.
357  *
358  * @package WordPress
359  * @subpackage XMLRPC
360  * @since 0.71
361  *
362  * @global string $post_default_category Default XMLRPC post category.
363  *
364  * @param string $content XMLRPC XML Request content
365  * @return string|array List of categories or category name.
366  */
367 function xmlrpc_getpostcategory( $content ) {
368         global $post_default_category;
369         if ( preg_match( '/<category>(.+?)<\/category>/is', $content, $matchcat ) ) {
370                 $post_category = trim( $matchcat[1], ',' );
371                 $post_category = explode( ',', $post_category );
372         } else {
373                 $post_category = $post_default_category;
374         }
375         return $post_category;
376 }
377
378 /**
379  * XMLRPC XML content without title and category elements.
380  *
381  * @package WordPress
382  * @subpackage XMLRPC
383  * @since 0.71
384  *
385  * @param string $content XMLRPC XML Request content
386  * @return string XMLRPC XML Request content without title and category elements.
387  */
388 function xmlrpc_removepostdata( $content ) {
389         $content = preg_replace( '/<title>(.+?)<\/title>/si', '', $content );
390         $content = preg_replace( '/<category>(.+?)<\/category>/si', '', $content );
391         $content = trim( $content );
392         return $content;
393 }
394
395 /**
396  * Check content for video and audio links to add as enclosures.
397  *
398  * Will not add enclosures that have already been added and will
399  * remove enclosures that are no longer in the post. This is called as
400  * pingbacks and trackbacks.
401  *
402  * @package WordPress
403  * @since 1.5.0
404  *
405  * @uses $wpdb
406  *
407  * @param string $content Post Content
408  * @param int $post_ID Post ID
409  */
410 function do_enclose( $content, $post_ID ) {
411         global $wpdb;
412
413         //TODO: Tidy this ghetto code up and make the debug code optional
414         include_once( ABSPATH . WPINC . '/class-IXR.php' );
415
416         $post_links = array();
417
418         $pung = get_enclosed( $post_ID );
419
420         $ltrs = '\w';
421         $gunk = '/#~:.?+=&%@!\-';
422         $punc = '.:?\-';
423         $any = $ltrs . $gunk . $punc;
424
425         preg_match_all( "{\b http : [$any] +? (?= [$punc] * [^$any] | $)}x", $content, $post_links_temp );
426
427         foreach ( $pung as $link_test ) {
428                 if ( !in_array( $link_test, $post_links_temp[0] ) ) { // link no longer in post
429                         $mids = $wpdb->get_col( $wpdb->prepare("SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = 'enclosure' AND meta_value LIKE (%s)", $post_ID, like_escape( $link_test ) . '%') );
430                         foreach ( $mids as $mid )
431                                 delete_metadata_by_mid( 'post', $mid );
432                 }
433         }
434
435         foreach ( (array) $post_links_temp[0] as $link_test ) {
436                 if ( !in_array( $link_test, $pung ) ) { // If we haven't pung it already
437                         $test = @parse_url( $link_test );
438                         if ( false === $test )
439                                 continue;
440                         if ( isset( $test['query'] ) )
441                                 $post_links[] = $link_test;
442                         elseif ( isset($test['path']) && ( $test['path'] != '/' ) &&  ($test['path'] != '' ) )
443                                 $post_links[] = $link_test;
444                 }
445         }
446
447         foreach ( (array) $post_links as $url ) {
448                 if ( $url != '' && !$wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = 'enclosure' AND meta_value LIKE (%s)", $post_ID, like_escape( $url ) . '%' ) ) ) {
449
450                         if ( $headers = wp_get_http_headers( $url) ) {
451                                 $len = isset( $headers['content-length'] ) ? (int) $headers['content-length'] : 0;
452                                 $type = isset( $headers['content-type'] ) ? $headers['content-type'] : '';
453                                 $allowed_types = array( 'video', 'audio' );
454
455                                 // Check to see if we can figure out the mime type from
456                                 // the extension
457                                 $url_parts = @parse_url( $url );
458                                 if ( false !== $url_parts ) {
459                                         $extension = pathinfo( $url_parts['path'], PATHINFO_EXTENSION );
460                                         if ( !empty( $extension ) ) {
461                                                 foreach ( wp_get_mime_types() as $exts => $mime ) {
462                                                         if ( preg_match( '!^(' . $exts . ')$!i', $extension ) ) {
463                                                                 $type = $mime;
464                                                                 break;
465                                                         }
466                                                 }
467                                         }
468                                 }
469
470                                 if ( in_array( substr( $type, 0, strpos( $type, "/" ) ), $allowed_types ) ) {
471                                         add_post_meta( $post_ID, 'enclosure', "$url\n$len\n$mime\n" );
472                                 }
473                         }
474                 }
475         }
476 }
477
478 /**
479  * Perform a HTTP HEAD or GET request.
480  *
481  * If $file_path is a writable filename, this will do a GET request and write
482  * the file to that path.
483  *
484  * @since 2.5.0
485  *
486  * @param string $url URL to fetch.
487  * @param string|bool $file_path Optional. File path to write request to.
488  * @param int $red (private) The number of Redirects followed, Upon 5 being hit, returns false.
489  * @return bool|string False on failure and string of headers if HEAD request.
490  */
491 function wp_get_http( $url, $file_path = false, $red = 1 ) {
492         @set_time_limit( 60 );
493
494         if ( $red > 5 )
495                 return false;
496
497         $options = array();
498         $options['redirection'] = 5;
499
500         if ( false == $file_path )
501                 $options['method'] = 'HEAD';
502         else
503                 $options['method'] = 'GET';
504
505         $response = wp_safe_remote_request( $url, $options );
506
507         if ( is_wp_error( $response ) )
508                 return false;
509
510         $headers = wp_remote_retrieve_headers( $response );
511         $headers['response'] = wp_remote_retrieve_response_code( $response );
512
513         // WP_HTTP no longer follows redirects for HEAD requests.
514         if ( 'HEAD' == $options['method'] && in_array($headers['response'], array(301, 302)) && isset( $headers['location'] ) ) {
515                 return wp_get_http( $headers['location'], $file_path, ++$red );
516         }
517
518         if ( false == $file_path )
519                 return $headers;
520
521         // GET request - write it to the supplied filename
522         $out_fp = fopen($file_path, 'w');
523         if ( !$out_fp )
524                 return $headers;
525
526         fwrite( $out_fp,  wp_remote_retrieve_body( $response ) );
527         fclose($out_fp);
528         clearstatcache();
529
530         return $headers;
531 }
532
533 /**
534  * Retrieve HTTP Headers from URL.
535  *
536  * @since 1.5.1
537  *
538  * @param string $url
539  * @param bool $deprecated Not Used.
540  * @return bool|string False on failure, headers on success.
541  */
542 function wp_get_http_headers( $url, $deprecated = false ) {
543         if ( !empty( $deprecated ) )
544                 _deprecated_argument( __FUNCTION__, '2.7' );
545
546         $response = wp_safe_remote_head( $url );
547
548         if ( is_wp_error( $response ) )
549                 return false;
550
551         return wp_remote_retrieve_headers( $response );
552 }
553
554 /**
555  * Whether today is a new day.
556  *
557  * @since 0.71
558  * @uses $day Today
559  * @uses $previousday Previous day
560  *
561  * @return int 1 when new day, 0 if not a new day.
562  */
563 function is_new_day() {
564         global $currentday, $previousday;
565         if ( $currentday != $previousday )
566                 return 1;
567         else
568                 return 0;
569 }
570
571 /**
572  * Build URL query based on an associative and, or indexed array.
573  *
574  * This is a convenient function for easily building url queries. It sets the
575  * separator to '&' and uses _http_build_query() function.
576  *
577  * @see _http_build_query() Used to build the query
578  * @link http://us2.php.net/manual/en/function.http-build-query.php more on what
579  *              http_build_query() does.
580  *
581  * @since 2.3.0
582  *
583  * @param array $data URL-encode key/value pairs.
584  * @return string URL encoded string
585  */
586 function build_query( $data ) {
587         return _http_build_query( $data, null, '&', '', false );
588 }
589
590 // from php.net (modified by Mark Jaquith to behave like the native PHP5 function)
591 function _http_build_query($data, $prefix=null, $sep=null, $key='', $urlencode=true) {
592         $ret = array();
593
594         foreach ( (array) $data as $k => $v ) {
595                 if ( $urlencode)
596                         $k = urlencode($k);
597                 if ( is_int($k) && $prefix != null )
598                         $k = $prefix.$k;
599                 if ( !empty($key) )
600                         $k = $key . '%5B' . $k . '%5D';
601                 if ( $v === null )
602                         continue;
603                 elseif ( $v === FALSE )
604                         $v = '0';
605
606                 if ( is_array($v) || is_object($v) )
607                         array_push($ret,_http_build_query($v, '', $sep, $k, $urlencode));
608                 elseif ( $urlencode )
609                         array_push($ret, $k.'='.urlencode($v));
610                 else
611                         array_push($ret, $k.'='.$v);
612         }
613
614         if ( null === $sep )
615                 $sep = ini_get('arg_separator.output');
616
617         return implode($sep, $ret);
618 }
619
620 /**
621  * Retrieve a modified URL query string.
622  *
623  * You can rebuild the URL and append a new query variable to the URL query by
624  * using this function. You can also retrieve the full URL with query data.
625  *
626  * Adding a single key & value or an associative array. Setting a key value to
627  * an empty string removes the key. Omitting oldquery_or_uri uses the $_SERVER
628  * value. Additional values provided are expected to be encoded appropriately
629  * with urlencode() or rawurlencode().
630  *
631  * @since 1.5.0
632  *
633  * @param mixed $param1 Either newkey or an associative_array
634  * @param mixed $param2 Either newvalue or oldquery or uri
635  * @param mixed $param3 Optional. Old query or uri
636  * @return string New URL query string.
637  */
638 function add_query_arg() {
639         $ret = '';
640         $args = func_get_args();
641         if ( is_array( $args[0] ) ) {
642                 if ( count( $args ) < 2 || false === $args[1] )
643                         $uri = $_SERVER['REQUEST_URI'];
644                 else
645                         $uri = $args[1];
646         } else {
647                 if ( count( $args ) < 3 || false === $args[2] )
648                         $uri = $_SERVER['REQUEST_URI'];
649                 else
650                         $uri = $args[2];
651         }
652
653         if ( $frag = strstr( $uri, '#' ) )
654                 $uri = substr( $uri, 0, -strlen( $frag ) );
655         else
656                 $frag = '';
657
658         if ( 0 === stripos( $uri, 'http://' ) ) {
659                 $protocol = 'http://';
660                 $uri = substr( $uri, 7 );
661         } elseif ( 0 === stripos( $uri, 'https://' ) ) {
662                 $protocol = 'https://';
663                 $uri = substr( $uri, 8 );
664         } else {
665                 $protocol = '';
666         }
667
668         if ( strpos( $uri, '?' ) !== false ) {
669                 $parts = explode( '?', $uri, 2 );
670                 if ( 1 == count( $parts ) ) {
671                         $base = '?';
672                         $query = $parts[0];
673                 } else {
674                         $base = $parts[0] . '?';
675                         $query = $parts[1];
676                 }
677         } elseif ( $protocol || strpos( $uri, '=' ) === false ) {
678                 $base = $uri . '?';
679                 $query = '';
680         } else {
681                 $base = '';
682                 $query = $uri;
683         }
684
685         wp_parse_str( $query, $qs );
686         $qs = urlencode_deep( $qs ); // this re-URL-encodes things that were already in the query string
687         if ( is_array( $args[0] ) ) {
688                 $kayvees = $args[0];
689                 $qs = array_merge( $qs, $kayvees );
690         } else {
691                 $qs[ $args[0] ] = $args[1];
692         }
693
694         foreach ( $qs as $k => $v ) {
695                 if ( $v === false )
696                         unset( $qs[$k] );
697         }
698
699         $ret = build_query( $qs );
700         $ret = trim( $ret, '?' );
701         $ret = preg_replace( '#=(&|$)#', '$1', $ret );
702         $ret = $protocol . $base . $ret . $frag;
703         $ret = rtrim( $ret, '?' );
704         return $ret;
705 }
706
707 /**
708  * Removes an item or list from the query string.
709  *
710  * @since 1.5.0
711  *
712  * @param string|array $key Query key or keys to remove.
713  * @param bool $query When false uses the $_SERVER value.
714  * @return string New URL query string.
715  */
716 function remove_query_arg( $key, $query=false ) {
717         if ( is_array( $key ) ) { // removing multiple keys
718                 foreach ( $key as $k )
719                         $query = add_query_arg( $k, false, $query );
720                 return $query;
721         }
722         return add_query_arg( $key, false, $query );
723 }
724
725 /**
726  * Walks the array while sanitizing the contents.
727  *
728  * @since 0.71
729  *
730  * @param array $array Array to walk while sanitizing contents.
731  * @return array Sanitized $array.
732  */
733 function add_magic_quotes( $array ) {
734         foreach ( (array) $array as $k => $v ) {
735                 if ( is_array( $v ) ) {
736                         $array[$k] = add_magic_quotes( $v );
737                 } else {
738                         $array[$k] = addslashes( $v );
739                 }
740         }
741         return $array;
742 }
743
744 /**
745  * HTTP request for URI to retrieve content.
746  *
747  * @since 1.5.1
748  * @uses wp_remote_get()
749  *
750  * @param string $uri URI/URL of web page to retrieve.
751  * @return bool|string HTTP content. False on failure.
752  */
753 function wp_remote_fopen( $uri ) {
754         $parsed_url = @parse_url( $uri );
755
756         if ( !$parsed_url || !is_array( $parsed_url ) )
757                 return false;
758
759         $options = array();
760         $options['timeout'] = 10;
761
762         $response = wp_safe_remote_get( $uri, $options );
763
764         if ( is_wp_error( $response ) )
765                 return false;
766
767         return wp_remote_retrieve_body( $response );
768 }
769
770 /**
771  * Set up the WordPress query.
772  *
773  * @since 2.0.0
774  *
775  * @param string $query_vars Default WP_Query arguments.
776  */
777 function wp( $query_vars = '' ) {
778         global $wp, $wp_query, $wp_the_query;
779         $wp->main( $query_vars );
780
781         if ( !isset($wp_the_query) )
782                 $wp_the_query = $wp_query;
783 }
784
785 /**
786  * Retrieve the description for the HTTP status.
787  *
788  * @since 2.3.0
789  *
790  * @param int $code HTTP status code.
791  * @return string Empty string if not found, or description if found.
792  */
793 function get_status_header_desc( $code ) {
794         global $wp_header_to_desc;
795
796         $code = absint( $code );
797
798         if ( !isset( $wp_header_to_desc ) ) {
799                 $wp_header_to_desc = array(
800                         100 => 'Continue',
801                         101 => 'Switching Protocols',
802                         102 => 'Processing',
803
804                         200 => 'OK',
805                         201 => 'Created',
806                         202 => 'Accepted',
807                         203 => 'Non-Authoritative Information',
808                         204 => 'No Content',
809                         205 => 'Reset Content',
810                         206 => 'Partial Content',
811                         207 => 'Multi-Status',
812                         226 => 'IM Used',
813
814                         300 => 'Multiple Choices',
815                         301 => 'Moved Permanently',
816                         302 => 'Found',
817                         303 => 'See Other',
818                         304 => 'Not Modified',
819                         305 => 'Use Proxy',
820                         306 => 'Reserved',
821                         307 => 'Temporary Redirect',
822
823                         400 => 'Bad Request',
824                         401 => 'Unauthorized',
825                         402 => 'Payment Required',
826                         403 => 'Forbidden',
827                         404 => 'Not Found',
828                         405 => 'Method Not Allowed',
829                         406 => 'Not Acceptable',
830                         407 => 'Proxy Authentication Required',
831                         408 => 'Request Timeout',
832                         409 => 'Conflict',
833                         410 => 'Gone',
834                         411 => 'Length Required',
835                         412 => 'Precondition Failed',
836                         413 => 'Request Entity Too Large',
837                         414 => 'Request-URI Too Long',
838                         415 => 'Unsupported Media Type',
839                         416 => 'Requested Range Not Satisfiable',
840                         417 => 'Expectation Failed',
841                         422 => 'Unprocessable Entity',
842                         423 => 'Locked',
843                         424 => 'Failed Dependency',
844                         426 => 'Upgrade Required',
845
846                         500 => 'Internal Server Error',
847                         501 => 'Not Implemented',
848                         502 => 'Bad Gateway',
849                         503 => 'Service Unavailable',
850                         504 => 'Gateway Timeout',
851                         505 => 'HTTP Version Not Supported',
852                         506 => 'Variant Also Negotiates',
853                         507 => 'Insufficient Storage',
854                         510 => 'Not Extended'
855                 );
856         }
857
858         if ( isset( $wp_header_to_desc[$code] ) )
859                 return $wp_header_to_desc[$code];
860         else
861                 return '';
862 }
863
864 /**
865  * Set HTTP status header.
866  *
867  * @since 2.0.0
868  * @uses apply_filters() Calls 'status_header' on status header string, HTTP
869  *              HTTP code, HTTP code description, and protocol string as separate
870  *              parameters.
871  *
872  * @param int $header HTTP status code
873  * @return unknown
874  */
875 function status_header( $header ) {
876         $text = get_status_header_desc( $header );
877
878         if ( empty( $text ) )
879                 return false;
880
881         $protocol = $_SERVER["SERVER_PROTOCOL"];
882         if ( 'HTTP/1.1' != $protocol && 'HTTP/1.0' != $protocol )
883                 $protocol = 'HTTP/1.0';
884         $status_header = "$protocol $header $text";
885         if ( function_exists( 'apply_filters' ) )
886                 $status_header = apply_filters( 'status_header', $status_header, $header, $text, $protocol );
887
888         return @header( $status_header, true, $header );
889 }
890
891 /**
892  * Gets the header information to prevent caching.
893  *
894  * The several different headers cover the different ways cache prevention is handled
895  * by different browsers
896  *
897  * @since 2.8.0
898  *
899  * @uses apply_filters()
900  * @return array The associative array of header names and field values.
901  */
902 function wp_get_nocache_headers() {
903         $headers = array(
904                 'Expires' => 'Wed, 11 Jan 1984 05:00:00 GMT',
905                 'Cache-Control' => 'no-cache, must-revalidate, max-age=0',
906                 'Pragma' => 'no-cache',
907         );
908
909         if ( function_exists('apply_filters') ) {
910                 $headers = (array) apply_filters('nocache_headers', $headers);
911         }
912         $headers['Last-Modified'] = false;
913         return $headers;
914 }
915
916 /**
917  * Sets the headers to prevent caching for the different browsers.
918  *
919  * Different browsers support different nocache headers, so several headers must
920  * be sent so that all of them get the point that no caching should occur.
921  *
922  * @since 2.0.0
923  * @uses wp_get_nocache_headers()
924  */
925 function nocache_headers() {
926         $headers = wp_get_nocache_headers();
927
928         unset( $headers['Last-Modified'] );
929
930         // In PHP 5.3+, make sure we are not sending a Last-Modified header.
931         if ( function_exists( 'header_remove' ) ) {
932                 @header_remove( 'Last-Modified' );
933         } else {
934                 // In PHP 5.2, send an empty Last-Modified header, but only as a
935                 // last resort to override a header already sent. #WP23021
936                 foreach ( headers_list() as $header ) {
937                         if ( 0 === stripos( $header, 'Last-Modified' ) ) {
938                                 $headers['Last-Modified'] = '';
939                                 break;
940                         }
941                 }
942         }
943
944         foreach( $headers as $name => $field_value )
945                 @header("{$name}: {$field_value}");
946 }
947
948 /**
949  * Set the headers for caching for 10 days with JavaScript content type.
950  *
951  * @since 2.1.0
952  */
953 function cache_javascript_headers() {
954         $expiresOffset = 10 * DAY_IN_SECONDS;
955         header( "Content-Type: text/javascript; charset=" . get_bloginfo( 'charset' ) );
956         header( "Vary: Accept-Encoding" ); // Handle proxies
957         header( "Expires: " . gmdate( "D, d M Y H:i:s", time() + $expiresOffset ) . " GMT" );
958 }
959
960 /**
961  * Retrieve the number of database queries during the WordPress execution.
962  *
963  * @since 2.0.0
964  *
965  * @return int Number of database queries
966  */
967 function get_num_queries() {
968         global $wpdb;
969         return $wpdb->num_queries;
970 }
971
972 /**
973  * Whether input is yes or no. Must be 'y' to be true.
974  *
975  * @since 1.0.0
976  *
977  * @param string $yn Character string containing either 'y' or 'n'
978  * @return bool True if yes, false on anything else
979  */
980 function bool_from_yn( $yn ) {
981         return ( strtolower( $yn ) == 'y' );
982 }
983
984 /**
985  * Loads the feed template from the use of an action hook.
986  *
987  * If the feed action does not have a hook, then the function will die with a
988  * message telling the visitor that the feed is not valid.
989  *
990  * It is better to only have one hook for each feed.
991  *
992  * @since 2.1.0
993  * @uses $wp_query Used to tell if the use a comment feed.
994  * @uses do_action() Calls 'do_feed_$feed' hook, if a hook exists for the feed.
995  */
996 function do_feed() {
997         global $wp_query;
998
999         $feed = get_query_var( 'feed' );
1000
1001         // Remove the pad, if present.
1002         $feed = preg_replace( '/^_+/', '', $feed );
1003
1004         if ( $feed == '' || $feed == 'feed' )
1005                 $feed = get_default_feed();
1006
1007         $hook = 'do_feed_' . $feed;
1008         if ( !has_action($hook) ) {
1009                 $message = sprintf( __( 'ERROR: %s is not a valid feed template.' ), esc_html($feed));
1010                 wp_die( $message, '', array( 'response' => 404 ) );
1011         }
1012
1013         do_action( $hook, $wp_query->is_comment_feed );
1014 }
1015
1016 /**
1017  * Load the RDF RSS 0.91 Feed template.
1018  *
1019  * @since 2.1.0
1020  */
1021 function do_feed_rdf() {
1022         load_template( ABSPATH . WPINC . '/feed-rdf.php' );
1023 }
1024
1025 /**
1026  * Load the RSS 1.0 Feed Template.
1027  *
1028  * @since 2.1.0
1029  */
1030 function do_feed_rss() {
1031         load_template( ABSPATH . WPINC . '/feed-rss.php' );
1032 }
1033
1034 /**
1035  * Load either the RSS2 comment feed or the RSS2 posts feed.
1036  *
1037  * @since 2.1.0
1038  *
1039  * @param bool $for_comments True for the comment feed, false for normal feed.
1040  */
1041 function do_feed_rss2( $for_comments ) {
1042         if ( $for_comments )
1043                 load_template( ABSPATH . WPINC . '/feed-rss2-comments.php' );
1044         else
1045                 load_template( ABSPATH . WPINC . '/feed-rss2.php' );
1046 }
1047
1048 /**
1049  * Load either Atom comment feed or Atom posts feed.
1050  *
1051  * @since 2.1.0
1052  *
1053  * @param bool $for_comments True for the comment feed, false for normal feed.
1054  */
1055 function do_feed_atom( $for_comments ) {
1056         if ($for_comments)
1057                 load_template( ABSPATH . WPINC . '/feed-atom-comments.php');
1058         else
1059                 load_template( ABSPATH . WPINC . '/feed-atom.php' );
1060 }
1061
1062 /**
1063  * Display the robots.txt file content.
1064  *
1065  * The echo content should be with usage of the permalinks or for creating the
1066  * robots.txt file.
1067  *
1068  * @since 2.1.0
1069  * @uses do_action() Calls 'do_robotstxt' hook for displaying robots.txt rules.
1070  */
1071 function do_robots() {
1072         header( 'Content-Type: text/plain; charset=utf-8' );
1073
1074         do_action( 'do_robotstxt' );
1075
1076         $output = "User-agent: *\n";
1077         $public = get_option( 'blog_public' );
1078         if ( '0' == $public ) {
1079                 $output .= "Disallow: /\n";
1080         } else {
1081                 $site_url = parse_url( site_url() );
1082                 $path = ( !empty( $site_url['path'] ) ) ? $site_url['path'] : '';
1083                 $output .= "Disallow: $path/wp-admin/\n";
1084                 $output .= "Disallow: $path/wp-includes/\n";
1085         }
1086
1087         echo apply_filters('robots_txt', $output, $public);
1088 }
1089
1090 /**
1091  * Test whether blog is already installed.
1092  *
1093  * The cache will be checked first. If you have a cache plugin, which saves the
1094  * cache values, then this will work. If you use the default WordPress cache,
1095  * and the database goes away, then you might have problems.
1096  *
1097  * Checks for the option siteurl for whether WordPress is installed.
1098  *
1099  * @since 2.1.0
1100  * @uses $wpdb
1101  *
1102  * @return bool Whether blog is already installed.
1103  */
1104 function is_blog_installed() {
1105         global $wpdb;
1106
1107         // Check cache first. If options table goes away and we have true cached, oh well.
1108         if ( wp_cache_get( 'is_blog_installed' ) )
1109                 return true;
1110
1111         $suppress = $wpdb->suppress_errors();
1112         if ( ! defined( 'WP_INSTALLING' ) ) {
1113                 $alloptions = wp_load_alloptions();
1114         }
1115         // If siteurl is not set to autoload, check it specifically
1116         if ( !isset( $alloptions['siteurl'] ) )
1117                 $installed = $wpdb->get_var( "SELECT option_value FROM $wpdb->options WHERE option_name = 'siteurl'" );
1118         else
1119                 $installed = $alloptions['siteurl'];
1120         $wpdb->suppress_errors( $suppress );
1121
1122         $installed = !empty( $installed );
1123         wp_cache_set( 'is_blog_installed', $installed );
1124
1125         if ( $installed )
1126                 return true;
1127
1128         // If visiting repair.php, return true and let it take over.
1129         if ( defined( 'WP_REPAIRING' ) )
1130                 return true;
1131
1132         $suppress = $wpdb->suppress_errors();
1133
1134         // Loop over the WP tables. If none exist, then scratch install is allowed.
1135         // If one or more exist, suggest table repair since we got here because the options
1136         // table could not be accessed.
1137         $wp_tables = $wpdb->tables();
1138         foreach ( $wp_tables as $table ) {
1139                 // The existence of custom user tables shouldn't suggest an insane state or prevent a clean install.
1140                 if ( defined( 'CUSTOM_USER_TABLE' ) && CUSTOM_USER_TABLE == $table )
1141                         continue;
1142                 if ( defined( 'CUSTOM_USER_META_TABLE' ) && CUSTOM_USER_META_TABLE == $table )
1143                         continue;
1144
1145                 if ( ! $wpdb->get_results( "DESCRIBE $table;" ) )
1146                         continue;
1147
1148                 // One or more tables exist. We are insane.
1149
1150                 wp_load_translations_early();
1151
1152                 // Die with a DB error.
1153                 $wpdb->error = sprintf( __( 'One or more database tables are unavailable. The database may need to be <a href="%s">repaired</a>.' ), 'maint/repair.php?referrer=is_blog_installed' );
1154                 dead_db();
1155         }
1156
1157         $wpdb->suppress_errors( $suppress );
1158
1159         wp_cache_set( 'is_blog_installed', false );
1160
1161         return false;
1162 }
1163
1164 /**
1165  * Retrieve URL with nonce added to URL query.
1166  *
1167  * @package WordPress
1168  * @subpackage Security
1169  * @since 2.0.4
1170  *
1171  * @param string $actionurl URL to add nonce action.
1172  * @param string $action Optional. Nonce action name.
1173  * @param string $name Optional. Nonce name.
1174  * @return string URL with nonce action added.
1175  */
1176 function wp_nonce_url( $actionurl, $action = -1, $name = '_wpnonce' ) {
1177         $actionurl = str_replace( '&amp;', '&', $actionurl );
1178         return esc_html( add_query_arg( $name, wp_create_nonce( $action ), $actionurl ) );
1179 }
1180
1181 /**
1182  * Retrieve or display nonce hidden field for forms.
1183  *
1184  * The nonce field is used to validate that the contents of the form came from
1185  * the location on the current site and not somewhere else. The nonce does not
1186  * offer absolute protection, but should protect against most cases. It is very
1187  * important to use nonce field in forms.
1188  *
1189  * The $action and $name are optional, but if you want to have better security,
1190  * it is strongly suggested to set those two parameters. It is easier to just
1191  * call the function without any parameters, because validation of the nonce
1192  * doesn't require any parameters, but since crackers know what the default is
1193  * it won't be difficult for them to find a way around your nonce and cause
1194  * damage.
1195  *
1196  * The input name will be whatever $name value you gave. The input value will be
1197  * the nonce creation value.
1198  *
1199  * @package WordPress
1200  * @subpackage Security
1201  * @since 2.0.4
1202  *
1203  * @param string $action Optional. Action name.
1204  * @param string $name Optional. Nonce name.
1205  * @param bool $referer Optional, default true. Whether to set the referer field for validation.
1206  * @param bool $echo Optional, default true. Whether to display or return hidden form field.
1207  * @return string Nonce field.
1208  */
1209 function wp_nonce_field( $action = -1, $name = "_wpnonce", $referer = true , $echo = true ) {
1210         $name = esc_attr( $name );
1211         $nonce_field = '<input type="hidden" id="' . $name . '" name="' . $name . '" value="' . wp_create_nonce( $action ) . '" />';
1212
1213         if ( $referer )
1214                 $nonce_field .= wp_referer_field( false );
1215
1216         if ( $echo )
1217                 echo $nonce_field;
1218
1219         return $nonce_field;
1220 }
1221
1222 /**
1223  * Retrieve or display referer hidden field for forms.
1224  *
1225  * The referer link is the current Request URI from the server super global. The
1226  * input name is '_wp_http_referer', in case you wanted to check manually.
1227  *
1228  * @package WordPress
1229  * @subpackage Security
1230  * @since 2.0.4
1231  *
1232  * @param bool $echo Whether to echo or return the referer field.
1233  * @return string Referer field.
1234  */
1235 function wp_referer_field( $echo = true ) {
1236         $referer_field = '<input type="hidden" name="_wp_http_referer" value="'. esc_attr( wp_unslash( $_SERVER['REQUEST_URI'] ) ) . '" />';
1237
1238         if ( $echo )
1239                 echo $referer_field;
1240         return $referer_field;
1241 }
1242
1243 /**
1244  * Retrieve or display original referer hidden field for forms.
1245  *
1246  * The input name is '_wp_original_http_referer' and will be either the same
1247  * value of {@link wp_referer_field()}, if that was posted already or it will
1248  * be the current page, if it doesn't exist.
1249  *
1250  * @package WordPress
1251  * @subpackage Security
1252  * @since 2.0.4
1253  *
1254  * @param bool $echo Whether to echo the original http referer
1255  * @param string $jump_back_to Optional, default is 'current'. Can be 'previous' or page you want to jump back to.
1256  * @return string Original referer field.
1257  */
1258 function wp_original_referer_field( $echo = true, $jump_back_to = 'current' ) {
1259         if ( ! $ref = wp_get_original_referer() ) {
1260                 $ref = 'previous' == $jump_back_to ? wp_get_referer() : wp_unslash( $_SERVER['REQUEST_URI'] );
1261         }
1262         $orig_referer_field = '<input type="hidden" name="_wp_original_http_referer" value="' . esc_attr( $ref ) . '" />';
1263         if ( $echo )
1264                 echo $orig_referer_field;
1265         return $orig_referer_field;
1266 }
1267
1268 /**
1269  * Retrieve referer from '_wp_http_referer' or HTTP referer. If it's the same
1270  * as the current request URL, will return false.
1271  *
1272  * @package WordPress
1273  * @subpackage Security
1274  * @since 2.0.4
1275  *
1276  * @return string|bool False on failure. Referer URL on success.
1277  */
1278 function wp_get_referer() {
1279         $ref = false;
1280         if ( ! empty( $_REQUEST['_wp_http_referer'] ) )
1281                 $ref = wp_unslash( $_REQUEST['_wp_http_referer'] );
1282         else if ( ! empty( $_SERVER['HTTP_REFERER'] ) )
1283                 $ref = wp_unslash( $_SERVER['HTTP_REFERER'] );
1284
1285         if ( $ref && $ref !== wp_unslash( $_SERVER['REQUEST_URI'] ) )
1286                 return wp_unslash( $ref );
1287         return false;
1288 }
1289
1290 /**
1291  * Retrieve original referer that was posted, if it exists.
1292  *
1293  * @package WordPress
1294  * @subpackage Security
1295  * @since 2.0.4
1296  *
1297  * @return string|bool False if no original referer or original referer if set.
1298  */
1299 function wp_get_original_referer() {
1300         if ( !empty( $_REQUEST['_wp_original_http_referer'] ) )
1301                 return wp_unslash( $_REQUEST['_wp_original_http_referer'] );
1302         return false;
1303 }
1304
1305 /**
1306  * Recursive directory creation based on full path.
1307  *
1308  * Will attempt to set permissions on folders.
1309  *
1310  * @since 2.0.1
1311  *
1312  * @param string $target Full path to attempt to create.
1313  * @return bool Whether the path was created. True if path already exists.
1314  */
1315 function wp_mkdir_p( $target ) {
1316         $wrapper = null;
1317
1318         // strip the protocol
1319         if( wp_is_stream( $target ) ) {
1320                 list( $wrapper, $target ) = explode( '://', $target, 2 );
1321         }
1322
1323         // from php.net/mkdir user contributed notes
1324         $target = str_replace( '//', '/', $target );
1325
1326         // put the wrapper back on the target
1327         if( $wrapper !== null ) {
1328                 $target = $wrapper . '://' . $target;
1329         }
1330
1331         // safe mode fails with a trailing slash under certain PHP versions.
1332         $target = rtrim($target, '/'); // Use rtrim() instead of untrailingslashit to avoid formatting.php dependency.
1333         if ( empty($target) )
1334                 $target = '/';
1335
1336         if ( file_exists( $target ) )
1337                 return @is_dir( $target );
1338
1339         // Attempting to create the directory may clutter up our display.
1340         if ( @mkdir( $target ) ) {
1341                 $stat = @stat( dirname( $target ) );
1342                 $dir_perms = $stat['mode'] & 0007777;  // Get the permission bits.
1343                 @chmod( $target, $dir_perms );
1344                 return true;
1345         } elseif ( is_dir( dirname( $target ) ) ) {
1346                         return false;
1347         }
1348
1349         // If the above failed, attempt to create the parent node, then try again.
1350         if ( ( $target != '/' ) && ( wp_mkdir_p( dirname( $target ) ) ) )
1351                 return wp_mkdir_p( $target );
1352
1353         return false;
1354 }
1355
1356 /**
1357  * Test if a give filesystem path is absolute ('/foo/bar', 'c:\windows').
1358  *
1359  * @since 2.5.0
1360  *
1361  * @param string $path File path
1362  * @return bool True if path is absolute, false is not absolute.
1363  */
1364 function path_is_absolute( $path ) {
1365         // this is definitive if true but fails if $path does not exist or contains a symbolic link
1366         if ( realpath($path) == $path )
1367                 return true;
1368
1369         if ( strlen($path) == 0 || $path[0] == '.' )
1370                 return false;
1371
1372         // windows allows absolute paths like this
1373         if ( preg_match('#^[a-zA-Z]:\\\\#', $path) )
1374                 return true;
1375
1376         // a path starting with / or \ is absolute; anything else is relative
1377         return ( $path[0] == '/' || $path[0] == '\\' );
1378 }
1379
1380 /**
1381  * Join two filesystem paths together (e.g. 'give me $path relative to $base').
1382  *
1383  * If the $path is absolute, then it the full path is returned.
1384  *
1385  * @since 2.5.0
1386  *
1387  * @param string $base
1388  * @param string $path
1389  * @return string The path with the base or absolute path.
1390  */
1391 function path_join( $base, $path ) {
1392         if ( path_is_absolute($path) )
1393                 return $path;
1394
1395         return rtrim($base, '/') . '/' . ltrim($path, '/');
1396 }
1397
1398 /**
1399  * Determines a writable directory for temporary files.
1400  * Function's preference is the return value of <code>sys_get_temp_dir()</code>,
1401  * followed by your PHP temporary upload directory, followed by WP_CONTENT_DIR,
1402  * before finally defaulting to /tmp/
1403  *
1404  * In the event that this function does not find a writable location,
1405  * It may be overridden by the <code>WP_TEMP_DIR</code> constant in
1406  * your <code>wp-config.php</code> file.
1407  *
1408  * @since 2.5.0
1409  *
1410  * @return string Writable temporary directory
1411  */
1412 function get_temp_dir() {
1413         static $temp;
1414         if ( defined('WP_TEMP_DIR') )
1415                 return trailingslashit(WP_TEMP_DIR);
1416
1417         if ( $temp )
1418                 return trailingslashit( rtrim( $temp, '\\' ) );
1419
1420         if ( function_exists('sys_get_temp_dir') ) {
1421                 $temp = sys_get_temp_dir();
1422                 if ( @is_dir( $temp ) && wp_is_writable( $temp ) )
1423                         return trailingslashit( rtrim( $temp, '\\' ) );
1424         }
1425
1426         $temp = ini_get('upload_tmp_dir');
1427         if ( is_dir( $temp ) && wp_is_writable( $temp ) )
1428                 return trailingslashit( rtrim( $temp, '\\' ) );
1429
1430         $temp = WP_CONTENT_DIR . '/';
1431         if ( is_dir( $temp ) && wp_is_writable( $temp ) )
1432                 return $temp;
1433
1434         $temp = '/tmp/';
1435         return $temp;
1436 }
1437
1438 /**
1439  * Determine if a directory is writable.
1440  *
1441  * This function is used to work around certain ACL issues
1442  * in PHP primarily affecting Windows Servers.
1443  *
1444  * @see win_is_writable()
1445  *
1446  * @since 3.6.0
1447  *
1448  * @param string $path
1449  * @return bool
1450  */
1451 function wp_is_writable( $path ) {
1452         if ( 'WIN' === strtoupper( substr( PHP_OS, 0, 3 ) ) )
1453                 return win_is_writable( $path );
1454         else
1455                 return @is_writable( $path );
1456 }
1457
1458 /**
1459  * Workaround for Windows bug in is_writable() function
1460  *
1461  * PHP has issues with Windows ACL's for determine if a
1462  * directory is writable or not, this works around them by
1463  * checking the ability to open files rather than relying
1464  * upon PHP to interprate the OS ACL.
1465  *
1466  * @link http://bugs.php.net/bug.php?id=27609
1467  * @link http://bugs.php.net/bug.php?id=30931
1468  *
1469  * @since 2.8.0
1470  *
1471  * @param string $path
1472  * @return bool
1473  */
1474 function win_is_writable( $path ) {
1475
1476         if ( $path[strlen( $path ) - 1] == '/' ) // if it looks like a directory, check a random file within the directory
1477                 return win_is_writable( $path . uniqid( mt_rand() ) . '.tmp');
1478         else if ( is_dir( $path ) ) // If it's a directory (and not a file) check a random file within the directory
1479                 return win_is_writable( $path . '/' . uniqid( mt_rand() ) . '.tmp' );
1480
1481         // check tmp file for read/write capabilities
1482         $should_delete_tmp_file = !file_exists( $path );
1483         $f = @fopen( $path, 'a' );
1484         if ( $f === false )
1485                 return false;
1486         fclose( $f );
1487         if ( $should_delete_tmp_file )
1488                 unlink( $path );
1489         return true;
1490 }
1491
1492 /**
1493  * Get an array containing the current upload directory's path and url.
1494  *
1495  * Checks the 'upload_path' option, which should be from the web root folder,
1496  * and if it isn't empty it will be used. If it is empty, then the path will be
1497  * 'WP_CONTENT_DIR/uploads'. If the 'UPLOADS' constant is defined, then it will
1498  * override the 'upload_path' option and 'WP_CONTENT_DIR/uploads' path.
1499  *
1500  * The upload URL path is set either by the 'upload_url_path' option or by using
1501  * the 'WP_CONTENT_URL' constant and appending '/uploads' to the path.
1502  *
1503  * If the 'uploads_use_yearmonth_folders' is set to true (checkbox if checked in
1504  * the administration settings panel), then the time will be used. The format
1505  * will be year first and then month.
1506  *
1507  * If the path couldn't be created, then an error will be returned with the key
1508  * 'error' containing the error message. The error suggests that the parent
1509  * directory is not writable by the server.
1510  *
1511  * On success, the returned array will have many indices:
1512  * 'path' - base directory and sub directory or full path to upload directory.
1513  * 'url' - base url and sub directory or absolute URL to upload directory.
1514  * 'subdir' - sub directory if uploads use year/month folders option is on.
1515  * 'basedir' - path without subdir.
1516  * 'baseurl' - URL path without subdir.
1517  * 'error' - set to false.
1518  *
1519  * @since 2.0.0
1520  * @uses apply_filters() Calls 'upload_dir' on returned array.
1521  *
1522  * @param string $time Optional. Time formatted in 'yyyy/mm'.
1523  * @return array See above for description.
1524  */
1525 function wp_upload_dir( $time = null ) {
1526         $siteurl = get_option( 'siteurl' );
1527         $upload_path = trim( get_option( 'upload_path' ) );
1528
1529         if ( empty( $upload_path ) || 'wp-content/uploads' == $upload_path ) {
1530                 $dir = WP_CONTENT_DIR . '/uploads';
1531         } elseif ( 0 !== strpos( $upload_path, ABSPATH ) ) {
1532                 // $dir is absolute, $upload_path is (maybe) relative to ABSPATH
1533                 $dir = path_join( ABSPATH, $upload_path );
1534         } else {
1535                 $dir = $upload_path;
1536         }
1537
1538         if ( !$url = get_option( 'upload_url_path' ) ) {
1539                 if ( empty($upload_path) || ( 'wp-content/uploads' == $upload_path ) || ( $upload_path == $dir ) )
1540                         $url = WP_CONTENT_URL . '/uploads';
1541                 else
1542                         $url = trailingslashit( $siteurl ) . $upload_path;
1543         }
1544
1545         // Obey the value of UPLOADS. This happens as long as ms-files rewriting is disabled.
1546         // We also sometimes obey UPLOADS when rewriting is enabled -- see the next block.
1547         if ( defined( 'UPLOADS' ) && ! ( is_multisite() && get_site_option( 'ms_files_rewriting' ) ) ) {
1548                 $dir = ABSPATH . UPLOADS;
1549                 $url = trailingslashit( $siteurl ) . UPLOADS;
1550         }
1551
1552         // If multisite (and if not the main site in a post-MU network)
1553         if ( is_multisite() && ! ( is_main_site() && defined( 'MULTISITE' ) ) ) {
1554
1555                 if ( ! get_site_option( 'ms_files_rewriting' ) ) {
1556                         // If ms-files rewriting is disabled (networks created post-3.5), it is fairly straightforward:
1557                         // Append sites/%d if we're not on the main site (for post-MU networks). (The extra directory
1558                         // prevents a four-digit ID from conflicting with a year-based directory for the main site.
1559                         // But if a MU-era network has disabled ms-files rewriting manually, they don't need the extra
1560                         // directory, as they never had wp-content/uploads for the main site.)
1561
1562                         if ( defined( 'MULTISITE' ) )
1563                                 $ms_dir = '/sites/' . get_current_blog_id();
1564                         else
1565                                 $ms_dir = '/' . get_current_blog_id();
1566
1567                         $dir .= $ms_dir;
1568                         $url .= $ms_dir;
1569
1570                 } elseif ( defined( 'UPLOADS' ) && ! ms_is_switched() ) {
1571                         // Handle the old-form ms-files.php rewriting if the network still has that enabled.
1572                         // When ms-files rewriting is enabled, then we only listen to UPLOADS when:
1573                         //   1) we are not on the main site in a post-MU network,
1574                         //      as wp-content/uploads is used there, and
1575                         //   2) we are not switched, as ms_upload_constants() hardcodes
1576                         //      these constants to reflect the original blog ID.
1577                         //
1578                         // Rather than UPLOADS, we actually use BLOGUPLOADDIR if it is set, as it is absolute.
1579                         // (And it will be set, see ms_upload_constants().) Otherwise, UPLOADS can be used, as
1580                         // as it is relative to ABSPATH. For the final piece: when UPLOADS is used with ms-files
1581                         // rewriting in multisite, the resulting URL is /files. (#WP22702 for background.)
1582
1583                         if ( defined( 'BLOGUPLOADDIR' ) )
1584                                 $dir = untrailingslashit( BLOGUPLOADDIR );
1585                         else
1586                                 $dir = ABSPATH . UPLOADS;
1587                         $url = trailingslashit( $siteurl ) . 'files';
1588                 }
1589         }
1590
1591         $basedir = $dir;
1592         $baseurl = $url;
1593
1594         $subdir = '';
1595         if ( get_option( 'uploads_use_yearmonth_folders' ) ) {
1596                 // Generate the yearly and monthly dirs
1597                 if ( !$time )
1598                         $time = current_time( 'mysql' );
1599                 $y = substr( $time, 0, 4 );
1600                 $m = substr( $time, 5, 2 );
1601                 $subdir = "/$y/$m";
1602         }
1603
1604         $dir .= $subdir;
1605         $url .= $subdir;
1606
1607         $uploads = apply_filters( 'upload_dir',
1608                 array(
1609                         'path'    => $dir,
1610                         'url'     => $url,
1611                         'subdir'  => $subdir,
1612                         'basedir' => $basedir,
1613                         'baseurl' => $baseurl,
1614                         'error'   => false,
1615                 ) );
1616
1617         // Make sure we have an uploads dir
1618         if ( ! wp_mkdir_p( $uploads['path'] ) ) {
1619                 if ( 0 === strpos( $uploads['basedir'], ABSPATH ) )
1620                         $error_path = str_replace( ABSPATH, '', $uploads['basedir'] ) . $uploads['subdir'];
1621                 else
1622                         $error_path = basename( $uploads['basedir'] ) . $uploads['subdir'];
1623
1624                 $message = sprintf( __( 'Unable to create directory %s. Is its parent directory writable by the server?' ), $error_path );
1625                 $uploads['error'] = $message;
1626         }
1627
1628         return $uploads;
1629 }
1630
1631 /**
1632  * Get a filename that is sanitized and unique for the given directory.
1633  *
1634  * If the filename is not unique, then a number will be added to the filename
1635  * before the extension, and will continue adding numbers until the filename is
1636  * unique.
1637  *
1638  * The callback is passed three parameters, the first one is the directory, the
1639  * second is the filename, and the third is the extension.
1640  *
1641  * @since 2.5.0
1642  *
1643  * @param string $dir
1644  * @param string $filename
1645  * @param mixed $unique_filename_callback Callback.
1646  * @return string New filename, if given wasn't unique.
1647  */
1648 function wp_unique_filename( $dir, $filename, $unique_filename_callback = null ) {
1649         // sanitize the file name before we begin processing
1650         $filename = sanitize_file_name($filename);
1651
1652         // separate the filename into a name and extension
1653         $info = pathinfo($filename);
1654         $ext = !empty($info['extension']) ? '.' . $info['extension'] : '';
1655         $name = basename($filename, $ext);
1656
1657         // edge case: if file is named '.ext', treat as an empty name
1658         if ( $name === $ext )
1659                 $name = '';
1660
1661         // Increment the file number until we have a unique file to save in $dir. Use callback if supplied.
1662         if ( $unique_filename_callback && is_callable( $unique_filename_callback ) ) {
1663                 $filename = call_user_func( $unique_filename_callback, $dir, $name, $ext );
1664         } else {
1665                 $number = '';
1666
1667                 // change '.ext' to lower case
1668                 if ( $ext && strtolower($ext) != $ext ) {
1669                         $ext2 = strtolower($ext);
1670                         $filename2 = preg_replace( '|' . preg_quote($ext) . '$|', $ext2, $filename );
1671
1672                         // check for both lower and upper case extension or image sub-sizes may be overwritten
1673                         while ( file_exists($dir . "/$filename") || file_exists($dir . "/$filename2") ) {
1674                                 $new_number = $number + 1;
1675                                 $filename = str_replace( "$number$ext", "$new_number$ext", $filename );
1676                                 $filename2 = str_replace( "$number$ext2", "$new_number$ext2", $filename2 );
1677                                 $number = $new_number;
1678                         }
1679                         return $filename2;
1680                 }
1681
1682                 while ( file_exists( $dir . "/$filename" ) ) {
1683                         if ( '' == "$number$ext" )
1684                                 $filename = $filename . ++$number . $ext;
1685                         else
1686                                 $filename = str_replace( "$number$ext", ++$number . $ext, $filename );
1687                 }
1688         }
1689
1690         return $filename;
1691 }
1692
1693 /**
1694  * Create a file in the upload folder with given content.
1695  *
1696  * If there is an error, then the key 'error' will exist with the error message.
1697  * If success, then the key 'file' will have the unique file path, the 'url' key
1698  * will have the link to the new file. and the 'error' key will be set to false.
1699  *
1700  * This function will not move an uploaded file to the upload folder. It will
1701  * create a new file with the content in $bits parameter. If you move the upload
1702  * file, read the content of the uploaded file, and then you can give the
1703  * filename and content to this function, which will add it to the upload
1704  * folder.
1705  *
1706  * The permissions will be set on the new file automatically by this function.
1707  *
1708  * @since 2.0.0
1709  *
1710  * @param string $name
1711  * @param null $deprecated Never used. Set to null.
1712  * @param mixed $bits File content
1713  * @param string $time Optional. Time formatted in 'yyyy/mm'.
1714  * @return array
1715  */
1716 function wp_upload_bits( $name, $deprecated, $bits, $time = null ) {
1717         if ( !empty( $deprecated ) )
1718                 _deprecated_argument( __FUNCTION__, '2.0' );
1719
1720         if ( empty( $name ) )
1721                 return array( 'error' => __( 'Empty filename' ) );
1722
1723         $wp_filetype = wp_check_filetype( $name );
1724         if ( ! $wp_filetype['ext'] && ! current_user_can( 'unfiltered_upload' ) )
1725                 return array( 'error' => __( 'Invalid file type' ) );
1726
1727         $upload = wp_upload_dir( $time );
1728
1729         if ( $upload['error'] !== false )
1730                 return $upload;
1731
1732         $upload_bits_error = apply_filters( 'wp_upload_bits', array( 'name' => $name, 'bits' => $bits, 'time' => $time ) );
1733         if ( !is_array( $upload_bits_error ) ) {
1734                 $upload[ 'error' ] = $upload_bits_error;
1735                 return $upload;
1736         }
1737
1738         $filename = wp_unique_filename( $upload['path'], $name );
1739
1740         $new_file = $upload['path'] . "/$filename";
1741         if ( ! wp_mkdir_p( dirname( $new_file ) ) ) {
1742                 if ( 0 === strpos( $upload['basedir'], ABSPATH ) )
1743                         $error_path = str_replace( ABSPATH, '', $upload['basedir'] ) . $upload['subdir'];
1744                 else
1745                         $error_path = basename( $upload['basedir'] ) . $upload['subdir'];
1746
1747                 $message = sprintf( __( 'Unable to create directory %s. Is its parent directory writable by the server?' ), $error_path );
1748                 return array( 'error' => $message );
1749         }
1750
1751         $ifp = @ fopen( $new_file, 'wb' );
1752         if ( ! $ifp )
1753                 return array( 'error' => sprintf( __( 'Could not write file %s' ), $new_file ) );
1754
1755         @fwrite( $ifp, $bits );
1756         fclose( $ifp );
1757         clearstatcache();
1758
1759         // Set correct file permissions
1760         $stat = @ stat( dirname( $new_file ) );
1761         $perms = $stat['mode'] & 0007777;
1762         $perms = $perms & 0000666;
1763         @ chmod( $new_file, $perms );
1764         clearstatcache();
1765
1766         // Compute the URL
1767         $url = $upload['url'] . "/$filename";
1768
1769         return array( 'file' => $new_file, 'url' => $url, 'error' => false );
1770 }
1771
1772 /**
1773  * Retrieve the file type based on the extension name.
1774  *
1775  * @package WordPress
1776  * @since 2.5.0
1777  * @uses apply_filters() Calls 'ext2type' hook on default supported types.
1778  *
1779  * @param string $ext The extension to search.
1780  * @return string|null The file type, example: audio, video, document, spreadsheet, etc. Null if not found.
1781  */
1782 function wp_ext2type( $ext ) {
1783         $ext2type = apply_filters( 'ext2type', array(
1784                 'audio'       => array( 'aac', 'ac3',  'aif',  'aiff', 'm3a',  'm4a',   'm4b',  'mka',  'mp1',  'mp2',  'mp3', 'ogg', 'oga', 'ram', 'wav', 'wma' ),
1785                 'video'       => array( 'asf', 'avi',  'divx', 'dv',   'flv',  'm4v',   'mkv',  'mov',  'mp4',  'mpeg', 'mpg', 'mpv', 'ogm', 'ogv', 'qt',  'rm', 'vob', 'wmv' ),
1786                 'document'    => array( 'doc', 'docx', 'docm', 'dotm', 'odt',  'pages', 'pdf',  'rtf',  'wp',   'wpd' ),
1787                 'spreadsheet' => array( 'numbers',     'ods',  'xls',  'xlsx', 'xlsm',  'xlsb' ),
1788                 'interactive' => array( 'swf', 'key',  'ppt',  'pptx', 'pptm', 'pps',   'ppsx', 'ppsm', 'sldx', 'sldm', 'odp' ),
1789                 'text'        => array( 'asc', 'csv',  'tsv',  'txt' ),
1790                 'archive'     => array( 'bz2', 'cab',  'dmg',  'gz',   'rar',  'sea',   'sit',  'sqx',  'tar',  'tgz',  'zip', '7z' ),
1791                 'code'        => array( 'css', 'htm',  'html', 'php',  'js' ),
1792         ));
1793         foreach ( $ext2type as $type => $exts )
1794                 if ( in_array( $ext, $exts ) )
1795                         return $type;
1796 }
1797
1798 /**
1799  * Retrieve the file type from the file name.
1800  *
1801  * You can optionally define the mime array, if needed.
1802  *
1803  * @since 2.0.4
1804  *
1805  * @param string $filename File name or path.
1806  * @param array $mimes Optional. Key is the file extension with value as the mime type.
1807  * @return array Values with extension first and mime type.
1808  */
1809 function wp_check_filetype( $filename, $mimes = null ) {
1810         if ( empty($mimes) )
1811                 $mimes = get_allowed_mime_types();
1812         $type = false;
1813         $ext = false;
1814
1815         foreach ( $mimes as $ext_preg => $mime_match ) {
1816                 $ext_preg = '!\.(' . $ext_preg . ')$!i';
1817                 if ( preg_match( $ext_preg, $filename, $ext_matches ) ) {
1818                         $type = $mime_match;
1819                         $ext = $ext_matches[1];
1820                         break;
1821                 }
1822         }
1823
1824         return compact( 'ext', 'type' );
1825 }
1826
1827 /**
1828  * Attempt to determine the real file type of a file.
1829  * If unable to, the file name extension will be used to determine type.
1830  *
1831  * If it's determined that the extension does not match the file's real type,
1832  * then the "proper_filename" value will be set with a proper filename and extension.
1833  *
1834  * Currently this function only supports validating images known to getimagesize().
1835  *
1836  * @since 3.0.0
1837  *
1838  * @param string $file Full path to the image.
1839  * @param string $filename The filename of the image (may differ from $file due to $file being in a tmp directory)
1840  * @param array $mimes Optional. Key is the file extension with value as the mime type.
1841  * @return array Values for the extension, MIME, and either a corrected filename or false if original $filename is valid
1842  */
1843 function wp_check_filetype_and_ext( $file, $filename, $mimes = null ) {
1844
1845         $proper_filename = false;
1846
1847         // Do basic extension validation and MIME mapping
1848         $wp_filetype = wp_check_filetype( $filename, $mimes );
1849         extract( $wp_filetype );
1850
1851         // We can't do any further validation without a file to work with
1852         if ( ! file_exists( $file ) )
1853                 return compact( 'ext', 'type', 'proper_filename' );
1854
1855         // We're able to validate images using GD
1856         if ( $type && 0 === strpos( $type, 'image/' ) && function_exists('getimagesize') ) {
1857
1858                 // Attempt to figure out what type of image it actually is
1859                 $imgstats = @getimagesize( $file );
1860
1861                 // If getimagesize() knows what kind of image it really is and if the real MIME doesn't match the claimed MIME
1862                 if ( !empty($imgstats['mime']) && $imgstats['mime'] != $type ) {
1863                         // This is a simplified array of MIMEs that getimagesize() can detect and their extensions
1864                         // You shouldn't need to use this filter, but it's here just in case
1865                         $mime_to_ext = apply_filters( 'getimagesize_mimes_to_exts', array(
1866                                 'image/jpeg' => 'jpg',
1867                                 'image/png'  => 'png',
1868                                 'image/gif'  => 'gif',
1869                                 'image/bmp'  => 'bmp',
1870                                 'image/tiff' => 'tif',
1871                         ) );
1872
1873                         // Replace whatever is after the last period in the filename with the correct extension
1874                         if ( ! empty( $mime_to_ext[ $imgstats['mime'] ] ) ) {
1875                                 $filename_parts = explode( '.', $filename );
1876                                 array_pop( $filename_parts );
1877                                 $filename_parts[] = $mime_to_ext[ $imgstats['mime'] ];
1878                                 $new_filename = implode( '.', $filename_parts );
1879
1880                                 if ( $new_filename != $filename )
1881                                         $proper_filename = $new_filename; // Mark that it changed
1882
1883                                 // Redefine the extension / MIME
1884                                 $wp_filetype = wp_check_filetype( $new_filename, $mimes );
1885                                 extract( $wp_filetype );
1886                         }
1887                 }
1888         }
1889
1890         // Let plugins try and validate other types of files
1891         // Should return an array in the style of array( 'ext' => $ext, 'type' => $type, 'proper_filename' => $proper_filename )
1892         return apply_filters( 'wp_check_filetype_and_ext', compact( 'ext', 'type', 'proper_filename' ), $file, $filename, $mimes );
1893 }
1894
1895 /**
1896  * Retrieve list of mime types and file extensions.
1897  *
1898  * @since 3.5.0
1899  *
1900  * @uses apply_filters() Calls 'mime_types' on returned array. This filter should
1901  * be used to add types, not remove them. To remove types use the upload_mimes filter.
1902  *
1903  * @return array Array of mime types keyed by the file extension regex corresponding to those types.
1904  */
1905 function wp_get_mime_types() {
1906         // Accepted MIME types are set here as PCRE unless provided.
1907         return apply_filters( 'mime_types', array(
1908         // Image formats
1909         'jpg|jpeg|jpe' => 'image/jpeg',
1910         'gif' => 'image/gif',
1911         'png' => 'image/png',
1912         'bmp' => 'image/bmp',
1913         'tif|tiff' => 'image/tiff',
1914         'ico' => 'image/x-icon',
1915         // Video formats
1916         'asf|asx' => 'video/x-ms-asf',
1917         'wmv' => 'video/x-ms-wmv',
1918         'wmx' => 'video/x-ms-wmx',
1919         'wm' => 'video/x-ms-wm',
1920         'avi' => 'video/avi',
1921         'divx' => 'video/divx',
1922         'flv' => 'video/x-flv',
1923         'mov|qt' => 'video/quicktime',
1924         'mpeg|mpg|mpe' => 'video/mpeg',
1925         'mp4|m4v' => 'video/mp4',
1926         'ogv' => 'video/ogg',
1927         'webm' => 'video/webm',
1928         'mkv' => 'video/x-matroska',
1929         // Text formats
1930         'txt|asc|c|cc|h' => 'text/plain',
1931         'csv' => 'text/csv',
1932         'tsv' => 'text/tab-separated-values',
1933         'ics' => 'text/calendar',
1934         'rtx' => 'text/richtext',
1935         'css' => 'text/css',
1936         'htm|html' => 'text/html',
1937         // Audio formats
1938         'mp3|m4a|m4b' => 'audio/mpeg',
1939         'ra|ram' => 'audio/x-realaudio',
1940         'wav' => 'audio/wav',
1941         'ogg|oga' => 'audio/ogg',
1942         'mid|midi' => 'audio/midi',
1943         'wma' => 'audio/x-ms-wma',
1944         'wax' => 'audio/x-ms-wax',
1945         'mka' => 'audio/x-matroska',
1946         // Misc application formats
1947         'rtf' => 'application/rtf',
1948         'js' => 'application/javascript',
1949         'pdf' => 'application/pdf',
1950         'swf' => 'application/x-shockwave-flash',
1951         'class' => 'application/java',
1952         'tar' => 'application/x-tar',
1953         'zip' => 'application/zip',
1954         'gz|gzip' => 'application/x-gzip',
1955         'rar' => 'application/rar',
1956         '7z' => 'application/x-7z-compressed',
1957         'exe' => 'application/x-msdownload',
1958         // MS Office formats
1959         'doc' => 'application/msword',
1960         'pot|pps|ppt' => 'application/vnd.ms-powerpoint',
1961         'wri' => 'application/vnd.ms-write',
1962         'xla|xls|xlt|xlw' => 'application/vnd.ms-excel',
1963         'mdb' => 'application/vnd.ms-access',
1964         'mpp' => 'application/vnd.ms-project',
1965         'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
1966         'docm' => 'application/vnd.ms-word.document.macroEnabled.12',
1967         'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
1968         'dotm' => 'application/vnd.ms-word.template.macroEnabled.12',
1969         'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
1970         'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
1971         'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
1972         'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
1973         'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
1974         'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
1975         'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
1976         'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
1977         'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
1978         'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
1979         'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
1980         'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
1981         'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
1982         'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
1983         'sldm' => 'application/vnd.ms-powerpoint.slide.macroEnabled.12',
1984         'onetoc|onetoc2|onetmp|onepkg' => 'application/onenote',
1985         // OpenOffice formats
1986         'odt' => 'application/vnd.oasis.opendocument.text',
1987         'odp' => 'application/vnd.oasis.opendocument.presentation',
1988         'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
1989         'odg' => 'application/vnd.oasis.opendocument.graphics',
1990         'odc' => 'application/vnd.oasis.opendocument.chart',
1991         'odb' => 'application/vnd.oasis.opendocument.database',
1992         'odf' => 'application/vnd.oasis.opendocument.formula',
1993         // WordPerfect formats
1994         'wp|wpd' => 'application/wordperfect',
1995         // iWork formats
1996         'key' => 'application/vnd.apple.keynote',
1997         'numbers' => 'application/vnd.apple.numbers',
1998         'pages' => 'application/vnd.apple.pages',
1999         ) );
2000 }
2001 /**
2002  * Retrieve list of allowed mime types and file extensions.
2003  *
2004  * @since 2.8.6
2005  *
2006  * @uses apply_filters() Calls 'upload_mimes' on returned array
2007  * @uses wp_get_upload_mime_types() to fetch the list of mime types
2008  *
2009  * @return array Array of mime types keyed by the file extension regex corresponding to those types.
2010  */
2011 function get_allowed_mime_types() {
2012         return apply_filters( 'upload_mimes', wp_get_mime_types() );
2013 }
2014
2015 /**
2016  * Display "Are You Sure" message to confirm the action being taken.
2017  *
2018  * If the action has the nonce explain message, then it will be displayed along
2019  * with the "Are you sure?" message.
2020  *
2021  * @package WordPress
2022  * @subpackage Security
2023  * @since 2.0.4
2024  *
2025  * @param string $action The nonce action.
2026  */
2027 function wp_nonce_ays( $action ) {
2028         $title = __( 'WordPress Failure Notice' );
2029         if ( 'log-out' == $action ) {
2030                 $html = sprintf( __( 'You are attempting to log out of %s' ), get_bloginfo( 'name' ) ) . '</p><p>';
2031                 $html .= sprintf( __( "Do you really want to <a href='%s'>log out</a>?"), wp_logout_url() );
2032         } else {
2033                 $html = __( 'Are you sure you want to do this?' );
2034                 if ( wp_get_referer() )
2035                         $html .= "</p><p><a href='" . esc_url( remove_query_arg( 'updated', wp_get_referer() ) ) . "'>" . __( 'Please try again.' ) . "</a>";
2036         }
2037
2038         wp_die( $html, $title, array('response' => 403) );
2039 }
2040
2041 /**
2042  * Kill WordPress execution and display HTML message with error message.
2043  *
2044  * This function complements the die() PHP function. The difference is that
2045  * HTML will be displayed to the user. It is recommended to use this function
2046  * only, when the execution should not continue any further. It is not
2047  * recommended to call this function very often and try to handle as many errors
2048  * as possible silently.
2049  *
2050  * @since 2.0.4
2051  *
2052  * @param string $message Error message.
2053  * @param string $title Error title.
2054  * @param string|array $args Optional arguments to control behavior.
2055  */
2056 function wp_die( $message = '', $title = '', $args = array() ) {
2057         if ( defined( 'DOING_AJAX' ) && DOING_AJAX )
2058                 $function = apply_filters( 'wp_die_ajax_handler', '_ajax_wp_die_handler' );
2059         elseif ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST )
2060                 $function = apply_filters( 'wp_die_xmlrpc_handler', '_xmlrpc_wp_die_handler' );
2061         else
2062                 $function = apply_filters( 'wp_die_handler', '_default_wp_die_handler' );
2063
2064         call_user_func( $function, $message, $title, $args );
2065 }
2066
2067 /**
2068  * Kill WordPress execution and display HTML message with error message.
2069  *
2070  * This is the default handler for wp_die if you want a custom one for your
2071  * site then you can overload using the wp_die_handler filter in wp_die
2072  *
2073  * @since 3.0.0
2074  * @access private
2075  *
2076  * @param string $message Error message.
2077  * @param string $title Error title.
2078  * @param string|array $args Optional arguments to control behavior.
2079  */
2080 function _default_wp_die_handler( $message, $title = '', $args = array() ) {
2081         $defaults = array( 'response' => 500 );
2082         $r = wp_parse_args($args, $defaults);
2083
2084         $have_gettext = function_exists('__');
2085
2086         if ( function_exists( 'is_wp_error' ) && is_wp_error( $message ) ) {
2087                 if ( empty( $title ) ) {
2088                         $error_data = $message->get_error_data();
2089                         if ( is_array( $error_data ) && isset( $error_data['title'] ) )
2090                                 $title = $error_data['title'];
2091                 }
2092                 $errors = $message->get_error_messages();
2093                 switch ( count( $errors ) ) :
2094                 case 0 :
2095                         $message = '';
2096                         break;
2097                 case 1 :
2098                         $message = "<p>{$errors[0]}</p>";
2099                         break;
2100                 default :
2101                         $message = "<ul>\n\t\t<li>" . join( "</li>\n\t\t<li>", $errors ) . "</li>\n\t</ul>";
2102                         break;
2103                 endswitch;
2104         } elseif ( is_string( $message ) ) {
2105                 $message = "<p>$message</p>";
2106         }
2107
2108         if ( isset( $r['back_link'] ) && $r['back_link'] ) {
2109                 $back_text = $have_gettext? __('&laquo; Back') : '&laquo; Back';
2110                 $message .= "\n<p><a href='javascript:history.back()'>$back_text</a></p>";
2111         }
2112
2113         if ( ! did_action( 'admin_head' ) ) :
2114                 if ( !headers_sent() ) {
2115                         status_header( $r['response'] );
2116                         nocache_headers();
2117                         header( 'Content-Type: text/html; charset=utf-8' );
2118                 }
2119
2120                 if ( empty($title) )
2121                         $title = $have_gettext ? __('WordPress &rsaquo; Error') : 'WordPress &rsaquo; Error';
2122
2123                 $text_direction = 'ltr';
2124                 if ( isset($r['text_direction']) && 'rtl' == $r['text_direction'] )
2125                         $text_direction = 'rtl';
2126                 elseif ( function_exists( 'is_rtl' ) && is_rtl() )
2127                         $text_direction = 'rtl';
2128 ?>
2129 <!DOCTYPE html>
2130 <!-- Ticket #11289, IE bug fix: always pad the error page with enough characters such that it is greater than 512 bytes, even after gzip compression abcdefghijklmnopqrstuvwxyz1234567890aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz11223344556677889900abacbcbdcdcededfefegfgfhghgihihjijikjkjlklkmlmlnmnmononpopoqpqprqrqsrsrtstsubcbcdcdedefefgfabcadefbghicjkldmnoepqrfstugvwxhyz1i234j567k890laabmbccnddeoeffpgghqhiirjjksklltmmnunoovppqwqrrxsstytuuzvvw0wxx1yyz2z113223434455666777889890091abc2def3ghi4jkl5mno6pqr7stu8vwx9yz11aab2bcc3dd4ee5ff6gg7hh8ii9j0jk1kl2lmm3nnoo4p5pq6qrr7ss8tt9uuvv0wwx1x2yyzz13aba4cbcb5dcdc6dedfef8egf9gfh0ghg1ihi2hji3jik4jkj5lkl6kml7mln8mnm9ono
2131 -->
2132 <html xmlns="http://www.w3.org/1999/xhtml" <?php if ( function_exists( 'language_attributes' ) && function_exists( 'is_rtl' ) ) language_attributes(); else echo "dir='$text_direction'"; ?>>
2133 <head>
2134         <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
2135         <title><?php echo $title ?></title>
2136         <style type="text/css">
2137                 html {
2138                         background: #f9f9f9;
2139                 }
2140                 body {
2141                         background: #fff;
2142                         color: #333;
2143                         font-family: sans-serif;
2144                         margin: 2em auto;
2145                         padding: 1em 2em;
2146                         -webkit-border-radius: 3px;
2147                         border-radius: 3px;
2148                         border: 1px solid #dfdfdf;
2149                         max-width: 700px;
2150                 }
2151                 h1 {
2152                         border-bottom: 1px solid #dadada;
2153                         clear: both;
2154                         color: #666;
2155                         font: 24px Georgia, "Times New Roman", Times, serif;
2156                         margin: 30px 0 0 0;
2157                         padding: 0;
2158                         padding-bottom: 7px;
2159                 }
2160                 #error-page {
2161                         margin-top: 50px;
2162                 }
2163                 #error-page p {
2164                         font-size: 14px;
2165                         line-height: 1.5;
2166                         margin: 25px 0 20px;
2167                 }
2168                 #error-page code {
2169                         font-family: Consolas, Monaco, monospace;
2170                 }
2171                 ul li {
2172                         margin-bottom: 10px;
2173                         font-size: 14px ;
2174                 }
2175                 a {
2176                         color: #21759B;
2177                         text-decoration: none;
2178                 }
2179                 a:hover {
2180                         color: #D54E21;
2181                 }
2182                 .button {
2183                         display: inline-block;
2184                         text-decoration: none;
2185                         font-size: 14px;
2186                         line-height: 23px;
2187                         height: 24px;
2188                         margin: 0;
2189                         padding: 0 10px 1px;
2190                         cursor: pointer;
2191                         border-width: 1px;
2192                         border-style: solid;
2193                         -webkit-border-radius: 3px;
2194                         border-radius: 3px;
2195                         white-space: nowrap;
2196                         -webkit-box-sizing: border-box;
2197                         -moz-box-sizing:    border-box;
2198                         box-sizing:         border-box;
2199                         background: #f3f3f3;
2200                         background-image: -webkit-gradient(linear, left top, left bottom, from(#fefefe), to(#f4f4f4));
2201                         background-image: -webkit-linear-gradient(top, #fefefe, #f4f4f4);
2202                         background-image:    -moz-linear-gradient(top, #fefefe, #f4f4f4);
2203                         background-image:      -o-linear-gradient(top, #fefefe, #f4f4f4);
2204                         background-image:   linear-gradient(to bottom, #fefefe, #f4f4f4);
2205                         border-color: #bbb;
2206                         color: #333;
2207                         text-shadow: 0 1px 0 #fff;
2208                 }
2209
2210                 .button.button-large {
2211                         height: 29px;
2212                         line-height: 28px;
2213                         padding: 0 12px;
2214                 }
2215
2216                 .button:hover,
2217                 .button:focus {
2218                         background: #f3f3f3;
2219                         background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f3f3f3));
2220                         background-image: -webkit-linear-gradient(top, #fff, #f3f3f3);
2221                         background-image:    -moz-linear-gradient(top, #fff, #f3f3f3);
2222                         background-image:     -ms-linear-gradient(top, #fff, #f3f3f3);
2223                         background-image:      -o-linear-gradient(top, #fff, #f3f3f3);
2224                         background-image:   linear-gradient(to bottom, #fff, #f3f3f3);
2225                         border-color: #999;
2226                         color: #222;
2227                 }
2228
2229                 .button:focus  {
2230                         -webkit-box-shadow: 1px 1px 1px rgba(0,0,0,.2);
2231                         box-shadow: 1px 1px 1px rgba(0,0,0,.2);
2232                 }
2233
2234                 .button:active {
2235                         outline: none;
2236                         background: #eee;
2237                         background-image: -webkit-gradient(linear, left top, left bottom, from(#f4f4f4), to(#fefefe));
2238                         background-image: -webkit-linear-gradient(top, #f4f4f4, #fefefe);
2239                         background-image:    -moz-linear-gradient(top, #f4f4f4, #fefefe);
2240                         background-image:     -ms-linear-gradient(top, #f4f4f4, #fefefe);
2241                         background-image:      -o-linear-gradient(top, #f4f4f4, #fefefe);
2242                         background-image:   linear-gradient(to bottom, #f4f4f4, #fefefe);
2243                         border-color: #999;
2244                         color: #333;
2245                         text-shadow: 0 -1px 0 #fff;
2246                         -webkit-box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 );
2247                         box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 );
2248                 }
2249
2250                 <?php if ( 'rtl' == $text_direction ) : ?>
2251                 body { font-family: Tahoma, Arial; }
2252                 <?php endif; ?>
2253         </style>
2254 </head>
2255 <body id="error-page">
2256 <?php endif; // ! did_action( 'admin_head' ) ?>
2257         <?php echo $message; ?>
2258 </body>
2259 </html>
2260 <?php
2261         die();
2262 }
2263
2264 /**
2265  * Kill WordPress execution and display XML message with error message.
2266  *
2267  * This is the handler for wp_die when processing XMLRPC requests.
2268  *
2269  * @since 3.2.0
2270  * @access private
2271  *
2272  * @param string $message Error message.
2273  * @param string $title Error title.
2274  * @param string|array $args Optional arguments to control behavior.
2275  */
2276 function _xmlrpc_wp_die_handler( $message, $title = '', $args = array() ) {
2277         global $wp_xmlrpc_server;
2278         $defaults = array( 'response' => 500 );
2279
2280         $r = wp_parse_args($args, $defaults);
2281
2282         if ( $wp_xmlrpc_server ) {
2283                 $error = new IXR_Error( $r['response'] , $message);
2284                 $wp_xmlrpc_server->output( $error->getXml() );
2285         }
2286         die();
2287 }
2288
2289 /**
2290  * Kill WordPress ajax execution.
2291  *
2292  * This is the handler for wp_die when processing Ajax requests.
2293  *
2294  * @since 3.4.0
2295  * @access private
2296  *
2297  * @param string $message Optional. Response to print.
2298  */
2299 function _ajax_wp_die_handler( $message = '' ) {
2300         if ( is_scalar( $message ) )
2301                 die( (string) $message );
2302         die( '0' );
2303 }
2304
2305 /**
2306  * Kill WordPress execution.
2307  *
2308  * This is the handler for wp_die when processing APP requests.
2309  *
2310  * @since 3.4.0
2311  * @access private
2312  *
2313  * @param string $message Optional. Response to print.
2314  */
2315 function _scalar_wp_die_handler( $message = '' ) {
2316         if ( is_scalar( $message ) )
2317                 die( (string) $message );
2318         die();
2319 }
2320
2321 /**
2322  * Send a JSON response back to an Ajax request.
2323  *
2324  * @since 3.5.0
2325  *
2326  * @param mixed $response Variable (usually an array or object) to encode as JSON, then print and die.
2327  */
2328 function wp_send_json( $response ) {
2329         @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
2330         echo json_encode( $response );
2331         if ( defined( 'DOING_AJAX' ) && DOING_AJAX )
2332                 wp_die();
2333         else
2334                 die;
2335 }
2336
2337 /**
2338  * Send a JSON response back to an Ajax request, indicating success.
2339  *
2340  * @since 3.5.0
2341  *
2342  * @param mixed $data Data to encode as JSON, then print and die.
2343  */
2344 function wp_send_json_success( $data = null ) {
2345         $response = array( 'success' => true );
2346
2347         if ( isset( $data ) )
2348                 $response['data'] = $data;
2349
2350         wp_send_json( $response );
2351 }
2352
2353 /**
2354  * Send a JSON response back to an Ajax request, indicating failure.
2355  *
2356  * @since 3.5.0
2357  *
2358  * @param mixed $data Data to encode as JSON, then print and die.
2359  */
2360 function wp_send_json_error( $data = null ) {
2361         $response = array( 'success' => false );
2362
2363         if ( isset( $data ) )
2364                 $response['data'] = $data;
2365
2366         wp_send_json( $response );
2367 }
2368
2369 /**
2370  * Retrieve the WordPress home page URL.
2371  *
2372  * If the constant named 'WP_HOME' exists, then it will be used and returned by
2373  * the function. This can be used to counter the redirection on your local
2374  * development environment.
2375  *
2376  * @access private
2377  * @package WordPress
2378  * @since 2.2.0
2379  *
2380  * @param string $url URL for the home location
2381  * @return string Homepage location.
2382  */
2383 function _config_wp_home( $url = '' ) {
2384         if ( defined( 'WP_HOME' ) )
2385                 return untrailingslashit( WP_HOME );
2386         return $url;
2387 }
2388
2389 /**
2390  * Retrieve the WordPress site URL.
2391  *
2392  * If the constant named 'WP_SITEURL' is defined, then the value in that
2393  * constant will always be returned. This can be used for debugging a site on
2394  * your localhost while not having to change the database to your URL.
2395  *
2396  * @access private
2397  * @package WordPress
2398  * @since 2.2.0
2399  *
2400  * @param string $url URL to set the WordPress site location.
2401  * @return string The WordPress Site URL
2402  */
2403 function _config_wp_siteurl( $url = '' ) {
2404         if ( defined( 'WP_SITEURL' ) )
2405                 return untrailingslashit( WP_SITEURL );
2406         return $url;
2407 }
2408
2409 /**
2410  * Set the localized direction for MCE plugin.
2411  *
2412  * Will only set the direction to 'rtl', if the WordPress locale has the text
2413  * direction set to 'rtl'.
2414  *
2415  * Fills in the 'directionality', 'plugins', and 'theme_advanced_button1' array
2416  * keys. These keys are then returned in the $input array.
2417  *
2418  * @access private
2419  * @package WordPress
2420  * @subpackage MCE
2421  * @since 2.1.0
2422  *
2423  * @param array $input MCE plugin array.
2424  * @return array Direction set for 'rtl', if needed by locale.
2425  */
2426 function _mce_set_direction( $input ) {
2427         if ( is_rtl() ) {
2428                 $input['directionality'] = 'rtl';
2429                 $input['plugins'] .= ',directionality';
2430                 $input['theme_advanced_buttons1'] .= ',ltr';
2431         }
2432
2433         return $input;
2434 }
2435
2436 /**
2437  * Convert smiley code to the icon graphic file equivalent.
2438  *
2439  * You can turn off smilies, by going to the write setting screen and unchecking
2440  * the box, or by setting 'use_smilies' option to false or removing the option.
2441  *
2442  * Plugins may override the default smiley list by setting the $wpsmiliestrans
2443  * to an array, with the key the code the blogger types in and the value the
2444  * image file.
2445  *
2446  * The $wp_smiliessearch global is for the regular expression and is set each
2447  * time the function is called.
2448  *
2449  * The full list of smilies can be found in the function and won't be listed in
2450  * the description. Probably should create a Codex page for it, so that it is
2451  * available.
2452  *
2453  * @global array $wpsmiliestrans
2454  * @global array $wp_smiliessearch
2455  * @since 2.2.0
2456  */
2457 function smilies_init() {
2458         global $wpsmiliestrans, $wp_smiliessearch;
2459
2460         // don't bother setting up smilies if they are disabled
2461         if ( !get_option( 'use_smilies' ) )
2462                 return;
2463
2464         if ( !isset( $wpsmiliestrans ) ) {
2465                 $wpsmiliestrans = array(
2466                 ':mrgreen:' => 'icon_mrgreen.gif',
2467                 ':neutral:' => 'icon_neutral.gif',
2468                 ':twisted:' => 'icon_twisted.gif',
2469                   ':arrow:' => 'icon_arrow.gif',
2470                   ':shock:' => 'icon_eek.gif',
2471                   ':smile:' => 'icon_smile.gif',
2472                     ':???:' => 'icon_confused.gif',
2473                    ':cool:' => 'icon_cool.gif',
2474                    ':evil:' => 'icon_evil.gif',
2475                    ':grin:' => 'icon_biggrin.gif',
2476                    ':idea:' => 'icon_idea.gif',
2477                    ':oops:' => 'icon_redface.gif',
2478                    ':razz:' => 'icon_razz.gif',
2479                    ':roll:' => 'icon_rolleyes.gif',
2480                    ':wink:' => 'icon_wink.gif',
2481                     ':cry:' => 'icon_cry.gif',
2482                     ':eek:' => 'icon_surprised.gif',
2483                     ':lol:' => 'icon_lol.gif',
2484                     ':mad:' => 'icon_mad.gif',
2485                     ':sad:' => 'icon_sad.gif',
2486                       '8-)' => 'icon_cool.gif',
2487                       '8-O' => 'icon_eek.gif',
2488                       ':-(' => 'icon_sad.gif',
2489                       ':-)' => 'icon_smile.gif',
2490                       ':-?' => 'icon_confused.gif',
2491                       ':-D' => 'icon_biggrin.gif',
2492                       ':-P' => 'icon_razz.gif',
2493                       ':-o' => 'icon_surprised.gif',
2494                       ':-x' => 'icon_mad.gif',
2495                       ':-|' => 'icon_neutral.gif',
2496                       ';-)' => 'icon_wink.gif',
2497                 // This one transformation breaks regular text with frequency.
2498                 //     '8)' => 'icon_cool.gif',
2499                        '8O' => 'icon_eek.gif',
2500                        ':(' => 'icon_sad.gif',
2501                        ':)' => 'icon_smile.gif',
2502                        ':?' => 'icon_confused.gif',
2503                        ':D' => 'icon_biggrin.gif',
2504                        ':P' => 'icon_razz.gif',
2505                        ':o' => 'icon_surprised.gif',
2506                        ':x' => 'icon_mad.gif',
2507                        ':|' => 'icon_neutral.gif',
2508                        ';)' => 'icon_wink.gif',
2509                       ':!:' => 'icon_exclaim.gif',
2510                       ':?:' => 'icon_question.gif',
2511                 );
2512         }
2513
2514         if (count($wpsmiliestrans) == 0) {
2515                 return;
2516         }
2517
2518         /*
2519          * NOTE: we sort the smilies in reverse key order. This is to make sure
2520          * we match the longest possible smilie (:???: vs :?) as the regular
2521          * expression used below is first-match
2522          */
2523         krsort($wpsmiliestrans);
2524
2525         $wp_smiliessearch = '/(?:\s|^)';
2526
2527         $subchar = '';
2528         foreach ( (array) $wpsmiliestrans as $smiley => $img ) {
2529                 $firstchar = substr($smiley, 0, 1);
2530                 $rest = substr($smiley, 1);
2531
2532                 // new subpattern?
2533                 if ($firstchar != $subchar) {
2534                         if ($subchar != '') {
2535                                 $wp_smiliessearch .= ')|(?:\s|^)';
2536                         }
2537                         $subchar = $firstchar;
2538                         $wp_smiliessearch .= preg_quote($firstchar, '/') . '(?:';
2539                 } else {
2540                         $wp_smiliessearch .= '|';
2541                 }
2542                 $wp_smiliessearch .= preg_quote($rest, '/');
2543         }
2544
2545         $wp_smiliessearch .= ')(?:\s|$)/m';
2546 }
2547
2548 /**
2549  * Merge user defined arguments into defaults array.
2550  *
2551  * This function is used throughout WordPress to allow for both string or array
2552  * to be merged into another array.
2553  *
2554  * @since 2.2.0
2555  *
2556  * @param string|array $args Value to merge with $defaults
2557  * @param array $defaults Array that serves as the defaults.
2558  * @return array Merged user defined values with defaults.
2559  */
2560 function wp_parse_args( $args, $defaults = '' ) {
2561         if ( is_object( $args ) )
2562                 $r = get_object_vars( $args );
2563         elseif ( is_array( $args ) )
2564                 $r =& $args;
2565         else
2566                 wp_parse_str( $args, $r );
2567
2568         if ( is_array( $defaults ) )
2569                 return array_merge( $defaults, $r );
2570         return $r;
2571 }
2572
2573 /**
2574  * Clean up an array, comma- or space-separated list of IDs.
2575  *
2576  * @since 3.0.0
2577  *
2578  * @param array|string $list
2579  * @return array Sanitized array of IDs
2580  */
2581 function wp_parse_id_list( $list ) {
2582         if ( !is_array($list) )
2583                 $list = preg_split('/[\s,]+/', $list);
2584
2585         return array_unique(array_map('absint', $list));
2586 }
2587
2588 /**
2589  * Extract a slice of an array, given a list of keys.
2590  *
2591  * @since 3.1.0
2592  *
2593  * @param array $array The original array
2594  * @param array $keys The list of keys
2595  * @return array The array slice
2596  */
2597 function wp_array_slice_assoc( $array, $keys ) {
2598         $slice = array();
2599         foreach ( $keys as $key )
2600                 if ( isset( $array[ $key ] ) )
2601                         $slice[ $key ] = $array[ $key ];
2602
2603         return $slice;
2604 }
2605
2606 /**
2607  * Filters a list of objects, based on a set of key => value arguments.
2608  *
2609  * @since 3.0.0
2610  *
2611  * @param array $list An array of objects to filter
2612  * @param array $args An array of key => value arguments to match against each object
2613  * @param string $operator The logical operation to perform. 'or' means only one element
2614  *      from the array needs to match; 'and' means all elements must match. The default is 'and'.
2615  * @param bool|string $field A field from the object to place instead of the entire object
2616  * @return array A list of objects or object fields
2617  */
2618 function wp_filter_object_list( $list, $args = array(), $operator = 'and', $field = false ) {
2619         if ( ! is_array( $list ) )
2620                 return array();
2621
2622         $list = wp_list_filter( $list, $args, $operator );
2623
2624         if ( $field )
2625                 $list = wp_list_pluck( $list, $field );
2626
2627         return $list;
2628 }
2629
2630 /**
2631  * Filters a list of objects, based on a set of key => value arguments.
2632  *
2633  * @since 3.1.0
2634  *
2635  * @param array $list An array of objects to filter
2636  * @param array $args An array of key => value arguments to match against each object
2637  * @param string $operator The logical operation to perform:
2638  *    'AND' means all elements from the array must match;
2639  *    'OR' means only one element needs to match;
2640  *    'NOT' means no elements may match.
2641  *   The default is 'AND'.
2642  * @return array
2643  */
2644 function wp_list_filter( $list, $args = array(), $operator = 'AND' ) {
2645         if ( ! is_array( $list ) )
2646                 return array();
2647
2648         if ( empty( $args ) )
2649                 return $list;
2650
2651         $operator = strtoupper( $operator );
2652         $count = count( $args );
2653         $filtered = array();
2654
2655         foreach ( $list as $key => $obj ) {
2656                 $to_match = (array) $obj;
2657
2658                 $matched = 0;
2659                 foreach ( $args as $m_key => $m_value ) {
2660                         if ( array_key_exists( $m_key, $to_match ) && $m_value == $to_match[ $m_key ] )
2661                                 $matched++;
2662                 }
2663
2664                 if ( ( 'AND' == $operator && $matched == $count )
2665                   || ( 'OR' == $operator && $matched > 0 )
2666                   || ( 'NOT' == $operator && 0 == $matched ) ) {
2667                         $filtered[$key] = $obj;
2668                 }
2669         }
2670
2671         return $filtered;
2672 }
2673
2674 /**
2675  * Pluck a certain field out of each object in a list.
2676  *
2677  * @since 3.1.0
2678  *
2679  * @param array $list A list of objects or arrays
2680  * @param int|string $field A field from the object to place instead of the entire object
2681  * @return array
2682  */
2683 function wp_list_pluck( $list, $field ) {
2684         foreach ( $list as $key => $value ) {
2685                 if ( is_object( $value ) )
2686                         $list[ $key ] = $value->$field;
2687                 else
2688                         $list[ $key ] = $value[ $field ];
2689         }
2690
2691         return $list;
2692 }
2693
2694 /**
2695  * Determines if Widgets library should be loaded.
2696  *
2697  * Checks to make sure that the widgets library hasn't already been loaded. If
2698  * it hasn't, then it will load the widgets library and run an action hook.
2699  *
2700  * @since 2.2.0
2701  * @uses add_action() Calls '_admin_menu' hook with 'wp_widgets_add_menu' value.
2702  */
2703 function wp_maybe_load_widgets() {
2704         if ( ! apply_filters('load_default_widgets', true) )
2705                 return;
2706         require_once( ABSPATH . WPINC . '/default-widgets.php' );
2707         add_action( '_admin_menu', 'wp_widgets_add_menu' );
2708 }
2709
2710 /**
2711  * Append the Widgets menu to the themes main menu.
2712  *
2713  * @since 2.2.0
2714  * @uses $submenu The administration submenu list.
2715  */
2716 function wp_widgets_add_menu() {
2717         global $submenu;
2718
2719         if ( ! current_theme_supports( 'widgets' ) )
2720                 return;
2721
2722         $submenu['themes.php'][7] = array( __( 'Widgets' ), 'edit_theme_options', 'widgets.php' );
2723         ksort( $submenu['themes.php'], SORT_NUMERIC );
2724 }
2725
2726 /**
2727  * Flush all output buffers for PHP 5.2.
2728  *
2729  * Make sure all output buffers are flushed before our singletons our destroyed.
2730  *
2731  * @since 2.2.0
2732  */
2733 function wp_ob_end_flush_all() {
2734         $levels = ob_get_level();
2735         for ($i=0; $i<$levels; $i++)
2736                 ob_end_flush();
2737 }
2738
2739 /**
2740  * Load custom DB error or display WordPress DB error.
2741  *
2742  * If a file exists in the wp-content directory named db-error.php, then it will
2743  * be loaded instead of displaying the WordPress DB error. If it is not found,
2744  * then the WordPress DB error will be displayed instead.
2745  *
2746  * The WordPress DB error sets the HTTP status header to 500 to try to prevent
2747  * search engines from caching the message. Custom DB messages should do the
2748  * same.
2749  *
2750  * This function was backported to WordPress 2.3.2, but originally was added
2751  * in WordPress 2.5.0.
2752  *
2753  * @since 2.3.2
2754  * @uses $wpdb
2755  */
2756 function dead_db() {
2757         global $wpdb;
2758
2759         // Load custom DB error template, if present.
2760         if ( file_exists( WP_CONTENT_DIR . '/db-error.php' ) ) {
2761                 require_once( WP_CONTENT_DIR . '/db-error.php' );
2762                 die();
2763         }
2764
2765         // If installing or in the admin, provide the verbose message.
2766         if ( defined('WP_INSTALLING') || defined('WP_ADMIN') )
2767                 wp_die($wpdb->error);
2768
2769         // Otherwise, be terse.
2770         status_header( 500 );
2771         nocache_headers();
2772         header( 'Content-Type: text/html; charset=utf-8' );
2773
2774         wp_load_translations_early();
2775 ?>
2776 <!DOCTYPE html>
2777 <html xmlns="http://www.w3.org/1999/xhtml"<?php if ( is_rtl() ) echo ' dir="rtl"'; ?>>
2778 <head>
2779 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
2780         <title><?php _e( 'Database Error' ); ?></title>
2781
2782 </head>
2783 <body>
2784         <h1><?php _e( 'Error establishing a database connection' ); ?></h1>
2785 </body>
2786 </html>
2787 <?php
2788         die();
2789 }
2790
2791 /**
2792  * Converts value to nonnegative integer.
2793  *
2794  * @since 2.5.0
2795  *
2796  * @param mixed $maybeint Data you wish to have converted to a nonnegative integer
2797  * @return int An nonnegative integer
2798  */
2799 function absint( $maybeint ) {
2800         return abs( intval( $maybeint ) );
2801 }
2802
2803 /**
2804  * Determines if the blog can be accessed over SSL.
2805  *
2806  * Determines if blog can be accessed over SSL by using cURL to access the site
2807  * using the https in the siteurl. Requires cURL extension to work correctly.
2808  *
2809  * @since 2.5.0
2810  *
2811  * @param string $url
2812  * @return bool Whether SSL access is available
2813  */
2814 function url_is_accessable_via_ssl($url)
2815 {
2816         if ( in_array( 'curl', get_loaded_extensions() ) ) {
2817                 $ssl = set_url_scheme( $url, 'https' );
2818
2819                 $ch = curl_init();
2820                 curl_setopt($ch, CURLOPT_URL, $ssl);
2821                 curl_setopt($ch, CURLOPT_FAILONERROR, true);
2822                 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
2823                 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
2824                 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
2825
2826                 curl_exec($ch);
2827
2828                 $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2829                 curl_close ($ch);
2830
2831                 if ($status == 200 || $status == 401) {
2832                         return true;
2833                 }
2834         }
2835         return false;
2836 }
2837
2838 /**
2839  * Marks a function as deprecated and informs when it has been used.
2840  *
2841  * There is a hook deprecated_function_run that will be called that can be used
2842  * to get the backtrace up to what file and function called the deprecated
2843  * function.
2844  *
2845  * The current behavior is to trigger a user error if WP_DEBUG is true.
2846  *
2847  * This function is to be used in every function that is deprecated.
2848  *
2849  * @package WordPress
2850  * @subpackage Debug
2851  * @since 2.5.0
2852  * @access private
2853  *
2854  * @uses do_action() Calls 'deprecated_function_run' and passes the function name, what to use instead,
2855  *   and the version the function was deprecated in.
2856  * @uses apply_filters() Calls 'deprecated_function_trigger_error' and expects boolean value of true to do
2857  *   trigger or false to not trigger error.
2858  *
2859  * @param string $function The function that was called
2860  * @param string $version The version of WordPress that deprecated the function
2861  * @param string $replacement Optional. The function that should have been called
2862  */
2863 function _deprecated_function( $function, $version, $replacement = null ) {
2864
2865         do_action( 'deprecated_function_run', $function, $replacement, $version );
2866
2867         // Allow plugin to filter the output error trigger
2868         if ( WP_DEBUG && apply_filters( 'deprecated_function_trigger_error', true ) ) {
2869                 if ( function_exists( '__' ) ) {
2870                         if ( ! is_null( $replacement ) )
2871                                 trigger_error( sprintf( __('%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.'), $function, $version, $replacement ) );
2872                         else
2873                                 trigger_error( sprintf( __('%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.'), $function, $version ) );
2874                 } else {
2875                         if ( ! is_null( $replacement ) )
2876                                 trigger_error( sprintf( '%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.', $function, $version, $replacement ) );
2877                         else
2878                                 trigger_error( sprintf( '%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.', $function, $version ) );
2879                 }
2880         }
2881 }
2882
2883 /**
2884  * Marks a file as deprecated and informs when it has been used.
2885  *
2886  * There is a hook deprecated_file_included that will be called that can be used
2887  * to get the backtrace up to what file and function included the deprecated
2888  * file.
2889  *
2890  * The current behavior is to trigger a user error if WP_DEBUG is true.
2891  *
2892  * This function is to be used in every file that is deprecated.
2893  *
2894  * @package WordPress
2895  * @subpackage Debug
2896  * @since 2.5.0
2897  * @access private
2898  *
2899  * @uses do_action() Calls 'deprecated_file_included' and passes the file name, what to use instead,
2900  *   the version in which the file was deprecated, and any message regarding the change.
2901  * @uses apply_filters() Calls 'deprecated_file_trigger_error' and expects boolean value of true to do
2902  *   trigger or false to not trigger error.
2903  *
2904  * @param string $file The file that was included
2905  * @param string $version The version of WordPress that deprecated the file
2906  * @param string $replacement Optional. The file that should have been included based on ABSPATH
2907  * @param string $message Optional. A message regarding the change
2908  */
2909 function _deprecated_file( $file, $version, $replacement = null, $message = '' ) {
2910
2911         do_action( 'deprecated_file_included', $file, $replacement, $version, $message );
2912
2913         // Allow plugin to filter the output error trigger
2914         if ( WP_DEBUG && apply_filters( 'deprecated_file_trigger_error', true ) ) {
2915                 $message = empty( $message ) ? '' : ' ' . $message;
2916                 if ( function_exists( '__' ) ) {
2917                         if ( ! is_null( $replacement ) )
2918                                 trigger_error( sprintf( __('%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.'), $file, $version, $replacement ) . $message );
2919                         else
2920                                 trigger_error( sprintf( __('%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.'), $file, $version ) . $message );
2921                 } else {
2922                         if ( ! is_null( $replacement ) )
2923                                 trigger_error( sprintf( '%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.', $file, $version, $replacement ) . $message );
2924                         else
2925                                 trigger_error( sprintf( '%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.', $file, $version ) . $message );
2926                 }
2927         }
2928 }
2929 /**
2930  * Marks a function argument as deprecated and informs when it has been used.
2931  *
2932  * This function is to be used whenever a deprecated function argument is used.
2933  * Before this function is called, the argument must be checked for whether it was
2934  * used by comparing it to its default value or evaluating whether it is empty.
2935  * For example:
2936  * <code>
2937  * if ( !empty($deprecated) )
2938  *      _deprecated_argument( __FUNCTION__, '3.0' );
2939  * </code>
2940  *
2941  * There is a hook deprecated_argument_run that will be called that can be used
2942  * to get the backtrace up to what file and function used the deprecated
2943  * argument.
2944  *
2945  * The current behavior is to trigger a user error if WP_DEBUG is true.
2946  *
2947  * @package WordPress
2948  * @subpackage Debug
2949  * @since 3.0.0
2950  * @access private
2951  *
2952  * @uses do_action() Calls 'deprecated_argument_run' and passes the function name, a message on the change,
2953  *   and the version in which the argument was deprecated.
2954  * @uses apply_filters() Calls 'deprecated_argument_trigger_error' and expects boolean value of true to do
2955  *   trigger or false to not trigger error.
2956  *
2957  * @param string $function The function that was called
2958  * @param string $version The version of WordPress that deprecated the argument used
2959  * @param string $message Optional. A message regarding the change.
2960  */
2961 function _deprecated_argument( $function, $version, $message = null ) {
2962
2963         do_action( 'deprecated_argument_run', $function, $message, $version );
2964
2965         // Allow plugin to filter the output error trigger
2966         if ( WP_DEBUG && apply_filters( 'deprecated_argument_trigger_error', true ) ) {
2967                 if ( function_exists( '__' ) ) {
2968                         if ( ! is_null( $message ) )
2969                                 trigger_error( sprintf( __('%1$s was called with an argument that is <strong>deprecated</strong> since version %2$s! %3$s'), $function, $version, $message ) );
2970                         else
2971                                 trigger_error( sprintf( __('%1$s was called with an argument that is <strong>deprecated</strong> since version %2$s with no alternative available.'), $function, $version ) );
2972                 } else {
2973                         if ( ! is_null( $message ) )
2974                                 trigger_error( sprintf( '%1$s was called with an argument that is <strong>deprecated</strong> since version %2$s! %3$s', $function, $version, $message ) );
2975                         else
2976                                 trigger_error( sprintf( '%1$s was called with an argument that is <strong>deprecated</strong> since version %2$s with no alternative available.', $function, $version ) );
2977                 }
2978         }
2979 }
2980
2981 /**
2982  * Marks something as being incorrectly called.
2983  *
2984  * There is a hook doing_it_wrong_run that will be called that can be used
2985  * to get the backtrace up to what file and function called the deprecated
2986  * function.
2987  *
2988  * The current behavior is to trigger a user error if WP_DEBUG is true.
2989  *
2990  * @package WordPress
2991  * @subpackage Debug
2992  * @since 3.1.0
2993  * @access private
2994  *
2995  * @uses do_action() Calls 'doing_it_wrong_run' and passes the function arguments.
2996  * @uses apply_filters() Calls 'doing_it_wrong_trigger_error' and expects boolean value of true to do
2997  *   trigger or false to not trigger error.
2998  *
2999  * @param string $function The function that was called.
3000  * @param string $message A message explaining what has been done incorrectly.
3001  * @param string $version The version of WordPress where the message was added.
3002  */
3003 function _doing_it_wrong( $function, $message, $version ) {
3004
3005         do_action( 'doing_it_wrong_run', $function, $message, $version );
3006
3007         // Allow plugin to filter the output error trigger
3008         if ( WP_DEBUG && apply_filters( 'doing_it_wrong_trigger_error', true ) ) {
3009                 if ( function_exists( '__' ) ) {
3010                         $version = is_null( $version ) ? '' : sprintf( __( '(This message was added in version %s.)' ), $version );
3011                         $message .= ' ' . __( 'Please see <a href="http://codex.wordpress.org/Debugging_in_WordPress">Debugging in WordPress</a> for more information.' );
3012                         trigger_error( sprintf( __( '%1$s was called <strong>incorrectly</strong>. %2$s %3$s' ), $function, $message, $version ) );
3013                 } else {
3014                         $version = is_null( $version ) ? '' : sprintf( '(This message was added in version %s.)', $version );
3015                         $message .= ' Please see <a href="http://codex.wordpress.org/Debugging_in_WordPress">Debugging in WordPress</a> for more information.';
3016                         trigger_error( sprintf( '%1$s was called <strong>incorrectly</strong>. %2$s %3$s', $function, $message, $version ) );
3017                 }
3018         }
3019 }
3020
3021 /**
3022  * Is the server running earlier than 1.5.0 version of lighttpd?
3023  *
3024  * @since 2.5.0
3025  *
3026  * @return bool Whether the server is running lighttpd < 1.5.0
3027  */
3028 function is_lighttpd_before_150() {
3029         $server_parts = explode( '/', isset( $_SERVER['SERVER_SOFTWARE'] )? $_SERVER['SERVER_SOFTWARE'] : '' );
3030         $server_parts[1] = isset( $server_parts[1] )? $server_parts[1] : '';
3031         return  'lighttpd' == $server_parts[0] && -1 == version_compare( $server_parts[1], '1.5.0' );
3032 }
3033
3034 /**
3035  * Does the specified module exist in the Apache config?
3036  *
3037  * @since 2.5.0
3038  *
3039  * @param string $mod e.g. mod_rewrite
3040  * @param bool $default The default return value if the module is not found
3041  * @return bool
3042  */
3043 function apache_mod_loaded($mod, $default = false) {
3044         global $is_apache;
3045
3046         if ( !$is_apache )
3047                 return false;
3048
3049         if ( function_exists('apache_get_modules') ) {
3050                 $mods = apache_get_modules();
3051                 if ( in_array($mod, $mods) )
3052                         return true;
3053         } elseif ( function_exists('phpinfo') ) {
3054                         ob_start();
3055                         phpinfo(8);
3056                         $phpinfo = ob_get_clean();
3057                         if ( false !== strpos($phpinfo, $mod) )
3058                                 return true;
3059         }
3060         return $default;
3061 }
3062
3063 /**
3064  * Check if IIS 7+ supports pretty permalinks.
3065  *
3066  * @since 2.8.0
3067  *
3068  * @return bool
3069  */
3070 function iis7_supports_permalinks() {
3071         global $is_iis7;
3072
3073         $supports_permalinks = false;
3074         if ( $is_iis7 ) {
3075                 /* First we check if the DOMDocument class exists. If it does not exist, then we cannot
3076                  * easily update the xml configuration file, hence we just bail out and tell user that
3077                  * pretty permalinks cannot be used.
3078                  *
3079                  * Next we check if the URL Rewrite Module 1.1 is loaded and enabled for the web site. When
3080                  * URL Rewrite 1.1 is loaded it always sets a server variable called 'IIS_UrlRewriteModule'.
3081                  * Lastly we make sure that PHP is running via FastCGI. This is important because if it runs
3082                  * via ISAPI then pretty permalinks will not work.
3083                  */
3084                 $supports_permalinks = class_exists('DOMDocument') && isset($_SERVER['IIS_UrlRewriteModule']) && ( php_sapi_name() == 'cgi-fcgi' );
3085         }
3086
3087         return apply_filters('iis7_supports_permalinks', $supports_permalinks);
3088 }
3089
3090 /**
3091  * File validates against allowed set of defined rules.
3092  *
3093  * A return value of '1' means that the $file contains either '..' or './'. A
3094  * return value of '2' means that the $file contains ':' after the first
3095  * character. A return value of '3' means that the file is not in the allowed
3096  * files list.
3097  *
3098  * @since 1.2.0
3099  *
3100  * @param string $file File path.
3101  * @param array $allowed_files List of allowed files.
3102  * @return int 0 means nothing is wrong, greater than 0 means something was wrong.
3103  */
3104 function validate_file( $file, $allowed_files = '' ) {
3105         if ( false !== strpos( $file, '..' ) )
3106                 return 1;
3107
3108         if ( false !== strpos( $file, './' ) )
3109                 return 1;
3110
3111         if ( ! empty( $allowed_files ) && ! in_array( $file, $allowed_files ) )
3112                 return 3;
3113
3114         if (':' == substr( $file, 1, 1 ) )
3115                 return 2;
3116
3117         return 0;
3118 }
3119
3120 /**
3121  * Determine if SSL is used.
3122  *
3123  * @since 2.6.0
3124  *
3125  * @return bool True if SSL, false if not used.
3126  */
3127 function is_ssl() {
3128         if ( isset($_SERVER['HTTPS']) ) {
3129                 if ( 'on' == strtolower($_SERVER['HTTPS']) )
3130                         return true;
3131                 if ( '1' == $_SERVER['HTTPS'] )
3132                         return true;
3133         } elseif ( isset($_SERVER['SERVER_PORT']) && ( '443' == $_SERVER['SERVER_PORT'] ) ) {
3134                 return true;
3135         }
3136         return false;
3137 }
3138
3139 /**
3140  * Whether SSL login should be forced.
3141  *
3142  * @since 2.6.0
3143  *
3144  * @param string|bool $force Optional.
3145  * @return bool True if forced, false if not forced.
3146  */
3147 function force_ssl_login( $force = null ) {
3148         static $forced = false;
3149
3150         if ( !is_null( $force ) ) {
3151                 $old_forced = $forced;
3152                 $forced = $force;
3153                 return $old_forced;
3154         }
3155
3156         return $forced;
3157 }
3158
3159 /**
3160  * Whether to force SSL used for the Administration Screens.
3161  *
3162  * @since 2.6.0
3163  *
3164  * @param string|bool $force
3165  * @return bool True if forced, false if not forced.
3166  */
3167 function force_ssl_admin( $force = null ) {
3168         static $forced = false;
3169
3170         if ( !is_null( $force ) ) {
3171                 $old_forced = $forced;
3172                 $forced = $force;
3173                 return $old_forced;
3174         }
3175
3176         return $forced;
3177 }
3178
3179 /**
3180  * Guess the URL for the site.
3181  *
3182  * Will remove wp-admin links to retrieve only return URLs not in the wp-admin
3183  * directory.
3184  *
3185  * @since 2.6.0
3186  *
3187  * @return string
3188  */
3189 function wp_guess_url() {
3190         if ( defined('WP_SITEURL') && '' != WP_SITEURL ) {
3191                 $url = WP_SITEURL;
3192         } else {
3193                 $schema = is_ssl() ? 'https://' : 'http://'; // set_url_scheme() is not defined yet
3194                 $url = preg_replace( '#/(wp-admin/.*|wp-login.php)#i', '', $schema . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
3195         }
3196
3197         return rtrim($url, '/');
3198 }
3199
3200 /**
3201  * Temporarily suspend cache additions.
3202  *
3203  * Stops more data being added to the cache, but still allows cache retrieval.
3204  * This is useful for actions, such as imports, when a lot of data would otherwise
3205  * be almost uselessly added to the cache.
3206  *
3207  * Suspension lasts for a single page load at most. Remember to call this
3208  * function again if you wish to re-enable cache adds earlier.
3209  *
3210  * @since 3.3.0
3211  *
3212  * @param bool $suspend Optional. Suspends additions if true, re-enables them if false.
3213  * @return bool The current suspend setting
3214  */
3215 function wp_suspend_cache_addition( $suspend = null ) {
3216         static $_suspend = false;
3217
3218         if ( is_bool( $suspend ) )
3219                 $_suspend = $suspend;
3220
3221         return $_suspend;
3222 }
3223
3224 /**
3225  * Suspend cache invalidation.
3226  *
3227  * Turns cache invalidation on and off. Useful during imports where you don't wont to do invalidations
3228  * every time a post is inserted. Callers must be sure that what they are doing won't lead to an inconsistent
3229  * cache when invalidation is suspended.
3230  *
3231  * @since 2.7.0
3232  *
3233  * @param bool $suspend Whether to suspend or enable cache invalidation
3234  * @return bool The current suspend setting
3235  */
3236 function wp_suspend_cache_invalidation($suspend = true) {
3237         global $_wp_suspend_cache_invalidation;
3238
3239         $current_suspend = $_wp_suspend_cache_invalidation;
3240         $_wp_suspend_cache_invalidation = $suspend;
3241         return $current_suspend;
3242 }
3243
3244 /**
3245  * Is main site?
3246  *
3247  *
3248  * @since 3.0.0
3249  * @package WordPress
3250  *
3251  * @param int $blog_id optional blog id to test (default current blog)
3252  * @return bool True if not multisite or $blog_id is main site
3253  */
3254 function is_main_site( $blog_id = '' ) {
3255         global $current_site;
3256
3257         if ( ! is_multisite() )
3258                 return true;
3259
3260         if ( ! $blog_id )
3261                 $blog_id = get_current_blog_id();
3262
3263         return $blog_id == $current_site->blog_id;
3264 }
3265
3266 /**
3267  * Whether global terms are enabled.
3268  *
3269  *
3270  * @since 3.0.0
3271  * @package WordPress
3272  *
3273  * @return bool True if multisite and global terms enabled
3274  */
3275 function global_terms_enabled() {
3276         if ( ! is_multisite() )
3277                 return false;
3278
3279         static $global_terms = null;
3280         if ( is_null( $global_terms ) ) {
3281                 $filter = apply_filters( 'global_terms_enabled', null );
3282                 if ( ! is_null( $filter ) )
3283                         $global_terms = (bool) $filter;
3284                 else
3285                         $global_terms = (bool) get_site_option( 'global_terms_enabled', false );
3286         }
3287         return $global_terms;
3288 }
3289
3290 /**
3291  * gmt_offset modification for smart timezone handling.
3292  *
3293  * Overrides the gmt_offset option if we have a timezone_string available.
3294  *
3295  * @since 2.8.0
3296  *
3297  * @return float|bool
3298  */
3299 function wp_timezone_override_offset() {
3300         if ( !$timezone_string = get_option( 'timezone_string' ) ) {
3301                 return false;
3302         }
3303
3304         $timezone_object = timezone_open( $timezone_string );
3305         $datetime_object = date_create();
3306         if ( false === $timezone_object || false === $datetime_object ) {
3307                 return false;
3308         }
3309         return round( timezone_offset_get( $timezone_object, $datetime_object ) / HOUR_IN_SECONDS, 2 );
3310 }
3311
3312 /**
3313  * {@internal Missing Short Description}}
3314  *
3315  * @since 2.9.0
3316  *
3317  * @param unknown_type $a
3318  * @param unknown_type $b
3319  * @return int
3320  */
3321 function _wp_timezone_choice_usort_callback( $a, $b ) {
3322         // Don't use translated versions of Etc
3323         if ( 'Etc' === $a['continent'] && 'Etc' === $b['continent'] ) {
3324                 // Make the order of these more like the old dropdown
3325                 if ( 'GMT+' === substr( $a['city'], 0, 4 ) && 'GMT+' === substr( $b['city'], 0, 4 ) ) {
3326                         return -1 * ( strnatcasecmp( $a['city'], $b['city'] ) );
3327                 }
3328                 if ( 'UTC' === $a['city'] ) {
3329                         if ( 'GMT+' === substr( $b['city'], 0, 4 ) ) {
3330                                 return 1;
3331                         }
3332                         return -1;
3333                 }
3334                 if ( 'UTC' === $b['city'] ) {
3335                         if ( 'GMT+' === substr( $a['city'], 0, 4 ) ) {
3336                                 return -1;
3337                         }
3338                         return 1;
3339                 }
3340                 return strnatcasecmp( $a['city'], $b['city'] );
3341         }
3342         if ( $a['t_continent'] == $b['t_continent'] ) {
3343                 if ( $a['t_city'] == $b['t_city'] ) {
3344                         return strnatcasecmp( $a['t_subcity'], $b['t_subcity'] );
3345                 }
3346                 return strnatcasecmp( $a['t_city'], $b['t_city'] );
3347         } else {
3348                 // Force Etc to the bottom of the list
3349                 if ( 'Etc' === $a['continent'] ) {
3350                         return 1;
3351                 }
3352                 if ( 'Etc' === $b['continent'] ) {
3353                         return -1;
3354                 }
3355                 return strnatcasecmp( $a['t_continent'], $b['t_continent'] );
3356         }
3357 }
3358
3359 /**
3360  * Gives a nicely formatted list of timezone strings.
3361  *
3362  * @since 2.9.0
3363  *
3364  * @param string $selected_zone Selected Zone
3365  * @return string
3366  */
3367 function wp_timezone_choice( $selected_zone ) {
3368         static $mo_loaded = false;
3369
3370         $continents = array( 'Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific');
3371
3372         // Load translations for continents and cities
3373         if ( !$mo_loaded ) {
3374                 $locale = get_locale();
3375                 $mofile = WP_LANG_DIR . '/continents-cities-' . $locale . '.mo';
3376                 load_textdomain( 'continents-cities', $mofile );
3377                 $mo_loaded = true;
3378         }
3379
3380         $zonen = array();
3381         foreach ( timezone_identifiers_list() as $zone ) {
3382                 $zone = explode( '/', $zone );
3383                 if ( !in_array( $zone[0], $continents ) ) {
3384                         continue;
3385                 }
3386
3387                 // This determines what gets set and translated - we don't translate Etc/* strings here, they are done later
3388                 $exists = array(
3389                         0 => ( isset( $zone[0] ) && $zone[0] ),
3390                         1 => ( isset( $zone[1] ) && $zone[1] ),
3391                         2 => ( isset( $zone[2] ) && $zone[2] ),
3392                 );
3393                 $exists[3] = ( $exists[0] && 'Etc' !== $zone[0] );
3394                 $exists[4] = ( $exists[1] && $exists[3] );
3395                 $exists[5] = ( $exists[2] && $exists[3] );
3396
3397                 $zonen[] = array(
3398                         'continent'   => ( $exists[0] ? $zone[0] : '' ),
3399                         'city'        => ( $exists[1] ? $zone[1] : '' ),
3400                         'subcity'     => ( $exists[2] ? $zone[2] : '' ),
3401                         't_continent' => ( $exists[3] ? translate( str_replace( '_', ' ', $zone[0] ), 'continents-cities' ) : '' ),
3402                         't_city'      => ( $exists[4] ? translate( str_replace( '_', ' ', $zone[1] ), 'continents-cities' ) : '' ),
3403                         't_subcity'   => ( $exists[5] ? translate( str_replace( '_', ' ', $zone[2] ), 'continents-cities' ) : '' )
3404                 );
3405         }
3406         usort( $zonen, '_wp_timezone_choice_usort_callback' );
3407
3408         $structure = array();
3409
3410         if ( empty( $selected_zone ) ) {
3411                 $structure[] = '<option selected="selected" value="">' . __( 'Select a city' ) . '</option>';
3412         }
3413
3414         foreach ( $zonen as $key => $zone ) {
3415                 // Build value in an array to join later
3416                 $value = array( $zone['continent'] );
3417
3418                 if ( empty( $zone['city'] ) ) {
3419                         // It's at the continent level (generally won't happen)
3420                         $display = $zone['t_continent'];
3421                 } else {
3422                         // It's inside a continent group
3423
3424                         // Continent optgroup
3425                         if ( !isset( $zonen[$key - 1] ) || $zonen[$key - 1]['continent'] !== $zone['continent'] ) {
3426                                 $label = $zone['t_continent'];
3427                                 $structure[] = '<optgroup label="'. esc_attr( $label ) .'">';
3428                         }
3429
3430                         // Add the city to the value
3431                         $value[] = $zone['city'];
3432
3433                         $display = $zone['t_city'];
3434                         if ( !empty( $zone['subcity'] ) ) {
3435                                 // Add the subcity to the value
3436                                 $value[] = $zone['subcity'];
3437                                 $display .= ' - ' . $zone['t_subcity'];
3438                         }
3439                 }
3440
3441                 // Build the value
3442                 $value = join( '/', $value );
3443                 $selected = '';
3444                 if ( $value === $selected_zone ) {
3445                         $selected = 'selected="selected" ';
3446                 }
3447                 $structure[] = '<option ' . $selected . 'value="' . esc_attr( $value ) . '">' . esc_html( $display ) . "</option>";
3448
3449                 // Close continent optgroup
3450                 if ( !empty( $zone['city'] ) && ( !isset($zonen[$key + 1]) || (isset( $zonen[$key + 1] ) && $zonen[$key + 1]['continent'] !== $zone['continent']) ) ) {
3451                         $structure[] = '</optgroup>';
3452                 }
3453         }
3454
3455         // Do UTC
3456         $structure[] = '<optgroup label="'. esc_attr__( 'UTC' ) .'">';
3457         $selected = '';
3458         if ( 'UTC' === $selected_zone )
3459                 $selected = 'selected="selected" ';
3460         $structure[] = '<option ' . $selected . 'value="' . esc_attr( 'UTC' ) . '">' . __('UTC') . '</option>';
3461         $structure[] = '</optgroup>';
3462
3463         // Do manual UTC offsets
3464         $structure[] = '<optgroup label="'. esc_attr__( 'Manual Offsets' ) .'">';
3465         $offset_range = array (-12, -11.5, -11, -10.5, -10, -9.5, -9, -8.5, -8, -7.5, -7, -6.5, -6, -5.5, -5, -4.5, -4, -3.5, -3, -2.5, -2, -1.5, -1, -0.5,
3466                 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 5.75, 6, 6.5, 7, 7.5, 8, 8.5, 8.75, 9, 9.5, 10, 10.5, 11, 11.5, 12, 12.75, 13, 13.75, 14);
3467         foreach ( $offset_range as $offset ) {
3468                 if ( 0 <= $offset )
3469                         $offset_name = '+' . $offset;
3470                 else
3471                         $offset_name = (string) $offset;
3472
3473                 $offset_value = $offset_name;
3474                 $offset_name = str_replace(array('.25','.5','.75'), array(':15',':30',':45'), $offset_name);
3475                 $offset_name = 'UTC' . $offset_name;
3476                 $offset_value = 'UTC' . $offset_value;
3477                 $selected = '';
3478                 if ( $offset_value === $selected_zone )
3479                         $selected = 'selected="selected" ';
3480                 $structure[] = '<option ' . $selected . 'value="' . esc_attr( $offset_value ) . '">' . esc_html( $offset_name ) . "</option>";
3481
3482         }
3483         $structure[] = '</optgroup>';
3484
3485         return join( "\n", $structure );
3486 }
3487
3488 /**
3489  * Strip close comment and close php tags from file headers used by WP.
3490  * See http://core.trac.wordpress.org/ticket/8497
3491  *
3492  * @since 2.8.0
3493  *
3494  * @param string $str
3495  * @return string
3496  */
3497 function _cleanup_header_comment($str) {
3498         return trim(preg_replace("/\s*(?:\*\/|\?>).*/", '', $str));
3499 }
3500
3501 /**
3502  * Permanently deletes posts, pages, attachments, and comments which have been in the trash for EMPTY_TRASH_DAYS.
3503  *
3504  * @since 2.9.0
3505  */
3506 function wp_scheduled_delete() {
3507         global $wpdb;
3508
3509         $delete_timestamp = time() - ( DAY_IN_SECONDS * EMPTY_TRASH_DAYS );
3510
3511         $posts_to_delete = $wpdb->get_results($wpdb->prepare("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_wp_trash_meta_time' AND meta_value < '%d'", $delete_timestamp), ARRAY_A);
3512
3513         foreach ( (array) $posts_to_delete as $post ) {
3514                 $post_id = (int) $post['post_id'];
3515                 if ( !$post_id )
3516                         continue;
3517
3518                 $del_post = get_post($post_id);
3519
3520                 if ( !$del_post || 'trash' != $del_post->post_status ) {
3521                         delete_post_meta($post_id, '_wp_trash_meta_status');
3522                         delete_post_meta($post_id, '_wp_trash_meta_time');
3523                 } else {
3524                         wp_delete_post($post_id);
3525                 }
3526         }
3527
3528         $comments_to_delete = $wpdb->get_results($wpdb->prepare("SELECT comment_id FROM $wpdb->commentmeta WHERE meta_key = '_wp_trash_meta_time' AND meta_value < '%d'", $delete_timestamp), ARRAY_A);
3529
3530         foreach ( (array) $comments_to_delete as $comment ) {
3531                 $comment_id = (int) $comment['comment_id'];
3532                 if ( !$comment_id )
3533                         continue;
3534
3535                 $del_comment = get_comment($comment_id);
3536
3537                 if ( !$del_comment || 'trash' != $del_comment->comment_approved ) {
3538                         delete_comment_meta($comment_id, '_wp_trash_meta_time');
3539                         delete_comment_meta($comment_id, '_wp_trash_meta_status');
3540                 } else {
3541                         wp_delete_comment($comment_id);
3542                 }
3543         }
3544 }
3545
3546 /**
3547  * Retrieve metadata from a file.
3548  *
3549  * Searches for metadata in the first 8kiB of a file, such as a plugin or theme.
3550  * Each piece of metadata must be on its own line. Fields can not span multiple
3551  * lines, the value will get cut at the end of the first line.
3552  *
3553  * If the file data is not within that first 8kiB, then the author should correct
3554  * their plugin file and move the data headers to the top.
3555  *
3556  * @see http://codex.wordpress.org/File_Header
3557  *
3558  * @since 2.9.0
3559  * @param string $file Path to the file
3560  * @param array $default_headers List of headers, in the format array('HeaderKey' => 'Header Name')
3561  * @param string $context If specified adds filter hook "extra_{$context}_headers"
3562  */
3563 function get_file_data( $file, $default_headers, $context = '' ) {
3564         // We don't need to write to the file, so just open for reading.
3565         $fp = fopen( $file, 'r' );
3566
3567         // Pull only the first 8kiB of the file in.
3568         $file_data = fread( $fp, 8192 );
3569
3570         // PHP will close file handle, but we are good citizens.
3571         fclose( $fp );
3572
3573         // Make sure we catch CR-only line endings.
3574         $file_data = str_replace( "\r", "\n", $file_data );
3575
3576         if ( $context && $extra_headers = apply_filters( "extra_{$context}_headers", array() ) ) {
3577                 $extra_headers = array_combine( $extra_headers, $extra_headers ); // keys equal values
3578                 $all_headers = array_merge( $extra_headers, (array) $default_headers );
3579         } else {
3580                 $all_headers = $default_headers;
3581         }
3582
3583         foreach ( $all_headers as $field => $regex ) {
3584                 if ( preg_match( '/^[ \t\/*#@]*' . preg_quote( $regex, '/' ) . ':(.*)$/mi', $file_data, $match ) && $match[1] )
3585                         $all_headers[ $field ] = _cleanup_header_comment( $match[1] );
3586                 else
3587                         $all_headers[ $field ] = '';
3588         }
3589
3590         return $all_headers;
3591 }
3592
3593 /**
3594  * Used internally to tidy up the search terms.
3595  *
3596  * @access private
3597  * @since 2.9.0
3598  *
3599  * @param string $t
3600  * @return string
3601  */
3602 function _search_terms_tidy($t) {
3603         return trim($t, "\"'\n\r ");
3604 }
3605
3606 /**
3607  * Returns true.
3608  *
3609  * Useful for returning true to filters easily.
3610  *
3611  * @since 3.0.0
3612  * @see __return_false()
3613  * @return bool true
3614  */
3615 function __return_true() {
3616         return true;
3617 }
3618
3619 /**
3620  * Returns false.
3621  *
3622  * Useful for returning false to filters easily.
3623  *
3624  * @since 3.0.0
3625  * @see __return_true()
3626  * @return bool false
3627  */
3628 function __return_false() {
3629         return false;
3630 }
3631
3632 /**
3633  * Returns 0.
3634  *
3635  * Useful for returning 0 to filters easily.
3636  *
3637  * @since 3.0.0
3638  * @see __return_zero()
3639  * @return int 0
3640  */
3641 function __return_zero() {
3642         return 0;
3643 }
3644
3645 /**
3646  * Returns an empty array.
3647  *
3648  * Useful for returning an empty array to filters easily.
3649  *
3650  * @since 3.0.0
3651  * @see __return_zero()
3652  * @return array Empty array
3653  */
3654 function __return_empty_array() {
3655         return array();
3656 }
3657
3658 /**
3659  * Returns null.
3660  *
3661  * Useful for returning null to filters easily.
3662  *
3663  * @since 3.4.0
3664  * @return null
3665  */
3666 function __return_null() {
3667         return null;
3668 }
3669
3670 /**
3671  * Send a HTTP header to disable content type sniffing in browsers which support it.
3672  *
3673  * @link http://blogs.msdn.com/ie/archive/2008/07/02/ie8-security-part-v-comprehensive-protection.aspx
3674  * @link http://src.chromium.org/viewvc/chrome?view=rev&revision=6985
3675  *
3676  * @since 3.0.0
3677  * @return none
3678  */
3679 function send_nosniff_header() {
3680         @header( 'X-Content-Type-Options: nosniff' );
3681 }
3682
3683 /**
3684  * Returns a MySQL expression for selecting the week number based on the start_of_week option.
3685  *
3686  * @internal
3687  * @since 3.0.0
3688  * @param string $column
3689  * @return string
3690  */
3691 function _wp_mysql_week( $column ) {
3692         switch ( $start_of_week = (int) get_option( 'start_of_week' ) ) {
3693         default :
3694         case 0 :
3695                 return "WEEK( $column, 0 )";
3696         case 1 :
3697                 return "WEEK( $column, 1 )";
3698         case 2 :
3699         case 3 :
3700         case 4 :
3701         case 5 :
3702         case 6 :
3703                 return "WEEK( DATE_SUB( $column, INTERVAL $start_of_week DAY ), 0 )";
3704         }
3705 }
3706
3707 /**
3708  * Finds hierarchy loops using a callback function that maps object IDs to parent IDs.
3709  *
3710  * @since 3.1.0
3711  * @access private
3712  *
3713  * @param callback $callback function that accepts ( ID, $callback_args ) and outputs parent_ID
3714  * @param int $start The ID to start the loop check at
3715  * @param int $start_parent the parent_ID of $start to use instead of calling $callback( $start ). Use null to always use $callback
3716  * @param array $callback_args optional additional arguments to send to $callback
3717  * @return array IDs of all members of loop
3718  */
3719 function wp_find_hierarchy_loop( $callback, $start, $start_parent, $callback_args = array() ) {
3720         $override = is_null( $start_parent ) ? array() : array( $start => $start_parent );
3721
3722         if ( !$arbitrary_loop_member = wp_find_hierarchy_loop_tortoise_hare( $callback, $start, $override, $callback_args ) )
3723                 return array();
3724
3725         return wp_find_hierarchy_loop_tortoise_hare( $callback, $arbitrary_loop_member, $override, $callback_args, true );
3726 }
3727
3728 /**
3729  * Uses the "The Tortoise and the Hare" algorithm to detect loops.
3730  *
3731  * For every step of the algorithm, the hare takes two steps and the tortoise one.
3732  * If the hare ever laps the tortoise, there must be a loop.
3733  *
3734  * @since 3.1.0
3735  * @access private
3736  *
3737  * @param callback $callback function that accepts ( ID, callback_arg, ... ) and outputs parent_ID
3738  * @param int $start The ID to start the loop check at
3739  * @param array $override an array of ( ID => parent_ID, ... ) to use instead of $callback
3740  * @param array $callback_args optional additional arguments to send to $callback
3741  * @param bool $_return_loop Return loop members or just detect presence of loop?
3742  *             Only set to true if you already know the given $start is part of a loop
3743  *             (otherwise the returned array might include branches)
3744  * @return mixed scalar ID of some arbitrary member of the loop, or array of IDs of all members of loop if $_return_loop
3745  */
3746 function wp_find_hierarchy_loop_tortoise_hare( $callback, $start, $override = array(), $callback_args = array(), $_return_loop = false ) {
3747         $tortoise = $hare = $evanescent_hare = $start;
3748         $return = array();
3749
3750         // Set evanescent_hare to one past hare
3751         // Increment hare two steps
3752         while (
3753                 $tortoise
3754         &&
3755                 ( $evanescent_hare = isset( $override[$hare] ) ? $override[$hare] : call_user_func_array( $callback, array_merge( array( $hare ), $callback_args ) ) )
3756         &&
3757                 ( $hare = isset( $override[$evanescent_hare] ) ? $override[$evanescent_hare] : call_user_func_array( $callback, array_merge( array( $evanescent_hare ), $callback_args ) ) )
3758         ) {
3759                 if ( $_return_loop )
3760                         $return[$tortoise] = $return[$evanescent_hare] = $return[$hare] = true;
3761
3762                 // tortoise got lapped - must be a loop
3763                 if ( $tortoise == $evanescent_hare || $tortoise == $hare )
3764                         return $_return_loop ? $return : $tortoise;
3765
3766                 // Increment tortoise by one step
3767                 $tortoise = isset( $override[$tortoise] ) ? $override[$tortoise] : call_user_func_array( $callback, array_merge( array( $tortoise ), $callback_args ) );
3768         }
3769
3770         return false;
3771 }
3772
3773 /**
3774  * Send a HTTP header to limit rendering of pages to same origin iframes.
3775  *
3776  * @link https://developer.mozilla.org/en/the_x-frame-options_response_header
3777  *
3778  * @since 3.1.3
3779  * @return none
3780  */
3781 function send_frame_options_header() {
3782         @header( 'X-Frame-Options: SAMEORIGIN' );
3783 }
3784
3785 /**
3786  * Retrieve a list of protocols to allow in HTML attributes.
3787  *
3788  * @since 3.3.0
3789  * @see wp_kses()
3790  * @see esc_url()
3791  *
3792  * @return array Array of allowed protocols
3793  */
3794 function wp_allowed_protocols() {
3795         static $protocols;
3796
3797         if ( empty( $protocols ) ) {
3798                 $protocols = array( 'http', 'https', 'ftp', 'ftps', 'mailto', 'news',&