comparison forum/Sources/Load.php @ 76:e3e11437ecea website

Add forum code
author Chris Cannam
date Sun, 07 Jul 2013 11:25:48 +0200
parents
children
comparison
equal deleted inserted replaced
75:72f59aa7e503 76:e3e11437ecea
1 <?php
2
3 /**
4 * Simple Machines Forum (SMF)
5 *
6 * @package SMF
7 * @author Simple Machines http://www.simplemachines.org
8 * @copyright 2011 Simple Machines
9 * @license http://www.simplemachines.org/about/smf/license.php BSD
10 *
11 * @version 2.0.3
12 */
13
14 if (!defined('SMF'))
15 die('Hacking attempt...');
16
17 /* This file has the hefty job of loading information for the forum. It uses
18 the following functions:
19
20 void reloadSettings()
21 - loads or reloads the $modSettings array.
22 - loads any integration settings, SMF_INTEGRATION_SETTINGS, etc.
23
24 void loadUserSettings()
25 - sets up the $user_info array
26 - assigns $user_info['query_wanna_see_board'] for what boards the user can see.
27 - first checks for cookie or intergration validation.
28 - uses the current session if no integration function or cookie is found.
29 - checks password length, if member is activated and the login span isn't over.
30 - if validation fails for the user, $id_member is set to 0.
31 - updates the last visit time when needed.
32
33 void loadBoard()
34 - sets up the $board_info array for current board information.
35 - if cache is enabled, the $board_info array is stored in cache.
36 - redirects to appropriate post if only message id is requested.
37 - is only used when inside a topic or board.
38 - determines the local moderators for the board.
39 - adds group id 3 if the user is a local moderator for the board they are in.
40 - prevents access if user is not in proper group nor a local moderator of the board.
41
42 void loadPermissions()
43 // !!!
44
45 array loadMemberData(array members, bool is_name = false, string set = 'normal')
46 // !!!
47
48 bool loadMemberContext(int id_member)
49 // !!!
50
51 void loadTheme(int id_theme = auto_detect)
52 // !!!
53
54 void loadTemplate(string template_name, array style_sheets = array(), bool fatal = true)
55 - loads a template file with the name template_name from the current,
56 default, or base theme.
57 - uses the template_include() function to include the file.
58 - detects a wrong default theme directory and tries to work around it.
59 - if fatal is true, dies with an error message if the template cannot
60 be found.
61
62 void loadSubTemplate(string sub_template_name, bool fatal = false)
63 - loads the sub template specified by sub_template_name, which must be
64 in an already-loaded template.
65 - if ?debug is in the query string, shows administrators a marker after
66 every sub template for debugging purposes.
67
68 string loadLanguage(string template_name, string language = default, bool fatal = true, bool force_reload = false)
69 // !!!
70
71 array getBoardParents(int id_parent)
72 - finds all the parents of id_parent, and that board itself.
73 - additionally detects the moderators of said boards.
74 - returns an array of information about the boards found.
75
76 string &censorText(string &text, bool force = false)
77 - censors the passed string.
78 - if the theme setting allow_no_censored is on, and the theme option
79 show_no_censored is enabled, does not censor - unless force is set.
80 - caches the list of censored words to reduce parsing.
81
82 void template_include(string filename, bool only_once = false)
83 - loads the template or language file specified by filename.
84 - if once is true, only includes the file once (like include_once.)
85 - uses eval unless disableTemplateEval is enabled.
86 - outputs a parse error if the file did not exist or contained errors.
87 - attempts to detect the error and line, and show detailed information.
88
89 void loadSession()
90 // !!!
91
92 void loadDatabase()
93 - takes care of mysql_set_mode, if set.
94 // !!!
95
96 bool sessionOpen(string session_save_path, string session_name)
97 bool sessionClose()
98 bool sessionRead(string session_id)
99 bool sessionWrite(string session_id, string data)
100 bool sessionDestroy(string session_id)
101 bool sessionGC(int max_lifetime)
102 - implementations of PHP's session API.
103 - handle the session data in the database (more scalable.)
104 - use the databaseSession_lifetime setting for garbage collection.
105 - set by loadSession().
106
107 void cache_put_data(string key, mixed value, int ttl = 120)
108 - puts value in the cache under key for ttl seconds.
109 - may "miss" so shouldn't be depended on, and may go to any of many
110 various caching servers.
111 - supports eAccelerator, Turck MMCache, ZPS, and memcached.
112
113 mixed cache_get_data(string key, int ttl = 120)
114 - gets the value from the cache specified by key, so long as it is not
115 older than ttl seconds.
116 - may often "miss", so shouldn't be depended on.
117 - supports the same as cache_put_data().
118
119 void get_memcached_server(int recursion_level = 3)
120 - used by cache_get_data() and cache_put_data().
121 - attempts to connect to a random server in the cache_memcached
122 setting.
123 - recursively calls itself up to recursion_level times.
124 */
125
126 // Load the $modSettings array.
127 function reloadSettings()
128 {
129 global $modSettings, $boarddir, $smcFunc, $txt, $db_character_set, $context, $sourcedir;
130
131 // Most database systems have not set UTF-8 as their default input charset.
132 if (!empty($db_character_set))
133 $smcFunc['db_query']('set_character_set', '
134 SET NAMES ' . $db_character_set,
135 array(
136 )
137 );
138
139 // Try to load it from the cache first; it'll never get cached if the setting is off.
140 if (($modSettings = cache_get_data('modSettings', 90)) == null)
141 {
142 $request = $smcFunc['db_query']('', '
143 SELECT variable, value
144 FROM {db_prefix}settings',
145 array(
146 )
147 );
148 $modSettings = array();
149 if (!$request)
150 db_fatal_error();
151 while ($row = $smcFunc['db_fetch_row']($request))
152 $modSettings[$row[0]] = $row[1];
153 $smcFunc['db_free_result']($request);
154
155 // Do a few things to protect against missing settings or settings with invalid values...
156 if (empty($modSettings['defaultMaxTopics']) || $modSettings['defaultMaxTopics'] <= 0 || $modSettings['defaultMaxTopics'] > 999)
157 $modSettings['defaultMaxTopics'] = 20;
158 if (empty($modSettings['defaultMaxMessages']) || $modSettings['defaultMaxMessages'] <= 0 || $modSettings['defaultMaxMessages'] > 999)
159 $modSettings['defaultMaxMessages'] = 15;
160 if (empty($modSettings['defaultMaxMembers']) || $modSettings['defaultMaxMembers'] <= 0 || $modSettings['defaultMaxMembers'] > 999)
161 $modSettings['defaultMaxMembers'] = 30;
162
163 if (!empty($modSettings['cache_enable']))
164 cache_put_data('modSettings', $modSettings, 90);
165 }
166
167 // UTF-8 in regular expressions is unsupported on PHP(win) versions < 4.2.3.
168 $utf8 = (empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8' && (strpos(strtolower(PHP_OS), 'win') === false || @version_compare(PHP_VERSION, '4.2.3') != -1);
169
170 // Set a list of common functions.
171 $ent_list = empty($modSettings['disableEntityCheck']) ? '&(#\d{1,7}|quot|amp|lt|gt|nbsp);' : '&(#021|quot|amp|lt|gt|nbsp);';
172 $ent_check = empty($modSettings['disableEntityCheck']) ? array('preg_replace(\'~(&#(\d{1,7}|x[0-9a-fA-F]{1,6});)~e\', \'$smcFunc[\\\'entity_fix\\\'](\\\'\\2\\\')\', ', ')') : array('', '');
173
174 // Preg_replace can handle complex characters only for higher PHP versions.
175 $space_chars = $utf8 ? (@version_compare(PHP_VERSION, '4.3.3') != -1 ? '\x{A0}\x{AD}\x{2000}-\x{200F}\x{201F}\x{202F}\x{3000}\x{FEFF}' : "\xC2\xA0\xC2\xAD\xE2\x80\x80-\xE2\x80\x8F\xE2\x80\x9F\xE2\x80\xAF\xE2\x80\x9F\xE3\x80\x80\xEF\xBB\xBF") : '\x00-\x08\x0B\x0C\x0E-\x19\xA0';
176
177 $smcFunc += array(
178 'entity_fix' => create_function('$string', '
179 $num = substr($string, 0, 1) === \'x\' ? hexdec(substr($string, 1)) : (int) $string;
180 return $num < 0x20 || $num > 0x10FFFF || ($num >= 0xD800 && $num <= 0xDFFF) || $num === 0x202E || $num === 0x202D ? \'\' : \'&#\' . $num . \';\';'),
181 'htmlspecialchars' => create_function('$string, $quote_style = ENT_COMPAT, $charset = \'ISO-8859-1\'', '
182 global $smcFunc;
183 return ' . strtr($ent_check[0], array('&' => '&amp;')) . 'htmlspecialchars($string, $quote_style, ' . ($utf8 ? '\'UTF-8\'' : '$charset') . ')' . $ent_check[1] . ';'),
184 'htmltrim' => create_function('$string', '
185 global $smcFunc;
186 return preg_replace(\'~^(?:[ \t\n\r\x0B\x00' . $space_chars . ']|&nbsp;)+|(?:[ \t\n\r\x0B\x00' . $space_chars . ']|&nbsp;)+$~' . ($utf8 ? 'u' : '') . '\', \'\', ' . implode('$string', $ent_check) . ');'),
187 'strlen' => create_function('$string', '
188 global $smcFunc;
189 return strlen(preg_replace(\'~' . $ent_list . ($utf8 ? '|.~u' : '~') . '\', \'_\', ' . implode('$string', $ent_check) . '));'),
190 'strpos' => create_function('$haystack, $needle, $offset = 0', '
191 global $smcFunc;
192 $haystack_arr = preg_split(\'~(&#' . (empty($modSettings['disableEntityCheck']) ? '\d{1,7}' : '021') . ';|&quot;|&amp;|&lt;|&gt;|&nbsp;|.)~' . ($utf8 ? 'u' : '') . '\', ' . implode('$haystack', $ent_check) . ', -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
193 $haystack_size = count($haystack_arr);
194 if (strlen($needle) === 1)
195 {
196 $result = array_search($needle, array_slice($haystack_arr, $offset));
197 return is_int($result) ? $result + $offset : false;
198 }
199 else
200 {
201 $needle_arr = preg_split(\'~(&#' . (empty($modSettings['disableEntityCheck']) ? '\d{1,7}' : '021') . ';|&quot;|&amp;|&lt;|&gt;|&nbsp;|.)~' . ($utf8 ? 'u' : '') . '\', ' . implode('$needle', $ent_check) . ', -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
202 $needle_size = count($needle_arr);
203
204 $result = array_search($needle_arr[0], array_slice($haystack_arr, $offset));
205 while (is_int($result))
206 {
207 $offset += $result;
208 if (array_slice($haystack_arr, $offset, $needle_size) === $needle_arr)
209 return $offset;
210 $result = array_search($needle_arr[0], array_slice($haystack_arr, ++$offset));
211 }
212 return false;
213 }'),
214 'substr' => create_function('$string, $start, $length = null', '
215 global $smcFunc;
216 $ent_arr = preg_split(\'~(&#' . (empty($modSettings['disableEntityCheck']) ? '\d{1,7}' : '021') . ';|&quot;|&amp;|&lt;|&gt;|&nbsp;|.)~' . ($utf8 ? 'u' : '') . '\', ' . implode('$string', $ent_check) . ', -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
217 return $length === null ? implode(\'\', array_slice($ent_arr, $start)) : implode(\'\', array_slice($ent_arr, $start, $length));'),
218 'strtolower' => $utf8 ? (function_exists('mb_strtolower') ? create_function('$string', '
219 return mb_strtolower($string, \'UTF-8\');') : create_function('$string', '
220 global $sourcedir;
221 require_once($sourcedir . \'/Subs-Charset.php\');
222 return utf8_strtolower($string);')) : 'strtolower',
223 'strtoupper' => $utf8 ? (function_exists('mb_strtoupper') ? create_function('$string', '
224 return mb_strtoupper($string, \'UTF-8\');') : create_function('$string', '
225 global $sourcedir;
226 require_once($sourcedir . \'/Subs-Charset.php\');
227 return utf8_strtoupper($string);')) : 'strtoupper',
228 'truncate' => create_function('$string, $length', (empty($modSettings['disableEntityCheck']) ? '
229 global $smcFunc;
230 $string = ' . implode('$string', $ent_check) . ';' : '') . '
231 preg_match(\'~^(' . $ent_list . '|.){\' . $smcFunc[\'strlen\'](substr($string, 0, $length)) . \'}~'. ($utf8 ? 'u' : '') . '\', $string, $matches);
232 $string = $matches[0];
233 while (strlen($string) > $length)
234 $string = preg_replace(\'~(?:' . $ent_list . '|.)$~'. ($utf8 ? 'u' : '') . '\', \'\', $string);
235 return $string;'),
236 'ucfirst' => $utf8 ? create_function('$string', '
237 global $smcFunc;
238 return $smcFunc[\'strtoupper\']($smcFunc[\'substr\']($string, 0, 1)) . $smcFunc[\'substr\']($string, 1);') : 'ucfirst',
239 'ucwords' => $utf8 ? create_function('$string', '
240 global $smcFunc;
241 $words = preg_split(\'~([\s\r\n\t]+)~\', $string, -1, PREG_SPLIT_DELIM_CAPTURE);
242 for ($i = 0, $n = count($words); $i < $n; $i += 2)
243 $words[$i] = $smcFunc[\'ucfirst\']($words[$i]);
244 return implode(\'\', $words);') : 'ucwords',
245 );
246
247 // Setting the timezone is a requirement for some functions in PHP >= 5.1.
248 if (isset($modSettings['default_timezone']) && function_exists('date_default_timezone_set'))
249 date_default_timezone_set($modSettings['default_timezone']);
250
251 // Check the load averages?
252 if (!empty($modSettings['loadavg_enable']))
253 {
254 if (($modSettings['load_average'] = cache_get_data('loadavg', 90)) == null)
255 {
256 $modSettings['load_average'] = @file_get_contents('/proc/loadavg');
257 if (!empty($modSettings['load_average']) && preg_match('~^([^ ]+?) ([^ ]+?) ([^ ]+)~', $modSettings['load_average'], $matches) != 0)
258 $modSettings['load_average'] = (float) $matches[1];
259 elseif (($modSettings['load_average'] = @`uptime`) != null && preg_match('~load average[s]?: (\d+\.\d+), (\d+\.\d+), (\d+\.\d+)~i', $modSettings['load_average'], $matches) != 0)
260 $modSettings['load_average'] = (float) $matches[1];
261 else
262 unset($modSettings['load_average']);
263
264 if (!empty($modSettings['load_average']))
265 cache_put_data('loadavg', $modSettings['load_average'], 90);
266 }
267
268 if (!empty($modSettings['loadavg_forum']) && !empty($modSettings['load_average']) && $modSettings['load_average'] >= $modSettings['loadavg_forum'])
269 db_fatal_error(true);
270 }
271
272 // Is post moderation alive and well?
273 $modSettings['postmod_active'] = isset($modSettings['admin_features']) ? in_array('pm', explode(',', $modSettings['admin_features'])) : true;
274
275 // Integration is cool.
276 if (defined('SMF_INTEGRATION_SETTINGS'))
277 {
278 $integration_settings = unserialize(SMF_INTEGRATION_SETTINGS);
279 foreach ($integration_settings as $hook => $function)
280 add_integration_function($hook, $function, false);
281 }
282
283 // Any files to pre include?
284 if (!empty($modSettings['integrate_pre_include']))
285 {
286 $pre_includes = explode(',', $modSettings['integrate_pre_include']);
287 foreach ($pre_includes as $include)
288 {
289 $include = strtr(trim($include), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir));
290 if (file_exists($include))
291 require_once($include);
292 }
293 }
294
295 // Call pre load integration functions.
296 call_integration_hook('integrate_pre_load');
297 }
298
299 // Load all the important user information...
300 function loadUserSettings()
301 {
302 global $modSettings, $user_settings, $sourcedir, $smcFunc;
303 global $cookiename, $user_info, $language;
304
305 // Check first the integration, then the cookie, and last the session.
306 if (count($integration_ids = call_integration_hook('integrate_verify_user')) > 0)
307 {
308 $id_member = 0;
309 foreach ($integration_ids as $integration_id)
310 {
311 $integration_id = (int) $integration_id;
312 if ($integration_id > 0)
313 {
314 $id_member = $integration_id;
315 $already_verified = true;
316 break;
317 }
318 }
319 }
320 else
321 $id_member = 0;
322
323 if (empty($id_member) && isset($_COOKIE[$cookiename]))
324 {
325 // Fix a security hole in PHP 4.3.9 and below...
326 if (preg_match('~^a:[34]:\{i:0;(i:\d{1,6}|s:[1-8]:"\d{1,8}");i:1;s:(0|40):"([a-fA-F0-9]{40})?";i:2;[id]:\d{1,14};(i:3;i:\d;)?\}$~i', $_COOKIE[$cookiename]) == 1)
327 {
328 list ($id_member, $password) = @unserialize($_COOKIE[$cookiename]);
329 $id_member = !empty($id_member) && strlen($password) > 0 ? (int) $id_member : 0;
330 }
331 else
332 $id_member = 0;
333 }
334 elseif (empty($id_member) && isset($_SESSION['login_' . $cookiename]) && ($_SESSION['USER_AGENT'] == $_SERVER['HTTP_USER_AGENT'] || !empty($modSettings['disableCheckUA'])))
335 {
336 // !!! Perhaps we can do some more checking on this, such as on the first octet of the IP?
337 list ($id_member, $password, $login_span) = @unserialize($_SESSION['login_' . $cookiename]);
338 $id_member = !empty($id_member) && strlen($password) == 40 && $login_span > time() ? (int) $id_member : 0;
339 }
340
341 // Only load this stuff if the user isn't a guest.
342 if ($id_member != 0)
343 {
344 // Is the member data cached?
345 if (empty($modSettings['cache_enable']) || $modSettings['cache_enable'] < 2 || ($user_settings = cache_get_data('user_settings-' . $id_member, 60)) == null)
346 {
347 $request = $smcFunc['db_query']('', '
348 SELECT mem.*, IFNULL(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type
349 FROM {db_prefix}members AS mem
350 LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = {int:id_member})
351 WHERE mem.id_member = {int:id_member}
352 LIMIT 1',
353 array(
354 'id_member' => $id_member,
355 )
356 );
357 $user_settings = $smcFunc['db_fetch_assoc']($request);
358 $smcFunc['db_free_result']($request);
359
360 if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
361 cache_put_data('user_settings-' . $id_member, $user_settings, 60);
362 }
363
364 // Did we find 'im? If not, junk it.
365 if (!empty($user_settings))
366 {
367 // As much as the password should be right, we can assume the integration set things up.
368 if (!empty($already_verified) && $already_verified === true)
369 $check = true;
370 // SHA-1 passwords should be 40 characters long.
371 elseif (strlen($password) == 40)
372 $check = sha1($user_settings['passwd'] . $user_settings['password_salt']) == $password;
373 else
374 $check = false;
375
376 // Wrong password or not activated - either way, you're going nowhere.
377 $id_member = $check && ($user_settings['is_activated'] == 1 || $user_settings['is_activated'] == 11) ? $user_settings['id_member'] : 0;
378 }
379 else
380 $id_member = 0;
381
382 // If we no longer have the member maybe they're being all hackey, stop brute force!
383 if (!$id_member)
384 {
385 require_once($sourcedir . '/LogInOut.php');
386 validatePasswordFlood(!empty($user_settings['id_member']) ? $user_settings['id_member'] : $id_member, !empty($user_settings['passwd_flood']) ? $user_settings['passwd_flood'] : false, $id_member != 0);
387 }
388 }
389
390 // Found 'im, let's set up the variables.
391 if ($id_member != 0)
392 {
393 // Let's not update the last visit time in these cases...
394 // 1. SSI doesn't count as visiting the forum.
395 // 2. RSS feeds and XMLHTTP requests don't count either.
396 // 3. If it was set within this session, no need to set it again.
397 // 4. New session, yet updated < five hours ago? Maybe cache can help.
398 if (SMF != 'SSI' && !isset($_REQUEST['xml']) && (!isset($_REQUEST['action']) || $_REQUEST['action'] != '.xml') && empty($_SESSION['id_msg_last_visit']) && (empty($modSettings['cache_enable']) || ($_SESSION['id_msg_last_visit'] = cache_get_data('user_last_visit-' . $id_member, 5 * 3600)) === null))
399 {
400 // Do a quick query to make sure this isn't a mistake.
401 $result = $smcFunc['db_query']('', '
402 SELECT poster_time
403 FROM {db_prefix}messages
404 WHERE id_msg = {int:id_msg}
405 LIMIT 1',
406 array(
407 'id_msg' => $user_settings['id_msg_last_visit'],
408 )
409 );
410 list ($visitTime) = $smcFunc['db_fetch_row']($result);
411 $smcFunc['db_free_result']($result);
412
413 $_SESSION['id_msg_last_visit'] = $user_settings['id_msg_last_visit'];
414
415 // If it was *at least* five hours ago...
416 if ($visitTime < time() - 5 * 3600)
417 {
418 updateMemberData($id_member, array('id_msg_last_visit' => (int) $modSettings['maxMsgID'], 'last_login' => time(), 'member_ip' => $_SERVER['REMOTE_ADDR'], 'member_ip2' => $_SERVER['BAN_CHECK_IP']));
419 $user_settings['last_login'] = time();
420
421 if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
422 cache_put_data('user_settings-' . $id_member, $user_settings, 60);
423
424 if (!empty($modSettings['cache_enable']))
425 cache_put_data('user_last_visit-' . $id_member, $_SESSION['id_msg_last_visit'], 5 * 3600);
426 }
427 }
428 elseif (empty($_SESSION['id_msg_last_visit']))
429 $_SESSION['id_msg_last_visit'] = $user_settings['id_msg_last_visit'];
430
431 $username = $user_settings['member_name'];
432
433 if (empty($user_settings['additional_groups']))
434 $user_info = array(
435 'groups' => array($user_settings['id_group'], $user_settings['id_post_group'])
436 );
437 else
438 $user_info = array(
439 'groups' => array_merge(
440 array($user_settings['id_group'], $user_settings['id_post_group']),
441 explode(',', $user_settings['additional_groups'])
442 )
443 );
444
445 // Because history has proven that it is possible for groups to go bad - clean up in case.
446 foreach ($user_info['groups'] as $k => $v)
447 $user_info['groups'][$k] = (int) $v;
448
449 // This is a logged in user, so definitely not a spider.
450 $user_info['possibly_robot'] = false;
451 }
452 // If the user is a guest, initialize all the critical user settings.
453 else
454 {
455 // This is what a guest's variables should be.
456 $username = '';
457 $user_info = array('groups' => array(-1));
458 $user_settings = array();
459
460 if (isset($_COOKIE[$cookiename]))
461 $_COOKIE[$cookiename] = '';
462
463 // Do we perhaps think this is a search robot? Check every five minutes just in case...
464 if ((!empty($modSettings['spider_mode']) || !empty($modSettings['spider_group'])) && (!isset($_SESSION['robot_check']) || $_SESSION['robot_check'] < time() - 300))
465 {
466 require_once($sourcedir . '/ManageSearchEngines.php');
467 $user_info['possibly_robot'] = SpiderCheck();
468 }
469 elseif (!empty($modSettings['spider_mode']))
470 $user_info['possibly_robot'] = isset($_SESSION['id_robot']) ? $_SESSION['id_robot'] : 0;
471 // If we haven't turned on proper spider hunts then have a guess!
472 else
473 {
474 $ci_user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
475 $user_info['possibly_robot'] = (strpos($_SERVER['HTTP_USER_AGENT'], 'Mozilla') === false && strpos($_SERVER['HTTP_USER_AGENT'], 'Opera') === false) || strpos($ci_user_agent, 'googlebot') !== false || strpos($ci_user_agent, 'slurp') !== false || strpos($ci_user_agent, 'crawl') !== false;
476 }
477 }
478
479 // Set up the $user_info array.
480 $user_info += array(
481 'id' => $id_member,
482 'username' => $username,
483 'name' => isset($user_settings['real_name']) ? $user_settings['real_name'] : '',
484 'email' => isset($user_settings['email_address']) ? $user_settings['email_address'] : '',
485 'passwd' => isset($user_settings['passwd']) ? $user_settings['passwd'] : '',
486 'language' => empty($user_settings['lngfile']) || empty($modSettings['userLanguage']) ? $language : $user_settings['lngfile'],
487 'is_guest' => $id_member == 0,
488 'is_admin' => in_array(1, $user_info['groups']),
489 'theme' => empty($user_settings['id_theme']) ? 0 : $user_settings['id_theme'],
490 'last_login' => empty($user_settings['last_login']) ? 0 : $user_settings['last_login'],
491 'ip' => $_SERVER['REMOTE_ADDR'],
492 'ip2' => $_SERVER['BAN_CHECK_IP'],
493 'posts' => empty($user_settings['posts']) ? 0 : $user_settings['posts'],
494 'time_format' => empty($user_settings['time_format']) ? $modSettings['time_format'] : $user_settings['time_format'],
495 'time_offset' => empty($user_settings['time_offset']) ? 0 : $user_settings['time_offset'],
496 'avatar' => array(
497 'url' => isset($user_settings['avatar']) ? $user_settings['avatar'] : '',
498 'filename' => empty($user_settings['filename']) ? '' : $user_settings['filename'],
499 'custom_dir' => !empty($user_settings['attachment_type']) && $user_settings['attachment_type'] == 1,
500 'id_attach' => isset($user_settings['id_attach']) ? $user_settings['id_attach'] : 0
501 ),
502 'smiley_set' => isset($user_settings['smiley_set']) ? $user_settings['smiley_set'] : '',
503 'messages' => empty($user_settings['instant_messages']) ? 0 : $user_settings['instant_messages'],
504 'unread_messages' => empty($user_settings['unread_messages']) ? 0 : $user_settings['unread_messages'],
505 'total_time_logged_in' => empty($user_settings['total_time_logged_in']) ? 0 : $user_settings['total_time_logged_in'],
506 'buddies' => !empty($modSettings['enable_buddylist']) && !empty($user_settings['buddy_list']) ? explode(',', $user_settings['buddy_list']) : array(),
507 'ignoreboards' => !empty($user_settings['ignore_boards']) && !empty($modSettings['allow_ignore_boards']) ? explode(',', $user_settings['ignore_boards']) : array(),
508 'ignoreusers' => !empty($user_settings['pm_ignore_list']) ? explode(',', $user_settings['pm_ignore_list']) : array(),
509 'warning' => isset($user_settings['warning']) ? $user_settings['warning'] : 0,
510 'permissions' => array(),
511 );
512 $user_info['groups'] = array_unique($user_info['groups']);
513 // Make sure that the last item in the ignore boards array is valid. If the list was too long it could have an ending comma that could cause problems.
514 if (!empty($user_info['ignoreboards']) && empty($user_info['ignoreboards'][$tmp = count($user_info['ignoreboards']) - 1]))
515 unset($user_info['ignoreboards'][$tmp]);
516
517 // Do we have any languages to validate this?
518 if (!empty($modSettings['userLanguage']) && (!empty($_GET['language']) || !empty($_SESSION['language'])))
519 $languages = getLanguages();
520
521 // Allow the user to change their language if its valid.
522 if (!empty($modSettings['userLanguage']) && !empty($_GET['language']) && isset($languages[strtr($_GET['language'], './\\:', '____')]))
523 {
524 $user_info['language'] = strtr($_GET['language'], './\\:', '____');
525 $_SESSION['language'] = $user_info['language'];
526 }
527 elseif (!empty($modSettings['userLanguage']) && !empty($_SESSION['language']) && isset($languages[strtr($_SESSION['language'], './\\:', '____')]))
528 $user_info['language'] = strtr($_SESSION['language'], './\\:', '____');
529
530 // Just build this here, it makes it easier to change/use - administrators can see all boards.
531 if ($user_info['is_admin'])
532 $user_info['query_see_board'] = '1=1';
533 // Otherwise just the groups in $user_info['groups'].
534 else
535 $user_info['query_see_board'] = '(FIND_IN_SET(' . implode(', b.member_groups) != 0 OR FIND_IN_SET(', $user_info['groups']) . ', b.member_groups) != 0' . (isset($user_info['mod_cache']) ? ' OR ' . $user_info['mod_cache']['mq'] : '') . ')';
536
537 // Build the list of boards they WANT to see.
538 // This will take the place of query_see_boards in certain spots, so it better include the boards they can see also
539
540 // If they aren't ignoring any boards then they want to see all the boards they can see
541 if (empty($user_info['ignoreboards']))
542 $user_info['query_wanna_see_board'] = $user_info['query_see_board'];
543 // Ok I guess they don't want to see all the boards
544 else
545 $user_info['query_wanna_see_board'] = '(' . $user_info['query_see_board'] . ' AND b.id_board NOT IN (' . implode(',', $user_info['ignoreboards']) . '))';
546 }
547
548 // Check for moderators and see if they have access to the board.
549 function loadBoard()
550 {
551 global $txt, $scripturl, $context, $modSettings;
552 global $board_info, $board, $topic, $user_info, $smcFunc;
553
554 // Assume they are not a moderator.
555 $user_info['is_mod'] = false;
556 $context['user']['is_mod'] = &$user_info['is_mod'];
557
558 // Start the linktree off empty..
559 $context['linktree'] = array();
560
561 // Have they by chance specified a message id but nothing else?
562 if (empty($_REQUEST['action']) && empty($topic) && empty($board) && !empty($_REQUEST['msg']))
563 {
564 // Make sure the message id is really an int.
565 $_REQUEST['msg'] = (int) $_REQUEST['msg'];
566
567 // Looking through the message table can be slow, so try using the cache first.
568 if (($topic = cache_get_data('msg_topic-' . $_REQUEST['msg'], 120)) === NULL)
569 {
570 $request = $smcFunc['db_query']('', '
571 SELECT id_topic
572 FROM {db_prefix}messages
573 WHERE id_msg = {int:id_msg}
574 LIMIT 1',
575 array(
576 'id_msg' => $_REQUEST['msg'],
577 )
578 );
579
580 // So did it find anything?
581 if ($smcFunc['db_num_rows']($request))
582 {
583 list ($topic) = $smcFunc['db_fetch_row']($request);
584 $smcFunc['db_free_result']($request);
585 // Save save save.
586 cache_put_data('msg_topic-' . $_REQUEST['msg'], $topic, 120);
587 }
588 }
589
590 // Remember redirection is the key to avoiding fallout from your bosses.
591 if (!empty($topic))
592 redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg']);
593 else
594 {
595 loadPermissions();
596 loadTheme();
597 fatal_lang_error('topic_gone', false);
598 }
599 }
600
601 // Load this board only if it is specified.
602 if (empty($board) && empty($topic))
603 {
604 $board_info = array('moderators' => array());
605 return;
606 }
607
608 if (!empty($modSettings['cache_enable']) && (empty($topic) || $modSettings['cache_enable'] >= 3))
609 {
610 // !!! SLOW?
611 if (!empty($topic))
612 $temp = cache_get_data('topic_board-' . $topic, 120);
613 else
614 $temp = cache_get_data('board-' . $board, 120);
615
616 if (!empty($temp))
617 {
618 $board_info = $temp;
619 $board = $board_info['id'];
620 }
621 }
622
623 if (empty($temp))
624 {
625 $request = $smcFunc['db_query']('', '
626 SELECT
627 c.id_cat, b.name AS bname, b.description, b.num_topics, b.member_groups,
628 b.id_parent, c.name AS cname, IFNULL(mem.id_member, 0) AS id_moderator,
629 mem.real_name' . (!empty($topic) ? ', b.id_board' : '') . ', b.child_level,
630 b.id_theme, b.override_theme, b.count_posts, b.id_profile, b.redirect,
631 b.unapproved_topics, b.unapproved_posts' . (!empty($topic) ? ', t.approved, t.id_member_started' : '') . '
632 FROM {db_prefix}boards AS b' . (!empty($topic) ? '
633 INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})' : '') . '
634 LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
635 LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = {raw:board_link})
636 LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
637 WHERE b.id_board = {raw:board_link}',
638 array(
639 'current_topic' => $topic,
640 'board_link' => empty($topic) ? $smcFunc['db_quote']('{int:current_board}', array('current_board' => $board)) : 't.id_board',
641 )
642 );
643 // If there aren't any, skip.
644 if ($smcFunc['db_num_rows']($request) > 0)
645 {
646 $row = $smcFunc['db_fetch_assoc']($request);
647
648 // Set the current board.
649 if (!empty($row['id_board']))
650 $board = $row['id_board'];
651
652 // Basic operating information. (globals... :/)
653 $board_info = array(
654 'id' => $board,
655 'moderators' => array(),
656 'cat' => array(
657 'id' => $row['id_cat'],
658 'name' => $row['cname']
659 ),
660 'name' => $row['bname'],
661 'description' => $row['description'],
662 'num_topics' => $row['num_topics'],
663 'unapproved_topics' => $row['unapproved_topics'],
664 'unapproved_posts' => $row['unapproved_posts'],
665 'unapproved_user_topics' => 0,
666 'parent_boards' => getBoardParents($row['id_parent']),
667 'parent' => $row['id_parent'],
668 'child_level' => $row['child_level'],
669 'theme' => $row['id_theme'],
670 'override_theme' => !empty($row['override_theme']),
671 'profile' => $row['id_profile'],
672 'redirect' => $row['redirect'],
673 'posts_count' => empty($row['count_posts']),
674 'cur_topic_approved' => empty($topic) || $row['approved'],
675 'cur_topic_starter' => empty($topic) ? 0 : $row['id_member_started'],
676 );
677
678 // Load the membergroups allowed, and check permissions.
679 $board_info['groups'] = $row['member_groups'] == '' ? array() : explode(',', $row['member_groups']);
680
681 do
682 {
683 if (!empty($row['id_moderator']))
684 $board_info['moderators'][$row['id_moderator']] = array(
685 'id' => $row['id_moderator'],
686 'name' => $row['real_name'],
687 'href' => $scripturl . '?action=profile;u=' . $row['id_moderator'],
688 'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_moderator'] . '">' . $row['real_name'] . '</a>'
689 );
690 }
691 while ($row = $smcFunc['db_fetch_assoc']($request));
692
693 // If the board only contains unapproved posts and the user isn't an approver then they can't see any topics.
694 // If that is the case do an additional check to see if they have any topics waiting to be approved.
695 if ($board_info['num_topics'] == 0 && $modSettings['postmod_active'] && !allowedTo('approve_posts'))
696 {
697 $smcFunc['db_free_result']($request); // Free the previous result
698
699 $request = $smcFunc['db_query']('', '
700 SELECT COUNT(id_topic)
701 FROM {db_prefix}topics
702 WHERE id_member_started={int:id_member}
703 AND approved = {int:unapproved}
704 AND id_board = {int:board}',
705 array(
706 'id_member' => $user_info['id'],
707 'unapproved' => 0,
708 'board' => $board,
709 )
710 );
711
712 list ($board_info['unapproved_user_topics']) = $smcFunc['db_fetch_row']($request);
713 }
714
715 if (!empty($modSettings['cache_enable']) && (empty($topic) || $modSettings['cache_enable'] >= 3))
716 {
717 // !!! SLOW?
718 if (!empty($topic))
719 cache_put_data('topic_board-' . $topic, $board_info, 120);
720 cache_put_data('board-' . $board, $board_info, 120);
721 }
722 }
723 else
724 {
725 // Otherwise the topic is invalid, there are no moderators, etc.
726 $board_info = array(
727 'moderators' => array(),
728 'error' => 'exist'
729 );
730 $topic = null;
731 $board = 0;
732 }
733 $smcFunc['db_free_result']($request);
734 }
735
736 if (!empty($topic))
737 $_GET['board'] = (int) $board;
738
739 if (!empty($board))
740 {
741 // Now check if the user is a moderator.
742 $user_info['is_mod'] = isset($board_info['moderators'][$user_info['id']]);
743
744 if (count(array_intersect($user_info['groups'], $board_info['groups'])) == 0 && !$user_info['is_admin'])
745 $board_info['error'] = 'access';
746
747 // Build up the linktree.
748 $context['linktree'] = array_merge(
749 $context['linktree'],
750 array(array(
751 'url' => $scripturl . '#c' . $board_info['cat']['id'],
752 'name' => $board_info['cat']['name']
753 )),
754 array_reverse($board_info['parent_boards']),
755 array(array(
756 'url' => $scripturl . '?board=' . $board . '.0',
757 'name' => $board_info['name']
758 ))
759 );
760 }
761
762 // Set the template contextual information.
763 $context['user']['is_mod'] = &$user_info['is_mod'];
764 $context['current_topic'] = $topic;
765 $context['current_board'] = $board;
766
767 // Hacker... you can't see this topic, I'll tell you that. (but moderators can!)
768 if (!empty($board_info['error']) && ($board_info['error'] != 'access' || !$user_info['is_mod']))
769 {
770 // The permissions and theme need loading, just to make sure everything goes smoothly.
771 loadPermissions();
772 loadTheme();
773
774 $_GET['board'] = '';
775 $_GET['topic'] = '';
776
777 // The linktree should not give the game away mate!
778 $context['linktree'] = array(
779 array(
780 'url' => $scripturl,
781 'name' => $context['forum_name_html_safe']
782 )
783 );
784
785 // If it's a prefetching agent or we're requesting an attachment.
786 if ((isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') || (!empty($_REQUEST['action']) && $_REQUEST['action'] === 'dlattach'))
787 {
788 ob_end_clean();
789 header('HTTP/1.1 403 Forbidden');
790 die;
791 }
792 elseif ($user_info['is_guest'])
793 {
794 loadLanguage('Errors');
795 is_not_guest($txt['topic_gone']);
796 }
797 else
798 fatal_lang_error('topic_gone', false);
799 }
800
801 if ($user_info['is_mod'])
802 $user_info['groups'][] = 3;
803 }
804
805 // Load this user's permissions.
806 function loadPermissions()
807 {
808 global $user_info, $board, $board_info, $modSettings, $smcFunc, $sourcedir;
809
810 if ($user_info['is_admin'])
811 {
812 banPermissions();
813 return;
814 }
815
816 if (!empty($modSettings['cache_enable']))
817 {
818 $cache_groups = $user_info['groups'];
819 asort($cache_groups);
820 $cache_groups = implode(',', $cache_groups);
821 // If it's a spider then cache it different.
822 if ($user_info['possibly_robot'])
823 $cache_groups .= '-spider';
824
825 if ($modSettings['cache_enable'] >= 2 && !empty($board) && ($temp = cache_get_data('permissions:' . $cache_groups . ':' . $board, 240)) != null && time() - 240 > $modSettings['settings_updated'])
826 {
827 list ($user_info['permissions']) = $temp;
828 banPermissions();
829
830 return;
831 }
832 elseif (($temp = cache_get_data('permissions:' . $cache_groups, 240)) != null && time() - 240 > $modSettings['settings_updated'])
833 list ($user_info['permissions'], $removals) = $temp;
834 }
835
836 // If it is detected as a robot, and we are restricting permissions as a special group - then implement this.
837 $spider_restrict = $user_info['possibly_robot'] && !empty($modSettings['spider_group']) ? ' OR (id_group = {int:spider_group} AND add_deny = 0)' : '';
838
839 if (empty($user_info['permissions']))
840 {
841 // Get the general permissions.
842 $request = $smcFunc['db_query']('', '
843 SELECT permission, add_deny
844 FROM {db_prefix}permissions
845 WHERE id_group IN ({array_int:member_groups})
846 ' . $spider_restrict,
847 array(
848 'member_groups' => $user_info['groups'],
849 'spider_group' => !empty($modSettings['spider_group']) ? $modSettings['spider_group'] : 0,
850 )
851 );
852 $removals = array();
853 while ($row = $smcFunc['db_fetch_assoc']($request))
854 {
855 if (empty($row['add_deny']))
856 $removals[] = $row['permission'];
857 else
858 $user_info['permissions'][] = $row['permission'];
859 }
860 $smcFunc['db_free_result']($request);
861
862 if (isset($cache_groups))
863 cache_put_data('permissions:' . $cache_groups, array($user_info['permissions'], $removals), 240);
864 }
865
866 // Get the board permissions.
867 if (!empty($board))
868 {
869 // Make sure the board (if any) has been loaded by loadBoard().
870 if (!isset($board_info['profile']))
871 fatal_lang_error('no_board');
872
873 $request = $smcFunc['db_query']('', '
874 SELECT permission, add_deny
875 FROM {db_prefix}board_permissions
876 WHERE (id_group IN ({array_int:member_groups})
877 ' . $spider_restrict . ')
878 AND id_profile = {int:id_profile}',
879 array(
880 'member_groups' => $user_info['groups'],
881 'id_profile' => $board_info['profile'],
882 'spider_group' => !empty($modSettings['spider_group']) ? $modSettings['spider_group'] : 0,
883 )
884 );
885 while ($row = $smcFunc['db_fetch_assoc']($request))
886 {
887 if (empty($row['add_deny']))
888 $removals[] = $row['permission'];
889 else
890 $user_info['permissions'][] = $row['permission'];
891 }
892 $smcFunc['db_free_result']($request);
893 }
894
895 // Remove all the permissions they shouldn't have ;).
896 if (!empty($modSettings['permission_enable_deny']))
897 $user_info['permissions'] = array_diff($user_info['permissions'], $removals);
898
899 if (isset($cache_groups) && !empty($board) && $modSettings['cache_enable'] >= 2)
900 cache_put_data('permissions:' . $cache_groups . ':' . $board, array($user_info['permissions'], null), 240);
901
902 // Banned? Watch, don't touch..
903 banPermissions();
904
905 // Load the mod cache so we can know what additional boards they should see, but no sense in doing it for guests
906 if (!$user_info['is_guest'])
907 {
908 if (!isset($_SESSION['mc']) || $_SESSION['mc']['time'] <= $modSettings['settings_updated'])
909 {
910 require_once($sourcedir . '/Subs-Auth.php');
911 rebuildModCache();
912 }
913 else
914 $user_info['mod_cache'] = $_SESSION['mc'];
915 }
916 }
917
918 // Loads an array of users' data by ID or member_name.
919 function loadMemberData($users, $is_name = false, $set = 'normal')
920 {
921 global $user_profile, $modSettings, $board_info, $smcFunc;
922
923 // Can't just look for no users :P.
924 if (empty($users))
925 return false;
926
927 // Make sure it's an array.
928 $users = !is_array($users) ? array($users) : array_unique($users);
929 $loaded_ids = array();
930
931 if (!$is_name && !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 3)
932 {
933 $users = array_values($users);
934 for ($i = 0, $n = count($users); $i < $n; $i++)
935 {
936 $data = cache_get_data('member_data-' . $set . '-' . $users[$i], 240);
937 if ($data == null)
938 continue;
939
940 $loaded_ids[] = $data['id_member'];
941 $user_profile[$data['id_member']] = $data;
942 unset($users[$i]);
943 }
944 }
945
946 if ($set == 'normal')
947 {
948 $select_columns = '
949 IFNULL(lo.log_time, 0) AS is_online, IFNULL(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type,
950 mem.signature, mem.personal_text, mem.location, mem.gender, mem.avatar, mem.id_member, mem.member_name,
951 mem.real_name, mem.email_address, mem.hide_email, mem.date_registered, mem.website_title, mem.website_url,
952 mem.birthdate, mem.member_ip, mem.member_ip2, mem.icq, mem.aim, mem.yim, mem.msn, mem.posts, mem.last_login,
953 mem.karma_good, mem.id_post_group, mem.karma_bad, mem.lngfile, mem.id_group, mem.time_offset, mem.show_online,
954 mem.buddy_list, mg.online_color AS member_group_color, IFNULL(mg.group_name, {string:blank_string}) AS member_group,
955 pg.online_color AS post_group_color, IFNULL(pg.group_name, {string:blank_string}) AS post_group, mem.is_activated, mem.warning,
956 CASE WHEN mem.id_group = 0 OR mg.stars = {string:blank_string} THEN pg.stars ELSE mg.stars END AS stars' . (!empty($modSettings['titlesEnable']) ? ',
957 mem.usertitle' : '');
958 $select_tables = '
959 LEFT JOIN {db_prefix}log_online AS lo ON (lo.id_member = mem.id_member)
960 LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = mem.id_member)
961 LEFT JOIN {db_prefix}membergroups AS pg ON (pg.id_group = mem.id_post_group)
962 LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = mem.id_group)';
963 }
964 elseif ($set == 'profile')
965 {
966 $select_columns = '
967 IFNULL(lo.log_time, 0) AS is_online, IFNULL(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type,
968 mem.signature, mem.personal_text, mem.location, mem.gender, mem.avatar, mem.id_member, mem.member_name,
969 mem.real_name, mem.email_address, mem.hide_email, mem.date_registered, mem.website_title, mem.website_url,
970 mem.openid_uri, mem.birthdate, mem.icq, mem.aim, mem.yim, mem.msn, mem.posts, mem.last_login, mem.karma_good,
971 mem.karma_bad, mem.member_ip, mem.member_ip2, mem.lngfile, mem.id_group, mem.id_theme, mem.buddy_list,
972 mem.pm_ignore_list, mem.pm_email_notify, mem.pm_receive_from, mem.time_offset' . (!empty($modSettings['titlesEnable']) ? ', mem.usertitle' : '') . ',
973 mem.time_format, mem.secret_question, mem.is_activated, mem.additional_groups, mem.smiley_set, mem.show_online,
974 mem.total_time_logged_in, mem.id_post_group, mem.notify_announcements, mem.notify_regularity, mem.notify_send_body,
975 mem.notify_types, lo.url, mg.online_color AS member_group_color, IFNULL(mg.group_name, {string:blank_string}) AS member_group,
976 pg.online_color AS post_group_color, IFNULL(pg.group_name, {string:blank_string}) AS post_group, mem.ignore_boards, mem.warning,
977 CASE WHEN mem.id_group = 0 OR mg.stars = {string:blank_string} THEN pg.stars ELSE mg.stars END AS stars, mem.password_salt, mem.pm_prefs';
978 $select_tables = '
979 LEFT JOIN {db_prefix}log_online AS lo ON (lo.id_member = mem.id_member)
980 LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = mem.id_member)
981 LEFT JOIN {db_prefix}membergroups AS pg ON (pg.id_group = mem.id_post_group)
982 LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = mem.id_group)';
983 }
984 elseif ($set == 'minimal')
985 {
986 $select_columns = '
987 mem.id_member, mem.member_name, mem.real_name, mem.email_address, mem.hide_email, mem.date_registered,
988 mem.posts, mem.last_login, mem.member_ip, mem.member_ip2, mem.lngfile, mem.id_group';
989 $select_tables = '';
990 }
991 else
992 trigger_error('loadMemberData(): Invalid member data set \'' . $set . '\'', E_USER_WARNING);
993
994 if (!empty($users))
995 {
996 // Load the member's data.
997 $request = $smcFunc['db_query']('', '
998 SELECT' . $select_columns . '
999 FROM {db_prefix}members AS mem' . $select_tables . '
1000 WHERE mem.' . ($is_name ? 'member_name' : 'id_member') . (count($users) == 1 ? ' = {' . ($is_name ? 'string' : 'int') . ':users}' : ' IN ({' . ($is_name ? 'array_string' : 'array_int') . ':users})'),
1001 array(
1002 'blank_string' => '',
1003 'users' => count($users) == 1 ? current($users) : $users,
1004 )
1005 );
1006 $new_loaded_ids = array();
1007 while ($row = $smcFunc['db_fetch_assoc']($request))
1008 {
1009 $new_loaded_ids[] = $row['id_member'];
1010 $loaded_ids[] = $row['id_member'];
1011 $row['options'] = array();
1012 $user_profile[$row['id_member']] = $row;
1013 }
1014 $smcFunc['db_free_result']($request);
1015 }
1016
1017 if (!empty($new_loaded_ids) && $set !== 'minimal')
1018 {
1019 $request = $smcFunc['db_query']('', '
1020 SELECT *
1021 FROM {db_prefix}themes
1022 WHERE id_member' . (count($new_loaded_ids) == 1 ? ' = {int:loaded_ids}' : ' IN ({array_int:loaded_ids})'),
1023 array(
1024 'loaded_ids' => count($new_loaded_ids) == 1 ? $new_loaded_ids[0] : $new_loaded_ids,
1025 )
1026 );
1027 while ($row = $smcFunc['db_fetch_assoc']($request))
1028 $user_profile[$row['id_member']]['options'][$row['variable']] = $row['value'];
1029 $smcFunc['db_free_result']($request);
1030 }
1031
1032 if (!empty($new_loaded_ids) && !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 3)
1033 {
1034 for ($i = 0, $n = count($new_loaded_ids); $i < $n; $i++)
1035 cache_put_data('member_data-' . $set . '-' . $new_loaded_ids[$i], $user_profile[$new_loaded_ids[$i]], 240);
1036 }
1037
1038 // Are we loading any moderators? If so, fix their group data...
1039 if (!empty($loaded_ids) && !empty($board_info['moderators']) && $set === 'normal' && count($temp_mods = array_intersect($loaded_ids, array_keys($board_info['moderators']))) !== 0)
1040 {
1041 if (($row = cache_get_data('moderator_group_info', 480)) == null)
1042 {
1043 $request = $smcFunc['db_query']('', '
1044 SELECT group_name AS member_group, online_color AS member_group_color, stars
1045 FROM {db_prefix}membergroups
1046 WHERE id_group = {int:moderator_group}
1047 LIMIT 1',
1048 array(
1049 'moderator_group' => 3,
1050 )
1051 );
1052 $row = $smcFunc['db_fetch_assoc']($request);
1053 $smcFunc['db_free_result']($request);
1054
1055 cache_put_data('moderator_group_info', $row, 480);
1056 }
1057
1058 foreach ($temp_mods as $id)
1059 {
1060 // By popular demand, don't show admins or global moderators as moderators.
1061 if ($user_profile[$id]['id_group'] != 1 && $user_profile[$id]['id_group'] != 2)
1062 $user_profile[$id]['member_group'] = $row['member_group'];
1063
1064 // If the Moderator group has no color or stars, but their group does... don't overwrite.
1065 if (!empty($row['stars']))
1066 $user_profile[$id]['stars'] = $row['stars'];
1067 if (!empty($row['member_group_color']))
1068 $user_profile[$id]['member_group_color'] = $row['member_group_color'];
1069 }
1070 }
1071
1072 return empty($loaded_ids) ? false : $loaded_ids;
1073 }
1074
1075 // Loads the user's basic values... meant for template/theme usage.
1076 function loadMemberContext($user, $display_custom_fields = false)
1077 {
1078 global $memberContext, $user_profile, $txt, $scripturl, $user_info;
1079 global $context, $modSettings, $board_info, $settings;
1080 global $smcFunc;
1081 static $dataLoaded = array();
1082
1083 // If this person's data is already loaded, skip it.
1084 if (isset($dataLoaded[$user]))
1085 return true;
1086
1087 // We can't load guests or members not loaded by loadMemberData()!
1088 if ($user == 0)
1089 return false;
1090 if (!isset($user_profile[$user]))
1091 {
1092 trigger_error('loadMemberContext(): member id ' . $user . ' not previously loaded by loadMemberData()', E_USER_WARNING);
1093 return false;
1094 }
1095
1096 // Well, it's loaded now anyhow.
1097 $dataLoaded[$user] = true;
1098 $profile = $user_profile[$user];
1099
1100 // Censor everything.
1101 censorText($profile['signature']);
1102 censorText($profile['personal_text']);
1103 censorText($profile['location']);
1104
1105 // Set things up to be used before hand.
1106 $gendertxt = $profile['gender'] == 2 ? $txt['female'] : ($profile['gender'] == 1 ? $txt['male'] : '');
1107 $profile['signature'] = str_replace(array("\n", "\r"), array('<br />', ''), $profile['signature']);
1108 $profile['signature'] = parse_bbc($profile['signature'], true, 'sig' . $profile['id_member']);
1109
1110 $profile['is_online'] = (!empty($profile['show_online']) || allowedTo('moderate_forum')) && $profile['is_online'] > 0;
1111 $profile['stars'] = empty($profile['stars']) ? array('', '') : explode('#', $profile['stars']);
1112 // Setup the buddy status here (One whole in_array call saved :P)
1113 $profile['buddy'] = in_array($profile['id_member'], $user_info['buddies']);
1114 $buddy_list = !empty($profile['buddy_list']) ? explode(',', $profile['buddy_list']) : array();
1115
1116 // If we're always html resizing, assume it's too large.
1117 if ($modSettings['avatar_action_too_large'] == 'option_html_resize' || $modSettings['avatar_action_too_large'] == 'option_js_resize')
1118 {
1119 $avatar_width = !empty($modSettings['avatar_max_width_external']) ? ' width="' . $modSettings['avatar_max_width_external'] . '"' : '';
1120 $avatar_height = !empty($modSettings['avatar_max_height_external']) ? ' height="' . $modSettings['avatar_max_height_external'] . '"' : '';
1121 }
1122 else
1123 {
1124 $avatar_width = '';
1125 $avatar_height = '';
1126 }
1127
1128 // What a monstrous array...
1129 $memberContext[$user] = array(
1130 'username' => $profile['member_name'],
1131 'name' => $profile['real_name'],
1132 'id' => $profile['id_member'],
1133 'is_buddy' => $profile['buddy'],
1134 'is_reverse_buddy' => in_array($user_info['id'], $buddy_list),
1135 'buddies' => $buddy_list,
1136 'title' => !empty($modSettings['titlesEnable']) ? $profile['usertitle'] : '',
1137 'href' => $scripturl . '?action=profile;u=' . $profile['id_member'],
1138 'link' => '<a href="' . $scripturl . '?action=profile;u=' . $profile['id_member'] . '" title="' . $txt['profile_of'] . ' ' . $profile['real_name'] . '">' . $profile['real_name'] . '</a>',
1139 'email' => $profile['email_address'],
1140 'show_email' => showEmailAddress(!empty($profile['hide_email']), $profile['id_member']),
1141 'registered' => empty($profile['date_registered']) ? $txt['not_applicable'] : timeformat($profile['date_registered']),
1142 'registered_timestamp' => empty($profile['date_registered']) ? 0 : forum_time(true, $profile['date_registered']),
1143 'blurb' => $profile['personal_text'],
1144 'gender' => array(
1145 'name' => $gendertxt,
1146 'image' => !empty($profile['gender']) ? '<img class="gender" src="' . $settings['images_url'] . '/' . ($profile['gender'] == 1 ? 'Male' : 'Female') . '.gif" alt="' . $gendertxt . '" />' : ''
1147 ),
1148 'website' => array(
1149 'title' => $profile['website_title'],
1150 'url' => $profile['website_url'],
1151 ),
1152 'birth_date' => empty($profile['birthdate']) || $profile['birthdate'] === '0001-01-01' ? '0000-00-00' : (substr($profile['birthdate'], 0, 4) === '0004' ? '0000' . substr($profile['birthdate'], 4) : $profile['birthdate']),
1153 'signature' => $profile['signature'],
1154 'location' => $profile['location'],
1155 'icq' => $profile['icq'] != '' && (empty($modSettings['guest_hideContacts']) || !$user_info['is_guest']) ? array(
1156 'name' => $profile['icq'],
1157 'href' => 'http://www.icq.com/whitepages/about_me.php?uin=' . $profile['icq'],
1158 'link' => '<a class="icq new_win" href="http://www.icq.com/whitepages/about_me.php?uin=' . $profile['icq'] . '" target="_blank" title="' . $txt['icq_title'] . ' - ' . $profile['icq'] . '"><img src="http://status.icq.com/online.gif?img=5&amp;icq=' . $profile['icq'] . '" alt="' . $txt['icq_title'] . ' - ' . $profile['icq'] . '" width="18" height="18" /></a>',
1159 'link_text' => '<a class="icq extern" href="http://www.icq.com/whitepages/about_me.php?uin=' . $profile['icq'] . '" title="' . $txt['icq_title'] . ' - ' . $profile['icq'] . '">' . $profile['icq'] . '</a>',
1160 ) : array('name' => '', 'add' => '', 'href' => '', 'link' => '', 'link_text' => ''),
1161 'aim' => $profile['aim'] != '' && (empty($modSettings['guest_hideContacts']) || !$user_info['is_guest']) ? array(
1162 'name' => $profile['aim'],
1163 'href' => 'aim:goim?screenname=' . urlencode(strtr($profile['aim'], array(' ' => '%20'))) . '&amp;message=' . $txt['aim_default_message'],
1164 'link' => '<a class="aim" href="aim:goim?screenname=' . urlencode(strtr($profile['aim'], array(' ' => '%20'))) . '&amp;message=' . $txt['aim_default_message'] . '" title="' . $txt['aim_title'] . ' - ' . $profile['aim'] . '"><img src="' . $settings['images_url'] . '/aim.gif" alt="' . $txt['aim_title'] . ' - ' . $profile['aim'] . '" /></a>',
1165 'link_text' => '<a class="aim" href="aim:goim?screenname=' . urlencode(strtr($profile['aim'], array(' ' => '%20'))) . '&amp;message=' . $txt['aim_default_message'] . '" title="' . $txt['aim_title'] . ' - ' . $profile['aim'] . '">' . $profile['aim'] . '</a>'
1166 ) : array('name' => '', 'href' => '', 'link' => '', 'link_text' => ''),
1167 'yim' => $profile['yim'] != '' && (empty($modSettings['guest_hideContacts']) || !$user_info['is_guest']) ? array(
1168 'name' => $profile['yim'],
1169 'href' => 'http://edit.yahoo.com/config/send_webmesg?.target=' . urlencode($profile['yim']),
1170 'link' => '<a class="yim" href="http://edit.yahoo.com/config/send_webmesg?.target=' . urlencode($profile['yim']) . '" title="' . $txt['yim_title'] . ' - ' . $profile['yim'] . '"><img src="http://opi.yahoo.com/online?u=' . urlencode($profile['yim']) . '&amp;m=g&amp;t=0" alt="' . $txt['yim_title'] . ' - ' . $profile['yim'] . '" /></a>',
1171 'link_text' => '<a class="yim" href="http://edit.yahoo.com/config/send_webmesg?.target=' . urlencode($profile['yim']) . '" title="' . $txt['yim_title'] . ' - ' . $profile['yim'] . '">' . $profile['yim'] . '</a>'
1172 ) : array('name' => '', 'href' => '', 'link' => '', 'link_text' => ''),
1173 'msn' => $profile['msn'] !='' && (empty($modSettings['guest_hideContacts']) || !$user_info['is_guest']) ? array(
1174 'name' => $profile['msn'],
1175 'href' => 'http://members.msn.com/' . $profile['msn'],
1176 'link' => '<a class="msn new_win" href="http://members.msn.com/' . $profile['msn'] . '" title="' . $txt['msn_title'] . ' - ' . $profile['msn'] . '"><img src="' . $settings['images_url'] . '/msntalk.gif" alt="' . $txt['msn_title'] . ' - ' . $profile['msn'] . '" /></a>',
1177 'link_text' => '<a class="msn new_win" href="http://members.msn.com/' . $profile['msn'] . '" title="' . $txt['msn_title'] . ' - ' . $profile['msn'] . '">' . $profile['msn'] . '</a>'
1178 ) : array('name' => '', 'href' => '', 'link' => '', 'link_text' => ''),
1179 'real_posts' => $profile['posts'],
1180 'posts' => $profile['posts'] > 500000 ? $txt['geek'] : comma_format($profile['posts']),
1181 'avatar' => array(
1182 'name' => $profile['avatar'],
1183 'image' => $profile['avatar'] == '' ? ($profile['id_attach'] > 0 ? '<img class="avatar" src="' . (empty($profile['attachment_type']) ? $scripturl . '?action=dlattach;attach=' . $profile['id_attach'] . ';type=avatar' : $modSettings['custom_avatar_url'] . '/' . $profile['filename']) . '" alt="" />' : '') : (stristr($profile['avatar'], 'http://') ? '<img class="avatar" src="' . $profile['avatar'] . '"' . $avatar_width . $avatar_height . ' alt="" />' : '<img class="avatar" src="' . $modSettings['avatar_url'] . '/' . htmlspecialchars($profile['avatar']) . '" alt="" />'),
1184 'href' => $profile['avatar'] == '' ? ($profile['id_attach'] > 0 ? (empty($profile['attachment_type']) ? $scripturl . '?action=dlattach;attach=' . $profile['id_attach'] . ';type=avatar' : $modSettings['custom_avatar_url'] . '/' . $profile['filename']) : '') : (stristr($profile['avatar'], 'http://') ? $profile['avatar'] : $modSettings['avatar_url'] . '/' . $profile['avatar']),
1185 'url' => $profile['avatar'] == '' ? '' : (stristr($profile['avatar'], 'http://') ? $profile['avatar'] : $modSettings['avatar_url'] . '/' . $profile['avatar'])
1186 ),
1187 'last_login' => empty($profile['last_login']) ? $txt['never'] : timeformat($profile['last_login']),
1188 'last_login_timestamp' => empty($profile['last_login']) ? 0 : forum_time(0, $profile['last_login']),
1189 'karma' => array(
1190 'good' => $profile['karma_good'],
1191 'bad' => $profile['karma_bad'],
1192 'allow' => !$user_info['is_guest'] && !empty($modSettings['karmaMode']) && $user_info['id'] != $user && allowedTo('karma_edit') &&
1193 ($user_info['posts'] >= $modSettings['karmaMinPosts'] || $user_info['is_admin']),
1194 ),
1195 'ip' => htmlspecialchars($profile['member_ip']),
1196 'ip2' => htmlspecialchars($profile['member_ip2']),
1197 'online' => array(
1198 'is_online' => $profile['is_online'],
1199 'text' => $txt[$profile['is_online'] ? 'online' : 'offline'],
1200 'href' => $scripturl . '?action=pm;sa=send;u=' . $profile['id_member'],
1201 'link' => '<a href="' . $scripturl . '?action=pm;sa=send;u=' . $profile['id_member'] . '">' . $txt[$profile['is_online'] ? 'online' : 'offline'] . '</a>',
1202 'image_href' => $settings['images_url'] . '/' . ($profile['buddy'] ? 'buddy_' : '') . ($profile['is_online'] ? 'useron' : 'useroff') . '.gif',
1203 'label' => $txt[$profile['is_online'] ? 'online' : 'offline']
1204 ),
1205 'language' => $smcFunc['ucwords'](strtr($profile['lngfile'], array('_' => ' ', '-utf8' => ''))),
1206 'is_activated' => isset($profile['is_activated']) ? $profile['is_activated'] : 1,
1207 'is_banned' => isset($profile['is_activated']) ? $profile['is_activated'] >= 10 : 0,
1208 'options' => $profile['options'],
1209 'is_guest' => false,
1210 'group' => $profile['member_group'],
1211 'group_color' => $profile['member_group_color'],
1212 'group_id' => $profile['id_group'],
1213 'post_group' => $profile['post_group'],
1214 'post_group_color' => $profile['post_group_color'],
1215 'group_stars' => str_repeat('<img src="' . str_replace('$language', $context['user']['language'], isset($profile['stars'][1]) ? $settings['images_url'] . '/' . $profile['stars'][1] : '') . '" alt="*" />', empty($profile['stars'][0]) || empty($profile['stars'][1]) ? 0 : $profile['stars'][0]),
1216 'warning' => $profile['warning'],
1217 'warning_status' => !empty($modSettings['warning_mute']) && $modSettings['warning_mute'] <= $profile['warning'] ? 'mute' : (!empty($modSettings['warning_moderate']) && $modSettings['warning_moderate'] <= $profile['warning'] ? 'moderate' : (!empty($modSettings['warning_watch']) && $modSettings['warning_watch'] <= $profile['warning'] ? 'watch' : (''))),
1218 'local_time' => timeformat(time() + ($profile['time_offset'] - $user_info['time_offset']) * 3600, false),
1219 );
1220
1221 // First do a quick run through to make sure there is something to be shown.
1222 $memberContext[$user]['has_messenger'] = false;
1223 foreach (array('icq', 'msn', 'aim', 'yim') as $messenger)
1224 {
1225 if (!isset($context['disabled_fields'][$messenger]) && !empty($memberContext[$user][$messenger]['link']))
1226 {
1227 $memberContext[$user]['has_messenger'] = true;
1228 break;
1229 }
1230 }
1231
1232 // Are we also loading the members custom fields into context?
1233 if ($display_custom_fields && !empty($modSettings['displayFields']))
1234 {
1235 $memberContext[$user]['custom_fields'] = array();
1236 if (!isset($context['display_fields']))
1237 $context['display_fields'] = unserialize($modSettings['displayFields']);
1238
1239 foreach ($context['display_fields'] as $custom)
1240 {
1241 if (empty($custom['title']) || empty($profile['options'][$custom['colname']]))
1242 continue;
1243
1244 $value = $profile['options'][$custom['colname']];
1245
1246 // BBC?
1247 if ($custom['bbc'])
1248 $value = parse_bbc($value);
1249 // ... or checkbox?
1250 elseif (isset($custom['type']) && $custom['type'] == 'check')
1251 $value = $value ? $txt['yes'] : $txt['no'];
1252
1253 // Enclosing the user input within some other text?
1254 if (!empty($custom['enclose']))
1255 $value = strtr($custom['enclose'], array(
1256 '{SCRIPTURL}' => $scripturl,
1257 '{IMAGES_URL}' => $settings['images_url'],
1258 '{DEFAULT_IMAGES_URL}' => $settings['default_images_url'],
1259 '{INPUT}' => $value,
1260 ));
1261
1262 $memberContext[$user]['custom_fields'][] = array(
1263 'title' => $custom['title'],
1264 'colname' => $custom['colname'],
1265 'value' => $value,
1266 'placement' => !empty($custom['placement']) ? $custom['placement'] : 0,
1267 );
1268 }
1269 }
1270
1271 return true;
1272 }
1273
1274 function detectBrowser()
1275 {
1276 global $context, $user_info;
1277
1278 // The following determines the user agent (browser) as best it can.
1279 $context['browser'] = array(
1280 'is_opera' => strpos($_SERVER['HTTP_USER_AGENT'], 'Opera') !== false,
1281 'is_opera6' => strpos($_SERVER['HTTP_USER_AGENT'], 'Opera 6') !== false,
1282 'is_opera7' => strpos($_SERVER['HTTP_USER_AGENT'], 'Opera 7') !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'Opera/7') !== false,
1283 'is_opera8' => strpos($_SERVER['HTTP_USER_AGENT'], 'Opera 8') !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'Opera/8') !== false,
1284 'is_opera9' => preg_match('~Opera[ /]9(?!\\.[89])~', $_SERVER['HTTP_USER_AGENT']) === 1,
1285 'is_opera10' => preg_match('~Opera[ /]10\\.~', $_SERVER['HTTP_USER_AGENT']) === 1 || (preg_match('~Opera[ /]9\\.[89]~', $_SERVER['HTTP_USER_AGENT']) === 1 && preg_match('~Version/1[0-9]\\.~', $_SERVER['HTTP_USER_AGENT']) === 1),
1286 'is_ie4' => strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 4') !== false && strpos($_SERVER['HTTP_USER_AGENT'], 'WebTV') === false,
1287 'is_webkit' => strpos($_SERVER['HTTP_USER_AGENT'], 'AppleWebKit') !== false,
1288 'is_mac_ie' => strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 5.') !== false && strpos($_SERVER['HTTP_USER_AGENT'], 'Mac') !== false,
1289 'is_web_tv' => strpos($_SERVER['HTTP_USER_AGENT'], 'WebTV') !== false,
1290 'is_konqueror' => strpos($_SERVER['HTTP_USER_AGENT'], 'Konqueror') !== false,
1291 'is_firefox' => preg_match('~(?:Firefox|Ice[wW]easel|IceCat)/~', $_SERVER['HTTP_USER_AGENT']) === 1,
1292 'is_firefox1' => preg_match('~(?:Firefox|Ice[wW]easel|IceCat)/1\\.~', $_SERVER['HTTP_USER_AGENT']) === 1,
1293 'is_firefox2' => preg_match('~(?:Firefox|Ice[wW]easel|IceCat)/2\\.~', $_SERVER['HTTP_USER_AGENT']) === 1,
1294 'is_firefox3' => preg_match('~(?:Firefox|Ice[wW]easel|IceCat|Shiretoko|Minefield)/3\\.~', $_SERVER['HTTP_USER_AGENT']) === 1,
1295 'is_iphone' => strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone') !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'iPod') !== false,
1296 'is_android' => strpos($_SERVER['HTTP_USER_AGENT'], 'Android') !== false,
1297 );
1298
1299 $context['browser']['is_chrome'] = $context['browser']['is_webkit'] && strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome') !== false;
1300 $context['browser']['is_safari'] = !$context['browser']['is_chrome'] && strpos($_SERVER['HTTP_USER_AGENT'], 'Safari') !== false;
1301 $context['browser']['is_gecko'] = strpos($_SERVER['HTTP_USER_AGENT'], 'Gecko') !== false && !$context['browser']['is_webkit'] && !$context['browser']['is_konqueror'];
1302
1303 // Internet Explorer 5 and 6 are often "emulated".
1304 $context['browser']['is_ie8'] = !$context['browser']['is_opera'] && !$context['browser']['is_gecko'] && !$context['browser']['is_web_tv'] && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 8') !== false;
1305 $context['browser']['is_ie7'] = !$context['browser']['is_opera'] && !$context['browser']['is_gecko'] && !$context['browser']['is_web_tv'] && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 7') !== false && !$context['browser']['is_ie8'];
1306 $context['browser']['is_ie6'] = !$context['browser']['is_opera'] && !$context['browser']['is_gecko'] && !$context['browser']['is_web_tv'] && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 6') !== false && !$context['browser']['is_ie8'] && !$context['browser']['is_ie7'];
1307 $context['browser']['is_ie5.5'] = !$context['browser']['is_opera'] && !$context['browser']['is_gecko'] && !$context['browser']['is_web_tv'] && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 5.5') !== false;
1308 $context['browser']['is_ie5'] = !$context['browser']['is_opera'] && !$context['browser']['is_gecko'] && !$context['browser']['is_web_tv'] && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 5.0') !== false;
1309
1310 $context['browser']['is_ie'] = $context['browser']['is_ie4'] || $context['browser']['is_ie5'] || $context['browser']['is_ie5.5'] || $context['browser']['is_ie6'] || $context['browser']['is_ie7'] || $context['browser']['is_ie8'];
1311 // Before IE8 we need to fix IE... lots!
1312 $context['browser']['ie_standards_fix'] = !$context['browser']['is_ie8'];
1313
1314 $context['browser']['needs_size_fix'] = ($context['browser']['is_ie5'] || $context['browser']['is_ie5.5'] || $context['browser']['is_ie4'] || $context['browser']['is_opera6']) && strpos($_SERVER['HTTP_USER_AGENT'], 'Mac') === false;
1315
1316 // This isn't meant to be reliable, it's just meant to catch most bots to prevent PHPSESSID from showing up.
1317 $context['browser']['possibly_robot'] = !empty($user_info['possibly_robot']);
1318
1319 // Robots shouldn't be logging in or registering. So, they aren't a bot. Better to be wrong than sorry (or people won't be able to log in!), anyway.
1320 if ((isset($_REQUEST['action']) && in_array($_REQUEST['action'], array('login', 'login2', 'register'))) || !$user_info['is_guest'])
1321 $context['browser']['possibly_robot'] = false;
1322 }
1323
1324 // Load a theme, by ID.
1325 function loadTheme($id_theme = 0, $initialize = true)
1326 {
1327 global $user_info, $user_settings, $board_info, $sc, $boarddir;
1328 global $txt, $boardurl, $scripturl, $mbname, $modSettings, $language;
1329 global $context, $settings, $options, $sourcedir, $ssi_theme, $smcFunc;
1330
1331 // The theme was specified by parameter.
1332 if (!empty($id_theme))
1333 $id_theme = (int) $id_theme;
1334 // The theme was specified by REQUEST.
1335 elseif (!empty($_REQUEST['theme']) && (!empty($modSettings['theme_allow']) || allowedTo('admin_forum')))
1336 {
1337 $id_theme = (int) $_REQUEST['theme'];
1338 $_SESSION['id_theme'] = $id_theme;
1339 }
1340 // The theme was specified by REQUEST... previously.
1341 elseif (!empty($_SESSION['id_theme']) && (!empty($modSettings['theme_allow']) || allowedTo('admin_forum')))
1342 $id_theme = (int) $_SESSION['id_theme'];
1343 // The theme is just the user's choice. (might use ?board=1;theme=0 to force board theme.)
1344 elseif (!empty($user_info['theme']) && !isset($_REQUEST['theme']) && (!empty($modSettings['theme_allow']) || allowedTo('admin_forum')))
1345 $id_theme = $user_info['theme'];
1346 // The theme was specified by the board.
1347 elseif (!empty($board_info['theme']))
1348 $id_theme = $board_info['theme'];
1349 // The theme is the forum's default.
1350 else
1351 $id_theme = $modSettings['theme_guests'];
1352
1353 // Verify the id_theme... no foul play.
1354 // Always allow the board specific theme, if they are overriding.
1355 if (!empty($board_info['theme']) && $board_info['override_theme'])
1356 $id_theme = $board_info['theme'];
1357 // If they have specified a particular theme to use with SSI allow it to be used.
1358 elseif (!empty($ssi_theme) && $id_theme == $ssi_theme)
1359 $id_theme = (int) $id_theme;
1360 elseif (!empty($modSettings['knownThemes']) && !allowedTo('admin_forum'))
1361 {
1362 $themes = explode(',', $modSettings['knownThemes']);
1363 if (!in_array($id_theme, $themes))
1364 $id_theme = $modSettings['theme_guests'];
1365 else
1366 $id_theme = (int) $id_theme;
1367 }
1368 else
1369 $id_theme = (int) $id_theme;
1370
1371 $member = empty($user_info['id']) ? -1 : $user_info['id'];
1372
1373 if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2 && ($temp = cache_get_data('theme_settings-' . $id_theme . ':' . $member, 60)) != null && time() - 60 > $modSettings['settings_updated'])
1374 {
1375 $themeData = $temp;
1376 $flag = true;
1377 }
1378 elseif (($temp = cache_get_data('theme_settings-' . $id_theme, 90)) != null && time() - 60 > $modSettings['settings_updated'])
1379 $themeData = $temp + array($member => array());
1380 else
1381 $themeData = array(-1 => array(), 0 => array(), $member => array());
1382
1383 if (empty($flag))
1384 {
1385 // Load variables from the current or default theme, global or this user's.
1386 $result = $smcFunc['db_query']('', '
1387 SELECT variable, value, id_member, id_theme
1388 FROM {db_prefix}themes
1389 WHERE id_member' . (empty($themeData[0]) ? ' IN (-1, 0, {int:id_member})' : ' = {int:id_member}') . '
1390 AND id_theme' . ($id_theme == 1 ? ' = {int:id_theme}' : ' IN ({int:id_theme}, 1)'),
1391 array(
1392 'id_theme' => $id_theme,
1393 'id_member' => $member,
1394 )
1395 );
1396 // Pick between $settings and $options depending on whose data it is.
1397 while ($row = $smcFunc['db_fetch_assoc']($result))
1398 {
1399 // There are just things we shouldn't be able to change as members.
1400 if ($row['id_member'] != 0 && in_array($row['variable'], array('actual_theme_url', 'actual_images_url', 'base_theme_dir', 'base_theme_url', 'default_images_url', 'default_theme_dir', 'default_theme_url', 'default_template', 'images_url', 'number_recent_posts', 'smiley_sets_default', 'theme_dir', 'theme_id', 'theme_layers', 'theme_templates', 'theme_url')))
1401 continue;
1402
1403 // If this is the theme_dir of the default theme, store it.
1404 if (in_array($row['variable'], array('theme_dir', 'theme_url', 'images_url')) && $row['id_theme'] == '1' && empty($row['id_member']))
1405 $themeData[0]['default_' . $row['variable']] = $row['value'];
1406
1407 // If this isn't set yet, is a theme option, or is not the default theme..
1408 if (!isset($themeData[$row['id_member']][$row['variable']]) || $row['id_theme'] != '1')
1409 $themeData[$row['id_member']][$row['variable']] = substr($row['variable'], 0, 5) == 'show_' ? $row['value'] == '1' : $row['value'];
1410 }
1411 $smcFunc['db_free_result']($result);
1412
1413 if (!empty($themeData[-1]))
1414 foreach ($themeData[-1] as $k => $v)
1415 {
1416 if (!isset($themeData[$member][$k]))
1417 $themeData[$member][$k] = $v;
1418 }
1419
1420 if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
1421 cache_put_data('theme_settings-' . $id_theme . ':' . $member, $themeData, 60);
1422 // Only if we didn't already load that part of the cache...
1423 elseif (!isset($temp))
1424 cache_put_data('theme_settings-' . $id_theme, array(-1 => $themeData[-1], 0 => $themeData[0]), 90);
1425 }
1426
1427 $settings = $themeData[0];
1428 $options = $themeData[$member];
1429
1430 $settings['theme_id'] = $id_theme;
1431
1432 $settings['actual_theme_url'] = $settings['theme_url'];
1433 $settings['actual_images_url'] = $settings['images_url'];
1434 $settings['actual_theme_dir'] = $settings['theme_dir'];
1435
1436 $settings['template_dirs'] = array();
1437 // This theme first.
1438 $settings['template_dirs'][] = $settings['theme_dir'];
1439
1440 // Based on theme (if there is one).
1441 if (!empty($settings['base_theme_dir']))
1442 $settings['template_dirs'][] = $settings['base_theme_dir'];
1443
1444 // Lastly the default theme.
1445 if ($settings['theme_dir'] != $settings['default_theme_dir'])
1446 $settings['template_dirs'][] = $settings['default_theme_dir'];
1447
1448 if (!$initialize)
1449 return;
1450
1451 // Check to see if they're accessing it from the wrong place.
1452 if (isset($_SERVER['HTTP_HOST']) || isset($_SERVER['SERVER_NAME']))
1453 {
1454 $detected_url = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on' ? 'https://' : 'http://';
1455 $detected_url .= empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] . (empty($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == '80' ? '' : ':' . $_SERVER['SERVER_PORT']) : $_SERVER['HTTP_HOST'];
1456 $temp = preg_replace('~/' . basename($scripturl) . '(/.+)?$~', '', strtr(dirname($_SERVER['PHP_SELF']), '\\', '/'));
1457 if ($temp != '/')
1458 $detected_url .= $temp;
1459 }
1460 if (isset($detected_url) && $detected_url != $boardurl)
1461 {
1462 // Try #1 - check if it's in a list of alias addresses.
1463 if (!empty($modSettings['forum_alias_urls']))
1464 {
1465 $aliases = explode(',', $modSettings['forum_alias_urls']);
1466
1467 foreach ($aliases as $alias)
1468 {
1469 // Rip off all the boring parts, spaces, etc.
1470 if ($detected_url == trim($alias) || strtr($detected_url, array('http://' => '', 'https://' => '')) == trim($alias))
1471 $do_fix = true;
1472 }
1473 }
1474
1475 // Hmm... check #2 - is it just different by a www? Send them to the correct place!!
1476 if (empty($do_fix) && strtr($detected_url, array('://' => '://www.')) == $boardurl && (empty($_GET) || count($_GET) == 1) && SMF != 'SSI')
1477 {
1478 // Okay, this seems weird, but we don't want an endless loop - this will make $_GET not empty ;).
1479 if (empty($_GET))
1480 redirectexit('wwwRedirect');
1481 else
1482 {
1483 list ($k, $v) = each($_GET);
1484
1485 if ($k != 'wwwRedirect')
1486 redirectexit('wwwRedirect;' . $k . '=' . $v);
1487 }
1488 }
1489
1490 // #3 is just a check for SSL...
1491 if (strtr($detected_url, array('https://' => 'http://')) == $boardurl)
1492 $do_fix = true;
1493
1494 // Okay, #4 - perhaps it's an IP address? We're gonna want to use that one, then. (assuming it's the IP or something...)
1495 if (!empty($do_fix) || preg_match('~^http[s]?://(?:[\d\.:]+|\[[\d:]+\](?::\d+)?)(?:$|/)~', $detected_url) == 1)
1496 {
1497 // Caching is good ;).
1498 $oldurl = $boardurl;
1499
1500 // Fix $boardurl and $scripturl.
1501 $boardurl = $detected_url;
1502 $scripturl = strtr($scripturl, array($oldurl => $boardurl));
1503 $_SERVER['REQUEST_URL'] = strtr($_SERVER['REQUEST_URL'], array($oldurl => $boardurl));
1504
1505 // Fix the theme urls...
1506 $settings['theme_url'] = strtr($settings['theme_url'], array($oldurl => $boardurl));
1507 $settings['default_theme_url'] = strtr($settings['default_theme_url'], array($oldurl => $boardurl));
1508 $settings['actual_theme_url'] = strtr($settings['actual_theme_url'], array($oldurl => $boardurl));
1509 $settings['images_url'] = strtr($settings['images_url'], array($oldurl => $boardurl));
1510 $settings['default_images_url'] = strtr($settings['default_images_url'], array($oldurl => $boardurl));
1511 $settings['actual_images_url'] = strtr($settings['actual_images_url'], array($oldurl => $boardurl));
1512
1513 // And just a few mod settings :).
1514 $modSettings['smileys_url'] = strtr($modSettings['smileys_url'], array($oldurl => $boardurl));
1515 $modSettings['avatar_url'] = strtr($modSettings['avatar_url'], array($oldurl => $boardurl));
1516
1517 // Clean up after loadBoard().
1518 if (isset($board_info['moderators']))
1519 {
1520 foreach ($board_info['moderators'] as $k => $dummy)
1521 {
1522 $board_info['moderators'][$k]['href'] = strtr($dummy['href'], array($oldurl => $boardurl));
1523 $board_info['moderators'][$k]['link'] = strtr($dummy['link'], array('"' . $oldurl => '"' . $boardurl));
1524 }
1525 }
1526 foreach ($context['linktree'] as $k => $dummy)
1527 $context['linktree'][$k]['url'] = strtr($dummy['url'], array($oldurl => $boardurl));
1528 }
1529 }
1530 // Set up the contextual user array.
1531 $context['user'] = array(
1532 'id' => $user_info['id'],
1533 'is_logged' => !$user_info['is_guest'],
1534 'is_guest' => &$user_info['is_guest'],
1535 'is_admin' => &$user_info['is_admin'],
1536 'is_mod' => &$user_info['is_mod'],
1537 // A user can mod if they have permission to see the mod center, or they are a board/group/approval moderator.
1538 'can_mod' => allowedTo('access_mod_center') || (!$user_info['is_guest'] && ($user_info['mod_cache']['gq'] != '0=1' || $user_info['mod_cache']['bq'] != '0=1' || ($modSettings['postmod_active'] && !empty($user_info['mod_cache']['ap'])))),
1539 'username' => $user_info['username'],
1540 'language' => $user_info['language'],
1541 'email' => $user_info['email'],
1542 'ignoreusers' => $user_info['ignoreusers'],
1543 );
1544 if (!$context['user']['is_guest'])
1545 $context['user']['name'] = $user_info['name'];
1546 elseif ($context['user']['is_guest'] && !empty($txt['guest_title']))
1547 $context['user']['name'] = $txt['guest_title'];
1548
1549 // Determine the current smiley set.
1550 $user_info['smiley_set'] = (!in_array($user_info['smiley_set'], explode(',', $modSettings['smiley_sets_known'])) && $user_info['smiley_set'] != 'none') || empty($modSettings['smiley_sets_enable']) ? (!empty($settings['smiley_sets_default']) ? $settings['smiley_sets_default'] : $modSettings['smiley_sets_default']) : $user_info['smiley_set'];
1551 $context['user']['smiley_set'] = $user_info['smiley_set'];
1552
1553 // Some basic information...
1554 if (!isset($context['html_headers']))
1555 $context['html_headers'] = '';
1556
1557 $context['menu_separator'] = !empty($settings['use_image_buttons']) ? ' ' : ' | ';
1558 $context['session_var'] = $_SESSION['session_var'];
1559 $context['session_id'] = $_SESSION['session_value'];
1560 $context['forum_name'] = $mbname;
1561 $context['forum_name_html_safe'] = $smcFunc['htmlspecialchars']($context['forum_name']);
1562 $context['header_logo_url_html_safe'] = empty($settings['header_logo_url']) ? '' : $smcFunc['htmlspecialchars']($settings['header_logo_url']);
1563 $context['current_action'] = isset($_REQUEST['action']) ? $_REQUEST['action'] : null;
1564 $context['current_subaction'] = isset($_REQUEST['sa']) ? $_REQUEST['sa'] : null;
1565 if (isset($modSettings['load_average']))
1566 $context['load_average'] = $modSettings['load_average'];
1567
1568 // Set some permission related settings.
1569 $context['show_login_bar'] = $user_info['is_guest'] && !empty($modSettings['enableVBStyleLogin']);
1570
1571 // This determines the server... not used in many places, except for login fixing.
1572 $context['server'] = array(
1573 'is_iis' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false,
1574 'is_apache' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false,
1575 'is_lighttpd' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') !== false,
1576 'is_nginx' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false,
1577 'is_cgi' => isset($_SERVER['SERVER_SOFTWARE']) && strpos(php_sapi_name(), 'cgi') !== false,
1578 'is_windows' => strpos(PHP_OS, 'WIN') === 0,
1579 'iso_case_folding' => ord(strtolower(chr(138))) === 154,
1580 'complex_preg_chars' => @version_compare(PHP_VERSION, '4.3.3') != -1,
1581 );
1582 // A bug in some versions of IIS under CGI (older ones) makes cookie setting not work with Location: headers.
1583 $context['server']['needs_login_fix'] = $context['server']['is_cgi'] && $context['server']['is_iis'];
1584
1585 // Detect the browser. This is separated out because it's also used in attachment downloads
1586 detectBrowser();
1587
1588 // Set the top level linktree up.
1589 array_unshift($context['linktree'], array(
1590 'url' => $scripturl,
1591 'name' => $context['forum_name_html_safe']
1592 ));
1593
1594 // This allows sticking some HTML on the page output - useful for controls.
1595 $context['insert_after_template'] = '';
1596
1597 if (!isset($txt))
1598 $txt = array();
1599 $simpleActions = array(
1600 'findmember',
1601 'helpadmin',
1602 'printpage',
1603 'quotefast',
1604 'spellcheck',
1605 );
1606
1607 // Wireless mode? Load up the wireless stuff.
1608 if (WIRELESS)
1609 {
1610 $context['template_layers'] = array(WIRELESS_PROTOCOL);
1611 loadTemplate('Wireless');
1612 loadLanguage('Wireless+index+Modifications');
1613 }
1614 // Output is fully XML, so no need for the index template.
1615 elseif (isset($_REQUEST['xml']))
1616 {
1617 loadLanguage('index+Modifications');
1618 loadTemplate('Xml');
1619 $context['template_layers'] = array();
1620 }
1621 // These actions don't require the index template at all.
1622 elseif (!empty($_REQUEST['action']) && in_array($_REQUEST['action'], $simpleActions))
1623 {
1624 loadLanguage('index+Modifications');
1625 $context['template_layers'] = array();
1626 }
1627 else
1628 {
1629 // Custom templates to load, or just default?
1630 if (isset($settings['theme_templates']))
1631 $templates = explode(',', $settings['theme_templates']);
1632 else
1633 $templates = array('index');
1634
1635 // Load each template...
1636 foreach ($templates as $template)
1637 loadTemplate($template);
1638
1639 // ...and attempt to load their associated language files.
1640 $required_files = implode('+', array_merge($templates, array('Modifications')));
1641 loadLanguage($required_files, '', false);
1642
1643 // Custom template layers?
1644 if (isset($settings['theme_layers']))
1645 $context['template_layers'] = explode(',', $settings['theme_layers']);
1646 else
1647 $context['template_layers'] = array('html', 'body');
1648 }
1649
1650 // Initialize the theme.
1651 loadSubTemplate('init', 'ignore');
1652
1653 // Load the compatibility stylesheet if the theme hasn't been updated for 2.0 RC2 (yet).
1654 if (isset($settings['theme_version']) && (version_compare($settings['theme_version'], '2.0 RC2', '<') || strpos($settings['theme_version'], '2.0 Beta') !== false))
1655 loadTemplate(false, 'compat');
1656
1657 // Guests may still need a name.
1658 if ($context['user']['is_guest'] && empty($context['user']['name']))
1659 $context['user']['name'] = $txt['guest_title'];
1660
1661 // Any theme-related strings that need to be loaded?
1662 if (!empty($settings['require_theme_strings']))
1663 loadLanguage('ThemeStrings', '', false);
1664
1665 // We allow theme variants, because we're cool.
1666 $context['theme_variant'] = '';
1667 $context['theme_variant_url'] = '';
1668 if (!empty($settings['theme_variants']))
1669 {
1670 // Overriding - for previews and that ilk.
1671 if (!empty($_REQUEST['variant']))
1672 $_SESSION['id_variant'] = $_REQUEST['variant'];
1673 // User selection?
1674 if (empty($settings['disable_user_variant']) || allowedTo('admin_forum'))
1675 $context['theme_variant'] = !empty($_SESSION['id_variant']) ? $_SESSION['id_variant'] : (!empty($options['theme_variant']) ? $options['theme_variant'] : '');
1676 // If not a user variant, select the default.
1677 if ($context['theme_variant'] == '' || !in_array($context['theme_variant'], $settings['theme_variants']))
1678 $context['theme_variant'] = !empty($settings['default_variant']) && in_array($settings['default_variant'], $settings['theme_variants']) ? $settings['default_variant'] : $settings['theme_variants'][0];
1679
1680 // Do this to keep things easier in the templates.
1681 $context['theme_variant'] = '_' . $context['theme_variant'];
1682 $context['theme_variant_url'] = $context['theme_variant'] . '/';
1683 }
1684
1685 // Let's be compatible with old themes!
1686 if (!function_exists('template_html_above') && in_array('html', $context['template_layers']))
1687 $context['template_layers'] = array('main');
1688
1689 // Allow overriding the board wide time/number formats.
1690 if (empty($user_settings['time_format']) && !empty($txt['time_format']))
1691 $user_info['time_format'] = $txt['time_format'];
1692 $txt['number_format'] = empty($txt['number_format']) ? empty($modSettings['number_format']) ? '' : $modSettings['number_format'] : $txt['number_format'];
1693
1694 if (isset($settings['use_default_images']) && $settings['use_default_images'] == 'always')
1695 {
1696 $settings['theme_url'] = $settings['default_theme_url'];
1697 $settings['images_url'] = $settings['default_images_url'];
1698 $settings['theme_dir'] = $settings['default_theme_dir'];
1699 }
1700 // Make a special URL for the language.
1701 $settings['lang_images_url'] = $settings['images_url'] . '/' . (!empty($txt['image_lang']) ? $txt['image_lang'] : $user_info['language']);
1702
1703 // Set the character set from the template.
1704 $context['character_set'] = empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set'];
1705 $context['utf8'] = $context['character_set'] === 'UTF-8' && (strpos(strtolower(PHP_OS), 'win') === false || @version_compare(PHP_VERSION, '4.2.3') != -1);
1706 $context['right_to_left'] = !empty($txt['lang_rtl']);
1707
1708 $context['tabindex'] = 1;
1709
1710 // Fix font size with HTML 4.01, etc.
1711 if (isset($settings['doctype']))
1712 $context['browser']['needs_size_fix'] |= $settings['doctype'] == 'html' && $context['browser']['is_ie6'];
1713
1714 // Compatibility.
1715 if (!isset($settings['theme_version']))
1716 $modSettings['memberCount'] = $modSettings['totalMembers'];
1717
1718 // This allows us to change the way things look for the admin.
1719 $context['admin_features'] = isset($modSettings['admin_features']) ? explode(',', $modSettings['admin_features']) : array('cd,cp,k,w,rg,ml,pm');
1720
1721 // If we think we have mail to send, let's offer up some possibilities... robots get pain (Now with scheduled task support!)
1722 if ((!empty($modSettings['mail_next_send']) && $modSettings['mail_next_send'] < time() && empty($modSettings['mail_queue_use_cron'])) || empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time())
1723 {
1724 if ($context['browser']['possibly_robot'])
1725 {
1726 //!!! Maybe move this somewhere better?!
1727 require_once($sourcedir . '/ScheduledTasks.php');
1728
1729 // What to do, what to do?!
1730 if (empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time())
1731 AutoTask();
1732 else
1733 ReduceMailQueue();
1734 }
1735 else
1736 {
1737 $type = empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time() ? 'task' : 'mailq';
1738 $ts = $type == 'mailq' ? $modSettings['mail_next_send'] : $modSettings['next_task_time'];
1739
1740 $context['html_headers'] .= '
1741 <script type="text/javascript">
1742 function smfAutoTask()
1743 {
1744 var tempImage = new Image();
1745 tempImage.src = "' . $scripturl . '?scheduled=' . $type . ';ts=' . $ts . '";
1746 }
1747 window.setTimeout("smfAutoTask();", 1);
1748 </script>';
1749 }
1750 }
1751
1752 // Any files to include at this point?
1753 if (!empty($modSettings['integrate_theme_include']))
1754 {
1755 $theme_includes = explode(',', $modSettings['integrate_theme_include']);
1756 foreach ($theme_includes as $include)
1757 {
1758 $include = strtr(trim($include), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir, '$themedir' => $settings['theme_dir']));
1759 if (file_exists($include))
1760 require_once($include);
1761 }
1762 }
1763
1764 // Call load theme integration functions.
1765 call_integration_hook('integrate_load_theme');
1766
1767 // We are ready to go.
1768 $context['theme_loaded'] = true;
1769 }
1770
1771 // Load a template - if the theme doesn't include it, use the default.
1772 function loadTemplate($template_name, $style_sheets = array(), $fatal = true)
1773 {
1774 global $context, $settings, $txt, $scripturl, $boarddir, $db_show_debug;
1775
1776 // Do any style sheets first, cause we're easy with those.
1777 if (!empty($style_sheets))
1778 {
1779 if (!is_array($style_sheets))
1780 $style_sheets = array($style_sheets);
1781
1782 foreach ($style_sheets as $sheet)
1783 {
1784 // Prevent the style sheet from being included twice.
1785 if (strpos($context['html_headers'], 'id="' . $sheet . '_css"') !== false)
1786 continue;
1787
1788 $sheet_path = file_exists($settings['theme_dir']. '/css/' . $sheet . '.css') ? 'theme_url' : (file_exists($settings['default_theme_dir']. '/css/' . $sheet . '.css') ? 'default_theme_url' : '');
1789 if ($sheet_path)
1790 {
1791 $context['html_headers'] .= "\n\t" . '<link rel="stylesheet" type="text/css" id="' . $sheet . '_css" href="' . $settings[$sheet_path] . '/css/' . $sheet . '.css" />';
1792 if ($db_show_debug === true)
1793 $context['debug']['sheets'][] = $sheet . ' (' . basename($settings[$sheet_path]) . ')';
1794 }
1795 }
1796 }
1797
1798 // No template to load?
1799 if ($template_name === false)
1800 return true;
1801
1802 $loaded = false;
1803 foreach ($settings['template_dirs'] as $template_dir)
1804 {
1805 if (file_exists($template_dir . '/' . $template_name . '.template.php'))
1806 {
1807 $loaded = true;
1808 template_include($template_dir . '/' . $template_name . '.template.php', true);
1809 break;
1810 }
1811 }
1812
1813 if ($loaded)
1814 {
1815 // For compatibility reasons, if this is the index template without new functions, include compatible stuff.
1816 if (substr($template_name, 0, 5) == 'index' && !function_exists('template_button_strip'))
1817 loadTemplate('Compat');
1818
1819 if ($db_show_debug === true)
1820 $context['debug']['templates'][] = $template_name . ' (' . basename($template_dir) . ')';
1821
1822 // If they have specified an initialization function for this template, go ahead and call it now.
1823 if (function_exists('template_' . $template_name . '_init'))
1824 call_user_func('template_' . $template_name . '_init');
1825 }
1826 // Hmmm... doesn't exist?! I don't suppose the directory is wrong, is it?
1827 elseif (!file_exists($settings['default_theme_dir']) && file_exists($boarddir . '/Themes/default'))
1828 {
1829 $settings['default_theme_dir'] = $boarddir . '/Themes/default';
1830 $settings['template_dirs'][] = $settings['default_theme_dir'];
1831
1832 if (!empty($context['user']['is_admin']) && !isset($_GET['th']))
1833 {
1834 loadLanguage('Errors');
1835 echo '
1836 <div class="alert errorbox">
1837 <a href="', $scripturl . '?action=admin;area=theme;sa=settings;th=1;' . $context['session_var'] . '=' . $context['session_id'], '" class="alert">', $txt['theme_dir_wrong'], '</a>
1838 </div>';
1839 }
1840
1841 loadTemplate($template_name);
1842 }
1843 // Cause an error otherwise.
1844 elseif ($template_name != 'Errors' && $template_name != 'index' && $fatal)
1845 fatal_lang_error('theme_template_error', 'template', array((string) $template_name));
1846 elseif ($fatal)
1847 die(log_error(sprintf(isset($txt['theme_template_error']) ? $txt['theme_template_error'] : 'Unable to load Themes/default/%s.template.php!', (string) $template_name), 'template'));
1848 else
1849 return false;
1850 }
1851
1852 // Load a sub template... fatal is for templates that shouldn't get a 'pretty' error screen.
1853 function loadSubTemplate($sub_template_name, $fatal = false)
1854 {
1855 global $context, $settings, $options, $txt, $db_show_debug;
1856
1857 if ($db_show_debug === true)
1858 $context['debug']['sub_templates'][] = $sub_template_name;
1859
1860 // Figure out what the template function is named.
1861 $theme_function = 'template_' . $sub_template_name;
1862 if (function_exists($theme_function))
1863 $theme_function();
1864 elseif ($fatal === false)
1865 fatal_lang_error('theme_template_error', 'template', array((string) $sub_template_name));
1866 elseif ($fatal !== 'ignore')
1867 die(log_error(sprintf(isset($txt['theme_template_error']) ? $txt['theme_template_error'] : 'Unable to load the %s sub template!', (string) $sub_template_name), 'template'));
1868
1869 // Are we showing debugging for templates? Just make sure not to do it before the doctype...
1870 if (allowedTo('admin_forum') && isset($_REQUEST['debug']) && !in_array($sub_template_name, array('init', 'main_below')) && ob_get_length() > 0 && !isset($_REQUEST['xml']))
1871 {
1872 echo '
1873 <div style="font-size: 8pt; border: 1px dashed red; background: orange; text-align: center; font-weight: bold;">---- ', $sub_template_name, ' ends ----</div>';
1874 }
1875 }
1876
1877 // Load a language file. Tries the current and default themes as well as the user and global languages.
1878 function loadLanguage($template_name, $lang = '', $fatal = true, $force_reload = false)
1879 {
1880 global $user_info, $language, $settings, $context, $modSettings;
1881 global $cachedir, $db_show_debug, $sourcedir, $txt;
1882 static $already_loaded = array();
1883
1884 // Default to the user's language.
1885 if ($lang == '')
1886 $lang = isset($user_info['language']) ? $user_info['language'] : $language;
1887
1888 // Do we want the English version of language file as fallback?
1889 if (empty($modSettings['disable_language_fallback']) && $lang != 'english')
1890 loadLanguage($template_name, 'english', false);
1891
1892 if (!$force_reload && isset($already_loaded[$template_name]) && $already_loaded[$template_name] == $lang)
1893 return $lang;
1894
1895 // Make sure we have $settings - if not we're in trouble and need to find it!
1896 if (empty($settings['default_theme_dir']))
1897 {
1898 require_once($sourcedir . '/ScheduledTasks.php');
1899 loadEssentialThemeData();
1900 }
1901
1902 // What theme are we in?
1903 $theme_name = basename($settings['theme_url']);
1904 if (empty($theme_name))
1905 $theme_name = 'unknown';
1906
1907 // For each file open it up and write it out!
1908 foreach (explode('+', $template_name) as $template)
1909 {
1910 // Obviously, the current theme is most important to check.
1911 $attempts = array(
1912 array($settings['theme_dir'], $template, $lang, $settings['theme_url']),
1913 array($settings['theme_dir'], $template, $language, $settings['theme_url']),
1914 );
1915
1916 // Do we have a base theme to worry about?
1917 if (isset($settings['base_theme_dir']))
1918 {
1919 $attempts[] = array($settings['base_theme_dir'], $template, $lang, $settings['base_theme_url']);
1920 $attempts[] = array($settings['base_theme_dir'], $template, $language, $settings['base_theme_url']);
1921 }
1922
1923 // Fall back on the default theme if necessary.
1924 $attempts[] = array($settings['default_theme_dir'], $template, $lang, $settings['default_theme_url']);
1925 $attempts[] = array($settings['default_theme_dir'], $template, $language, $settings['default_theme_url']);
1926
1927 // Fall back on the English language if none of the preferred languages can be found.
1928 if (!in_array('english', array($lang, $language)))
1929 {
1930 $attempts[] = array($settings['theme_dir'], $template, 'english', $settings['theme_url']);
1931 $attempts[] = array($settings['default_theme_dir'], $template, 'english', $settings['default_theme_url']);
1932 }
1933
1934 // Try to find the language file.
1935 $found = false;
1936 foreach ($attempts as $k => $file)
1937 {
1938 if (file_exists($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php'))
1939 {
1940 // Include it!
1941 template_include($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php');
1942
1943 // Note that we found it.
1944 $found = true;
1945
1946 break;
1947 }
1948 }
1949
1950 // That couldn't be found! Log the error, but *try* to continue normally.
1951 if (!$found && $fatal)
1952 {
1953 log_error(sprintf($txt['theme_language_error'], $template_name . '.' . $lang, 'template'));
1954 break;
1955 }
1956 }
1957
1958 // Keep track of what we're up to soldier.
1959 if ($db_show_debug === true)
1960 $context['debug']['language_files'][] = $template_name . '.' . $lang . ' (' . $theme_name . ')';
1961
1962 // Remember what we have loaded, and in which language.
1963 $already_loaded[$template_name] = $lang;
1964
1965 // Return the language actually loaded.
1966 return $lang;
1967 }
1968
1969 // Get all parent boards (requires first parent as parameter)
1970 function getBoardParents($id_parent)
1971 {
1972 global $scripturl, $smcFunc;
1973
1974 // First check if we have this cached already.
1975 if (($boards = cache_get_data('board_parents-' . $id_parent, 480)) === null)
1976 {
1977 $boards = array();
1978 $original_parent = $id_parent;
1979
1980 // Loop while the parent is non-zero.
1981 while ($id_parent != 0)
1982 {
1983 $result = $smcFunc['db_query']('', '
1984 SELECT
1985 b.id_parent, b.name, {int:board_parent} AS id_board, IFNULL(mem.id_member, 0) AS id_moderator,
1986 mem.real_name, b.child_level
1987 FROM {db_prefix}boards AS b
1988 LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board)
1989 LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
1990 WHERE b.id_board = {int:board_parent}',
1991 array(
1992 'board_parent' => $id_parent,
1993 )
1994 );
1995 // In the EXTREMELY unlikely event this happens, give an error message.
1996 if ($smcFunc['db_num_rows']($result) == 0)
1997 fatal_lang_error('parent_not_found', 'critical');
1998 while ($row = $smcFunc['db_fetch_assoc']($result))
1999 {
2000 if (!isset($boards[$row['id_board']]))
2001 {
2002 $id_parent = $row['id_parent'];
2003 $boards[$row['id_board']] = array(
2004 'url' => $scripturl . '?board=' . $row['id_board'] . '.0',
2005 'name' => $row['name'],
2006 'level' => $row['child_level'],
2007 'moderators' => array()
2008 );
2009 }
2010 // If a moderator exists for this board, add that moderator for all children too.
2011 if (!empty($row['id_moderator']))
2012 foreach ($boards as $id => $dummy)
2013 {
2014 $boards[$id]['moderators'][$row['id_moderator']] = array(
2015 'id' => $row['id_moderator'],
2016 'name' => $row['real_name'],
2017 'href' => $scripturl . '?action=profile;u=' . $row['id_moderator'],
2018 'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_moderator'] . '">' . $row['real_name'] . '</a>'
2019 );
2020 }
2021 }
2022 $smcFunc['db_free_result']($result);
2023 }
2024
2025 cache_put_data('board_parents-' . $original_parent, $boards, 480);
2026 }
2027
2028 return $boards;
2029 }
2030
2031 // Attempt to reload our languages.
2032 function getLanguages($use_cache = true, $favor_utf8 = true)
2033 {
2034 global $context, $smcFunc, $settings, $modSettings;
2035
2036 // Either we don't use the cache, or its expired.
2037 if (!$use_cache || ($context['languages'] = cache_get_data('known_languages' . ($favor_utf8 ? '' : '_all'), !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600)) == null)
2038 {
2039 // If we don't have our theme information yet, lets get it.
2040 if (empty($settings['default_theme_dir']))
2041 loadTheme(0, false);
2042
2043 // Default language directories to try.
2044 $language_directories = array(
2045 $settings['default_theme_dir'] . '/languages',
2046 $settings['actual_theme_dir'] . '/languages',
2047 );
2048
2049 // We possibly have a base theme directory.
2050 if (!empty($settings['base_theme_dir']))
2051 $language_directories[] = $settings['base_theme_dir'] . '/languages';
2052
2053 // Remove any duplicates.
2054 $language_directories = array_unique($language_directories);
2055
2056 foreach ($language_directories as $language_dir)
2057 {
2058 // Can't look in here... doesn't exist!
2059 if (!file_exists($language_dir))
2060 continue;
2061
2062 $dir = dir($language_dir);
2063 while ($entry = $dir->read())
2064 {
2065 // Look for the index language file....
2066 if (!preg_match('~^index\.(.+)\.php$~', $entry, $matches))
2067 continue;
2068
2069 $context['languages'][$matches[1]] = array(
2070 'name' => $smcFunc['ucwords'](strtr($matches[1], array('_' => ' '))),
2071 'selected' => false,
2072 'filename' => $matches[1],
2073 'location' => $language_dir . '/index.' . $matches[1] . '.php',
2074 );
2075
2076 }
2077 $dir->close();
2078 }
2079
2080 // Favoring UTF8? Then prevent us from selecting non-UTF8 versions.
2081 if ($favor_utf8)
2082 {
2083 foreach ($context['languages'] as $lang)
2084 if (substr($lang['filename'], strlen($lang['filename']) - 5, 5) != '-utf8' && isset($context['languages'][$lang['filename'] . '-utf8']))
2085 unset($context['languages'][$lang['filename']]);
2086 }
2087
2088 // Lets cash in on this deal.
2089 if (!empty($modSettings['cache_enable']))
2090 cache_put_data('known_languages' . ($favor_utf8 ? '' : '_all'), $context['languages'], !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600);
2091 }
2092
2093 return $context['languages'];
2094 }
2095
2096 // Replace all vulgar words with respective proper words. (substring or whole words..)
2097 function &censorText(&$text, $force = false)
2098 {
2099 global $modSettings, $options, $settings, $txt;
2100 static $censor_vulgar = null, $censor_proper;
2101
2102 if ((!empty($options['show_no_censored']) && $settings['allow_no_censored'] && !$force) || empty($modSettings['censor_vulgar']))
2103 return $text;
2104
2105 // If they haven't yet been loaded, load them.
2106 if ($censor_vulgar == null)
2107 {
2108 $censor_vulgar = explode("\n", $modSettings['censor_vulgar']);
2109 $censor_proper = explode("\n", $modSettings['censor_proper']);
2110
2111 // Quote them for use in regular expressions.
2112 for ($i = 0, $n = count($censor_vulgar); $i < $n; $i++)
2113 {
2114 $censor_vulgar[$i] = strtr(preg_quote($censor_vulgar[$i], '/'), array('\\\\\\*' => '[*]', '\\*' => '[^\s]*?', '&' => '&amp;'));
2115 $censor_vulgar[$i] = (empty($modSettings['censorWholeWord']) ? '/' . $censor_vulgar[$i] . '/' : '/(?<=^|\W)' . $censor_vulgar[$i] . '(?=$|\W)/') . (empty($modSettings['censorIgnoreCase']) ? '' : 'i') . ((empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8' ? 'u' : '');
2116
2117 if (strpos($censor_vulgar[$i], '\'') !== false)
2118 {
2119 $censor_proper[count($censor_vulgar)] = $censor_proper[$i];
2120 $censor_vulgar[count($censor_vulgar)] = strtr($censor_vulgar[$i], array('\'' => '&#039;'));
2121 }
2122 }
2123 }
2124
2125 // Censoring isn't so very complicated :P.
2126 $text = preg_replace($censor_vulgar, $censor_proper, $text);
2127 return $text;
2128 }
2129
2130 // Load the template/language file using eval or require? (with eval we can show an error message!)
2131 function template_include($filename, $once = false)
2132 {
2133 global $context, $settings, $options, $txt, $scripturl, $modSettings;
2134 global $user_info, $boardurl, $boarddir, $sourcedir;
2135 global $maintenance, $mtitle, $mmessage;
2136 static $templates = array();
2137
2138 // We want to be able to figure out any errors...
2139 @ini_set('track_errors', '1');
2140
2141 // Don't include the file more than once, if $once is true.
2142 if ($once && in_array($filename, $templates))
2143 return;
2144 // Add this file to the include list, whether $once is true or not.
2145 else
2146 $templates[] = $filename;
2147
2148 // Are we going to use eval?
2149 if (empty($modSettings['disableTemplateEval']))
2150 {
2151 $file_found = file_exists($filename) && eval('?' . '>' . rtrim(file_get_contents($filename))) !== false;
2152 $settings['current_include_filename'] = $filename;
2153 }
2154 else
2155 {
2156 $file_found = file_exists($filename);
2157
2158 if ($once && $file_found)
2159 require_once($filename);
2160 elseif ($file_found)
2161 require($filename);
2162 }
2163
2164 if ($file_found !== true)
2165 {
2166 ob_end_clean();
2167 if (!empty($modSettings['enableCompressedOutput']))
2168 @ob_start('ob_gzhandler');
2169 else
2170 ob_start();
2171
2172 if (isset($_GET['debug']) && !WIRELESS)
2173 header('Content-Type: application/xhtml+xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
2174
2175 // Don't cache error pages!!
2176 header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
2177 header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
2178 header('Cache-Control: no-cache');
2179
2180 if (!isset($txt['template_parse_error']))
2181 {
2182 $txt['template_parse_error'] = 'Template Parse Error!';
2183 $txt['template_parse_error_message'] = 'It seems something has gone sour on the forum with the template system. This problem should only be temporary, so please come back later and try again. If you continue to see this message, please contact the administrator.<br /><br />You can also try <a href="javascript:location.reload();">refreshing this page</a>.';
2184 $txt['template_parse_error_details'] = 'There was a problem loading the <tt><strong>%1$s</strong></tt> template or language file. Please check the syntax and try again - remember, single quotes (<tt>\'</tt>) often have to be escaped with a slash (<tt>\\</tt>). To see more specific error information from PHP, try <a href="' . $boardurl . '%1$s" class="extern">accessing the file directly</a>.<br /><br />You may want to try to <a href="javascript:location.reload();">refresh this page</a> or <a href="' . $scripturl . '?theme=1">use the default theme</a>.';
2185 }
2186
2187 // First, let's get the doctype and language information out of the way.
2188 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2189 <html xmlns="http://www.w3.org/1999/xhtml"', !empty($context['right_to_left']) ? ' dir="rtl"' : '', '>
2190 <head>';
2191 if (isset($context['character_set']))
2192 echo '
2193 <meta http-equiv="Content-Type" content="text/html; charset=', $context['character_set'], '" />';
2194
2195 if (!empty($maintenance) && !allowedTo('admin_forum'))
2196 echo '
2197 <title>', $mtitle, '</title>
2198 </head>
2199 <body>
2200 <h3>', $mtitle, '</h3>
2201 ', $mmessage, '
2202 </body>
2203 </html>';
2204 elseif (!allowedTo('admin_forum'))
2205 echo '
2206 <title>', $txt['template_parse_error'], '</title>
2207 </head>
2208 <body>
2209 <h3>', $txt['template_parse_error'], '</h3>
2210 ', $txt['template_parse_error_message'], '
2211 </body>
2212 </html>';
2213 else
2214 {
2215 require_once($sourcedir . '/Subs-Package.php');
2216
2217 $error = fetch_web_data($boardurl . strtr($filename, array($boarddir => '', strtr($boarddir, '\\', '/') => '')));
2218 if (empty($error))
2219 $error = $php_errormsg;
2220
2221 $error = strtr($error, array('<b>' => '<strong>', '</b>' => '</strong>'));
2222
2223 echo '
2224 <title>', $txt['template_parse_error'], '</title>
2225 </head>
2226 <body>
2227 <h3>', $txt['template_parse_error'], '</h3>
2228 ', sprintf($txt['template_parse_error_details'], strtr($filename, array($boarddir => '', strtr($boarddir, '\\', '/') => '')));
2229
2230 if (!empty($error))
2231 echo '
2232 <hr />
2233
2234 <div style="margin: 0 20px;"><tt>', strtr(strtr($error, array('<strong>' . $boarddir => '<strong>...', '<strong>' . strtr($boarddir, '\\', '/') => '<strong>...')), '\\', '/'), '</tt></div>';
2235
2236 // I know, I know... this is VERY COMPLICATED. Still, it's good.
2237 if (preg_match('~ <strong>(\d+)</strong><br( /)?' . '>$~i', $error, $match) != 0)
2238 {
2239 $data = file($filename);
2240 $data2 = highlight_php_code(implode('', $data));
2241 $data2 = preg_split('~\<br( /)?\>~', $data2);
2242
2243 // Fix the PHP code stuff...
2244 if ($context['browser']['is_ie4'] || $context['browser']['is_ie5'] || $context['browser']['is_ie5.5'])
2245 $data2 = str_replace("\t", '<pre style="display: inline;">' . "\t" . '</pre>', $data2);
2246 elseif (!$context['browser']['is_gecko'])
2247 $data2 = str_replace("\t", '<span style="white-space: pre;">' . "\t" . '</span>', $data2);
2248 else
2249 $data2 = str_replace('<pre style="display: inline;">' . "\t" . '</pre>', "\t", $data2);
2250
2251 // Now we get to work around a bug in PHP where it doesn't escape <br />s!
2252 $j = -1;
2253 foreach ($data as $line)
2254 {
2255 $j++;
2256
2257 if (substr_count($line, '<br />') == 0)
2258 continue;
2259
2260 $n = substr_count($line, '<br />');
2261 for ($i = 0; $i < $n; $i++)
2262 {
2263 $data2[$j] .= '&lt;br /&gt;' . $data2[$j + $i + 1];
2264 unset($data2[$j + $i + 1]);
2265 }
2266 $j += $n;
2267 }
2268 $data2 = array_values($data2);
2269 array_unshift($data2, '');
2270
2271 echo '
2272 <div style="margin: 2ex 20px; width: 96%; overflow: auto;"><pre style="margin: 0;">';
2273
2274 // Figure out what the color coding was before...
2275 $line = max($match[1] - 9, 1);
2276 $last_line = '';
2277 for ($line2 = $line - 1; $line2 > 1; $line2--)
2278 if (strpos($data2[$line2], '<') !== false)
2279 {
2280 if (preg_match('~(<[^/>]+>)[^<]*$~', $data2[$line2], $color_match) != 0)
2281 $last_line = $color_match[1];
2282 break;
2283 }
2284
2285 // Show the relevant lines...
2286 for ($n = min($match[1] + 4, count($data2) + 1); $line <= $n; $line++)
2287 {
2288 if ($line == $match[1])
2289 echo '</pre><div style="background-color: #ffb0b5;"><pre style="margin: 0;">';
2290
2291 echo '<span style="color: black;">', sprintf('%' . strlen($n) . 's', $line), ':</span> ';
2292 if (isset($data2[$line]) && $data2[$line] != '')
2293 echo substr($data2[$line], 0, 2) == '</' ? preg_replace('~^</[^>]+>~', '', $data2[$line]) : $last_line . $data2[$line];
2294
2295 if (isset($data2[$line]) && preg_match('~(<[^/>]+>)[^<]*$~', $data2[$line], $color_match) != 0)
2296 {
2297 $last_line = $color_match[1];
2298 echo '</', substr($last_line, 1, 4), '>';
2299 }
2300 elseif ($last_line != '' && strpos($data2[$line], '<') !== false)
2301 $last_line = '';
2302 elseif ($last_line != '' && $data2[$line] != '')
2303 echo '</', substr($last_line, 1, 4), '>';
2304
2305 if ($line == $match[1])
2306 echo '</pre></div><pre style="margin: 0;">';
2307 else
2308 echo "\n";
2309 }
2310
2311 echo '</pre></div>';
2312 }
2313
2314 echo '
2315 </body>
2316 </html>';
2317 }
2318
2319 die;
2320 }
2321 }
2322
2323 // Attempt to start the session, unless it already has been.
2324 function loadSession()
2325 {
2326 global $HTTP_SESSION_VARS, $modSettings, $boardurl, $sc;
2327
2328 // Attempt to change a few PHP settings.
2329 @ini_set('session.use_cookies', true);
2330 @ini_set('session.use_only_cookies', false);
2331 @ini_set('url_rewriter.tags', '');
2332 @ini_set('session.use_trans_sid', false);
2333 @ini_set('arg_separator.output', '&amp;');
2334
2335 if (!empty($modSettings['globalCookies']))
2336 {
2337 $parsed_url = parse_url($boardurl);
2338
2339 if (preg_match('~^\d{1,3}(\.\d{1,3}){3}$~', $parsed_url['host']) == 0 && preg_match('~(?:[^\.]+\.)?([^\.]{2,}\..+)\z~i', $parsed_url['host'], $parts) == 1)
2340 @ini_set('session.cookie_domain', '.' . $parts[1]);
2341 }
2342 // !!! Set the session cookie path?
2343
2344 // If it's already been started... probably best to skip this.
2345 if ((@ini_get('session.auto_start') == 1 && !empty($modSettings['databaseSession_enable'])) || session_id() == '')
2346 {
2347 // Attempt to end the already-started session.
2348 if (@ini_get('session.auto_start') == 1)
2349 @session_write_close();
2350
2351 // This is here to stop people from using bad junky PHPSESSIDs.
2352 if (isset($_REQUEST[session_name()]) && preg_match('~^[A-Za-z0-9,-]{16,32}$~', $_REQUEST[session_name()]) == 0 && !isset($_COOKIE[session_name()]))
2353 {
2354 $session_id = md5(md5('smf_sess_' . time()) . mt_rand());
2355 $_REQUEST[session_name()] = $session_id;
2356 $_GET[session_name()] = $session_id;
2357 $_POST[session_name()] = $session_id;
2358 }
2359
2360 // Use database sessions? (they don't work in 4.1.x!)
2361 if (!empty($modSettings['databaseSession_enable']) && @version_compare(PHP_VERSION, '4.2.0') != -1)
2362 {
2363 session_set_save_handler('sessionOpen', 'sessionClose', 'sessionRead', 'sessionWrite', 'sessionDestroy', 'sessionGC');
2364 @ini_set('session.gc_probability', '1');
2365 }
2366 elseif (@ini_get('session.gc_maxlifetime') <= 1440 && !empty($modSettings['databaseSession_lifetime']))
2367 @ini_set('session.gc_maxlifetime', max($modSettings['databaseSession_lifetime'], 60));
2368
2369 // Use cache setting sessions?
2370 if (empty($modSettings['databaseSession_enable']) && !empty($modSettings['cache_enable']) && php_sapi_name() != 'cli')
2371 {
2372 if (function_exists('mmcache_set_session_handlers'))
2373 mmcache_set_session_handlers();
2374 elseif (function_exists('eaccelerator_set_session_handlers'))
2375 eaccelerator_set_session_handlers();
2376 }
2377
2378 session_start();
2379
2380 // Change it so the cache settings are a little looser than default.
2381 if (!empty($modSettings['databaseSession_loose']))
2382 header('Cache-Control: private');
2383 }
2384
2385 // While PHP 4.1.x should use $_SESSION, it seems to need this to do it right.
2386 if (@version_compare(PHP_VERSION, '4.2.0') == -1)
2387 $HTTP_SESSION_VARS['php_412_bugfix'] = true;
2388
2389 // Set the randomly generated code.
2390 if (!isset($_SESSION['session_var']))
2391 {
2392 $_SESSION['session_value'] = md5(session_id() . mt_rand());
2393 $_SESSION['session_var'] = substr(preg_replace('~^\d+~', '', sha1(mt_rand() . session_id() . mt_rand())), 0, rand(7, 12));
2394 }
2395 $sc = $_SESSION['session_value'];
2396 }
2397
2398 function sessionOpen($save_path, $session_name)
2399 {
2400 return true;
2401 }
2402
2403 function sessionClose()
2404 {
2405 return true;
2406 }
2407
2408 function sessionRead($session_id)
2409 {
2410 global $smcFunc;
2411
2412 if (preg_match('~^[A-Za-z0-9,-]{16,32}$~', $session_id) == 0)
2413 return false;
2414
2415 // Look for it in the database.
2416 $result = $smcFunc['db_query']('', '
2417 SELECT data
2418 FROM {db_prefix}sessions
2419 WHERE session_id = {string:session_id}
2420 LIMIT 1',
2421 array(
2422 'session_id' => $session_id,
2423 )
2424 );
2425 list ($sess_data) = $smcFunc['db_fetch_row']($result);
2426 $smcFunc['db_free_result']($result);
2427
2428 return $sess_data;
2429 }
2430
2431 function sessionWrite($session_id, $data)
2432 {
2433 global $smcFunc;
2434
2435 if (preg_match('~^[A-Za-z0-9,-]{16,32}$~', $session_id) == 0)
2436 return false;
2437
2438 // First try to update an existing row...
2439 $result = $smcFunc['db_query']('', '
2440 UPDATE {db_prefix}sessions
2441 SET data = {string:data}, last_update = {int:last_update}
2442 WHERE session_id = {string:session_id}',
2443 array(
2444 'last_update' => time(),
2445 'data' => $data,
2446 'session_id' => $session_id,
2447 )
2448 );
2449
2450 // If that didn't work, try inserting a new one.
2451 if ($smcFunc['db_affected_rows']() == 0)
2452 $result = $smcFunc['db_insert']('ignore',
2453 '{db_prefix}sessions',
2454 array('session_id' => 'string', 'data' => 'string', 'last_update' => 'int'),
2455 array($session_id, $data, time()),
2456 array('session_id')
2457 );
2458
2459 return $result;
2460 }
2461
2462 function sessionDestroy($session_id)
2463 {
2464 global $smcFunc;
2465
2466 if (preg_match('~^[A-Za-z0-9,-]{16,32}$~', $session_id) == 0)
2467 return false;
2468
2469 // Just delete the row...
2470 return $smcFunc['db_query']('', '
2471 DELETE FROM {db_prefix}sessions
2472 WHERE session_id = {string:session_id}',
2473 array(
2474 'session_id' => $session_id,
2475 )
2476 );
2477 }
2478
2479 function sessionGC($max_lifetime)
2480 {
2481 global $modSettings, $smcFunc;
2482
2483 // Just set to the default or lower? Ignore it for a higher value. (hopefully)
2484 if (!empty($modSettings['databaseSession_lifetime']) && ($max_lifetime <= 1440 || $modSettings['databaseSession_lifetime'] > $max_lifetime))
2485 $max_lifetime = max($modSettings['databaseSession_lifetime'], 60);
2486
2487 // Clean up ;).
2488 return $smcFunc['db_query']('', '
2489 DELETE FROM {db_prefix}sessions
2490 WHERE last_update < {int:last_update}',
2491 array(
2492 'last_update' => time() - $max_lifetime,
2493 )
2494 );
2495 }
2496
2497 // Load up a database connection.
2498 function loadDatabase()
2499 {
2500 global $db_persist, $db_connection, $db_server, $db_user, $db_passwd;
2501 global $db_type, $db_name, $ssi_db_user, $ssi_db_passwd, $sourcedir, $db_prefix;
2502
2503 // Figure out what type of database we are using.
2504 if (empty($db_type) || !file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
2505 $db_type = 'mysql';
2506
2507 // Load the file for the database.
2508 require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
2509
2510 // If we are in SSI try them first, but don't worry if it doesn't work, we have the normal username and password we can use.
2511 if (SMF == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd))
2512 $db_connection = smf_db_initiate($db_server, $db_name, $ssi_db_user, $ssi_db_passwd, $db_prefix, array('persist' => $db_persist, 'non_fatal' => true, 'dont_select_db' => true));
2513
2514 // Either we aren't in SSI mode, or it failed.
2515 if (empty($db_connection))
2516 $db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, array('persist' => $db_persist, 'dont_select_db' => SMF == 'SSI'));
2517
2518 // Safe guard here, if there isn't a valid connection lets put a stop to it.
2519 if (!$db_connection)
2520 db_fatal_error();
2521
2522 // If in SSI mode fix up the prefix.
2523 if (SMF == 'SSI')
2524 db_fix_prefix($db_prefix, $db_name);
2525 }
2526
2527 // Try to retrieve a cache entry. On failure, call the appropriate function.
2528 function cache_quick_get($key, $file, $function, $params, $level = 1)
2529 {
2530 global $modSettings, $sourcedir;
2531
2532 // Refresh the cache if either:
2533 // 1. Caching is disabled.
2534 // 2. The cache level isn't high enough.
2535 // 3. The item has not been cached or the cached item expired.
2536 // 4. The cached item has a custom expiration condition evaluating to true.
2537 // 5. The expire time set in the cache item has passed (needed for Zend).
2538 if (empty($modSettings['cache_enable']) || $modSettings['cache_enable'] < $level || !is_array($cache_block = cache_get_data($key, 3600)) || (!empty($cache_block['refresh_eval']) && eval($cache_block['refresh_eval'])) || (!empty($cache_block['expires']) && $cache_block['expires'] < time()))
2539 {
2540 require_once($sourcedir . '/' . $file);
2541 $cache_block = call_user_func_array($function, $params);
2542
2543 if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= $level)
2544 cache_put_data($key, $cache_block, $cache_block['expires'] - time());
2545 }
2546
2547 // Some cached data may need a freshening up after retrieval.
2548 if (!empty($cache_block['post_retri_eval']))
2549 eval($cache_block['post_retri_eval']);
2550
2551 return $cache_block['data'];
2552 }
2553
2554 function cache_put_data($key, $value, $ttl = 120)
2555 {
2556 global $boardurl, $sourcedir, $modSettings, $memcached;
2557 global $cache_hits, $cache_count, $db_show_debug, $cachedir;
2558
2559 if (empty($modSettings['cache_enable']) && !empty($modSettings))
2560 return;
2561
2562 $cache_count = isset($cache_count) ? $cache_count + 1 : 1;
2563 if (isset($db_show_debug) && $db_show_debug === true)
2564 {
2565 $cache_hits[$cache_count] = array('k' => $key, 'd' => 'put', 's' => $value === null ? 0 : strlen(serialize($value)));
2566 $st = microtime();
2567 }
2568
2569 $key = md5($boardurl . filemtime($sourcedir . '/Load.php')) . '-SMF-' . strtr($key, ':', '-');
2570 $value = $value === null ? null : serialize($value);
2571
2572 // The simple yet efficient memcached.
2573 if (function_exists('memcache_set') && isset($modSettings['cache_memcached']) && trim($modSettings['cache_memcached']) != '')
2574 {
2575 // Not connected yet?
2576 if (empty($memcached))
2577 get_memcached_server();
2578 if (!$memcached)
2579 return;
2580
2581 memcache_set($memcached, $key, $value, 0, $ttl);
2582 }
2583 // eAccelerator...
2584 elseif (function_exists('eaccelerator_put'))
2585 {
2586 if (mt_rand(0, 10) == 1)
2587 eaccelerator_gc();
2588
2589 if ($value === null)
2590 @eaccelerator_rm($key);
2591 else
2592 eaccelerator_put($key, $value, $ttl);
2593 }
2594 // Turck MMCache?
2595 elseif (function_exists('mmcache_put'))
2596 {
2597 if (mt_rand(0, 10) == 1)
2598 mmcache_gc();
2599
2600 if ($value === null)
2601 @mmcache_rm($key);
2602 else
2603 mmcache_put($key, $value, $ttl);
2604 }
2605 // Alternative PHP Cache, ahoy!
2606 elseif (function_exists('apc_store'))
2607 {
2608 // An extended key is needed to counteract a bug in APC.
2609 if ($value === null)
2610 apc_delete($key . 'smf');
2611 else
2612 apc_store($key . 'smf', $value, $ttl);
2613 }
2614 // Zend Platform/ZPS/etc.
2615 elseif (function_exists('output_cache_put'))
2616 output_cache_put($key, $value);
2617 elseif (function_exists('xcache_set') && ini_get('xcache.var_size') > 0)
2618 {
2619 if ($value === null)
2620 xcache_unset($key);
2621 else
2622 xcache_set($key, $value, $ttl);
2623 }
2624 // Otherwise custom cache?
2625 else
2626 {
2627 if ($value === null)
2628 @unlink($cachedir . '/data_' . $key . '.php');
2629 else
2630 {
2631 $cache_data = '<' . '?' . 'php if (!defined(\'SMF\')) die; if (' . (time() + $ttl) . ' < time()) $expired = true; else{$expired = false; $value = \'' . addcslashes($value, '\\\'') . '\';}' . '?' . '>';
2632 $fh = @fopen($cachedir . '/data_' . $key . '.php', 'w');
2633 if ($fh)
2634 {
2635 // Write the file.
2636 set_file_buffer($fh, 0);
2637 flock($fh, LOCK_EX);
2638 $cache_bytes = fwrite($fh, $cache_data);
2639 flock($fh, LOCK_UN);
2640 fclose($fh);
2641
2642 // Check that the cache write was successful; all the data should be written
2643 // If it fails due to low diskspace, remove the cache file
2644 if ($cache_bytes != strlen($cache_data))
2645 @unlink($cachedir . '/data_' . $key . '.php');
2646 }
2647 }
2648 }
2649
2650 if (isset($db_show_debug) && $db_show_debug === true)
2651 $cache_hits[$cache_count]['t'] = array_sum(explode(' ', microtime())) - array_sum(explode(' ', $st));
2652 }
2653
2654 function cache_get_data($key, $ttl = 120)
2655 {
2656 global $boardurl, $sourcedir, $modSettings, $memcached;
2657 global $cache_hits, $cache_count, $db_show_debug, $cachedir;
2658
2659 if (empty($modSettings['cache_enable']) && !empty($modSettings))
2660 return;
2661
2662 $cache_count = isset($cache_count) ? $cache_count + 1 : 1;
2663 if (isset($db_show_debug) && $db_show_debug === true)
2664 {
2665 $cache_hits[$cache_count] = array('k' => $key, 'd' => 'get');
2666 $st = microtime();
2667 }
2668
2669 $key = md5($boardurl . filemtime($sourcedir . '/Load.php')) . '-SMF-' . strtr($key, ':', '-');
2670
2671 // Okay, let's go for it memcached!
2672 if (function_exists('memcache_get') && isset($modSettings['cache_memcached']) && trim($modSettings['cache_memcached']) != '')
2673 {
2674 // Not connected yet?
2675 if (empty($memcached))
2676 get_memcached_server();
2677 if (!$memcached)
2678 return;
2679
2680 $value = memcache_get($memcached, $key);
2681 }
2682 // Again, eAccelerator.
2683 elseif (function_exists('eaccelerator_get'))
2684 $value = eaccelerator_get($key);
2685 // The older, but ever-stable, Turck MMCache...
2686 elseif (function_exists('mmcache_get'))
2687 $value = mmcache_get($key);
2688 // This is the free APC from PECL.
2689 elseif (function_exists('apc_fetch'))
2690 $value = apc_fetch($key . 'smf');
2691 // Zend's pricey stuff.
2692 elseif (function_exists('output_cache_get'))
2693 $value = output_cache_get($key, $ttl);
2694 elseif (function_exists('xcache_get') && ini_get('xcache.var_size') > 0)
2695 $value = xcache_get($key);
2696 // Otherwise it's SMF data!
2697 elseif (file_exists($cachedir . '/data_' . $key . '.php') && filesize($cachedir . '/data_' . $key . '.php') > 10)
2698 {
2699 require($cachedir . '/data_' . $key . '.php');
2700 if (!empty($expired) && isset($value))
2701 {
2702 @unlink($cachedir . '/data_' . $key . '.php');
2703 unset($value);
2704 }
2705 }
2706
2707 if (isset($db_show_debug) && $db_show_debug === true)
2708 {
2709 $cache_hits[$cache_count]['t'] = array_sum(explode(' ', microtime())) - array_sum(explode(' ', $st));
2710 $cache_hits[$cache_count]['s'] = isset($value) ? strlen($value) : 0;
2711 }
2712
2713 if (empty($value))
2714 return null;
2715 // If it's broke, it's broke... so give up on it.
2716 else
2717 return @unserialize($value);
2718 }
2719
2720 function get_memcached_server($level = 3)
2721 {
2722 global $modSettings, $memcached, $db_persist;
2723
2724 $servers = explode(',', $modSettings['cache_memcached']);
2725 $server = explode(':', trim($servers[array_rand($servers)]));
2726
2727 // Don't try more times than we have servers!
2728 $level = min(count($servers), $level);
2729
2730 // Don't wait too long: yes, we want the server, but we might be able to run the query faster!
2731 if (empty($db_persist))
2732 $memcached = memcache_connect($server[0], empty($server[1]) ? 11211 : $server[1]);
2733 else
2734 $memcached = memcache_pconnect($server[0], empty($server[1]) ? 11211 : $server[1]);
2735
2736 if (!$memcached && $level > 0)
2737 get_memcached_server($level - 1);
2738 }
2739
2740 ?>