]> CyberLeo.Net >> Repos - Github/YOURLS.git/blob - includes/functions.php
Maintenance mode (interrupt everything is activated). Closes issue 221.
[Github/YOURLS.git] / includes / functions.php
1 <?php\r
2 /*\r
3  * YOURLS\r
4  * Function library\r
5  */\r
6 \r
7 // Determine the allowed character set in short URLs\r
8 function yourls_get_shorturl_charset() {\r
9         static $charset = null;\r
10         if( $charset !== null )\r
11                 return $charset;\r
12                 \r
13         if( !defined('YOURLS_URL_CONVERT') ) {\r
14                 $charset = '0123456789abcdefghijklmnopqrstuvwxyz';\r
15         } else {\r
16                 switch( YOURLS_URL_CONVERT ) {\r
17                         case 36:\r
18                                 $charset = '0123456789abcdefghijklmnopqrstuvwxyz';\r
19                                 break;\r
20                         case 62:\r
21                         case 64: // just because some people get this wrong in their config.php\r
22                                 $charset = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';\r
23                                 break;\r
24                 }\r
25         }\r
26         \r
27         $charset = yourls_apply_filter( 'get_shorturl_charset', $charset );\r
28         return $charset;\r
29 }\r
30  \r
31 // Make an optimized regexp pattern from a string of characters\r
32 function yourls_make_regexp_pattern( $string ) {\r
33         $pattern = preg_quote( $string, '-' ); // add - as an escaped characters -- this is fixed in PHP 5.3\r
34         // TODO: replace char sequences by smart sequences such as 0-9, a-z, A-Z ... ?\r
35         return $pattern;\r
36 }\r
37 \r
38 // Is an URL a short URL?\r
39 function yourls_is_shorturl( $shorturl ) {\r
40         // TODO: make sure this function evolves with the feature set.\r
41         \r
42         $is_short = false;\r
43         $keyword = preg_replace( '!^'.YOURLS_SITE.'/!', '', $shorturl ); // accept either 'http://ozh.in/abc' or 'abc'\r
44         if( $keyword && $keyword == yourls_sanitize_string( $keyword ) && yourls_keyword_is_taken( $keyword ) ) {\r
45                 $is_short = true;\r
46         }\r
47         \r
48         return yourls_apply_filter( 'is_shorturl', $is_short, $shorturl );\r
49 }\r
50 \r
51 // Check to see if a given keyword is reserved (ie reserved URL or an existing page)\r
52 // Returns bool\r
53 function yourls_keyword_is_reserved( $keyword ) {\r
54         global $yourls_reserved_URL;\r
55         $keyword = yourls_sanitize_keyword( $keyword );\r
56         $reserved = false;\r
57         \r
58         if ( in_array( $keyword, $yourls_reserved_URL)\r
59                 or file_exists( YOURLS_ABSPATH ."/pages/$keyword.php" )\r
60                 or is_dir( YOURLS_ABSPATH ."/$keyword" )\r
61         )\r
62                 $reserved = true;\r
63         \r
64         return yourls_apply_filter( 'keyword_is_reserved', $reserved, $keyword );\r
65 }\r
66 \r
67 // Function: Get client IP Address. Returns a DB safe string.\r
68 function yourls_get_IP() {\r
69         // Precedence: if set, X-Forwarded-For > HTTP_X_FORWARDED_FOR > HTTP_CLIENT_IP > HTTP_VIA > REMOTE_ADDR\r
70         $headers = array( 'X-Forwarded-For', 'HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_VIA', 'REMOTE_ADDR' );\r
71         foreach( $headers as $header ) {\r
72                 if ( !empty( $_SERVER[ $header ] ) ) {\r
73                         $ip = $_SERVER[ $header ];\r
74                         break;\r
75                 }\r
76         }\r
77         \r
78         // headers can contain multiple IPs (X-Forwarded-For = client, proxy1, proxy2). Take first one.\r
79         if ( strpos( $ip, ',' ) !== false )\r
80                 $ip = substr( $ip, 0, strpos( $ip, ',' ) );\r
81         \r
82         return yourls_apply_filter( 'get_IP', yourls_sanitize_ip( $ip ) );\r
83 }\r
84 \r
85 // Get next id a new link will have if no custom keyword provided\r
86 function yourls_get_next_decimal() {\r
87         return yourls_apply_filter( 'get_next_decimal', (int)yourls_get_option( 'next_id' ) );\r
88 }\r
89 \r
90 // Update id for next link with no custom keyword\r
91 function yourls_update_next_decimal( $int = '' ) {\r
92         $int = ( $int == '' ) ? yourls_get_next_decimal() + 1 : (int)$int ;\r
93         $update = yourls_update_option( 'next_id', $int );\r
94         yourls_do_action( 'update_next_decimal', $int, $update );\r
95         return $update;\r
96 }\r
97 \r
98 // Delete a link in the DB\r
99 function yourls_delete_link_by_keyword( $keyword ) {\r
100         global $ydb;\r
101 \r
102         $table = YOURLS_DB_TABLE_URL;\r
103         $keyword = yourls_sanitize_string( $keyword );\r
104         $delete = $ydb->query("DELETE FROM `$table` WHERE `keyword` = '$keyword';");\r
105         yourls_do_action( 'delete_link', $keyword, $delete );\r
106         return $delete;\r
107 }\r
108 \r
109 // SQL query to insert a new link in the DB. Returns boolean for success or failure of the inserting\r
110 function yourls_insert_link_in_db( $url, $keyword, $title = '' ) {\r
111         global $ydb;\r
112         \r
113         $url     = addslashes( yourls_sanitize_url( $url ) );\r
114         $keyword = addslashes( yourls_sanitize_keyword( $keyword ) );\r
115         $title   = addslashes( yourls_sanitize_title( $title ) );\r
116 \r
117         $table = YOURLS_DB_TABLE_URL;\r
118         $timestamp = date('Y-m-d H:i:s');\r
119         $ip = yourls_get_IP();\r
120         $insert = $ydb->query("INSERT INTO `$table` (`keyword`, `url`, `title`, `timestamp`, `ip`, `clicks`) VALUES('$keyword', '$url', '$title', '$timestamp', '$ip', 0);");\r
121         \r
122         yourls_do_action( 'insert_link', (bool)$insert, $url, $keyword, $title, $timestamp, $ip );\r
123         \r
124         return (bool)$insert;\r
125 }\r
126 \r
127 // Check if a URL already exists in the DB. Return NULL (doesn't exist) or an object with URL informations.\r
128 function yourls_url_exists( $url ) {\r
129         // Allow plugins to short-circuit the whole function\r
130         $pre = yourls_apply_filter( 'shunt_url_exists', false, $url );\r
131         if ( false !== $pre )\r
132                 return $pre;\r
133 \r
134         global $ydb;\r
135         $table = YOURLS_DB_TABLE_URL;\r
136         $strip_url = stripslashes($url);\r
137         $url_exists = $ydb->get_row("SELECT * FROM `$table` WHERE `url` = '".$strip_url."';");\r
138         \r
139         return yourls_apply_filter( 'url_exists', $url_exists, $url );\r
140 }\r
141 \r
142 // Add a new link in the DB, either with custom keyword, or find one\r
143 function yourls_add_new_link( $url, $keyword = '', $title = '' ) {\r
144         global $ydb;\r
145 \r
146         // Allow plugins to short-circuit the whole function\r
147         $pre = yourls_apply_filter( 'shunt_add_new_link', false, $url, $keyword, $title );\r
148         if ( false !== $pre )\r
149                 return $pre;\r
150 \r
151         if ( !$url || $url == 'http://' || $url == 'https://' ) {\r
152                 $return['status']    = 'fail';\r
153                 $return['code']      = 'error:nourl';\r
154                 $return['message']   = 'Missing URL input';\r
155                 $return['errorCode'] = '400';\r
156                 return yourls_apply_filter( 'add_new_link_fail_nourl', $return, $url, $keyword, $title );\r
157         }\r
158         \r
159         // Prevent DB flood\r
160         $ip = yourls_get_IP();\r
161         yourls_check_IP_flood( $ip );\r
162         \r
163         // Prevent internal redirection loops: cannot shorten a shortened URL\r
164         $url = yourls_escape( yourls_sanitize_url( $url ) );\r
165         if( preg_match( '!^'.YOURLS_SITE.'/!', $url ) ) {\r
166                 if( yourls_is_shorturl( $url ) ) {\r
167                         $return['status']    = 'fail';\r
168                         $return['code']      = 'error:noloop';\r
169                         $return['message']   = 'URL is a short URL';\r
170                         $return['errorCode'] = '400';\r
171                         return yourls_apply_filter( 'add_new_link_fail_noloop', $return, $url, $keyword, $title );\r
172                 }\r
173         }\r
174 \r
175         yourls_do_action( 'pre_add_new_link', $url, $keyword, $title );\r
176         \r
177         $strip_url = stripslashes( $url );\r
178         $return = array();\r
179 \r
180         // duplicates allowed or new URL => store it\r
181         if( yourls_allow_duplicate_longurls() || !( $url_exists = yourls_url_exists( $url ) ) ) {\r
182         \r
183                 if( isset( $title ) && !empty( $title ) ) {\r
184                         $title = yourls_sanitize_title( $title );\r
185                 } else {\r
186                         $title = yourls_get_remote_title( $url );\r
187                 }\r
188                 $title = yourls_apply_filter( 'add_new_title', $title, $url, $keyword );\r
189 \r
190                 // Custom keyword provided\r
191                 if ( $keyword ) {\r
192                         \r
193                         yourls_do_action( 'add_new_link_custom_keyword', $url, $keyword, $title );\r
194                 \r
195                         $keyword = yourls_escape( yourls_sanitize_string( $keyword ) );\r
196                         $keyword = yourls_apply_filter( 'custom_keyword', $keyword, $url, $title );\r
197                         if ( !yourls_keyword_is_free( $keyword ) ) {\r
198                                 // This shorturl either reserved or taken already\r
199                                 $return['status']  = 'fail';\r
200                                 $return['code']    = 'error:keyword';\r
201                                 $return['message'] = 'Short URL '.$keyword.' already exists in database or is reserved';\r
202                         } else {\r
203                                 // all clear, store !\r
204                                 yourls_insert_link_in_db( $url, $keyword, $title );\r
205                                 $return['url']      = array('keyword' => $keyword, 'url' => $strip_url, 'title' => $title, 'date' => date('Y-m-d H:i:s'), 'ip' => $ip );\r
206                                 $return['status']   = 'success';\r
207                                 $return['message']  = yourls_trim_long_string( $strip_url ).' added to database';\r
208                                 $return['title']    = $title;\r
209                                 $return['html']     = yourls_table_add_row( $keyword, $url, $title, $ip, 0, time() );\r
210                                 $return['shorturl'] = YOURLS_SITE .'/'. $keyword;\r
211                         }\r
212 \r
213                 // Create random keyword        \r
214                 } else {\r
215                         \r
216                         yourls_do_action( 'add_new_link_create_keyword', $url, $keyword, $title );\r
217                 \r
218                         $timestamp = date( 'Y-m-d H:i:s' );\r
219                         $id = yourls_get_next_decimal();\r
220                         $ok = false;\r
221                         do {\r
222                                 $keyword = yourls_int2string( $id );\r
223                                 $keyword = yourls_apply_filter( 'random_keyword', $keyword, $url, $title );\r
224                                 $free = yourls_keyword_is_free($keyword);\r
225                                 $add_url = @yourls_insert_link_in_db( $url, $keyword, $title );\r
226                                 $ok = ($free && $add_url);\r
227                                 if ( $ok === false && $add_url === 1 ) {\r
228                                         // we stored something, but shouldn't have (ie reserved id)\r
229                                         $delete = yourls_delete_link_by_keyword( $keyword );\r
230                                         $return['extra_info'] .= '(deleted '.$keyword.')';\r
231                                 } else {\r
232                                         // everything ok, populate needed vars\r
233                                         $return['url']      = array('keyword' => $keyword, 'url' => $strip_url, 'title' => $title, 'date' => $timestamp, 'ip' => $ip );\r
234                                         $return['status']   = 'success';\r
235                                         $return['message']  = yourls_trim_long_string( $strip_url ).' added to database';\r
236                                         $return['title']    = $title;\r
237                                         $return['html']     = yourls_table_add_row( $keyword, $url, $title, $ip, 0, time() );\r
238                                         $return['shorturl'] = YOURLS_SITE .'/'. $keyword;\r
239                                 }\r
240                                 $id++;\r
241                         } while ( !$ok );\r
242                         @yourls_update_next_decimal( $id );\r
243                 }\r
244 \r
245         // URL was already stored\r
246         } else {\r
247                         \r
248                 yourls_do_action( 'add_new_link_already_stored', $url, $keyword, $title );\r
249                 \r
250                 $return['status']   = 'fail';\r
251                 $return['code']     = 'error:url';\r
252                 $return['url']      = array( 'keyword' => $url_exists->keyword, 'url' => $strip_url, 'title' => $url_exists->title, 'date' => $url_exists->timestamp, 'ip' => $url_exists->ip, 'clicks' => $url_exists->clicks );\r
253                 $return['message']  = yourls_trim_long_string( $strip_url ).' already exists in database';\r
254                 $return['title']    = $url_exists->title; \r
255                 $return['shorturl'] = YOURLS_SITE .'/'. $url_exists->keyword;\r
256         }\r
257         \r
258         yourls_do_action( 'post_add_new_link', $url, $keyword, $title );\r
259 \r
260         $return['statusCode'] = 200; // regardless of result, this is still a valid request\r
261         return yourls_apply_filter( 'add_new_link', $return, $url, $keyword, $title );\r
262 }\r
263 \r
264 \r
265 // Edit a link\r
266 function yourls_edit_link( $url, $keyword, $newkeyword='', $title='' ) {\r
267         global $ydb;\r
268 \r
269         $table = YOURLS_DB_TABLE_URL;\r
270         $url = yourls_escape (yourls_sanitize_url( $url ) );\r
271         $keyword = yourls_escape( yourls_sanitize_string( $keyword ) );\r
272         $title = yourls_escape( yourls_sanitize_title( $title ) );\r
273         $newkeyword = yourls_escape( yourls_sanitize_string( $newkeyword ) );\r
274         $strip_url = stripslashes( $url );\r
275         $strip_title = stripslashes( $title );\r
276         $old_url = $ydb->get_var( "SELECT `url` FROM `$table` WHERE `keyword` = '$keyword';" );\r
277         \r
278         // Check if new URL is not here already\r
279         if ( $old_url != $url && !yourls_allow_duplicate_longurls() ) {\r
280                 $new_url_already_there = intval($ydb->get_var("SELECT COUNT(keyword) FROM `$table` WHERE `url` = '$strip_url';"));\r
281         } else {\r
282                 $new_url_already_there = false;\r
283         }\r
284         \r
285         // Check if the new keyword is not here already\r
286         if ( $newkeyword != $keyword ) {\r
287                 $keyword_is_ok = yourls_keyword_is_free( $newkeyword );\r
288         } else {\r
289                 $keyword_is_ok = true;\r
290         }\r
291         \r
292         yourls_do_action( 'pre_edit_link', $url, $keyword, $newkeyword, $new_url_already_there, $keyword_is_ok );\r
293         \r
294         // All clear, update\r
295         if ( ( !$new_url_already_there || yourls_allow_duplicate_longurls() ) && $keyword_is_ok ) {\r
296                         $update_url = $ydb->query( "UPDATE `$table` SET `url` = '$url', `keyword` = '$newkeyword', `title` = '$title' WHERE `keyword` = '$keyword';" );\r
297                 if( $update_url ) {\r
298                         $return['url']     = array( 'keyword' => $newkeyword, 'shorturl' => YOURLS_SITE.'/'.$newkeyword, 'url' => $strip_url, 'display_url' => yourls_trim_long_string( $strip_url ), 'title' => $strip_title, 'display_title' => yourls_trim_long_string( $strip_title ) );\r
299                         $return['status']  = 'success';\r
300                         $return['message'] = 'Link updated in database';\r
301                 } else {\r
302                         $return['status']  = 'fail';\r
303                         $return['message'] = 'Error updating '. yourls_trim_long_string( $strip_url ).' (Short URL: '.$keyword.') to database';\r
304                 }\r
305         \r
306         // Nope\r
307         } else {\r
308                 $return['status']  = 'fail';\r
309                 $return['message'] = 'URL or keyword already exists in database';\r
310         }\r
311         \r
312         return yourls_apply_filter( 'edit_link', $return, $url, $keyword, $newkeyword, $title, $new_url_already_there, $keyword_is_ok );\r
313 }\r
314 \r
315 // Update a title link (no checks for duplicates etc..)\r
316 function yourls_edit_link_title( $keyword, $title ) {\r
317         global $ydb;\r
318         \r
319         $keyword = yourls_escape( yourls_sanitize_keyword( $keyword ) );\r
320         $title = yourls_escape( yourls_sanitize_title( $title ) );\r
321         \r
322         $table = YOURLS_DB_TABLE_URL;\r
323         $update = $ydb->query("UPDATE `$table` SET `title` = '$title' WHERE `keyword` = '$keyword';");\r
324 \r
325         return $update;\r
326 }\r
327 \r
328 \r
329 // Check if keyword id is free (ie not already taken, and not reserved). Return bool.\r
330 function yourls_keyword_is_free( $keyword ) {\r
331         $free = true;\r
332         if ( yourls_keyword_is_reserved( $keyword ) or yourls_keyword_is_taken( $keyword ) )\r
333                 $free = false;\r
334                 \r
335         return yourls_apply_filter( 'keyword_is_free', $free, $keyword );\r
336 }\r
337 \r
338 // Check if a keyword is taken (ie there is already a short URL with this id). Return bool.             \r
339 function yourls_keyword_is_taken( $keyword ) {\r
340         global $ydb;\r
341         $keyword = yourls_sanitize_keyword( $keyword );\r
342         $taken = false;\r
343         $table = YOURLS_DB_TABLE_URL;\r
344         $already_exists = $ydb->get_var( "SELECT COUNT(`keyword`) FROM `$table` WHERE `keyword` = '$keyword';" );\r
345         if ( $already_exists )\r
346                 $taken = true;\r
347 \r
348         return yourls_apply_filter( 'keyword_is_taken', $taken, $keyword );\r
349 }\r
350 \r
351 \r
352 // Connect to DB\r
353 function yourls_db_connect() {\r
354         global $ydb;\r
355 \r
356         if (   !defined( 'YOURLS_DB_USER' )\r
357                 or !defined( 'YOURLS_DB_PASS' )\r
358                 or !defined( 'YOURLS_DB_NAME' )\r
359                 or !defined( 'YOURLS_DB_HOST' )\r
360                 or !class_exists( 'ezSQL_mysql' )\r
361         ) yourls_die ( 'DB config missing, or could not find DB class', 'Fatal error', 503 );\r
362         \r
363         // Are we standalone or in the WordPress environment?\r
364         if ( class_exists( 'wpdb' ) ) {\r
365                 $ydb =  new wpdb( YOURLS_DB_USER, YOURLS_DB_PASS, YOURLS_DB_NAME, YOURLS_DB_HOST );\r
366         } else {\r
367                 $ydb =  new ezSQL_mysql( YOURLS_DB_USER, YOURLS_DB_PASS, YOURLS_DB_NAME, YOURLS_DB_HOST );\r
368         }\r
369         if ( $ydb->last_error )\r
370                 yourls_die( $ydb->last_error, 'Fatal error', 503 );\r
371         \r
372         if ( defined( 'YOURLS_DEBUG' ) && YOURLS_DEBUG === true )\r
373                 $ydb->show_errors = true;\r
374         \r
375         return $ydb;\r
376 }\r
377 \r
378 // Return XML output.\r
379 function yourls_xml_encode( $array ) {\r
380         require_once( YOURLS_INC.'/functions-xml.php' );\r
381         $converter= new yourls_array2xml;\r
382         return $converter->array2xml( $array );\r
383 }\r
384 \r
385 // Return array of all informations associated with keyword. Returns false if keyword not found. Set optional $use_cache to false to force fetching from DB\r
386 function yourls_get_keyword_infos( $keyword, $use_cache = true ) {\r
387         global $ydb;\r
388         $keyword = yourls_sanitize_string( $keyword );\r
389 \r
390         yourls_do_action( 'pre_get_keyword', $keyword, $use_cache );\r
391 \r
392         if( isset( $ydb->infos[$keyword] ) && $use_cache == true ) {\r
393                 return yourls_apply_filter( 'get_keyword_infos', $ydb->infos[$keyword], $keyword );\r
394         }\r
395         \r
396         yourls_do_action( 'get_keyword_not_cached', $keyword );\r
397         \r
398         $table = YOURLS_DB_TABLE_URL;\r
399         $infos = $ydb->get_row( "SELECT * FROM `$table` WHERE `keyword` = '$keyword'" );\r
400         \r
401         if( $infos ) {\r
402                 $infos = (array)$infos;\r
403                 $ydb->infos[ $keyword ] = $infos;\r
404         } else {\r
405                 $ydb->infos[ $keyword ] = false;\r
406         }\r
407                 \r
408         return yourls_apply_filter( 'get_keyword_infos', $ydb->infos[$keyword], $keyword );\r
409 }\r
410 \r
411 // Return (string) selected information associated with a keyword. Optional $notfound = string default message if nothing found\r
412 function yourls_get_keyword_info( $keyword, $field, $notfound = false ) {\r
413 \r
414         // Allow plugins to short-circuit the whole function\r
415         $pre = yourls_apply_filter( 'shunt_get_keyword_info', false, $keyword, $field, $notfound );\r
416         if ( false !== $pre )\r
417                 return $pre;\r
418 \r
419         $keyword = yourls_sanitize_string( $keyword );\r
420         $infos = yourls_get_keyword_infos( $keyword );\r
421         \r
422         $return = $notfound;\r
423         if ( isset( $infos[ $field ] ) && $infos[ $field ] !== false )\r
424                 $return = $infos[ $field ];\r
425 \r
426         return yourls_apply_filter( 'get_keyword_info', $return, $keyword, $field, $notfound ); \r
427 }\r
428 \r
429 // Return title associated with keyword. Optional $notfound = string default message if nothing found\r
430 function yourls_get_keyword_title( $keyword, $notfound = false ) {\r
431         return yourls_get_keyword_info( $keyword, 'title', $notfound );\r
432 }\r
433 \r
434 // Return long URL associated with keyword. Optional $notfound = string default message if nothing found\r
435 function yourls_get_keyword_longurl( $keyword, $notfound = false ) {\r
436         return yourls_get_keyword_info( $keyword, 'url', $notfound );\r
437 }\r
438 \r
439 // Return number of clicks on a keyword. Optional $notfound = string default message if nothing found\r
440 function yourls_get_keyword_clicks( $keyword, $notfound = false ) {\r
441         return yourls_get_keyword_info( $keyword, 'clicks', $notfound );\r
442 }\r
443 \r
444 // Return IP that added a keyword. Optional $notfound = string default message if nothing found\r
445 function yourls_get_keyword_IP( $keyword, $notfound = false ) {\r
446         return yourls_get_keyword_info( $keyword, 'ip', $notfound );\r
447 }\r
448 \r
449 // Return timestamp associated with a keyword. Optional $notfound = string default message if nothing found\r
450 function yourls_get_keyword_timestamp( $keyword, $notfound = false ) {\r
451         return yourls_get_keyword_info( $keyword, 'timestamp', $notfound );\r
452 }\r
453 \r
454 // Update click count on a short URL. Return 0/1 for error/success.\r
455 function yourls_update_clicks( $keyword, $clicks = false ) {\r
456         // Allow plugins to short-circuit the whole function\r
457         $pre = yourls_apply_filter( 'shunt_update_clicks', false, $keyword, $clicks );\r
458         if ( false !== $pre )\r
459                 return $pre;\r
460 \r
461         global $ydb;\r
462         $keyword = yourls_sanitize_string( $keyword );\r
463         $table = YOURLS_DB_TABLE_URL;\r
464         if ( $clicks !== false && is_int( $clicks ) && $clicks >= 0 )\r
465                 $update = $ydb->query( "UPDATE `$table` SET `clicks` = $clicks WHERE `keyword` = '$keyword'" );\r
466         else\r
467                 $update = $ydb->query( "UPDATE `$table` SET `clicks` = clicks + 1 WHERE `keyword` = '$keyword'" );\r
468 \r
469         yourls_do_action( 'update_clicks', $keyword, $update, $clicks );\r
470         return $update;\r
471 }\r
472 \r
473 // Return array of stats. (string)$filter is 'bottom', 'last', 'rand' or 'top'. (int)$limit is the number of links to return\r
474 function yourls_get_stats( $filter = 'top', $limit = 10, $start = 0 ) {\r
475         global $ydb;\r
476 \r
477         switch( $filter ) {\r
478                 case 'bottom':\r
479                         $sort_by    = 'clicks';\r
480                         $sort_order = 'asc';\r
481                         break;\r
482                 case 'last':\r
483                         $sort_by    = 'timestamp';\r
484                         $sort_order = 'desc';\r
485                         break;\r
486                 case 'rand':\r
487                 case 'random':\r
488                         $sort_by    = 'RAND()';\r
489                         $sort_order = '';\r
490                         break;\r
491                 case 'top':\r
492                 default:\r
493                         $sort_by    = 'clicks';\r
494                         $sort_order = 'desc';\r
495                         break;\r
496         }\r
497         \r
498         // Fetch links\r
499         $limit = intval( $limit );\r
500         $start = intval( $start );\r
501         if ( $limit > 0 ) {\r
502 \r
503                 $table_url = YOURLS_DB_TABLE_URL;\r
504                 $results = $ydb->get_results( "SELECT * FROM `$table_url` WHERE 1=1 ORDER BY `$sort_by` $sort_order LIMIT $start, $limit;" );\r
505                 \r
506                 $return = array();\r
507                 $i = 1;\r
508                 \r
509                 foreach ( (array)$results as $res ) {\r
510                         $return['links']['link_'.$i++] = array(\r
511                                 'shorturl' => YOURLS_SITE .'/'. $res->keyword,\r
512                                 'url'      => $res->url,\r
513                                 'title'    => $res->title,\r
514                                 'timestamp'=> $res->timestamp,\r
515                                 'ip'       => $res->ip,\r
516                                 'clicks'   => $res->clicks,\r
517                         );\r
518                 }\r
519         }\r
520 \r
521         $return['stats'] = yourls_get_db_stats();\r
522         \r
523         $return['statusCode'] = 200;\r
524 \r
525         return yourls_apply_filter( 'get_stats', $return, $filter, $limit, $start );\r
526 }\r
527 \r
528 // Return array of stats. (string)$filter is 'bottom', 'last', 'rand' or 'top'. (int)$limit is the number of links to return\r
529 function yourls_get_link_stats( $shorturl ) {\r
530         global $ydb;\r
531 \r
532         $table_url = YOURLS_DB_TABLE_URL;\r
533         $res = $ydb->get_row( "SELECT * FROM `$table_url` WHERE keyword = '$shorturl';" );\r
534         $return = array();\r
535 \r
536         if( !$res ) {\r
537                 // non existent link\r
538                 $return = array(\r
539                         'statusCode' => 404,\r
540                         'message'    => 'Error: short URL not found',\r
541                 );\r
542         } else {\r
543                 $return = array(\r
544                         'statusCode' => 200,\r
545                         'message'    => 'success',\r
546                         'link'       => array(\r
547                                 'shorturl' => YOURLS_SITE .'/'. $res->keyword,\r
548                                 'url'      => $res->url,\r
549                                 'title'    => $res->title,\r
550                                 'timestamp'=> $res->timestamp,\r
551                                 'ip'       => $res->ip,\r
552                                 'clicks'   => $res->clicks,\r
553                         )\r
554                 );\r
555         }\r
556 \r
557         return yourls_apply_filter( 'get_link_stats', $return, $shorturl );\r
558 }\r
559 \r
560 // Return array for API stat requests\r
561 function yourls_api_stats( $filter = 'top', $limit = 10, $start = 0 ) {\r
562         $return = yourls_get_stats( $filter, $limit, $start );\r
563         $return['simple']  = 'Need either XML or JSON format for stats';\r
564         $return['message'] = 'success';\r
565         return yourls_apply_filter( 'api_stats', $return, $filter, $limit, $start );\r
566 }\r
567 \r
568 // Return array for counts of shorturls and clicks\r
569 function yourls_api_db_stats() {\r
570         $return = array(\r
571                 'db-stats'   => yourls_get_db_stats(),\r
572                 'statusCode' => 200,\r
573                 'simple'     => 'Need either XML or JSON format for stats',\r
574                 'message'    => 'success',\r
575         );\r
576                 \r
577         return yourls_apply_filter( 'api_db_stats', $return );\r
578 }\r
579 \r
580 // Return array for API stat requests\r
581 function yourls_api_url_stats( $shorturl ) {\r
582         $keyword = str_replace( YOURLS_SITE . '/' , '', $shorturl ); // accept either 'http://ozh.in/abc' or 'abc'\r
583         $keyword = yourls_sanitize_string( $keyword );\r
584 \r
585         $return = yourls_get_link_stats( $keyword );\r
586         $return['simple']  = 'Need either XML or JSON format for stats';\r
587         return yourls_apply_filter( 'api_url_stats', $return, $shorturl );\r
588 }\r
589 \r
590 // Expand short url to long url\r
591 function yourls_api_expand( $shorturl ) {\r
592         $keyword = str_replace( YOURLS_SITE . '/' , '', $shorturl ); // accept either 'http://ozh.in/abc' or 'abc'\r
593         $keyword = yourls_sanitize_string( $keyword );\r
594         \r
595         $longurl = yourls_get_keyword_longurl( $keyword );\r
596         \r
597         if( $longurl ) {\r
598                 $return = array(\r
599                         'keyword'   => $keyword,\r
600                         'shorturl'  => YOURLS_SITE . "/$keyword",\r
601                         'longurl'   => $longurl,\r
602                         'simple'    => $longurl,\r
603                         'message'   => 'success',\r
604                         'statusCode' => 200,\r
605                 );\r
606         } else {\r
607                 $return = array(\r
608                         'keyword'   => $keyword,\r
609                         'simple'    => 'not found',\r
610                         'message'   => 'Error: short URL not found',\r
611                         'errorCode' => 404,\r
612                 );\r
613         }\r
614         \r
615         return yourls_apply_filter( 'api_expand', $return, $shorturl );\r
616 }\r
617 \r
618 \r
619 // Get total number of URLs and sum of clicks. Input: optional "AND WHERE" clause. Returns array\r
620 function yourls_get_db_stats( $where = '' ) {\r
621         global $ydb;\r
622         $table_url = YOURLS_DB_TABLE_URL;\r
623 \r
624         $totals = $ydb->get_row( "SELECT COUNT(keyword) as count, SUM(clicks) as sum FROM `$table_url` WHERE 1=1 $where" );\r
625         $return = array( 'total_links' => $totals->count, 'total_clicks' => $totals->sum );\r
626         \r
627         return yourls_apply_filter( 'get_db_stats', $return, $where );\r
628 }\r
629 \r
630 // Return API result. Dies after this\r
631 function yourls_api_output( $mode, $return ) {\r
632         if( isset( $return['simple'] ) ) {\r
633                 $simple = $return['simple'];\r
634                 unset( $return['simple'] );\r
635         }\r
636         \r
637         yourls_do_action( 'pre_api_output', $mode, $return );\r
638         \r
639         switch ( $mode ) {\r
640                 case 'jsonp':\r
641                         header( 'Content-type: application/javascript' );\r
642                         echo $return['callback'] . '(' . json_encode( $return ) . ')';\r
643                         break;\r
644     \r
645                 case 'json':\r
646                         header( 'Content-type: application/json' );\r
647                         echo json_encode( $return );\r
648                         break;\r
649                 \r
650                 case 'xml':\r
651                         header( 'Content-type: application/xml' );\r
652                         echo yourls_xml_encode( $return );\r
653                         break;\r
654                         \r
655                 case 'simple':\r
656                 default:\r
657                         if( isset( $simple ) )\r
658                                 echo $simple;\r
659                         break;\r
660         }\r
661 \r
662         yourls_do_action( 'api_output', $mode, $return );\r
663         \r
664         die();\r
665 }\r
666 \r
667 // Get number of SQL queries performed\r
668 function yourls_get_num_queries() {\r
669         global $ydb;\r
670 \r
671         return yourls_apply_filter( 'get_num_queries', $ydb->num_queries );\r
672 }\r
673 \r
674 // Returns a sanitized a user agent string. Given what I found on http://www.user-agents.org/ it should be OK.\r
675 function yourls_get_user_agent() {\r
676         if ( !isset( $_SERVER['HTTP_USER_AGENT'] ) )\r
677                 return '-';\r
678         \r
679         $ua = strip_tags( html_entity_decode( $_SERVER['HTTP_USER_AGENT'] ));\r
680         $ua = preg_replace('![^0-9a-zA-Z\':., /{}\(\)\[\]\+@&\!\?;_\-=~\*\#]!', '', $ua );\r
681                 \r
682         return yourls_apply_filter( 'get_user_agent', substr( $ua, 0, 254 ) );\r
683 }\r
684 \r
685 // Redirect to another page\r
686 function yourls_redirect( $location, $code = 301 ) {\r
687         yourls_do_action( 'pre_redirect', $location, $code );\r
688         $location = yourls_apply_filter( 'redirect', $location, $code );\r
689         // Redirect, either properly if possible, or via Javascript otherwise\r
690         if( !headers_sent() ) {\r
691                 yourls_status_header( $code );\r
692                 header( "Location: $location" );\r
693         } else {\r
694                 yourls_redirect_javascript( $location );\r
695         }\r
696         die();\r
697 }\r
698 \r
699 // Set HTTP status header\r
700 function yourls_status_header( $code = 200 ) {\r
701         if( headers_sent() )\r
702                 return;\r
703                 \r
704         $protocol = $_SERVER["SERVER_PROTOCOL"];\r
705         if ( 'HTTP/1.1' != $protocol && 'HTTP/1.0' != $protocol )\r
706                 $protocol = 'HTTP/1.0';\r
707 \r
708         $code = intval( $code );\r
709         $desc = yourls_get_HTTP_status( $code );\r
710 \r
711         @header ("$protocol $code $desc"); // This causes problems on IIS and some FastCGI setups\r
712         yourls_do_action( 'status_header', $code );\r
713 }\r
714 \r
715 // Redirect to another page using Javascript. Set optional (bool)$dontwait to false to force manual redirection (make sure a message has been read by user)\r
716 function yourls_redirect_javascript( $location, $dontwait = true ) {\r
717         yourls_do_action( 'pre_redirect_javascript', $location, $dontwait );\r
718         $location = yourls_apply_filter( 'redirect_javascript', $location, $dontwait );\r
719         if( $dontwait ) {\r
720                 echo <<<REDIR\r
721                 <script type="text/javascript">\r
722                 window.location="$location";\r
723                 </script>\r
724                 <small>(if you are not redirected after 10 seconds, please <a href="$location">click here</a>)</small>\r
725 REDIR;\r
726         } else {\r
727                 echo <<<MANUAL\r
728                 <p>Please <a href="$location">click here</a></p>\r
729 MANUAL;\r
730         }\r
731         yourls_do_action( 'post_redirect_javascript', $location );\r
732 }\r
733 \r
734 // Return a HTTP status code\r
735 function yourls_get_HTTP_status( $code ) {\r
736         $code = intval( $code );\r
737         $headers_desc = array(\r
738                 100 => 'Continue',\r
739                 101 => 'Switching Protocols',\r
740                 102 => 'Processing',\r
741 \r
742                 200 => 'OK',\r
743                 201 => 'Created',\r
744                 202 => 'Accepted',\r
745                 203 => 'Non-Authoritative Information',\r
746                 204 => 'No Content',\r
747                 205 => 'Reset Content',\r
748                 206 => 'Partial Content',\r
749                 207 => 'Multi-Status',\r
750                 226 => 'IM Used',\r
751 \r
752                 300 => 'Multiple Choices',\r
753                 301 => 'Moved Permanently',\r
754                 302 => 'Found',\r
755                 303 => 'See Other',\r
756                 304 => 'Not Modified',\r
757                 305 => 'Use Proxy',\r
758                 306 => 'Reserved',\r
759                 307 => 'Temporary Redirect',\r
760 \r
761                 400 => 'Bad Request',\r
762                 401 => 'Unauthorized',\r
763                 402 => 'Payment Required',\r
764                 403 => 'Forbidden',\r
765                 404 => 'Not Found',\r
766                 405 => 'Method Not Allowed',\r
767                 406 => 'Not Acceptable',\r
768                 407 => 'Proxy Authentication Required',\r
769                 408 => 'Request Timeout',\r
770                 409 => 'Conflict',\r
771                 410 => 'Gone',\r
772                 411 => 'Length Required',\r
773                 412 => 'Precondition Failed',\r
774                 413 => 'Request Entity Too Large',\r
775                 414 => 'Request-URI Too Long',\r
776                 415 => 'Unsupported Media Type',\r
777                 416 => 'Requested Range Not Satisfiable',\r
778                 417 => 'Expectation Failed',\r
779                 422 => 'Unprocessable Entity',\r
780                 423 => 'Locked',\r
781                 424 => 'Failed Dependency',\r
782                 426 => 'Upgrade Required',\r
783 \r
784                 500 => 'Internal Server Error',\r
785                 501 => 'Not Implemented',\r
786                 502 => 'Bad Gateway',\r
787                 503 => 'Service Unavailable',\r
788                 504 => 'Gateway Timeout',\r
789                 505 => 'HTTP Version Not Supported',\r
790                 506 => 'Variant Also Negotiates',\r
791                 507 => 'Insufficient Storage',\r
792                 510 => 'Not Extended'\r
793         );\r
794 \r
795         if ( isset( $headers_desc[$code] ) )\r
796                 return $headers_desc[$code];\r
797         else\r
798                 return '';\r
799 }\r
800 \r
801 \r
802 // Log a redirect (for stats)\r
803 function yourls_log_redirect( $keyword ) {\r
804         // Allow plugins to short-circuit the whole function\r
805         $pre = yourls_apply_filter( 'shunt_log_redirect', false, $keyword );\r
806         if ( false !== $pre )\r
807                 return $pre;\r
808 \r
809         if ( !yourls_do_log_redirect() )\r
810                 return true;\r
811 \r
812         global $ydb;\r
813         $table = YOURLS_DB_TABLE_LOG;\r
814         \r
815         $keyword = yourls_sanitize_string( $keyword );\r
816         $referrer = ( isset( $_SERVER['HTTP_REFERER'] ) ? yourls_sanitize_url( $_SERVER['HTTP_REFERER'] ) : 'direct' );\r
817         $ua = yourls_get_user_agent();\r
818         $ip = yourls_get_IP();\r
819         $location = yourls_geo_ip_to_countrycode( $ip );\r
820         \r
821         return $ydb->query( "INSERT INTO `$table` (click_time, shorturl, referrer, user_agent, ip_address, country_code) VALUES (NOW(), '$keyword', '$referrer', '$ua', '$ip', '$location')" );\r
822 }\r
823 \r
824 // Check if we want to not log redirects (for stats)\r
825 function yourls_do_log_redirect() {\r
826         return ( !defined( 'YOURLS_NOSTATS' ) || YOURLS_NOSTATS != true );\r
827 }\r
828 \r
829 // Converts an IP to a 2 letter country code, using GeoIP database if available in includes/geo/\r
830 function yourls_geo_ip_to_countrycode( $ip = '', $default = '' ) {\r
831         // Allow plugins to short-circuit the Geo IP API\r
832         $location = yourls_apply_filter( 'shunt_geo_ip_to_countrycode', false, $ip, $default ); // at this point $ip can be '', check if your plugin hooks in here\r
833         if ( false !== $location )\r
834                 return $location;\r
835 \r
836         if ( !file_exists( YOURLS_INC.'/geo/GeoIP.dat') || !file_exists( YOURLS_INC.'/geo/geoip.inc') )\r
837                 return $default;\r
838 \r
839         if ( $ip == '' )\r
840                 $ip = yourls_get_IP();\r
841         \r
842         require_once( YOURLS_INC.'/geo/geoip.inc') ;\r
843         $gi = geoip_open( YOURLS_INC.'/geo/GeoIP.dat', GEOIP_STANDARD);\r
844         $location = geoip_country_code_by_addr($gi, $ip);\r
845         geoip_close($gi);\r
846 \r
847         return yourls_apply_filter( 'geo_ip_to_countrycode', $location, $ip, $default );\r
848 }\r
849 \r
850 // Converts a 2 letter country code to long name (ie AU -> Australia)\r
851 function yourls_geo_countrycode_to_countryname( $code ) {\r
852         // Allow plugins to short-circuit the Geo IP API\r
853         $country = yourls_apply_filter( 'shunt_geo_countrycode_to_countryname', false, $code );\r
854         if ( false !== $country )\r
855                 return $country;\r
856 \r
857         // Load the Geo class if not already done\r
858         if( !class_exists( 'GeoIP' ) ) {\r
859                 $temp = yourls_geo_ip_to_countrycode( '127.0.0.1' );\r
860         }\r
861         \r
862         if( class_exists( 'GeoIP' ) ) {\r
863                 $geo  = new GeoIP;\r
864                 $id   = $geo->GEOIP_COUNTRY_CODE_TO_NUMBER[ $code ];\r
865                 $long = $geo->GEOIP_COUNTRY_NAMES[ $id ];\r
866                 return $long;\r
867         } else {\r
868                 return false;\r
869         }\r
870 }\r
871 \r
872 // Return flag URL from 2 letter country code\r
873 function yourls_geo_get_flag( $code ) {\r
874         if( file_exists( YOURLS_INC.'/geo/flags/flag_'.strtolower($code).'.gif' ) ) {\r
875                 $img = yourls_match_current_protocol( YOURLS_SITE.'/includes/geo/flags/flag_'.( strtolower( $code ) ).'.gif' );\r
876         } else {\r
877                 $img = false;\r
878         }\r
879         return yourls_apply_filter( 'geo_get_flag', $img, $code );\r
880 }\r
881 \r
882 \r
883 // Check if an upgrade is needed\r
884 function yourls_upgrade_is_needed() {\r
885         // check YOURLS_DB_VERSION exist && match values stored in YOURLS_DB_TABLE_OPTIONS\r
886         list( $currentver, $currentsql ) = yourls_get_current_version_from_sql();\r
887         if( $currentsql < YOURLS_DB_VERSION )\r
888                 return true;\r
889                 \r
890         return false;\r
891 }\r
892 \r
893 // Get current version & db version as stored in the options DB. Prior to 1.4 there's no option table.\r
894 function yourls_get_current_version_from_sql() {\r
895         $currentver = yourls_get_option( 'version' );\r
896         $currentsql = yourls_get_option( 'db_version' );\r
897 \r
898         // Values if version is 1.3\r
899         if( !$currentver )\r
900                 $currentver = '1.3';\r
901         if( !$currentsql )\r
902                 $currentsql = '100';\r
903                 \r
904         return array( $currentver, $currentsql);\r
905 }\r
906 \r
907 // Read an option from DB (or from cache if available). Return value or $default if not found\r
908 function yourls_get_option( $option_name, $default = false ) {\r
909         global $ydb;\r
910         \r
911         // Allow plugins to short-circuit options\r
912         $pre = yourls_apply_filter( 'shunt_option_'.$option_name, false );\r
913         if ( false !== $pre )\r
914                 return $pre;\r
915 \r
916         // If option not cached already, get its value from the DB\r
917         if ( !isset( $ydb->option[$option_name] ) ) {\r
918                 $table = YOURLS_DB_TABLE_OPTIONS;\r
919                 $option_name = yourls_escape( $option_name );\r
920                 $row = $ydb->get_row( "SELECT `option_value` FROM `$table` WHERE `option_name` = '$option_name' LIMIT 1" );\r
921                 if ( is_object( $row) ) { // Has to be get_row instead of get_var because of funkiness with 0, false, null values\r
922                         $value = $row->option_value;\r
923                 } else { // option does not exist, so we must cache its non-existence\r
924                         $value = $default;\r
925                 }\r
926                 $ydb->option[ $option_name ] = yourls_maybe_unserialize( $value );\r
927         }\r
928 \r
929         return yourls_apply_filter( 'get_option_'.$option_name, $ydb->option[$option_name] );\r
930 }\r
931 \r
932 // Read all options from DB at once\r
933 function yourls_get_all_options() {\r
934         global $ydb;\r
935 \r
936         // Allow plugins to short-circuit all options. (Note: regular plugins are loaded after all options)\r
937         $pre = yourls_apply_filter( 'shunt_all_options', false );\r
938         if ( false !== $pre )\r
939                 return $pre;\r
940 \r
941         $table = YOURLS_DB_TABLE_OPTIONS;\r
942         \r
943         $allopt = $ydb->get_results( "SELECT `option_name`, `option_value` FROM `$table` WHERE 1=1" );\r
944         \r
945         foreach( (array)$allopt as $option ) {\r
946                 $ydb->option[$option->option_name] = yourls_maybe_unserialize( $option->option_value );\r
947         }\r
948         \r
949         $ydb->option = yourls_apply_filter( 'get_all_options', $ydb->option );\r
950 }\r
951 \r
952 // Update (add if doesn't exist) an option to DB\r
953 function yourls_update_option( $option_name, $newvalue ) {\r
954         global $ydb;\r
955         $table = YOURLS_DB_TABLE_OPTIONS;\r
956 \r
957         $safe_option_name = yourls_escape( $option_name );\r
958 \r
959         $oldvalue = yourls_get_option( $safe_option_name );\r
960 \r
961         // If the new and old values are the same, no need to update.\r
962         if ( $newvalue === $oldvalue )\r
963                 return false;\r
964 \r
965         if ( false === $oldvalue ) {\r
966                 yourls_add_option( $option_name, $newvalue );\r
967                 return true;\r
968         }\r
969 \r
970         $_newvalue = yourls_escape( yourls_maybe_serialize( $newvalue ) );\r
971         \r
972         yourls_do_action( 'update_option', $option_name, $oldvalue, $newvalue );\r
973 \r
974         $ydb->query( "UPDATE `$table` SET `option_value` = '$_newvalue' WHERE `option_name` = '$option_name'" );\r
975 \r
976         if ( $ydb->rows_affected == 1 ) {\r
977                 $ydb->option[ $option_name ] = $newvalue;\r
978                 return true;\r
979         }\r
980         return false;\r
981 }\r
982 \r
983 // Add an option to the DB\r
984 function yourls_add_option( $name, $value = '' ) {\r
985         global $ydb;\r
986         $table = YOURLS_DB_TABLE_OPTIONS;\r
987         $safe_name = yourls_escape( $name );\r
988 \r
989         // Make sure the option doesn't already exist\r
990         if ( false !== yourls_get_option( $safe_name ) )\r
991                 return;\r
992 \r
993         $_value = yourls_escape( yourls_maybe_serialize( $value ) );\r
994 \r
995         yourls_do_action( 'add_option', $safe_name, $_value );\r
996 \r
997         $ydb->query( "INSERT INTO `$table` (`option_name`, `option_value`) VALUES ('$name', '$_value')" );\r
998         $ydb->option[ $name ] = $value;\r
999         return;\r
1000 }\r
1001 \r
1002 \r
1003 // Delete an option from the DB\r
1004 function yourls_delete_option( $name ) {\r
1005         global $ydb;\r
1006         $table = YOURLS_DB_TABLE_OPTIONS;\r
1007         $name = yourls_escape( $name );\r
1008 \r
1009         // Get the ID, if no ID then return\r
1010         $option = $ydb->get_row( "SELECT option_id FROM `$table` WHERE `option_name` = '$name'" );\r
1011         if ( is_null( $option ) || !$option->option_id )\r
1012                 return false;\r
1013                 \r
1014         yourls_do_action( 'delete_option', $option_name );\r
1015                 \r
1016         $ydb->query( "DELETE FROM `$table` WHERE `option_name` = '$name'" );\r
1017         return true;\r
1018 }\r
1019 \r
1020 \r
1021 \r
1022 // Serialize data if needed. Stolen from WordPress\r
1023 function yourls_maybe_serialize( $data ) {\r
1024         if ( is_array( $data ) || is_object( $data ) )\r
1025                 return serialize( $data );\r
1026 \r
1027         if ( yourls_is_serialized( $data ) )\r
1028                 return serialize( $data );\r
1029 \r
1030         return $data;\r
1031 }\r
1032 \r
1033 // Check value to find if it was serialized. Stolen from WordPress\r
1034 function yourls_is_serialized( $data ) {\r
1035         // if it isn't a string, it isn't serialized\r
1036         if ( !is_string( $data ) )\r
1037                 return false;\r
1038         $data = trim( $data );\r
1039         if ( 'N;' == $data )\r
1040                 return true;\r
1041         if ( !preg_match( '/^([adObis]):/', $data, $badions ) )\r
1042                 return false;\r
1043         switch ( $badions[1] ) {\r
1044                 case 'a' :\r
1045                 case 'O' :\r
1046                 case 's' :\r
1047                         if ( preg_match( "/^{$badions[1]}:[0-9]+:.*[;}]\$/s", $data ) )\r
1048                                 return true;\r
1049                         break;\r
1050                 case 'b' :\r
1051                 case 'i' :\r
1052                 case 'd' :\r
1053                         if ( preg_match( "/^{$badions[1]}:[0-9.E-]+;\$/", $data ) )\r
1054                                 return true;\r
1055                         break;\r
1056         }\r
1057         return false;\r
1058 }\r
1059 \r
1060 // Unserialize value only if it was serialized. Stolen from WP\r
1061 function yourls_maybe_unserialize( $original ) {\r
1062         if ( yourls_is_serialized( $original ) ) // don't attempt to unserialize data that wasn't serialized going in\r
1063                 return @unserialize( $original );\r
1064         return $original;\r
1065 }\r
1066 \r
1067 // Determine if the current page is private\r
1068 function yourls_is_private() {\r
1069         $private = false;\r
1070 \r
1071         if ( defined('YOURLS_PRIVATE') && YOURLS_PRIVATE == true ) {\r
1072 \r
1073                 // Allow overruling for particular pages:\r
1074                 \r
1075                 // API\r
1076                 if( yourls_is_API() ) {\r
1077                         if( !defined('YOURLS_PRIVATE_API') || YOURLS_PRIVATE_API != false )\r
1078                                 $private = true;                \r
1079 \r
1080                 // Infos\r
1081                 } elseif( yourls_is_infos() ) {\r
1082                         if( !defined('YOURLS_PRIVATE_INFOS') || YOURLS_PRIVATE_INFOS !== false )\r
1083                                 $private = true;\r
1084                 \r
1085                 // Others\r
1086                 } else {\r
1087                         $private = true;\r
1088                 }\r
1089                 \r
1090         }\r
1091                         \r
1092         return yourls_apply_filter( 'is_private', $private );\r
1093 }\r
1094 \r
1095 // Show login form if required\r
1096 function yourls_maybe_require_auth() {\r
1097         if( yourls_is_private() )\r
1098                 require_once( YOURLS_INC.'/auth.php' );\r
1099 }\r
1100 \r
1101 // Allow several short URLs for the same long URL ?\r
1102 function yourls_allow_duplicate_longurls() {\r
1103         // special treatment if API to check for WordPress plugin requests\r
1104         if( yourls_is_API() ) {\r
1105                 if ( isset($_REQUEST['source']) && $_REQUEST['source'] == 'plugin' ) \r
1106                         return false;\r
1107         }\r
1108         return ( defined( 'YOURLS_UNIQUE_URLS' ) && YOURLS_UNIQUE_URLS == false );\r
1109 }\r
1110 \r
1111 // Return list of all shorturls associated to the same long URL. Returns NULL or array of keywords.\r
1112 function yourls_get_duplicate_keywords( $longurl ) {\r
1113         if( !yourls_allow_duplicate_longurls() )\r
1114                 return NULL;\r
1115         \r
1116         global $ydb;\r
1117         $longurl = yourls_escape( yourls_sanitize_url($longurl) );\r
1118         $table = YOURLS_DB_TABLE_URL;\r
1119         \r
1120         $return = $ydb->get_col( "SELECT `keyword` FROM `$table` WHERE `url` = '$longurl'" );\r
1121         return yourls_apply_filter( 'get_duplicate_keywords', $return, $longurl );\r
1122 }\r
1123 \r
1124 // Check if an IP shortens URL too fast to prevent DB flood. Return true, or die.\r
1125 function yourls_check_IP_flood( $ip = '' ) {\r
1126 \r
1127         // Allow plugins to short-circuit the whole function\r
1128         $pre = yourls_apply_filter( 'shunt_check_IP_flood', false, $ip );\r
1129         if ( false !== $pre )\r
1130                 return $pre;\r
1131 \r
1132         yourls_do_action( 'pre_check_ip_flood', $ip ); // at this point $ip can be '', check it if your plugin hooks in here\r
1133 \r
1134         if(\r
1135                 ( defined('YOURLS_FLOOD_DELAY_SECONDS') && YOURLS_FLOOD_DELAY_SECONDS === 0 ) ||\r
1136                 !defined('YOURLS_FLOOD_DELAY_SECONDS')\r
1137         )\r
1138                 return true;\r
1139 \r
1140         $ip = ( $ip ? yourls_sanitize_ip( $ip ) : yourls_get_IP() );\r
1141 \r
1142         // Don't throttle whitelist IPs\r
1143         if( defined( 'YOURLS_FLOOD_IP_WHITELIST' ) && YOURLS_FLOOD_IP_WHITELIST ) {\r
1144                 $whitelist_ips = explode( ',', YOURLS_FLOOD_IP_WHITELIST );\r
1145                 foreach( (array)$whitelist_ips as $whitelist_ip ) {\r
1146                         $whitelist_ip = trim( $whitelist_ip );\r
1147                         if ( $whitelist_ip == $ip )\r
1148                                 return true;\r
1149                 }\r
1150         }\r
1151         \r
1152         // Don't throttle logged in users\r
1153         if( yourls_is_private() ) {\r
1154                  if( yourls_is_valid_user() === true )\r
1155                         return true;\r
1156         }\r
1157         \r
1158         yourls_do_action( 'check_ip_flood', $ip );\r
1159         \r
1160         global $ydb;\r
1161         $table = YOURLS_DB_TABLE_URL;\r
1162         \r
1163         $lasttime = $ydb->get_var( "SELECT `timestamp` FROM $table WHERE `ip` = '$ip' ORDER BY `timestamp` DESC LIMIT 1" );\r
1164         if( $lasttime ) {\r
1165                 $now = date( 'U' );\r
1166                 $then = date( 'U', strtotime( $lasttime ) );\r
1167                 if( ( $now - $then ) <= YOURLS_FLOOD_DELAY_SECONDS ) {\r
1168                         // Flood!\r
1169                         yourls_do_action( 'ip_flood', $ip, $now - $then );\r
1170                         yourls_die( 'Too many URLs added too fast. Slow down please.', 'Forbidden', 403 );\r
1171                 }\r
1172         }\r
1173         \r
1174         return true;\r
1175 }\r
1176 \r
1177 // Check if YOURLS is installed\r
1178 function yourls_is_installed() {\r
1179         static $is_installed = false;\r
1180         if ( $is_installed === false ) {\r
1181                 $check_14 = $check_13 = false;\r
1182                 global $ydb;\r
1183                 if( defined('YOURLS_DB_TABLE_NEXTDEC') )\r
1184                         $check_13 = $ydb->get_var('SELECT `next_id` FROM '.YOURLS_DB_TABLE_NEXTDEC);\r
1185                 $check_14 = yourls_get_option( 'version' );\r
1186                 $is_installed = $check_13 || $check_14;\r
1187         }\r
1188         return yourls_apply_filter( 'is_installed', $is_installed );\r
1189 }\r
1190 \r
1191 // Generate random string of (int)$length length and type $type (see function for details)\r
1192 function yourls_rnd_string ( $length = 5, $type = 0, $charlist = '' ) {\r
1193         $str = '';\r
1194         $length = intval( $length );\r
1195 \r
1196         // define possible characters\r
1197         switch ( $type ) {\r
1198 \r
1199                 // custom char list, or comply to charset as defined in config\r
1200                 case '0':\r
1201                         $possible = $charlist ? $charlist : yourls_get_shorturl_charset() ;\r
1202                         break;\r
1203         \r
1204                 // no vowels to make no offending word, no 0/1/o/l to avoid confusion between letters & digits. Perfect for passwords.\r
1205                 case '1':\r
1206                         $possible = "23456789bcdfghjkmnpqrstvwxyz";\r
1207                         break;\r
1208                 \r
1209                 // Same, with lower + upper\r
1210                 case '2':\r
1211                         $possible = "23456789bcdfghjkmnpqrstvwxyzBCDFGHJKMNPQRSTVWXYZ";\r
1212                         break;\r
1213                 \r
1214                 // all letters, lowercase\r
1215                 case '3':\r
1216                         $possible = "abcdefghijklmnopqrstuvwxyz";\r
1217                         break;\r
1218                 \r
1219                 // all letters, lowercase + uppercase\r
1220                 case '4':\r
1221                         $possible = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";\r
1222                         break;\r
1223                 \r
1224                 // all digits & letters lowercase \r
1225                 case '5':\r
1226                         $possible = "0123456789abcdefghijklmnopqrstuvwxyz";\r
1227                         break;\r
1228                 \r
1229                 // all digits & letters lowercase + uppercase\r
1230                 case '6':\r
1231                         $possible = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";\r
1232                         break;\r
1233                 \r
1234         }\r
1235 \r
1236         $i = 0;\r
1237         while ($i < $length) {\r
1238                 $str .= substr($possible, mt_rand(0, strlen($possible)-1), 1);\r
1239                 $i++;\r
1240         }\r
1241         \r
1242         return yourls_apply_filter( 'rnd_string', $str, $length, $type, $charlist );\r
1243 }\r
1244 \r
1245 // Return salted string\r
1246 function yourls_salt( $string ) {\r
1247         $salt = defined('YOURLS_COOKIEKEY') ? YOURLS_COOKIEKEY : md5(__FILE__) ;\r
1248         return yourls_apply_filter( 'yourls_salt', md5 ($string . $salt), $string );\r
1249 }\r
1250 \r
1251 // Add a query var to a URL and return URL. Completely stolen from WP.\r
1252 // Works with one of these parameter patterns:\r
1253 //     array( 'var' => 'value' )\r
1254 //     array( 'var' => 'value' ), $url\r
1255 //     'var', 'value'\r
1256 //     'var', 'value', $url\r
1257 // If $url ommited, uses $_SERVER['REQUEST_URI']\r
1258 function yourls_add_query_arg() {\r
1259         $ret = '';\r
1260         if ( is_array( func_get_arg(0) ) ) {\r
1261                 if ( @func_num_args() < 2 || false === @func_get_arg( 1 ) )\r
1262                         $uri = $_SERVER['REQUEST_URI'];\r
1263                 else\r
1264                         $uri = @func_get_arg( 1 );\r
1265         } else {\r
1266                 if ( @func_num_args() < 3 || false === @func_get_arg( 2 ) )\r
1267                         $uri = $_SERVER['REQUEST_URI'];\r
1268                 else\r
1269                         $uri = @func_get_arg( 2 );\r
1270         }\r
1271         \r
1272         $uri = str_replace( '&amp;', '&', $uri );\r
1273 \r
1274         \r
1275         if ( $frag = strstr( $uri, '#' ) )\r
1276                 $uri = substr( $uri, 0, -strlen( $frag ) );\r
1277         else\r
1278                 $frag = '';\r
1279 \r
1280         if ( preg_match( '|^https?://|i', $uri, $matches ) ) {\r
1281                 $protocol = $matches[0];\r
1282                 $uri = substr( $uri, strlen( $protocol ) );\r
1283         } else {\r
1284                 $protocol = '';\r
1285         }\r
1286 \r
1287         if ( strpos( $uri, '?' ) !== false ) {\r
1288                 $parts = explode( '?', $uri, 2 );\r
1289                 if ( 1 == count( $parts ) ) {\r
1290                         $base = '?';\r
1291                         $query = $parts[0];\r
1292                 } else {\r
1293                         $base = $parts[0] . '?';\r
1294                         $query = $parts[1];\r
1295                 }\r
1296         } elseif ( !empty( $protocol ) || strpos( $uri, '=' ) === false ) {\r
1297                 $base = $uri . '?';\r
1298                 $query = '';\r
1299         } else {\r
1300                 $base = '';\r
1301                 $query = $uri;\r
1302         }\r
1303 \r
1304         parse_str( $query, $qs );\r
1305         $qs = yourls_urlencode_deep( $qs ); // this re-URL-encodes things that were already in the query string\r
1306         if ( is_array( func_get_arg( 0 ) ) ) {\r
1307                 $kayvees = func_get_arg( 0 );\r
1308                 $qs = array_merge( $qs, $kayvees );\r
1309         } else {\r
1310                 $qs[func_get_arg( 0 )] = func_get_arg( 1 );\r
1311         }\r
1312 \r
1313         foreach ( (array) $qs as $k => $v ) {\r
1314                 if ( $v === false )\r
1315                         unset( $qs[$k] );\r
1316         }\r
1317 \r
1318         $ret = http_build_query( $qs );\r
1319         $ret = trim( $ret, '?' );\r
1320         $ret = preg_replace( '#=(&|$)#', '$1', $ret );\r
1321         $ret = $protocol . $base . $ret . $frag;\r
1322         $ret = rtrim( $ret, '?' );\r
1323         return $ret;\r
1324 }\r
1325 \r
1326 // Navigates through an array and encodes the values to be used in a URL. Stolen from WP, used in yourls_add_query_arg()\r
1327 function yourls_urlencode_deep( $value ) {\r
1328         $value = is_array( $value ) ? array_map( 'yourls_urlencode_deep', $value ) : urlencode( $value );\r
1329         return $value;\r
1330 }\r
1331 \r
1332 // Remove arg from query. Opposite of yourls_add_query_arg. Stolen from WP.\r
1333 function yourls_remove_query_arg( $key, $query = false ) {\r
1334         if ( is_array( $key ) ) { // removing multiple keys\r
1335                 foreach ( $key as $k )\r
1336                         $query = yourls_add_query_arg( $k, false, $query );\r
1337                 return $query;\r
1338         }\r
1339         return yourls_add_query_arg( $key, false, $query );\r
1340 }\r
1341 \r
1342 // Return a time-dependent string for nonce creation\r
1343 function yourls_tick() {\r
1344         return ceil( time() / YOURLS_NONCE_LIFE );\r
1345 }\r
1346 \r
1347 // Create a time limited, action limited and user limited token\r
1348 function yourls_create_nonce( $action, $user = false ) {\r
1349         if( false == $user )\r
1350                 $user = defined( 'YOURLS_USER' ) ? YOURLS_USER : '-1';\r
1351         $tick = yourls_tick();\r
1352         return substr( yourls_salt($tick . $action . $user), 0, 10 );\r
1353 }\r
1354 \r
1355 // Create a nonce field for inclusion into a form\r
1356 function yourls_nonce_field( $action, $name = 'nonce', $user = false, $echo = true ) {\r
1357         $field = '<input type="hidden" id="'.$name.'" name="'.$name.'" value="'.yourls_create_nonce( $action, $user ).'" />';\r
1358         if( $echo )\r
1359                 echo $field."\n";\r
1360         return $field;\r
1361 }\r
1362 \r
1363 // Add a nonce to a URL. If URL omitted, adds nonce to current URL\r
1364 function yourls_nonce_url( $action, $url = false, $name = 'nonce', $user = false ) {\r
1365         $nonce = yourls_create_nonce( $action, $user );\r
1366         return yourls_add_query_arg( $name, $nonce, $url );\r
1367 }\r
1368 \r
1369 // Check validity of a nonce (ie time span, user and action match).\r
1370 // Returns true if valid, dies otherwise (yourls_die() or die($return) if defined)\r
1371 // if $nonce is false or unspecified, it will use $_REQUEST['nonce']\r
1372 function yourls_verify_nonce( $action, $nonce = false, $user = false, $return = '' ) {\r
1373         // get user\r
1374         if( false == $user )\r
1375                 $user = defined( 'YOURLS_USER' ) ? YOURLS_USER : '-1';\r
1376                 \r
1377         // get current nonce value\r
1378         if( false == $nonce && isset( $_REQUEST['nonce'] ) )\r
1379                 $nonce = $_REQUEST['nonce'];\r
1380 \r
1381         // what nonce should be\r
1382         $valid = yourls_create_nonce( $action, $user );\r
1383         \r
1384         if( $nonce == $valid ) {\r
1385                 return true;\r
1386         } else {\r
1387                 if( $return )\r
1388                         die( $return );\r
1389                 yourls_die( 'Unauthorized action or expired link', 'Error', 403 );\r
1390         }\r
1391 }\r
1392 \r
1393 // Converts keyword into short link (prepend with YOURLS base URL)\r
1394 function yourls_link( $keyword = '' ) {\r
1395         $link = YOURLS_SITE . '/' . yourls_sanitize_keyword( $keyword );\r
1396         return yourls_apply_filter( 'yourls_link', $link, $keyword );\r
1397 }\r
1398 \r
1399 // Converts keyword into stat link (prepend with YOURLS base URL, append +)\r
1400 function yourls_statlink( $keyword = '' ) {\r
1401         $link = YOURLS_SITE . '/' . yourls_sanitize_keyword( $keyword ) . '+';\r
1402         return yourls_apply_filter( 'yourls_statlink', $link, $keyword );\r
1403 }\r
1404 \r
1405 // Check if we're in API mode. Returns bool\r
1406 function yourls_is_API() {\r
1407         if ( defined( 'YOURLS_API' ) && YOURLS_API == true )\r
1408                 return true;\r
1409         return false;\r
1410 }\r
1411 \r
1412 // Check if we're in Ajax mode. Returns bool\r
1413 function yourls_is_Ajax() {\r
1414         if ( defined( 'YOURLS_AJAX' ) && YOURLS_AJAX == true )\r
1415                 return true;\r
1416         return false;\r
1417 }\r
1418 \r
1419 // Check if we're in GO mode (yourls-go.php). Returns bool\r
1420 function yourls_is_GO() {\r
1421         if ( defined( 'YOURLS_GO' ) && YOURLS_GO == true )\r
1422                 return true;\r
1423         return false;\r
1424 }\r
1425 \r
1426 // Check if we're displaying stats infos (yourls-infos.php). Returns bool\r
1427 function yourls_is_infos() {\r
1428         if ( defined( 'YOURLS_INFOS' ) && YOURLS_INFOS == true )\r
1429                 return true;\r
1430         return false;\r
1431 }\r
1432 \r
1433 // Check if we'll need interface display function (ie not API or redirection)\r
1434 function yourls_has_interface() {\r
1435         if( yourls_is_API() or yourls_is_GO() )\r
1436                 return false;\r
1437         return true;\r
1438 }\r
1439 \r
1440 // Check if we're in the admin area. Returns bool\r
1441 function yourls_is_admin() {\r
1442         if ( defined( 'YOURLS_ADMIN' ) && YOURLS_ADMIN == true )\r
1443                 return true;\r
1444         return false;\r
1445 }\r
1446 \r
1447 // Check if the server seems to be running on Windows. Not exactly sure how reliable this is.\r
1448 function yourls_is_windows() {\r
1449         return defined( 'DIRECTORY_SEPARATOR' ) && DIRECTORY_SEPARATOR == '\\';\r
1450 }\r
1451 \r
1452 // Check if SSL is required. Returns bool.\r
1453 function yourls_needs_ssl() {\r
1454         if ( defined('YOURLS_ADMIN_SSL') && YOURLS_ADMIN_SSL == true )\r
1455                 return true;\r
1456         return false;\r
1457 }\r
1458 \r
1459 // Return admin link, with SSL preference if applicable.\r
1460 function yourls_admin_url( $page = '' ) {\r
1461         $admin = YOURLS_SITE . '/admin/' . $page;\r
1462         if( yourls_is_ssl() or yourls_needs_ssl() )\r
1463                 $admin = str_replace('http://', 'https://', $admin);\r
1464         return yourls_apply_filter( 'admin_url', $admin, $page );\r
1465 }\r
1466 \r
1467 // Return YOURLS_SITE or URL under YOURLS setup, with SSL preference\r
1468 function yourls_site_url( $echo = true, $url = '' ) {\r
1469         if( !$url ) {\r
1470                 $url = YOURLS_SITE;\r
1471         } else {\r
1472                 // make $url relative to YOURLS base in case a full URL has been provided. Will break if user mixes https/http.\r
1473                 $url = str_replace( YOURLS_SITE, '', $url );\r
1474                 $url = YOURLS_SITE . '/' . ltrim( $url, '/' );\r
1475         }\r
1476         // Do not enforce (checking yourls_need_ssl() ) but check current usage so it won't force SSL on non-admin pages\r
1477         if( yourls_is_ssl() )\r
1478                 $url = str_replace( 'http://', 'https://', $url );\r
1479         $url = yourls_apply_filter( 'site_url', $url );\r
1480         if( $echo )\r
1481                 echo $url;\r
1482         return $url;\r
1483 }\r
1484 \r
1485 // Check if SSL is used, returns bool. Stolen from WP.\r
1486 function yourls_is_ssl() {\r
1487         $is_ssl = false;\r
1488         if ( isset( $_SERVER['HTTPS'] ) ) {\r
1489                 if ( 'on' == strtolower( $_SERVER['HTTPS'] ) )\r
1490                         $is_ssl = true;\r
1491                 if ( '1' == $_SERVER['HTTPS'] )\r
1492                         $is_ssl = true;\r
1493         } elseif ( isset( $_SERVER['SERVER_PORT'] ) && ( '443' == $_SERVER['SERVER_PORT'] ) ) {\r
1494                 $is_ssl = true;\r
1495         }\r
1496         return yourls_apply_filter( 'is_ssl', $is_ssl );\r
1497 }\r
1498 \r
1499 \r
1500 // Get a remote page <title>, return a string (either title or url)\r
1501 function yourls_get_remote_title( $url ) {\r
1502         // Allow plugins to short-circuit the whole function\r
1503         $pre = yourls_apply_filter( 'shunt_get_remote_title', false, $url );\r
1504         if ( false !== $pre )\r
1505                 return $pre;\r
1506 \r
1507         require_once( YOURLS_INC.'/functions-http.php' );\r
1508 \r
1509         $url = yourls_sanitize_url( $url );\r
1510 \r
1511         $title = $charset = false;\r
1512         \r
1513         $content = yourls_get_remote_content( $url );\r
1514         \r
1515         // If false, return url as title.\r
1516         // Todo: improve this with temporary title when shorturl_meta available?\r
1517         if( false === $content )\r
1518                 return $url;\r
1519 \r
1520         if( $content !== false ) {\r
1521                 // look for <title>\r
1522                 if ( preg_match('/<title>(.*?)<\/title>/is', $content, $found ) ) {\r
1523                         $title = $found[1];\r
1524                         unset( $found );\r
1525                 }\r
1526 \r
1527                 // look for charset\r
1528                 // <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\r
1529                 if ( preg_match('/<meta[^>]*?charset=([^>]*?)\/?>/is', $content, $found ) ) {\r
1530                         $charset = trim($found[1], '"\' ');\r
1531                         unset( $found );\r
1532                 }\r
1533         }\r
1534         \r
1535         // if title not found, guess if returned content was actually an error message\r
1536         if( $title == false && strpos( $content, 'Error' ) === 0 ) {\r
1537                 $title = $content;\r
1538         }\r
1539         \r
1540         if( $title == false )\r
1541                 $title = $url;\r
1542         \r
1543         /*\r
1544         if( !yourls_seems_utf8( $title ) )\r
1545                 $title = utf8_encode( $title );\r
1546         */\r
1547         \r
1548         // Charset conversion. We use @ to remove warnings (mb_ functions are easily bitching about illegal chars)\r
1549         if( function_exists( 'mb_convert_encoding' ) ) {\r
1550                 if( $charset ) {\r
1551                         $title = @mb_convert_encoding( $title, 'UTF-8', $charset );\r
1552                 } else {\r
1553                         $title = @mb_convert_encoding( $title, 'UTF-8' );\r
1554                 }\r
1555         }\r
1556         \r
1557         // Remove HTML entities\r
1558         $title = html_entity_decode( $title, ENT_QUOTES, 'UTF-8' );\r
1559         \r
1560         // Strip out evil things\r
1561         $title = yourls_sanitize_title( $title );\r
1562         \r
1563         return yourls_apply_filter( 'get_remote_title', $title, $url );\r
1564 }\r
1565 \r
1566 // Quick UA check for mobile devices. Return boolean.\r
1567 function yourls_is_mobile_device() {\r
1568         // Strings searched\r
1569         $mobiles = array(\r
1570                 'android', 'blackberry', 'blazer',\r
1571                 'compal', 'elaine', 'fennec', 'hiptop',\r
1572                 'iemobile', 'iphone', 'ipod', 'ipad',\r
1573                 'iris', 'kindle', 'opera mobi', 'opera mini',\r
1574                 'palm', 'phone', 'pocket', 'psp', 'symbian',\r
1575                 'treo', 'wap', 'windows ce', 'windows phone'\r
1576         );\r
1577         \r
1578         // Current user-agent\r
1579         $current = strtolower( $_SERVER['HTTP_USER_AGENT'] );\r
1580         \r
1581         // Check and return\r
1582         $is_mobile = ( str_replace( $mobiles, '', $current ) != $current );\r
1583         return yourls_apply_filter( 'is_mobile_device', $is_mobile );\r
1584 }\r
1585 \r
1586 // Get request in YOURLS base (eg in 'http://site.com/yourls/abcd' get 'abdc')\r
1587 function yourls_get_request() {\r
1588         // Allow plugins to short-circuit the whole function\r
1589         $pre = yourls_apply_filter( 'shunt_get_request', false );\r
1590         if ( false !== $pre )\r
1591                 return $pre;\r
1592                 \r
1593         yourls_do_action( 'pre_get_request' );\r
1594         \r
1595         // Ignore protocol & www. prefix\r
1596         $root = str_replace( array( 'https://', 'http://', 'https://www.', 'http://www.' ), '', YOURLS_SITE );\r
1597         // Case insensitive comparison of the YOURLS root to match both http://Sho.rt/blah and http://sho.rt/blah\r
1598         $request = preg_replace( "!$root/!i", '', $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], 1 );\r
1599 \r
1600         // Unless request looks like a full URL (ie request is a simple keyword) strip query string\r
1601         if( !preg_match( "@^[a-zA-Z]+://.+@", $request ) ) {\r
1602                 $request = current( explode( '?', $request ) );\r
1603         }\r
1604         \r
1605         return yourls_apply_filter( 'get_request', $request );\r
1606 }\r
1607 \r
1608 // Change protocol to match current scheme used (http or https)\r
1609 function yourls_match_current_protocol( $url, $normal = 'http', $ssl = 'https' ) {\r
1610         if( yourls_is_ssl() )\r
1611                 $url = str_replace( $normal, $ssl, $url );\r
1612         return yourls_apply_filter( 'match_current_protocol', $url );\r
1613 }\r
1614 \r
1615 // Fix $_SERVER['REQUEST_URI'] variable for various setups. Stolen from WP.\r
1616 function yourls_fix_request_uri() {\r
1617 \r
1618         $default_server_values = array(\r
1619                 'SERVER_SOFTWARE' => '',\r
1620                 'REQUEST_URI' => '',\r
1621         );\r
1622         $_SERVER = array_merge( $default_server_values, $_SERVER );\r
1623 \r
1624         // Fix for IIS when running with PHP ISAPI\r
1625         if ( empty( $_SERVER['REQUEST_URI'] ) || ( php_sapi_name() != 'cgi-fcgi' && preg_match( '/^Microsoft-IIS\//', $_SERVER['SERVER_SOFTWARE'] ) ) ) {\r
1626 \r
1627                 // IIS Mod-Rewrite\r
1628                 if ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] ) ) {\r
1629                         $_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_ORIGINAL_URL'];\r
1630                 }\r
1631                 // IIS Isapi_Rewrite\r
1632                 else if ( isset( $_SERVER['HTTP_X_REWRITE_URL'] ) ) {\r
1633                         $_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_REWRITE_URL'];\r
1634                 } else {\r
1635                         // Use ORIG_PATH_INFO if there is no PATH_INFO\r
1636                         if ( !isset( $_SERVER['PATH_INFO'] ) && isset( $_SERVER['ORIG_PATH_INFO'] ) )\r
1637                                 $_SERVER['PATH_INFO'] = $_SERVER['ORIG_PATH_INFO'];\r
1638 \r
1639                         // Some IIS + PHP configurations puts the script-name in the path-info (No need to append it twice)\r
1640                         if ( isset( $_SERVER['PATH_INFO'] ) ) {\r
1641                                 if ( $_SERVER['PATH_INFO'] == $_SERVER['SCRIPT_NAME'] )\r
1642                                         $_SERVER['REQUEST_URI'] = $_SERVER['PATH_INFO'];\r
1643                                 else\r
1644                                         $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] . $_SERVER['PATH_INFO'];\r
1645                         }\r
1646 \r
1647                         // Append the query string if it exists and isn't null\r
1648                         if ( ! empty( $_SERVER['QUERY_STRING'] ) ) {\r
1649                                 $_SERVER['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];\r
1650                         }\r
1651                 }\r
1652         }\r
1653 }\r
1654 \r
1655 // Shutdown function, runs just before PHP shuts down execution. Stolen from WP\r
1656 function yourls_shutdown() {\r
1657         yourls_do_action( 'shutdown' );\r
1658 }\r
1659 \r
1660 // Auto detect custom favicon in /user directory, fallback to YOURLS favicon, and echo/return its URL\r
1661 function yourls_favicon( $echo = true ) {\r
1662         static $favicon = null;\r
1663         if( $favicon !== null )\r
1664                 return $favicon;\r
1665         \r
1666         $custom = null;\r
1667         // search for favicon.(gif|ico|png|jpg|svg)\r
1668         foreach( array( 'gif', 'ico', 'png', 'jpg', 'svg' ) as $ext ) {\r
1669                 if( file_exists( YOURLS_USERDIR. '/favicon.' . $ext ) ) {\r
1670                         $custom = 'favicon.' . $ext;\r
1671                         break;\r
1672                 }\r
1673         }\r
1674         \r
1675         if( $custom ) {\r
1676                 $favicon = yourls_site_url( false, YOURLS_USERURL . '/' . $custom );\r
1677         } else {\r
1678                 $favicon = yourls_site_url( false ) . '/images/favicon.gif';\r
1679         }\r
1680         if( $echo )\r
1681                 echo $favicon;\r
1682         return $favicon;\r
1683 }\r
1684 \r
1685 // Check for maintenance mode. If yes, die. See yourls_maintenance_mode(). Stolen from WP.\r
1686 function yourls_check_maintenance_mode() {\r
1687 \r
1688         $file = YOURLS_ABSPATH . '/.maintenance' ;\r
1689         if ( !file_exists( $file ) || defined( 'YOURLS_UPGRADING' ) || defined( 'YOURLS_INSTALLING' ) )\r
1690                 return;\r
1691         \r
1692         global $maintenance_start;\r
1693 \r
1694         include( $file );\r
1695         // If the $maintenance_start timestamp is older than 10 minutes, don't die.\r
1696         if ( ( time() - $maintenance_start ) >= 600 )\r
1697                 return;\r
1698 \r
1699         // Use any /user/maintenance.php file\r
1700         if( file_exists( YOURLS_USERDIR.'/maintenance.php' ) ) {\r
1701                 include( YOURLS_USERDIR.'/maintenance.php' );\r
1702                 die();\r
1703         }\r
1704         \r
1705         // https://www.youtube.com/watch?v=Xw-m4jEY-Ns\r
1706         $title   = 'Service temporarily unavailable';\r
1707         $message = 'Our service is currently undergoing scheduled maintenance.</p>\r
1708         <p>Things should not last very long, thank you for your patience and please excuse the inconvenience';\r
1709         yourls_die( $message, $title , 503 );\r
1710 \r
1711 }\r