1
|
|
|
<?php |
2
|
|
|
/* |
3
|
|
|
* YOURLS |
4
|
|
|
* Function library |
5
|
|
|
*/ |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* Determine the allowed character set in short URLs |
9
|
|
|
* |
10
|
|
|
*/ |
11
|
|
|
function yourls_get_shorturl_charset() { |
12
|
|
|
static $charset = null; |
13
|
|
|
if( $charset !== null ) |
14
|
|
|
return $charset; |
15
|
|
|
|
16
|
|
|
if( defined('YOURLS_URL_CONVERT') && in_array( YOURLS_URL_CONVERT, array( 62, 64 ) ) ) { |
17
|
|
|
$charset = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; |
18
|
|
|
} else { |
19
|
|
|
// defined to 36, or wrongly defined |
20
|
|
|
$charset = '0123456789abcdefghijklmnopqrstuvwxyz'; |
21
|
|
|
} |
22
|
|
|
|
23
|
|
|
$charset = yourls_apply_filter( 'get_shorturl_charset', $charset ); |
24
|
|
|
return $charset; |
25
|
|
|
} |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Make an optimized regexp pattern from a string of characters |
29
|
|
|
* |
30
|
|
|
*/ |
31
|
|
|
function yourls_make_regexp_pattern( $string ) { |
32
|
|
|
$pattern = preg_quote( $string, '@' ); // add @ as an escaped character because @ is used as the regexp delimiter in yourls-loader.php |
33
|
|
|
// Simple benchmarks show that regexp with smarter sequences (0-9, a-z, A-Z...) are not faster or slower than 0123456789 etc... |
34
|
|
|
return $pattern; |
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Is a URL a short URL? Accept either 'http://sho.rt/abc' or 'abc' |
39
|
|
|
* |
40
|
|
|
*/ |
41
|
|
|
function yourls_is_shorturl( $shorturl ) { |
42
|
|
|
// TODO: make sure this function evolves with the feature set. |
43
|
|
|
|
44
|
|
|
$is_short = false; |
45
|
|
|
|
46
|
|
|
// Is $shorturl a URL (http://sho.rt/abc) or a keyword (abc) ? |
47
|
|
|
if( yourls_get_protocol( $shorturl ) ) { |
48
|
|
|
$keyword = yourls_get_relative_url( $shorturl ); |
49
|
|
|
} else { |
50
|
|
|
$keyword = $shorturl; |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
// Check if it's a valid && used keyword |
54
|
|
|
if( $keyword && $keyword == yourls_sanitize_string( $keyword ) && yourls_keyword_is_taken( $keyword ) ) { |
55
|
|
|
$is_short = true; |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
return yourls_apply_filter( 'is_shorturl', $is_short, $shorturl ); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Check to see if a given keyword is reserved (ie reserved URL or an existing page). Returns bool |
63
|
|
|
* |
64
|
|
|
*/ |
65
|
|
|
function yourls_keyword_is_reserved( $keyword ) { |
66
|
|
|
global $yourls_reserved_URL; |
67
|
|
|
$keyword = yourls_sanitize_keyword( $keyword ); |
68
|
|
|
$reserved = false; |
69
|
|
|
|
70
|
|
|
if ( in_array( $keyword, $yourls_reserved_URL) |
71
|
|
|
or file_exists( YOURLS_ABSPATH ."/pages/$keyword.php" ) |
72
|
|
|
or is_dir( YOURLS_ABSPATH ."/$keyword" ) |
73
|
|
|
) |
74
|
|
|
$reserved = true; |
75
|
|
|
|
76
|
|
|
return yourls_apply_filter( 'keyword_is_reserved', $reserved, $keyword ); |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Function: Get client IP Address. Returns a DB safe string. |
81
|
|
|
* |
82
|
|
|
*/ |
83
|
|
|
function yourls_get_IP() { |
84
|
|
|
$ip = ''; |
85
|
|
|
|
86
|
|
|
// Precedence: if set, X-Forwarded-For > HTTP_X_FORWARDED_FOR > HTTP_CLIENT_IP > HTTP_VIA > REMOTE_ADDR |
87
|
|
|
$headers = array( 'X-Forwarded-For', 'HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_VIA', 'REMOTE_ADDR' ); |
88
|
|
|
foreach( $headers as $header ) { |
89
|
|
|
if ( !empty( $_SERVER[ $header ] ) ) { |
90
|
|
|
$ip = $_SERVER[ $header ]; |
91
|
|
|
break; |
92
|
|
|
} |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
// headers can contain multiple IPs (X-Forwarded-For = client, proxy1, proxy2). Take first one. |
96
|
|
|
if ( strpos( $ip, ',' ) !== false ) |
97
|
|
|
$ip = substr( $ip, 0, strpos( $ip, ',' ) ); |
98
|
|
|
|
99
|
|
|
return yourls_apply_filter( 'get_IP', yourls_sanitize_ip( $ip ) ); |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* Get next id a new link will have if no custom keyword provided |
104
|
|
|
* |
105
|
|
|
*/ |
106
|
|
|
function yourls_get_next_decimal() { |
107
|
|
|
return yourls_apply_filter( 'get_next_decimal', (int)yourls_get_option( 'next_id' ) ); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Update id for next link with no custom keyword |
112
|
|
|
* |
113
|
|
|
*/ |
114
|
|
|
function yourls_update_next_decimal( $int = '' ) { |
115
|
|
|
$int = ( $int == '' ) ? yourls_get_next_decimal() + 1 : (int)$int ; |
116
|
|
|
$update = yourls_update_option( 'next_id', $int ); |
117
|
|
|
yourls_do_action( 'update_next_decimal', $int, $update ); |
118
|
|
|
return $update; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* Delete a link in the DB |
123
|
|
|
* |
124
|
|
|
*/ |
125
|
|
View Code Duplication |
function yourls_delete_link_by_keyword( $keyword ) { |
|
|
|
|
126
|
|
|
// Allow plugins to short-circuit the whole function |
127
|
|
|
$pre = yourls_apply_filter( 'shunt_delete_link_by_keyword', null, $keyword ); |
128
|
|
|
if ( null !== $pre ) |
129
|
|
|
return $pre; |
130
|
|
|
|
131
|
|
|
global $ydb; |
132
|
|
|
|
133
|
|
|
$table = YOURLS_DB_TABLE_URL; |
134
|
|
|
$keyword = yourls_sanitize_string($keyword); |
135
|
|
|
$delete = $ydb->fetchAffected("DELETE FROM `$table` WHERE `keyword` = :keyword", array('keyword' => $keyword)); |
136
|
|
|
yourls_do_action( 'delete_link', $keyword, $delete ); |
137
|
|
|
return $delete; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* SQL query to insert a new link in the DB. Returns boolean for success or failure of the inserting |
142
|
|
|
* |
143
|
|
|
*/ |
144
|
|
|
function yourls_insert_link_in_db( $url, $keyword, $title = '' ) { |
145
|
|
|
global $ydb; |
146
|
|
|
|
147
|
|
|
$url = yourls_sanitize_url($url); |
148
|
|
|
$keyword = yourls_sanitize_keyword($keyword); |
149
|
|
|
$title = yourls_sanitize_title($title); |
150
|
|
|
$timestamp = date('Y-m-d H:i:s'); |
151
|
|
|
$ip = yourls_get_IP(); |
152
|
|
|
|
153
|
|
|
$table = YOURLS_DB_TABLE_URL; |
154
|
|
|
$binds = array( |
155
|
|
|
'keyword' => $keyword, |
156
|
|
|
'url' => $url, |
157
|
|
|
'title' => $title, |
158
|
|
|
'timestamp' => $timestamp, |
159
|
|
|
'ip' => $ip, |
160
|
|
|
); |
161
|
|
|
$insert = $ydb->fetchAffected("INSERT INTO `$table` (`keyword`, `url`, `title`, `timestamp`, `ip`, `clicks`) VALUES(:keyword, :url, :title, :timestamp, :ip, 0);", $binds); |
162
|
|
|
|
163
|
|
|
yourls_do_action( 'insert_link', (bool)$insert, $url, $keyword, $title, $timestamp, $ip ); |
164
|
|
|
|
165
|
|
|
return (bool)$insert; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Check if a long URL already exists in the DB. Return NULL (doesn't exist) or an object with URL informations. |
170
|
|
|
* |
171
|
|
|
* @since 1.5.1 |
172
|
|
|
* @param string $url URL to check if already shortened |
173
|
|
|
* @return mixed NULL if does not already exist in DB, or object with URL information as properties (eg keyword, url, title, ...) |
174
|
|
|
*/ |
175
|
|
View Code Duplication |
function yourls_url_exists( $url ) { |
|
|
|
|
176
|
|
|
// Allow plugins to short-circuit the whole function |
177
|
|
|
$pre = yourls_apply_filter( 'shunt_url_exists', false, $url ); |
178
|
|
|
if ( false !== $pre ) |
179
|
|
|
return $pre; |
180
|
|
|
|
181
|
|
|
global $ydb; |
182
|
|
|
$table = YOURLS_DB_TABLE_URL; |
183
|
|
|
$url = yourls_sanitize_url($url); |
184
|
|
|
$url_exists = $ydb->fetchObject("SELECT * FROM `$table` WHERE `url` = :url", array('url'=>$url)); |
185
|
|
|
|
186
|
|
|
if ($url_exists === false) { |
187
|
|
|
$url_exists = NULL; |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
return yourls_apply_filter( 'url_exists', $url_exists, $url ); |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* Add a new link in the DB, either with custom keyword, or find one |
195
|
|
|
* |
196
|
|
|
*/ |
197
|
|
|
function yourls_add_new_link( $url, $keyword = '', $title = '' ) { |
198
|
|
|
// Allow plugins to short-circuit the whole function |
199
|
|
|
$pre = yourls_apply_filter( 'shunt_add_new_link', false, $url, $keyword, $title ); |
200
|
|
|
if ( false !== $pre ) |
201
|
|
|
return $pre; |
202
|
|
|
|
203
|
|
|
$url = yourls_encodeURI( $url ); |
204
|
|
|
$url = yourls_sanitize_url( $url ); |
205
|
|
|
if ( !$url || $url == 'http://' || $url == 'https://' ) { |
206
|
|
|
$return['status'] = 'fail'; |
|
|
|
|
207
|
|
|
$return['code'] = 'error:nourl'; |
208
|
|
|
$return['message'] = yourls__( 'Missing or malformed URL' ); |
209
|
|
|
$return['errorCode'] = '400'; |
210
|
|
|
return yourls_apply_filter( 'add_new_link_fail_nourl', $return, $url, $keyword, $title ); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
// Prevent DB flood |
214
|
|
|
$ip = yourls_get_IP(); |
215
|
|
|
yourls_check_IP_flood( $ip ); |
216
|
|
|
|
217
|
|
|
// Prevent internal redirection loops: cannot shorten a shortened URL |
218
|
|
|
if( yourls_get_relative_url( $url ) ) { |
219
|
|
|
if( yourls_is_shorturl( $url ) ) { |
220
|
|
|
$return['status'] = 'fail'; |
|
|
|
|
221
|
|
|
$return['code'] = 'error:noloop'; |
222
|
|
|
$return['message'] = yourls__( 'URL is a short URL' ); |
223
|
|
|
$return['errorCode'] = '400'; |
224
|
|
|
return yourls_apply_filter( 'add_new_link_fail_noloop', $return, $url, $keyword, $title ); |
225
|
|
|
} |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
yourls_do_action( 'pre_add_new_link', $url, $keyword, $title ); |
229
|
|
|
|
230
|
|
|
$strip_url = stripslashes( $url ); |
231
|
|
|
$return = array(); |
232
|
|
|
|
233
|
|
|
// duplicates allowed or new URL => store it |
234
|
|
|
if( yourls_allow_duplicate_longurls() || !( $url_exists = yourls_url_exists( $url ) ) ) { |
235
|
|
|
|
236
|
|
|
if( isset( $title ) && !empty( $title ) ) { |
237
|
|
|
$title = yourls_sanitize_title( $title ); |
238
|
|
|
} else { |
239
|
|
|
$title = yourls_get_remote_title( $url ); |
240
|
|
|
} |
241
|
|
|
$title = yourls_apply_filter( 'add_new_title', $title, $url, $keyword ); |
242
|
|
|
|
243
|
|
|
// Custom keyword provided |
244
|
|
|
if ( $keyword ) { |
245
|
|
|
|
246
|
|
|
yourls_do_action( 'add_new_link_custom_keyword', $url, $keyword, $title ); |
247
|
|
|
|
248
|
|
|
$keyword = yourls_sanitize_string( $keyword ); |
249
|
|
|
$keyword = yourls_apply_filter( 'custom_keyword', $keyword, $url, $title ); |
250
|
|
View Code Duplication |
if ( !yourls_keyword_is_free( $keyword ) ) { |
|
|
|
|
251
|
|
|
// This shorturl either reserved or taken already |
252
|
|
|
$return['status'] = 'fail'; |
253
|
|
|
$return['code'] = 'error:keyword'; |
254
|
|
|
$return['message'] = yourls_s( 'Short URL %s already exists in database or is reserved', $keyword ); |
255
|
|
|
} else { |
256
|
|
|
// all clear, store ! |
257
|
|
|
yourls_insert_link_in_db( $url, $keyword, $title ); |
258
|
|
|
$return['url'] = array('keyword' => $keyword, 'url' => $strip_url, 'title' => $title, 'date' => date('Y-m-d H:i:s'), 'ip' => $ip ); |
259
|
|
|
$return['status'] = 'success'; |
260
|
|
|
$return['message'] = /* //translators: eg "http://someurl/ added to DB" */ yourls_s( '%s added to database', yourls_trim_long_string( $strip_url ) ); |
261
|
|
|
$return['title'] = $title; |
262
|
|
|
$return['html'] = yourls_table_add_row( $keyword, $url, $title, $ip, 0, time() ); |
263
|
|
|
$return['shorturl'] = YOURLS_SITE .'/'. $keyword; |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
// Create random keyword |
267
|
|
|
} else { |
268
|
|
|
|
269
|
|
|
yourls_do_action( 'add_new_link_create_keyword', $url, $keyword, $title ); |
270
|
|
|
|
271
|
|
|
$timestamp = date( 'Y-m-d H:i:s' ); |
272
|
|
|
$id = yourls_get_next_decimal(); |
273
|
|
|
$ok = false; |
274
|
|
|
do { |
275
|
|
|
$keyword = yourls_int2string( $id ); |
276
|
|
|
$keyword = yourls_apply_filter( 'random_keyword', $keyword, $url, $title ); |
277
|
|
View Code Duplication |
if ( yourls_keyword_is_free($keyword) ) { |
|
|
|
|
278
|
|
|
if (yourls_insert_link_in_db( $url, $keyword, $title )){ |
279
|
|
|
// everything ok, populate needed vars |
280
|
|
|
$return['url'] = array('keyword' => $keyword, 'url' => $strip_url, 'title' => $title, 'date' => $timestamp, 'ip' => $ip ); |
281
|
|
|
$return['status'] = 'success'; |
282
|
|
|
$return['message'] = /* //translators: eg "http://someurl/ added to DB" */ yourls_s( '%s added to database', yourls_trim_long_string( $strip_url ) ); |
283
|
|
|
$return['title'] = $title; |
284
|
|
|
$return['html'] = yourls_table_add_row( $keyword, $url, $title, $ip, 0, time() ); |
285
|
|
|
$return['shorturl'] = YOURLS_SITE .'/'. $keyword; |
286
|
|
|
} else { |
287
|
|
|
// database error, couldnt store result |
288
|
|
|
$return['status'] = 'fail'; |
289
|
|
|
$return['code'] = 'error:db'; |
290
|
|
|
$return['message'] = yourls_s( 'Error saving url to database' ); |
291
|
|
|
} |
292
|
|
|
$ok = true; |
293
|
|
|
} |
294
|
|
|
$id++; |
295
|
|
|
} while ( !$ok ); |
296
|
|
|
@yourls_update_next_decimal( $id ); |
|
|
|
|
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
// URL was already stored |
300
|
|
|
} else { |
301
|
|
|
|
302
|
|
|
yourls_do_action( 'add_new_link_already_stored', $url, $keyword, $title ); |
303
|
|
|
|
304
|
|
|
$return['status'] = 'fail'; |
305
|
|
|
$return['code'] = 'error:url'; |
306
|
|
|
$return['url'] = array( 'keyword' => $url_exists->keyword, 'url' => $strip_url, 'title' => $url_exists->title, 'date' => $url_exists->timestamp, 'ip' => $url_exists->ip, 'clicks' => $url_exists->clicks ); |
307
|
|
|
$return['message'] = /* //translators: eg "http://someurl/ already exists" */ yourls_s( '%s already exists in database', yourls_trim_long_string( $strip_url ) ); |
308
|
|
|
$return['title'] = $url_exists->title; |
309
|
|
|
$return['shorturl'] = YOURLS_SITE .'/'. $url_exists->keyword; |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
yourls_do_action( 'post_add_new_link', $url, $keyword, $title ); |
313
|
|
|
|
314
|
|
|
$return['statusCode'] = 200; // regardless of result, this is still a valid request |
315
|
|
|
return yourls_apply_filter( 'add_new_link', $return, $url, $keyword, $title ); |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* Edit a link |
321
|
|
|
* |
322
|
|
|
*/ |
323
|
|
|
function yourls_edit_link( $url, $keyword, $newkeyword='', $title='' ) { |
324
|
|
|
// Allow plugins to short-circuit the whole function |
325
|
|
|
$pre = yourls_apply_filter( 'shunt_edit_link', null, $keyword, $url, $keyword, $newkeyword, $title ); |
326
|
|
|
if ( null !== $pre ) |
327
|
|
|
return $pre; |
328
|
|
|
|
329
|
|
|
global $ydb; |
330
|
|
|
|
331
|
|
|
$table = YOURLS_DB_TABLE_URL; |
332
|
|
|
$url = yourls_sanitize_url($url); |
333
|
|
|
$keyword = yourls_sanitize_string($keyword); |
334
|
|
|
$title = yourls_sanitize_title($title); |
335
|
|
|
$newkeyword = yourls_sanitize_string($newkeyword); |
336
|
|
|
$strip_url = stripslashes( $url ); |
337
|
|
|
$strip_title = stripslashes( $title ); |
338
|
|
|
|
339
|
|
|
$old_url = $ydb->fetchValue("SELECT `url` FROM `$table` WHERE `keyword` = :keyword", array('keyword' => $keyword)); |
340
|
|
|
|
341
|
|
|
// Check if new URL is not here already |
342
|
|
|
if ( $old_url != $url && !yourls_allow_duplicate_longurls() ) { |
343
|
|
|
$new_url_already_there = intval($ydb->fetchValue("SELECT COUNT(keyword) FROM `$table` WHERE `url` = :url;", array('url' => $url))); |
344
|
|
|
} else { |
345
|
|
|
$new_url_already_there = false; |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
// Check if the new keyword is not here already |
349
|
|
|
if ( $newkeyword != $keyword ) { |
350
|
|
|
$keyword_is_ok = yourls_keyword_is_free( $newkeyword ); |
351
|
|
|
} else { |
352
|
|
|
$keyword_is_ok = true; |
353
|
|
|
} |
354
|
|
|
|
355
|
|
|
yourls_do_action( 'pre_edit_link', $url, $keyword, $newkeyword, $new_url_already_there, $keyword_is_ok ); |
356
|
|
|
|
357
|
|
|
// All clear, update |
358
|
|
|
if ( ( !$new_url_already_there || yourls_allow_duplicate_longurls() ) && $keyword_is_ok ) { |
|
|
|
|
359
|
|
|
$sql = "UPDATE `$table` SET `url` = :url, `keyword` = :newkeyword, `title` = :title WHERE `keyword` = :keyword"; |
360
|
|
|
$binds = array('url' => $url, 'newkeyword' => $newkeyword, 'title' => $title, 'keyword' => $keyword); |
361
|
|
|
$update_url = $ydb->fetchAffected($sql, $binds); |
362
|
|
|
if( $update_url ) { |
363
|
|
|
$return['url'] = array( 'keyword' => $newkeyword, 'shorturl' => YOURLS_SITE.'/'.$newkeyword, 'url' => $strip_url, 'display_url' => yourls_trim_long_string( $strip_url ), 'title' => $strip_title, 'display_title' => yourls_trim_long_string( $strip_title ) ); |
|
|
|
|
364
|
|
|
$return['status'] = 'success'; |
365
|
|
|
$return['message'] = yourls__( 'Link updated in database' ); |
366
|
|
|
} else { |
367
|
|
|
$return['status'] = 'fail'; |
|
|
|
|
368
|
|
|
$return['message'] = /* //translators: "Error updating http://someurl/ (Shorturl: http://sho.rt/blah)" */ yourls_s( 'Error updating %s (Short URL: %s)', yourls_trim_long_string( $strip_url ), $keyword ) ; |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
// Nope |
372
|
|
|
} else { |
373
|
|
|
$return['status'] = 'fail'; |
|
|
|
|
374
|
|
|
$return['message'] = yourls__( 'URL or keyword already exists in database' ); |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
return yourls_apply_filter( 'edit_link', $return, $url, $keyword, $newkeyword, $title, $new_url_already_there, $keyword_is_ok ); |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
/** |
381
|
|
|
* Update a title link (no checks for duplicates etc..) |
382
|
|
|
* |
383
|
|
|
*/ |
384
|
|
View Code Duplication |
function yourls_edit_link_title( $keyword, $title ) { |
|
|
|
|
385
|
|
|
// Allow plugins to short-circuit the whole function |
386
|
|
|
$pre = yourls_apply_filter( 'shunt_edit_link_title', null, $keyword, $title ); |
387
|
|
|
if ( null !== $pre ) |
388
|
|
|
return $pre; |
389
|
|
|
|
390
|
|
|
global $ydb; |
391
|
|
|
|
392
|
|
|
$keyword = yourls_sanitize_keyword( $keyword ); |
393
|
|
|
$title = yourls_sanitize_title( $title ); |
394
|
|
|
|
395
|
|
|
$table = YOURLS_DB_TABLE_URL; |
396
|
|
|
$update = $ydb->fetchAffected("UPDATE `$table` SET `title` = :title WHERE `keyword` = :keyword;", array('title' => $title, 'keyword' => $keyword)); |
397
|
|
|
|
398
|
|
|
return $update; |
399
|
|
|
} |
400
|
|
|
|
401
|
|
|
|
402
|
|
|
/** |
403
|
|
|
* Check if keyword id is free (ie not already taken, and not reserved). Return bool. |
404
|
|
|
* |
405
|
|
|
*/ |
406
|
|
|
function yourls_keyword_is_free( $keyword ) { |
407
|
|
|
$free = true; |
408
|
|
|
if ( yourls_keyword_is_reserved( $keyword ) or yourls_keyword_is_taken( $keyword ) ) |
409
|
|
|
$free = false; |
410
|
|
|
|
411
|
|
|
return yourls_apply_filter( 'keyword_is_free', $free, $keyword ); |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
/** |
415
|
|
|
* Check if a keyword is taken (ie there is already a short URL with this id). Return bool. |
416
|
|
|
* |
417
|
|
|
*/ |
418
|
|
View Code Duplication |
function yourls_keyword_is_taken( $keyword ) { |
|
|
|
|
419
|
|
|
|
420
|
|
|
// Allow plugins to short-circuit the whole function |
421
|
|
|
$pre = yourls_apply_filter( 'shunt_keyword_is_taken', false, $keyword ); |
422
|
|
|
if ( false !== $pre ) |
423
|
|
|
return $pre; |
424
|
|
|
|
425
|
|
|
global $ydb; |
426
|
|
|
$keyword = yourls_sanitize_keyword($keyword); |
427
|
|
|
$taken = false; |
428
|
|
|
$table = YOURLS_DB_TABLE_URL; |
429
|
|
|
|
430
|
|
|
$already_exists = $ydb->fetchValue("SELECT COUNT(`keyword`) FROM `$table` WHERE `keyword` = :keyword;", array('keyword' => $keyword)); |
431
|
|
|
if ( $already_exists ) |
432
|
|
|
$taken = true; |
433
|
|
|
|
434
|
|
|
return yourls_apply_filter( 'keyword_is_taken', $taken, $keyword ); |
435
|
|
|
} |
436
|
|
|
|
437
|
|
|
/** |
438
|
|
|
* Return XML output. |
439
|
|
|
* |
440
|
|
|
*/ |
441
|
|
|
function yourls_xml_encode( $array ) { |
442
|
|
|
require_once( YOURLS_INC.'/functions-xml.php' ); |
443
|
|
|
$converter= new yourls_array2xml; |
444
|
|
|
return $converter->array2xml( $array ); |
445
|
|
|
} |
446
|
|
|
|
447
|
|
|
/** |
448
|
|
|
* Return array of all information associated with keyword. Returns false if keyword not found. Set optional $use_cache to false to force fetching from DB |
449
|
|
|
* |
450
|
|
|
* @since 1.4 |
451
|
|
|
* @param string $keyword Short URL keyword |
452
|
|
|
* @param bool $use_cache Default true, set to false to force fetching from DB |
453
|
|
|
* @return false|object false if not found, object with URL properties if found |
454
|
|
|
*/ |
455
|
|
|
function yourls_get_keyword_infos( $keyword, $use_cache = true ) { |
456
|
|
|
global $ydb; |
457
|
|
|
$keyword = yourls_sanitize_string( $keyword ); |
458
|
|
|
|
459
|
|
|
yourls_do_action( 'pre_get_keyword', $keyword, $use_cache ); |
460
|
|
|
|
461
|
|
|
if( $ydb->has_infos($keyword) && $use_cache === true ) { |
462
|
|
|
return yourls_apply_filter( 'get_keyword_infos', $ydb->get_infos($keyword), $keyword ); |
463
|
|
|
} |
464
|
|
|
|
465
|
|
|
yourls_do_action( 'get_keyword_not_cached', $keyword ); |
466
|
|
|
|
467
|
|
|
$table = YOURLS_DB_TABLE_URL; |
468
|
|
|
$infos = $ydb->fetchObject("SELECT * FROM `$table` WHERE `keyword` = :keyword", array('keyword' => $keyword)); |
469
|
|
|
|
470
|
|
|
if( $infos ) { |
471
|
|
|
$infos = (array)$infos; |
472
|
|
|
$ydb->set_infos($keyword, $infos); |
473
|
|
|
} else { |
474
|
|
|
// is NULL if not found |
475
|
|
|
$infos = false; |
476
|
|
|
$ydb->set_infos($keyword, false); |
477
|
|
|
} |
478
|
|
|
|
479
|
|
|
return yourls_apply_filter( 'get_keyword_infos', $infos, $keyword ); |
480
|
|
|
} |
481
|
|
|
|
482
|
|
|
/** |
483
|
|
|
* Return (string) selected information associated with a keyword. Optional $notfound = string default message if nothing found |
484
|
|
|
* |
485
|
|
|
*/ |
486
|
|
|
function yourls_get_keyword_info( $keyword, $field, $notfound = false ) { |
487
|
|
|
|
488
|
|
|
// Allow plugins to short-circuit the whole function |
489
|
|
|
$pre = yourls_apply_filter( 'shunt_get_keyword_info', false, $keyword, $field, $notfound ); |
490
|
|
|
if ( false !== $pre ) |
491
|
|
|
return $pre; |
492
|
|
|
|
493
|
|
|
$keyword = yourls_sanitize_string( $keyword ); |
494
|
|
|
$infos = yourls_get_keyword_infos( $keyword ); |
495
|
|
|
|
496
|
|
|
$return = $notfound; |
497
|
|
|
if ( isset( $infos[ $field ] ) && $infos[ $field ] !== false ) |
498
|
|
|
$return = $infos[ $field ]; |
499
|
|
|
|
500
|
|
|
return yourls_apply_filter( 'get_keyword_info', $return, $keyword, $field, $notfound ); |
501
|
|
|
} |
502
|
|
|
|
503
|
|
|
/** |
504
|
|
|
* Return title associated with keyword. Optional $notfound = string default message if nothing found |
505
|
|
|
* |
506
|
|
|
*/ |
507
|
|
|
function yourls_get_keyword_title( $keyword, $notfound = false ) { |
508
|
|
|
return yourls_get_keyword_info( $keyword, 'title', $notfound ); |
509
|
|
|
} |
510
|
|
|
|
511
|
|
|
/** |
512
|
|
|
* Return long URL associated with keyword. Optional $notfound = string default message if nothing found |
513
|
|
|
* |
514
|
|
|
*/ |
515
|
|
|
function yourls_get_keyword_longurl( $keyword, $notfound = false ) { |
516
|
|
|
return yourls_get_keyword_info( $keyword, 'url', $notfound ); |
517
|
|
|
} |
518
|
|
|
|
519
|
|
|
/** |
520
|
|
|
* Return number of clicks on a keyword. Optional $notfound = string default message if nothing found |
521
|
|
|
* |
522
|
|
|
*/ |
523
|
|
|
function yourls_get_keyword_clicks( $keyword, $notfound = false ) { |
524
|
|
|
return yourls_get_keyword_info( $keyword, 'clicks', $notfound ); |
525
|
|
|
} |
526
|
|
|
|
527
|
|
|
/** |
528
|
|
|
* Return IP that added a keyword. Optional $notfound = string default message if nothing found |
529
|
|
|
* |
530
|
|
|
*/ |
531
|
|
|
function yourls_get_keyword_IP( $keyword, $notfound = false ) { |
532
|
|
|
return yourls_get_keyword_info( $keyword, 'ip', $notfound ); |
533
|
|
|
} |
534
|
|
|
|
535
|
|
|
/** |
536
|
|
|
* Return timestamp associated with a keyword. Optional $notfound = string default message if nothing found |
537
|
|
|
* |
538
|
|
|
*/ |
539
|
|
|
function yourls_get_keyword_timestamp( $keyword, $notfound = false ) { |
540
|
|
|
return yourls_get_keyword_info( $keyword, 'timestamp', $notfound ); |
541
|
|
|
} |
542
|
|
|
|
543
|
|
|
/** |
544
|
|
|
* Update click count on a short URL. Return 0/1 for error/success. |
545
|
|
|
* |
546
|
|
|
*/ |
547
|
|
|
function yourls_update_clicks( $keyword, $clicks = false ) { |
548
|
|
|
// Allow plugins to short-circuit the whole function |
549
|
|
|
$pre = yourls_apply_filter( 'shunt_update_clicks', false, $keyword, $clicks ); |
550
|
|
|
if ( false !== $pre ) |
551
|
|
|
return $pre; |
552
|
|
|
|
553
|
|
|
global $ydb; |
554
|
|
|
$keyword = yourls_sanitize_string( $keyword ); |
555
|
|
|
$table = YOURLS_DB_TABLE_URL; |
556
|
|
|
if ( $clicks !== false && is_int( $clicks ) && $clicks >= 0 ) |
557
|
|
|
$update = $ydb->fetchAffected( "UPDATE `$table` SET `clicks` = :clicks WHERE `keyword` = :keyword", array('clicks' => $clicks, 'keyword' => $keyword) ); |
558
|
|
|
else |
559
|
|
|
$update = $ydb->fetchAffected( "UPDATE `$table` SET `clicks` = clicks + 1 WHERE `keyword` = :keyword", array('keyword' => $keyword) ); |
560
|
|
|
|
561
|
|
|
yourls_do_action( 'update_clicks', $keyword, $update, $clicks ); |
562
|
|
|
return $update; |
563
|
|
|
} |
564
|
|
|
|
565
|
|
|
/** |
566
|
|
|
* Return array of stats. (string)$filter is 'bottom', 'last', 'rand' or 'top'. (int)$limit is the number of links to return |
567
|
|
|
* |
568
|
|
|
*/ |
569
|
|
|
function yourls_get_stats( $filter = 'top', $limit = 10, $start = 0 ) { |
570
|
|
|
global $ydb; |
571
|
|
|
|
572
|
|
|
switch( $filter ) { |
573
|
|
|
case 'bottom': |
574
|
|
|
$sort_by = 'clicks'; |
575
|
|
|
$sort_order = 'asc'; |
576
|
|
|
break; |
577
|
|
|
case 'last': |
578
|
|
|
$sort_by = 'timestamp'; |
579
|
|
|
$sort_order = 'desc'; |
580
|
|
|
break; |
581
|
|
|
case 'rand': |
582
|
|
|
case 'random': |
583
|
|
|
$sort_by = 'RAND()'; |
584
|
|
|
$sort_order = ''; |
585
|
|
|
break; |
586
|
|
|
case 'top': |
587
|
|
|
default: |
588
|
|
|
$sort_by = 'clicks'; |
589
|
|
|
$sort_order = 'desc'; |
590
|
|
|
break; |
591
|
|
|
} |
592
|
|
|
|
593
|
|
|
// Fetch links |
594
|
|
|
$limit = intval( $limit ); |
595
|
|
|
$start = intval( $start ); |
596
|
|
|
if ( $limit > 0 ) { |
597
|
|
|
|
598
|
|
|
$table_url = YOURLS_DB_TABLE_URL; |
599
|
|
|
$results = $ydb->fetchObjects( "SELECT * FROM `$table_url` WHERE 1=1 ORDER BY `$sort_by` $sort_order LIMIT $start, $limit;" ); |
600
|
|
|
|
601
|
|
|
$return = array(); |
602
|
|
|
$i = 1; |
603
|
|
|
|
604
|
|
|
foreach ( (array)$results as $res ) { |
605
|
|
|
$return['links']['link_'.$i++] = array( |
606
|
|
|
'shorturl' => YOURLS_SITE .'/'. $res->keyword, |
607
|
|
|
'url' => $res->url, |
608
|
|
|
'title' => $res->title, |
609
|
|
|
'timestamp'=> $res->timestamp, |
610
|
|
|
'ip' => $res->ip, |
611
|
|
|
'clicks' => $res->clicks, |
612
|
|
|
); |
613
|
|
|
} |
614
|
|
|
} |
615
|
|
|
|
616
|
|
|
$return['stats'] = yourls_get_db_stats(); |
|
|
|
|
617
|
|
|
|
618
|
|
|
$return['statusCode'] = 200; |
619
|
|
|
|
620
|
|
|
return yourls_apply_filter( 'get_stats', $return, $filter, $limit, $start ); |
621
|
|
|
} |
622
|
|
|
|
623
|
|
|
/** |
624
|
|
|
* Return array of stats. (string)$filter is 'bottom', 'last', 'rand' or 'top'. (int)$limit is the number of links to return |
625
|
|
|
* |
626
|
|
|
*/ |
627
|
|
|
function yourls_get_link_stats( $shorturl ) { |
628
|
|
|
global $ydb; |
629
|
|
|
|
630
|
|
|
$table_url = YOURLS_DB_TABLE_URL; |
631
|
|
|
$shorturl = yourls_sanitize_keyword( $shorturl ); |
632
|
|
|
|
633
|
|
|
$res = $ydb->fetchObject("SELECT * FROM `$table_url` WHERE `keyword` = :keyword", array('keyword' => $shorturl)); |
634
|
|
|
$return = array(); |
|
|
|
|
635
|
|
|
|
636
|
|
|
if( !$res ) { |
637
|
|
|
// non existent link |
638
|
|
|
$return = array( |
639
|
|
|
'statusCode' => 404, |
640
|
|
|
'message' => 'Error: short URL not found', |
641
|
|
|
); |
642
|
|
|
} else { |
643
|
|
|
$return = array( |
644
|
|
|
'statusCode' => 200, |
645
|
|
|
'message' => 'success', |
646
|
|
|
'link' => array( |
647
|
|
|
'shorturl' => YOURLS_SITE .'/'. $res->keyword, |
648
|
|
|
'url' => $res->url, |
649
|
|
|
'title' => $res->title, |
650
|
|
|
'timestamp'=> $res->timestamp, |
651
|
|
|
'ip' => $res->ip, |
652
|
|
|
'clicks' => $res->clicks, |
653
|
|
|
) |
654
|
|
|
); |
655
|
|
|
} |
656
|
|
|
|
657
|
|
|
return yourls_apply_filter( 'get_link_stats', $return, $shorturl ); |
658
|
|
|
} |
659
|
|
|
|
660
|
|
|
/** |
661
|
|
|
* Get total number of URLs and sum of clicks. Input: optional "AND WHERE" clause. Returns array |
662
|
|
|
* |
663
|
|
|
* The $where parameter will contain additional SQL arguments: |
664
|
|
|
* $where['sql'] will concatenate SQL clauses: $where['sql'] = ' AND something = :value AND otherthing < :othervalue'; |
665
|
|
|
* $where['binds'] will hold the (name => value) placeholder pairs: $where['binds'] = array('value' => $value, 'othervalue' => $value2) |
666
|
|
|
* |
667
|
|
|
* @param $where array See comment above |
668
|
|
|
* @return array |
669
|
|
|
*/ |
670
|
|
|
function yourls_get_db_stats( $where = array('sql' => '', 'binds' => array()) ) { |
671
|
|
|
global $ydb; |
672
|
|
|
$table_url = YOURLS_DB_TABLE_URL; |
673
|
|
|
|
674
|
|
|
$totals = $ydb->fetchObject( "SELECT COUNT(keyword) as count, SUM(clicks) as sum FROM `$table_url` WHERE 1=1 " . $where['sql'] , $where['binds'] ); |
675
|
|
|
$return = array( 'total_links' => $totals->count, 'total_clicks' => $totals->sum ); |
676
|
|
|
|
677
|
|
|
return yourls_apply_filter( 'get_db_stats', $return, $where ); |
678
|
|
|
} |
679
|
|
|
|
680
|
|
|
/** |
681
|
|
|
* Get number of SQL queries performed |
682
|
|
|
* |
683
|
|
|
*/ |
684
|
|
|
function yourls_get_num_queries() { |
685
|
|
|
global $ydb; |
686
|
|
|
|
687
|
|
|
return yourls_apply_filter( 'get_num_queries', $ydb->get_num_queries() ); |
688
|
|
|
} |
689
|
|
|
|
690
|
|
|
/** |
691
|
|
|
* Returns a sanitized a user agent string. Given what I found on http://www.user-agents.org/ it should be OK. |
692
|
|
|
* |
693
|
|
|
*/ |
694
|
|
|
function yourls_get_user_agent() { |
695
|
|
|
if ( !isset( $_SERVER['HTTP_USER_AGENT'] ) ) |
696
|
|
|
return '-'; |
697
|
|
|
|
698
|
|
|
$ua = strip_tags( html_entity_decode( $_SERVER['HTTP_USER_AGENT'] )); |
699
|
|
|
$ua = preg_replace('![^0-9a-zA-Z\':., /{}\(\)\[\]\+@&\!\?;_\-=~\*\#]!', '', $ua ); |
700
|
|
|
|
701
|
|
|
return yourls_apply_filter( 'get_user_agent', substr( $ua, 0, 254 ) ); |
702
|
|
|
} |
703
|
|
|
|
704
|
|
|
/** |
705
|
|
|
* Redirect to another page |
706
|
|
|
* |
707
|
|
|
*/ |
708
|
|
|
function yourls_redirect( $location, $code = 301 ) { |
709
|
|
|
yourls_do_action( 'pre_redirect', $location, $code ); |
710
|
|
|
$location = yourls_apply_filter( 'redirect_location', $location, $code ); |
711
|
|
|
$code = yourls_apply_filter( 'redirect_code', $code, $location ); |
712
|
|
|
// Redirect, either properly if possible, or via Javascript otherwise |
713
|
|
|
if( !headers_sent() ) { |
714
|
|
|
yourls_status_header( $code ); |
715
|
|
|
header( "Location: $location" ); |
716
|
|
|
} else { |
717
|
|
|
yourls_redirect_javascript( $location ); |
718
|
|
|
} |
719
|
|
|
die(); |
720
|
|
|
} |
721
|
|
|
|
722
|
|
|
/** |
723
|
|
|
* Set HTTP status header |
724
|
|
|
* |
725
|
|
|
* @since 1.4 |
726
|
|
|
* @param int $code status header code |
727
|
|
|
* @return bool whether header was sent |
728
|
|
|
*/ |
729
|
|
|
function yourls_status_header( $code = 200 ) { |
730
|
|
|
yourls_do_action( 'status_header', $code ); |
731
|
|
|
|
732
|
|
|
if( headers_sent() ) |
733
|
|
|
return false; |
734
|
|
|
|
735
|
|
|
$protocol = $_SERVER['SERVER_PROTOCOL']; |
736
|
|
|
if ( 'HTTP/1.1' != $protocol && 'HTTP/1.0' != $protocol ) |
737
|
|
|
$protocol = 'HTTP/1.0'; |
738
|
|
|
|
739
|
|
|
$code = intval( $code ); |
740
|
|
|
$desc = yourls_get_HTTP_status( $code ); |
741
|
|
|
|
742
|
|
|
@header ("$protocol $code $desc"); // This causes problems on IIS and some FastCGI setups |
|
|
|
|
743
|
|
|
|
744
|
|
|
return true; |
745
|
|
|
} |
746
|
|
|
|
747
|
|
|
/** |
748
|
|
|
* 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) |
749
|
|
|
* |
750
|
|
|
*/ |
751
|
|
|
function yourls_redirect_javascript( $location, $dontwait = true ) { |
752
|
|
|
yourls_do_action( 'pre_redirect_javascript', $location, $dontwait ); |
753
|
|
|
$location = yourls_apply_filter( 'redirect_javascript', $location, $dontwait ); |
754
|
|
|
if( $dontwait ) { |
755
|
|
|
$message = yourls_s( 'if you are not redirected after 10 seconds, please <a href="%s">click here</a>', $location ); |
756
|
|
|
echo <<<REDIR |
757
|
|
|
<script type="text/javascript"> |
758
|
|
|
window.location="$location"; |
759
|
|
|
</script> |
760
|
|
|
<small>($message)</small> |
761
|
|
|
REDIR; |
762
|
|
|
} else { |
763
|
|
|
echo '<p>' . yourls_s( 'Please <a href="%s">click here</a>', $location ) . '</p>'; |
764
|
|
|
} |
765
|
|
|
yourls_do_action( 'post_redirect_javascript', $location ); |
766
|
|
|
} |
767
|
|
|
|
768
|
|
|
/** |
769
|
|
|
* Return a HTTP status code |
770
|
|
|
* |
771
|
|
|
*/ |
772
|
|
|
function yourls_get_HTTP_status( $code ) { |
773
|
|
|
$code = intval( $code ); |
774
|
|
|
$headers_desc = array( |
775
|
|
|
100 => 'Continue', |
776
|
|
|
101 => 'Switching Protocols', |
777
|
|
|
102 => 'Processing', |
778
|
|
|
|
779
|
|
|
200 => 'OK', |
780
|
|
|
201 => 'Created', |
781
|
|
|
202 => 'Accepted', |
782
|
|
|
203 => 'Non-Authoritative Information', |
783
|
|
|
204 => 'No Content', |
784
|
|
|
205 => 'Reset Content', |
785
|
|
|
206 => 'Partial Content', |
786
|
|
|
207 => 'Multi-Status', |
787
|
|
|
226 => 'IM Used', |
788
|
|
|
|
789
|
|
|
300 => 'Multiple Choices', |
790
|
|
|
301 => 'Moved Permanently', |
791
|
|
|
302 => 'Found', |
792
|
|
|
303 => 'See Other', |
793
|
|
|
304 => 'Not Modified', |
794
|
|
|
305 => 'Use Proxy', |
795
|
|
|
306 => 'Reserved', |
796
|
|
|
307 => 'Temporary Redirect', |
797
|
|
|
|
798
|
|
|
400 => 'Bad Request', |
799
|
|
|
401 => 'Unauthorized', |
800
|
|
|
402 => 'Payment Required', |
801
|
|
|
403 => 'Forbidden', |
802
|
|
|
404 => 'Not Found', |
803
|
|
|
405 => 'Method Not Allowed', |
804
|
|
|
406 => 'Not Acceptable', |
805
|
|
|
407 => 'Proxy Authentication Required', |
806
|
|
|
408 => 'Request Timeout', |
807
|
|
|
409 => 'Conflict', |
808
|
|
|
410 => 'Gone', |
809
|
|
|
411 => 'Length Required', |
810
|
|
|
412 => 'Precondition Failed', |
811
|
|
|
413 => 'Request Entity Too Large', |
812
|
|
|
414 => 'Request-URI Too Long', |
813
|
|
|
415 => 'Unsupported Media Type', |
814
|
|
|
416 => 'Requested Range Not Satisfiable', |
815
|
|
|
417 => 'Expectation Failed', |
816
|
|
|
422 => 'Unprocessable Entity', |
817
|
|
|
423 => 'Locked', |
818
|
|
|
424 => 'Failed Dependency', |
819
|
|
|
426 => 'Upgrade Required', |
820
|
|
|
|
821
|
|
|
500 => 'Internal Server Error', |
822
|
|
|
501 => 'Not Implemented', |
823
|
|
|
502 => 'Bad Gateway', |
824
|
|
|
503 => 'Service Unavailable', |
825
|
|
|
504 => 'Gateway Timeout', |
826
|
|
|
505 => 'HTTP Version Not Supported', |
827
|
|
|
506 => 'Variant Also Negotiates', |
828
|
|
|
507 => 'Insufficient Storage', |
829
|
|
|
510 => 'Not Extended' |
830
|
|
|
); |
831
|
|
|
|
832
|
|
|
if ( isset( $headers_desc[$code] ) ) |
833
|
|
|
return $headers_desc[$code]; |
834
|
|
|
else |
835
|
|
|
return ''; |
836
|
|
|
} |
837
|
|
|
|
838
|
|
|
/** |
839
|
|
|
* Log a redirect (for stats) |
840
|
|
|
* |
841
|
|
|
* This function does not check for the existence of a valid keyword, in order to save a query. Make sure the keyword |
842
|
|
|
* exists before calling it. |
843
|
|
|
* |
844
|
|
|
* @since 1.4 |
845
|
|
|
* @param string $keyword short URL keyword |
846
|
|
|
* @return mixed Result of the INSERT query (1 on success) |
847
|
|
|
*/ |
848
|
|
|
function yourls_log_redirect( $keyword ) { |
849
|
|
|
// Allow plugins to short-circuit the whole function |
850
|
|
|
$pre = yourls_apply_filter( 'shunt_log_redirect', false, $keyword ); |
851
|
|
|
if ( false !== $pre ) |
852
|
|
|
return $pre; |
853
|
|
|
|
854
|
|
|
if ( !yourls_do_log_redirect() ) |
855
|
|
|
return true; |
856
|
|
|
|
857
|
|
|
global $ydb; |
858
|
|
|
$table = YOURLS_DB_TABLE_LOG; |
859
|
|
|
$ip = yourls_get_IP(); |
860
|
|
|
$binds = array( |
861
|
|
|
'now' => date( 'Y-m-d H:i:s' ), |
862
|
|
|
'keyword' => yourls_sanitize_string($keyword), |
863
|
|
|
'referrer' => isset($_SERVER['HTTP_REFERER']) ? yourls_sanitize_url_safe($_SERVER['HTTP_REFERER']) : 'direct', |
864
|
|
|
'ua' => yourls_get_user_agent(), |
865
|
|
|
'ip' => $ip, |
866
|
|
|
'location' => yourls_geo_ip_to_countrycode($ip), |
867
|
|
|
); |
868
|
|
|
|
869
|
|
|
return $ydb->fetchAffected("INSERT INTO `$table` (click_time, shorturl, referrer, user_agent, ip_address, country_code) VALUES (:now, :keyword, :referrer, :ua, :ip, :location)", $binds ); |
870
|
|
|
} |
871
|
|
|
|
872
|
|
|
/** |
873
|
|
|
* Check if we want to not log redirects (for stats) |
874
|
|
|
* |
875
|
|
|
*/ |
876
|
|
|
function yourls_do_log_redirect() { |
877
|
|
|
return ( !defined( 'YOURLS_NOSTATS' ) || YOURLS_NOSTATS != true ); |
|
|
|
|
878
|
|
|
} |
879
|
|
|
|
880
|
|
|
/** |
881
|
|
|
* Converts an IP to a 2 letter country code, using GeoIP database if available in includes/geo/ |
882
|
|
|
* |
883
|
|
|
* @since 1.4 |
884
|
|
|
* @param string $ip IP or, if empty string, will be current user IP |
885
|
|
|
* @param string $defaut Default string to return if IP doesn't resolve to a country (malformed, private IP...) |
|
|
|
|
886
|
|
|
* @return string 2 letter country code (eg 'US') or $default |
887
|
|
|
*/ |
888
|
|
|
function yourls_geo_ip_to_countrycode( $ip = '', $default = '' ) { |
889
|
|
|
// Allow plugins to short-circuit the Geo IP API |
890
|
|
|
$location = yourls_apply_filter( 'shunt_geo_ip_to_countrycode', false, $ip, $default ); // at this point $ip can be '', check if your plugin hooks in here |
891
|
|
|
if ( false !== $location ) |
892
|
|
|
return $location; |
893
|
|
|
|
894
|
|
|
if ( $ip == '' ) |
895
|
|
|
$ip = yourls_get_IP(); |
896
|
|
|
|
897
|
|
|
// Allow plugins to stick to YOURLS internals but provide another DB |
898
|
|
|
$db = yourls_apply_filter('geo_ip_path_to_db', YOURLS_INC.'/geo/GeoLite2-Country.mmdb'); |
899
|
|
|
if (!is_readable($db)) { |
900
|
|
|
return $default; |
901
|
|
|
} |
902
|
|
|
|
903
|
|
|
$reader = new \GeoIp2\Database\Reader($db); |
904
|
|
|
try { |
905
|
|
|
$record = $reader->country($ip); |
906
|
|
|
$location = $record->country->isoCode; // eg 'US' |
907
|
|
|
} catch (\Exception $e) { |
908
|
|
|
/* |
909
|
|
|
Unused for now, Exception and $e->getMessage() can be one of : |
910
|
|
|
|
911
|
|
|
- Exception: \GeoIp2\Exception\AddressNotFoundException |
912
|
|
|
When: valid IP not found |
913
|
|
|
Error message: "The address 10.0.0.30 is not in the database" |
914
|
|
|
|
915
|
|
|
- Exception: \InvalidArgumentException |
916
|
|
|
When: IP is not valid, or DB not readable |
917
|
|
|
Error message: "The value "10.0.0.300" is not a valid IP address", "The file "/path/to/GeoLite2-Country.mmdb" does not exist or is not readable" |
918
|
|
|
|
919
|
|
|
- Exception: \MaxMind\Db\Reader\InvalidDatabaseException |
920
|
|
|
When: DB is readable but is corrupt or invalid |
921
|
|
|
Error message: "The MaxMind DB file's search tree is corrupt" |
922
|
|
|
|
923
|
|
|
- or obviously \Exception for any other error (?) |
924
|
|
|
*/ |
925
|
|
|
$location = $default; |
926
|
|
|
} |
927
|
|
|
|
928
|
|
|
return yourls_apply_filter( 'geo_ip_to_countrycode', $location, $ip, $default ); |
929
|
|
|
} |
930
|
|
|
|
931
|
|
|
/** |
932
|
|
|
* Converts a 2 letter country code to long name (ie AU -> Australia) |
933
|
|
|
* |
934
|
|
|
* This associative array is the one used by MaxMind internal functions, it may differ from other lists (eg "A1" does not universally stand for "Anon proxy") |
935
|
|
|
* |
936
|
|
|
* @since 1.4 |
937
|
|
|
* @param string $code 2 letter country code, eg 'FR' |
938
|
|
|
* @return string Country long name (eg 'France') or an empty string if not found |
939
|
|
|
*/ |
940
|
|
|
function yourls_geo_countrycode_to_countryname( $code ) { |
941
|
|
|
// Allow plugins to short-circuit the function |
942
|
|
|
$country = yourls_apply_filter( 'shunt_geo_countrycode_to_countryname', false, $code ); |
943
|
|
|
if ( false !== $country ) |
944
|
|
|
return $country; |
945
|
|
|
|
946
|
|
|
// Weeeeeeeeeeee |
947
|
|
|
$countries = array( |
948
|
|
|
'A1' => 'Anonymous Proxy', 'A2' => 'Satellite Provider', 'AD' => 'Andorra', 'AE' => 'United Arab Emirates', 'AF' => 'Afghanistan', |
949
|
|
|
'AG' => 'Antigua and Barbuda', 'AI' => 'Anguilla', 'AL' => 'Albania', 'AM' => 'Armenia', 'AO' => 'Angola', |
950
|
|
|
'AP' => 'Asia/Pacific Region', 'AQ' => 'Antarctica', 'AR' => 'Argentina', 'AS' => 'American Samoa', 'AT' => 'Austria', |
951
|
|
|
'AU' => 'Australia', 'AW' => 'Aruba', 'AX' => 'Aland Islands', 'AZ' => 'Azerbaijan', 'BA' => 'Bosnia and Herzegovina', |
952
|
|
|
'BB' => 'Barbados', 'BD' => 'Bangladesh', 'BE' => 'Belgium', 'BF' => 'Burkina Faso', 'BG' => 'Bulgaria', |
953
|
|
|
'BH' => 'Bahrain', 'BI' => 'Burundi', 'BJ' => 'Benin', 'BL' => 'Saint Barthelemy', 'BM' => 'Bermuda', |
954
|
|
|
'BN' => 'Brunei Darussalam', 'BO' => 'Bolivia', 'BQ' => 'Bonaire, Saint Eustatius and Saba', 'BR' => 'Brazil', 'BS' => 'Bahamas', |
955
|
|
|
'BT' => 'Bhutan', 'BV' => 'Bouvet Island', 'BW' => 'Botswana', 'BY' => 'Belarus', 'BZ' => 'Belize', |
956
|
|
|
'CA' => 'Canada', 'CC' => 'Cocos (Keeling) Islands', 'CD' => 'Congo, The Democratic Republic of the', 'CF' => 'Central African Republic', 'CG' => 'Congo', |
957
|
|
|
'CH' => 'Switzerland', 'CI' => 'Cote D\'Ivoire', 'CK' => 'Cook Islands', 'CL' => 'Chile', 'CM' => 'Cameroon', |
958
|
|
|
'CN' => 'China', 'CO' => 'Colombia', 'CR' => 'Costa Rica', 'CU' => 'Cuba', 'CV' => 'Cape Verde', |
959
|
|
|
'CW' => 'Curacao', 'CX' => 'Christmas Island', 'CY' => 'Cyprus', 'CZ' => 'Czech Republic', 'DE' => 'Germany', |
960
|
|
|
'DJ' => 'Djibouti', 'DK' => 'Denmark', 'DM' => 'Dominica', 'DO' => 'Dominican Republic', 'DZ' => 'Algeria', |
961
|
|
|
'EC' => 'Ecuador', 'EE' => 'Estonia', 'EG' => 'Egypt', 'EH' => 'Western Sahara', 'ER' => 'Eritrea', |
962
|
|
|
'ES' => 'Spain', 'ET' => 'Ethiopia', 'EU' => 'Europe', 'FI' => 'Finland', 'FJ' => 'Fiji', |
963
|
|
|
'FK' => 'Falkland Islands (Malvinas)', 'FM' => 'Micronesia, Federated States of', 'FO' => 'Faroe Islands', 'FR' => 'France', 'GA' => 'Gabon', |
964
|
|
|
'GB' => 'United Kingdom', 'GD' => 'Grenada', 'GE' => 'Georgia', 'GF' => 'French Guiana', 'GG' => 'Guernsey', |
965
|
|
|
'GH' => 'Ghana', 'GI' => 'Gibraltar', 'GL' => 'Greenland', 'GM' => 'Gambia', 'GN' => 'Guinea', |
966
|
|
|
'GP' => 'Guadeloupe', 'GQ' => 'Equatorial Guinea', 'GR' => 'Greece', 'GS' => 'South Georgia and the South Sandwich Islands', 'GT' => 'Guatemala', |
967
|
|
|
'GU' => 'Guam', 'GW' => 'Guinea-Bissau', 'GY' => 'Guyana', 'HK' => 'Hong Kong', 'HM' => 'Heard Island and McDonald Islands', |
968
|
|
|
'HN' => 'Honduras', 'HR' => 'Croatia', 'HT' => 'Haiti', 'HU' => 'Hungary', 'ID' => 'Indonesia', |
969
|
|
|
'IE' => 'Ireland', 'IL' => 'Israel', 'IM' => 'Isle of Man', 'IN' => 'India', 'IO' => 'British Indian Ocean Territory', |
970
|
|
|
'IQ' => 'Iraq', 'IR' => 'Iran, Islamic Republic of', 'IS' => 'Iceland', 'IT' => 'Italy', 'JE' => 'Jersey', |
971
|
|
|
'JM' => 'Jamaica', 'JO' => 'Jordan', 'JP' => 'Japan', 'KE' => 'Kenya', 'KG' => 'Kyrgyzstan', |
972
|
|
|
'KH' => 'Cambodia', 'KI' => 'Kiribati', 'KM' => 'Comoros', 'KN' => 'Saint Kitts and Nevis', 'KP' => 'Korea, Democratic People\'s Republic of', |
973
|
|
|
'KR' => 'Korea, Republic of', 'KW' => 'Kuwait', 'KY' => 'Cayman Islands', 'KZ' => 'Kazakhstan', 'LA' => 'Lao People\'s Democratic Republic', |
974
|
|
|
'LB' => 'Lebanon', 'LC' => 'Saint Lucia', 'LI' => 'Liechtenstein', 'LK' => 'Sri Lanka', 'LR' => 'Liberia', |
975
|
|
|
'LS' => 'Lesotho', 'LT' => 'Lithuania', 'LU' => 'Luxembourg', 'LV' => 'Latvia', 'LY' => 'Libya', |
976
|
|
|
'MA' => 'Morocco', 'MC' => 'Monaco', 'MD' => 'Moldova, Republic of', 'ME' => 'Montenegro', 'MF' => 'Saint Martin', |
977
|
|
|
'MG' => 'Madagascar', 'MH' => 'Marshall Islands', 'MK' => 'Macedonia', 'ML' => 'Mali', 'MM' => 'Myanmar', |
978
|
|
|
'MN' => 'Mongolia', 'MO' => 'Macau', 'MP' => 'Northern Mariana Islands', 'MQ' => 'Martinique', 'MR' => 'Mauritania', |
979
|
|
|
'MS' => 'Montserrat', 'MT' => 'Malta', 'MU' => 'Mauritius', 'MV' => 'Maldives', 'MW' => 'Malawi', |
980
|
|
|
'MX' => 'Mexico', 'MY' => 'Malaysia', 'MZ' => 'Mozambique', 'NA' => 'Namibia', 'NC' => 'New Caledonia', |
981
|
|
|
'NE' => 'Niger', 'NF' => 'Norfolk Island', 'NG' => 'Nigeria', 'NI' => 'Nicaragua', 'NL' => 'Netherlands', |
982
|
|
|
'NO' => 'Norway', 'NP' => 'Nepal', 'NR' => 'Nauru', 'NU' => 'Niue', 'NZ' => 'New Zealand', |
983
|
|
|
'O1' => 'Other', 'OM' => 'Oman', 'PA' => 'Panama', 'PE' => 'Peru', 'PF' => 'French Polynesia', |
984
|
|
|
'PG' => 'Papua New Guinea', 'PH' => 'Philippines', 'PK' => 'Pakistan', 'PL' => 'Poland', 'PM' => 'Saint Pierre and Miquelon', |
985
|
|
|
'PN' => 'Pitcairn Islands', 'PR' => 'Puerto Rico', 'PS' => 'Palestinian Territory', 'PT' => 'Portugal', 'PW' => 'Palau', |
986
|
|
|
'PY' => 'Paraguay', 'QA' => 'Qatar', 'RE' => 'Reunion', 'RO' => 'Romania', 'RS' => 'Serbia', |
987
|
|
|
'RU' => 'Russian Federation', 'RW' => 'Rwanda', 'SA' => 'Saudi Arabia', 'SB' => 'Solomon Islands', 'SC' => 'Seychelles', |
988
|
|
|
'SD' => 'Sudan', 'SE' => 'Sweden', 'SG' => 'Singapore', 'SH' => 'Saint Helena', 'SI' => 'Slovenia', |
989
|
|
|
'SJ' => 'Svalbard and Jan Mayen', 'SK' => 'Slovakia', 'SL' => 'Sierra Leone', 'SM' => 'San Marino', 'SN' => 'Senegal', |
990
|
|
|
'SO' => 'Somalia', 'SR' => 'Suriname', 'SS' => 'South Sudan', 'ST' => 'Sao Tome and Principe', 'SV' => 'El Salvador', |
991
|
|
|
'SX' => 'Sint Maarten (Dutch part)', 'SY' => 'Syrian Arab Republic', 'SZ' => 'Swaziland', 'TC' => 'Turks and Caicos Islands', 'TD' => 'Chad', |
992
|
|
|
'TF' => 'French Southern Territories', 'TG' => 'Togo', 'TH' => 'Thailand', 'TJ' => 'Tajikistan', 'TK' => 'Tokelau', |
993
|
|
|
'TL' => 'Timor-Leste', 'TM' => 'Turkmenistan', 'TN' => 'Tunisia', 'TO' => 'Tonga', 'TR' => 'Turkey', |
994
|
|
|
'TT' => 'Trinidad and Tobago', 'TV' => 'Tuvalu', 'TW' => 'Taiwan', 'TZ' => 'Tanzania, United Republic of', 'UA' => 'Ukraine', |
995
|
|
|
'UG' => 'Uganda', 'UM' => 'United States Minor Outlying Islands', 'US' => 'United States', 'UY' => 'Uruguay', 'UZ' => 'Uzbekistan', |
996
|
|
|
'VA' => 'Holy See (Vatican City State)', 'VC' => 'Saint Vincent and the Grenadines', 'VE' => 'Venezuela', 'VG' => 'Virgin Islands, British', 'VI' => 'Virgin Islands, U.S.', |
997
|
|
|
'VN' => 'Vietnam', 'VU' => 'Vanuatu', 'WF' => 'Wallis and Futuna', 'WS' => 'Samoa', 'YE' => 'Yemen', |
998
|
|
|
'YT' => 'Mayotte', 'ZA' => 'South Africa', 'ZM' => 'Zambia', 'ZW' => 'Zimbabwe', |
999
|
|
|
); |
1000
|
|
|
|
1001
|
|
|
$code = strtoupper($code); |
1002
|
|
|
if(array_key_exists($code, $countries)) { |
1003
|
|
|
$name = $countries[$code]; |
1004
|
|
|
} else { |
1005
|
|
|
$name = ''; |
1006
|
|
|
} |
1007
|
|
|
|
1008
|
|
|
return yourls_apply_filter( 'geo_countrycode_to_countryname', $name ); |
1009
|
|
|
} |
1010
|
|
|
|
1011
|
|
|
/** |
1012
|
|
|
* Return flag URL from 2 letter country code |
1013
|
|
|
* |
1014
|
|
|
*/ |
1015
|
|
|
function yourls_geo_get_flag( $code ) { |
1016
|
|
|
if( file_exists( YOURLS_INC.'/geo/flags/flag_'.strtolower($code).'.gif' ) ) { |
1017
|
|
|
$img = yourls_match_current_protocol( YOURLS_SITE.'/includes/geo/flags/flag_'.( strtolower( $code ) ).'.gif' ); |
1018
|
|
|
} else { |
1019
|
|
|
$img = false; |
1020
|
|
|
} |
1021
|
|
|
return yourls_apply_filter( 'geo_get_flag', $img, $code ); |
1022
|
|
|
} |
1023
|
|
|
|
1024
|
|
|
|
1025
|
|
|
/** |
1026
|
|
|
* Check if an upgrade is needed |
1027
|
|
|
* |
1028
|
|
|
*/ |
1029
|
|
|
function yourls_upgrade_is_needed() { |
1030
|
|
|
// check YOURLS_DB_VERSION exist && match values stored in YOURLS_DB_TABLE_OPTIONS |
1031
|
|
|
list( $currentver, $currentsql ) = yourls_get_current_version_from_sql(); |
|
|
|
|
1032
|
|
|
if( $currentsql < YOURLS_DB_VERSION ) |
1033
|
|
|
return true; |
1034
|
|
|
|
1035
|
|
|
return false; |
1036
|
|
|
} |
1037
|
|
|
|
1038
|
|
|
/** |
1039
|
|
|
* Get current version & db version as stored in the options DB. Prior to 1.4 there's no option table. |
1040
|
|
|
* |
1041
|
|
|
*/ |
1042
|
|
|
function yourls_get_current_version_from_sql() { |
1043
|
|
|
$currentver = yourls_get_option( 'version' ); |
1044
|
|
|
$currentsql = yourls_get_option( 'db_version' ); |
1045
|
|
|
|
1046
|
|
|
// Values if version is 1.3 |
1047
|
|
|
if( !$currentver ) |
1048
|
|
|
$currentver = '1.3'; |
1049
|
|
|
if( !$currentsql ) |
1050
|
|
|
$currentsql = '100'; |
1051
|
|
|
|
1052
|
|
|
return array( $currentver, $currentsql); |
1053
|
|
|
} |
1054
|
|
|
|
1055
|
|
|
/** |
1056
|
|
|
* Read an option from DB (or from cache if available). Return value or $default if not found |
1057
|
|
|
* |
1058
|
|
|
* Pretty much stolen from WordPress |
1059
|
|
|
* |
1060
|
|
|
* @since 1.4 |
1061
|
|
|
* @param string $option_name Option name. Expected to not be SQL-escaped. |
1062
|
|
|
* @param mixed $default Optional value to return if option doesn't exist. Default false. |
1063
|
|
|
* @return mixed Value set for the option. |
1064
|
|
|
*/ |
1065
|
|
|
function yourls_get_option( $option_name, $default = false ) { |
1066
|
|
|
// Allow plugins to short-circuit options |
1067
|
|
|
$pre = yourls_apply_filter( 'shunt_option_'.$option_name, false ); |
1068
|
|
|
if ( false !== $pre ) |
1069
|
|
|
return $pre; |
1070
|
|
|
|
1071
|
|
|
global $ydb; |
1072
|
|
|
$option = new \YOURLS\Database\Options($ydb); |
1073
|
|
|
$value = $option->get($option_name, $default); |
1074
|
|
|
|
1075
|
|
|
return yourls_apply_filter( 'get_option_'.$option_name, $value ); |
1076
|
|
|
} |
1077
|
|
|
|
1078
|
|
|
/** |
1079
|
|
|
* Read all options from DB at once |
1080
|
|
|
* |
1081
|
|
|
* The goal is to read all options at once and then populate array $ydb->option, to prevent further |
1082
|
|
|
* SQL queries if we need to read an option value later. |
1083
|
|
|
* It's also a simple check whether YOURLS is installed or not (no option = assuming not installed) after |
1084
|
|
|
* a check for DB server reachability has been performed |
1085
|
|
|
* |
1086
|
|
|
* @since 1.4 |
1087
|
|
|
*/ |
1088
|
|
|
function yourls_get_all_options() { |
1089
|
|
|
// Allow plugins to short-circuit all options. (Note: regular plugins are loaded after all options) |
1090
|
|
|
$pre = yourls_apply_filter( 'shunt_all_options', false ); |
1091
|
|
|
if ( false !== $pre ) |
1092
|
|
|
return $pre; |
1093
|
|
|
|
1094
|
|
|
global $ydb; |
1095
|
|
|
$options = new \YOURLS\Database\Options($ydb); |
1096
|
|
|
|
1097
|
|
|
if($options->get_all_options() === false) { |
1098
|
|
|
// Zero option found: either YOURLS is not installed or DB server is dead |
1099
|
|
|
if( !yourls_is_db_alive() ) { |
1100
|
|
|
yourls_db_dead(); // YOURLS will die here |
1101
|
|
|
} |
1102
|
|
|
yourls_set_installed(false); |
1103
|
|
|
} |
1104
|
|
|
|
1105
|
|
|
yourls_set_installed(true); |
1106
|
|
|
} |
1107
|
|
|
|
1108
|
|
|
/** |
1109
|
|
|
* Update (add if doesn't exist) an option to DB |
1110
|
|
|
* |
1111
|
|
|
* Pretty much stolen from WordPress |
1112
|
|
|
* |
1113
|
|
|
* @since 1.4 |
1114
|
|
|
* @param string $option_name Option name. Expected to not be SQL-escaped. |
1115
|
|
|
* @param mixed $newvalue Option value. Must be serializable if non-scalar. Expected to not be SQL-escaped. |
1116
|
|
|
* @return bool False if value was not updated, true otherwise. |
1117
|
|
|
*/ |
1118
|
|
|
function yourls_update_option( $option_name, $newvalue ) { |
1119
|
|
|
global $ydb; |
1120
|
|
|
|
1121
|
|
|
$option = new \YOURLS\Database\Options($ydb); |
1122
|
|
|
$update = $option->update($option_name, $newvalue); |
1123
|
|
|
|
1124
|
|
|
return $update; |
1125
|
|
|
} |
1126
|
|
|
|
1127
|
|
|
/** |
1128
|
|
|
* Add an option to the DB |
1129
|
|
|
* |
1130
|
|
|
* Pretty much stolen from WordPress |
1131
|
|
|
* |
1132
|
|
|
* @since 1.4 |
1133
|
|
|
* @param string $name Name of option to add. Expected to not be SQL-escaped. |
1134
|
|
|
* @param mixed $value Optional option value. Must be serializable if non-scalar. Expected to not be SQL-escaped. |
1135
|
|
|
* @return bool False if option was not added and true otherwise. |
1136
|
|
|
*/ |
1137
|
|
|
function yourls_add_option( $name, $value = '' ) { |
1138
|
|
|
global $ydb; |
1139
|
|
|
|
1140
|
|
|
$option = new \YOURLS\Database\Options($ydb); |
1141
|
|
|
$add = $option->add($name, $value); |
1142
|
|
|
|
1143
|
|
|
return $add; |
1144
|
|
|
} |
1145
|
|
|
|
1146
|
|
|
|
1147
|
|
|
/** |
1148
|
|
|
* Delete an option from the DB |
1149
|
|
|
* |
1150
|
|
|
* Pretty much stolen from WordPress |
1151
|
|
|
* |
1152
|
|
|
* @since 1.4 |
1153
|
|
|
* @param string $name Option name to delete. Expected to not be SQL-escaped. |
1154
|
|
|
* @return bool True, if option is successfully deleted. False on failure. |
1155
|
|
|
*/ |
1156
|
|
|
function yourls_delete_option( $name ) { |
1157
|
|
|
global $ydb; |
1158
|
|
|
|
1159
|
|
|
$option = new \YOURLS\Database\Options($ydb); |
1160
|
|
|
$delete = $option->delete($name); |
1161
|
|
|
|
1162
|
|
|
return $delete; |
1163
|
|
|
} |
1164
|
|
|
|
1165
|
|
|
|
1166
|
|
|
/** |
1167
|
|
|
* Serialize data if needed. Stolen from WordPress |
1168
|
|
|
* |
1169
|
|
|
* @since 1.4 |
1170
|
|
|
* @param mixed $data Data that might be serialized. |
1171
|
|
|
* @return mixed A scalar data |
1172
|
|
|
*/ |
1173
|
|
|
function yourls_maybe_serialize( $data ) { |
1174
|
|
|
if ( is_array( $data ) || is_object( $data ) ) |
1175
|
|
|
return serialize( $data ); |
1176
|
|
|
|
1177
|
|
|
if ( yourls_is_serialized( $data, false ) ) |
1178
|
|
|
return serialize( $data ); |
1179
|
|
|
|
1180
|
|
|
return $data; |
1181
|
|
|
} |
1182
|
|
|
|
1183
|
|
|
/** |
1184
|
|
|
* Check value to find if it was serialized. Stolen from WordPress |
1185
|
|
|
* |
1186
|
|
|
* @since 1.4 |
1187
|
|
|
* @param mixed $data Value to check to see if was serialized. |
1188
|
|
|
* @param bool $strict Optional. Whether to be strict about the end of the string. Defaults true. |
1189
|
|
|
* @return bool False if not serialized and true if it was. |
1190
|
|
|
*/ |
1191
|
|
|
function yourls_is_serialized( $data, $strict = true ) { |
1192
|
|
|
// if it isn't a string, it isn't serialized |
1193
|
|
|
if ( ! is_string( $data ) ) |
1194
|
|
|
return false; |
1195
|
|
|
$data = trim( $data ); |
1196
|
|
|
if ( 'N;' == $data ) |
1197
|
|
|
return true; |
1198
|
|
|
$length = strlen( $data ); |
1199
|
|
|
if ( $length < 4 ) |
1200
|
|
|
return false; |
1201
|
|
|
if ( ':' !== $data[1] ) |
1202
|
|
|
return false; |
1203
|
|
|
if ( $strict ) { |
1204
|
|
|
$lastc = $data[ $length - 1 ]; |
1205
|
|
|
if ( ';' !== $lastc && '}' !== $lastc ) |
1206
|
|
|
return false; |
1207
|
|
|
} else { |
1208
|
|
|
$semicolon = strpos( $data, ';' ); |
1209
|
|
|
$brace = strpos( $data, '}' ); |
1210
|
|
|
// Either ; or } must exist. |
1211
|
|
|
if ( false === $semicolon && false === $brace ) |
1212
|
|
|
return false; |
1213
|
|
|
// But neither must be in the first X characters. |
1214
|
|
|
if ( false !== $semicolon && $semicolon < 3 ) |
1215
|
|
|
return false; |
1216
|
|
|
if ( false !== $brace && $brace < 4 ) |
1217
|
|
|
return false; |
1218
|
|
|
} |
1219
|
|
|
$token = $data[0]; |
1220
|
|
|
switch ( $token ) { |
1221
|
|
|
case 's' : |
1222
|
|
|
if ( $strict ) { |
1223
|
|
|
if ( '"' !== $data[ $length - 2 ] ) |
1224
|
|
|
return false; |
1225
|
|
|
} elseif ( false === strpos( $data, '"' ) ) { |
1226
|
|
|
return false; |
1227
|
|
|
} |
1228
|
|
|
// or else fall through |
1229
|
|
|
case 'a' : |
1230
|
|
|
case 'O' : |
1231
|
|
|
return (bool) preg_match( "/^{$token}:[0-9]+:/s", $data ); |
1232
|
|
|
case 'b' : |
1233
|
|
|
case 'i' : |
1234
|
|
|
case 'd' : |
1235
|
|
|
$end = $strict ? '$' : ''; |
1236
|
|
|
return (bool) preg_match( "/^{$token}:[0-9.E-]+;$end/", $data ); |
1237
|
|
|
} |
1238
|
|
|
return false; |
1239
|
|
|
} |
1240
|
|
|
|
1241
|
|
|
/** |
1242
|
|
|
* Unserialize value only if it was serialized. Stolen from WP |
1243
|
|
|
* |
1244
|
|
|
* @since 1.4 |
1245
|
|
|
* @param string $original Maybe unserialized original, if is needed. |
1246
|
|
|
* @return mixed Unserialized data can be any type. |
1247
|
|
|
*/ |
1248
|
|
|
function yourls_maybe_unserialize( $original ) { |
1249
|
|
|
if ( yourls_is_serialized( $original ) ) // don't attempt to unserialize data that wasn't serialized going in |
1250
|
|
|
return @unserialize( $original ); |
1251
|
|
|
return $original; |
1252
|
|
|
} |
1253
|
|
|
|
1254
|
|
|
/** |
1255
|
|
|
* Determine if the current page is private |
1256
|
|
|
* |
1257
|
|
|
*/ |
1258
|
|
|
function yourls_is_private() { |
1259
|
|
|
$private = false; |
1260
|
|
|
|
1261
|
|
|
if ( defined('YOURLS_PRIVATE') && YOURLS_PRIVATE == true ) { |
|
|
|
|
1262
|
|
|
|
1263
|
|
|
// Allow overruling for particular pages: |
1264
|
|
|
|
1265
|
|
|
// API |
1266
|
|
|
if( yourls_is_API() ) { |
1267
|
|
|
if( !defined('YOURLS_PRIVATE_API') || YOURLS_PRIVATE_API != false ) |
1268
|
|
|
$private = true; |
1269
|
|
|
|
1270
|
|
|
// Infos |
1271
|
|
|
} elseif( yourls_is_infos() ) { |
1272
|
|
|
if( !defined('YOURLS_PRIVATE_INFOS') || YOURLS_PRIVATE_INFOS !== false ) |
1273
|
|
|
$private = true; |
1274
|
|
|
|
1275
|
|
|
// Others |
1276
|
|
|
} else { |
1277
|
|
|
$private = true; |
1278
|
|
|
} |
1279
|
|
|
|
1280
|
|
|
} |
1281
|
|
|
|
1282
|
|
|
return yourls_apply_filter( 'is_private', $private ); |
1283
|
|
|
} |
1284
|
|
|
|
1285
|
|
|
/** |
1286
|
|
|
* Show login form if required |
1287
|
|
|
* |
1288
|
|
|
*/ |
1289
|
|
|
function yourls_maybe_require_auth() { |
1290
|
|
|
if( yourls_is_private() ) { |
1291
|
|
|
yourls_do_action( 'require_auth' ); |
1292
|
|
|
require_once( YOURLS_INC.'/auth.php' ); |
1293
|
|
|
} else { |
1294
|
|
|
yourls_do_action( 'require_no_auth' ); |
1295
|
|
|
} |
1296
|
|
|
} |
1297
|
|
|
|
1298
|
|
|
/** |
1299
|
|
|
* Allow several short URLs for the same long URL ? |
1300
|
|
|
* |
1301
|
|
|
*/ |
1302
|
|
|
function yourls_allow_duplicate_longurls() { |
1303
|
|
|
// special treatment if API to check for WordPress plugin requests |
1304
|
|
|
if( yourls_is_API() ) { |
1305
|
|
|
if ( isset($_REQUEST['source']) && $_REQUEST['source'] == 'plugin' ) |
1306
|
|
|
return false; |
1307
|
|
|
} |
1308
|
|
|
return ( defined( 'YOURLS_UNIQUE_URLS' ) && YOURLS_UNIQUE_URLS == false ); |
|
|
|
|
1309
|
|
|
} |
1310
|
|
|
|
1311
|
|
|
/** |
1312
|
|
|
* Return array of keywords that redirect to the submitted long URL |
1313
|
|
|
* |
1314
|
|
|
* @since 1.7 |
1315
|
|
|
* @param string $longurl long url |
1316
|
|
|
* @param string $order Optional SORT order (can be 'ASC' or 'DESC') |
1317
|
|
|
* @return array array of keywords |
1318
|
|
|
*/ |
1319
|
|
|
function yourls_get_longurl_keywords( $longurl, $order = 'ASC' ) { |
1320
|
|
|
global $ydb; |
1321
|
|
|
$longurl = yourls_sanitize_url($longurl); |
1322
|
|
|
$table = YOURLS_DB_TABLE_URL; |
1323
|
|
|
$sql = "SELECT `keyword` FROM `$table` WHERE `url` = :url"; |
1324
|
|
|
|
1325
|
|
|
if (in_array($order, array('ASC','DESC'))) { |
1326
|
|
|
$sql .= " ORDER BY `keyword` ".$order; |
1327
|
|
|
} |
1328
|
|
|
|
1329
|
|
|
return yourls_apply_filter( 'get_longurl_keywords', $ydb->fetchCol($sql, array('url'=>$longurl)), $longurl ); |
1330
|
|
|
} |
1331
|
|
|
|
1332
|
|
|
/** |
1333
|
|
|
* Check if an IP shortens URL too fast to prevent DB flood. Return true, or die. |
1334
|
|
|
* |
1335
|
|
|
*/ |
1336
|
|
|
function yourls_check_IP_flood( $ip = '' ) { |
1337
|
|
|
|
1338
|
|
|
// Allow plugins to short-circuit the whole function |
1339
|
|
|
$pre = yourls_apply_filter( 'shunt_check_IP_flood', false, $ip ); |
1340
|
|
|
if ( false !== $pre ) |
1341
|
|
|
return $pre; |
1342
|
|
|
|
1343
|
|
|
yourls_do_action( 'pre_check_ip_flood', $ip ); // at this point $ip can be '', check it if your plugin hooks in here |
1344
|
|
|
|
1345
|
|
|
// Raise white flag if installing or if no flood delay defined |
1346
|
|
|
if( |
1347
|
|
|
( defined('YOURLS_FLOOD_DELAY_SECONDS') && YOURLS_FLOOD_DELAY_SECONDS === 0 ) || |
1348
|
|
|
!defined('YOURLS_FLOOD_DELAY_SECONDS') || |
1349
|
|
|
yourls_is_installing() |
1350
|
|
|
) |
1351
|
|
|
return true; |
1352
|
|
|
|
1353
|
|
|
// Don't throttle logged in users |
1354
|
|
|
if( yourls_is_private() ) { |
1355
|
|
|
if( yourls_is_valid_user() === true ) |
1356
|
|
|
return true; |
1357
|
|
|
} |
1358
|
|
|
|
1359
|
|
|
// Don't throttle whitelist IPs |
1360
|
|
|
if( defined( 'YOURLS_FLOOD_IP_WHITELIST' ) && YOURLS_FLOOD_IP_WHITELIST ) { |
1361
|
|
|
$whitelist_ips = explode( ',', YOURLS_FLOOD_IP_WHITELIST ); |
1362
|
|
|
foreach( (array)$whitelist_ips as $whitelist_ip ) { |
1363
|
|
|
$whitelist_ip = trim( $whitelist_ip ); |
1364
|
|
|
if ( $whitelist_ip == $ip ) |
1365
|
|
|
return true; |
1366
|
|
|
} |
1367
|
|
|
} |
1368
|
|
|
|
1369
|
|
|
$ip = ( $ip ? yourls_sanitize_ip( $ip ) : yourls_get_IP() ); |
1370
|
|
|
|
1371
|
|
|
yourls_do_action( 'check_ip_flood', $ip ); |
1372
|
|
|
|
1373
|
|
|
global $ydb; |
1374
|
|
|
$table = YOURLS_DB_TABLE_URL; |
1375
|
|
|
|
1376
|
|
|
$lasttime = $ydb->fetchValue( "SELECT `timestamp` FROM $table WHERE `ip` = :ip ORDER BY `timestamp` DESC LIMIT 1", array('ip' => $ip) ); |
1377
|
|
|
if( $lasttime ) { |
1378
|
|
|
$now = date( 'U' ); |
1379
|
|
|
$then = date( 'U', strtotime( $lasttime ) ); |
1380
|
|
|
if( ( $now - $then ) <= YOURLS_FLOOD_DELAY_SECONDS ) { |
1381
|
|
|
// Flood! |
1382
|
|
|
yourls_do_action( 'ip_flood', $ip, $now - $then ); |
1383
|
|
|
yourls_die( yourls__( 'Too many URLs added too fast. Slow down please.' ), yourls__( 'Forbidden' ), 403 ); |
1384
|
|
|
} |
1385
|
|
|
} |
1386
|
|
|
|
1387
|
|
|
return true; |
1388
|
|
|
} |
1389
|
|
|
|
1390
|
|
|
/** |
1391
|
|
|
* Check if YOURLS is installing |
1392
|
|
|
* |
1393
|
|
|
* @return bool |
1394
|
|
|
* @since 1.6 |
1395
|
|
|
*/ |
1396
|
|
|
function yourls_is_installing() { |
1397
|
|
|
$installing = defined( 'YOURLS_INSTALLING' ) && YOURLS_INSTALLING == true; |
|
|
|
|
1398
|
|
|
return yourls_apply_filter( 'is_installing', $installing ); |
1399
|
|
|
} |
1400
|
|
|
|
1401
|
|
|
/** |
1402
|
|
|
* Check if YOURLS is upgrading |
1403
|
|
|
* |
1404
|
|
|
* @return bool |
1405
|
|
|
* @since 1.6 |
1406
|
|
|
*/ |
1407
|
|
|
function yourls_is_upgrading() { |
1408
|
|
|
$upgrading = defined( 'YOURLS_UPGRADING' ) && YOURLS_UPGRADING == true; |
|
|
|
|
1409
|
|
|
return yourls_apply_filter( 'is_upgrading', $upgrading ); |
1410
|
|
|
} |
1411
|
|
|
|
1412
|
|
|
|
1413
|
|
|
/** |
1414
|
|
|
* Check if YOURLS is installed |
1415
|
|
|
* |
1416
|
|
|
* Checks property $ydb->installed that is created by yourls_get_all_options() |
1417
|
|
|
* |
1418
|
|
|
* See inline comment for updating from 1.3 or prior. |
1419
|
|
|
* |
1420
|
|
|
*/ |
1421
|
|
|
function yourls_is_installed() { |
1422
|
|
|
global $ydb; |
1423
|
|
|
return yourls_apply_filter( 'is_installed', $ydb->is_installed() ); |
1424
|
|
|
} |
1425
|
|
|
|
1426
|
|
|
/** |
1427
|
|
|
* Set installed state |
1428
|
|
|
* |
1429
|
|
|
* @since 1.7.3 |
1430
|
|
|
* @param bool $bool whether YOURLS is installed or not |
1431
|
|
|
* @return void |
1432
|
|
|
*/ |
1433
|
|
|
function yourls_set_installed($bool) { |
1434
|
|
|
global $ydb; |
1435
|
|
|
$ydb->set_installed($bool); |
1436
|
|
|
} |
1437
|
|
|
|
1438
|
|
|
/** |
1439
|
|
|
* Generate random string of (int)$length length and type $type (see function for details) |
1440
|
|
|
* |
1441
|
|
|
*/ |
1442
|
|
|
function yourls_rnd_string ( $length = 5, $type = 0, $charlist = '' ) { |
1443
|
|
|
$str = ''; |
|
|
|
|
1444
|
|
|
$length = intval( $length ); |
1445
|
|
|
|
1446
|
|
|
// define possible characters |
1447
|
|
|
switch ( $type ) { |
1448
|
|
|
|
1449
|
|
|
// custom char list, or comply to charset as defined in config |
1450
|
|
|
case '0': |
1451
|
|
|
$possible = $charlist ? $charlist : yourls_get_shorturl_charset() ; |
1452
|
|
|
break; |
1453
|
|
|
|
1454
|
|
|
// no vowels to make no offending word, no 0/1/o/l to avoid confusion between letters & digits. Perfect for passwords. |
1455
|
|
|
case '1': |
1456
|
|
|
$possible = "23456789bcdfghjkmnpqrstvwxyz"; |
1457
|
|
|
break; |
1458
|
|
|
|
1459
|
|
|
// Same, with lower + upper |
1460
|
|
|
case '2': |
1461
|
|
|
$possible = "23456789bcdfghjkmnpqrstvwxyzBCDFGHJKMNPQRSTVWXYZ"; |
1462
|
|
|
break; |
1463
|
|
|
|
1464
|
|
|
// all letters, lowercase |
1465
|
|
|
case '3': |
1466
|
|
|
$possible = "abcdefghijklmnopqrstuvwxyz"; |
1467
|
|
|
break; |
1468
|
|
|
|
1469
|
|
|
// all letters, lowercase + uppercase |
1470
|
|
|
case '4': |
1471
|
|
|
$possible = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
1472
|
|
|
break; |
1473
|
|
|
|
1474
|
|
|
// all digits & letters lowercase |
1475
|
|
|
case '5': |
1476
|
|
|
$possible = "0123456789abcdefghijklmnopqrstuvwxyz"; |
1477
|
|
|
break; |
1478
|
|
|
|
1479
|
|
|
// all digits & letters lowercase + uppercase |
1480
|
|
|
case '6': |
1481
|
|
|
$possible = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
1482
|
|
|
break; |
1483
|
|
|
|
1484
|
|
|
} |
1485
|
|
|
|
1486
|
|
|
$str = substr( str_shuffle( $possible ), 0, $length ); |
|
|
|
|
1487
|
|
|
|
1488
|
|
|
return yourls_apply_filter( 'rnd_string', $str, $length, $type, $charlist ); |
1489
|
|
|
} |
1490
|
|
|
|
1491
|
|
|
/** |
1492
|
|
|
* Return salted string |
1493
|
|
|
* |
1494
|
|
|
*/ |
1495
|
|
|
function yourls_salt( $string ) { |
1496
|
|
|
$salt = defined('YOURLS_COOKIEKEY') ? YOURLS_COOKIEKEY : md5(__FILE__) ; |
1497
|
|
|
return yourls_apply_filter( 'yourls_salt', md5 ($string . $salt), $string ); |
1498
|
|
|
} |
1499
|
|
|
|
1500
|
|
|
/** |
1501
|
|
|
* Add a query var to a URL and return URL. Completely stolen from WP. |
1502
|
|
|
* |
1503
|
|
|
* Works with one of these parameter patterns: |
1504
|
|
|
* array( 'var' => 'value' ) |
1505
|
|
|
* array( 'var' => 'value' ), $url |
1506
|
|
|
* 'var', 'value' |
1507
|
|
|
* 'var', 'value', $url |
1508
|
|
|
* If $url omitted, uses $_SERVER['REQUEST_URI'] |
1509
|
|
|
* |
1510
|
|
|
* The result of this function call is a URL : it should be escaped before being printed as HTML |
1511
|
|
|
* |
1512
|
|
|
* @since 1.5 |
1513
|
|
|
* @param string|array $param1 Either newkey or an associative_array. |
|
|
|
|
1514
|
|
|
* @param string $param2 Either newvalue or oldquery or URI. |
|
|
|
|
1515
|
|
|
* @param string $param3 Optional. Old query or URI. |
|
|
|
|
1516
|
|
|
* @return string New URL query string. |
1517
|
|
|
*/ |
1518
|
|
|
function yourls_add_query_arg() { |
1519
|
|
|
$ret = ''; |
|
|
|
|
1520
|
|
|
if ( is_array( func_get_arg(0) ) ) { |
1521
|
|
View Code Duplication |
if ( @func_num_args() < 2 || false === @func_get_arg( 1 ) ) |
|
|
|
|
1522
|
|
|
$uri = $_SERVER['REQUEST_URI']; |
1523
|
|
|
else |
1524
|
|
|
$uri = @func_get_arg( 1 ); |
1525
|
|
View Code Duplication |
} else { |
|
|
|
|
1526
|
|
|
if ( @func_num_args() < 3 || false === @func_get_arg( 2 ) ) |
1527
|
|
|
$uri = $_SERVER['REQUEST_URI']; |
1528
|
|
|
else |
1529
|
|
|
$uri = @func_get_arg( 2 ); |
1530
|
|
|
} |
1531
|
|
|
|
1532
|
|
|
$uri = str_replace( '&', '&', $uri ); |
1533
|
|
|
|
1534
|
|
|
|
1535
|
|
|
if ( $frag = strstr( $uri, '#' ) ) |
1536
|
|
|
$uri = substr( $uri, 0, -strlen( $frag ) ); |
1537
|
|
|
else |
1538
|
|
|
$frag = ''; |
1539
|
|
|
|
1540
|
|
|
if ( preg_match( '|^https?://|i', $uri, $matches ) ) { |
1541
|
|
|
$protocol = $matches[0]; |
1542
|
|
|
$uri = substr( $uri, strlen( $protocol ) ); |
1543
|
|
|
} else { |
1544
|
|
|
$protocol = ''; |
1545
|
|
|
} |
1546
|
|
|
|
1547
|
|
|
if ( strpos( $uri, '?' ) !== false ) { |
1548
|
|
|
$parts = explode( '?', $uri, 2 ); |
1549
|
|
|
if ( 1 == count( $parts ) ) { |
1550
|
|
|
$base = '?'; |
1551
|
|
|
$query = $parts[0]; |
1552
|
|
|
} else { |
1553
|
|
|
$base = $parts[0] . '?'; |
1554
|
|
|
$query = $parts[1]; |
1555
|
|
|
} |
1556
|
|
|
} elseif ( !empty( $protocol ) || strpos( $uri, '=' ) === false ) { |
1557
|
|
|
$base = $uri . '?'; |
1558
|
|
|
$query = ''; |
1559
|
|
|
} else { |
1560
|
|
|
$base = ''; |
1561
|
|
|
$query = $uri; |
1562
|
|
|
} |
1563
|
|
|
|
1564
|
|
|
parse_str( $query, $qs ); |
1565
|
|
|
$qs = yourls_urlencode_deep( $qs ); // this re-URL-encodes things that were already in the query string |
1566
|
|
|
if ( is_array( func_get_arg( 0 ) ) ) { |
1567
|
|
|
$kayvees = func_get_arg( 0 ); |
1568
|
|
|
$qs = array_merge( $qs, $kayvees ); |
1569
|
|
|
} else { |
1570
|
|
|
$qs[func_get_arg( 0 )] = func_get_arg( 1 ); |
1571
|
|
|
} |
1572
|
|
|
|
1573
|
|
|
foreach ( (array) $qs as $k => $v ) { |
1574
|
|
|
if ( $v === false ) |
1575
|
|
|
unset( $qs[$k] ); |
1576
|
|
|
} |
1577
|
|
|
|
1578
|
|
|
$ret = http_build_query( $qs ); |
1579
|
|
|
$ret = trim( $ret, '?' ); |
1580
|
|
|
$ret = preg_replace( '#=(&|$)#', '$1', $ret ); |
1581
|
|
|
$ret = $protocol . $base . $ret . $frag; |
1582
|
|
|
$ret = rtrim( $ret, '?' ); |
1583
|
|
|
return $ret; |
1584
|
|
|
} |
1585
|
|
|
|
1586
|
|
|
/** |
1587
|
|
|
* Navigates through an array and encodes the values to be used in a URL. Stolen from WP, used in yourls_add_query_arg() |
1588
|
|
|
* |
1589
|
|
|
*/ |
1590
|
|
|
function yourls_urlencode_deep( $value ) { |
1591
|
|
|
$value = is_array( $value ) ? array_map( 'yourls_urlencode_deep', $value ) : urlencode( $value ); |
1592
|
|
|
return $value; |
1593
|
|
|
} |
1594
|
|
|
|
1595
|
|
|
/** |
1596
|
|
|
* Remove arg from query. Opposite of yourls_add_query_arg. Stolen from WP. |
1597
|
|
|
* |
1598
|
|
|
* The result of this function call is a URL : it should be escaped before being printed as HTML |
1599
|
|
|
* |
1600
|
|
|
* @since 1.5 |
1601
|
|
|
* @param string|array $key Query key or keys to remove. |
1602
|
|
|
* @param bool|string $query Optional. When false uses the $_SERVER value. Default false. |
1603
|
|
|
* @return string New URL query string. |
1604
|
|
|
*/ |
1605
|
|
|
function yourls_remove_query_arg( $key, $query = false ) { |
1606
|
|
|
if ( is_array( $key ) ) { // removing multiple keys |
1607
|
|
|
foreach ( $key as $k ) |
1608
|
|
|
$query = yourls_add_query_arg( $k, false, $query ); |
1609
|
|
|
return $query; |
1610
|
|
|
} |
1611
|
|
|
return yourls_add_query_arg( $key, false, $query ); |
1612
|
|
|
} |
1613
|
|
|
|
1614
|
|
|
/** |
1615
|
|
|
* Return a time-dependent string for nonce creation |
1616
|
|
|
* |
1617
|
|
|
*/ |
1618
|
|
|
function yourls_tick() { |
1619
|
|
|
return ceil( time() / YOURLS_NONCE_LIFE ); |
1620
|
|
|
} |
1621
|
|
|
|
1622
|
|
|
/** |
1623
|
|
|
* Create a time limited, action limited and user limited token |
1624
|
|
|
* |
1625
|
|
|
*/ |
1626
|
|
|
function yourls_create_nonce( $action, $user = false ) { |
1627
|
|
|
if( false == $user ) |
|
|
|
|
1628
|
|
|
$user = defined( 'YOURLS_USER' ) ? YOURLS_USER : '-1'; |
1629
|
|
|
$tick = yourls_tick(); |
1630
|
|
|
return substr( yourls_salt($tick . $action . $user), 0, 10 ); |
1631
|
|
|
} |
1632
|
|
|
|
1633
|
|
|
/** |
1634
|
|
|
* Create a nonce field for inclusion into a form |
1635
|
|
|
* |
1636
|
|
|
*/ |
1637
|
|
|
function yourls_nonce_field( $action, $name = 'nonce', $user = false, $echo = true ) { |
1638
|
|
|
$field = '<input type="hidden" id="'.$name.'" name="'.$name.'" value="'.yourls_create_nonce( $action, $user ).'" />'; |
1639
|
|
|
if( $echo ) |
1640
|
|
|
echo $field."\n"; |
1641
|
|
|
return $field; |
1642
|
|
|
} |
1643
|
|
|
|
1644
|
|
|
/** |
1645
|
|
|
* Add a nonce to a URL. If URL omitted, adds nonce to current URL |
1646
|
|
|
* |
1647
|
|
|
*/ |
1648
|
|
|
function yourls_nonce_url( $action, $url = false, $name = 'nonce', $user = false ) { |
1649
|
|
|
$nonce = yourls_create_nonce( $action, $user ); |
1650
|
|
|
return yourls_add_query_arg( $name, $nonce, $url ); |
1651
|
|
|
} |
1652
|
|
|
|
1653
|
|
|
/** |
1654
|
|
|
* Check validity of a nonce (ie time span, user and action match). |
1655
|
|
|
* |
1656
|
|
|
* Returns true if valid, dies otherwise (yourls_die() or die($return) if defined) |
1657
|
|
|
* if $nonce is false or unspecified, it will use $_REQUEST['nonce'] |
1658
|
|
|
* |
1659
|
|
|
*/ |
1660
|
|
|
function yourls_verify_nonce( $action, $nonce = false, $user = false, $return = '' ) { |
1661
|
|
|
// get user |
1662
|
|
|
if( false == $user ) |
|
|
|
|
1663
|
|
|
$user = defined( 'YOURLS_USER' ) ? YOURLS_USER : '-1'; |
1664
|
|
|
|
1665
|
|
|
// get current nonce value |
1666
|
|
|
if( false == $nonce && isset( $_REQUEST['nonce'] ) ) |
|
|
|
|
1667
|
|
|
$nonce = $_REQUEST['nonce']; |
1668
|
|
|
|
1669
|
|
|
// what nonce should be |
1670
|
|
|
$valid = yourls_create_nonce( $action, $user ); |
1671
|
|
|
|
1672
|
|
|
if( $nonce == $valid ) { |
1673
|
|
|
return true; |
1674
|
|
|
} else { |
1675
|
|
|
if( $return ) |
1676
|
|
|
die( $return ); |
1677
|
|
|
yourls_die( yourls__( 'Unauthorized action or expired link' ), yourls__( 'Error' ), 403 ); |
1678
|
|
|
} |
1679
|
|
|
} |
1680
|
|
|
|
1681
|
|
|
/** |
1682
|
|
|
* Converts keyword into short link (prepend with YOURLS base URL) |
1683
|
|
|
* |
1684
|
|
|
*/ |
1685
|
|
|
function yourls_link( $keyword = '' ) { |
1686
|
|
|
$link = YOURLS_SITE . '/' . yourls_sanitize_keyword( $keyword ); |
1687
|
|
|
return yourls_apply_filter( 'yourls_link', $link, $keyword ); |
1688
|
|
|
} |
1689
|
|
|
|
1690
|
|
|
/** |
1691
|
|
|
* Converts keyword into stat link (prepend with YOURLS base URL, append +) |
1692
|
|
|
* |
1693
|
|
|
*/ |
1694
|
|
|
function yourls_statlink( $keyword = '' ) { |
1695
|
|
|
$link = YOURLS_SITE . '/' . yourls_sanitize_keyword( $keyword ) . '+'; |
1696
|
|
|
if( yourls_is_ssl() ) |
1697
|
|
|
$link = yourls_set_url_scheme( $link, 'https' ); |
1698
|
|
|
return yourls_apply_filter( 'yourls_statlink', $link, $keyword ); |
1699
|
|
|
} |
1700
|
|
|
|
1701
|
|
|
/** |
1702
|
|
|
* Check if we're in API mode. Returns bool |
1703
|
|
|
* |
1704
|
|
|
*/ |
1705
|
|
|
function yourls_is_API() { |
1706
|
|
|
$return = defined( 'YOURLS_API' ) && YOURLS_API == true; |
|
|
|
|
1707
|
|
|
return yourls_apply_filter( 'is_API', $return ); |
1708
|
|
|
} |
1709
|
|
|
|
1710
|
|
|
/** |
1711
|
|
|
* Check if we're in Ajax mode. Returns bool |
1712
|
|
|
* |
1713
|
|
|
*/ |
1714
|
|
|
function yourls_is_Ajax() { |
1715
|
|
|
$return = defined( 'YOURLS_AJAX' ) && YOURLS_AJAX == true; |
|
|
|
|
1716
|
|
|
return yourls_apply_filter( 'is_Ajax', $return ); |
1717
|
|
|
} |
1718
|
|
|
|
1719
|
|
|
/** |
1720
|
|
|
* Check if we're in GO mode (yourls-go.php). Returns bool |
1721
|
|
|
* |
1722
|
|
|
*/ |
1723
|
|
|
function yourls_is_GO() { |
1724
|
|
|
$return = defined( 'YOURLS_GO' ) && YOURLS_GO == true; |
|
|
|
|
1725
|
|
|
return yourls_apply_filter( 'is_GO', $return ); |
1726
|
|
|
} |
1727
|
|
|
|
1728
|
|
|
/** |
1729
|
|
|
* Check if we're displaying stats infos (yourls-infos.php). Returns bool |
1730
|
|
|
* |
1731
|
|
|
*/ |
1732
|
|
|
function yourls_is_infos() { |
1733
|
|
|
$return = defined( 'YOURLS_INFOS' ) && YOURLS_INFOS == true; |
|
|
|
|
1734
|
|
|
return yourls_apply_filter( 'is_infos', $return ); |
1735
|
|
|
} |
1736
|
|
|
|
1737
|
|
|
/** |
1738
|
|
|
* Check if we're in the admin area. Returns bool |
1739
|
|
|
* |
1740
|
|
|
*/ |
1741
|
|
|
function yourls_is_admin() { |
1742
|
|
|
$return = defined( 'YOURLS_ADMIN' ) && YOURLS_ADMIN == true; |
|
|
|
|
1743
|
|
|
return yourls_apply_filter( 'is_admin', $return ); |
1744
|
|
|
} |
1745
|
|
|
|
1746
|
|
|
/** |
1747
|
|
|
* Check if the server seems to be running on Windows. Not exactly sure how reliable this is. |
1748
|
|
|
* |
1749
|
|
|
*/ |
1750
|
|
|
function yourls_is_windows() { |
1751
|
|
|
return defined( 'DIRECTORY_SEPARATOR' ) && DIRECTORY_SEPARATOR == '\\'; |
1752
|
|
|
} |
1753
|
|
|
|
1754
|
|
|
/** |
1755
|
|
|
* Check if SSL is required. Returns bool. |
1756
|
|
|
* |
1757
|
|
|
*/ |
1758
|
|
|
function yourls_needs_ssl() { |
1759
|
|
|
$return = defined('YOURLS_ADMIN_SSL') && YOURLS_ADMIN_SSL == true; |
|
|
|
|
1760
|
|
|
return yourls_apply_filter( 'needs_ssl', $return ); |
1761
|
|
|
} |
1762
|
|
|
|
1763
|
|
|
/** |
1764
|
|
|
* Return admin link, with SSL preference if applicable. |
1765
|
|
|
* |
1766
|
|
|
*/ |
1767
|
|
|
function yourls_admin_url( $page = '' ) { |
1768
|
|
|
$admin = YOURLS_SITE . '/admin/' . $page; |
1769
|
|
|
if( yourls_is_ssl() or yourls_needs_ssl() ) { |
1770
|
|
|
$admin = yourls_set_url_scheme( $admin, 'https' ); |
1771
|
|
|
} |
1772
|
|
|
return yourls_apply_filter( 'admin_url', $admin, $page ); |
1773
|
|
|
} |
1774
|
|
|
|
1775
|
|
|
/** |
1776
|
|
|
* Return YOURLS_SITE or URL under YOURLS setup, with SSL preference |
1777
|
|
|
* |
1778
|
|
|
*/ |
1779
|
|
|
function yourls_site_url( $echo = true, $url = '' ) { |
1780
|
|
|
$url = yourls_get_relative_url( $url ); |
1781
|
|
|
$url = trim( YOURLS_SITE . '/' . $url, '/' ); |
1782
|
|
|
|
1783
|
|
|
// Do not enforce (checking yourls_need_ssl() ) but check current usage so it won't force SSL on non-admin pages |
1784
|
|
|
if( yourls_is_ssl() ) { |
1785
|
|
|
$url = yourls_set_url_scheme( $url, 'https' ); |
1786
|
|
|
} |
1787
|
|
|
$url = yourls_apply_filter( 'site_url', $url ); |
1788
|
|
|
if( $echo ) { |
1789
|
|
|
echo $url; |
1790
|
|
|
} |
1791
|
|
|
return $url; |
1792
|
|
|
} |
1793
|
|
|
|
1794
|
|
|
/** |
1795
|
|
|
* Check if SSL is used, returns bool. Stolen from WP. |
1796
|
|
|
* |
1797
|
|
|
*/ |
1798
|
|
|
function yourls_is_ssl() { |
1799
|
|
|
$is_ssl = false; |
1800
|
|
|
if ( isset( $_SERVER['HTTPS'] ) ) { |
1801
|
|
|
if ( 'on' == strtolower( $_SERVER['HTTPS'] ) ) |
1802
|
|
|
$is_ssl = true; |
1803
|
|
|
if ( '1' == $_SERVER['HTTPS'] ) |
1804
|
|
|
$is_ssl = true; |
1805
|
|
|
} elseif ( isset( $_SERVER['SERVER_PORT'] ) && ( '443' == $_SERVER['SERVER_PORT'] ) ) { |
1806
|
|
|
$is_ssl = true; |
1807
|
|
|
} |
1808
|
|
|
return yourls_apply_filter( 'is_ssl', $is_ssl ); |
1809
|
|
|
} |
1810
|
|
|
|
1811
|
|
|
/** |
1812
|
|
|
* Get a remote page title |
1813
|
|
|
* |
1814
|
|
|
* This function returns a string: either the page title as defined in HTML, or the URL if not found |
1815
|
|
|
* The function tries to convert funky characters found in titles to UTF8, from the detected charset. |
1816
|
|
|
* Charset in use is guessed from HTML meta tag, or if not found, from server's 'content-type' response. |
1817
|
|
|
* |
1818
|
|
|
* @param string $url URL |
1819
|
|
|
* @return string Title (sanitized) or the URL if no title found |
1820
|
|
|
*/ |
1821
|
|
|
function yourls_get_remote_title( $url ) { |
1822
|
|
|
// Allow plugins to short-circuit the whole function |
1823
|
|
|
$pre = yourls_apply_filter( 'shunt_get_remote_title', false, $url ); |
1824
|
|
|
if ( false !== $pre ) |
1825
|
|
|
return $pre; |
1826
|
|
|
|
1827
|
|
|
$url = yourls_sanitize_url( $url ); |
1828
|
|
|
|
1829
|
|
|
// Only deal with http(s):// |
1830
|
|
|
if( !in_array( yourls_get_protocol( $url ), array( 'http://', 'https://' ) ) ) |
1831
|
|
|
return $url; |
1832
|
|
|
|
1833
|
|
|
$title = $charset = false; |
1834
|
|
|
|
1835
|
|
|
$max_bytes = yourls_apply_filter( 'get_remote_title_max_byte', 32768 ); // limit data fetching to 32K in order to find a <title> tag |
1836
|
|
|
|
1837
|
|
|
$response = yourls_http_get( $url, array(), array(), array( 'max_bytes' => $max_bytes ) ); // can be a Request object or an error string |
1838
|
|
|
if( is_string( $response ) ) { |
1839
|
|
|
return $url; |
1840
|
|
|
} |
1841
|
|
|
|
1842
|
|
|
// Page content. No content? Return the URL |
1843
|
|
|
$content = $response->body; |
1844
|
|
|
if( !$content ) |
1845
|
|
|
return $url; |
1846
|
|
|
|
1847
|
|
|
// look for <title>. No title found? Return the URL |
1848
|
|
|
if ( preg_match('/<title>(.*?)<\/title>/is', $content, $found ) ) { |
1849
|
|
|
$title = $found[1]; |
1850
|
|
|
unset( $found ); |
1851
|
|
|
} |
1852
|
|
|
if( !$title ) |
|
|
|
|
1853
|
|
|
return $url; |
1854
|
|
|
|
1855
|
|
|
// Now we have a title. We'll try to get proper utf8 from it. |
1856
|
|
|
|
1857
|
|
|
// Get charset as (and if) defined by the HTML meta tag. We should match |
1858
|
|
|
// <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> |
1859
|
|
|
// or <meta charset='utf-8'> and all possible variations: see https://gist.github.com/ozh/7951236 |
1860
|
|
|
if ( preg_match( '/<meta[^>]*charset\s*=["\' ]*([a-zA-Z0-9\-_]+)/is', $content, $found ) ) { |
1861
|
|
|
$charset = $found[1]; |
1862
|
|
|
unset( $found ); |
1863
|
|
|
} else { |
1864
|
|
|
// No charset found in HTML. Get charset as (and if) defined by the server response |
1865
|
|
|
$_charset = current( $response->headers->getValues( 'content-type' ) ); |
1866
|
|
|
if( preg_match( '/charset=(\S+)/', $_charset, $found ) ) { |
1867
|
|
|
$charset = trim( $found[1], ';' ); |
1868
|
|
|
unset( $found ); |
1869
|
|
|
} |
1870
|
|
|
} |
1871
|
|
|
|
1872
|
|
|
// Conversion to utf-8 if what we have is not utf8 already |
1873
|
|
|
if( strtolower( $charset ) != 'utf-8' && function_exists( 'mb_convert_encoding' ) ) { |
1874
|
|
|
// We use @ to remove warnings because mb_ functions are easily bitching about illegal chars |
1875
|
|
|
if( $charset ) { |
|
|
|
|
1876
|
|
|
$title = @mb_convert_encoding( $title, 'UTF-8', $charset ); |
1877
|
|
|
} else { |
1878
|
|
|
$title = @mb_convert_encoding( $title, 'UTF-8' ); |
1879
|
|
|
} |
1880
|
|
|
} |
1881
|
|
|
|
1882
|
|
|
// Remove HTML entities |
1883
|
|
|
$title = html_entity_decode( $title, ENT_QUOTES, 'UTF-8' ); |
1884
|
|
|
|
1885
|
|
|
// Strip out evil things |
1886
|
|
|
$title = yourls_sanitize_title( $title, $url ); |
1887
|
|
|
|
1888
|
|
|
return yourls_apply_filter( 'get_remote_title', $title, $url ); |
1889
|
|
|
} |
1890
|
|
|
|
1891
|
|
|
/** |
1892
|
|
|
* Quick UA check for mobile devices. Return boolean. |
1893
|
|
|
* |
1894
|
|
|
*/ |
1895
|
|
|
function yourls_is_mobile_device() { |
1896
|
|
|
// Strings searched |
1897
|
|
|
$mobiles = array( |
1898
|
|
|
'android', 'blackberry', 'blazer', |
1899
|
|
|
'compal', 'elaine', 'fennec', 'hiptop', |
1900
|
|
|
'iemobile', 'iphone', 'ipod', 'ipad', |
1901
|
|
|
'iris', 'kindle', 'opera mobi', 'opera mini', |
1902
|
|
|
'palm', 'phone', 'pocket', 'psp', 'symbian', |
1903
|
|
|
'treo', 'wap', 'windows ce', 'windows phone' |
1904
|
|
|
); |
1905
|
|
|
|
1906
|
|
|
// Current user-agent |
1907
|
|
|
$current = strtolower( $_SERVER['HTTP_USER_AGENT'] ); |
1908
|
|
|
|
1909
|
|
|
// Check and return |
1910
|
|
|
$is_mobile = ( str_replace( $mobiles, '', $current ) != $current ); |
1911
|
|
|
return yourls_apply_filter( 'is_mobile_device', $is_mobile ); |
1912
|
|
|
} |
1913
|
|
|
|
1914
|
|
|
/** |
1915
|
|
|
* Get request in YOURLS base (eg in 'http://sho.rt/yourls/abcd' get 'abdc') |
1916
|
|
|
* |
1917
|
|
|
* With no parameter passed, this function will guess current page and consider |
1918
|
|
|
* it is the current page requested. |
1919
|
|
|
* For testing purposes, parameters can be passed. |
1920
|
|
|
* |
1921
|
|
|
* @since 1.5 |
1922
|
|
|
* @param string $yourls_site Optional, YOURLS installation URL (default to constant YOURLS_SITE) |
1923
|
|
|
* @param string $uri Optional, page requested (default to $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'] eg 'sho.rt/yourls/abcd' ) |
1924
|
|
|
* @return string request relative to YOURLS base (eg 'abdc') |
1925
|
|
|
*/ |
1926
|
|
|
function yourls_get_request($yourls_site = false, $uri = false) { |
1927
|
|
|
// Allow plugins to short-circuit the whole function |
1928
|
|
|
$pre = yourls_apply_filter( 'shunt_get_request', false ); |
1929
|
|
|
if ( false !== $pre ) |
1930
|
|
|
return $pre; |
1931
|
|
|
|
1932
|
|
|
yourls_do_action( 'pre_get_request', $yourls_site, $uri ); |
1933
|
|
|
|
1934
|
|
|
// Default values |
1935
|
|
|
if (false === $yourls_site) { |
1936
|
|
|
$yourls_site = YOURLS_SITE; |
1937
|
|
|
} |
1938
|
|
|
if (false === $uri) { |
1939
|
|
|
$uri = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; |
1940
|
|
|
} |
1941
|
|
|
|
1942
|
|
|
// Even though the config sample states YOURLS_SITE should be set without trailing slash... |
1943
|
|
|
$yourls_site = rtrim($yourls_site,'/'); |
1944
|
|
|
|
1945
|
|
|
// Ignore protocol & www. prefix |
1946
|
|
|
$root = str_replace( array( 'https://www.', 'http://www.', 'https://', 'http://' ), '', $yourls_site ); |
1947
|
|
|
// Case insensitive comparison of the YOURLS root with the requested URL, to match http://Sho.rt/blah, http://sho.rt/blah, http://www.Sho.rt/blah ... |
1948
|
|
|
$request = preg_replace( "!(?:www\.)?$root/!i", '', $uri, 1 ); |
1949
|
|
|
|
1950
|
|
|
// Unless request looks like a full URL (ie request is a simple keyword) strip query string |
1951
|
|
|
if( !preg_match( "@^[a-zA-Z]+://.+@", $request ) ) { |
1952
|
|
|
$request = current( explode( '?', $request ) ); |
1953
|
|
|
} |
1954
|
|
|
|
1955
|
|
|
return yourls_apply_filter( 'get_request', $request ); |
1956
|
|
|
} |
1957
|
|
|
|
1958
|
|
|
/** |
1959
|
|
|
* Change protocol to match current scheme used (http or https) |
1960
|
|
|
* |
1961
|
|
|
*/ |
1962
|
|
|
function yourls_match_current_protocol( $url, $normal = 'http://', $ssl = 'https://' ) { |
1963
|
|
|
if( yourls_is_ssl() ) |
1964
|
|
|
$url = str_replace( $normal, $ssl, $url ); |
1965
|
|
|
return yourls_apply_filter( 'match_current_protocol', $url ); |
1966
|
|
|
} |
1967
|
|
|
|
1968
|
|
|
/** |
1969
|
|
|
* Fix $_SERVER['REQUEST_URI'] variable for various setups. Stolen from WP. |
1970
|
|
|
* |
1971
|
|
|
*/ |
1972
|
|
|
function yourls_fix_request_uri() { |
1973
|
|
|
|
1974
|
|
|
$default_server_values = array( |
1975
|
|
|
'SERVER_SOFTWARE' => '', |
1976
|
|
|
'REQUEST_URI' => '', |
1977
|
|
|
); |
1978
|
|
|
$_SERVER = array_merge( $default_server_values, $_SERVER ); |
1979
|
|
|
|
1980
|
|
|
// Fix for IIS when running with PHP ISAPI |
1981
|
|
|
if ( empty( $_SERVER['REQUEST_URI'] ) || ( php_sapi_name() != 'cgi-fcgi' && preg_match( '/^Microsoft-IIS\//', $_SERVER['SERVER_SOFTWARE'] ) ) ) { |
1982
|
|
|
|
1983
|
|
|
// IIS Mod-Rewrite |
1984
|
|
|
if ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] ) ) { |
1985
|
|
|
$_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_ORIGINAL_URL']; |
1986
|
|
|
} |
1987
|
|
|
// IIS Isapi_Rewrite |
1988
|
|
|
else if ( isset( $_SERVER['HTTP_X_REWRITE_URL'] ) ) { |
1989
|
|
|
$_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_REWRITE_URL']; |
1990
|
|
|
} else { |
1991
|
|
|
// Use ORIG_PATH_INFO if there is no PATH_INFO |
1992
|
|
|
if ( !isset( $_SERVER['PATH_INFO'] ) && isset( $_SERVER['ORIG_PATH_INFO'] ) ) |
1993
|
|
|
$_SERVER['PATH_INFO'] = $_SERVER['ORIG_PATH_INFO']; |
1994
|
|
|
|
1995
|
|
|
// Some IIS + PHP configurations puts the script-name in the path-info (No need to append it twice) |
1996
|
|
|
if ( isset( $_SERVER['PATH_INFO'] ) ) { |
1997
|
|
|
if ( $_SERVER['PATH_INFO'] == $_SERVER['SCRIPT_NAME'] ) |
1998
|
|
|
$_SERVER['REQUEST_URI'] = $_SERVER['PATH_INFO']; |
1999
|
|
|
else |
2000
|
|
|
$_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] . $_SERVER['PATH_INFO']; |
2001
|
|
|
} |
2002
|
|
|
|
2003
|
|
|
// Append the query string if it exists and isn't null |
2004
|
|
|
if ( ! empty( $_SERVER['QUERY_STRING'] ) ) { |
2005
|
|
|
$_SERVER['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING']; |
2006
|
|
|
} |
2007
|
|
|
} |
2008
|
|
|
} |
2009
|
|
|
} |
2010
|
|
|
|
2011
|
|
|
/** |
2012
|
|
|
* Shutdown function, runs just before PHP shuts down execution. Stolen from WP |
2013
|
|
|
* |
2014
|
|
|
*/ |
2015
|
|
|
function yourls_shutdown() { |
2016
|
|
|
yourls_do_action( 'shutdown' ); |
2017
|
|
|
} |
2018
|
|
|
|
2019
|
|
|
/** |
2020
|
|
|
* Auto detect custom favicon in /user directory, fallback to YOURLS favicon, and echo/return its URL |
2021
|
|
|
* |
2022
|
|
|
*/ |
2023
|
|
|
function yourls_favicon( $echo = true ) { |
2024
|
|
|
static $favicon = null; |
2025
|
|
|
|
2026
|
|
|
if( $favicon !== null ) { |
2027
|
|
|
if( $echo ) { |
2028
|
|
|
echo $favicon; |
2029
|
|
|
} |
2030
|
|
|
return $favicon; |
2031
|
|
|
} |
2032
|
|
|
|
2033
|
|
|
$custom = null; |
2034
|
|
|
// search for favicon.(gif|ico|png|jpg|svg) |
2035
|
|
|
foreach( array( 'gif', 'ico', 'png', 'jpg', 'svg' ) as $ext ) { |
2036
|
|
|
if( file_exists( YOURLS_USERDIR. '/favicon.' . $ext ) ) { |
2037
|
|
|
$custom = 'favicon.' . $ext; |
2038
|
|
|
break; |
2039
|
|
|
} |
2040
|
|
|
} |
2041
|
|
|
|
2042
|
|
|
if( $custom ) { |
|
|
|
|
2043
|
|
|
$favicon = yourls_site_url( false, YOURLS_USERURL . '/' . $custom ); |
2044
|
|
|
} else { |
2045
|
|
|
$favicon = yourls_site_url( false ) . '/images/favicon.gif'; |
2046
|
|
|
} |
2047
|
|
|
|
2048
|
|
|
if( $echo ) { |
2049
|
|
|
echo $favicon; |
2050
|
|
|
} |
2051
|
|
|
return $favicon; |
2052
|
|
|
} |
2053
|
|
|
|
2054
|
|
|
/** |
2055
|
|
|
* Check for maintenance mode. If yes, die. See yourls_maintenance_mode(). Stolen from WP. |
2056
|
|
|
* |
2057
|
|
|
*/ |
2058
|
|
|
function yourls_check_maintenance_mode() { |
2059
|
|
|
|
2060
|
|
|
$file = YOURLS_ABSPATH . '/.maintenance' ; |
2061
|
|
|
if ( !file_exists( $file ) || yourls_is_upgrading() || yourls_is_installing() ) |
2062
|
|
|
return; |
2063
|
|
|
|
2064
|
|
|
global $maintenance_start; |
2065
|
|
|
|
2066
|
|
|
include_once( $file ); |
2067
|
|
|
// If the $maintenance_start timestamp is older than 10 minutes, don't die. |
2068
|
|
|
if ( ( time() - $maintenance_start ) >= 600 ) |
2069
|
|
|
return; |
2070
|
|
|
|
2071
|
|
|
// Use any /user/maintenance.php file |
2072
|
|
|
if( file_exists( YOURLS_USERDIR.'/maintenance.php' ) ) { |
2073
|
|
|
include_once( YOURLS_USERDIR.'/maintenance.php' ); |
2074
|
|
|
die(); |
2075
|
|
|
} |
2076
|
|
|
|
2077
|
|
|
// https://www.youtube.com/watch?v=Xw-m4jEY-Ns |
2078
|
|
|
$title = yourls__( 'Service temporarily unavailable' ); |
2079
|
|
|
$message = yourls__( 'Our service is currently undergoing scheduled maintenance.' ) . "</p>\n<p>" . |
2080
|
|
|
yourls__( 'Things should not last very long, thank you for your patience and please excuse the inconvenience' ); |
2081
|
|
|
yourls_die( $message, $title , 503 ); |
2082
|
|
|
|
2083
|
|
|
} |
2084
|
|
|
|
2085
|
|
|
/** |
2086
|
|
|
* Return current admin page, or null if not an admin page |
2087
|
|
|
* |
2088
|
|
|
* @return mixed string if admin page, null if not an admin page |
2089
|
|
|
* @since 1.6 |
2090
|
|
|
*/ |
2091
|
|
|
function yourls_current_admin_page() { |
2092
|
|
|
if( yourls_is_admin() ) { |
2093
|
|
|
$current = substr( yourls_get_request(), 6 ); |
2094
|
|
|
if( $current === false ) |
2095
|
|
|
$current = 'index.php'; // if current page is http://sho.rt/admin/ instead of http://sho.rt/admin/index.php |
2096
|
|
|
|
2097
|
|
|
return $current; |
2098
|
|
|
} |
2099
|
|
|
return null; |
2100
|
|
|
} |
2101
|
|
|
|
2102
|
|
|
/** |
2103
|
|
|
* Check if a URL protocol is allowed |
2104
|
|
|
* |
2105
|
|
|
* Checks a URL against a list of whitelisted protocols. Protocols must be defined with |
2106
|
|
|
* their complete scheme name, ie 'stuff:' or 'stuff://' (for instance, 'mailto:' is a valid |
2107
|
|
|
* protocol, 'mailto://' isn't, and 'http:' with no double slashed isn't either |
2108
|
|
|
* |
2109
|
|
|
* @since 1.6 |
2110
|
|
|
* @see yourls_get_protocol() |
2111
|
|
|
* |
2112
|
|
|
* @param string $url URL to be check |
2113
|
|
|
* @param array $protocols Optional. Array of protocols, defaults to global $yourls_allowedprotocols |
2114
|
|
|
* @return boolean true if protocol allowed, false otherwise |
2115
|
|
|
*/ |
2116
|
|
|
function yourls_is_allowed_protocol( $url, $protocols = array() ) { |
2117
|
|
|
if( ! $protocols ) { |
|
|
|
|
2118
|
|
|
global $yourls_allowedprotocols; |
2119
|
|
|
$protocols = $yourls_allowedprotocols; |
2120
|
|
|
} |
2121
|
|
|
|
2122
|
|
|
$protocol = yourls_get_protocol( $url ); |
2123
|
|
|
return yourls_apply_filter( 'is_allowed_protocol', in_array( $protocol, $protocols ), $url, $protocols ); |
2124
|
|
|
} |
2125
|
|
|
|
2126
|
|
|
/** |
2127
|
|
|
* Get protocol from a URL (eg mailto:, http:// ...) |
2128
|
|
|
* |
2129
|
|
|
* What we liberally call a "protocol" in YOURLS is the scheme name + colon + double slashes if present of a URI. Examples: |
2130
|
|
|
* "something://blah" -> "something://" |
2131
|
|
|
* "something:blah" -> "something:" |
2132
|
|
|
* "something:/blah" -> "something:" |
2133
|
|
|
* |
2134
|
|
|
* Unit Tests for this function are located in tests/format/urls.php |
2135
|
|
|
* |
2136
|
|
|
* @since 1.6 |
2137
|
|
|
* |
2138
|
|
|
* @param string $url URL to be check |
2139
|
|
|
* @return string Protocol, with slash slash if applicable. Empty string if no protocol |
2140
|
|
|
*/ |
2141
|
|
|
function yourls_get_protocol( $url ) { |
2142
|
|
|
preg_match( '!^[a-zA-Z][a-zA-Z0-9\+\.-]+:(//)?!', $url, $matches ); |
2143
|
|
|
/* |
2144
|
|
|
http://en.wikipedia.org/wiki/URI_scheme#Generic_syntax |
2145
|
|
|
The scheme name consists of a sequence of characters beginning with a letter and followed by any |
2146
|
|
|
combination of letters, digits, plus ("+"), period ("."), or hyphen ("-"). Although schemes are |
2147
|
|
|
case-insensitive, the canonical form is lowercase and documents that specify schemes must do so |
2148
|
|
|
with lowercase letters. It is followed by a colon (":"). |
2149
|
|
|
*/ |
2150
|
|
|
$protocol = ( isset( $matches[0] ) ? $matches[0] : '' ); |
2151
|
|
|
return yourls_apply_filter( 'get_protocol', $protocol, $url ); |
2152
|
|
|
} |
2153
|
|
|
|
2154
|
|
|
/** |
2155
|
|
|
* Get relative URL (eg 'abc' from 'http://sho.rt/abc') |
2156
|
|
|
* |
2157
|
|
|
* Treat indifferently http & https. If a URL isn't relative to the YOURLS install, return it as is |
2158
|
|
|
* or return empty string if $strict is true |
2159
|
|
|
* |
2160
|
|
|
* @since 1.6 |
2161
|
|
|
* @param string $url URL to relativize |
2162
|
|
|
* @param bool $strict if true and if URL isn't relative to YOURLS install, return empty string |
2163
|
|
|
* @return string URL |
2164
|
|
|
*/ |
2165
|
|
|
function yourls_get_relative_url( $url, $strict = true ) { |
2166
|
|
|
$url = yourls_sanitize_url( $url ); |
2167
|
|
|
|
2168
|
|
|
// Remove protocols to make it easier |
2169
|
|
|
$noproto_url = str_replace( 'https:', 'http:', $url ); |
2170
|
|
|
$noproto_site = str_replace( 'https:', 'http:', YOURLS_SITE ); |
2171
|
|
|
|
2172
|
|
|
// Trim URL from YOURLS root URL : if no modification made, URL wasn't relative |
2173
|
|
|
$_url = str_replace( $noproto_site . '/', '', $noproto_url ); |
2174
|
|
|
if( $_url == $noproto_url ) |
2175
|
|
|
$_url = ( $strict ? '' : $url ); |
2176
|
|
|
|
2177
|
|
|
return yourls_apply_filter( 'get_relative_url', $_url, $url ); |
2178
|
|
|
} |
2179
|
|
|
|
2180
|
|
|
/** |
2181
|
|
|
* Marks a function as deprecated and informs when it has been used. Stolen from WP. |
2182
|
|
|
* |
2183
|
|
|
* There is a hook deprecated_function that will be called that can be used |
2184
|
|
|
* to get the backtrace up to what file and function called the deprecated |
2185
|
|
|
* function. |
2186
|
|
|
* |
2187
|
|
|
* The current behavior is to trigger a user error if YOURLS_DEBUG is true. |
2188
|
|
|
* |
2189
|
|
|
* This function is to be used in every function that is deprecated. |
2190
|
|
|
* |
2191
|
|
|
* @since 1.6 |
2192
|
|
|
* @uses yourls_do_action() Calls 'deprecated_function' and passes the function name, what to use instead, |
2193
|
|
|
* and the version the function was deprecated in. |
2194
|
|
|
* @uses yourls_apply_filter() Calls 'deprecated_function_trigger_error' and expects boolean value of true to do |
2195
|
|
|
* trigger or false to not trigger error. |
2196
|
|
|
* |
2197
|
|
|
* @param string $function The function that was called |
2198
|
|
|
* @param string $version The version of WordPress that deprecated the function |
2199
|
|
|
* @param string $replacement Optional. The function that should have been called |
2200
|
|
|
*/ |
2201
|
|
|
function yourls_deprecated_function( $function, $version, $replacement = null ) { |
2202
|
|
|
|
2203
|
|
|
yourls_do_action( 'deprecated_function', $function, $replacement, $version ); |
2204
|
|
|
|
2205
|
|
|
// Allow plugin to filter the output error trigger |
2206
|
|
|
if ( YOURLS_DEBUG && yourls_apply_filter( 'deprecated_function_trigger_error', true ) ) { |
2207
|
|
|
if ( ! is_null( $replacement ) ) |
2208
|
|
|
trigger_error( sprintf( yourls__('%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.'), $function, $version, $replacement ) ); |
2209
|
|
|
else |
2210
|
|
|
trigger_error( sprintf( yourls__('%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.'), $function, $version ) ); |
2211
|
|
|
} |
2212
|
|
|
} |
2213
|
|
|
|
2214
|
|
|
/** |
2215
|
|
|
* Return the value if not an empty string |
2216
|
|
|
* |
2217
|
|
|
* Used with array_filter(), to remove empty keys but not keys with value 0 or false |
2218
|
|
|
* |
2219
|
|
|
* @since 1.6 |
2220
|
|
|
* @param mixed $val Value to test against '' |
2221
|
|
|
* @return bool True if not an empty string |
2222
|
|
|
*/ |
2223
|
|
|
function yourls_return_if_not_empty_string( $val ) { |
2224
|
|
|
return( $val !== '' ); |
2225
|
|
|
} |
2226
|
|
|
|
2227
|
|
|
/** |
2228
|
|
|
* Returns true. |
2229
|
|
|
* |
2230
|
|
|
* Useful for returning true to filters easily. |
2231
|
|
|
* |
2232
|
|
|
* @since 1.7.1 |
2233
|
|
|
* @return bool True. |
2234
|
|
|
*/ |
2235
|
|
|
function yourls_return_true() { |
2236
|
|
|
return true; |
2237
|
|
|
} |
2238
|
|
|
|
2239
|
|
|
/** |
2240
|
|
|
* Returns false. |
2241
|
|
|
* |
2242
|
|
|
* Useful for returning false to filters easily. |
2243
|
|
|
* |
2244
|
|
|
* @since 1.7.1 |
2245
|
|
|
* @return bool False. |
2246
|
|
|
*/ |
2247
|
|
|
function yourls_return_false() { |
2248
|
|
|
return false; |
2249
|
|
|
} |
2250
|
|
|
|
2251
|
|
|
/** |
2252
|
|
|
* Returns 0. |
2253
|
|
|
* |
2254
|
|
|
* Useful for returning 0 to filters easily. |
2255
|
|
|
* |
2256
|
|
|
* @since 1.7.1 |
2257
|
|
|
* @return int 0. |
2258
|
|
|
*/ |
2259
|
|
|
function yourls_return_zero() { |
2260
|
|
|
return 0; |
2261
|
|
|
} |
2262
|
|
|
|
2263
|
|
|
/** |
2264
|
|
|
* Returns an empty array. |
2265
|
|
|
* |
2266
|
|
|
* Useful for returning an empty array to filters easily. |
2267
|
|
|
* |
2268
|
|
|
* @since 1.7.1 |
2269
|
|
|
* @return array Empty array. |
2270
|
|
|
*/ |
2271
|
|
|
function yourls_return_empty_array() { |
2272
|
|
|
return array(); |
2273
|
|
|
} |
2274
|
|
|
|
2275
|
|
|
/** |
2276
|
|
|
* Returns null. |
2277
|
|
|
* |
2278
|
|
|
* Useful for returning null to filters easily. |
2279
|
|
|
* |
2280
|
|
|
* @since 1.7.1 |
2281
|
|
|
* @return null Null value. |
2282
|
|
|
*/ |
2283
|
|
|
function yourls_return_null() { |
2284
|
|
|
return null; |
2285
|
|
|
} |
2286
|
|
|
|
2287
|
|
|
/** |
2288
|
|
|
* Returns an empty string. |
2289
|
|
|
* |
2290
|
|
|
* Useful for returning an empty string to filters easily. |
2291
|
|
|
* |
2292
|
|
|
* @since 1.7.1 |
2293
|
|
|
* @return string Empty string. |
2294
|
|
|
*/ |
2295
|
|
|
function yourls_return_empty_string() { |
2296
|
|
|
return ''; |
2297
|
|
|
} |
2298
|
|
|
|
2299
|
|
|
/** |
2300
|
|
|
* Add a message to the debug log |
2301
|
|
|
* |
2302
|
|
|
* When in debug mode ( YOURLS_DEBUG == true ) the debug log is echoed in yourls_html_footer() |
2303
|
|
|
* Log messages are appended to $ydb->debug_log array, which is instanciated within class ezSQLcore_YOURLS |
2304
|
|
|
* |
2305
|
|
|
* @since 1.7 |
2306
|
|
|
* @param string $msg Message to add to the debug log |
2307
|
|
|
* @return string The message itself |
2308
|
|
|
*/ |
2309
|
|
|
function yourls_debug_log( $msg ) { |
2310
|
|
|
global $ydb; |
2311
|
|
|
$ydb->getProfiler()->log($msg); |
2312
|
|
|
return $msg; |
2313
|
|
|
} |
2314
|
|
|
|
2315
|
|
|
/** |
2316
|
|
|
* Get the debug log |
2317
|
|
|
* |
2318
|
|
|
* @since 1.7.3 |
2319
|
|
|
* @return array |
2320
|
|
|
*/ |
2321
|
|
|
function yourls_get_debug_log() { |
2322
|
|
|
global $ydb; |
2323
|
|
|
return $ydb->getProfiler()->get_log(); |
2324
|
|
|
} |
2325
|
|
|
|
2326
|
|
|
/** |
2327
|
|
|
* Debug mode toggle |
2328
|
|
|
* |
2329
|
|
|
* @since 1.7.3 |
2330
|
|
|
* @param bool $bool Debug on or off |
2331
|
|
|
*/ |
2332
|
|
|
function yourls_debug_mode($bool) { |
2333
|
|
|
global $ydb; |
2334
|
|
|
$bool = (bool)$bool; |
2335
|
|
|
$ydb->getProfiler()->setActive($bool); |
2336
|
|
|
} |
2337
|
|
|
|
2338
|
|
|
/** |
2339
|
|
|
* Explode a URL in an array of ( 'protocol' , 'slashes if any', 'rest of the URL' ) |
2340
|
|
|
* |
2341
|
|
|
* Some hosts trip up when a query string contains 'http://' - see http://git.io/j1FlJg |
2342
|
|
|
* The idea is that instead of passing the whole URL to a bookmarklet, eg index.php?u=http://blah.com, |
2343
|
|
|
* we pass it by pieces to fool the server, eg index.php?proto=http:&slashes=//&rest=blah.com |
2344
|
|
|
* |
2345
|
|
|
* Known limitation: this won't work if the rest of the URL itself contains 'http://', for example |
2346
|
|
|
* if rest = blah.com/file.php?url=http://foo.com |
2347
|
|
|
* |
2348
|
|
|
* Sample returns: |
2349
|
|
|
* |
2350
|
|
|
* with 'mailto:[email protected]?subject=hey' : |
2351
|
|
|
* array( 'protocol' => 'mailto:', 'slashes' => '', 'rest' => '[email protected]?subject=hey' ) |
2352
|
|
|
* |
2353
|
|
|
* with 'http://example.com/blah.html' : |
2354
|
|
|
* array( 'protocol' => 'http:', 'slashes' => '//', 'rest' => 'example.com/blah.html' ) |
2355
|
|
|
* |
2356
|
|
|
* @since 1.7 |
2357
|
|
|
* @param string $url URL to be parsed |
2358
|
|
|
* @param array $array Optional, array of key names to be used in returned array |
2359
|
|
|
* @return mixed false if no protocol found, array of ('protocol' , 'slashes', 'rest') otherwise |
2360
|
|
|
*/ |
2361
|
|
|
function yourls_get_protocol_slashes_and_rest( $url, $array = array( 'protocol', 'slashes', 'rest' ) ) { |
2362
|
|
|
$proto = yourls_get_protocol( $url ); |
2363
|
|
|
|
2364
|
|
|
if( !$proto or count( $array ) != 3 ) |
2365
|
|
|
return false; |
2366
|
|
|
|
2367
|
|
|
list( $null, $rest ) = explode( $proto, $url, 2 ); |
|
|
|
|
2368
|
|
|
|
2369
|
|
|
list( $proto, $slashes ) = explode( ':', $proto ); |
2370
|
|
|
|
2371
|
|
|
return array( $array[0] => $proto . ':', $array[1] => $slashes, $array[2] => $rest ); |
2372
|
|
|
} |
2373
|
|
|
|
2374
|
|
|
/** |
2375
|
|
|
* Set URL scheme (to HTTP or HTTPS) |
2376
|
|
|
* |
2377
|
|
|
* @since 1.7.1 |
2378
|
|
|
* @param string $url URL |
2379
|
|
|
* @param string $scheme scheme, either 'http' or 'https' |
2380
|
|
|
* @return string URL with chosen scheme |
2381
|
|
|
*/ |
2382
|
|
|
function yourls_set_url_scheme( $url, $scheme = false ) { |
2383
|
|
|
if( $scheme != 'http' && $scheme != 'https' ) { |
2384
|
|
|
return $url; |
2385
|
|
|
} |
2386
|
|
|
return preg_replace( '!^[a-zA-Z0-9\+\.-]+://!', $scheme . '://', $url ); |
2387
|
|
|
} |
2388
|
|
|
|
2389
|
|
|
/** |
2390
|
|
|
* Tell if there is a new YOURLS version |
2391
|
|
|
* |
2392
|
|
|
* This function checks, if needed, if there's a new version of YOURLS and, if applicable, display |
2393
|
|
|
* an update notice. |
2394
|
|
|
* |
2395
|
|
|
* @since 1.7.3 |
2396
|
|
|
*/ |
2397
|
|
|
function yourls_tell_if_new_version() { |
2398
|
|
|
$check = yourls_maybe_check_core_version(); |
2399
|
|
|
yourls_debug_log( 'Check for new version: ' . ($check ? 'yes' : 'no') ); |
2400
|
|
|
yourls_new_core_version_notice(); |
2401
|
|
|
} |
2402
|
|
|
|
2403
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.