1<?php
2
3/**
4 * Check if we have PDO installed, returns bool
5 *
6 * @since 1.7.3
7 * @return bool
8 */
9function yourls_check_PDO() {
10    return extension_loaded('pdo');
11}
12
13/**
14 * Check if server has MySQL 5.0+
15 *
16 */
17function yourls_check_database_version() {
18    return ( version_compare( '5.0', yourls_get_database_version() ) <= 0 );
19}
20
21/**
22 * Get DB version
23 *
24 * @since 1.7
25 * @return string sanitized DB version
26 */
27function yourls_get_database_version() {
28	// Allow plugins to short-circuit the whole function
29	$pre = yourls_apply_filter( 'shunt_get_database_version', false );
30	if ( false !== $pre ) {
31		return $pre;
32    }
33
34	return yourls_sanitize_version(yourls_get_db()->mysql_version());
35}
36
37/**
38 * Check if PHP > 7.2
39 *
40 * As of 1.8 we advertise YOURLS as being 7.4+ but it should work on 7.2 (although untested)
41 * so we don't want to strictly enforce a limitation that may not be necessary.
42 *
43 */
44function yourls_check_php_version() {
45    return version_compare( PHP_VERSION, '7.2.0', '>=' );
46}
47
48/**
49 * Check if server is an Apache
50 *
51 */
52function yourls_is_apache() {
53	if( !array_key_exists( 'SERVER_SOFTWARE', $_SERVER ) )
54		return false;
55	return (
56	   strpos( $_SERVER['SERVER_SOFTWARE'], 'Apache' ) !== false
57	|| strpos( $_SERVER['SERVER_SOFTWARE'], 'LiteSpeed' ) !== false
58	);
59}
60
61/**
62 * Check if server is running IIS
63 *
64 */
65function yourls_is_iis() {
66	return ( array_key_exists( 'SERVER_SOFTWARE', $_SERVER ) ? ( strpos( $_SERVER['SERVER_SOFTWARE'], 'IIS' ) !== false ) : false );
67}
68
69
70/**
71 * Create .htaccess or web.config. Returns boolean
72 *
73 */
74function yourls_create_htaccess() {
75	$host = parse_url( yourls_get_yourls_site() );
76	$path = ( isset( $host['path'] ) ? $host['path'] : '' );
77
78	if ( yourls_is_iis() ) {
79		// Prepare content for a web.config file
80		$content = array(
81			'<?'.'xml version="1.0" encoding="UTF-8"?>',
82			'<configuration>',
83			'    <system.webServer>',
84			'        <security>',
85			'            <requestFiltering allowDoubleEscaping="true" />',
86			'        </security>',
87			'        <rewrite>',
88			'            <rules>',
89			'                <rule name="YOURLS" stopProcessing="true">',
90			'                    <match url="^(.*)$" ignoreCase="false" />',
91			'                    <conditions>',
92			'                        <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />',
93			'                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />',
94			'                    </conditions>',
95			'                    <action type="Rewrite" url="'.$path.'/yourls-loader.php" appendQueryString="true" />',
96			'                </rule>',
97			'            </rules>',
98			'        </rewrite>',
99			'    </system.webServer>',
100			'</configuration>',
101		);
102
103		$filename = YOURLS_ABSPATH.'/web.config';
104		$marker = 'none';
105
106	} else {
107		// Prepare content for a .htaccess file
108		$content = array(
109			'<IfModule mod_rewrite.c>',
110			'RewriteEngine On',
111			'RewriteBase '.$path.'/',
112			'RewriteCond %{REQUEST_FILENAME} !-f',
113			'RewriteCond %{REQUEST_FILENAME} !-d',
114			'RewriteRule ^.*$ '.$path.'/yourls-loader.php [L]',
115			'</IfModule>',
116		);
117
118		$filename = YOURLS_ABSPATH.'/.htaccess';
119		$marker = 'YOURLS';
120
121	}
122
123	return ( yourls_insert_with_markers( $filename, $marker, $content ) );
124}
125
126/**
127 * Insert text into a file between BEGIN/END markers, return bool. Stolen from WP
128 *
129 * Inserts an array of strings into a file (eg .htaccess ), placing it between
130 * BEGIN and END markers. Replaces existing marked info. Retains surrounding
131 * data. Creates file if none exists.
132 *
133 * @since 1.3
134 *
135 * @param string $filename
136 * @param string $marker
137 * @param array  $insertion
138 * @return bool True on write success, false on failure.
139 */
140function yourls_insert_with_markers( $filename, $marker, $insertion ) {
141	if ( !file_exists( $filename ) || is_writeable( $filename ) ) {
142		if ( !file_exists( $filename ) ) {
143			$markerdata = '';
144		} else {
145			$markerdata = explode( "\n", implode( '', file( $filename ) ) );
146		}
147
148		if ( !$f = @fopen( $filename, 'w' ) )
149			return false;
150
151		$foundit = false;
152		if ( $markerdata ) {
153			$state = true;
154			foreach ( $markerdata as $n => $markerline ) {
155				if ( strpos( $markerline, '# BEGIN ' . $marker ) !== false )
156					$state = false;
157				if ( $state ) {
158					if ( $n + 1 < count( $markerdata ) )
159						fwrite( $f, "{$markerline}\n" );
160					else
161						fwrite( $f, "{$markerline}" );
162				}
163				if ( strpos( $markerline, '# END ' . $marker ) !== false ) {
164					if ( $marker != 'none' )
165						fwrite( $f, "# BEGIN {$marker}\n" );
166					if ( is_array( $insertion ) )
167						foreach ( $insertion as $insertline )
168							fwrite( $f, "{$insertline}\n" );
169					if ( $marker != 'none' )
170						fwrite( $f, "# END {$marker}\n" );
171					$state = true;
172					$foundit = true;
173				}
174			}
175		}
176		if ( !$foundit ) {
177			if ( $marker != 'none' )
178				fwrite( $f, "\n\n# BEGIN {$marker}\n" );
179			foreach ( $insertion as $insertline )
180				fwrite( $f, "{$insertline}\n" );
181			if ( $marker != 'none' )
182				fwrite( $f, "# END {$marker}\n\n" );
183		}
184		fclose( $f );
185		return true;
186	} else {
187		return false;
188	}
189}
190
191/**
192 * Create MySQL tables. Return array( 'success' => array of success strings, 'errors' => array of error strings )
193 *
194 * @since 1.3
195 * @return array  An array like array( 'success' => array of success strings, 'errors' => array of error strings )
196 */
197function yourls_create_sql_tables() {
198    // Allow plugins (most likely a custom db.php layer in user dir) to short-circuit the whole function
199    $pre = yourls_apply_filter( 'shunt_yourls_create_sql_tables', null );
200    // your filter function should return an array of ( 'success' => $success_msg, 'error' => $error_msg ), see below
201    if ( null !== $pre ) {
202        return $pre;
203    }
204
205	$ydb = yourls_get_db();
206
207	$error_msg = array();
208	$success_msg = array();
209
210	// Create Table Query
211	$create_tables = array();
212	$create_tables[YOURLS_DB_TABLE_URL] =
213        'CREATE TABLE IF NOT EXISTS `'.YOURLS_DB_TABLE_URL.'` ('.
214         '`keyword` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT "",'.
215         '`url` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,'.
216         '`title` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,'.
217         '`timestamp` timestamp NOT NULL DEFAULT current_timestamp(),'.
218         '`ip` varchar(41) COLLATE utf8mb4_unicode_ci NOT NULL,'.
219         '`clicks` int(10) unsigned NOT NULL,'.
220         'PRIMARY KEY (`keyword`),'.
221         'KEY `ip` (`ip`),'.
222         'KEY `timestamp` (`timestamp`)'.
223        ') DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;';
224
225	$create_tables[YOURLS_DB_TABLE_OPTIONS] =
226		'CREATE TABLE IF NOT EXISTS `'.YOURLS_DB_TABLE_OPTIONS.'` ('.
227		'`option_id` bigint(20) unsigned NOT NULL auto_increment,'.
228		'`option_name` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL default "",'.
229		'`option_value` longtext COLLATE utf8mb4_unicode_ci NOT NULL,'.
230		'PRIMARY KEY  (`option_id`,`option_name`),'.
231		'KEY `option_name` (`option_name`)'.
232		') AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;';
233
234	$create_tables[YOURLS_DB_TABLE_LOG] =
235		'CREATE TABLE IF NOT EXISTS `'.YOURLS_DB_TABLE_LOG.'` ('.
236		'`click_id` int(11) NOT NULL auto_increment,'.
237		'`click_time` datetime NOT NULL,'.
238		'`shorturl` varchar(100) BINARY NOT NULL,'.
239		'`referrer` varchar(200) NOT NULL,'.
240		'`user_agent` varchar(255) NOT NULL,'.
241		'`ip_address` varchar(41) NOT NULL,'.
242		'`country_code` char(2) NOT NULL,'.
243		'PRIMARY KEY  (`click_id`),'.
244		'KEY `shorturl` (`shorturl`)'.
245		') AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;';
246
247
248	$create_table_count = 0;
249
250    yourls_debug_mode(true);
251
252	// Create tables
253	foreach ( $create_tables as $table_name => $table_query ) {
254		$ydb->perform( $table_query );
255		$create_success = $ydb->fetchAffected( "SHOW TABLES LIKE '$table_name'" );
256		if( $create_success ) {
257			$create_table_count++;
258			$success_msg[] = yourls_s( "Table '%s' created.", $table_name );
259		} else {
260			$error_msg[] = yourls_s( "Error creating table '%s'.", $table_name );
261		}
262	}
263
264	// Initializes the option table
265	if( !yourls_initialize_options() )
266		$error_msg[] = yourls__( 'Could not initialize options' );
267
268	// Insert sample links
269	if( !yourls_insert_sample_links() )
270		$error_msg[] = yourls__( 'Could not insert sample short URLs' );
271
272	// Check results of operations
273	if ( sizeof( $create_tables ) == $create_table_count ) {
274		$success_msg[] = yourls__( 'YOURLS tables successfully created.' );
275	} else {
276		$error_msg[] = yourls__( 'Error creating YOURLS tables.' );
277	}
278
279	return array( 'success' => $success_msg, 'error' => $error_msg );
280}
281
282/**
283 * Initializes the option table
284 *
285 * Each yourls_update_option() returns either true on success (option updated) or false on failure (new value == old value, or
286 * for some reason it could not save to DB).
287 * Since true & true & true = 1, we cast it to boolean type to return true (or false)
288 *
289 * @since 1.7
290 * @return bool
291 */
292function yourls_initialize_options() {
293	return ( bool ) (
294		  yourls_update_option( 'version', YOURLS_VERSION )
295		& yourls_update_option( 'db_version', YOURLS_DB_VERSION )
296		& yourls_update_option( 'next_id', 1 )
297        & yourls_update_option( 'active_plugins', array() )
298	);
299}
300
301/**
302 * Populates the URL table with a few sample links
303 *
304 * @since 1.7
305 * @return bool
306 */
307function yourls_insert_sample_links() {
308	$link1 = yourls_add_new_link( 'http://blog.yourls.org/', 'yourlsblog', 'YOURLS\' Blog' );
309	$link2 = yourls_add_new_link( 'http://yourls.org/',      'yourls',     'YOURLS: Your Own URL Shortener' );
310	$link3 = yourls_add_new_link( 'http://ozh.org/',         'ozh',        'ozh.org' );
311	return ( bool ) (
312		  $link1['status'] == 'success'
313		& $link2['status'] == 'success'
314		& $link3['status'] == 'success'
315	);
316}
317
318
319/**
320 * Toggle maintenance mode. Inspired from WP. Returns true for success, false otherwise
321 *
322 */
323function yourls_maintenance_mode( $maintenance = true ) {
324
325	$file = YOURLS_ABSPATH . '/.maintenance' ;
326
327	// Turn maintenance mode on : create .maintenance file
328	if ( (bool)$maintenance ) {
329		if ( ! ( $fp = @fopen( $file, 'w' ) ) )
330			return false;
331
332		$maintenance_string = '<?php $maintenance_start = ' . time() . '; ?>';
333		@fwrite( $fp, $maintenance_string );
334		@fclose( $fp );
335		@chmod( $file, 0644 ); // Read and write for owner, read for everybody else
336
337		// Not sure why the fwrite would fail if the fopen worked... Just in case
338		return( is_readable( $file ) );
339
340	// Turn maintenance mode off : delete the .maintenance file
341	} else {
342		return @unlink($file);
343	}
344}
345
346