7 // Bump this when updating the SVN repo
\r
8 define('YOURLS_VERSION', '1.1');
\r
10 // function to convert an integer (1337) to a string (3jk). Input integer processed as a string to beat PHP's int max value
\r
11 function yourls_int2string( $id ) {
\r
12 $str = yourls_base2base(trim(strval($id)), 10, YOURLS_URL_CONVERT);
\r
13 if (YOURLS_URL_CONVERT <= 37)
\r
14 $str = strtolower($str);
\r
18 // function to convert a string (3jk) to an integer (1337)
\r
19 function yourls_string2int( $str ) {
\r
20 if (YOURLS_URL_CONVERT <= 37)
\r
21 $str = strtolower($str);
\r
22 return yourls_base2base(trim($str), YOURLS_URL_CONVERT, 10);
\r
25 // Make sure a link id (site.com/1fv) is valid.
\r
26 function yourls_sanitize_string ($in) {
\r
27 if (YOURLS_URL_CONVERT <= 37)
\r
28 $in = strtolower($in);
\r
29 return substr(preg_replace('/[^a-zA-Z0-9]/', '', $in), 0, 12);
\r
32 // make sure there's one and only one 'http://' at the beginning (prevents omitting or pasting a URL right after the default 'http://')
\r
33 function yourls_sanitize_url($url) {
\r
34 if(substr($url, 0, 5) == 'https') {
\r
35 return preg_replace('#^(https://)+#', 'https://', 'https://'.$url);
\r
37 return preg_replace('#^(http://)+#', 'http://', 'http://'.$url);
\r
41 // Make sure an id link is a valid integer (PHP's intval() limits to too small numbers)
\r
42 function yourls_sanitize_int($in) {
\r
43 return ( substr(preg_replace('/[^0-9]/', '', strval($in) ), 0, 20) );
\r
46 // Make sure a integer is safe
\r
47 // Note: this is not checking for integers, since integers on 32bits system are way too limited
\r
48 // TODO: find a way to validate as integer
\r
49 function yourls_intval($in) {
\r
50 return mysql_real_escape_string($in);
\r
54 // Check to see if a given integer id is reserved (ie reserved URL or an existing page)
\r
56 function yourls_is_reserved_id($id) {
\r
57 global $yourls_reserved_URL;
\r
58 $keyword = yourls_int2string( yourls_intval($id) );
\r
59 if ( in_array( $keyword, $yourls_reserved_URL)
\r
60 or file_exists(dirname(dirname(__FILE__))."/pages/$keyword.php")
\r
61 or is_dir(dirname(dirname(__FILE__))."$keyword")
\r
68 // Function: Get IP Address
\r
69 function yourls_get_IP() {
\r
70 if(!empty($_SERVER['HTTP_CLIENT_IP'])) {
\r
71 $ip_address = $_SERVER['HTTP_CLIENT_IP'];
\r
72 } else if(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
\r
73 $ip_address = $_SERVER['HTTP_X_FORWARDED_FOR'];
\r
74 } else if(!empty($_SERVER['REMOTE_ADDR'])) {
\r
75 $ip_address = $_SERVER['REMOTE_ADDR'];
\r
79 if(strpos($ip_address, ',') !== false) {
\r
80 $ip_address = explode(',', $ip_address);
\r
81 $ip_address = $ip_address[0];
\r
86 // Add the "Edit" row
\r
87 function yourls_table_edit_row($id, $db) {
\r
88 $id = yourls_intval($id);
\r
89 $table = YOURLS_DB_TABLE_URL;
\r
90 $url = $db->get_row("SELECT `url` FROM `$table` WHERE `id` = '$id';");
\r
91 $safe_url = stripslashes($url->url);
\r
92 $keyword = yourls_int2string($id);
\r
95 <tr id="edit-$id" class="edit-row">
\r
96 <td colspan="6">Edit: <strong>original URL</strong>:<input type="text" id="edit-url-$id" name="edit-url-$id" value="$safe_url" class="text" size="100" />
\r
97 <strong>short URL</strong>:<input type="text" id="edit-id-$id" name="edit-id-$id" value="$keyword" class="text" size="10" />
\r
99 <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');" /> <input type="button" id="edit-close-$id" name="edit-close-$id" value="X" title="Cancel editing" class="button" onclick="hide_edit('$id');" /></td>
\r
103 $return = '<tr><td colspan="7">Invalid URL ID</td></tr>';
\r
110 function yourls_table_add_row( $id, $keyword, $url, $ip, $clicks, $timestamp ) {
\r
111 $date = date( 'M d, Y H:i', $timestamp+( yourls_HOURS_OFFSET * 3600) );
\r
112 $clicks = number_format($clicks);
\r
113 $www = YOURLS_SITE;
\r
117 <td id="keyword-$id">$keyword</td>
\r
118 <td id="url-$id"><a href="$url" title="$url">$url</a></td>
\r
119 <td id="shorturl-$id"><a href="$www/$keyword" title="$www/$keyword">$www/$keyword</a></td>
\r
120 <td id="timestamp-$id">$date</td>
\r
123 <td class="actions">
\r
124 <input type="button" id="edit-button-$id" name="edit-button" value="Edit" class="button" onclick="edit('$id');" /> <input type="button" id="delete-button-$id" name="delete-button" value="Del" class="button" onclick="remove('$id');" />
\r
130 // Get next id a new link will have if no custom keyword provided
\r
131 function yourls_get_next_decimal($db) {
\r
132 $table = YOURLS_DB_TABLE_NEXTDEC;
\r
133 return $db->get_var("SELECT `next_id` FROM `$table`");
\r
136 // Update id for next link with no custom keyword
\r
137 function yourls_update_next_decimal($int = '', $db) {
\r
138 $int = ( $int == '' ) ? 'next+1' : (int)$int ;
\r
139 $table = YOURLS_DB_TABLE_NEXTDEC;
\r
140 return $db->query("UPDATE `$table` set next_id=$int");
\r
143 // Delete a link in the DB
\r
144 function yourls_delete_link_by_id($id, $db) {
\r
145 $table = YOURLS_DB_TABLE_URL;
\r
146 $id = yourls_intval($id);
\r
147 return $db->query("DELETE FROM `$table` WHERE `id` = $id;");
\r
150 // SQL query to insert a new link in the DB. Needs sanitized data. Returns boolean for success or failure of the inserting
\r
151 function yourls_insert_link_in_db($url, $id, $db) {
\r
152 $table = YOURLS_DB_TABLE_URL;
\r
153 $timestamp = date('Y-m-d H:i:s');
\r
154 $ip = yourls_get_IP();
\r
155 return $db->query("INSERT INTO `$table` VALUES($id, '$url', '$timestamp', '$ip', 0);");
\r
158 // Add a new link in the DB, either with custom keyword, or find one
\r
159 function yourls_add_new_link($url, $keyword = '', $db) {
\r
160 if ( !$url || $url == 'http://' || $url == 'https://' ) {
\r
161 $return['status'] = 'fail';
\r
162 $return['message'] = 'Missing URL input';
\r
166 $table = YOURLS_DB_TABLE_URL;
\r
167 $url = mysql_real_escape_string(yourls_sanitize_url($url));
\r
168 $strip_url = stripslashes($url);
\r
169 $url_exists = $db->get_row("SELECT id,url FROM `$table` WHERE `url` = '".$strip_url."';");
\r
170 $ip = yourls_get_IP();
\r
173 // New URL : store it
\r
174 if( !$url_exists ) {
\r
176 // Custom keyword provided
\r
178 $keyword = yourls_sanitize_string($keyword);
\r
179 if (!yourls_keyword_is_free($keyword, $db)) {
\r
180 // This id either reserved or taken already
\r
181 $return['status'] = 'fail';
\r
182 $return['message'] = 'URL id '.$keyword.' already exists in database or is reserved';
\r
184 // all clear, store !
\r
185 $id = yourls_string2int($keyword);
\r
186 yourls_insert_link_in_db($url, $id, $db);
\r
187 $return['url'] = array('id' => $id, 'keyword' => $keyword, 'url' => $strip_url, 'date' => date('Y-m-d H:i:s'), 'ip' => yourls_get_IP() );
\r
188 $return['status'] = 'success';
\r
189 $return['message'] = $strip_url.' (ID: '.$keyword.') added to database';
\r
190 $return['html'] = yourls_table_add_row( $id, $keyword, $url, yourls_get_IP(), 0, time() );
\r
191 $return['shorturl'] = YOURLS_SITE .'/'. $keyword;
\r
194 // Create random keyword
\r
196 $timestamp = date('Y-m-d H:i:s');
\r
197 $id = yourls_get_next_decimal($db);
\r
199 $add_url = @yourls_insert_link_in_db($url, $id, $db);
\r
200 $free = !yourls_is_reserved_id( $id );
\r
201 $ok = ($free && $add_url);
\r
202 if ( $ok === false && $add_url === 1 ) {
\r
203 // we stored something, but shouldn't have (ie reserved id)
\r
204 $delete = yourls_delete_link_by_id( $id, $db );
\r
205 $return['extra_info'] .= '(deleted '.$id.')';
\r
207 // everything ok, populate needed vars
\r
208 $keyword = yourls_int2string($id);
\r
209 $return['url'] = array('id' => $id, 'keyword' => $keyword, 'url' => $strip_url, 'date' => $timestamp, 'ip' => $ip);
\r
210 $return['status'] = 'success';
\r
211 $return['message'] = $strip_url.' (ID: '.$id.') added to database';
\r
212 $return['html'] = yourls_table_add_row( $id, $keyword, $url, $ip, 0, time() );
\r
213 $return['shorturl'] = YOURLS_SITE .'/'. $keyword;
\r
217 @yourls_update_next_decimal($id, $db);
\r
220 // URL was already stored
\r
221 $return['status'] = 'fail';
\r
222 $return['message'] = $strip_url.' already exists in database';
\r
223 $return['shorturl'] = YOURLS_SITE .'/'. yourls_int2string( $url_exists->id );
\r
231 function yourls_edit_link($url, $id, $keyword='', $db) {
\r
232 $table = YOURLS_DB_TABLE_URL;
\r
233 $url = mysql_real_escape_string(yourls_sanitize_url($url));
\r
234 $id = yourls_intval($id);
\r
235 $strip_url = stripslashes($url);
\r
236 $old_url = $db->get_var("SELECT `url` FROM `$table` WHERE `id` = '".$id."';");
\r
239 // Check if new URL is not here already
\r
240 if ($old_url != $url) {
\r
241 $url_exists = intval($db->get_var("SELECT id FROM `$table` WHERE `url` = '".$strip_url."';"));
\r
243 $url_exists = false;
\r
246 // Check if the new keyword is not here already
\r
247 $newid = ( $keyword ? yourls_string2int($keyword) : $id );
\r
248 if ($newid != $id) {
\r
249 $id_exists = intval($db->get_var("SELECT id FROM `$table` WHERE `id` = '".$newid."';"));
\r
250 $id_free = yourls_keyword_is_free($keyword, $db);
\r
251 $id_is_ok = ($id_exists == 0) && $id_free;
\r
256 // All clear, update
\r
257 if($url_exists == 0 && $id_is_ok ) {
\r
258 $timestamp4screen = date( 'Y M d H:i', time()+( yourls_HOURS_OFFSET * 3600) );
\r
259 $timestamp4db = date('Y-m-d H:i:s', time()+( yourls_HOURS_OFFSET * 3600) );
\r
260 $update_url = $db->query("UPDATE `$table` SET `url` = '$url', `timestamp` = '$timestamp4db', `id` = '$newid' WHERE `id` = $id;");
\r
262 $return['url'] = array('id' => $newid, 'keyword' => $keyword, 'shorturl' => YOURLS_SITE.'/'.$keyword, 'url' => $strip_url, 'date' => $timestamp4screen);
\r
263 $return['status'] = 'success';
\r
264 $return['message'] = 'Link updated in database';
\r
266 $return['status'] = 'fail';
\r
267 $return['message'] = 'Error updating '.$strip_url.' (ID: '.$id.') to database';
\r
272 $return['status'] = 'fail';
\r
273 $return['message'] = 'URL or keyword already exists in database';
\r
280 // Check if keyword id is free (ie not already taken, and not reserved)
\r
281 function yourls_keyword_is_free($str, $db) {
\r
282 $table = YOURLS_DB_TABLE_URL;
\r
283 $id = yourls_string2int($str);
\r
284 if ( yourls_is_reserved_id($id) )
\r
287 $already_exists = intval($db->get_var("SELECT `id` FROM `$table` WHERE `id` = '".$id."';"));
\r
288 if ( $already_exists )
\r
296 function yourls_page($page) {
\r
297 $include = dirname(dirname(__FILE__))."/pages/$page.php";
\r
298 if (!file_exists($include)) {
\r
299 die("Page '$page' not found");
\r
306 function yourls_db_connect() {
\r
307 if (!defined('YOURLS_DB_USER')
\r
308 or !defined('YOURLS_DB_PASS')
\r
309 or !defined('YOURLS_DB_NAME')
\r
310 or !defined('YOURLS_DB_HOST')
\r
311 or !class_exists('ezSQL_mysql')
\r
312 ) die ('DB config/class missing');
\r
314 return new ezSQL_mysql(YOURLS_DB_USER, YOURLS_DB_PASS, YOURLS_DB_NAME, YOURLS_DB_HOST);
\r
317 // Return JSON output. Compatible with PHP prior to 5.2
\r
318 function yourls_json_encode($array) {
\r
319 if (function_exists('json_encode')) {
\r
320 return json_encode($array);
\r
322 require_once(dirname(__FILE__).'/functions-json.php');
\r
323 return yourls_array_to_json($array);
\r
327 // Return XML output.
\r
328 function yourls_xml_encode($array) {
\r
329 require_once(dirname(__FILE__).'/functions-xml.php');
\r
330 $converter= new yourls_array2xml;
\r
331 return $converter->array2xml($array);
\r
334 // Return array for API stat requests
\r
335 function yourls_api_stats( $filter, $limit, $db ) {
\r
336 switch( $filter ) {
\r
338 $sort_by = 'clicks';
\r
339 $sort_order = 'asc';
\r
342 $sort_by = 'timestamp';
\r
343 $sort_order = 'desc';
\r
347 $sort_by = 'clicks';
\r
348 $sort_order = 'desc';
\r
352 $limit = intval( $limit );
\r
353 $table_url = YOURLS_DB_TABLE_URL;
\r
354 $results = $db->get_results("SELECT * FROM $table_url WHERE 1=1 ORDER BY $sort_by $sort_order LIMIT 0, $limit;");
\r
359 foreach ($results as $res) {
\r
360 $return['links']['link_'.$i++] = array(
\r
361 'shorturl' => YOURLS_SITE .'/'. yourls_int2string($res->id),
\r
362 'url' => $res->url,
\r
363 'timestamp' => $res->timestamp,
\r
365 'clicks' => $res->clicks
\r
369 $totals = $db->get_row("SELECT COUNT(id) as c, SUM(clicks) as s FROM $table_url WHERE 1=1");
\r
370 $return['stats'] = array( 'total_links' => $totals->c, 'total_clicks' => $totals->s );
\r
375 // Return API result. Dies after this
\r
376 function yourls_api_output( $mode, $return ) {
\r
379 header('Content-type: application/json');
\r
380 echo yourls_json_encode($return);
\r
384 header('Content-type: application/xml');
\r
385 echo yourls_xml_encode($return);
\r
390 echo $return['shorturl'];
\r