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