1<?php
2/**
3 * Caching functions.
4 *
5 * This file contains all of the functions used to generate the cache files used by the site.
6 *
7 * @copyright (C) 2008-2012 PunBB, partially based on code (C) 2008-2009 FluxBB.org
8 * @license http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
9 * @package PunBB
10 */
11
12
13// Make sure no one attempts to run this script "directly"
14if (!defined('FORUM'))
15	exit;
16
17
18// Safe create or write of cache files
19// Use LOCK
20function write_cache_file($file, $content)
21{
22	// Open
23	$handle = @fopen($file, 'r+b'); // @ - file may not exist
24	if (!$handle)
25	{
26		$handle = fopen($file, 'wb');
27		if (!$handle)
28		{
29			return false;
30		}
31	}
32
33	// Lock
34	flock($handle, LOCK_EX);
35	ftruncate($handle, 0);
36
37	// Write
38	if (fwrite($handle, $content) === false)
39	{
40		// Unlock and close
41		flock($handle, LOCK_UN);
42		fclose($handle);
43
44		return false;
45	}
46
47	// Unlock and close
48	flock($handle, LOCK_UN);
49	fclose($handle);
50
51	// Force opcache to recompile this script
52	if (function_exists('opcache_invalidate')) {
53		opcache_invalidate($file, true);
54	}
55
56	return true;
57}
58
59
60//
61// Generate the config cache PHP script
62//
63function generate_config_cache()
64{
65	global $forum_db;
66
67	$return = ($hook = get_hook('ch_fn_generate_config_cache_start')) ? eval($hook) : null;
68	if ($return !== null)
69		return;
70
71	// Get the forum config from the DB
72	$query = array(
73		'SELECT'	=> 'c.*',
74		'FROM'		=> 'config AS c'
75	);
76
77	($hook = get_hook('ch_fn_generate_config_cache_qr_get_config')) ? eval($hook) : null;
78	$result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
79
80	$output = array();
81	while ($cur_config_item = $forum_db->fetch_assoc($result))
82		$output[$cur_config_item['conf_name']] = $cur_config_item['conf_value'];
83
84	// Output config as PHP code
85	if (!write_cache_file(FORUM_CACHE_DIR.'cache_config.php', '<?php'."\n\n".'define(\'FORUM_CONFIG_LOADED\', 1);'."\n\n".'$forum_config = '.var_export($output, true).';'."\n\n".'?>'))
86	{
87		error('Unable to write configuration cache file to cache directory.<br />Please make sure PHP has write access to the directory \'cache\'.', __FILE__, __LINE__);
88	}
89}
90
91
92//
93// Generate the bans cache PHP script
94//
95function generate_bans_cache()
96{
97	global $forum_db;
98
99	$return = ($hook = get_hook('ch_fn_generate_bans_cache_start')) ? eval($hook) : null;
100	if ($return !== null)
101		return;
102
103	// Get the ban list from the DB
104	$query = array(
105		'SELECT'	=> 'b.*, u.username AS ban_creator_username',
106		'FROM'		=> 'bans AS b',
107		'JOINS'		=> array(
108			array(
109				'LEFT JOIN'		=> 'users AS u',
110				'ON'			=> 'u.id=b.ban_creator'
111			)
112		),
113		'ORDER BY'	=> 'b.id'
114	);
115
116	($hook = get_hook('ch_fn_generate_bans_cache_qr_get_bans')) ? eval($hook) : null;
117	$result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
118
119	$output = array();
120	while ($cur_ban = $forum_db->fetch_assoc($result))
121		$output[] = $cur_ban;
122
123	// Output ban list as PHP code
124	if (!write_cache_file(FORUM_CACHE_DIR.'cache_bans.php', '<?php'."\n\n".'define(\'FORUM_BANS_LOADED\', 1);'."\n\n".'$forum_bans = '.var_export($output, true).';'."\n\n".'?>'))
125	{
126		error('Unable to write bans cache file to cache directory.<br />Please make sure PHP has write access to the directory \'cache\'.', __FILE__, __LINE__);
127	}
128}
129
130
131//
132// Generate the ranks cache PHP script
133//
134function generate_ranks_cache()
135{
136	global $forum_db;
137
138	$return = ($hook = get_hook('ch_fn_generate_ranks_cache_start')) ? eval($hook) : null;
139	if ($return !== null)
140		return;
141
142	// Get the rank list from the DB
143	$query = array(
144		'SELECT'	=> 'r.*',
145		'FROM'		=> 'ranks AS r',
146		'ORDER BY'	=> 'r.min_posts'
147	);
148
149	($hook = get_hook('ch_fn_generate_ranks_cache_qr_get_ranks')) ? eval($hook) : null;
150	$result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
151
152	$output = array();
153	while ($cur_rank = $forum_db->fetch_assoc($result))
154		$output[] = $cur_rank;
155
156	// Output ranks list as PHP code
157	if (!write_cache_file(FORUM_CACHE_DIR.'cache_ranks.php', '<?php'."\n\n".'define(\'FORUM_RANKS_LOADED\', 1);'."\n\n".'$forum_ranks = '.var_export($output, true).';'."\n\n".'?>'))
158	{
159		error('Unable to write ranks cache file to cache directory.<br />Please make sure PHP has write access to the directory \'cache\'.', __FILE__, __LINE__);
160	}
161}
162
163
164//
165// Generate the forum stats cache PHP script
166//
167function generate_stats_cache()
168{
169	global $forum_db;
170
171	$stats = array();
172
173	$return = ($hook = get_hook('ch_fn_generate_stats_cache_start')) ? eval($hook) : null;
174	if ($return !== null)
175		return;
176
177	// Collect some statistics from the database
178	$query = array(
179		'SELECT'	=> 'COUNT(u.id) - 1',
180		'FROM'		=> 'users AS u',
181		'WHERE'		=> 'u.group_id != '.FORUM_UNVERIFIED
182	);
183
184	($hook = get_hook('ch_fn_generate_stats_cache_qr_get_user_count')) ? eval($hook) : null;
185	$result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
186	$stats['total_users'] = $forum_db->result($result);
187
188
189	// Get last registered user info
190	$query = array(
191		'SELECT'	=> 'u.id, u.username',
192		'FROM'		=> 'users AS u',
193		'WHERE'		=> 'u.group_id != '.FORUM_UNVERIFIED,
194		'ORDER BY'	=> 'u.registered DESC',
195		'LIMIT'		=> '1'
196	);
197
198	($hook = get_hook('ch_fn_generate_stats_cache_qr_get_newest_user')) ? eval($hook) : null;
199	$result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
200	$stats['last_user'] = $forum_db->fetch_assoc($result);
201
202	// Get num topics and posts
203	$query = array(
204		'SELECT'	=> 'SUM(f.num_topics) AS num_topics, SUM(f.num_posts) AS num_posts',
205		'FROM'		=> 'forums AS f'
206	);
207
208	($hook = get_hook('ch_fn_generate_stats_cache_qr_get_post_stats')) ? eval($hook) : null;
209	$result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
210
211	$stats_topics_and_posts = $forum_db->fetch_assoc($result);
212	$stats['total_topics'] = $stats_topics_and_posts['num_topics'];
213	$stats['total_posts'] = $stats_topics_and_posts['num_posts'];
214
215	$stats['cached'] = time();
216
217	// Output ranks list as PHP code
218	if (!write_cache_file(FORUM_CACHE_DIR.'cache_stats.php', '<?php'."\n\n".'if (!defined(\'FORUM_STATS_LOADED\')) define(\'FORUM_STATS_LOADED\', 1);'."\n\n".'$forum_stats = '.var_export($stats, true).';'."\n\n".'?>'))
219	{
220		error('Unable to write stats cache file to cache directory.<br />Please make sure PHP has write access to the directory \'cache\'.', __FILE__, __LINE__);
221	}
222
223	unset($stats);
224}
225
226
227//
228// Clean stats cache PHP scripts
229//
230function clean_stats_cache()
231{
232	$cache_file = FORUM_CACHE_DIR.'cache_stats.php';
233	if (file_exists($cache_file))
234	{
235		unlink($cache_file);
236	}
237}
238
239
240//
241// Generate the censor cache PHP script
242//
243function generate_censors_cache()
244{
245	global $forum_db;
246
247	$return = ($hook = get_hook('ch_fn_generate_censors_cache_start')) ? eval($hook) : null;
248	if ($return !== null)
249		return;
250
251	// Get the censor list from the DB
252	$query = array(
253		'SELECT'	=> 'c.*',
254		'FROM'		=> 'censoring AS c',
255		'ORDER BY'	=> 'c.search_for'
256	);
257
258	($hook = get_hook('ch_fn_generate_censors_cache_qr_get_censored_words')) ? eval($hook) : null;
259	$result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
260
261	$output = array();
262	while ($cur_censor = $forum_db->fetch_assoc($result))
263		$output[] = $cur_censor;
264
265	// Output censors list as PHP code
266	if (!write_cache_file(FORUM_CACHE_DIR.'cache_censors.php', '<?php'."\n\n".'define(\'FORUM_CENSORS_LOADED\', 1);'."\n\n".'$forum_censors = '.var_export($output, true).';'."\n\n".'?>'))
267	{
268		error('Unable to write censor cache file to cache directory.<br />Please make sure PHP has write access to the directory \'cache\'.', __FILE__, __LINE__);
269	}
270}
271
272
273//
274// Generate quickjump cache PHP scripts
275//
276function generate_quickjump_cache($group_id = false)
277{
278	global $forum_db, $lang_common, $forum_url, $forum_config, $forum_user, $base_url;
279
280	$return = ($hook = get_hook('ch_fn_generate_quickjump_cache_start')) ? eval($hook) : null;
281	if ($return !== null)
282		return;
283
284	$groups = array();
285
286	// If a group_id was supplied, we generate the quickjump cache for that group only
287	if ($group_id !== false)
288		$groups[0] = $group_id;
289	else
290	{
291		// A group_id was not supplied, so we generate the quickjump cache for all groups
292		$query = array(
293			'SELECT'	=> 'g.g_id',
294			'FROM'		=> 'groups AS g'
295		);
296
297		($hook = get_hook('ch_fn_generate_quickjump_cache_qr_get_groups')) ? eval($hook) : null;
298		$result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
299
300		while ($cur_group = $forum_db->fetch_assoc($result))
301		{
302			$groups[] = $cur_group['g_id'];
303		}
304	}
305
306	// Loop through the groups in $groups and output the cache for each of them
307	foreach ($groups as $group_id)
308	{
309		$output = '<?php'."\n\n".'if (!defined(\'FORUM\')) exit;'."\n".'define(\'FORUM_QJ_LOADED\', 1);'."\n".'$forum_id = isset($forum_id) ? $forum_id : 0;'."\n\n".'?>';
310		$output .= '<form id="qjump" method="get" accept-charset="utf-8" action="'.$base_url.'/viewforum.php">'."\n\t".'<div class="frm-fld frm-select">'."\n\t\t".'<label for="qjump-select"><span><?php echo $lang_common[\'Jump to\'] ?>'.'</span></label><br />'."\n\t\t".'<span class="frm-input"><select id="qjump-select" name="id">'."\n";
311
312		// Get the list of categories and forums from the DB
313		$query = array(
314			'SELECT'	=> 'c.id AS cid, c.cat_name, f.id AS fid, f.forum_name, f.redirect_url',
315			'FROM'		=> 'categories AS c',
316			'JOINS'		=> array(
317				array(
318					'INNER JOIN'	=> 'forums AS f',
319					'ON'			=> 'c.id=f.cat_id'
320				),
321				array(
322					'LEFT JOIN'		=> 'forum_perms AS fp',
323					'ON'			=> '(fp.forum_id=f.id AND fp.group_id='.$group_id.')'
324				)
325			),
326			'WHERE'		=> 'fp.read_forum IS NULL OR fp.read_forum=1',
327			'ORDER BY'	=> 'c.disp_position, c.id, f.disp_position'
328		);
329
330		($hook = get_hook('ch_fn_generate_quickjump_cache_qr_get_cats_and_forums')) ? eval($hook) : null;
331		$result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
332
333		$forums = array();
334		while ($cur_forum = $forum_db->fetch_assoc($result))
335		{
336			$forums[] = $cur_forum;
337		}
338
339		$cur_category = 0;
340		$forum_count = 0;
341		$sef_friendly_names = array();
342		foreach ($forums as $cur_forum)
343		{
344			($hook = get_hook('ch_fn_generate_quickjump_cache_forum_loop_start')) ? eval($hook) : null;
345
346			if ($cur_forum['cid'] != $cur_category)	// A new category since last iteration?
347			{
348				if ($cur_category)
349					$output .= "\t\t\t".'</optgroup>'."\n";
350
351				$output .= "\t\t\t".'<optgroup label="'.forum_htmlencode($cur_forum['cat_name']).'">'."\n";
352				$cur_category = $cur_forum['cid'];
353			}
354
355			$sef_friendly_names[$cur_forum['fid']] = sef_friendly($cur_forum['forum_name']);
356			$redirect_tag = ($cur_forum['redirect_url'] != '') ? ' &gt;&gt;&gt;' : '';
357			$output .= "\t\t\t\t".'<option value="'.$cur_forum['fid'].'"<?php echo ($forum_id == '.$cur_forum['fid'].') ? \' selected="selected"\' : \'\' ?>>'.forum_htmlencode($cur_forum['forum_name']).$redirect_tag.'</option>'."\n";
358			$forum_count++;
359
360			($hook = get_hook('ch_fn_generate_quickjump_cache_forum_loop_end')) ? eval($hook) : null;
361		}
362
363		$output .= "\t\t\t".'</optgroup>'."\n\t\t".'</select>'."\n\t\t".'<input type="submit" id="qjump-submit" value="<?php echo $lang_common[\'Go\'] ?>" /></span>'."\n\t".'</div>'."\n".'</form>'."\n";
364		$output_js = "\n".'(function () {'."\n\t".'var forum_quickjump_url = "'.forum_link($forum_url['forum']).'";'."\n\t".'var sef_friendly_url_array = new Array('.count($forums).');';
365
366		foreach ($sef_friendly_names as $forum_id => $forum_name)
367			$output_js .= "\n\t".'sef_friendly_url_array['.$forum_id.'] = "'.forum_htmlencode($forum_name).'";';
368
369		// Add Load Event
370		$output_js .= "\n\n\t".'PUNBB.common.addDOMReadyEvent(function () { PUNBB.common.attachQuickjumpRedirect(forum_quickjump_url, sef_friendly_url_array); });'."\n".'}());';
371
372		if ($forum_count < 2)
373			$output = '<?php'."\n\n".'if (!defined(\'FORUM\')) exit;'."\n".'define(\'FORUM_QJ_LOADED\', 1);';
374		else
375			$output .= '<?php'."\n\n".'$forum_javascript_quickjump_code = <<<EOL'.$output_js."\nEOL;\n\n".'$forum_loader->add_js($forum_javascript_quickjump_code, array(\'type\' => \'inline\', \'weight\' => 60, \'group\' => FORUM_JS_GROUP_SYSTEM));'."\n".'?>'."\n";
376
377		// Output quickjump as PHP code
378		if (!write_cache_file(FORUM_CACHE_DIR.'cache_quickjump_'.$group_id.'.php', $output))
379		{
380			error('Unable to write quickjump cache file to cache directory.<br />Please make sure PHP has write access to the directory \'cache\'.', __FILE__, __LINE__);
381		}
382	}
383}
384
385
386//
387// Clean quickjump cache PHP scripts
388//
389function clean_quickjump_cache($group_id = false)
390{
391	global $forum_db;
392
393	$return = ($hook = get_hook('ch_fn_clean_quickjump_cache_start')) ? eval($hook) : null;
394	if ($return !== null)
395		return;
396
397	$groups = array();
398
399	// If a group_id was supplied, we generate the quickjump cache for that group only
400	if ($group_id !== false)
401		$groups[0] = $group_id;
402	else
403	{
404		// A group_id was not supplied, so we generate the quickjump cache for all groups
405		$query = array(
406			'SELECT'	=> 'g.g_id',
407			'FROM'		=> 'groups AS g'
408		);
409
410		($hook = get_hook('ch_fn_clean_quickjump_cache_qr_get_groups')) ? eval($hook) : null;
411		$result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
412
413		while ($cur_group = $forum_db->fetch_assoc($result))
414		{
415			$groups[] = $cur_group['g_id'];
416		}
417	}
418
419	// Loop through the groups in $groups and output the cache for each of them
420	foreach ($groups as $group_id)
421	{
422		// Output quickjump as PHP code
423		$qj_cache_file = FORUM_CACHE_DIR.'cache_quickjump_'.$group_id.'.php';
424		if (file_exists($qj_cache_file))
425		{
426			unlink($qj_cache_file);
427		}
428	}
429}
430
431
432
433//
434// Generate the hooks cache PHP script
435//
436function generate_hooks_cache()
437{
438	global $forum_db, $forum_config, $base_url;
439
440	$return = ($hook = get_hook('ch_fn_generate_hooks_cache_start')) ? eval($hook) : null;
441	if ($return !== null)
442		return;
443
444	// Get the hooks from the DB
445	$query = array(
446		'SELECT'	=> 'eh.id, eh.code, eh.extension_id, e.dependencies',
447		'FROM'		=> 'extension_hooks AS eh',
448		'JOINS'		=> array(
449			array(
450				'INNER JOIN'	=> 'extensions AS e',
451				'ON'			=> 'e.id=eh.extension_id'
452			)
453		),
454		'WHERE'		=> 'e.disabled=0',
455		'ORDER BY'	=> 'eh.priority, eh.installed'
456	);
457
458	($hook = get_hook('ch_fn_generate_hooks_cache_qr_get_hooks')) ? eval($hook) : null;
459	$result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
460
461	$output = array();
462	while ($cur_hook = $forum_db->fetch_assoc($result))
463	{
464		$load_ext_info = '$GLOBALS[\'ext_info_stack\'][] = array('."\n".
465			'\'id\'				=> \''.$cur_hook['extension_id'].'\','."\n".
466			'\'path\'			=> FORUM_ROOT.\'extensions/'.$cur_hook['extension_id'].'\','."\n".
467			'\'url\'			=> $GLOBALS[\'base_url\'].\'/extensions/'.$cur_hook['extension_id'].'\','."\n".
468			'\'dependencies\'	=> array ('."\n";
469
470		$dependencies = explode('|', substr($cur_hook['dependencies'], 1, -1));
471		foreach ($dependencies as $cur_dependency)
472		{
473			// This happens if there are no dependencies because explode ends up returning an array with one empty element
474			if (empty($cur_dependency))
475				continue;
476
477			$load_ext_info .= '\''.$cur_dependency.'\'	=> array('."\n".
478				'\'id\'				=> \''.$cur_dependency.'\','."\n".
479				'\'path\'			=> FORUM_ROOT.\'extensions/'.$cur_dependency.'\','."\n".
480				'\'url\'			=> $GLOBALS[\'base_url\'].\'/extensions/'.$cur_dependency.'\'),'."\n";
481		}
482
483		$load_ext_info .= ')'."\n".');'."\n".'$ext_info = $GLOBALS[\'ext_info_stack\'][count($GLOBALS[\'ext_info_stack\']) - 1];';
484		$unload_ext_info = 'array_pop($GLOBALS[\'ext_info_stack\']);'."\n".'$ext_info = empty($GLOBALS[\'ext_info_stack\']) ? array() : $GLOBALS[\'ext_info_stack\'][count($GLOBALS[\'ext_info_stack\']) - 1];';
485
486		$output[$cur_hook['id']][] = $load_ext_info."\n\n".$cur_hook['code']."\n\n".$unload_ext_info."\n";
487	}
488
489	// Output hooks as PHP code
490	if (!write_cache_file(FORUM_CACHE_DIR.'cache_hooks.php', '<?php'."\n\n".'define(\'FORUM_HOOKS_LOADED\', 1);'."\n\n".'$forum_hooks = '.var_export($output, true).';'."\n\n".'?>'))
491	{
492		error('Unable to write hooks cache file to cache directory.<br />Please make sure PHP has write access to the directory \'cache\'.', __FILE__, __LINE__);
493	}
494}
495
496
497//
498// Generate the updates cache PHP script
499//
500function generate_updates_cache()
501{
502	global $forum_db, $forum_config;
503
504	$return = ($hook = get_hook('ch_fn_generate_updates_cache_start')) ? eval($hook) : null;
505	if ($return !== null)
506		return;
507
508	// Get a list of installed hotfix extensions
509	$query = array(
510		'SELECT'	=> 'e.id',
511		'FROM'		=> 'extensions AS e',
512		'WHERE'		=> 'e.id LIKE \'hotfix_%\''
513	);
514
515	($hook = get_hook('ch_fn_generate_updates_cache_qr_get_hotfixes')) ? eval($hook) : null;
516	$result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
517
518	$hotfixes = array();
519	while ($cur_ext_hotfix = $forum_db->fetch_assoc($result))
520	{
521		$hotfixes[] = urlencode($cur_ext_hotfix['id']);
522	}
523
524	// Contact the punbb.informer.com updates service
525	$result = get_remote_file('https://punbb.informer.com/update/?type=xml&version='.urlencode($forum_config['o_cur_version']).'&hotfixes='.implode(',', $hotfixes), 8);
526
527	// Make sure we got everything we need
528	if ($result != null && strpos($result['content'], '</updates>') !== false)
529	{
530		if (!defined('FORUM_XML_FUNCTIONS_LOADED'))
531			require FORUM_ROOT.'include/xml.php';
532
533		$output = xml_to_array(forum_trim($result['content']));
534		$output = current($output);
535
536		if (!empty($output['hotfix']) && is_array($output['hotfix']) && !is_array(current($output['hotfix'])))
537			$output['hotfix'] = array($output['hotfix']);
538
539		$output['cached'] = time();
540		$output['fail'] = false;
541	}
542	else	// If the update check failed, set the fail flag
543		$output = array('cached' => time(), 'fail' => true);
544
545	// This hook could potentially (and responsibly) be used by an extension to do its own little update check
546	($hook = get_hook('ch_fn_generate_updates_cache_write')) ? eval($hook) : null;
547
548	// Output update status as PHP code
549	if (!write_cache_file(FORUM_CACHE_DIR.'cache_updates.php', '<?php'."\n\n".'if (!defined(\'FORUM_UPDATES_LOADED\')) define(\'FORUM_UPDATES_LOADED\', 1);'."\n\n".'$forum_updates = '.var_export($output, true).';'."\n\n".'?>'))
550	{
551		error('Unable to write updates cache file to cache directory.<br />Please make sure PHP has write access to the directory \'cache\'.', __FILE__, __LINE__);
552	}
553}
554
555function generate_ext_versions_cache($inst_exts, $repository_urls, $repository_url_by_extension)
556{
557	$forum_ext_last_versions = array();
558	$forum_ext_repos = array();
559
560	foreach (array_unique(array_merge($repository_urls, $repository_url_by_extension)) as $url)
561	{
562		// Get repository timestamp
563		$remote_file = get_remote_file($url.'/timestamp', 2);
564		$repository_timestamp = empty($remote_file['content']) ? '' : forum_trim($remote_file['content']);
565		unset($remote_file);
566		if (!is_numeric($repository_timestamp))
567			continue;
568
569		if (!isset($forum_ext_repos[$url]['timestamp']))
570			$forum_ext_repos[$url]['timestamp'] = $repository_timestamp;
571
572		if ($forum_ext_repos[$url]['timestamp'] <= $repository_timestamp)
573		{
574			foreach ($inst_exts as $ext)
575			{
576
577			    if ((0 === strpos($ext['id'], 'pun_') AND FORUM_PUN_EXTENSION_REPOSITORY_URL != $url) OR
578			            ((FALSE === strpos($ext['id'], 'pun_') AND !isset($ext['repo_url'])) OR (isset($ext['repo_url']) AND $ext['repo_url'] != $url)))
579			        continue;
580
581				$remote_file = get_remote_file($url.'/'.$ext['id'].'/lastversion', 2);
582				$version = empty($remote_file['content']) ? '' : forum_trim($remote_file['content']);
583				unset($remote_file);
584				if (empty($version) || !preg_match('~^[0-9a-zA-Z\. +-]+$~u', $version))
585					continue;
586
587				$forum_ext_repos[$url]['extension_versions'][$ext['id']] = $version;
588
589				// If key with current extension exist in array, compare it with version in repository
590				if (!isset($forum_ext_last_versions[$ext['id']]) || (version_compare($forum_ext_last_versions[$ext['id']]['version'], $version, '<')))
591				{
592					$forum_ext_last_versions[$ext['id']] = array('version' => $version, 'repo_url' => $url);
593
594					$remote_file = get_remote_file($url.'/'.$ext['id'].'/lastchanges', 2);
595					$last_changes = empty($remote_file['content']) ? '' : forum_trim($remote_file['content']);
596					unset($remote_file);
597					if (!empty($last_changes))
598						$forum_ext_last_versions[$ext['id']]['changes'] = $last_changes;
599				}
600			}
601
602			// Write timestamp to cache
603			$forum_ext_repos[$url]['timestamp'] = $repository_timestamp;
604		}
605	}
606
607	if (array_keys($forum_ext_last_versions) != array_keys($inst_exts))
608		foreach ($inst_exts as $ext)
609			if (!in_array($ext['id'], array_keys($forum_ext_last_versions)))
610				$forum_ext_last_versions[$ext['id']] = array('version' => $ext['version'], 'repo_url' => '', 'changes' => '');
611
612	($hook = get_hook('ch_generate_ext_versions_cache_check_repository')) ? eval($hook) : null;
613
614	// Output config as PHP code
615	if (!write_cache_file(FORUM_CACHE_DIR.'cache_ext_version_notifications.php', '<?php'."\n\n".'if (!defined(\'FORUM_EXT_VERSIONS_LOADED\')) define(\'FORUM_EXT_VERSIONS_LOADED\', 1);'."\n\n".'$forum_ext_repos = '.var_export($forum_ext_repos, true).';'."\n\n".' $forum_ext_last_versions = '.var_export($forum_ext_last_versions, true).";\n\n".'$forum_ext_versions_update_cache = '.time().";\n\n".'?>'))
616	{
617		error('Unable to write configuration cache file to cache directory.<br />Please make sure PHP has write access to the directory \'cache\'.', __FILE__, __LINE__);
618	}
619}
620
621define('FORUM_CACHE_FUNCTIONS_LOADED', 1);
622