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