]> CyberLeo.Net >> Repos - Github/YOURLS.git/blob - includes/functions.php
Whitespaces, bitches!! Because, you know, CodingStandards.
[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 = yourls_get_db_stats();\r
571         $return['simple']  = 'Need either XML or JSON format for stats';\r
572         $return['message'] = 'success';\r
573         return yourls_apply_filter( 'api_db_stats', $return );\r
574 }\r
575 \r
576 // Return array for API stat requests\r
577 function yourls_api_url_stats( $shorturl ) {\r
578         $keyword = str_replace( YOURLS_SITE . '/' , '', $shorturl ); // accept either 'http://ozh.in/abc' or 'abc'\r
579         $keyword = yourls_sanitize_string( $keyword );\r
580 \r
581         $return = yourls_get_link_stats( $keyword );\r
582         $return['simple']  = 'Need either XML or JSON format for stats';\r
583         return yourls_apply_filter( 'api_url_stats', $return, $shorturl );\r
584 }\r
585 \r
586 // Expand short url to long url\r
587 function yourls_api_expand( $shorturl ) {\r
588         $keyword = str_replace( YOURLS_SITE . '/' , '', $shorturl ); // accept either 'http://ozh.in/abc' or 'abc'\r
589         $keyword = yourls_sanitize_string( $keyword );\r
590         \r
591         $longurl = yourls_get_keyword_longurl( $keyword );\r
592         \r
593         if( $longurl ) {\r
594                 $return = array(\r
595                         'keyword'   => $keyword,\r
596                         'shorturl'  => YOURLS_SITE . "/$keyword",\r
597                         'longurl'   => $longurl,\r
598                         'simple'    => $longurl,\r
599                         'message'   => 'success',\r
600                         'statusCode' => 200,\r
601                 );\r
602         } else {\r
603                 $return = array(\r
604                         'keyword'   => $keyword,\r
605                         'simple'    => 'not found',\r
606                         'message'   => 'Error: short URL not found',\r
607                         'errorCode' => 404,\r
608                 );\r
609         }\r
610         \r
611         return yourls_apply_filter( 'api_expand', $return, $shorturl );\r
612 }\r
613 \r
614 \r
615 // Get total number of URLs and sum of clicks. Input: optional "AND WHERE" clause. Returns array\r
616 function yourls_get_db_stats( $where = '' ) {\r
617         global $ydb;\r
618         $table_url = YOURLS_DB_TABLE_URL;\r
619 \r
620         $totals = $ydb->get_row( "SELECT COUNT(keyword) as count, SUM(clicks) as sum FROM `$table_url` WHERE 1=1 $where" );\r
621         $return = array( 'total_links' => $totals->count, 'total_clicks' => $totals->sum );\r
622         \r
623         return yourls_apply_filter( 'get_db_stats', $return, $where );\r
624 }\r
625 \r
626 // Return API result. Dies after this\r
627 function yourls_api_output( $mode, $return ) {\r
628         if( isset( $return['simple'] ) ) {\r
629                 $simple = $return['simple'];\r
630                 unset( $return['simple'] );\r
631         }\r
632         \r
633         yourls_do_action( 'pre_api_output', $mode, $return );\r
634         \r
635         switch ( $mode ) {\r
636                 case 'jsonp':\r
637                         header( 'Content-type: application/javascript' );\r
638                         echo $return['callback'] . '(' . json_encode( $return ) . ')';\r
639                         break;\r
640     \r
641                 case 'json':\r
642                         header( 'Content-type: application/json' );\r
643                         echo json_encode( $return );\r
644                         break;\r
645                 \r
646                 case 'xml':\r
647                         header( 'Content-type: application/xml' );\r
648                         echo yourls_xml_encode( $return );\r
649                         break;\r
650                         \r
651                 case 'simple':\r
652                 default:\r
653                         if( isset( $simple ) )\r
654                                 echo $simple;\r
655                         break;\r
656         }\r
657 \r
658         yourls_do_action( 'api_output', $mode, $return );\r
659         \r
660         die();\r
661 }\r
662 \r
663 // Get number of SQL queries performed\r
664 function yourls_get_num_queries() {\r
665         global $ydb;\r
666 \r
667         return yourls_apply_filter( 'get_num_queries', $ydb->num_queries );\r
668 }\r
669 \r
670 // Returns a sanitized a user agent string. Given what I found on http://www.user-agents.org/ it should be OK.\r
671 function yourls_get_user_agent() {\r
672         if ( !isset( $_SERVER['HTTP_USER_AGENT'] ) )\r
673                 return '-';\r
674         \r
675         $ua = strip_tags( html_entity_decode( $_SERVER['HTTP_USER_AGENT'] ));\r
676         $ua = preg_replace('![^0-9a-zA-Z\':., /{}\(\)\[\]\+@&\!\?;_\-=~\*\#]!', '', $ua );\r
677                 \r
678         return yourls_apply_filter( 'get_user_agent', substr( $ua, 0, 254 ) );\r
679 }\r
680 \r
681 // Redirect to another page\r
682 function yourls_redirect( $location, $code = 301 ) {\r
683         yourls_do_action( 'pre_redirect', $location, $code );\r
684         $location = yourls_apply_filter( 'redirect', $location, $code );\r
685         // Redirect, either properly if possible, or via Javascript otherwise\r
686         if( !headers_sent() ) {\r
687                 yourls_status_header( $code );\r
688                 header( "Location: $location" );\r
689         } else {\r
690                 yourls_redirect_javascript( $location );\r
691         }\r
692         die();\r
693 }\r
694 \r
695 // Set HTTP status header\r
696 function yourls_status_header( $code = 200 ) {\r
697         if( headers_sent() )\r
698                 return;\r
699                 \r
700         $protocol = $_SERVER["SERVER_PROTOCOL"];\r
701         if ( 'HTTP/1.1' != $protocol && 'HTTP/1.0' != $protocol )\r
702                 $protocol = 'HTTP/1.0';\r
703 \r
704         $code = intval( $code );\r
705         $desc = yourls_get_HTTP_status( $code );\r
706 \r
707         @header ("$protocol $code $desc"); // This causes problems on IIS and some FastCGI setups\r
708         yourls_do_action( 'status_header', $code );\r
709 }\r
710 \r
711 // 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
712 function yourls_redirect_javascript( $location, $dontwait = true ) {\r
713         yourls_do_action( 'pre_redirect_javascript', $location, $dontwait );\r
714         $location = yourls_apply_filter( 'redirect_javascript', $location, $dontwait );\r
715         if( $dontwait ) {\r
716                 echo <<<REDIR\r
717                 <script type="text/javascript">\r
718                 window.location="$location";\r
719                 </script>\r
720                 <small>(if you are not redirected after 10 seconds, please <a href="$location">click here</a>)</small>\r
721 REDIR;\r
722         } else {\r
723                 echo <<<MANUAL\r
724                 <p>Please <a href="$location">click here</a></p>\r
725 MANUAL;\r
726         }\r
727         yourls_do_action( 'post_redirect_javascript', $location );\r
728 }\r
729 \r
730 // Return a HTTP status code\r
731 function yourls_get_HTTP_status( $code ) {\r
732         $code = intval( $code );\r
733         $headers_desc = array(\r
734                 100 => 'Continue',\r
735                 101 => 'Switching Protocols',\r
736                 102 => 'Processing',\r
737 \r
738                 200 => 'OK',\r
739                 201 => 'Created',\r
740                 202 => 'Accepted',\r
741                 203 => 'Non-Authoritative Information',\r
742                 204 => 'No Content',\r
743                 205 => 'Reset Content',\r
744                 206 => 'Partial Content',\r
745                 207 => 'Multi-Status',\r
746                 226 => 'IM Used',\r
747 \r
748                 300 => 'Multiple Choices',\r
749                 301 => 'Moved Permanently',\r
750                 302 => 'Found',\r
751                 303 => 'See Other',\r
752                 304 => 'Not Modified',\r
753                 305 => 'Use Proxy',\r
754                 306 => 'Reserved',\r
755                 307 => 'Temporary Redirect',\r
756 \r
757                 400 => 'Bad Request',\r
758                 401 => 'Unauthorized',\r
759                 402 => 'Payment Required',\r
760                 403 => 'Forbidden',\r
761                 404 => 'Not Found',\r
762                 405 => 'Method Not Allowed',\r
763                 406 => 'Not Acceptable',\r
764                 407 => 'Proxy Authentication Required',\r
765                 408 => 'Request Timeout',\r
766                 409 => 'Conflict',\r
767                 410 => 'Gone',\r
768                 411 => 'Length Required',\r
769                 412 => 'Precondition Failed',\r
770                 413 => 'Request Entity Too Large',\r
771                 414 => 'Request-URI Too Long',\r
772                 415 => 'Unsupported Media Type',\r
773                 416 => 'Requested Range Not Satisfiable',\r
774                 417 => 'Expectation Failed',\r
775                 422 => 'Unprocessable Entity',\r
776                 423 => 'Locked',\r
777                 424 => 'Failed Dependency',\r
778                 426 => 'Upgrade Required',\r
779 \r
780                 500 => 'Internal Server Error',\r
781                 501 => 'Not Implemented',\r
782                 502 => 'Bad Gateway',\r
783                 503 => 'Service Unavailable',\r
784                 504 => 'Gateway Timeout',\r
785                 505 => 'HTTP Version Not Supported',\r
786                 506 => 'Variant Also Negotiates',\r
787                 507 => 'Insufficient Storage',\r
788                 510 => 'Not Extended'\r
789         );\r
790 \r
791         if ( isset( $headers_desc[$code] ) )\r
792                 return $headers_desc[$code];\r
793         else\r
794                 return '';\r
795 }\r
796 \r
797 \r
798 // Log a redirect (for stats)\r
799 function yourls_log_redirect( $keyword ) {\r
800         // Allow plugins to short-circuit the whole function\r
801         $pre = yourls_apply_filter( 'shunt_log_redirect', false, $keyword );\r
802         if ( false !== $pre )\r
803                 return $pre;\r
804 \r
805         if ( !yourls_do_log_redirect() )\r
806                 return true;\r
807 \r
808         global $ydb;\r
809         $table = YOURLS_DB_TABLE_LOG;\r
810         \r
811         $keyword = yourls_sanitize_string( $keyword );\r
812         $referrer = ( isset( $_SERVER['HTTP_REFERER'] ) ? yourls_sanitize_url( $_SERVER['HTTP_REFERER'] ) : 'direct' );\r
813         $ua = yourls_get_user_agent();\r
814         $ip = yourls_get_IP();\r
815         $location = yourls_geo_ip_to_countrycode( $ip );\r
816         \r
817         return $ydb->query( "INSERT INTO `$table` (click_time, shorturl, referrer, user_agent, ip_address, country_code) VALUES (NOW(), '$keyword', '$referrer', '$ua', '$ip', '$location')" );\r
818 }\r
819 \r
820 // Check if we want to not log redirects (for stats)\r
821 function yourls_do_log_redirect() {\r
822         return ( !defined( 'YOURLS_NOSTATS' ) || YOURLS_NOSTATS != true );\r
823 }\r
824 \r
825 // Converts an IP to a 2 letter country code, using GeoIP database if available in includes/geo/\r
826 function yourls_geo_ip_to_countrycode( $ip = '', $default = '' ) {\r
827         // Allow plugins to short-circuit the Geo IP API\r
828         $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
829         if ( false !== $location )\r
830                 return $location;\r
831 \r
832         if ( !file_exists( YOURLS_INC.'/geo/GeoIP.dat') || !file_exists( YOURLS_INC.'/geo/geoip.inc') )\r
833                 return $default;\r
834 \r
835         if ( $ip == '' )\r
836                 $ip = yourls_get_IP();\r
837         \r
838         require_once( YOURLS_INC.'/geo/geoip.inc') ;\r
839         $gi = geoip_open( YOURLS_INC.'/geo/GeoIP.dat', GEOIP_STANDARD);\r
840         $location = geoip_country_code_by_addr($gi, $ip);\r
841         geoip_close($gi);\r
842 \r
843         return yourls_apply_filter( 'geo_ip_to_countrycode', $location, $ip, $default );\r
844 }\r
845 \r
846 // Converts a 2 letter country code to long name (ie AU -> Australia)\r
847 function yourls_geo_countrycode_to_countryname( $code ) {\r
848         // Allow plugins to short-circuit the Geo IP API\r
849         $country = yourls_apply_filter( 'shunt_geo_countrycode_to_countryname', false, $code );\r
850         if ( false !== $country )\r
851                 return $country;\r
852 \r
853         // Load the Geo class if not already done\r
854         if( !class_exists( 'GeoIP' ) ) {\r
855                 $temp = yourls_geo_ip_to_countrycode( '127.0.0.1' );\r
856         }\r
857         \r
858         if( class_exists( 'GeoIP' ) ) {\r
859                 $geo  = new GeoIP;\r
860                 $id   = $geo->GEOIP_COUNTRY_CODE_TO_NUMBER[ $code ];\r
861                 $long = $geo->GEOIP_COUNTRY_NAMES[ $id ];\r
862                 return $long;\r
863         } else {\r
864                 return false;\r
865         }\r
866 }\r
867 \r
868 // Return flag URL from 2 letter country code\r
869 function yourls_geo_get_flag( $code ) {\r
870         if( file_exists( YOURLS_INC.'/geo/flags/flag_'.strtolower($code).'.gif' ) ) {\r
871                 $img = yourls_match_current_protocol( YOURLS_SITE.'/includes/geo/flags/flag_'.( strtolower( $code ) ).'.gif' );\r
872         } else {\r
873                 $img = false;\r
874         }\r
875         return yourls_apply_filter( 'geo_get_flag', $img, $code );\r
876 }\r
877 \r
878 \r
879 // Check if an upgrade is needed\r
880 function yourls_upgrade_is_needed() {\r
881         // check YOURLS_DB_VERSION exist && match values stored in YOURLS_DB_TABLE_OPTIONS\r
882         list( $currentver, $currentsql ) = yourls_get_current_version_from_sql();\r
883         if( $currentsql < YOURLS_DB_VERSION )\r
884                 return true;\r
885                 \r
886         return false;\r
887 }\r
888 \r
889 // Get current version & db version as stored in the options DB. Prior to 1.4 there's no option table.\r
890 function yourls_get_current_version_from_sql() {\r
891         $currentver = yourls_get_option( 'version' );\r
892         $currentsql = yourls_get_option( 'db_version' );\r
893 \r
894         // Values if version is 1.3\r
895         if( !$currentver )\r
896                 $currentver = '1.3';\r
897         if( !$currentsql )\r
898                 $currentsql = '100';\r
899                 \r
900         return array( $currentver, $currentsql);\r
901 }\r
902 \r
903 // Read an option from DB (or from cache if available). Return value or $default if not found\r
904 function yourls_get_option( $option_name, $default = false ) {\r
905         global $ydb;\r
906         \r
907         // Allow plugins to short-circuit options\r
908         $pre = yourls_apply_filter( 'shunt_option_'.$option_name, false );\r
909         if ( false !== $pre )\r
910                 return $pre;\r
911 \r
912         // If option not cached already, get its value from the DB\r
913         if ( !isset( $ydb->option[$option_name] ) ) {\r
914                 $table = YOURLS_DB_TABLE_OPTIONS;\r
915                 $option_name = yourls_escape( $option_name );\r
916                 $row = $ydb->get_row( "SELECT `option_value` FROM `$table` WHERE `option_name` = '$option_name' LIMIT 1" );\r
917                 if ( is_object( $row) ) { // Has to be get_row instead of get_var because of funkiness with 0, false, null values\r
918                         $value = $row->option_value;\r
919                 } else { // option does not exist, so we must cache its non-existence\r
920                         $value = $default;\r
921                 }\r
922                 $ydb->option[ $option_name ] = yourls_maybe_unserialize( $value );\r
923         }\r
924 \r
925         return yourls_apply_filter( 'get_option_'.$option_name, $ydb->option[$option_name] );\r
926 }\r
927 \r
928 // Read all options from DB at once\r
929 function yourls_get_all_options() {\r
930         global $ydb;\r
931 \r
932         // Allow plugins to short-circuit all options. (Note: regular plugins are loaded after all options)\r
933         $pre = yourls_apply_filter( 'shunt_all_options', false );\r
934         if ( false !== $pre )\r
935                 return $pre;\r
936 \r
937         $table = YOURLS_DB_TABLE_OPTIONS;\r
938         \r
939         $allopt = $ydb->get_results( "SELECT `option_name`, `option_value` FROM `$table` WHERE 1=1" );\r
940         \r
941         foreach( (array)$allopt as $option ) {\r
942                 $ydb->option[$option->option_name] = yourls_maybe_unserialize( $option->option_value );\r
943         }\r
944         \r
945         $ydb->option = yourls_apply_filter( 'get_all_options', $ydb->option );\r
946 }\r
947 \r
948 // Update (add if doesn't exist) an option to DB\r
949 function yourls_update_option( $option_name, $newvalue ) {\r
950         global $ydb;\r
951         $table = YOURLS_DB_TABLE_OPTIONS;\r
952 \r
953         $safe_option_name = yourls_escape( $option_name );\r
954 \r
955         $oldvalue = yourls_get_option( $safe_option_name );\r
956 \r
957         // If the new and old values are the same, no need to update.\r
958         if ( $newvalue === $oldvalue )\r
959                 return false;\r
960 \r
961         if ( false === $oldvalue ) {\r
962                 yourls_add_option( $option_name, $newvalue );\r
963                 return true;\r
964         }\r
965 \r
966         $_newvalue = yourls_escape( yourls_maybe_serialize( $newvalue ) );\r
967         \r
968         yourls_do_action( 'update_option', $option_name, $oldvalue, $newvalue );\r
969 \r
970         $ydb->query( "UPDATE `$table` SET `option_value` = '$_newvalue' WHERE `option_name` = '$option_name'" );\r
971 \r
972         if ( $ydb->rows_affected == 1 ) {\r
973                 $ydb->option[ $option_name ] = $newvalue;\r
974                 return true;\r
975         }\r
976         return false;\r
977 }\r
978 \r
979 // Add an option to the DB\r
980 function yourls_add_option( $name, $value = '' ) {\r
981         global $ydb;\r
982         $table = YOURLS_DB_TABLE_OPTIONS;\r
983         $safe_name = yourls_escape( $name );\r
984 \r
985         // Make sure the option doesn't already exist\r
986         if ( false !== yourls_get_option( $safe_name ) )\r
987                 return;\r
988 \r
989         $_value = yourls_escape( yourls_maybe_serialize( $value ) );\r
990 \r
991         yourls_do_action( 'add_option', $safe_name, $_value );\r
992 \r
993         $ydb->query( "INSERT INTO `$table` (`option_name`, `option_value`) VALUES ('$name', '$_value')" );\r
994         $ydb->option[ $name ] = $value;\r
995         return;\r
996 }\r
997 \r
998 \r
999 // Delete an option from the DB\r
1000 function yourls_delete_option( $name ) {\r
1001         global $ydb;\r
1002         $table = YOURLS_DB_TABLE_OPTIONS;\r
1003         $name = yourls_escape( $name );\r
1004 \r
1005         // Get the ID, if no ID then return\r
1006         $option = $ydb->get_row( "SELECT option_id FROM `$table` WHERE `option_name` = '$name'" );\r
1007         if ( is_null( $option ) || !$option->option_id )\r
1008                 return false;\r
1009                 \r
1010         yourls_do_action( 'delete_option', $option_name );\r
1011                 \r
1012         $ydb->query( "DELETE FROM `$table` WHERE `option_name` = '$name'" );\r
1013         return true;\r
1014 }\r
1015 \r
1016 \r
1017 \r
1018 // Serialize data if needed. Stolen from WordPress\r
1019 function yourls_maybe_serialize( $data ) {\r
1020         if ( is_array( $data ) || is_object( $data ) )\r
1021                 return serialize( $data );\r
1022 \r
1023         if ( yourls_is_serialized( $data ) )\r
1024                 return serialize( $data );\r
1025 \r
1026         return $data;\r
1027 }\r
1028 \r
1029 // Check value to find if it was serialized. Stolen from WordPress\r
1030 function yourls_is_serialized( $data ) {\r
1031         // if it isn't a string, it isn't serialized\r
1032         if ( !is_string( $data ) )\r
1033                 return false;\r
1034         $data = trim( $data );\r
1035         if ( 'N;' == $data )\r
1036                 return true;\r
1037         if ( !preg_match( '/^([adObis]):/', $data, $badions ) )\r
1038                 return false;\r
1039         switch ( $badions[1] ) {\r
1040                 case 'a' :\r
1041                 case 'O' :\r
1042                 case 's' :\r
1043                         if ( preg_match( "/^{$badions[1]}:[0-9]+:.*[;}]\$/s", $data ) )\r
1044                                 return true;\r
1045                         break;\r
1046                 case 'b' :\r
1047                 case 'i' :\r
1048                 case 'd' :\r
1049                         if ( preg_match( "/^{$badions[1]}:[0-9.E-]+;\$/", $data ) )\r
1050                                 return true;\r
1051                         break;\r
1052         }\r
1053         return false;\r
1054 }\r
1055 \r
1056 // Unserialize value only if it was serialized. Stolen from WP\r
1057 function yourls_maybe_unserialize( $original ) {\r
1058         if ( yourls_is_serialized( $original ) ) // don't attempt to unserialize data that wasn't serialized going in\r
1059                 return @unserialize( $original );\r
1060         return $original;\r
1061 }\r
1062 \r
1063 // Determine if the current page is private\r
1064 function yourls_is_private() {\r
1065         $private = false;\r
1066 \r
1067         if ( defined('YOURLS_PRIVATE') && YOURLS_PRIVATE == true ) {\r
1068 \r
1069                 // Allow overruling for particular pages:\r
1070                 \r
1071                 // API\r
1072                 if( yourls_is_API() ) {\r
1073                         if( !defined('YOURLS_PRIVATE_API') || YOURLS_PRIVATE_API != false )\r
1074                                 $private = true;                \r
1075 \r
1076                 // Infos\r
1077                 } elseif( yourls_is_infos() ) {\r
1078                         if( !defined('YOURLS_PRIVATE_INFOS') || YOURLS_PRIVATE_INFOS !== false )\r
1079                                 $private = true;\r
1080                 \r
1081                 // Others\r
1082                 } else {\r
1083                         $private = true;\r
1084                 }\r
1085                 \r
1086         }\r
1087                         \r
1088         return yourls_apply_filter( 'is_private', $private );\r
1089 }\r
1090 \r
1091 // Show login form if required\r
1092 function yourls_maybe_require_auth() {\r
1093         if( yourls_is_private() )\r
1094                 require_once( YOURLS_INC.'/auth.php' );\r
1095 }\r
1096 \r
1097 // Allow several short URLs for the same long URL ?\r
1098 function yourls_allow_duplicate_longurls() {\r
1099         // special treatment if API to check for WordPress plugin requests\r
1100         if( yourls_is_API() ) {\r
1101                 if ( isset($_REQUEST['source']) && $_REQUEST['source'] == 'plugin' ) \r
1102                         return false;\r
1103         }\r
1104         return ( defined( 'YOURLS_UNIQUE_URLS' ) && YOURLS_UNIQUE_URLS == false );\r
1105 }\r
1106 \r
1107 // Return list of all shorturls associated to the same long URL. Returns NULL or array of keywords.\r
1108 function yourls_get_duplicate_keywords( $longurl ) {\r
1109         if( !yourls_allow_duplicate_longurls() )\r
1110                 return NULL;\r
1111         \r
1112         global $ydb;\r
1113         $longurl = yourls_escape( yourls_sanitize_url($longurl) );\r
1114         $table = YOURLS_DB_TABLE_URL;\r
1115         \r
1116         $return = $ydb->get_col( "SELECT `keyword` FROM `$table` WHERE `url` = '$longurl'" );\r
1117         return yourls_apply_filter( 'get_duplicate_keywords', $return, $longurl );\r
1118 }\r
1119 \r
1120 // Check if an IP shortens URL too fast to prevent DB flood. Return true, or die.\r
1121 function yourls_check_IP_flood( $ip = '' ) {\r
1122 \r
1123         // Allow plugins to short-circuit the whole function\r
1124         $pre = yourls_apply_filter( 'shunt_check_IP_flood', false, $ip );\r
1125         if ( false !== $pre )\r
1126                 return $pre;\r
1127 \r
1128         yourls_do_action( 'pre_check_ip_flood', $ip ); // at this point $ip can be '', check it if your plugin hooks in here\r
1129 \r
1130         if(\r
1131                 ( defined('YOURLS_FLOOD_DELAY_SECONDS') && YOURLS_FLOOD_DELAY_SECONDS === 0 ) ||\r
1132                 !defined('YOURLS_FLOOD_DELAY_SECONDS')\r
1133         )\r
1134                 return true;\r
1135 \r
1136         $ip = ( $ip ? yourls_sanitize_ip( $ip ) : yourls_get_IP() );\r
1137 \r
1138         // Don't throttle whitelist IPs\r
1139         if( defined( 'YOURLS_FLOOD_IP_WHITELIST' ) && YOURLS_FLOOD_IP_WHITELIST ) {\r
1140                 $whitelist_ips = explode( ',', YOURLS_FLOOD_IP_WHITELIST );\r
1141                 foreach( (array)$whitelist_ips as $whitelist_ip ) {\r
1142                         $whitelist_ip = trim( $whitelist_ip );\r
1143                         if ( $whitelist_ip == $ip )\r
1144                                 return true;\r
1145                 }\r
1146         }\r
1147         \r
1148         // Don't throttle logged in users\r
1149         if( yourls_is_private() ) {\r
1150                  if( yourls_is_valid_user() === true )\r
1151                         return true;\r
1152         }\r
1153         \r
1154         yourls_do_action( 'check_ip_flood', $ip );\r
1155         \r
1156         global $ydb;\r
1157         $table = YOURLS_DB_TABLE_URL;\r
1158         \r
1159         $lasttime = $ydb->get_var( "SELECT `timestamp` FROM $table WHERE `ip` = '$ip' ORDER BY `timestamp` DESC LIMIT 1" );\r
1160         if( $lasttime ) {\r
1161                 $now = date( 'U' );\r
1162                 $then = date( 'U', strtotime( $lasttime ) );\r
1163                 if( ( $now - $then ) <= YOURLS_FLOOD_DELAY_SECONDS ) {\r
1164                         // Flood!\r
1165                         yourls_do_action( 'ip_flood', $ip, $now - $then );\r
1166                         yourls_die( 'Too many URLs added too fast. Slow down please.', 'Forbidden', 403 );\r
1167                 }\r
1168         }\r
1169         \r
1170         return true;\r
1171 }\r
1172 \r
1173 // Check if YOURLS is installed\r
1174 function yourls_is_installed() {\r
1175         static $is_installed = false;\r
1176         if ( $is_installed === false ) {\r
1177                 $check_14 = $check_13 = false;\r
1178                 global $ydb;\r
1179                 if( defined('YOURLS_DB_TABLE_NEXTDEC') )\r
1180                         $check_13 = $ydb->get_var('SELECT `next_id` FROM '.YOURLS_DB_TABLE_NEXTDEC);\r
1181                 $check_14 = yourls_get_option( 'version' );\r
1182                 $is_installed = $check_13 || $check_14;\r
1183         }\r
1184         return yourls_apply_filter( 'is_installed', $is_installed );\r
1185 }\r
1186 \r
1187 // Generate random string of (int)$length length and type $type (see function for details)\r
1188 function yourls_rnd_string ( $length = 5, $type = 0, $charlist = '' ) {\r
1189         $str = '';\r
1190         $length = intval( $length );\r
1191 \r
1192         // define possible characters\r
1193         switch ( $type ) {\r
1194 \r
1195                 // custom char list, or comply to charset as defined in config\r
1196                 case '0':\r
1197                         $possible = $charlist ? $charlist : yourls_get_shorturl_charset() ;\r
1198                         break;\r
1199         \r
1200                 // no vowels to make no offending word, no 0/1/o/l to avoid confusion between letters & digits. Perfect for passwords.\r
1201                 case '1':\r
1202                         $possible = "23456789bcdfghjkmnpqrstvwxyz";\r
1203                         break;\r
1204                 \r
1205                 // Same, with lower + upper\r
1206                 case '2':\r
1207                         $possible = "23456789bcdfghjkmnpqrstvwxyzBCDFGHJKMNPQRSTVWXYZ";\r
1208                         break;\r
1209                 \r
1210                 // all letters, lowercase\r
1211                 case '3':\r
1212                         $possible = "abcdefghijklmnopqrstuvwxyz";\r
1213                         break;\r
1214                 \r
1215                 // all letters, lowercase + uppercase\r
1216                 case '4':\r
1217                         $possible = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";\r
1218                         break;\r
1219                 \r
1220                 // all digits & letters lowercase \r
1221                 case '5':\r
1222                         $possible = "0123456789abcdefghijklmnopqrstuvwxyz";\r
1223                         break;\r
1224                 \r
1225                 // all digits & letters lowercase + uppercase\r
1226                 case '6':\r
1227                         $possible = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";\r
1228                         break;\r
1229                 \r
1230         }\r
1231 \r
1232         $i = 0;\r
1233         while ($i < $length) {\r
1234                 $str .= substr($possible, mt_rand(0, strlen($possible)-1), 1);\r
1235                 $i++;\r
1236         }\r
1237         \r
1238         return yourls_apply_filter( 'rnd_string', $str, $length, $type, $charlist );\r
1239 }\r
1240 \r
1241 // Return salted string\r
1242 function yourls_salt( $string ) {\r
1243         $salt = defined('YOURLS_COOKIEKEY') ? YOURLS_COOKIEKEY : md5(__FILE__) ;\r
1244         return yourls_apply_filter( 'yourls_salt', md5 ($string . $salt), $string );\r
1245 }\r
1246 \r
1247 // Add a query var to a URL and return URL. Completely stolen from WP.\r
1248 // Works with one of these parameter patterns:\r
1249 //     array( 'var' => 'value' )\r
1250 //     array( 'var' => 'value' ), $url\r
1251 //     'var', 'value'\r
1252 //     'var', 'value', $url\r
1253 // If $url ommited, uses $_SERVER['REQUEST_URI']\r
1254 function yourls_add_query_arg() {\r
1255         $ret = '';\r
1256         if ( is_array( func_get_arg(0) ) ) {\r
1257                 if ( @func_num_args() < 2 || false === @func_get_arg( 1 ) )\r
1258                         $uri = $_SERVER['REQUEST_URI'];\r
1259                 else\r
1260                         $uri = @func_get_arg( 1 );\r
1261         } else {\r
1262                 if ( @func_num_args() < 3 || false === @func_get_arg( 2 ) )\r
1263                         $uri = $_SERVER['REQUEST_URI'];\r
1264                 else\r
1265                         $uri = @func_get_arg( 2 );\r
1266         }\r
1267         \r
1268         $uri = str_replace( '&amp;', '&', $uri );\r
1269 \r
1270         \r
1271         if ( $frag = strstr( $uri, '#' ) )\r
1272                 $uri = substr( $uri, 0, -strlen( $frag ) );\r
1273         else\r
1274                 $frag = '';\r
1275 \r
1276         if ( preg_match( '|^https?://|i', $uri, $matches ) ) {\r
1277                 $protocol = $matches[0];\r
1278                 $uri = substr( $uri, strlen( $protocol ) );\r
1279         } else {\r
1280                 $protocol = '';\r
1281         }\r
1282 \r
1283         if ( strpos( $uri, '?' ) !== false ) {\r
1284                 $parts = explode( '?', $uri, 2 );\r
1285                 if ( 1 == count( $parts ) ) {\r
1286                         $base = '?';\r
1287                         $query = $parts[0];\r
1288                 } else {\r
1289                         $base = $parts[0] . '?';\r
1290                         $query = $parts[1];\r
1291                 }\r
1292         } elseif ( !empty( $protocol ) || strpos( $uri, '=' ) === false ) {\r
1293                 $base = $uri . '?';\r
1294                 $query = '';\r
1295         } else {\r
1296                 $base = '';\r
1297                 $query = $uri;\r
1298         }\r
1299 \r
1300         parse_str( $query, $qs );\r
1301         $qs = yourls_urlencode_deep( $qs ); // this re-URL-encodes things that were already in the query string\r
1302         if ( is_array( func_get_arg( 0 ) ) ) {\r
1303                 $kayvees = func_get_arg( 0 );\r
1304                 $qs = array_merge( $qs, $kayvees );\r
1305         } else {\r
1306                 $qs[func_get_arg( 0 )] = func_get_arg( 1 );\r
1307         }\r
1308 \r
1309         foreach ( (array) $qs as $k => $v ) {\r
1310                 if ( $v === false )\r
1311                         unset( $qs[$k] );\r
1312         }\r
1313 \r
1314         $ret = http_build_query( $qs );\r
1315         $ret = trim( $ret, '?' );\r
1316         $ret = preg_replace( '#=(&|$)#', '$1', $ret );\r
1317         $ret = $protocol . $base . $ret . $frag;\r
1318         $ret = rtrim( $ret, '?' );\r
1319         return $ret;\r
1320 }\r
1321 \r
1322 // Navigates through an array and encodes the values to be used in a URL. Stolen from WP, used in yourls_add_query_arg()\r
1323 function yourls_urlencode_deep( $value ) {\r
1324         $value = is_array( $value ) ? array_map( 'yourls_urlencode_deep', $value ) : urlencode( $value );\r
1325         return $value;\r
1326 }\r
1327 \r
1328 // Remove arg from query. Opposite of yourls_add_query_arg. Stolen from WP.\r
1329 function yourls_remove_query_arg( $key, $query = false ) {\r
1330         if ( is_array( $key ) ) { // removing multiple keys\r
1331                 foreach ( $key as $k )\r
1332                         $query = yourls_add_query_arg( $k, false, $query );\r
1333                 return $query;\r
1334         }\r
1335         return yourls_add_query_arg( $key, false, $query );\r
1336 }\r
1337 \r
1338 // Return a time-dependent string for nonce creation\r
1339 function yourls_tick() {\r
1340         return ceil( time() / YOURLS_NONCE_LIFE );\r
1341 }\r
1342 \r
1343 // Create a time limited, action limited and user limited token\r
1344 function yourls_create_nonce( $action, $user = false ) {\r
1345         if( false == $user )\r
1346                 $user = defined( 'YOURLS_USER' ) ? YOURLS_USER : '-1';\r
1347         $tick = yourls_tick();\r
1348         return substr( yourls_salt($tick . $action . $user), 0, 10 );\r
1349 }\r
1350 \r
1351 // Create a nonce field for inclusion into a form\r
1352 function yourls_nonce_field( $action, $name = 'nonce', $user = false, $echo = true ) {\r
1353         $field = '<input type="hidden" id="'.$name.'" name="'.$name.'" value="'.yourls_create_nonce( $action, $user ).'" />';\r
1354         if( $echo )\r
1355                 echo $field."\n";\r
1356         return $field;\r
1357 }\r
1358 \r
1359 // Add a nonce to a URL. If URL omitted, adds nonce to current URL\r
1360 function yourls_nonce_url( $action, $url = false, $name = 'nonce', $user = false ) {\r
1361         $nonce = yourls_create_nonce( $action, $user );\r
1362         return yourls_add_query_arg( $name, $nonce, $url );\r
1363 }\r
1364 \r
1365 // Check validity of a nonce (ie time span, user and action match).\r
1366 // Returns true if valid, dies otherwise (yourls_die() or die($return) if defined)\r
1367 // if $nonce is false or unspecified, it will use $_REQUEST['nonce']\r
1368 function yourls_verify_nonce( $action, $nonce = false, $user = false, $return = '' ) {\r
1369         // get user\r
1370         if( false == $user )\r
1371                 $user = defined( 'YOURLS_USER' ) ? YOURLS_USER : '-1';\r
1372                 \r
1373         // get current nonce value\r
1374         if( false == $nonce && isset( $_REQUEST['nonce'] ) )\r
1375                 $nonce = $_REQUEST['nonce'];\r
1376 \r
1377         // what nonce should be\r
1378         $valid = yourls_create_nonce( $action, $user );\r
1379         \r
1380         if( $nonce == $valid ) {\r
1381                 return true;\r
1382         } else {\r
1383                 if( $return )\r
1384                         die( $return );\r
1385                 yourls_die( 'Unauthorized action or expired link', 'Error', 403 );\r
1386         }\r
1387 }\r
1388 \r
1389 // Converts keyword into short link (prepend with YOURLS base URL)\r
1390 function yourls_link( $keyword = '' ) {\r
1391         $link = YOURLS_SITE . '/' . yourls_sanitize_keyword( $keyword );\r
1392         return yourls_apply_filter( 'yourls_link', $link, $keyword );\r
1393 }\r
1394 \r
1395 // Converts keyword into stat link (prepend with YOURLS base URL, append +)\r
1396 function yourls_statlink( $keyword = '' ) {\r
1397         $link = YOURLS_SITE . '/' . yourls_sanitize_keyword( $keyword ) . '+';\r
1398         return yourls_apply_filter( 'yourls_statlink', $link, $keyword );\r
1399 }\r
1400 \r
1401 // Check if we're in API mode. Returns bool\r
1402 function yourls_is_API() {\r
1403         if ( defined( 'YOURLS_API' ) && YOURLS_API == true )\r
1404                 return true;\r
1405         return false;\r
1406 }\r
1407 \r
1408 // Check if we're in Ajax mode. Returns bool\r
1409 function yourls_is_Ajax() {\r
1410         if ( defined( 'YOURLS_AJAX' ) && YOURLS_AJAX == true )\r
1411                 return true;\r
1412         return false;\r
1413 }\r
1414 \r
1415 // Check if we're in GO mode (yourls-go.php). Returns bool\r
1416 function yourls_is_GO() {\r
1417         if ( defined( 'YOURLS_GO' ) && YOURLS_GO == true )\r
1418                 return true;\r
1419         return false;\r
1420 }\r
1421 \r
1422 // Check if we're displaying stats infos (yourls-infos.php). Returns bool\r
1423 function yourls_is_infos() {\r
1424         if ( defined( 'YOURLS_INFOS' ) && YOURLS_INFOS == true )\r
1425                 return true;\r
1426         return false;\r
1427 }\r
1428 \r
1429 // Check if we'll need interface display function (ie not API or redirection)\r
1430 function yourls_has_interface() {\r
1431         if( yourls_is_API() or yourls_is_GO() )\r
1432                 return false;\r
1433         return true;\r
1434 }\r
1435 \r
1436 // Check if we're in the admin area. Returns bool\r
1437 function yourls_is_admin() {\r
1438         if ( defined( 'YOURLS_ADMIN' ) && YOURLS_ADMIN == true )\r
1439                 return true;\r
1440         return false;\r
1441 }\r
1442 \r
1443 // Check if the server seems to be running on Windows. Not exactly sure how reliable this is.\r
1444 function yourls_is_windows() {\r
1445         return defined( 'DIRECTORY_SEPARATOR' ) && DIRECTORY_SEPARATOR == '\\';\r
1446 }\r
1447 \r
1448 // Check if SSL is required. Returns bool.\r
1449 function yourls_needs_ssl() {\r
1450         if ( defined('YOURLS_ADMIN_SSL') && YOURLS_ADMIN_SSL == true )\r
1451                 return true;\r
1452         return false;\r
1453 }\r
1454 \r
1455 // Return admin link, with SSL preference if applicable.\r
1456 function yourls_admin_url( $page = '' ) {\r
1457         $admin = YOURLS_SITE . '/admin/' . $page;\r
1458         if( yourls_is_ssl() or yourls_needs_ssl() )\r
1459                 $admin = str_replace('http://', 'https://', $admin);\r
1460         return yourls_apply_filter( 'admin_url', $admin, $page );\r
1461 }\r
1462 \r
1463 // Return YOURLS_SITE or URL under YOURLS setup, with SSL preference\r
1464 function yourls_site_url( $echo = true, $url = '' ) {\r
1465         if( !$url ) {\r
1466                 $url = YOURLS_SITE;\r
1467         } else {\r
1468                 // make $url relative to YOURLS base in case a full URL has been provided. Will break if user mixes https/http.\r
1469                 $url = str_replace( YOURLS_SITE, '', $url );\r
1470                 $url = YOURLS_SITE . '/' . ltrim( $url, '/' );\r
1471         }\r
1472         // Do not enforce (checking yourls_need_ssl() ) but check current usage so it won't force SSL on non-admin pages\r
1473         if( yourls_is_ssl() )\r
1474                 $url = str_replace( 'http://', 'https://', $url );\r
1475         $url = yourls_apply_filter( 'site_url', $url );\r
1476         if( $echo )\r
1477                 echo $url;\r
1478         return $url;\r
1479 }\r
1480 \r
1481 // Check if SSL is used, returns bool. Stolen from WP.\r
1482 function yourls_is_ssl() {\r
1483         $is_ssl = false;\r
1484         if ( isset( $_SERVER['HTTPS'] ) ) {\r
1485                 if ( 'on' == strtolower( $_SERVER['HTTPS'] ) )\r
1486                         $is_ssl = true;\r
1487                 if ( '1' == $_SERVER['HTTPS'] )\r
1488                         $is_ssl = true;\r
1489         } elseif ( isset( $_SERVER['SERVER_PORT'] ) && ( '443' == $_SERVER['SERVER_PORT'] ) ) {\r
1490                 $is_ssl = true;\r
1491         }\r
1492         return yourls_apply_filter( 'is_ssl', $is_ssl );\r
1493 }\r
1494 \r
1495 \r
1496 // Get a remote page <title>, return a string (either title or url)\r
1497 function yourls_get_remote_title( $url ) {\r
1498         // Allow plugins to short-circuit the whole function\r
1499         $pre = yourls_apply_filter( 'shunt_get_remote_title', false, $url );\r
1500         if ( false !== $pre )\r
1501                 return $pre;\r
1502 \r
1503         require_once( YOURLS_INC.'/functions-http.php' );\r
1504 \r
1505         $url = yourls_sanitize_url( $url );\r
1506 \r
1507         $title = $charset = false;\r
1508         \r
1509         $content = yourls_get_remote_content( $url );\r
1510         \r
1511         // If false, return url as title.\r
1512         // Todo: improve this with temporary title when shorturl_meta available?\r
1513         if( false === $content )\r
1514                 return $url;\r
1515 \r
1516         if( $content !== false ) {\r
1517                 // look for <title>\r
1518                 if ( preg_match('/<title>(.*?)<\/title>/is', $content, $found ) ) {\r
1519                         $title = $found[1];\r
1520                         unset( $found );\r
1521                 }\r
1522 \r
1523                 // look for charset\r
1524                 // <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\r
1525                 if ( preg_match('/<meta[^>]*?charset=([^>]*?)\/?>/is', $content, $found ) ) {\r
1526                         $charset = trim($found[1], '"\' ');\r
1527                         unset( $found );\r
1528                 }\r
1529         }\r
1530         \r
1531         // if title not found, guess if returned content was actually an error message\r
1532         if( $title == false && strpos( $content, 'Error' ) === 0 ) {\r
1533                 $title = $content;\r
1534         }\r
1535         \r
1536         if( $title == false )\r
1537                 $title = $url;\r
1538         \r
1539         /*\r
1540         if( !yourls_seems_utf8( $title ) )\r
1541                 $title = utf8_encode( $title );\r
1542         */\r
1543         \r
1544         // Charset conversion. We use @ to remove warnings (mb_ functions are easily bitching about illegal chars)\r
1545         if( function_exists( 'mb_convert_encoding' ) ) {\r
1546                 if( $charset ) {\r
1547                         $title = @mb_convert_encoding( $title, 'UTF-8', $charset );\r
1548                 } else {\r
1549                         $title = @mb_convert_encoding( $title, 'UTF-8' );\r
1550                 }\r
1551         }\r
1552         \r
1553         // Remove HTML entities\r
1554         $title = html_entity_decode( $title, ENT_QUOTES, 'UTF-8' );\r
1555         \r
1556         // Strip out evil things\r
1557         $title = yourls_sanitize_title( $title );\r
1558         \r
1559         return yourls_apply_filter( 'get_remote_title', $title, $url );\r
1560 }\r
1561 \r
1562 // Check for maintenance mode that will shortcut everything\r
1563 function yourls_check_maintenance_mode() {\r
1564         \r
1565         // TODO: all cases that always display the sites (is_admin but not is_ajax?)\r
1566         if( 1 )\r
1567                 return;\r
1568 \r
1569         // first case: /user/maintenance.php file\r
1570         if( file_exists( YOURLS_USERDIR.'/maintenance.php' ) ) {\r
1571                 include( YOURLS_USERDIR.'/maintenance.php' );\r
1572                 die();  \r
1573         }\r
1574         \r
1575         // second case: option in DB\r
1576         if( yourls_get_option( 'maintenance_mode' ) !== false ) {\r
1577                 require_once( YOURLS_INC.'/functions-html.php' );\r
1578                 $title = 'Service temporarily unavailable';\r
1579                 $message = 'Our service is currently undergoing scheduled maintenance.</p>\r
1580                 <p>Things should not last very long, thank you for your patience and please excuse the inconvenience';\r
1581                 yourls_die( $message, $title , 503 );\r
1582         }\r
1583         \r
1584 }\r
1585 \r
1586 // Toggle maintenance mode\r
1587 function yourls_maintenance_mode( $maintenance = true ) {\r
1588         yourls_update_option( 'maintenance_mode', (bool)$maintenance );\r
1589 }\r
1590 \r
1591 // Quick UA check for mobile devices. Return boolean.\r
1592 function yourls_is_mobile_device() {\r
1593         // Strings searched\r
1594         $mobiles = array(\r
1595                 'android', 'blackberry', 'blazer',\r
1596                 'compal', 'elaine', 'fennec', 'hiptop',\r
1597                 'iemobile', 'iphone', 'ipod', 'ipad',\r
1598                 'iris', 'kindle', 'opera mobi', 'opera mini',\r
1599                 'palm', 'phone', 'pocket', 'psp', 'symbian',\r
1600                 'treo', 'wap', 'windows ce', 'windows phone'\r
1601         );\r
1602         \r
1603         // Current user-agent\r
1604         $current = strtolower( $_SERVER['HTTP_USER_AGENT'] );\r
1605         \r
1606         // Check and return\r
1607         $is_mobile = ( str_replace( $mobiles, '', $current ) != $current );\r
1608         return yourls_apply_filter( 'is_mobile_device', $is_mobile );\r
1609 }\r
1610 \r
1611 // Get request in YOURLS base (eg in 'http://site.com/yourls/abcd' get 'abdc')\r
1612 function yourls_get_request() {\r
1613         // Allow plugins to short-circuit the whole function\r
1614         $pre = yourls_apply_filter( 'shunt_get_request', false );\r
1615         if ( false !== $pre )\r
1616                 return $pre;\r
1617                 \r
1618         yourls_do_action( 'pre_get_request' );\r
1619         \r
1620         // Ignore protocol, www. prefix and extract keyword\r
1621         $base = str_replace( array( 'https://', 'http://', 'https://www.', 'http://www.' ), '', YOURLS_SITE );\r
1622         $request = str_replace( $base.'/', '', $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );\r
1623         \r
1624         // Strip query string from keyword\r
1625         $request = current( explode( '?', $request ) );\r
1626         \r
1627         return yourls_apply_filter( 'get_request', $request );\r
1628 }\r
1629 \r
1630 // Change protocol to match current scheme used (http or https)\r
1631 function yourls_match_current_protocol( $url, $normal = 'http', $ssl = 'https' ) {\r
1632         if( yourls_is_ssl() )\r
1633                 $url = str_replace( $normal, $ssl, $url );\r
1634         return yourls_apply_filter( 'match_current_protocol', $url );\r
1635 }\r
1636 \r
1637 // Fix $_SERVER['REQUEST_URI'] variable for various setups. Stolen from WP.\r
1638 function yourls_fix_request_uri() {\r
1639 \r
1640         $default_server_values = array(\r
1641                 'SERVER_SOFTWARE' => '',\r
1642                 'REQUEST_URI' => '',\r
1643         );\r
1644         $_SERVER = array_merge( $default_server_values, $_SERVER );\r
1645 \r
1646         // Fix for IIS when running with PHP ISAPI\r
1647         if ( empty( $_SERVER['REQUEST_URI'] ) || ( php_sapi_name() != 'cgi-fcgi' && preg_match( '/^Microsoft-IIS\//', $_SERVER['SERVER_SOFTWARE'] ) ) ) {\r
1648 \r
1649                 // IIS Mod-Rewrite\r
1650                 if ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] ) ) {\r
1651                         $_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_ORIGINAL_URL'];\r
1652                 }\r
1653                 // IIS Isapi_Rewrite\r
1654                 else if ( isset( $_SERVER['HTTP_X_REWRITE_URL'] ) ) {\r
1655                         $_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_REWRITE_URL'];\r
1656                 } else {\r
1657                         // Use ORIG_PATH_INFO if there is no PATH_INFO\r
1658                         if ( !isset( $_SERVER['PATH_INFO'] ) && isset( $_SERVER['ORIG_PATH_INFO'] ) )\r
1659                                 $_SERVER['PATH_INFO'] = $_SERVER['ORIG_PATH_INFO'];\r
1660 \r
1661                         // Some IIS + PHP configurations puts the script-name in the path-info (No need to append it twice)\r
1662                         if ( isset( $_SERVER['PATH_INFO'] ) ) {\r
1663                                 if ( $_SERVER['PATH_INFO'] == $_SERVER['SCRIPT_NAME'] )\r
1664                                         $_SERVER['REQUEST_URI'] = $_SERVER['PATH_INFO'];\r
1665                                 else\r
1666                                         $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] . $_SERVER['PATH_INFO'];\r
1667                         }\r
1668 \r
1669                         // Append the query string if it exists and isn't null\r
1670                         if ( ! empty( $_SERVER['QUERY_STRING'] ) ) {\r
1671                                 $_SERVER['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];\r
1672                         }\r
1673                 }\r
1674         }\r
1675 }\r
1676 \r
1677 // Shutdown function, runs just before PHP shuts down execution. Stolen from WP\r
1678 function yourls_shutdown() {\r
1679         yourls_do_action( 'shutdown' );\r
1680 }\r
1681 \r
1682 // Auto detect custom favicon in /user directory, fallback to YOURLS favicon, and echo/return its URL\r
1683 function yourls_favicon( $echo = true ) {\r
1684         static $favicon = null;\r
1685         if( $favicon !== null )\r
1686                 return $favicon;\r
1687         \r
1688         $custom = null;\r
1689         // search for favicon.(gif|ico|png|jpg|svg)\r
1690         foreach( array( 'gif', 'ico', 'png', 'jpg', 'svg' ) as $ext ) {\r
1691                 if( file_exists( YOURLS_USERDIR. '/favicon.' . $ext ) ) {\r
1692                         $custom = 'favicon.' . $ext;\r
1693                         break;\r
1694                 }\r
1695         }\r
1696         \r
1697         if( $custom ) {\r
1698                 $favicon = yourls_site_url( false, YOURLS_USERURL . '/' . $custom );\r
1699         } else {\r
1700                 $favicon = yourls_site_url( false ) . '/images/favicon.gif';\r
1701         }\r
1702         if( $echo )\r
1703                 echo $favicon;\r
1704         return $favicon;\r
1705 }\r