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