]> CyberLeo.Net >> Repos - Github/YOURLS.git/blob - includes/functions.php
Better function comment
[Github/YOURLS.git] / includes / functions.php
1 <?php\r
2 /*\r
3  * YOURLS\r
4  * Function library\r
5  */\r
6 \r
7 if (defined('YOURLS_DEBUG') && YOURLS_DEBUG == true) {\r
8         error_reporting(E_ALL);\r
9 } else {\r
10         error_reporting(E_ERROR | E_PARSE);\r
11 }\r
12 \r
13 // function to convert an integer (1337) to a string (3jk). Input integer processed as a string to beat PHP's int max value\r
14 function yourls_int2string( $id ) {\r
15         $str = yourls_base2base(trim(strval($id)), 10, YOURLS_URL_CONVERT);\r
16         if (YOURLS_URL_CONVERT <= 37)\r
17                 $str = strtolower($str);\r
18         return $str;\r
19 }\r
20 \r
21 // function to convert a string (3jk) to an integer (1337)\r
22 function yourls_string2int( $str ) {\r
23         if (YOURLS_URL_CONVERT <= 37)\r
24                 $str = strtolower($str);\r
25         return yourls_base2base(trim($str), YOURLS_URL_CONVERT, 10);\r
26 }\r
27 \r
28 // Make sure a link keyword (ie "1fv" as in "site.com/1fv") is valid.\r
29 function yourls_sanitize_string($in) {\r
30         if (YOURLS_URL_CONVERT <= 37)\r
31                 $in = strtolower($in);\r
32         return substr(preg_replace('/[^a-zA-Z0-9]/', '', $in), 0, 199);\r
33 }\r
34 \r
35 // Alias function. I was always getting it wrong.\r
36 function yourls_sanitize_keyword( $keyword ) {\r
37         return yourls_sanitize_string( $keyword );\r
38 }\r
39 \r
40 // A few sanity checks on the URL\r
41 function yourls_sanitize_url($url) {\r
42         // make sure there's only one 'http://' at the beginning (prevents pasting a URL right after the default 'http://')\r
43         $url = str_replace('http://http://', 'http://', $url);\r
44 \r
45         // make sure there's a protocol, add http:// if not\r
46         if ( !preg_match('!^([a-zA-Z]+://)!', $url ) )\r
47                 $url = 'http://'.$url;\r
48         \r
49         $url = yourls_clean_url($url);\r
50         \r
51         return substr( $url, 0, 1999 );\r
52 }\r
53 \r
54 // Function to filter all invalid characters from a URL. Stolen from WP's clean_url()\r
55 function yourls_clean_url( $url ) {\r
56         $url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'"()\\x80-\\xff]|i', '', $url );\r
57         $strip = array('%0d', '%0a', '%0D', '%0A');\r
58         $url = yourls_deep_replace($strip, $url);\r
59         $url = str_replace(';//', '://', $url);\r
60         $url = str_replace('&amp;', '&', $url); // Revert & not to break query strings\r
61         \r
62         return $url;\r
63 }\r
64 \r
65 // Perform a replacement while a string is found, eg $subject = '%0%0%0DDD', $search ='%0D' -> $result =''\r
66 // Stolen from WP's _deep_replace\r
67 function yourls_deep_replace($search, $subject){\r
68         $found = true;\r
69         while($found) {\r
70                 $found = false;\r
71                 foreach( (array) $search as $val ) {\r
72                         while(strpos($subject, $val) !== false) {\r
73                                 $found = true;\r
74                                 $subject = str_replace($val, '', $subject);\r
75                         }\r
76                 }\r
77         }\r
78         \r
79         return $subject;\r
80 }\r
81 \r
82 // Make sure an integer is a valid integer (PHP's intval() limits to too small numbers)\r
83 // TODO FIXME FFS: unused ?\r
84 function yourls_sanitize_int($in) {\r
85         return ( substr(preg_replace('/[^0-9]/', '', strval($in) ), 0, 20) );\r
86 }\r
87 \r
88 // Make sure a integer is safe\r
89 // Note: this is not checking for integers, since integers on 32bits system are way too limited\r
90 // TODO: find a way to validate as integer\r
91 function yourls_intval($in) {\r
92         return yourls_escape($in);\r
93 }\r
94 \r
95 // Escape a string\r
96 function yourls_escape( $in ) {\r
97         return mysql_real_escape_string($in);\r
98 }\r
99 \r
100 // Check to see if a given keyword is reserved (ie reserved URL or an existing page)\r
101 // Returns bool\r
102 function yourls_keyword_is_reserved( $keyword ) {\r
103         global $yourls_reserved_URL;\r
104         $keyword = yourls_sanitize_keyword( $keyword );\r
105         \r
106         if ( in_array( $keyword, $yourls_reserved_URL)\r
107                 or file_exists(dirname(dirname(__FILE__))."/pages/$keyword.php")\r
108                 or is_dir(dirname(dirname(__FILE__))."/$keyword")\r
109         )\r
110                 return true;\r
111         \r
112         return false;\r
113 }\r
114 \r
115 // Function: Get IP Address. Returns a DB safe string.\r
116 function yourls_get_IP() {\r
117         if( !empty( $_SERVER['REMOTE_ADDR'] ) ) {\r
118                 $ip = $_SERVER['REMOTE_ADDR'];\r
119         } else {\r
120                 if(!empty($_SERVER['HTTP_CLIENT_IP'])) {\r
121                         $ip = $_SERVER['HTTP_CLIENT_IP'];\r
122                 } else if(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {\r
123                         $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];\r
124                 } else if(!empty($_SERVER['HTTP_VIA '])) {\r
125                         $ip = $_SERVER['HTTP_VIA '];\r
126                 }\r
127         }\r
128 \r
129         return yourls_sanitize_ip( $ip );\r
130 }\r
131 \r
132 // Sanitize an IP address\r
133 function yourls_sanitize_ip( $ip ) {\r
134         return preg_replace( '/[^0-9a-fA-F:., ]/', '', $ip );\r
135 }\r
136 \r
137 // Make sure a date is m(m)/d(d)/yyyy, return false otherwise\r
138 function yourls_sanitize_date( $date ) {\r
139         if( !preg_match( '!^\d{1,2}/\d{1,2}/\d{4}$!' , $date ) ) {\r
140                 return false;\r
141         }\r
142         return $date;\r
143 }\r
144 \r
145 // Sanitize a date for SQL search. Return false if malformed input.\r
146 function yourls_sanitize_date_for_sql( $date ) {\r
147         if( !yourls_sanitize_date( $date ) )\r
148                 return false;\r
149         return date('Y-m-d', strtotime( $date ) );\r
150 }\r
151 \r
152 // Add the "Edit" row\r
153 function yourls_table_edit_row( $keyword ) {\r
154         global $ydb;\r
155         \r
156         $table = YOURLS_DB_TABLE_URL;\r
157         $keyword = yourls_sanitize_string( $keyword );\r
158         $id = yourls_string2int( $keyword ); // used as HTML #id\r
159         $url = $ydb->get_row("SELECT `url` FROM `$table` WHERE `keyword` = '$keyword';");\r
160         $safe_url = stripslashes( $url->url );\r
161         $www = YOURLS_SITE;\r
162         \r
163         if( $url ) {\r
164                 $return = <<<RETURN\r
165 <tr id="edit-$id" class="edit-row"><td colspan="5"><strong>Original URL</strong>:<input type="text" id="edit-url-$id" name="edit-url-$id" value="$safe_url" class="text" size="70" /> <strong>Short URL</strong>: $www/<input type="text" id="edit-keyword-$id" name="edit-keyword-$id" value="$keyword" class="text" size="10" /></td><td colspan="1"><input type="button" id="edit-submit-$id" name="edit-submit-$id" value="Save" title="Save new values" class="button" onclick="edit_save('$id');" />&nbsp;<input type="button" id="edit-close-$id" name="edit-close-$id" value="X" title="Cancel editing" class="button" onclick="hide_edit('$id');" /><input type="hidden" id="old_keyword_$id" value="$keyword"/></td></tr>\r
166 RETURN;\r
167         } else {\r
168                 $return = '<tr><td colspan="6">Error, URL not found</td></tr>';\r
169         }\r
170         \r
171         return $return;\r
172 }\r
173 \r
174 // Add a link row\r
175 function yourls_table_add_row( $keyword, $url, $ip, $clicks, $timestamp ) {\r
176         $keyword = yourls_sanitize_string( $keyword );\r
177         $id = yourls_string2int( $keyword ); // used as HTML #id\r
178         $date = date( 'M d, Y H:i', $timestamp+( YOURLS_HOURS_OFFSET * 3600) );\r
179         $clicks = number_format($clicks, 0, '', '');\r
180         $shorturl = YOURLS_SITE.'/'.$keyword;\r
181         $display_url = htmlentities( yourls_trim_long_string( $url ) );\r
182         $statlink = $shorturl.'+';\r
183         $url = htmlentities( $url );\r
184         \r
185         return <<<ROW\r
186 <tr id="id-$id"><td id="keyword-$id"><a href="$shorturl">$keyword</a></td><td id="url-$id"><a href="$url" title="$url">$display_url</a></td><td id="timestamp-$id">$date</td><td id="ip-$id">$ip</td><td id="clicks-$id">$clicks</td><td class="actions" id="actions-$id"><a href="$statlink" id="statlink-$id" class="button button_stats">&nbsp;&nbsp;&nbsp;</a>&nbsp;<input type="button" id="edit-button-$id" name="edit-button" value="" title="Edit" class="button button_edit" onclick="edit('$id');" />&nbsp;<input type="button" id="delete-button-$id" name="delete-button" value="" title="Delete" class="button button_delete" onclick="remove('$id');" /><input type="hidden" id="keyword_$id" value="$keyword"/></td></tr>\r
187 ROW;\r
188 }\r
189 \r
190 // Get next id a new link will have if no custom keyword provided\r
191 function yourls_get_next_decimal() {\r
192         return (int)yourls_get_option( 'next_id' );\r
193 }\r
194 \r
195 // Update id for next link with no custom keyword\r
196 function yourls_update_next_decimal( $int = '' ) {\r
197         $int = ( $int == '' ) ? yourls_get_next_decimal() + 1 : (int)$int ;\r
198         return yourls_update_option( 'next_id', $int );\r
199 }\r
200 \r
201 // Delete a link in the DB\r
202 function yourls_delete_link_by_keyword( $keyword ) {\r
203         global $ydb;\r
204 \r
205         $table = YOURLS_DB_TABLE_URL;\r
206         $keyword = yourls_sanitize_string( $keyword );\r
207         return $ydb->query("DELETE FROM `$table` WHERE `keyword` = '$keyword';");\r
208 }\r
209 \r
210 // SQL query to insert a new link in the DB. Needs sanitized data. Returns boolean for success or failure of the inserting\r
211 function yourls_insert_link_in_db($url, $keyword) {\r
212         global $ydb;\r
213 \r
214         $table = YOURLS_DB_TABLE_URL;\r
215         $timestamp = date('Y-m-d H:i:s');\r
216         $ip = yourls_get_IP();\r
217         $insert = $ydb->query("INSERT INTO `$table` VALUES('$keyword', '$url', '$timestamp', '$ip', 0);");\r
218         \r
219         return (bool)$insert;\r
220 }\r
221 \r
222 // Add a new link in the DB, either with custom keyword, or find one\r
223 function yourls_add_new_link( $url, $keyword = '' ) {\r
224         global $ydb;\r
225 \r
226         if ( !$url || $url == 'http://' || $url == 'https://' ) {\r
227                 $return['status'] = 'fail';\r
228                 $return['code'] = 'error:nourl';\r
229                 $return['message'] = 'Missing URL input';\r
230                 $return['errorCode'] = '400';\r
231                 return $return;\r
232         }\r
233         \r
234         // Prevent DB flood\r
235         $ip = yourls_get_IP();\r
236         yourls_check_IP_flood( $ip );\r
237 \r
238         // TODO? Prevent internal redirection loops: cannot shorten a shortened URL\r
239         // Could ban site.com/abc if 'abc' is already a taken keyword\r
240         // and site.com/abc-def\r
241         // Could allow site.com/abc+\r
242         // and site.com/abc+all\r
243 \r
244         $url = yourls_escape( yourls_sanitize_url($url) );\r
245         $table = YOURLS_DB_TABLE_URL;\r
246         $strip_url = stripslashes($url);\r
247         $url_exists = $ydb->get_row("SELECT keyword,url FROM `$table` WHERE `url` = '".$strip_url."';");\r
248         $return = array();\r
249 \r
250         // New URL : store it -- or: URL exists, but duplicates allowed\r
251         if( !$url_exists || yourls_allow_duplicate_longurls() ) {\r
252 \r
253                 // Custom keyword provided\r
254                 if ( $keyword ) {\r
255                         $keyword = yourls_escape( yourls_sanitize_string($keyword) );\r
256                         if ( !yourls_keyword_is_free($keyword) ) {\r
257                                 // This shorturl either reserved or taken already\r
258                                 $return['status'] = 'fail';\r
259                                 $return['code'] = 'error:keyword';\r
260                                 $return['message'] = 'Short URL '.$keyword.' already exists in database or is reserved';\r
261                         } else {\r
262                                 // all clear, store !\r
263                                 yourls_insert_link_in_db($url, $keyword);\r
264                                 $return['url'] = array('keyword' => $keyword, 'url' => $strip_url, 'date' => date('Y-m-d H:i:s'), 'ip' => $ip );\r
265                                 $return['status'] = 'success';\r
266                                 $return['message'] = $strip_url.' added to database';\r
267                                 $return['html'] = yourls_table_add_row( $keyword, $url, $ip, 0, time() );\r
268                                 $return['shorturl'] = YOURLS_SITE .'/'. $keyword;\r
269                         }\r
270 \r
271                 // Create random keyword        \r
272                 } else {\r
273                         $timestamp = date('Y-m-d H:i:s');\r
274                         $id = yourls_get_next_decimal();\r
275                         $ok = false;\r
276                         do {\r
277                                 $keyword = yourls_int2string( $id );\r
278                                 $free = yourls_keyword_is_free($keyword);\r
279                                 $add_url = @yourls_insert_link_in_db($url, $keyword);\r
280                                 $ok = ($free && $add_url);\r
281                                 if ( $ok === false && $add_url === 1 ) {\r
282                                         // we stored something, but shouldn't have (ie reserved id)\r
283                                         $delete = yourls_delete_link_by_keyword( $keyword );\r
284                                         $return['extra_info'] .= '(deleted '.$keyword.')';\r
285                                 } else {\r
286                                         // everything ok, populate needed vars\r
287                                         $return['url'] = array('keyword' => $keyword, 'url' => $strip_url, 'date' => $timestamp, 'ip' => $ip );\r
288                                         $return['status'] = 'success';\r
289                                         $return['message'] = $strip_url.' added to database';\r
290                                         $return['html'] = yourls_table_add_row( $keyword, $url, $ip, 0, time() );\r
291                                         $return['shorturl'] = YOURLS_SITE .'/'. $keyword;\r
292                                 }\r
293                                 $id++;\r
294                         } while (!$ok);\r
295                         @yourls_update_next_decimal($id);\r
296                 }\r
297         } else {\r
298                 // URL was already stored\r
299                 $return['status'] = 'fail';\r
300                 $return['code'] = 'error:url';\r
301                 $return['message'] = $strip_url.' already exists in database';\r
302                 $return['shorturl'] = YOURLS_SITE .'/'. $url_exists->keyword;\r
303         }\r
304 \r
305         $return['statusCode'] = 200; // regardless of result, this is still a valid request\r
306         return $return;\r
307 }\r
308 \r
309 \r
310 // Edit a link\r
311 function yourls_edit_link($url, $keyword, $newkeyword='') {\r
312         global $ydb;\r
313 \r
314         $table = YOURLS_DB_TABLE_URL;\r
315         $url = yourls_escape(yourls_sanitize_url($url));\r
316         $keyword = yourls_sanitize_string( $keyword );\r
317         $newkeyword = yourls_sanitize_string( $newkeyword );\r
318         $strip_url = stripslashes($url);\r
319         $old_url = $ydb->get_var("SELECT `url` FROM `$table` WHERE `keyword` = '$keyword';");\r
320         $old_id = $id = yourls_string2int( $keyword );\r
321         $new_id = ( $newkeyword == '' ? $old_id : yourls_string2int( $newkeyword ) );\r
322         \r
323         // Check if new URL is not here already\r
324         if ($old_url != $url) {\r
325                 $new_url_already_there = intval($ydb->get_var("SELECT COUNT(keyword) FROM `$table` WHERE `url` = '$strip_url';"));\r
326         } else {\r
327                 $new_url_already_there = false;\r
328         }\r
329         \r
330         // Check if the new keyword is not here already\r
331         if ( $newkeyword != $keyword ) {\r
332                 $keyword_is_ok = yourls_keyword_is_free( $newkeyword );\r
333         } else {\r
334                 $keyword_is_ok = true;\r
335         }\r
336         \r
337         // All clear, update\r
338         if ( ( !$new_url_already_there || yourls_allow_duplicate_longurls() ) && $keyword_is_ok ) {\r
339                         $update_url = $ydb->query("UPDATE `$table` SET `url` = '$url', `keyword` = '$newkeyword' WHERE `keyword` = '$keyword';");\r
340                 if( $update_url ) {\r
341                         $return['url'] = array( 'keyword' => $newkeyword, 'shorturl' => YOURLS_SITE.'/'.$newkeyword, 'url' => $strip_url, 'display_url' => yourls_trim_long_string( $strip_url ), 'new_id' => $new_id );\r
342                         $return['status'] = 'success';\r
343                         $return['message'] = 'Link updated in database';\r
344                 } else {\r
345                         $return['status'] = 'fail';\r
346                         $return['message'] = 'Error updating '.$strip_url.' (Short URL: '.$keyword.') to database';\r
347                 }\r
348         \r
349         // Nope\r
350         } else {\r
351                 $return['status'] = 'fail';\r
352                 $return['message'] = 'URL or keyword already exists in database';\r
353         }\r
354         \r
355         return $return;\r
356 }\r
357 \r
358 \r
359 // Check if keyword id is free (ie not already taken, and not reserved). Return bool.\r
360 function yourls_keyword_is_free( $keyword ) {\r
361         if ( yourls_keyword_is_reserved( $keyword ) or yourls_keyword_is_taken( $keyword ) )\r
362                 return false;\r
363                 \r
364         return true;\r
365 }\r
366 \r
367 // Check if a keyword is taken (ie there is already a short URL with this id). Return bool.             \r
368 function yourls_keyword_is_taken( $keyword ) {\r
369         global $ydb;\r
370         $keyword = yourls_sanitize_keyword( $keyword );\r
371                 \r
372         $table = YOURLS_DB_TABLE_URL;\r
373         $already_exists = $ydb->get_var("SELECT COUNT(`keyword`) FROM `$table` WHERE `keyword` = '$keyword';");\r
374         if ( $already_exists )\r
375                 return true;\r
376 \r
377         return false;\r
378 }\r
379 \r
380 \r
381 // Display a page\r
382 function yourls_page( $page ) {\r
383         $include = dirname(dirname(__FILE__))."/pages/$page.php";\r
384         if (!file_exists($include)) {\r
385                 yourls_die("Page '$page' not found", 'Not found', 404);\r
386         }\r
387         include($include);\r
388         die();  \r
389 }\r
390 \r
391 // Connect to DB\r
392 function yourls_db_connect() {\r
393         global $ydb;\r
394 \r
395         if (!defined('YOURLS_DB_USER')\r
396                 or !defined('YOURLS_DB_PASS')\r
397                 or !defined('YOURLS_DB_NAME')\r
398                 or !defined('YOURLS_DB_HOST')\r
399                 or !class_exists('ezSQL_mysql')\r
400         ) yourls_die ('DB config missigin, or could not find DB class', 'Fatal error', 503);\r
401         \r
402         // Are we standalone or in the WordPress environment?\r
403         if ( class_exists('wpdb') ) {\r
404                 $ydb =  new wpdb(YOURLS_DB_USER, YOURLS_DB_PASS, YOURLS_DB_NAME, YOURLS_DB_HOST);\r
405         } else {\r
406                 $ydb =  new ezSQL_mysql(YOURLS_DB_USER, YOURLS_DB_PASS, YOURLS_DB_NAME, YOURLS_DB_HOST);\r
407         }\r
408         if ( $ydb->last_error )\r
409                 yourls_die( $ydb->last_error, 'Fatal error', 503 );\r
410         \r
411         if ( defined('YOURLS_DEBUG') && YOURLS_DEBUG === true )\r
412                 $ydb->show_errors = true;\r
413         \r
414         return $ydb;\r
415 }\r
416 \r
417 // Return JSON output. Compatible with PHP prior to 5.2\r
418 function yourls_json_encode($array) {\r
419         if (function_exists('json_encode')) {\r
420                 return json_encode($array);\r
421         } else {\r
422                 require_once(dirname(__FILE__).'/functions-json.php');\r
423                 return yourls_array_to_json($array);\r
424         }\r
425 }\r
426 \r
427 // Return XML output.\r
428 function yourls_xml_encode($array) {\r
429         require_once(dirname(__FILE__).'/functions-xml.php');\r
430         $converter= new yourls_array2xml;\r
431         return $converter->array2xml($array);\r
432 }\r
433 \r
434 // 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
435 function yourls_get_keyword_infos( $keyword, $use_cache = true ) {\r
436         global $ydb;\r
437         $keyword = yourls_sanitize_string( $keyword );\r
438 \r
439         if( isset( $ydb->infos[$keyword] ) && $use_cache == true ) {\r
440                 return $ydb->infos[$keyword];\r
441         }\r
442         \r
443         $table = YOURLS_DB_TABLE_URL;\r
444         $infos = $ydb->get_row("SELECT * FROM `$table` WHERE `keyword` = '$keyword'");\r
445         \r
446         if( $infos ) {\r
447                 $infos = (array)$infos;\r
448                 $ydb->infos[$keyword] = $infos;\r
449         } else {\r
450                 $ydb->infos[$keyword] = false;\r
451         }\r
452                 \r
453         return $ydb->infos[$keyword];\r
454 }\r
455 \r
456 // Return (string) selected information associated with a keyword. Optional $notfound = string default message if nothing found\r
457 function yourls_get_keyword_info( $keyword, $field, $notfound = false ) {\r
458         global $ydb;\r
459         \r
460         $keyword = yourls_sanitize_string( $keyword );\r
461         $infos = yourls_get_keyword_infos( $keyword );\r
462         \r
463         if ( isset($infos[$field]) && $infos[$field] !== false )\r
464                 return $infos[$field];\r
465 \r
466         return $notfound;       \r
467 }\r
468 \r
469 // Return long URL associated with keyword. Optional $notfound = string default message if nothing found\r
470 function yourls_get_keyword_longurl( $keyword, $notfound = false ) {\r
471         return yourls_get_keyword_info( $keyword, 'url', $notfound );\r
472 }\r
473 \r
474 // Return number of clicks on a keyword. Optional $notfound = string default message if nothing found\r
475 function yourls_get_keyword_clicks( $keyword, $notfound = false ) {\r
476         return yourls_get_keyword_info( $keyword, 'clicks', $notfound );\r
477 }\r
478 \r
479 // Return IP that added a keyword. Optional $notfound = string default message if nothing found\r
480 function yourls_get_keyword_IP( $keyword, $notfound = false ) {\r
481         return yourls_get_keyword_info( $keyword, 'ip', $notfound );\r
482 }\r
483 \r
484 // Return timestamp associated with a keyword. Optional $notfound = string default message if nothing found\r
485 function yourls_get_keyword_timestamp( $keyword, $notfound = false ) {\r
486         return yourls_get_keyword_info( $keyword, 'timestamp', $notfound );\r
487 }\r
488 \r
489 // Update click count on a short URL\r
490 function yourls_update_clicks( $keyword ) {\r
491         global $ydb;\r
492         $keyword = yourls_sanitize_string( $keyword );\r
493         $table = YOURLS_DB_TABLE_URL;\r
494         return $ydb->query("UPDATE `$table` SET `clicks` = clicks + 1 WHERE `keyword` = '$keyword'");\r
495 }\r
496 \r
497 // Return array of stats. (string)$filter is 'bottom', 'last', 'rand' or 'top'. (int)$limit is the number of links to return\r
498 function yourls_get_stats( $filter = 'top', $limit = 10 ) {\r
499         global $ydb;\r
500 \r
501         switch( $filter ) {\r
502                 case 'bottom':\r
503                         $sort_by = 'clicks';\r
504                         $sort_order = 'asc';\r
505                         break;\r
506                 case 'last':\r
507                         $sort_by = 'timestamp';\r
508                         $sort_order = 'desc';\r
509                         break;\r
510                 case 'rand':\r
511                 case 'random':\r
512                         $sort_by = 'RAND()';\r
513                         $sort_order = '';\r
514                         break;\r
515                 case 'top':\r
516                 default:\r
517                         $sort_by = 'clicks';\r
518                         $sort_order = 'desc';\r
519                         break;\r
520         }\r
521         \r
522         $limit = intval( $limit );\r
523         if ( $limit == 0 )\r
524                 $limit = 1;\r
525         $table_url = YOURLS_DB_TABLE_URL;\r
526         $results = $ydb->get_results("SELECT * FROM `$table_url` WHERE 1=1 ORDER BY `$sort_by` $sort_order LIMIT 0, $limit;");\r
527         \r
528         $return = array();\r
529         $i = 1;\r
530         \r
531         foreach ($results as $res) {\r
532                 $return['links']['link_'.$i++] = array(\r
533                         'shorturl' => YOURLS_SITE .'/'. $res->keyword,\r
534                         'url' => $res->url,\r
535                         'timestamp' => $res->timestamp,\r
536                         'ip' => $res->ip,\r
537                         'clicks' => $res->clicks,\r
538                 );\r
539         }\r
540 \r
541         $return['stats'] = yourls_get_db_stats();\r
542         \r
543         $return['statusCode'] = 200;\r
544 \r
545         return $return;\r
546 }\r
547 \r
548 // Return array of stats. (string)$filter is 'bottom', 'last', 'rand' or 'top'. (int)$limit is the number of links to return\r
549 function yourls_get_link_stats( $shorturl ) {\r
550         global $ydb;\r
551 \r
552         $table_url = YOURLS_DB_TABLE_URL;\r
553         $res = $ydb->get_row("SELECT * FROM `$table_url` WHERE keyword = '$shorturl';");\r
554 \r
555         $return = array();\r
556 \r
557     $return['link'] = array(\r
558         'shorturl' => YOURLS_SITE .'/'. $res->keyword,\r
559         'url' => $res->url,\r
560         'timestamp' => $res->timestamp,\r
561         'ip' => $res->ip,\r
562         'clicks' => $res->clicks,\r
563     );\r
564 \r
565         $return['statusCode'] = 200;\r
566 \r
567         return $return;\r
568 }\r
569 \r
570 // Return array for API stat requests\r
571 function yourls_api_stats( $filter = 'top', $limit = 10 ) {\r
572         $return = yourls_get_stats( $filter, $limit );\r
573         $return['simple']  = 'Need either XML or JSON format for stats';\r
574         $return['message'] = 'success';\r
575         return $return;\r
576 }\r
577 \r
578 // Return array for API stat requests\r
579 function yourls_api_url_stats($shorturl) {\r
580         $keyword = str_replace( YOURLS_SITE . '/' , '', $shorturl ); // accept either 'http://ozh.in/abc' or 'abc'\r
581         $keyword = yourls_sanitize_string( $keyword );\r
582 \r
583         $return = yourls_get_link_stats( $keyword );\r
584         $return['simple']  = 'Need either XML or JSON format for stats';\r
585         $return['message'] = 'success';\r
586         return $return;\r
587 }\r
588 \r
589 // Expand short url to long url\r
590 function yourls_api_expand( $shorturl ) {\r
591         $keyword = str_replace( YOURLS_SITE . '/' , '', $shorturl ); // accept either 'http://ozh.in/abc' or 'abc'\r
592         $keyword = yourls_sanitize_string( $keyword );\r
593         \r
594         $longurl = yourls_get_keyword_longurl( $keyword );\r
595         \r
596         if( $longurl ) {\r
597                 return array(\r
598                         'keyword'  => $keyword,\r
599                         'shorturl' => YOURLS_SITE . "/$keyword",\r
600                         'longurl'  => $longurl,\r
601                         'simple'   => $longurl,\r
602                         'message'  => 'success',\r
603                         'statusCode' => 200,\r
604                 );\r
605         } else {\r
606                 return array(\r
607                         'keyword'  => $keyword,\r
608                         'simple'   => 'not found',\r
609                         'message'  => 'Error: short URL not found',\r
610                         'errorCode' => 404,\r
611                 );\r
612         }\r
613 }\r
614 \r
615 \r
616 // Get total number of URLs and sum of clicks. Input: optional "AND WHERE" clause. Returns array\r
617 function yourls_get_db_stats( $where = '' ) {\r
618         global $ydb;\r
619         $table_url = YOURLS_DB_TABLE_URL;\r
620 \r
621         $totals = $ydb->get_row("SELECT COUNT(keyword) as count, SUM(clicks) as sum FROM `$table_url` WHERE 1=1 $where");\r
622         return array( 'total_links' => $totals->count, 'total_clicks' => $totals->sum );\r
623 }\r
624 \r
625 // Return API result. Dies after this\r
626 function yourls_api_output( $mode, $return ) {\r
627         if( isset( $return['simple'] ) ) {\r
628                 $simple = $return['simple'];\r
629                 unset( $return['simple'] );\r
630         }\r
631         switch ( $mode ) {\r
632                 case 'json':\r
633                         header('Content-type: application/json');\r
634                         echo yourls_json_encode($return);\r
635                         break;\r
636                 \r
637                 case 'xml':\r
638                         header('Content-type: application/xml');\r
639                         echo yourls_xml_encode($return);\r
640                         break;\r
641                         \r
642                 case 'simple':\r
643                 default:\r
644                         if( isset( $simple ) )\r
645                                 echo $simple;\r
646                         break;\r
647         }\r
648         die();\r
649 }\r
650 \r
651 // Get number of SQL queries performed\r
652 function yourls_get_num_queries() {\r
653         global $ydb;\r
654 \r
655         return $ydb->num_queries;\r
656 }\r
657 \r
658 // Compat http_build_query for PHP4\r
659 if (!function_exists('http_build_query')) {\r
660         function http_build_query($data, $prefix=null, $sep=null) {\r
661                 return yourls_http_build_query($data, $prefix, $sep);\r
662         }\r
663 }\r
664 \r
665 // from php.net (modified by Mark Jaquith to behave like the native PHP5 function)\r
666 function yourls_http_build_query($data, $prefix=null, $sep=null, $key='', $urlencode=true) {\r
667         $ret = array();\r
668 \r
669         foreach ( (array) $data as $k => $v ) {\r
670                 if ( $urlencode)\r
671                         $k = urlencode($k);\r
672                 if ( is_int($k) && $prefix != null )\r
673                         $k = $prefix.$k;\r
674                 if ( !empty($key) )\r
675                         $k = $key . '%5B' . $k . '%5D';\r
676                 if ( $v === NULL )\r
677                         continue;\r
678                 elseif ( $v === FALSE )\r
679                         $v = '0';\r
680 \r
681                 if ( is_array($v) || is_object($v) )\r
682                         array_push($ret,yourls_http_build_query($v, '', $sep, $k, $urlencode));\r
683                 elseif ( $urlencode )\r
684                         array_push($ret, $k.'='.urlencode($v));\r
685                 else\r
686                         array_push($ret, $k.'='.$v);\r
687         }\r
688 \r
689         if ( NULL === $sep )\r
690                 $sep = ini_get('arg_separator.output');\r
691 \r
692         return implode($sep, $ret);\r
693 }\r
694 \r
695 // Returns a sanitized a user agent string. Given what I found on http://www.user-agents.org/ it should be OK.\r
696 function yourls_get_user_agent() {\r
697         if ( !isset( $_SERVER['HTTP_USER_AGENT'] ) )\r
698                 return '-';\r
699         \r
700         $ua = strip_tags( html_entity_decode( $_SERVER['HTTP_USER_AGENT'] ));\r
701         $ua = preg_replace('![^0-9a-zA-Z\':., /{}\(\)\[\]\+@&\!\?;_\-=~\*\#]!', '', $ua );\r
702                 \r
703         return substr( $ua, 0, 254 );\r
704 }\r
705 \r
706 // Redirect to another page\r
707 function yourls_redirect( $location, $code = 301 ) {\r
708         // Redirect, either properly if possible, or via Javascript otherwise\r
709         if( !headers_sent() ) {\r
710                 yourls_status_header( $code );\r
711                 header("Location: $location");\r
712         } else {\r
713                 yourls_redirect_javascript( $location );\r
714         }\r
715         die();\r
716 }\r
717 \r
718 // Set HTTP status header\r
719 function yourls_status_header( $code = 200 ) {\r
720         if( headers_sent() )\r
721                 return;\r
722                 \r
723         $protocol = $_SERVER["SERVER_PROTOCOL"];\r
724         if ( 'HTTP/1.1' != $protocol && 'HTTP/1.0' != $protocol )\r
725                 $protocol = 'HTTP/1.0';\r
726 \r
727         $code = intval( $code );\r
728         $desc = yourls_get_HTTP_status($code);\r
729 \r
730         @header ("$protocol $code $desc"); // This causes problems on IIS and some FastCGI setups\r
731 }\r
732 \r
733 // 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
734 function yourls_redirect_javascript( $location, $dontwait = true ) {\r
735         if( $dontwait ) {\r
736         echo <<<REDIR\r
737         <script type="text/javascript">\r
738         window.location="$location";\r
739         </script>\r
740         <small>(if you are not redirected after 10 seconds, please <a href="$location">click here</a>)</small>\r
741 REDIR;\r
742         } else {\r
743         echo <<<MANUAL\r
744         <p>Please <a href="$location">click here</a></p>\r
745 MANUAL;\r
746         }\r
747 }\r
748 \r
749 // Return a HTTP status code\r
750 function yourls_get_HTTP_status( $code ) {\r
751         $code = intval( $code );\r
752         $headers_desc = array(\r
753                 100 => 'Continue',\r
754                 101 => 'Switching Protocols',\r
755                 102 => 'Processing',\r
756 \r
757                 200 => 'OK',\r
758                 201 => 'Created',\r
759                 202 => 'Accepted',\r
760                 203 => 'Non-Authoritative Information',\r
761                 204 => 'No Content',\r
762                 205 => 'Reset Content',\r
763                 206 => 'Partial Content',\r
764                 207 => 'Multi-Status',\r
765                 226 => 'IM Used',\r
766 \r
767                 300 => 'Multiple Choices',\r
768                 301 => 'Moved Permanently',\r
769                 302 => 'Found',\r
770                 303 => 'See Other',\r
771                 304 => 'Not Modified',\r
772                 305 => 'Use Proxy',\r
773                 306 => 'Reserved',\r
774                 307 => 'Temporary Redirect',\r
775 \r
776                 400 => 'Bad Request',\r
777                 401 => 'Unauthorized',\r
778                 402 => 'Payment Required',\r
779                 403 => 'Forbidden',\r
780                 404 => 'Not Found',\r
781                 405 => 'Method Not Allowed',\r
782                 406 => 'Not Acceptable',\r
783                 407 => 'Proxy Authentication Required',\r
784                 408 => 'Request Timeout',\r
785                 409 => 'Conflict',\r
786                 410 => 'Gone',\r
787                 411 => 'Length Required',\r
788                 412 => 'Precondition Failed',\r
789                 413 => 'Request Entity Too Large',\r
790                 414 => 'Request-URI Too Long',\r
791                 415 => 'Unsupported Media Type',\r
792                 416 => 'Requested Range Not Satisfiable',\r
793                 417 => 'Expectation Failed',\r
794                 422 => 'Unprocessable Entity',\r
795                 423 => 'Locked',\r
796                 424 => 'Failed Dependency',\r
797                 426 => 'Upgrade Required',\r
798 \r
799                 500 => 'Internal Server Error',\r
800                 501 => 'Not Implemented',\r
801                 502 => 'Bad Gateway',\r
802                 503 => 'Service Unavailable',\r
803                 504 => 'Gateway Timeout',\r
804                 505 => 'HTTP Version Not Supported',\r
805                 506 => 'Variant Also Negotiates',\r
806                 507 => 'Insufficient Storage',\r
807                 510 => 'Not Extended'\r
808         );\r
809 \r
810         if ( isset( $headers_desc[$code] ) )\r
811                 return $headers_desc[$code];\r
812         else\r
813                 return '';\r
814 }\r
815 \r
816 \r
817 // Log a redirect (for stats)\r
818 function yourls_log_redirect( $keyword ) {\r
819         if ( !yourls_do_log_redirect() )\r
820                 return true;\r
821 \r
822         global $ydb;\r
823         $table = YOURLS_DB_TABLE_LOG;\r
824         \r
825         $keyword = yourls_sanitize_string( $keyword );\r
826         $referrer = ( isset( $_SERVER['HTTP_REFERER'] ) ? yourls_sanitize_url( $_SERVER['HTTP_REFERER'] ) : 'direct' );\r
827         $ua = yourls_get_user_agent();\r
828         $ip = yourls_get_IP();\r
829         $location = yourls_geo_ip_to_countrycode( $ip );\r
830         \r
831         return $ydb->query( "INSERT INTO `$table` VALUES ('', NOW(), '$keyword', '$referrer', '$ua', '$ip', '$location')" );\r
832 }\r
833 \r
834 // Check if we want to not log redirects (for stats)\r
835 function yourls_do_log_redirect() {\r
836         return ( !defined('YOURLS_NOSTATS') || YOURLS_NOSTATS != true );\r
837 }\r
838 \r
839 // Converts an IP to a 2 letter country code, using GeoIP database if available in includes/geo/\r
840 function yourls_geo_ip_to_countrycode( $ip = '', $default = '' ) {\r
841         if ( !file_exists( dirname(__FILE__).'/geo/GeoIP.dat') || !file_exists( dirname(__FILE__).'/geo/geoip.inc') )\r
842                 return $default;\r
843 \r
844         if ( $ip == '' )\r
845                 $ip = yourls_get_IP();\r
846                 \r
847         require_once( dirname(__FILE__).'/geo/geoip.inc') ;\r
848         $gi = geoip_open( dirname(__FILE__).'/geo/GeoIP.dat', GEOIP_STANDARD);\r
849         $location = geoip_country_code_by_addr($gi, $ip);\r
850         geoip_close($gi);\r
851 \r
852         return $location;\r
853 }\r
854 \r
855 // Converts a 2 letter country code to long name (ie AU -> Australia)\r
856 function yourls_geo_countrycode_to_countryname( $code ) {\r
857         // Load the Geo class if not already done\r
858         if( !class_exists('GeoIP') ) {\r
859                 $temp = yourls_geo_ip_to_countrycode('127.0.0.1');\r
860         }\r
861         \r
862         if( class_exists('GeoIP') ) {\r
863                 $geo = new GeoIP;\r
864                 $id = $geo->GEOIP_COUNTRY_CODE_TO_NUMBER[$code];\r
865                 $long = $geo->GEOIP_COUNTRY_NAMES[$id];\r
866                 return $long;\r
867         } else {\r
868                 return false;\r
869         }\r
870 }\r
871 \r
872 // Return flag URL from 2 letter country code\r
873 function yourls_geo_get_flag( $code ) {\r
874         // Load the Geo class if not already done\r
875         if( !class_exists('GeoIP') ) {\r
876                 $temp = yourls_geo_ip_to_countrycode('127.0.0.1');\r
877         }\r
878         \r
879         if( class_exists('GeoIP') ) {\r
880                 return YOURLS_SITE.'/includes/geo/flags/flag_'.(strtolower($code)).'.gif';\r
881         } else {\r
882                 return false;\r
883         }\r
884 }\r
885 \r
886 \r
887 // Check if an upgrade is needed\r
888 function yourls_upgrade_is_needed() {\r
889         // check YOURLS_DB_VERSION exist && match values stored in YOURLS_DB_TABLE_OPTIONS\r
890         list( $currentver, $currentsql ) = yourls_get_current_version_from_sql();\r
891         if( $currentsql < YOURLS_DB_VERSION )\r
892                 return true;\r
893                 \r
894         return false;\r
895 }\r
896 \r
897 // Get current version & db version as stored in the options DB. Prior to 1.4 there's no option table.\r
898 function yourls_get_current_version_from_sql() {\r
899         $currentver = yourls_get_option( 'version' );\r
900         $currentsql = yourls_get_option( 'db_version' );\r
901 \r
902         // Values if version is 1.3\r
903         if( !$currentver )\r
904                 $currentver = '1.3';\r
905         if( !$currentsql )\r
906                 $currentsql = '100';\r
907                 \r
908         return array( $currentver, $currentsql);\r
909 }\r
910 \r
911 // Read an option from DB (or from cache if available). Return value or $default if not found\r
912 function yourls_get_option( $option_name, $default = false ) {\r
913         global $ydb;\r
914         if ( !isset( $ydb->option[$option_name] ) ) {\r
915                 $table = YOURLS_DB_TABLE_OPTIONS;\r
916                 $option_name = yourls_escape( $option_name );\r
917                 $row = $ydb->get_row( "SELECT `option_value` FROM `$table` WHERE `option_name` = '$option_name' LIMIT 1" );\r
918                 if ( is_object( $row) ) { // Has to be get_row instead of get_var because of funkiness with 0, false, null values\r
919                         $value = $row->option_value;\r
920                 } else { // option does not exist, so we must cache its non-existence\r
921                         $value = $default;\r
922                 }\r
923                 $ydb->option[$option_name] = yourls_maybe_unserialize( $value );\r
924         }\r
925 \r
926         return $ydb->option[$option_name];\r
927 }\r
928 \r
929 // Read all options from DB at once\r
930 function yourls_get_all_options() {\r
931         global $ydb;\r
932         $table = YOURLS_DB_TABLE_OPTIONS;\r
933         \r
934         $allopt = $ydb->get_results("SELECT `option_name`, `option_value` FROM `$table` WHERE 1=1");\r
935         \r
936         foreach( (array)$allopt as $option ) {\r
937                 $ydb->option[$option->option_name] = yourls_maybe_unserialize( $option->option_value );\r
938         }\r
939 }\r
940 \r
941 // Update (add if doesn't exist) an option to DB\r
942 function yourls_update_option( $option_name, $newvalue ) {\r
943         global $ydb;\r
944         $table = YOURLS_DB_TABLE_OPTIONS;\r
945 \r
946         $safe_option_name = yourls_escape( $option_name );\r
947 \r
948         $oldvalue = yourls_get_option( $safe_option_name );\r
949 \r
950         // If the new and old values are the same, no need to update.\r
951         if ( $newvalue === $oldvalue )\r
952                 return false;\r
953 \r
954         if ( false === $oldvalue ) {\r
955                 yourls_add_option( $option_name, $newvalue );\r
956                 return true;\r
957         }\r
958 \r
959         $_newvalue = yourls_escape( yourls_maybe_serialize( $newvalue ) );\r
960 \r
961         $ydb->query( "UPDATE `$table` SET `option_value` = '$_newvalue' WHERE `option_name` = '$option_name'");\r
962 \r
963         if ( $ydb->rows_affected == 1 ) {\r
964                 $ydb->option[$option_name] = $newvalue;\r
965                 return true;\r
966         }\r
967         return false;\r
968 }\r
969 \r
970 // Add an option to the DB\r
971 function yourls_add_option( $name, $value = '' ) {\r
972         global $ydb;\r
973         $table = YOURLS_DB_TABLE_OPTIONS;\r
974         $safe_name = yourls_escape( $name );\r
975 \r
976         // Make sure the option doesn't already exist. We can check the 'notoptions' cache before we ask for a db query\r
977         if ( false !== yourls_get_option( $safe_name ) )\r
978                 return;\r
979 \r
980         $_value = yourls_escape( yourls_maybe_serialize( $value ) );\r
981 \r
982         $ydb->query( "INSERT INTO `$table` (`option_name`, `option_value`) VALUES ('$name', '$_value')" );\r
983         $ydb->option[$name] = $value;\r
984         return;\r
985 }\r
986 \r
987 \r
988 // Delete an option from the DB\r
989 function yourls_delete_option( $name ) {\r
990         global $ydb;\r
991         $table = YOURLS_DB_TABLE_OPTIONS;\r
992         $name = yourls_escape( $name );\r
993 \r
994         // Get the ID, if no ID then return\r
995         $option = $ydb->get_row( "SELECT option_id FROM `$table` WHERE `option_name` = '$name'" );\r
996         if ( is_null($option) || !$option->option_id )\r
997                 return false;\r
998         $ydb->query( "DELETE FROM `$table` WHERE `option_name` = '$name'" );\r
999         return true;\r
1000 }\r
1001 \r
1002 \r
1003 \r
1004 // Serialize data if needed. Stolen from WordPress\r
1005 function yourls_maybe_serialize( $data ) {\r
1006         if ( is_array( $data ) || is_object( $data ) )\r
1007                 return serialize( $data );\r
1008 \r
1009         if ( yourls_is_serialized( $data ) )\r
1010                 return serialize( $data );\r
1011 \r
1012         return $data;\r
1013 }\r
1014 \r
1015 // Check value to find if it was serialized. Stolen from WordPress\r
1016 function yourls_is_serialized( $data ) {\r
1017         // if it isn't a string, it isn't serialized\r
1018         if ( !is_string( $data ) )\r
1019                 return false;\r
1020         $data = trim( $data );\r
1021         if ( 'N;' == $data )\r
1022                 return true;\r
1023         if ( !preg_match( '/^([adObis]):/', $data, $badions ) )\r
1024                 return false;\r
1025         switch ( $badions[1] ) {\r
1026                 case 'a' :\r
1027                 case 'O' :\r
1028                 case 's' :\r
1029                         if ( preg_match( "/^{$badions[1]}:[0-9]+:.*[;}]\$/s", $data ) )\r
1030                                 return true;\r
1031                         break;\r
1032                 case 'b' :\r
1033                 case 'i' :\r
1034                 case 'd' :\r
1035                         if ( preg_match( "/^{$badions[1]}:[0-9.E-]+;\$/", $data ) )\r
1036                                 return true;\r
1037                         break;\r
1038         }\r
1039         return false;\r
1040 }\r
1041 \r
1042 // Unserialize value only if it was serialized. Stolen from WP\r
1043 function yourls_maybe_unserialize( $original ) {\r
1044         if ( yourls_is_serialized( $original ) ) // don't attempt to unserialize data that wasn't serialized going in\r
1045                 return @unserialize( $original );\r
1046         return $original;\r
1047 }\r
1048 \r
1049 // Determine if the current page is private\r
1050 function yourls_is_private() {\r
1051         if (defined('YOURLS_PRIVATE') && YOURLS_PRIVATE == true) {\r
1052 \r
1053                 // Allow overruling of particular pages\r
1054                 $current = basename( $_SERVER["SCRIPT_NAME"] );\r
1055 \r
1056                 switch( $current ) {\r
1057                 \r
1058                 case 'yourls-api.php':\r
1059                         if( !defined('YOURLS_PRIVATE_API') || YOURLS_PRIVATE_API != false )\r
1060                                 return true;\r
1061                         break;\r
1062                                 \r
1063                 case 'yourls-infos.php':\r
1064                         if( !defined('YOURLS_PRIVATE_INFOS') || YOURLS_PRIVATE_INFOS !== false )\r
1065                                 return true;\r
1066                         break;\r
1067                 \r
1068                 default:\r
1069                         return true;\r
1070                         break;\r
1071                 }\r
1072         }\r
1073         \r
1074         return false;\r
1075 }\r
1076 \r
1077 // Show login form if required\r
1078 function yourls_maybe_require_auth() {\r
1079         if( yourls_is_private() )\r
1080                 require_once( dirname(__FILE__).'/auth.php' );\r
1081 }\r
1082 \r
1083 // Return word or words if more than one\r
1084 function yourls_plural( $word, $count=1 ) {\r
1085         return $word . ($count > 1 ? 's' : '');\r
1086 }\r
1087 \r
1088 // Return trimmed string\r
1089 function yourls_trim_long_string( $string, $length = 60, $append = '[...]' ) {\r
1090         if ( strlen( $string ) > $length ) {\r
1091                 $string = substr( $string, 0, $length - strlen( $append ) ) . $append;  \r
1092         }\r
1093         return $string;\r
1094 }\r
1095 \r
1096 // Allow several short URLs for the same long URL ?\r
1097 function yourls_allow_duplicate_longurls() {\r
1098         return ( defined( 'YOURLS_UNIQUE_URLS' ) && YOURLS_UNIQUE_URLS == false );\r
1099 }\r
1100 \r
1101 // Return list of all shorturls associated to the same long URL. Returns NULL or array of keywords.\r
1102 function yourls_get_duplicate_keywords( $longurl ) {\r
1103         if( !yourls_allow_duplicate_longurls() )\r
1104                 return NULL;\r
1105         \r
1106         global $ydb;\r
1107         $longurl = yourls_escape( yourls_sanitize_url($longurl) );\r
1108         $table = YOURLS_DB_TABLE_URL;\r
1109         \r
1110         return $ydb->get_col( "SELECT `keyword` FROM `$table` WHERE `url` = '$longurl'" );\r
1111 }\r
1112 \r
1113 // Check if an IP shortens URL too fast to prevent DB flood. Return true, or die.\r
1114 function yourls_check_IP_flood( $ip = '' ) {\r
1115         if(\r
1116                 ( defined('YOURLS_FLOOD_DELAY_SECONDS') && YOURLS_FLOOD_DELAY_SECONDS === 0 ) ||\r
1117                 !defined('YOURLS_FLOOD_DELAY_SECONDS')\r
1118         )\r
1119                 return true;\r
1120 \r
1121         $ip = ( $ip ? yourls_sanitize_ip( $ip ) : yourls_get_IP() );\r
1122 \r
1123         // Don't throttle whitelist IPs\r
1124         if( defined('YOURLS_FLOOD_IP_WHITELIST' && YOURLS_FLOOD_IP_WHITELIST ) ) {\r
1125                 $whitelist_ips = explode( ',', YOURLS_FLOOD_IP_WHITELIST );\r
1126                 foreach( $whitelist_ips as $whitelist_ip ) {\r
1127                         $whitelist_ip = trim( $whitelist_ip );\r
1128                         if ( $whitelist_ip == $ip )\r
1129                                 return true;\r
1130                 }\r
1131         }\r
1132         \r
1133         // Don't throttle logged in users\r
1134         if( yourls_is_private() ) {\r
1135                  if( yourls_is_valid_user() === true )\r
1136                         return true;\r
1137         }\r
1138 \r
1139         global $ydb;\r
1140         $table = YOURLS_DB_TABLE_URL;\r
1141         \r
1142         $lasttime = $ydb->get_var( "SELECT `timestamp` FROM $table WHERE `ip` = '$ip' ORDER BY `timestamp` DESC LIMIT 1" );\r
1143         if( $lasttime ) {\r
1144                 $now = date( 'U' );\r
1145                 $then = date( 'U', strtotime( $lasttime ) );\r
1146                 if( ( $now - $then ) <= YOURLS_FLOOD_DELAY_SECONDS ) {\r
1147                         // Flood!\r
1148                         yourls_die( 'Too many URLs added too fast. Slow down please.', 'Forbidden', 403 );\r
1149                 }\r
1150         }\r
1151         \r
1152         return true;\r
1153 }\r
1154 \r
1155 // Check if YOURLS is installed\r
1156 function yourls_is_installed() {\r
1157         static $is_installed = false;\r
1158         if ( $is_installed === false ) {\r
1159                 $check_14 = $check_13 = false;\r
1160                 global $ydb;\r
1161                 if( defined('YOURLS_DB_TABLE_NEXTDEC') )\r
1162                         $check_13 = $ydb->get_var('SELECT next_id FROM '.YOURLS_DB_TABLE_NEXTDEC);\r
1163                 $check_14 = yourls_get_option( 'version' );\r
1164                 $is_installed = $check_13 || $check_14;\r
1165         }\r
1166         return $is_installed;\r
1167 }\r
1168 \r
1169 // Compat for PHP < 5.1\r
1170 if ( !function_exists('htmlspecialchars_decode') ) {\r
1171         function htmlspecialchars_decode($text) {\r
1172                 return strtr($text, array_flip(get_html_translation_table(HTML_SPECIALCHARS)));\r
1173         }\r
1174 }\r
1175 \r
1176 // Generate random string of (int)$lenght length and type $type (see function for details)\r
1177 function yourls_rnd_string ( $length = 5, $type = 1 ) {\r
1178         $str = "";\r
1179         $length = intval( $length );\r
1180 \r
1181         // define possible characters\r
1182         switch ( $type ) {\r
1183                 // no vowels to make no offending word, no 0 or 1 to avoid confusion betwee letters & digits. Perfect for passwords.\r
1184                 case '1':\r
1185                         $possible = "23456789bcdfghjkmnpqrstvwxyz";\r
1186                         break;\r
1187                 \r
1188                 // all letters, lowercase\r
1189                 case '2':\r
1190                         $possible = "abcdefghijklmnopqrstuvwxyz";\r
1191                         break;\r
1192                 \r
1193                 // all letters, lowercase + uppercase\r
1194                 case '3':\r
1195                         $possible = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";\r
1196                         break;\r
1197                 \r
1198                 // all digits & letters lowercase \r
1199                 case '4':\r
1200                         $possible = "0123456789abcdefghijklmnopqrstuvwxyz";\r
1201                         break;\r
1202                 \r
1203                 // all digits & letters lowercase + uppercase\r
1204                 case '5':\r
1205                         $possible = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";\r
1206                         break;\r
1207                 \r
1208         }\r
1209 \r
1210         $i = 0;\r
1211         while ($i < $length) {\r
1212         $str .= substr($possible, mt_rand(0, strlen($possible)-1), 1);\r
1213                 $i++;\r
1214         }\r
1215         \r
1216         return $str;\r
1217 }\r
1218 \r
1219 // Return salted string\r
1220 function yourls_salt( $string ) {\r
1221         $salt = defined('YOURLS_COOKIEKEY') ? YOURLS_COOKIEKEY : md5(__FILE__) ;\r
1222         return md5 ($string . $salt);\r
1223 }\r
1224 \r
1225 // Return a time-dependent string for nonce creation\r
1226 function yourls_tick() {\r
1227         return ceil( time() / YOURLS_NONCE_LIFE );\r
1228 }\r
1229 \r
1230 // Create a time limited, action limited and user limited token\r
1231 function yourls_create_nonce( $action = '-1', $user = false ) {\r
1232         if( false == $user )\r
1233                 $user = defined('YOURLS_USER') ? YOURLS_USER : '-1';\r
1234         $tick = yourls_tick();\r
1235         return substr( yourls_salt($tick . $action . $user), 0, 10 );\r
1236 }\r
1237 \r
1238 // Check validity of a nonce (ie time span, user and action match)\r
1239 function yourls_verify_nonce( $nonce, $action = -1, $user = false ) {\r
1240         if( false == $user )\r
1241                 $user = defined('YOURLS_USER') ? YOURLS_USER : '-1';\r
1242         $valid = yourls_create_nonce( $action, $user );\r
1243         \r
1244         return $nonce == $valid ;\r
1245 }\r
1246 \r
1247 // Sanitize a version number\r
1248 function yourls_sanitize_version( $ver ) {\r
1249         return preg_replace( '/[^0-9a-zA-Z-.]/', '', $ver );\r
1250 }\r
1251 \r
1252 // Converts keyword into short link\r
1253 function yourls_link( $keyword = '' ) {\r
1254         return YOURLS_SITE . '/' . yourls_sanitize_keyword( $keyword );\r
1255 }\r
1256 \r
1257 // Check if we're in API mode. Returns bool\r
1258 function yourls_is_API() {\r
1259         if ( defined('YOURLS_API') && YOURLS_API == true )\r
1260                 return true;\r
1261         return false;\r
1262 }\r
1263 \r
1264 // Check if we're in Ajax mode. Returns bool\r
1265 function yourls_is_Ajax() {\r
1266         if ( defined('YOURLS_AJAX') && YOURLS_AJAX == true )\r
1267                 return true;\r
1268         return false;\r
1269 }\r
1270 \r
1271 // Check if we're in GO mode (redirection on yourls-go.php). Returns bool\r
1272 function yourls_is_GO() {\r
1273         if ( defined('YOURLS_GO') && YOURLS_GO == true )\r
1274                 return true;\r
1275         return false;\r
1276 }\r
1277 \r
1278 // Check if we'll need interface display function (ie not API or redirection)\r
1279 function yourls_has_interface() {\r
1280         if( yourls_is_API() or yourls_is_GO() or yourls_is_Ajax() )\r
1281                 return false;\r
1282         return true;\r
1283 }\r
1284 \r
1285 // Check if we're in the admin area. Returns bool\r
1286 function yourls_is_admin() {\r
1287         if ( defined('YOURLS_ADMIN') && YOURLS_ADMIN == true )\r
1288                 return true;\r
1289         return false;\r
1290 }\r
1291 \r
1292 // Check if SSL is required. Returns bool.\r
1293 function yourls_needs_ssl() {\r
1294         if ( defined('YOURLS_ADMIN_SSL') && YOURLS_ADMIN_SSL == true )\r
1295                 return true;\r
1296         return false;\r
1297 }\r
1298 \r
1299 // Return admin link, with SSL preference if applicable.\r
1300 function yourls_admin_url( $page = '' ) {\r
1301         $admin = YOURLS_SITE . '/admin/' . $page;\r
1302         if( defined('YOURLS_ADMIN_SSL') && YOURLS_ADMIN_SSL == true )\r
1303                 $admin = str_replace('http://', 'https://', $admin);\r
1304         return $admin;\r
1305 }\r
1306 \r
1307 // Check if SSL is used, returns bool. Stolen from WP.\r
1308 function yourls_is_ssl() {\r
1309         if ( isset($_SERVER['HTTPS']) ) {\r
1310                 if ( 'on' == strtolower($_SERVER['HTTPS']) )\r
1311                         return true;\r
1312                 if ( '1' == $_SERVER['HTTPS'] )\r
1313                         return true;\r
1314         } elseif ( isset($_SERVER['SERVER_PORT']) && ( '443' == $_SERVER['SERVER_PORT'] ) ) {\r
1315                 return true;\r
1316         }\r
1317         return false;\r
1318 }\r