source: branches/posledni/forum/includes/functions.php

Last change on this file was 702, checked in by george, 15 years ago
  • Upraveno: Aktualizace fóra.
File size: 125.5 KB
Line 
1<?php
2/**
3*
4* @package phpBB3
5* @version $Id$
6* @copyright (c) 2005 phpBB Group
7* @license http://opensource.org/licenses/gpl-license.php GNU Public License
8*
9*/
10
11/**
12* @ignore
13*/
14if (!defined('IN_PHPBB'))
15{
16 exit;
17}
18
19// Common global functions
20
21/**
22* set_var
23*
24* Set variable, used by {@link request_var the request_var function}
25*
26* @access private
27*/
28function set_var(&$result, $var, $type, $multibyte = false)
29{
30 settype($var, $type);
31 $result = $var;
32
33 if ($type == 'string')
34 {
35 $result = trim(htmlspecialchars(str_replace(array("\r\n", "\r", "\0"), array("\n", "\n", ''), $result), ENT_COMPAT, 'UTF-8'));
36
37 if (!empty($result))
38 {
39 // Make sure multibyte characters are wellformed
40 if ($multibyte)
41 {
42 if (!preg_match('/^./u', $result))
43 {
44 $result = '';
45 }
46 }
47 else
48 {
49 // no multibyte, allow only ASCII (0-127)
50 $result = preg_replace('/[\x80-\xFF]/', '?', $result);
51 }
52 }
53
54 $result = (STRIP) ? stripslashes($result) : $result;
55 }
56}
57
58/**
59* request_var
60*
61* Used to get passed variable
62*/
63function request_var($var_name, $default, $multibyte = false, $cookie = false)
64{
65 if (!$cookie && isset($_COOKIE[$var_name]))
66 {
67 if (!isset($_GET[$var_name]) && !isset($_POST[$var_name]))
68 {
69 return (is_array($default)) ? array() : $default;
70 }
71 $_REQUEST[$var_name] = isset($_POST[$var_name]) ? $_POST[$var_name] : $_GET[$var_name];
72 }
73
74 $super_global = ($cookie) ? '_COOKIE' : '_REQUEST';
75 if (!isset($GLOBALS[$super_global][$var_name]) || is_array($GLOBALS[$super_global][$var_name]) != is_array($default))
76 {
77 return (is_array($default)) ? array() : $default;
78 }
79
80 $var = $GLOBALS[$super_global][$var_name];
81 if (!is_array($default))
82 {
83 $type = gettype($default);
84 }
85 else
86 {
87 list($key_type, $type) = each($default);
88 $type = gettype($type);
89 $key_type = gettype($key_type);
90 if ($type == 'array')
91 {
92 reset($default);
93 $default = current($default);
94 list($sub_key_type, $sub_type) = each($default);
95 $sub_type = gettype($sub_type);
96 $sub_type = ($sub_type == 'array') ? 'NULL' : $sub_type;
97 $sub_key_type = gettype($sub_key_type);
98 }
99 }
100
101 if (is_array($var))
102 {
103 $_var = $var;
104 $var = array();
105
106 foreach ($_var as $k => $v)
107 {
108 set_var($k, $k, $key_type);
109 if ($type == 'array' && is_array($v))
110 {
111 foreach ($v as $_k => $_v)
112 {
113 if (is_array($_v))
114 {
115 $_v = null;
116 }
117 set_var($_k, $_k, $sub_key_type);
118 set_var($var[$k][$_k], $_v, $sub_type, $multibyte);
119 }
120 }
121 else
122 {
123 if ($type == 'array' || is_array($v))
124 {
125 $v = null;
126 }
127 set_var($var[$k], $v, $type, $multibyte);
128 }
129 }
130 }
131 else
132 {
133 set_var($var, $var, $type, $multibyte);
134 }
135
136 return $var;
137}
138
139/**
140* Set config value. Creates missing config entry.
141*/
142function set_config($config_name, $config_value, $is_dynamic = false)
143{
144 global $db, $cache, $config;
145
146 $sql = 'UPDATE ' . CONFIG_TABLE . "
147 SET config_value = '" . $db->sql_escape($config_value) . "'
148 WHERE config_name = '" . $db->sql_escape($config_name) . "'";
149 $db->sql_query($sql);
150
151 if (!$db->sql_affectedrows() && !isset($config[$config_name]))
152 {
153 $sql = 'INSERT INTO ' . CONFIG_TABLE . ' ' . $db->sql_build_array('INSERT', array(
154 'config_name' => $config_name,
155 'config_value' => $config_value,
156 'is_dynamic' => ($is_dynamic) ? 1 : 0));
157 $db->sql_query($sql);
158 }
159
160 $config[$config_name] = $config_value;
161
162 if (!$is_dynamic)
163 {
164 $cache->destroy('config');
165 }
166}
167
168/**
169* Set dynamic config value with arithmetic operation.
170*/
171function set_config_count($config_name, $increment, $is_dynamic = false)
172{
173 global $db, $cache;
174
175 switch ($db->sql_layer)
176 {
177 case 'firebird':
178 $sql_update = 'CAST(CAST(config_value as integer) + ' . (int) $increment . ' as VARCHAR(255))';
179 break;
180
181 case 'postgres':
182 $sql_update = 'int4(config_value) + ' . (int) $increment;
183 break;
184
185 // MySQL, SQlite, mssql, mssql_odbc, oracle
186 default:
187 $sql_update = 'config_value + ' . (int) $increment;
188 break;
189 }
190
191 $db->sql_query('UPDATE ' . CONFIG_TABLE . ' SET config_value = ' . $sql_update . " WHERE config_name = '" . $db->sql_escape($config_name) . "'");
192
193 if (!$is_dynamic)
194 {
195 $cache->destroy('config');
196 }
197}
198
199/**
200* Generates an alphanumeric random string of given length
201*/
202function gen_rand_string($num_chars = 8)
203{
204 $rand_str = unique_id();
205 $rand_str = str_replace('0', 'Z', strtoupper(base_convert($rand_str, 16, 35)));
206
207 return substr($rand_str, 0, $num_chars);
208}
209
210/**
211* Return unique id
212* @param string $extra additional entropy
213*/
214function unique_id($extra = 'c')
215{
216 static $dss_seeded = false;
217 global $config;
218
219 $val = $config['rand_seed'] . microtime();
220 $val = md5($val);
221 $config['rand_seed'] = md5($config['rand_seed'] . $val . $extra);
222
223 if ($dss_seeded !== true && ($config['rand_seed_last_update'] < time() - rand(1,10)))
224 {
225 set_config('rand_seed', $config['rand_seed'], true);
226 set_config('rand_seed_last_update', time(), true);
227 $dss_seeded = true;
228 }
229
230 return substr($val, 4, 16);
231}
232
233/**
234* Return formatted string for filesizes
235*
236* @param int $value filesize in bytes
237* @param bool $string_only true if language string should be returned
238* @param array $allowed_units only allow these units (data array indexes)
239*
240* @return mixed data array if $string_only is false
241* @author bantu
242*/
243function get_formatted_filesize($value, $string_only = true, $allowed_units = false)
244{
245 global $user;
246
247 $available_units = array(
248 'gb' => array(
249 'min' => 1073741824, // pow(2, 30)
250 'index' => 3,
251 'si_unit' => 'GB',
252 'iec_unit' => 'GIB',
253 ),
254 'mb' => array(
255 'min' => 1048576, // pow(2, 20)
256 'index' => 2,
257 'si_unit' => 'MB',
258 'iec_unit' => 'MIB',
259 ),
260 'kb' => array(
261 'min' => 1024, // pow(2, 10)
262 'index' => 1,
263 'si_unit' => 'KB',
264 'iec_unit' => 'KIB',
265 ),
266 'b' => array(
267 'min' => 0,
268 'index' => 0,
269 'si_unit' => 'BYTES', // Language index
270 'iec_unit' => 'BYTES', // Language index
271 ),
272 );
273
274 foreach ($available_units as $si_identifier => $unit_info)
275 {
276 if (!empty($allowed_units) && $si_identifier != 'b' && !in_array($si_identifier, $allowed_units))
277 {
278 continue;
279 }
280
281 if ($value >= $unit_info['min'])
282 {
283 $unit_info['si_identifier'] = $si_identifier;
284
285 break;
286 }
287 }
288 unset($available_units);
289
290 for ($i = 0; $i < $unit_info['index']; $i++)
291 {
292 $value /= 1024;
293 }
294 $value = round($value, 2);
295
296 // Lookup units in language dictionary
297 $unit_info['si_unit'] = (isset($user->lang[$unit_info['si_unit']])) ? $user->lang[$unit_info['si_unit']] : $unit_info['si_unit'];
298 $unit_info['iec_unit'] = (isset($user->lang[$unit_info['iec_unit']])) ? $user->lang[$unit_info['iec_unit']] : $unit_info['iec_unit'];
299
300 // Default to IEC
301 $unit_info['unit'] = $unit_info['iec_unit'];
302
303 if (!$string_only)
304 {
305 $unit_info['value'] = $value;
306
307 return $unit_info;
308 }
309
310 return $value . ' ' . $unit_info['unit'];
311}
312
313/**
314* Determine whether we are approaching the maximum execution time. Should be called once
315* at the beginning of the script in which it's used.
316* @return bool Either true if the maximum execution time is nearly reached, or false
317* if some time is still left.
318*/
319function still_on_time($extra_time = 15)
320{
321 static $max_execution_time, $start_time;
322
323 $time = explode(' ', microtime());
324 $current_time = $time[0] + $time[1];
325
326 if (empty($max_execution_time))
327 {
328 $max_execution_time = (function_exists('ini_get')) ? (int) @ini_get('max_execution_time') : (int) @get_cfg_var('max_execution_time');
329
330 // If zero, then set to something higher to not let the user catch the ten seconds barrier.
331 if ($max_execution_time === 0)
332 {
333 $max_execution_time = 50 + $extra_time;
334 }
335
336 $max_execution_time = min(max(10, ($max_execution_time - $extra_time)), 50);
337
338 // For debugging purposes
339 // $max_execution_time = 10;
340
341 global $starttime;
342 $start_time = (empty($starttime)) ? $current_time : $starttime;
343 }
344
345 return (ceil($current_time - $start_time) < $max_execution_time) ? true : false;
346}
347
348/**
349*
350* @version Version 0.1 / slightly modified for phpBB 3.0.x (using $H$ as hash type identifier)
351*
352* Portable PHP password hashing framework.
353*
354* Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
355* the public domain.
356*
357* There's absolutely no warranty.
358*
359* The homepage URL for this framework is:
360*
361* http://www.openwall.com/phpass/
362*
363* Please be sure to update the Version line if you edit this file in any way.
364* It is suggested that you leave the main version number intact, but indicate
365* your project name (after the slash) and add your own revision information.
366*
367* Please do not change the "private" password hashing method implemented in
368* here, thereby making your hashes incompatible. However, if you must, please
369* change the hash type identifier (the "$P$") to something different.
370*
371* Obviously, since this code is in the public domain, the above are not
372* requirements (there can be none), but merely suggestions.
373*
374*
375* Hash the password
376*/
377function phpbb_hash($password)
378{
379 $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
380
381 $random_state = unique_id();
382 $random = '';
383 $count = 6;
384
385 if (($fh = @fopen('/dev/urandom', 'rb')))
386 {
387 $random = fread($fh, $count);
388 fclose($fh);
389 }
390
391 if (strlen($random) < $count)
392 {
393 $random = '';
394
395 for ($i = 0; $i < $count; $i += 16)
396 {
397 $random_state = md5(unique_id() . $random_state);
398 $random .= pack('H*', md5($random_state));
399 }
400 $random = substr($random, 0, $count);
401 }
402
403 $hash = _hash_crypt_private($password, _hash_gensalt_private($random, $itoa64), $itoa64);
404
405 if (strlen($hash) == 34)
406 {
407 return $hash;
408 }
409
410 return md5($password);
411}
412
413/**
414* Check for correct password
415*
416* @param string $password The password in plain text
417* @param string $hash The stored password hash
418*
419* @return bool Returns true if the password is correct, false if not.
420*/
421function phpbb_check_hash($password, $hash)
422{
423 $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
424 if (strlen($hash) == 34)
425 {
426 return (_hash_crypt_private($password, $hash, $itoa64) === $hash) ? true : false;
427 }
428
429 return (md5($password) === $hash) ? true : false;
430}
431
432/**
433* Generate salt for hash generation
434*/
435function _hash_gensalt_private($input, &$itoa64, $iteration_count_log2 = 6)
436{
437 if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
438 {
439 $iteration_count_log2 = 8;
440 }
441
442 $output = '$H$';
443 $output .= $itoa64[min($iteration_count_log2 + ((PHP_VERSION >= 5) ? 5 : 3), 30)];
444 $output .= _hash_encode64($input, 6, $itoa64);
445
446 return $output;
447}
448
449/**
450* Encode hash
451*/
452function _hash_encode64($input, $count, &$itoa64)
453{
454 $output = '';
455 $i = 0;
456
457 do
458 {
459 $value = ord($input[$i++]);
460 $output .= $itoa64[$value & 0x3f];
461
462 if ($i < $count)
463 {
464 $value |= ord($input[$i]) << 8;
465 }
466
467 $output .= $itoa64[($value >> 6) & 0x3f];
468
469 if ($i++ >= $count)
470 {
471 break;
472 }
473
474 if ($i < $count)
475 {
476 $value |= ord($input[$i]) << 16;
477 }
478
479 $output .= $itoa64[($value >> 12) & 0x3f];
480
481 if ($i++ >= $count)
482 {
483 break;
484 }
485
486 $output .= $itoa64[($value >> 18) & 0x3f];
487 }
488 while ($i < $count);
489
490 return $output;
491}
492
493/**
494* The crypt function/replacement
495*/
496function _hash_crypt_private($password, $setting, &$itoa64)
497{
498 $output = '*';
499
500 // Check for correct hash
501 if (substr($setting, 0, 3) != '$H$')
502 {
503 return $output;
504 }
505
506 $count_log2 = strpos($itoa64, $setting[3]);
507
508 if ($count_log2 < 7 || $count_log2 > 30)
509 {
510 return $output;
511 }
512
513 $count = 1 << $count_log2;
514 $salt = substr($setting, 4, 8);
515
516 if (strlen($salt) != 8)
517 {
518 return $output;
519 }
520
521 /**
522 * We're kind of forced to use MD5 here since it's the only
523 * cryptographic primitive available in all versions of PHP
524 * currently in use. To implement our own low-level crypto
525 * in PHP would result in much worse performance and
526 * consequently in lower iteration counts and hashes that are
527 * quicker to crack (by non-PHP code).
528 */
529 if (PHP_VERSION >= 5)
530 {
531 $hash = md5($salt . $password, true);
532 do
533 {
534 $hash = md5($hash . $password, true);
535 }
536 while (--$count);
537 }
538 else
539 {
540 $hash = pack('H*', md5($salt . $password));
541 do
542 {
543 $hash = pack('H*', md5($hash . $password));
544 }
545 while (--$count);
546 }
547
548 $output = substr($setting, 0, 12);
549 $output .= _hash_encode64($hash, 16, $itoa64);
550
551 return $output;
552}
553
554/**
555* Hashes an email address to a big integer
556*
557* @param string $email Email address
558*
559* @return string Unsigned Big Integer
560*/
561function phpbb_email_hash($email)
562{
563 return sprintf('%u', crc32(strtolower($email))) . strlen($email);
564}
565
566/**
567* Global function for chmodding directories and files for internal use
568*
569* This function determines owner and group whom the file belongs to and user and group of PHP and then set safest possible file permissions.
570* The function determines owner and group from common.php file and sets the same to the provided file.
571* The function uses bit fields to build the permissions.
572* The function sets the appropiate execute bit on directories.
573*
574* Supported constants representing bit fields are:
575*
576* CHMOD_ALL - all permissions (7)
577* CHMOD_READ - read permission (4)
578* CHMOD_WRITE - write permission (2)
579* CHMOD_EXECUTE - execute permission (1)
580*
581* NOTE: The function uses POSIX extension and fileowner()/filegroup() functions. If any of them is disabled, this function tries to build proper permissions, by calling is_readable() and is_writable() functions.
582*
583* @param string $filename The file/directory to be chmodded
584* @param int $perms Permissions to set
585*
586* @return bool true on success, otherwise false
587* @author faw, phpBB Group
588*/
589function phpbb_chmod($filename, $perms = CHMOD_READ)
590{
591 static $_chmod_info;
592
593 // Return if the file no longer exists.
594 if (!file_exists($filename))
595 {
596 return false;
597 }
598
599 // Determine some common vars
600 if (empty($_chmod_info))
601 {
602 if (!function_exists('fileowner') || !function_exists('filegroup'))
603 {
604 // No need to further determine owner/group - it is unknown
605 $_chmod_info['process'] = false;
606 }
607 else
608 {
609 global $phpbb_root_path, $phpEx;
610
611 // Determine owner/group of common.php file and the filename we want to change here
612 $common_php_owner = @fileowner($phpbb_root_path . 'common.' . $phpEx);
613 $common_php_group = @filegroup($phpbb_root_path . 'common.' . $phpEx);
614
615 // And the owner and the groups PHP is running under.
616 $php_uid = (function_exists('posix_getuid')) ? @posix_getuid() : false;
617 $php_gids = (function_exists('posix_getgroups')) ? @posix_getgroups() : false;
618
619 // If we are unable to get owner/group, then do not try to set them by guessing
620 if (!$php_uid || empty($php_gids) || !$common_php_owner || !$common_php_group)
621 {
622 $_chmod_info['process'] = false;
623 }
624 else
625 {
626 $_chmod_info = array(
627 'process' => true,
628 'common_owner' => $common_php_owner,
629 'common_group' => $common_php_group,
630 'php_uid' => $php_uid,
631 'php_gids' => $php_gids,
632 );
633 }
634 }
635 }
636
637 if ($_chmod_info['process'])
638 {
639 $file_uid = @fileowner($filename);
640 $file_gid = @filegroup($filename);
641
642 // Change owner
643 if (@chown($filename, $_chmod_info['common_owner']))
644 {
645 clearstatcache();
646 $file_uid = @fileowner($filename);
647 }
648
649 // Change group
650 if (@chgrp($filename, $_chmod_info['common_group']))
651 {
652 clearstatcache();
653 $file_gid = @filegroup($filename);
654 }
655
656 // If the file_uid/gid now match the one from common.php we can process further, else we are not able to change something
657 if ($file_uid != $_chmod_info['common_owner'] || $file_gid != $_chmod_info['common_group'])
658 {
659 $_chmod_info['process'] = false;
660 }
661 }
662
663 // Still able to process?
664 if ($_chmod_info['process'])
665 {
666 if ($file_uid == $_chmod_info['php_uid'])
667 {
668 $php = 'owner';
669 }
670 else if (in_array($file_gid, $_chmod_info['php_gids']))
671 {
672 $php = 'group';
673 }
674 else
675 {
676 // Since we are setting the everyone bit anyway, no need to do expensive operations
677 $_chmod_info['process'] = false;
678 }
679 }
680
681 // We are not able to determine or change something
682 if (!$_chmod_info['process'])
683 {
684 $php = 'other';
685 }
686
687 // Owner always has read/write permission
688 $owner = CHMOD_READ | CHMOD_WRITE;
689 if (is_dir($filename))
690 {
691 $owner |= CHMOD_EXECUTE;
692
693 // Only add execute bit to the permission if the dir needs to be readable
694 if ($perms & CHMOD_READ)
695 {
696 $perms |= CHMOD_EXECUTE;
697 }
698 }
699
700 switch ($php)
701 {
702 case 'owner':
703 $result = @chmod($filename, ($owner << 6) + (0 << 3) + (0 << 0));
704
705 clearstatcache();
706
707 if (is_readable($filename) && is_writable($filename))
708 {
709 break;
710 }
711
712 case 'group':
713 $result = @chmod($filename, ($owner << 6) + ($perms << 3) + (0 << 0));
714
715 clearstatcache();
716
717 if ((!($perms & CHMOD_READ) || is_readable($filename)) && (!($perms & CHMOD_WRITE) || is_writable($filename)))
718 {
719 break;
720 }
721
722 case 'other':
723 $result = @chmod($filename, ($owner << 6) + ($perms << 3) + ($perms << 0));
724
725 clearstatcache();
726
727 if ((!($perms & CHMOD_READ) || is_readable($filename)) && (!($perms & CHMOD_WRITE) || is_writable($filename)))
728 {
729 break;
730 }
731
732 default:
733 return false;
734 break;
735 }
736
737 return $result;
738}
739
740/**
741* Test if a file/directory is writable
742*
743* This function calls the native is_writable() when not running under
744* Windows and it is not disabled.
745*
746* @param string $file Path to perform write test on
747* @return bool True when the path is writable, otherwise false.
748*/
749function phpbb_is_writable($file)
750{
751 if (strtolower(substr(PHP_OS, 0, 3)) === 'win' || !function_exists('is_writable'))
752 {
753 if (file_exists($file))
754 {
755 // Canonicalise path to absolute path
756 $file = phpbb_realpath($file);
757
758 if (is_dir($file))
759 {
760 // Test directory by creating a file inside the directory
761 $result = @tempnam($file, 'i_w');
762
763 if (is_string($result) && file_exists($result))
764 {
765 unlink($result);
766
767 // Ensure the file is actually in the directory (returned realpathed)
768 return (strpos($result, $file) === 0) ? true : false;
769 }
770 }
771 else
772 {
773 $handle = @fopen($file, 'r+');
774
775 if (is_resource($handle))
776 {
777 fclose($handle);
778 return true;
779 }
780 }
781 }
782 else
783 {
784 // file does not exist test if we can write to the directory
785 $dir = dirname($file);
786
787 if (file_exists($dir) && is_dir($dir) && phpbb_is_writable($dir))
788 {
789 return true;
790 }
791 }
792
793 return false;
794 }
795 else
796 {
797 return is_writable($file);
798 }
799}
800
801// Compatibility functions
802
803if (!function_exists('array_combine'))
804{
805 /**
806 * A wrapper for the PHP5 function array_combine()
807 * @param array $keys contains keys for the resulting array
808 * @param array $values contains values for the resulting array
809 *
810 * @return Returns an array by using the values from the keys array as keys and the
811 * values from the values array as the corresponding values. Returns false if the
812 * number of elements for each array isn't equal or if the arrays are empty.
813 */
814 function array_combine($keys, $values)
815 {
816 $keys = array_values($keys);
817 $values = array_values($values);
818
819 $n = sizeof($keys);
820 $m = sizeof($values);
821 if (!$n || !$m || ($n != $m))
822 {
823 return false;
824 }
825
826 $combined = array();
827 for ($i = 0; $i < $n; $i++)
828 {
829 $combined[$keys[$i]] = $values[$i];
830 }
831 return $combined;
832 }
833}
834
835if (!function_exists('str_split'))
836{
837 /**
838 * A wrapper for the PHP5 function str_split()
839 * @param array $string contains the string to be converted
840 * @param array $split_length contains the length of each chunk
841 *
842 * @return Converts a string to an array. If the optional split_length parameter is specified,
843 * the returned array will be broken down into chunks with each being split_length in length,
844 * otherwise each chunk will be one character in length. FALSE is returned if split_length is
845 * less than 1. If the split_length length exceeds the length of string, the entire string is
846 * returned as the first (and only) array element.
847 */
848 function str_split($string, $split_length = 1)
849 {
850 if ($split_length < 1)
851 {
852 return false;
853 }
854 else if ($split_length >= strlen($string))
855 {
856 return array($string);
857 }
858 else
859 {
860 preg_match_all('#.{1,' . $split_length . '}#s', $string, $matches);
861 return $matches[0];
862 }
863 }
864}
865
866if (!function_exists('stripos'))
867{
868 /**
869 * A wrapper for the PHP5 function stripos
870 * Find position of first occurrence of a case-insensitive string
871 *
872 * @param string $haystack is the string to search in
873 * @param string $needle is the string to search for
874 *
875 * @return mixed Returns the numeric position of the first occurrence of needle in the haystack string. Unlike strpos(), stripos() is case-insensitive.
876 * Note that the needle may be a string of one or more characters.
877 * If needle is not found, stripos() will return boolean FALSE.
878 */
879 function stripos($haystack, $needle)
880 {
881 if (preg_match('#' . preg_quote($needle, '#') . '#i', $haystack, $m))
882 {
883 return strpos($haystack, $m[0]);
884 }
885
886 return false;
887 }
888}
889
890/**
891* Checks if a path ($path) is absolute or relative
892*
893* @param string $path Path to check absoluteness of
894* @return boolean
895*/
896function is_absolute($path)
897{
898 return ($path[0] == '/' || (DIRECTORY_SEPARATOR == '\\' && preg_match('#^[a-z]:[/\\\]#i', $path))) ? true : false;
899}
900
901/**
902* @author Chris Smith <chris@project-minerva.org>
903* @copyright 2006 Project Minerva Team
904* @param string $path The path which we should attempt to resolve.
905* @return mixed
906*/
907function phpbb_own_realpath($path)
908{
909 // Now to perform funky shizzle
910
911 // Switch to use UNIX slashes
912 $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
913 $path_prefix = '';
914
915 // Determine what sort of path we have
916 if (is_absolute($path))
917 {
918 $absolute = true;
919
920 if ($path[0] == '/')
921 {
922 // Absolute path, *NIX style
923 $path_prefix = '';
924 }
925 else
926 {
927 // Absolute path, Windows style
928 // Remove the drive letter and colon
929 $path_prefix = $path[0] . ':';
930 $path = substr($path, 2);
931 }
932 }
933 else
934 {
935 // Relative Path
936 // Prepend the current working directory
937 if (function_exists('getcwd'))
938 {
939 // This is the best method, hopefully it is enabled!
940 $path = str_replace(DIRECTORY_SEPARATOR, '/', getcwd()) . '/' . $path;
941 $absolute = true;
942 if (preg_match('#^[a-z]:#i', $path))
943 {
944 $path_prefix = $path[0] . ':';
945 $path = substr($path, 2);
946 }
947 else
948 {
949 $path_prefix = '';
950 }
951 }
952 else if (isset($_SERVER['SCRIPT_FILENAME']) && !empty($_SERVER['SCRIPT_FILENAME']))
953 {
954 // Warning: If chdir() has been used this will lie!
955 // Warning: This has some problems sometime (CLI can create them easily)
956 $path = str_replace(DIRECTORY_SEPARATOR, '/', dirname($_SERVER['SCRIPT_FILENAME'])) . '/' . $path;
957 $absolute = true;
958 $path_prefix = '';
959 }
960 else
961 {
962 // We have no way of getting the absolute path, just run on using relative ones.
963 $absolute = false;
964 $path_prefix = '.';
965 }
966 }
967
968 // Remove any repeated slashes
969 $path = preg_replace('#/{2,}#', '/', $path);
970
971 // Remove the slashes from the start and end of the path
972 $path = trim($path, '/');
973
974 // Break the string into little bits for us to nibble on
975 $bits = explode('/', $path);
976
977 // Remove any . in the path, renumber array for the loop below
978 $bits = array_values(array_diff($bits, array('.')));
979
980 // Lets get looping, run over and resolve any .. (up directory)
981 for ($i = 0, $max = sizeof($bits); $i < $max; $i++)
982 {
983 // @todo Optimise
984 if ($bits[$i] == '..' )
985 {
986 if (isset($bits[$i - 1]))
987 {
988 if ($bits[$i - 1] != '..')
989 {
990 // We found a .. and we are able to traverse upwards, lets do it!
991 unset($bits[$i]);
992 unset($bits[$i - 1]);
993 $i -= 2;
994 $max -= 2;
995 $bits = array_values($bits);
996 }
997 }
998 else if ($absolute) // ie. !isset($bits[$i - 1]) && $absolute
999 {
1000 // We have an absolute path trying to descend above the root of the filesystem
1001 // ... Error!
1002 return false;
1003 }
1004 }
1005 }
1006
1007 // Prepend the path prefix
1008 array_unshift($bits, $path_prefix);
1009
1010 $resolved = '';
1011
1012 $max = sizeof($bits) - 1;
1013
1014 // Check if we are able to resolve symlinks, Windows cannot.
1015 $symlink_resolve = (function_exists('readlink')) ? true : false;
1016
1017 foreach ($bits as $i => $bit)
1018 {
1019 if (@is_dir("$resolved/$bit") || ($i == $max && @is_file("$resolved/$bit")))
1020 {
1021 // Path Exists
1022 if ($symlink_resolve && is_link("$resolved/$bit") && ($link = readlink("$resolved/$bit")))
1023 {
1024 // Resolved a symlink.
1025 $resolved = $link . (($i == $max) ? '' : '/');
1026 continue;
1027 }
1028 }
1029 else
1030 {
1031 // Something doesn't exist here!
1032 // This is correct realpath() behaviour but sadly open_basedir and safe_mode make this problematic
1033 // return false;
1034 }
1035 $resolved .= $bit . (($i == $max) ? '' : '/');
1036 }
1037
1038 // @todo If the file exists fine and open_basedir only has one path we should be able to prepend it
1039 // because we must be inside that basedir, the question is where...
1040 // @internal The slash in is_dir() gets around an open_basedir restriction
1041 if (!@file_exists($resolved) || (!is_dir($resolved . '/') && !is_file($resolved)))
1042 {
1043 return false;
1044 }
1045
1046 // Put the slashes back to the native operating systems slashes
1047 $resolved = str_replace('/', DIRECTORY_SEPARATOR, $resolved);
1048
1049 // Check for DIRECTORY_SEPARATOR at the end (and remove it!)
1050 if (substr($resolved, -1) == DIRECTORY_SEPARATOR)
1051 {
1052 return substr($resolved, 0, -1);
1053 }
1054
1055 return $resolved; // We got here, in the end!
1056}
1057
1058if (!function_exists('realpath'))
1059{
1060 /**
1061 * A wrapper for realpath
1062 * @ignore
1063 */
1064 function phpbb_realpath($path)
1065 {
1066 return phpbb_own_realpath($path);
1067 }
1068}
1069else
1070{
1071 /**
1072 * A wrapper for realpath
1073 */
1074 function phpbb_realpath($path)
1075 {
1076 $realpath = realpath($path);
1077
1078 // Strangely there are provider not disabling realpath but returning strange values. :o
1079 // We at least try to cope with them.
1080 if ($realpath === $path || $realpath === false)
1081 {
1082 return phpbb_own_realpath($path);
1083 }
1084
1085 // Check for DIRECTORY_SEPARATOR at the end (and remove it!)
1086 if (substr($realpath, -1) == DIRECTORY_SEPARATOR)
1087 {
1088 $realpath = substr($realpath, 0, -1);
1089 }
1090
1091 return $realpath;
1092 }
1093}
1094
1095if (!function_exists('htmlspecialchars_decode'))
1096{
1097 /**
1098 * A wrapper for htmlspecialchars_decode
1099 * @ignore
1100 */
1101 function htmlspecialchars_decode($string, $quote_style = ENT_COMPAT)
1102 {
1103 return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style)));
1104 }
1105}
1106
1107// functions used for building option fields
1108
1109/**
1110* Pick a language, any language ...
1111*/
1112function language_select($default = '')
1113{
1114 global $db;
1115
1116 $sql = 'SELECT lang_iso, lang_local_name
1117 FROM ' . LANG_TABLE . '
1118 ORDER BY lang_english_name';
1119 $result = $db->sql_query($sql);
1120
1121 $lang_options = '';
1122 while ($row = $db->sql_fetchrow($result))
1123 {
1124 $selected = ($row['lang_iso'] == $default) ? ' selected="selected"' : '';
1125 $lang_options .= '<option value="' . $row['lang_iso'] . '"' . $selected . '>' . $row['lang_local_name'] . '</option>';
1126 }
1127 $db->sql_freeresult($result);
1128
1129 return $lang_options;
1130}
1131
1132/**
1133* Pick a template/theme combo,
1134*/
1135function style_select($default = '', $all = false)
1136{
1137 global $db;
1138
1139 $sql_where = (!$all) ? 'WHERE style_active = 1 ' : '';
1140 $sql = 'SELECT style_id, style_name
1141 FROM ' . STYLES_TABLE . "
1142 $sql_where
1143 ORDER BY style_name";
1144 $result = $db->sql_query($sql);
1145
1146 $style_options = '';
1147 while ($row = $db->sql_fetchrow($result))
1148 {
1149 $selected = ($row['style_id'] == $default) ? ' selected="selected"' : '';
1150 $style_options .= '<option value="' . $row['style_id'] . '"' . $selected . '>' . $row['style_name'] . '</option>';
1151 }
1152 $db->sql_freeresult($result);
1153
1154 return $style_options;
1155}
1156
1157/**
1158* Pick a timezone
1159*/
1160function tz_select($default = '', $truncate = false)
1161{
1162 global $user;
1163
1164 $tz_select = '';
1165 foreach ($user->lang['tz_zones'] as $offset => $zone)
1166 {
1167 if ($truncate)
1168 {
1169 $zone_trunc = truncate_string($zone, 50, 255, false, '...');
1170 }
1171 else
1172 {
1173 $zone_trunc = $zone;
1174 }
1175
1176 if (is_numeric($offset))
1177 {
1178 $selected = ($offset == $default) ? ' selected="selected"' : '';
1179 $tz_select .= '<option title="' . $zone . '" value="' . $offset . '"' . $selected . '>' . $zone_trunc . '</option>';
1180 }
1181 }
1182
1183 return $tz_select;
1184}
1185
1186// Functions handling topic/post tracking/marking
1187
1188/**
1189* Marks a topic/forum as read
1190* Marks a topic as posted to
1191*
1192* @param int $user_id can only be used with $mode == 'post'
1193*/
1194function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $user_id = 0)
1195{
1196 global $db, $user, $config;
1197
1198 if ($mode == 'all')
1199 {
1200 if ($forum_id === false || !sizeof($forum_id))
1201 {
1202 if ($config['load_db_lastread'] && $user->data['is_registered'])
1203 {
1204 // Mark all forums read (index page)
1205 $db->sql_query('DELETE FROM ' . TOPICS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}");
1206 $db->sql_query('DELETE FROM ' . FORUMS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}");
1207 $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}");
1208 }
1209 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1210 {
1211 $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
1212 $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
1213
1214 unset($tracking_topics['tf']);
1215 unset($tracking_topics['t']);
1216 unset($tracking_topics['f']);
1217 $tracking_topics['l'] = base_convert(time() - $config['board_startdate'], 10, 36);
1218
1219 $user->set_cookie('track', tracking_serialize($tracking_topics), time() + 31536000);
1220 $_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking_topics)) : tracking_serialize($tracking_topics);
1221
1222 unset($tracking_topics);
1223
1224 if ($user->data['is_registered'])
1225 {
1226 $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}");
1227 }
1228 }
1229 }
1230
1231 return;
1232 }
1233 else if ($mode == 'topics')
1234 {
1235 // Mark all topics in forums read
1236 if (!is_array($forum_id))
1237 {
1238 $forum_id = array($forum_id);
1239 }
1240
1241 // Add 0 to forums array to mark global announcements correctly
1242 // $forum_id[] = 0;
1243
1244 if ($config['load_db_lastread'] && $user->data['is_registered'])
1245 {
1246 $sql = 'DELETE FROM ' . TOPICS_TRACK_TABLE . "
1247 WHERE user_id = {$user->data['user_id']}
1248 AND " . $db->sql_in_set('forum_id', $forum_id);
1249 $db->sql_query($sql);
1250
1251 $sql = 'SELECT forum_id
1252 FROM ' . FORUMS_TRACK_TABLE . "
1253 WHERE user_id = {$user->data['user_id']}
1254 AND " . $db->sql_in_set('forum_id', $forum_id);
1255 $result = $db->sql_query($sql);
1256
1257 $sql_update = array();
1258 while ($row = $db->sql_fetchrow($result))
1259 {
1260 $sql_update[] = (int) $row['forum_id'];
1261 }
1262 $db->sql_freeresult($result);
1263
1264 if (sizeof($sql_update))
1265 {
1266 $sql = 'UPDATE ' . FORUMS_TRACK_TABLE . '
1267 SET mark_time = ' . time() . "
1268 WHERE user_id = {$user->data['user_id']}
1269 AND " . $db->sql_in_set('forum_id', $sql_update);
1270 $db->sql_query($sql);
1271 }
1272
1273 if ($sql_insert = array_diff($forum_id, $sql_update))
1274 {
1275 $sql_ary = array();
1276 foreach ($sql_insert as $f_id)
1277 {
1278 $sql_ary[] = array(
1279 'user_id' => (int) $user->data['user_id'],
1280 'forum_id' => (int) $f_id,
1281 'mark_time' => time()
1282 );
1283 }
1284
1285 $db->sql_multi_insert(FORUMS_TRACK_TABLE, $sql_ary);
1286 }
1287 }
1288 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1289 {
1290 $tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
1291 $tracking = ($tracking) ? tracking_unserialize($tracking) : array();
1292
1293 foreach ($forum_id as $f_id)
1294 {
1295 $topic_ids36 = (isset($tracking['tf'][$f_id])) ? $tracking['tf'][$f_id] : array();
1296
1297 if (isset($tracking['tf'][$f_id]))
1298 {
1299 unset($tracking['tf'][$f_id]);
1300 }
1301
1302 foreach ($topic_ids36 as $topic_id36)
1303 {
1304 unset($tracking['t'][$topic_id36]);
1305 }
1306
1307 if (isset($tracking['f'][$f_id]))
1308 {
1309 unset($tracking['f'][$f_id]);
1310 }
1311
1312 $tracking['f'][$f_id] = base_convert(time() - $config['board_startdate'], 10, 36);
1313 }
1314
1315 if (isset($tracking['tf']) && empty($tracking['tf']))
1316 {
1317 unset($tracking['tf']);
1318 }
1319
1320 $user->set_cookie('track', tracking_serialize($tracking), time() + 31536000);
1321 $_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking)) : tracking_serialize($tracking);
1322
1323 unset($tracking);
1324 }
1325
1326 return;
1327 }
1328 else if ($mode == 'topic')
1329 {
1330 if ($topic_id === false || $forum_id === false)
1331 {
1332 return;
1333 }
1334
1335 if ($config['load_db_lastread'] && $user->data['is_registered'])
1336 {
1337 $sql = 'UPDATE ' . TOPICS_TRACK_TABLE . '
1338 SET mark_time = ' . (($post_time) ? $post_time : time()) . "
1339 WHERE user_id = {$user->data['user_id']}
1340 AND topic_id = $topic_id";
1341 $db->sql_query($sql);
1342
1343 // insert row
1344 if (!$db->sql_affectedrows())
1345 {
1346 $db->sql_return_on_error(true);
1347
1348 $sql_ary = array(
1349 'user_id' => (int) $user->data['user_id'],
1350 'topic_id' => (int) $topic_id,
1351 'forum_id' => (int) $forum_id,
1352 'mark_time' => ($post_time) ? (int) $post_time : time(),
1353 );
1354
1355 $db->sql_query('INSERT INTO ' . TOPICS_TRACK_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
1356
1357 $db->sql_return_on_error(false);
1358 }
1359 }
1360 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1361 {
1362 $tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
1363 $tracking = ($tracking) ? tracking_unserialize($tracking) : array();
1364
1365 $topic_id36 = base_convert($topic_id, 10, 36);
1366
1367 if (!isset($tracking['t'][$topic_id36]))
1368 {
1369 $tracking['tf'][$forum_id][$topic_id36] = true;
1370 }
1371
1372 $post_time = ($post_time) ? $post_time : time();
1373 $tracking['t'][$topic_id36] = base_convert($post_time - $config['board_startdate'], 10, 36);
1374
1375 // If the cookie grows larger than 10000 characters we will remove the smallest value
1376 // This can result in old topics being unread - but most of the time it should be accurate...
1377 if (isset($_COOKIE[$config['cookie_name'] . '_track']) && strlen($_COOKIE[$config['cookie_name'] . '_track']) > 10000)
1378 {
1379 //echo 'Cookie grown too large' . print_r($tracking, true);
1380
1381 // We get the ten most minimum stored time offsets and its associated topic ids
1382 $time_keys = array();
1383 for ($i = 0; $i < 10 && sizeof($tracking['t']); $i++)
1384 {
1385 $min_value = min($tracking['t']);
1386 $m_tkey = array_search($min_value, $tracking['t']);
1387 unset($tracking['t'][$m_tkey]);
1388
1389 $time_keys[$m_tkey] = $min_value;
1390 }
1391
1392 // Now remove the topic ids from the array...
1393 foreach ($tracking['tf'] as $f_id => $topic_id_ary)
1394 {
1395 foreach ($time_keys as $m_tkey => $min_value)
1396 {
1397 if (isset($topic_id_ary[$m_tkey]))
1398 {
1399 $tracking['f'][$f_id] = $min_value;
1400 unset($tracking['tf'][$f_id][$m_tkey]);
1401 }
1402 }
1403 }
1404
1405 if ($user->data['is_registered'])
1406 {
1407 $user->data['user_lastmark'] = intval(base_convert(max($time_keys) + $config['board_startdate'], 36, 10));
1408 $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . $user->data['user_lastmark'] . " WHERE user_id = {$user->data['user_id']}");
1409 }
1410 else
1411 {
1412 $tracking['l'] = max($time_keys);
1413 }
1414 }
1415
1416 $user->set_cookie('track', tracking_serialize($tracking), time() + 31536000);
1417 $_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking)) : tracking_serialize($tracking);
1418 }
1419
1420 return;
1421 }
1422 else if ($mode == 'post')
1423 {
1424 if ($topic_id === false)
1425 {
1426 return;
1427 }
1428
1429 $use_user_id = (!$user_id) ? $user->data['user_id'] : $user_id;
1430
1431 if ($config['load_db_track'] && $use_user_id != ANONYMOUS)
1432 {
1433 $db->sql_return_on_error(true);
1434
1435 $sql_ary = array(
1436 'user_id' => (int) $use_user_id,
1437 'topic_id' => (int) $topic_id,
1438 'topic_posted' => 1
1439 );
1440
1441 $db->sql_query('INSERT INTO ' . TOPICS_POSTED_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
1442
1443 $db->sql_return_on_error(false);
1444 }
1445
1446 return;
1447 }
1448}
1449
1450/**
1451* Get topic tracking info by using already fetched info
1452*/
1453function get_topic_tracking($forum_id, $topic_ids, &$rowset, $forum_mark_time, $global_announce_list = false)
1454{
1455 global $config, $user;
1456
1457 $last_read = array();
1458
1459 if (!is_array($topic_ids))
1460 {
1461 $topic_ids = array($topic_ids);
1462 }
1463
1464 foreach ($topic_ids as $topic_id)
1465 {
1466 if (!empty($rowset[$topic_id]['mark_time']))
1467 {
1468 $last_read[$topic_id] = $rowset[$topic_id]['mark_time'];
1469 }
1470 }
1471
1472 $topic_ids = array_diff($topic_ids, array_keys($last_read));
1473
1474 if (sizeof($topic_ids))
1475 {
1476 $mark_time = array();
1477
1478 // Get global announcement info
1479 if ($global_announce_list && sizeof($global_announce_list))
1480 {
1481 if (!isset($forum_mark_time[0]))
1482 {
1483 global $db;
1484
1485 $sql = 'SELECT mark_time
1486 FROM ' . FORUMS_TRACK_TABLE . "
1487 WHERE user_id = {$user->data['user_id']}
1488 AND forum_id = 0";
1489 $result = $db->sql_query($sql);
1490 $row = $db->sql_fetchrow($result);
1491 $db->sql_freeresult($result);
1492
1493 if ($row)
1494 {
1495 $mark_time[0] = $row['mark_time'];
1496 }
1497 }
1498 else
1499 {
1500 if ($forum_mark_time[0] !== false)
1501 {
1502 $mark_time[0] = $forum_mark_time[0];
1503 }
1504 }
1505 }
1506
1507 if (!empty($forum_mark_time[$forum_id]) && $forum_mark_time[$forum_id] !== false)
1508 {
1509 $mark_time[$forum_id] = $forum_mark_time[$forum_id];
1510 }
1511
1512 $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark'];
1513
1514 foreach ($topic_ids as $topic_id)
1515 {
1516 if ($global_announce_list && isset($global_announce_list[$topic_id]))
1517 {
1518 $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
1519 }
1520 else
1521 {
1522 $last_read[$topic_id] = $user_lastmark;
1523 }
1524 }
1525 }
1526
1527 return $last_read;
1528}
1529
1530/**
1531* Get topic tracking info from db (for cookie based tracking only this function is used)
1532*/
1533function get_complete_topic_tracking($forum_id, $topic_ids, $global_announce_list = false)
1534{
1535 global $config, $user;
1536
1537 $last_read = array();
1538
1539 if (!is_array($topic_ids))
1540 {
1541 $topic_ids = array($topic_ids);
1542 }
1543
1544 if ($config['load_db_lastread'] && $user->data['is_registered'])
1545 {
1546 global $db;
1547
1548 $sql = 'SELECT topic_id, mark_time
1549 FROM ' . TOPICS_TRACK_TABLE . "
1550 WHERE user_id = {$user->data['user_id']}
1551 AND " . $db->sql_in_set('topic_id', $topic_ids);
1552 $result = $db->sql_query($sql);
1553
1554 while ($row = $db->sql_fetchrow($result))
1555 {
1556 $last_read[$row['topic_id']] = $row['mark_time'];
1557 }
1558 $db->sql_freeresult($result);
1559
1560 $topic_ids = array_diff($topic_ids, array_keys($last_read));
1561
1562 if (sizeof($topic_ids))
1563 {
1564 $sql = 'SELECT forum_id, mark_time
1565 FROM ' . FORUMS_TRACK_TABLE . "
1566 WHERE user_id = {$user->data['user_id']}
1567 AND forum_id " .
1568 (($global_announce_list && sizeof($global_announce_list)) ? "IN (0, $forum_id)" : "= $forum_id");
1569 $result = $db->sql_query($sql);
1570
1571 $mark_time = array();
1572 while ($row = $db->sql_fetchrow($result))
1573 {
1574 $mark_time[$row['forum_id']] = $row['mark_time'];
1575 }
1576 $db->sql_freeresult($result);
1577
1578 $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark'];
1579
1580 foreach ($topic_ids as $topic_id)
1581 {
1582 if ($global_announce_list && isset($global_announce_list[$topic_id]))
1583 {
1584 $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
1585 }
1586 else
1587 {
1588 $last_read[$topic_id] = $user_lastmark;
1589 }
1590 }
1591 }
1592 }
1593 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1594 {
1595 global $tracking_topics;
1596
1597 if (!isset($tracking_topics) || !sizeof($tracking_topics))
1598 {
1599 $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
1600 $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
1601 }
1602
1603 if (!$user->data['is_registered'])
1604 {
1605 $user_lastmark = (isset($tracking_topics['l'])) ? base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate'] : 0;
1606 }
1607 else
1608 {
1609 $user_lastmark = $user->data['user_lastmark'];
1610 }
1611
1612 foreach ($topic_ids as $topic_id)
1613 {
1614 $topic_id36 = base_convert($topic_id, 10, 36);
1615
1616 if (isset($tracking_topics['t'][$topic_id36]))
1617 {
1618 $last_read[$topic_id] = base_convert($tracking_topics['t'][$topic_id36], 36, 10) + $config['board_startdate'];
1619 }
1620 }
1621
1622 $topic_ids = array_diff($topic_ids, array_keys($last_read));
1623
1624 if (sizeof($topic_ids))
1625 {
1626 $mark_time = array();
1627 if ($global_announce_list && sizeof($global_announce_list))
1628 {
1629 if (isset($tracking_topics['f'][0]))
1630 {
1631 $mark_time[0] = base_convert($tracking_topics['f'][0], 36, 10) + $config['board_startdate'];
1632 }
1633 }
1634
1635 if (isset($tracking_topics['f'][$forum_id]))
1636 {
1637 $mark_time[$forum_id] = base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate'];
1638 }
1639
1640 $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user_lastmark;
1641
1642 foreach ($topic_ids as $topic_id)
1643 {
1644 if ($global_announce_list && isset($global_announce_list[$topic_id]))
1645 {
1646 $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
1647 }
1648 else
1649 {
1650 $last_read[$topic_id] = $user_lastmark;
1651 }
1652 }
1653 }
1654 }
1655
1656 return $last_read;
1657}
1658
1659/**
1660* Get list of unread topics
1661*
1662* @param int $user_id User ID (or false for current user)
1663* @param string $sql_extra Extra WHERE SQL statement
1664* @param string $sql_sort ORDER BY SQL sorting statement
1665* @param string $sql_limit Limits the size of unread topics list, 0 for unlimited query
1666*
1667* @return array[int][int] Topic ids as keys, mark_time of topic as value
1668*/
1669function get_unread_topics($user_id = false, $sql_extra = '', $sql_sort = '', $sql_limit = 1001)
1670{
1671 global $config, $db, $user;
1672
1673 $user_id = ($user_id === false) ? (int) $user->data['user_id'] : (int) $user_id;
1674
1675 // Data array we're going to return
1676 $unread_topics = array();
1677
1678 if (empty($sql_sort))
1679 {
1680 $sql_sort = 'ORDER BY t.topic_last_post_time DESC';
1681 }
1682
1683 if ($config['load_db_lastread'] && $user->data['is_registered'])
1684 {
1685 // Get list of the unread topics
1686 $last_mark = $user->data['user_lastmark'];
1687
1688 $sql_array = array(
1689 'SELECT' => 't.topic_id, t.topic_last_post_time, tt.mark_time as topic_mark_time, ft.mark_time as forum_mark_time',
1690
1691 'FROM' => array(TOPICS_TABLE => 't'),
1692
1693 'LEFT_JOIN' => array(
1694 array(
1695 'FROM' => array(TOPICS_TRACK_TABLE => 'tt'),
1696 'ON' => "tt.user_id = $user_id AND t.topic_id = tt.topic_id",
1697 ),
1698 array(
1699 'FROM' => array(FORUMS_TRACK_TABLE => 'ft'),
1700 'ON' => "ft.user_id = $user_id AND t.forum_id = ft.forum_id",
1701 ),
1702 ),
1703
1704 'WHERE' => "
1705 (
1706 (tt.mark_time IS NOT NULL AND t.topic_last_post_time > tt.mark_time) OR
1707 (tt.mark_time IS NULL AND ft.mark_time IS NOT NULL AND t.topic_last_post_time > ft.mark_time) OR
1708 (tt.mark_time IS NULL AND ft.mark_time IS NULL AND t.topic_last_post_time > $last_mark)
1709 )
1710 $sql_extra
1711 $sql_sort",
1712 );
1713
1714 $sql = $db->sql_build_query('SELECT', $sql_array);
1715 $result = $db->sql_query_limit($sql, $sql_limit);
1716
1717 while ($row = $db->sql_fetchrow($result))
1718 {
1719 $topic_id = (int) $row['topic_id'];
1720 $unread_topics[$topic_id] = ($row['topic_mark_time']) ? (int) $row['topic_mark_time'] : (($row['forum_mark_time']) ? (int) $row['forum_mark_time'] : $last_mark);
1721 }
1722 $db->sql_freeresult($result);
1723 }
1724 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1725 {
1726 global $tracking_topics;
1727
1728 if (empty($tracking_topics))
1729 {
1730 $tracking_topics = request_var($config['cookie_name'] . '_track', '', false, true);
1731 $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
1732 }
1733
1734 if (!$user->data['is_registered'])
1735 {
1736 $user_lastmark = (isset($tracking_topics['l'])) ? base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate'] : 0;
1737 }
1738 else
1739 {
1740 $user_lastmark = (int) $user->data['user_lastmark'];
1741 }
1742
1743 $sql = 'SELECT t.topic_id, t.forum_id, t.topic_last_post_time
1744 FROM ' . TOPICS_TABLE . ' t
1745 WHERE t.topic_last_post_time > ' . $user_lastmark . "
1746 $sql_extra
1747 $sql_sort";
1748 $result = $db->sql_query_limit($sql, $sql_limit);
1749
1750 while ($row = $db->sql_fetchrow($result))
1751 {
1752 $forum_id = (int) $row['forum_id'];
1753 $topic_id = (int) $row['topic_id'];
1754 $topic_id36 = base_convert($topic_id, 10, 36);
1755
1756 if (isset($tracking_topics['t'][$topic_id36]))
1757 {
1758 $last_read = base_convert($tracking_topics['t'][$topic_id36], 36, 10) + $config['board_startdate'];
1759
1760 if ($row['topic_last_post_time'] > $last_read)
1761 {
1762 $unread_topics[$topic_id] = $last_read;
1763 }
1764 }
1765 else if (isset($tracking_topics['f'][$forum_id]))
1766 {
1767 $mark_time = base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate'];
1768
1769 if ($row['topic_last_post_time'] > $mark_time)
1770 {
1771 $unread_topics[$topic_id] = $mark_time;
1772 }
1773 }
1774 else
1775 {
1776 $unread_topics[$topic_id] = $user_lastmark;
1777 }
1778 }
1779 $db->sql_freeresult($result);
1780 }
1781
1782 return $unread_topics;
1783}
1784
1785/**
1786* Check for read forums and update topic tracking info accordingly
1787*
1788* @param int $forum_id the forum id to check
1789* @param int $forum_last_post_time the forums last post time
1790* @param int $f_mark_time the forums last mark time if user is registered and load_db_lastread enabled
1791* @param int $mark_time_forum false if the mark time needs to be obtained, else the last users forum mark time
1792*
1793* @return true if complete forum got marked read, else false.
1794*/
1795function update_forum_tracking_info($forum_id, $forum_last_post_time, $f_mark_time = false, $mark_time_forum = false)
1796{
1797 global $db, $tracking_topics, $user, $config;
1798
1799 // Determine the users last forum mark time if not given.
1800 if ($mark_time_forum === false)
1801 {
1802 if ($config['load_db_lastread'] && $user->data['is_registered'])
1803 {
1804 $mark_time_forum = (!empty($f_mark_time)) ? $f_mark_time : $user->data['user_lastmark'];
1805 }
1806 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1807 {
1808 $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
1809 $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
1810
1811 if (!$user->data['is_registered'])
1812 {
1813 $user->data['user_lastmark'] = (isset($tracking_topics['l'])) ? (int) (base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate']) : 0;
1814 }
1815
1816 $mark_time_forum = (isset($tracking_topics['f'][$forum_id])) ? (int) (base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate']) : $user->data['user_lastmark'];
1817 }
1818 }
1819
1820 // Check the forum for any left unread topics.
1821 // If there are none, we mark the forum as read.
1822 if ($config['load_db_lastread'] && $user->data['is_registered'])
1823 {
1824 if ($mark_time_forum >= $forum_last_post_time)
1825 {
1826 // We do not need to mark read, this happened before. Therefore setting this to true
1827 $row = true;
1828 }
1829 else
1830 {
1831 $sql = 'SELECT t.forum_id FROM ' . TOPICS_TABLE . ' t
1832 LEFT JOIN ' . TOPICS_TRACK_TABLE . ' tt ON (tt.topic_id = t.topic_id AND tt.user_id = ' . $user->data['user_id'] . ')
1833 WHERE t.forum_id = ' . $forum_id . '
1834 AND t.topic_last_post_time > ' . $mark_time_forum . '
1835 AND t.topic_moved_id = 0
1836 AND (tt.topic_id IS NULL OR tt.mark_time < t.topic_last_post_time)
1837 GROUP BY t.forum_id';
1838 $result = $db->sql_query_limit($sql, 1);
1839 $row = $db->sql_fetchrow($result);
1840 $db->sql_freeresult($result);
1841 }
1842 }
1843 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1844 {
1845 // Get information from cookie
1846 $row = false;
1847
1848 if (!isset($tracking_topics['tf'][$forum_id]))
1849 {
1850 // We do not need to mark read, this happened before. Therefore setting this to true
1851 $row = true;
1852 }
1853 else
1854 {
1855 $sql = 'SELECT topic_id
1856 FROM ' . TOPICS_TABLE . '
1857 WHERE forum_id = ' . $forum_id . '
1858 AND topic_last_post_time > ' . $mark_time_forum . '
1859 AND topic_moved_id = 0';
1860 $result = $db->sql_query($sql);
1861
1862 $check_forum = $tracking_topics['tf'][$forum_id];
1863 $unread = false;
1864
1865 while ($row = $db->sql_fetchrow($result))
1866 {
1867 if (!isset($check_forum[base_convert($row['topic_id'], 10, 36)]))
1868 {
1869 $unread = true;
1870 break;
1871 }
1872 }
1873 $db->sql_freeresult($result);
1874
1875 $row = $unread;
1876 }
1877 }
1878 else
1879 {
1880 $row = true;
1881 }
1882
1883 if (!$row)
1884 {
1885 markread('topics', $forum_id);
1886 return true;
1887 }
1888
1889 return false;
1890}
1891
1892/**
1893* Transform an array into a serialized format
1894*/
1895function tracking_serialize($input)
1896{
1897 $out = '';
1898 foreach ($input as $key => $value)
1899 {
1900 if (is_array($value))
1901 {
1902 $out .= $key . ':(' . tracking_serialize($value) . ');';
1903 }
1904 else
1905 {
1906 $out .= $key . ':' . $value . ';';
1907 }
1908 }
1909 return $out;
1910}
1911
1912/**
1913* Transform a serialized array into an actual array
1914*/
1915function tracking_unserialize($string, $max_depth = 3)
1916{
1917 $n = strlen($string);
1918 if ($n > 10010)
1919 {
1920 die('Invalid data supplied');
1921 }
1922 $data = $stack = array();
1923 $key = '';
1924 $mode = 0;
1925 $level = &$data;
1926 for ($i = 0; $i < $n; ++$i)
1927 {
1928 switch ($mode)
1929 {
1930 case 0:
1931 switch ($string[$i])
1932 {
1933 case ':':
1934 $level[$key] = 0;
1935 $mode = 1;
1936 break;
1937 case ')':
1938 unset($level);
1939 $level = array_pop($stack);
1940 $mode = 3;
1941 break;
1942 default:
1943 $key .= $string[$i];
1944 }
1945 break;
1946
1947 case 1:
1948 switch ($string[$i])
1949 {
1950 case '(':
1951 if (sizeof($stack) >= $max_depth)
1952 {
1953 die('Invalid data supplied');
1954 }
1955 $stack[] = &$level;
1956 $level[$key] = array();
1957 $level = &$level[$key];
1958 $key = '';
1959 $mode = 0;
1960 break;
1961 default:
1962 $level[$key] = $string[$i];
1963 $mode = 2;
1964 break;
1965 }
1966 break;
1967
1968 case 2:
1969 switch ($string[$i])
1970 {
1971 case ')':
1972 unset($level);
1973 $level = array_pop($stack);
1974 $mode = 3;
1975 break;
1976 case ';':
1977 $key = '';
1978 $mode = 0;
1979 break;
1980 default:
1981 $level[$key] .= $string[$i];
1982 break;
1983 }
1984 break;
1985
1986 case 3:
1987 switch ($string[$i])
1988 {
1989 case ')':
1990 unset($level);
1991 $level = array_pop($stack);
1992 break;
1993 case ';':
1994 $key = '';
1995 $mode = 0;
1996 break;
1997 default:
1998 die('Invalid data supplied');
1999 break;
2000 }
2001 break;
2002 }
2003 }
2004
2005 if (sizeof($stack) != 0 || ($mode != 0 && $mode != 3))
2006 {
2007 die('Invalid data supplied');
2008 }
2009
2010 return $level;
2011}
2012
2013// Pagination functions
2014
2015/**
2016* Pagination routine, generates page number sequence
2017* tpl_prefix is for using different pagination blocks at one page
2018*/
2019function generate_pagination($base_url, $num_items, $per_page, $start_item, $add_prevnext_text = false, $tpl_prefix = '')
2020{
2021 global $template, $user;
2022
2023 // Make sure $per_page is a valid value
2024 $per_page = ($per_page <= 0) ? 1 : $per_page;
2025
2026 $seperator = '<span class="page-sep">' . $user->lang['COMMA_SEPARATOR'] . '</span>';
2027 $total_pages = ceil($num_items / $per_page);
2028
2029 if ($total_pages == 1 || !$num_items)
2030 {
2031 return false;
2032 }
2033
2034 $on_page = floor($start_item / $per_page) + 1;
2035 $url_delim = (strpos($base_url, '?') === false) ? '?' : ((strpos($base_url, '?') === strlen($base_url) - 1) ? '' : '&amp;');
2036
2037 $page_string = ($on_page == 1) ? '<strong>1</strong>' : '<a href="' . $base_url . '">1</a>';
2038
2039 if ($total_pages > 5)
2040 {
2041 $start_cnt = min(max(1, $on_page - 4), $total_pages - 5);
2042 $end_cnt = max(min($total_pages, $on_page + 4), 6);
2043
2044 $page_string .= ($start_cnt > 1) ? ' ... ' : $seperator;
2045
2046 for ($i = $start_cnt + 1; $i < $end_cnt; $i++)
2047 {
2048 $page_string .= ($i == $on_page) ? '<strong>' . $i . '</strong>' : '<a href="' . $base_url . "{$url_delim}start=" . (($i - 1) * $per_page) . '">' . $i . '</a>';
2049 if ($i < $end_cnt - 1)
2050 {
2051 $page_string .= $seperator;
2052 }
2053 }
2054
2055 $page_string .= ($end_cnt < $total_pages) ? ' ... ' : $seperator;
2056 }
2057 else
2058 {
2059 $page_string .= $seperator;
2060
2061 for ($i = 2; $i < $total_pages; $i++)
2062 {
2063 $page_string .= ($i == $on_page) ? '<strong>' . $i . '</strong>' : '<a href="' . $base_url . "{$url_delim}start=" . (($i - 1) * $per_page) . '">' . $i . '</a>';
2064 if ($i < $total_pages)
2065 {
2066 $page_string .= $seperator;
2067 }
2068 }
2069 }
2070
2071 $page_string .= ($on_page == $total_pages) ? '<strong>' . $total_pages . '</strong>' : '<a href="' . $base_url . "{$url_delim}start=" . (($total_pages - 1) * $per_page) . '">' . $total_pages . '</a>';
2072
2073 if ($add_prevnext_text)
2074 {
2075 if ($on_page != 1)
2076 {
2077 $page_string = '<a href="' . $base_url . "{$url_delim}start=" . (($on_page - 2) * $per_page) . '">' . $user->lang['PREVIOUS'] . '</a>&nbsp;&nbsp;' . $page_string;
2078 }
2079
2080 if ($on_page != $total_pages)
2081 {
2082 $page_string .= '&nbsp;&nbsp;<a href="' . $base_url . "{$url_delim}start=" . ($on_page * $per_page) . '">' . $user->lang['NEXT'] . '</a>';
2083 }
2084 }
2085
2086 $template->assign_vars(array(
2087 $tpl_prefix . 'BASE_URL' => $base_url,
2088 'A_' . $tpl_prefix . 'BASE_URL' => addslashes($base_url),
2089 $tpl_prefix . 'PER_PAGE' => $per_page,
2090
2091 $tpl_prefix . 'PREVIOUS_PAGE' => ($on_page == 1) ? '' : $base_url . "{$url_delim}start=" . (($on_page - 2) * $per_page),
2092 $tpl_prefix . 'NEXT_PAGE' => ($on_page == $total_pages) ? '' : $base_url . "{$url_delim}start=" . ($on_page * $per_page),
2093 $tpl_prefix . 'TOTAL_PAGES' => $total_pages,
2094 ));
2095
2096 return $page_string;
2097}
2098
2099/**
2100* Return current page (pagination)
2101*/
2102function on_page($num_items, $per_page, $start)
2103{
2104 global $template, $user;
2105
2106 // Make sure $per_page is a valid value
2107 $per_page = ($per_page <= 0) ? 1 : $per_page;
2108
2109 $on_page = floor($start / $per_page) + 1;
2110
2111 $template->assign_vars(array(
2112 'ON_PAGE' => $on_page)
2113 );
2114
2115 return sprintf($user->lang['PAGE_OF'], $on_page, max(ceil($num_items / $per_page), 1));
2116}
2117
2118// Server functions (building urls, redirecting...)
2119
2120/**
2121* Append session id to url.
2122* This function supports hooks.
2123*
2124* @param string $url The url the session id needs to be appended to (can have params)
2125* @param mixed $params String or array of additional url parameters
2126* @param bool $is_amp Is url using &amp; (true) or & (false)
2127* @param string $session_id Possibility to use a custom session id instead of the global one
2128*
2129* Examples:
2130* <code>
2131* append_sid("{$phpbb_root_path}viewtopic.$phpEx?t=1&amp;f=2");
2132* append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&amp;f=2');
2133* append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&f=2', false);
2134* append_sid("{$phpbb_root_path}viewtopic.$phpEx", array('t' => 1, 'f' => 2));
2135* </code>
2136*
2137*/
2138function append_sid($url, $params = false, $is_amp = true, $session_id = false)
2139{
2140 global $_SID, $_EXTRA_URL, $phpbb_hook;
2141
2142 // Developers using the hook function need to globalise the $_SID and $_EXTRA_URL on their own and also handle it appropiatly.
2143 // They could mimick most of what is within this function
2144 if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__, $url, $params, $is_amp, $session_id))
2145 {
2146 if ($phpbb_hook->hook_return(__FUNCTION__))
2147 {
2148 return $phpbb_hook->hook_return_result(__FUNCTION__);
2149 }
2150 }
2151
2152 $params_is_array = is_array($params);
2153
2154 // Get anchor
2155 $anchor = '';
2156 if (strpos($url, '#') !== false)
2157 {
2158 list($url, $anchor) = explode('#', $url, 2);
2159 $anchor = '#' . $anchor;
2160 }
2161 else if (!$params_is_array && strpos($params, '#') !== false)
2162 {
2163 list($params, $anchor) = explode('#', $params, 2);
2164 $anchor = '#' . $anchor;
2165 }
2166
2167 // Handle really simple cases quickly
2168 if ($_SID == '' && $session_id === false && empty($_EXTRA_URL) && !$params_is_array && !$anchor)
2169 {
2170 if ($params === false)
2171 {
2172 return $url;
2173 }
2174
2175 $url_delim = (strpos($url, '?') === false) ? '?' : (($is_amp) ? '&amp;' : '&');
2176 return $url . ($params !== false ? $url_delim. $params : '');
2177 }
2178
2179 // Assign sid if session id is not specified
2180 if ($session_id === false)
2181 {
2182 $session_id = $_SID;
2183 }
2184
2185 $amp_delim = ($is_amp) ? '&amp;' : '&';
2186 $url_delim = (strpos($url, '?') === false) ? '?' : $amp_delim;
2187
2188 // Appending custom url parameter?
2189 $append_url = (!empty($_EXTRA_URL)) ? implode($amp_delim, $_EXTRA_URL) : '';
2190
2191 // Use the short variant if possible ;)
2192 if ($params === false)
2193 {
2194 // Append session id
2195 if (!$session_id)
2196 {
2197 return $url . (($append_url) ? $url_delim . $append_url : '') . $anchor;
2198 }
2199 else
2200 {
2201 return $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . 'sid=' . $session_id . $anchor;
2202 }
2203 }
2204
2205 // Build string if parameters are specified as array
2206 if (is_array($params))
2207 {
2208 $output = array();
2209
2210 foreach ($params as $key => $item)
2211 {
2212 if ($item === NULL)
2213 {
2214 continue;
2215 }
2216
2217 if ($key == '#')
2218 {
2219 $anchor = '#' . $item;
2220 continue;
2221 }
2222
2223 $output[] = $key . '=' . $item;
2224 }
2225
2226 $params = implode($amp_delim, $output);
2227 }
2228
2229 // Append session id and parameters (even if they are empty)
2230 // If parameters are empty, the developer can still append his/her parameters without caring about the delimiter
2231 return $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . $params . ((!$session_id) ? '' : $amp_delim . 'sid=' . $session_id) . $anchor;
2232}
2233
2234/**
2235* Generate board url (example: http://www.example.com/phpBB)
2236* @param bool $without_script_path if set to true the script path gets not appended (example: http://www.example.com)
2237*/
2238function generate_board_url($without_script_path = false)
2239{
2240 global $config, $user;
2241
2242 $server_name = $user->host;
2243 $server_port = (!empty($_SERVER['SERVER_PORT'])) ? (int) $_SERVER['SERVER_PORT'] : (int) getenv('SERVER_PORT');
2244
2245 // Forcing server vars is the only way to specify/override the protocol
2246 if ($config['force_server_vars'] || !$server_name)
2247 {
2248 $server_protocol = ($config['server_protocol']) ? $config['server_protocol'] : (($config['cookie_secure']) ? 'https://' : 'http://');
2249 $server_name = $config['server_name'];
2250 $server_port = (int) $config['server_port'];
2251 $script_path = $config['script_path'];
2252
2253 $url = $server_protocol . $server_name;
2254 $cookie_secure = $config['cookie_secure'];
2255 }
2256 else
2257 {
2258 // Do not rely on cookie_secure, users seem to think that it means a secured cookie instead of an encrypted connection
2259 $cookie_secure = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 1 : 0;
2260 $url = (($cookie_secure) ? 'https://' : 'http://') . $server_name;
2261
2262 $script_path = $user->page['root_script_path'];
2263 }
2264
2265 if ($server_port && (($cookie_secure && $server_port <> 443) || (!$cookie_secure && $server_port <> 80)))
2266 {
2267 // HTTP HOST can carry a port number (we fetch $user->host, but for old versions this may be true)
2268 if (strpos($server_name, ':') === false)
2269 {
2270 $url .= ':' . $server_port;
2271 }
2272 }
2273
2274 if (!$without_script_path)
2275 {
2276 $url .= $script_path;
2277 }
2278
2279 // Strip / from the end
2280 if (substr($url, -1, 1) == '/')
2281 {
2282 $url = substr($url, 0, -1);
2283 }
2284
2285 return $url;
2286}
2287
2288/**
2289* Redirects the user to another page then exits the script nicely
2290* This function is intended for urls within the board. It's not meant to redirect to cross-domains.
2291*
2292* @param string $url The url to redirect to
2293* @param bool $return If true, do not redirect but return the sanitized URL. Default is no return.
2294* @param bool $disable_cd_check If true, redirect() will redirect to an external domain. If false, the redirect point to the boards url if it does not match the current domain. Default is false.
2295*/
2296function redirect($url, $return = false, $disable_cd_check = false)
2297{
2298 global $db, $cache, $config, $user, $phpbb_root_path;
2299
2300 if (empty($user->lang))
2301 {
2302 $user->add_lang('common');
2303 }
2304
2305 if (!$return)
2306 {
2307 garbage_collection();
2308 }
2309
2310 // Make sure no &amp;'s are in, this will break the redirect
2311 $url = str_replace('&amp;', '&', $url);
2312
2313 // Determine which type of redirect we need to handle...
2314 $url_parts = @parse_url($url);
2315
2316 if ($url_parts === false)
2317 {
2318 // Malformed url, redirect to current page...
2319 $url = generate_board_url() . '/' . $user->page['page'];
2320 }
2321 else if (!empty($url_parts['scheme']) && !empty($url_parts['host']))
2322 {
2323 // Attention: only able to redirect within the same domain if $disable_cd_check is false (yourdomain.com -> www.yourdomain.com will not work)
2324 if (!$disable_cd_check && $url_parts['host'] !== $user->host)
2325 {
2326 $url = generate_board_url();
2327 }
2328 }
2329 else if ($url[0] == '/')
2330 {
2331 // Absolute uri, prepend direct url...
2332 $url = generate_board_url(true) . $url;
2333 }
2334 else
2335 {
2336 // Relative uri
2337 $pathinfo = pathinfo($url);
2338
2339 // Is the uri pointing to the current directory?
2340 if ($pathinfo['dirname'] == '.')
2341 {
2342 $url = str_replace('./', '', $url);
2343
2344 // Strip / from the beginning
2345 if ($url && substr($url, 0, 1) == '/')
2346 {
2347 $url = substr($url, 1);
2348 }
2349
2350 if ($user->page['page_dir'])
2351 {
2352 $url = generate_board_url() . '/' . $user->page['page_dir'] . '/' . $url;
2353 }
2354 else
2355 {
2356 $url = generate_board_url() . '/' . $url;
2357 }
2358 }
2359 else
2360 {
2361 // Used ./ before, but $phpbb_root_path is working better with urls within another root path
2362 $root_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($phpbb_root_path)));
2363 $page_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($pathinfo['dirname'])));
2364 $intersection = array_intersect_assoc($root_dirs, $page_dirs);
2365
2366 $root_dirs = array_diff_assoc($root_dirs, $intersection);
2367 $page_dirs = array_diff_assoc($page_dirs, $intersection);
2368
2369 $dir = str_repeat('../', sizeof($root_dirs)) . implode('/', $page_dirs);
2370
2371 // Strip / from the end
2372 if ($dir && substr($dir, -1, 1) == '/')
2373 {
2374 $dir = substr($dir, 0, -1);
2375 }
2376
2377 // Strip / from the beginning
2378 if ($dir && substr($dir, 0, 1) == '/')
2379 {
2380 $dir = substr($dir, 1);
2381 }
2382
2383 $url = str_replace($pathinfo['dirname'] . '/', '', $url);
2384
2385 // Strip / from the beginning
2386 if (substr($url, 0, 1) == '/')
2387 {
2388 $url = substr($url, 1);
2389 }
2390
2391 $url = (!empty($dir) ? $dir . '/' : '') . $url;
2392 $url = generate_board_url() . '/' . $url;
2393 }
2394 }
2395
2396 // Make sure no linebreaks are there... to prevent http response splitting for PHP < 4.4.2
2397 if (strpos(urldecode($url), "\n") !== false || strpos(urldecode($url), "\r") !== false || strpos($url, ';') !== false)
2398 {
2399 trigger_error('Tried to redirect to potentially insecure url.', E_USER_ERROR);
2400 }
2401
2402 // Now, also check the protocol and for a valid url the last time...
2403 $allowed_protocols = array('http', 'https', 'ftp', 'ftps');
2404 $url_parts = parse_url($url);
2405
2406 if ($url_parts === false || empty($url_parts['scheme']) || !in_array($url_parts['scheme'], $allowed_protocols))
2407 {
2408 trigger_error('Tried to redirect to potentially insecure url.', E_USER_ERROR);
2409 }
2410
2411 if ($return)
2412 {
2413 return $url;
2414 }
2415
2416 // Redirect via an HTML form for PITA webservers
2417 if (@preg_match('#Microsoft|WebSTAR|Xitami#', getenv('SERVER_SOFTWARE')))
2418 {
2419 header('Refresh: 0; URL=' . $url);
2420
2421 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
2422 echo '<html xmlns="http://www.w3.org/1999/xhtml" dir="' . $user->lang['DIRECTION'] . '" lang="' . $user->lang['USER_LANG'] . '" xml:lang="' . $user->lang['USER_LANG'] . '">';
2423 echo '<head>';
2424 echo '<meta http-equiv="content-type" content="text/html; charset=utf-8" />';
2425 echo '<meta http-equiv="refresh" content="0; url=' . str_replace('&', '&amp;', $url) . '" />';
2426 echo '<title>' . $user->lang['REDIRECT'] . '</title>';
2427 echo '</head>';
2428 echo '<body>';
2429 echo '<div style="text-align: center;">' . sprintf($user->lang['URL_REDIRECT'], '<a href="' . str_replace('&', '&amp;', $url) . '">', '</a>') . '</div>';
2430 echo '</body>';
2431 echo '</html>';
2432
2433 exit;
2434 }
2435
2436 // Behave as per HTTP/1.1 spec for others
2437 header('Location: ' . $url);
2438 exit;
2439}
2440
2441/**
2442* Re-Apply session id after page reloads
2443*/
2444function reapply_sid($url)
2445{
2446 global $phpEx, $phpbb_root_path;
2447
2448 if ($url === "index.$phpEx")
2449 {
2450 return append_sid("index.$phpEx");
2451 }
2452 else if ($url === "{$phpbb_root_path}index.$phpEx")
2453 {
2454 return append_sid("{$phpbb_root_path}index.$phpEx");
2455 }
2456
2457 // Remove previously added sid
2458 if (strpos($url, 'sid=') !== false)
2459 {
2460 // All kind of links
2461 $url = preg_replace('/(\?)?(&amp;|&)?sid=[a-z0-9]+/', '', $url);
2462 // if the sid was the first param, make the old second as first ones
2463 $url = preg_replace("/$phpEx(&amp;|&)+?/", "$phpEx?", $url);
2464 }
2465
2466 return append_sid($url);
2467}
2468
2469/**
2470* Returns url from the session/current page with an re-appended SID with optionally stripping vars from the url
2471*/
2472function build_url($strip_vars = false)
2473{
2474 global $user, $phpbb_root_path;
2475
2476 // Append SID
2477 $redirect = append_sid($user->page['page'], false, false);
2478
2479 // Add delimiter if not there...
2480 if (strpos($redirect, '?') === false)
2481 {
2482 $redirect .= '?';
2483 }
2484
2485 // Strip vars...
2486 if ($strip_vars !== false && strpos($redirect, '?') !== false)
2487 {
2488 if (!is_array($strip_vars))
2489 {
2490 $strip_vars = array($strip_vars);
2491 }
2492
2493 $query = $_query = array();
2494
2495 $args = substr($redirect, strpos($redirect, '?') + 1);
2496 $args = ($args) ? explode('&', $args) : array();
2497 $redirect = substr($redirect, 0, strpos($redirect, '?'));
2498
2499 foreach ($args as $argument)
2500 {
2501 $arguments = explode('=', $argument);
2502 $key = $arguments[0];
2503 unset($arguments[0]);
2504
2505 $query[$key] = implode('=', $arguments);
2506 }
2507
2508 // Strip the vars off
2509 foreach ($strip_vars as $strip)
2510 {
2511 if (isset($query[$strip]))
2512 {
2513 unset($query[$strip]);
2514 }
2515 }
2516
2517 // Glue the remaining parts together... already urlencoded
2518 foreach ($query as $key => $value)
2519 {
2520 $_query[] = $key . '=' . $value;
2521 }
2522 $query = implode('&', $_query);
2523
2524 $redirect .= ($query) ? '?' . $query : '';
2525 }
2526
2527 // We need to be cautious here.
2528 // On some situations, the redirect path is an absolute URL, sometimes a relative path
2529 // For a relative path, let's prefix it with $phpbb_root_path to point to the correct location,
2530 // else we use the URL directly.
2531 $url_parts = @parse_url($redirect);
2532
2533 // URL
2534 if ($url_parts !== false && !empty($url_parts['scheme']) && !empty($url_parts['host']))
2535 {
2536 return str_replace('&', '&amp;', $redirect);
2537 }
2538
2539 return $phpbb_root_path . str_replace('&', '&amp;', $redirect);
2540}
2541
2542/**
2543* Meta refresh assignment
2544* Adds META template variable with meta http tag.
2545*
2546* @param int $time Time in seconds for meta refresh tag
2547* @param string $url URL to redirect to. The url will go through redirect() first before the template variable is assigned
2548* @param bool $disable_cd_check If true, meta_refresh() will redirect to an external domain. If false, the redirect point to the boards url if it does not match the current domain. Default is false.
2549*/
2550function meta_refresh($time, $url, $disable_cd_check = false)
2551{
2552 global $template;
2553
2554 $url = redirect($url, true, $disable_cd_check);
2555 $url = str_replace('&', '&amp;', $url);
2556
2557 // For XHTML compatibility we change back & to &amp;
2558 $template->assign_vars(array(
2559 'META' => '<meta http-equiv="refresh" content="' . $time . ';url=' . $url . '" />')
2560 );
2561
2562 return $url;
2563}
2564
2565//Form validation
2566
2567
2568/**
2569* Add a secret hash for use in links/GET requests
2570* @param string $link_name The name of the link; has to match the name used in check_link_hash, otherwise no restrictions apply
2571* @return string the hash
2572
2573*/
2574function generate_link_hash($link_name)
2575{
2576 global $user;
2577
2578 if (!isset($user->data["hash_$link_name"]))
2579 {
2580 $user->data["hash_$link_name"] = substr(sha1($user->data['user_form_salt'] . $link_name), 0, 8);
2581 }
2582
2583 return $user->data["hash_$link_name"];
2584}
2585
2586
2587/**
2588* checks a link hash - for GET requests
2589* @param string $token the submitted token
2590* @param string $link_name The name of the link
2591* @return boolean true if all is fine
2592*/
2593function check_link_hash($token, $link_name)
2594{
2595 return $token === generate_link_hash($link_name);
2596}
2597
2598/**
2599* Add a secret token to the form (requires the S_FORM_TOKEN template variable)
2600* @param string $form_name The name of the form; has to match the name used in check_form_key, otherwise no restrictions apply
2601*/
2602function add_form_key($form_name)
2603{
2604 global $config, $template, $user;
2605
2606 $now = time();
2607 $token_sid = ($user->data['user_id'] == ANONYMOUS && !empty($config['form_token_sid_guests'])) ? $user->session_id : '';
2608 $token = sha1($now . $user->data['user_form_salt'] . $form_name . $token_sid);
2609
2610 $s_fields = build_hidden_fields(array(
2611 'creation_time' => $now,
2612 'form_token' => $token,
2613 ));
2614
2615 $template->assign_vars(array(
2616 'S_FORM_TOKEN' => $s_fields,
2617 ));
2618}
2619
2620/**
2621* Check the form key. Required for all altering actions not secured by confirm_box
2622* @param string $form_name The name of the form; has to match the name used in add_form_key, otherwise no restrictions apply
2623* @param int $timespan The maximum acceptable age for a submitted form in seconds. Defaults to the config setting.
2624* @param string $return_page The address for the return link
2625* @param bool $trigger If true, the function will triger an error when encountering an invalid form
2626*/
2627function check_form_key($form_name, $timespan = false, $return_page = '', $trigger = false)
2628{
2629 global $config, $user;
2630
2631 if ($timespan === false)
2632 {
2633 // we enforce a minimum value of half a minute here.
2634 $timespan = ($config['form_token_lifetime'] == -1) ? -1 : max(30, $config['form_token_lifetime']);
2635 }
2636
2637 if (isset($_POST['creation_time']) && isset($_POST['form_token']))
2638 {
2639 $creation_time = abs(request_var('creation_time', 0));
2640 $token = request_var('form_token', '');
2641
2642 $diff = time() - $creation_time;
2643
2644 // If creation_time and the time() now is zero we can assume it was not a human doing this (the check for if ($diff)...
2645 if ($diff && ($diff <= $timespan || $timespan === -1))
2646 {
2647 $token_sid = ($user->data['user_id'] == ANONYMOUS && !empty($config['form_token_sid_guests'])) ? $user->session_id : '';
2648 $key = sha1($creation_time . $user->data['user_form_salt'] . $form_name . $token_sid);
2649
2650 if ($key === $token)
2651 {
2652 return true;
2653 }
2654 }
2655 }
2656
2657 if ($trigger)
2658 {
2659 trigger_error($user->lang['FORM_INVALID'] . $return_page);
2660 }
2661
2662 return false;
2663}
2664
2665// Message/Login boxes
2666
2667/**
2668* Build Confirm box
2669* @param boolean $check True for checking if confirmed (without any additional parameters) and false for displaying the confirm box
2670* @param string $title Title/Message used for confirm box.
2671* message text is _CONFIRM appended to title.
2672* If title cannot be found in user->lang a default one is displayed
2673* If title_CONFIRM cannot be found in user->lang the text given is used.
2674* @param string $hidden Hidden variables
2675* @param string $html_body Template used for confirm box
2676* @param string $u_action Custom form action
2677*/
2678function confirm_box($check, $title = '', $hidden = '', $html_body = 'confirm_body.html', $u_action = '')
2679{
2680 global $user, $template, $db;
2681 global $phpEx, $phpbb_root_path;
2682
2683 if (isset($_POST['cancel']))
2684 {
2685 return false;
2686 }
2687
2688 $confirm = false;
2689 if (isset($_POST['confirm']))
2690 {
2691 // language frontier
2692 if ($_POST['confirm'] === $user->lang['YES'])
2693 {
2694 $confirm = true;
2695 }
2696 }
2697
2698 if ($check && $confirm)
2699 {
2700 $user_id = request_var('confirm_uid', 0);
2701 $session_id = request_var('sess', '');
2702 $confirm_key = request_var('confirm_key', '');
2703
2704 if ($user_id != $user->data['user_id'] || $session_id != $user->session_id || !$confirm_key || !$user->data['user_last_confirm_key'] || $confirm_key != $user->data['user_last_confirm_key'])
2705 {
2706 return false;
2707 }
2708
2709 // Reset user_last_confirm_key
2710 $sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = ''
2711 WHERE user_id = " . $user->data['user_id'];
2712 $db->sql_query($sql);
2713
2714 return true;
2715 }
2716 else if ($check)
2717 {
2718 return false;
2719 }
2720
2721 $s_hidden_fields = build_hidden_fields(array(
2722 'confirm_uid' => $user->data['user_id'],
2723 'sess' => $user->session_id,
2724 'sid' => $user->session_id,
2725 ));
2726
2727 // generate activation key
2728 $confirm_key = gen_rand_string(10);
2729
2730 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
2731 {
2732 adm_page_header((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]);
2733 }
2734 else
2735 {
2736 page_header(((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]), false);
2737 }
2738
2739 $template->set_filenames(array(
2740 'body' => $html_body)
2741 );
2742
2743 // If activation key already exist, we better do not re-use the key (something very strange is going on...)
2744 if (request_var('confirm_key', ''))
2745 {
2746 // This should not occur, therefore we cancel the operation to safe the user
2747 return false;
2748 }
2749
2750 // re-add sid / transform & to &amp; for user->page (user->page is always using &)
2751 $use_page = ($u_action) ? $phpbb_root_path . $u_action : $phpbb_root_path . str_replace('&', '&amp;', $user->page['page']);
2752 $u_action = reapply_sid($use_page);
2753 $u_action .= ((strpos($u_action, '?') === false) ? '?' : '&amp;') . 'confirm_key=' . $confirm_key;
2754
2755 $template->assign_vars(array(
2756 'MESSAGE_TITLE' => (!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title],
2757 'MESSAGE_TEXT' => (!isset($user->lang[$title . '_CONFIRM'])) ? $title : $user->lang[$title . '_CONFIRM'],
2758
2759 'YES_VALUE' => $user->lang['YES'],
2760 'S_CONFIRM_ACTION' => $u_action,
2761 'S_HIDDEN_FIELDS' => $hidden . $s_hidden_fields)
2762 );
2763
2764 $sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = '" . $db->sql_escape($confirm_key) . "'
2765 WHERE user_id = " . $user->data['user_id'];
2766 $db->sql_query($sql);
2767
2768 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
2769 {
2770 adm_page_footer();
2771 }
2772 else
2773 {
2774 page_footer();
2775 }
2776}
2777
2778/**
2779* Generate login box or verify password
2780*/
2781function login_box($redirect = '', $l_explain = '', $l_success = '', $admin = false, $s_display = true)
2782{
2783 global $db, $user, $template, $auth, $phpEx, $phpbb_root_path, $config;
2784
2785 if (!class_exists('phpbb_captcha_factory'))
2786 {
2787 include($phpbb_root_path . 'includes/captcha/captcha_factory.' . $phpEx);
2788 }
2789
2790 $err = '';
2791
2792 // Make sure user->setup() has been called
2793 if (empty($user->lang))
2794 {
2795 $user->setup();
2796 }
2797
2798 // Print out error if user tries to authenticate as an administrator without having the privileges...
2799 if ($admin && !$auth->acl_get('a_'))
2800 {
2801 // Not authd
2802 // anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions
2803 if ($user->data['is_registered'])
2804 {
2805 add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
2806 }
2807 trigger_error('NO_AUTH_ADMIN');
2808 }
2809
2810 if (isset($_POST['login']))
2811 {
2812 // Get credential
2813 if ($admin)
2814 {
2815 $credential = request_var('credential', '');
2816
2817 if (strspn($credential, 'abcdef0123456789') !== strlen($credential) || strlen($credential) != 32)
2818 {
2819 if ($user->data['is_registered'])
2820 {
2821 add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
2822 }
2823 trigger_error('NO_AUTH_ADMIN');
2824 }
2825
2826 $password = request_var('password_' . $credential, '', true);
2827 }
2828 else
2829 {
2830 $password = request_var('password', '', true);
2831 }
2832
2833 $username = request_var('username', '', true);
2834 $autologin = (!empty($_POST['autologin'])) ? true : false;
2835 $viewonline = (!empty($_POST['viewonline'])) ? 0 : 1;
2836 $admin = ($admin) ? 1 : 0;
2837 $viewonline = ($admin) ? $user->data['session_viewonline'] : $viewonline;
2838
2839 // Check if the supplied username is equal to the one stored within the database if re-authenticating
2840 if ($admin && utf8_clean_string($username) != utf8_clean_string($user->data['username']))
2841 {
2842 // We log the attempt to use a different username...
2843 add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
2844 trigger_error('NO_AUTH_ADMIN_USER_DIFFER');
2845 }
2846
2847 // If authentication is successful we redirect user to previous page
2848 $result = $auth->login($username, $password, $autologin, $viewonline, $admin);
2849
2850 // If admin authentication and login, we will log if it was a success or not...
2851 // We also break the operation on the first non-success login - it could be argued that the user already knows
2852 if ($admin)
2853 {
2854 if ($result['status'] == LOGIN_SUCCESS)
2855 {
2856 add_log('admin', 'LOG_ADMIN_AUTH_SUCCESS');
2857 }
2858 else
2859 {
2860 // Only log the failed attempt if a real user tried to.
2861 // anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions
2862 if ($user->data['is_registered'])
2863 {
2864 add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
2865 }
2866 }
2867 }
2868
2869 // The result parameter is always an array, holding the relevant information...
2870 if ($result['status'] == LOGIN_SUCCESS)
2871 {
2872 $redirect = request_var('redirect', "{$phpbb_root_path}index.$phpEx");
2873 $message = ($l_success) ? $l_success : $user->lang['LOGIN_REDIRECT'];
2874 $l_redirect = ($admin) ? $user->lang['PROCEED_TO_ACP'] : (($redirect === "{$phpbb_root_path}index.$phpEx" || $redirect === "index.$phpEx") ? $user->lang['RETURN_INDEX'] : $user->lang['RETURN_PAGE']);
2875
2876 // append/replace SID (may change during the session for AOL users)
2877 $redirect = reapply_sid($redirect);
2878
2879 // Special case... the user is effectively banned, but we allow founders to login
2880 if (defined('IN_CHECK_BAN') && $result['user_row']['user_type'] != USER_FOUNDER)
2881 {
2882 return;
2883 }
2884
2885 $redirect = meta_refresh(3, $redirect);
2886 trigger_error($message . '<br /><br />' . sprintf($l_redirect, '<a href="' . $redirect . '">', '</a>'));
2887 }
2888
2889 // Something failed, determine what...
2890 if ($result['status'] == LOGIN_BREAK)
2891 {
2892 trigger_error($result['error_msg']);
2893 }
2894
2895 // Special cases... determine
2896 switch ($result['status'])
2897 {
2898 case LOGIN_ERROR_ATTEMPTS:
2899
2900 $captcha = phpbb_captcha_factory::get_instance($config['captcha_plugin']);
2901 $captcha->init(CONFIRM_LOGIN);
2902 // $captcha->reset();
2903
2904 $template->assign_vars(array(
2905 'CAPTCHA_TEMPLATE' => $captcha->get_template(),
2906 ));
2907
2908 $err = $user->lang[$result['error_msg']];
2909 break;
2910
2911 case LOGIN_ERROR_PASSWORD_CONVERT:
2912 $err = sprintf(
2913 $user->lang[$result['error_msg']],
2914 ($config['email_enable']) ? '<a href="' . append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=sendpassword') . '">' : '',
2915 ($config['email_enable']) ? '</a>' : '',
2916 ($config['board_contact']) ? '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">' : '',
2917 ($config['board_contact']) ? '</a>' : ''
2918 );
2919 break;
2920
2921 // Username, password, etc...
2922 default:
2923 $err = $user->lang[$result['error_msg']];
2924
2925 // Assign admin contact to some error messages
2926 if ($result['error_msg'] == 'LOGIN_ERROR_USERNAME' || $result['error_msg'] == 'LOGIN_ERROR_PASSWORD')
2927 {
2928 $err = (!$config['board_contact']) ? sprintf($user->lang[$result['error_msg']], '', '') : sprintf($user->lang[$result['error_msg']], '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">', '</a>');
2929 }
2930
2931 break;
2932 }
2933 }
2934
2935 // Assign credential for username/password pair
2936 $credential = ($admin) ? md5(unique_id()) : false;
2937
2938 $s_hidden_fields = array(
2939 'sid' => $user->session_id,
2940 );
2941
2942 if ($redirect)
2943 {
2944 $s_hidden_fields['redirect'] = $redirect;
2945 }
2946
2947 if ($admin)
2948 {
2949 $s_hidden_fields['credential'] = $credential;
2950 }
2951
2952 $s_hidden_fields = build_hidden_fields($s_hidden_fields);
2953
2954 $template->assign_vars(array(
2955 'LOGIN_ERROR' => $err,
2956 'LOGIN_EXPLAIN' => $l_explain,
2957
2958 'U_SEND_PASSWORD' => ($config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=sendpassword') : '',
2959 'U_RESEND_ACTIVATION' => ($config['require_activation'] == USER_ACTIVATION_SELF && $config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=resend_act') : '',
2960 'U_TERMS_USE' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=terms'),
2961 'U_PRIVACY' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=privacy'),
2962
2963 'S_DISPLAY_FULL_LOGIN' => ($s_display) ? true : false,
2964 'S_HIDDEN_FIELDS' => $s_hidden_fields,
2965
2966 'S_ADMIN_AUTH' => $admin,
2967 'USERNAME' => ($admin) ? $user->data['username'] : '',
2968
2969 'USERNAME_CREDENTIAL' => 'username',
2970 'PASSWORD_CREDENTIAL' => ($admin) ? 'password_' . $credential : 'password',
2971 ));
2972
2973 page_header($user->lang['LOGIN'], false);
2974
2975 $template->set_filenames(array(
2976 'body' => 'login_body.html')
2977 );
2978 make_jumpbox(append_sid("{$phpbb_root_path}viewforum.$phpEx"));
2979
2980 page_footer();
2981}
2982
2983/**
2984* Generate forum login box
2985*/
2986function login_forum_box($forum_data)
2987{
2988 global $db, $config, $user, $template, $phpEx;
2989
2990 $password = request_var('password', '', true);
2991
2992 $sql = 'SELECT forum_id
2993 FROM ' . FORUMS_ACCESS_TABLE . '
2994 WHERE forum_id = ' . $forum_data['forum_id'] . '
2995 AND user_id = ' . $user->data['user_id'] . "
2996 AND session_id = '" . $db->sql_escape($user->session_id) . "'";
2997 $result = $db->sql_query($sql);
2998 $row = $db->sql_fetchrow($result);
2999 $db->sql_freeresult($result);
3000
3001 if ($row)
3002 {
3003 return true;
3004 }
3005
3006 if ($password)
3007 {
3008 // Remove expired authorised sessions
3009 $sql = 'SELECT f.session_id
3010 FROM ' . FORUMS_ACCESS_TABLE . ' f
3011 LEFT JOIN ' . SESSIONS_TABLE . ' s ON (f.session_id = s.session_id)
3012 WHERE s.session_id IS NULL';
3013 $result = $db->sql_query($sql);
3014
3015 if ($row = $db->sql_fetchrow($result))
3016 {
3017 $sql_in = array();
3018 do
3019 {
3020 $sql_in[] = (string) $row['session_id'];
3021 }
3022 while ($row = $db->sql_fetchrow($result));
3023
3024 // Remove expired sessions
3025 $sql = 'DELETE FROM ' . FORUMS_ACCESS_TABLE . '
3026 WHERE ' . $db->sql_in_set('session_id', $sql_in);
3027 $db->sql_query($sql);
3028 }
3029 $db->sql_freeresult($result);
3030
3031 if (phpbb_check_hash($password, $forum_data['forum_password']))
3032 {
3033 $sql_ary = array(
3034 'forum_id' => (int) $forum_data['forum_id'],
3035 'user_id' => (int) $user->data['user_id'],
3036 'session_id' => (string) $user->session_id,
3037 );
3038
3039 $db->sql_query('INSERT INTO ' . FORUMS_ACCESS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
3040
3041 return true;
3042 }
3043
3044 $template->assign_var('LOGIN_ERROR', $user->lang['WRONG_PASSWORD']);
3045 }
3046
3047 page_header($user->lang['LOGIN'], false);
3048
3049 $template->assign_vars(array(
3050 'S_LOGIN_ACTION' => build_url(array('f')),
3051 'S_HIDDEN_FIELDS' => build_hidden_fields(array('f' => $forum_data['forum_id'])))
3052 );
3053
3054 $template->set_filenames(array(
3055 'body' => 'login_forum.html')
3056 );
3057
3058 page_footer();
3059}
3060
3061// Little helpers
3062
3063/**
3064* Little helper for the build_hidden_fields function
3065*/
3066function _build_hidden_fields($key, $value, $specialchar, $stripslashes)
3067{
3068 $hidden_fields = '';
3069
3070 if (!is_array($value))
3071 {
3072 $value = ($stripslashes) ? stripslashes($value) : $value;
3073 $value = ($specialchar) ? htmlspecialchars($value, ENT_COMPAT, 'UTF-8') : $value;
3074
3075 $hidden_fields .= '<input type="hidden" name="' . $key . '" value="' . $value . '" />' . "\n";
3076 }
3077 else
3078 {
3079 foreach ($value as $_key => $_value)
3080 {
3081 $_key = ($stripslashes) ? stripslashes($_key) : $_key;
3082 $_key = ($specialchar) ? htmlspecialchars($_key, ENT_COMPAT, 'UTF-8') : $_key;
3083
3084 $hidden_fields .= _build_hidden_fields($key . '[' . $_key . ']', $_value, $specialchar, $stripslashes);
3085 }
3086 }
3087
3088 return $hidden_fields;
3089}
3090
3091/**
3092* Build simple hidden fields from array
3093*
3094* @param array $field_ary an array of values to build the hidden field from
3095* @param bool $specialchar if true, keys and values get specialchared
3096* @param bool $stripslashes if true, keys and values get stripslashed
3097*
3098* @return string the hidden fields
3099*/
3100function build_hidden_fields($field_ary, $specialchar = false, $stripslashes = false)
3101{
3102 $s_hidden_fields = '';
3103
3104 foreach ($field_ary as $name => $vars)
3105 {
3106 $name = ($stripslashes) ? stripslashes($name) : $name;
3107 $name = ($specialchar) ? htmlspecialchars($name, ENT_COMPAT, 'UTF-8') : $name;
3108
3109 $s_hidden_fields .= _build_hidden_fields($name, $vars, $specialchar, $stripslashes);
3110 }
3111
3112 return $s_hidden_fields;
3113}
3114
3115/**
3116* Parse cfg file
3117*/
3118function parse_cfg_file($filename, $lines = false)
3119{
3120 $parsed_items = array();
3121
3122 if ($lines === false)
3123 {
3124 $lines = file($filename);
3125 }
3126
3127 foreach ($lines as $line)
3128 {
3129 $line = trim($line);
3130
3131 if (!$line || $line[0] == '#' || ($delim_pos = strpos($line, '=')) === false)
3132 {
3133 continue;
3134 }
3135
3136 // Determine first occurrence, since in values the equal sign is allowed
3137 $key = strtolower(trim(substr($line, 0, $delim_pos)));
3138 $value = trim(substr($line, $delim_pos + 1));
3139
3140 if (in_array($value, array('off', 'false', '0')))
3141 {
3142 $value = false;
3143 }
3144 else if (in_array($value, array('on', 'true', '1')))
3145 {
3146 $value = true;
3147 }
3148 else if (!trim($value))
3149 {
3150 $value = '';
3151 }
3152 else if (($value[0] == "'" && $value[sizeof($value) - 1] == "'") || ($value[0] == '"' && $value[sizeof($value) - 1] == '"'))
3153 {
3154 $value = substr($value, 1, sizeof($value)-2);
3155 }
3156
3157 $parsed_items[$key] = $value;
3158 }
3159
3160 return $parsed_items;
3161}
3162
3163/**
3164* Add log event
3165*/
3166function add_log()
3167{
3168 global $db, $user;
3169
3170 // In phpBB 3.1.x i want to have logging in a class to be able to control it
3171 // For now, we need a quite hakish approach to circumvent logging for some actions
3172 // @todo implement cleanly
3173 if (!empty($GLOBALS['skip_add_log']))
3174 {
3175 return false;
3176 }
3177
3178 $args = func_get_args();
3179
3180 $mode = array_shift($args);
3181 $reportee_id = ($mode == 'user') ? intval(array_shift($args)) : '';
3182 $forum_id = ($mode == 'mod') ? intval(array_shift($args)) : '';
3183 $topic_id = ($mode == 'mod') ? intval(array_shift($args)) : '';
3184 $action = array_shift($args);
3185 $data = (!sizeof($args)) ? '' : serialize($args);
3186
3187 $sql_ary = array(
3188 'user_id' => (empty($user->data)) ? ANONYMOUS : $user->data['user_id'],
3189 'log_ip' => $user->ip,
3190 'log_time' => time(),
3191 'log_operation' => $action,
3192 'log_data' => $data,
3193 );
3194
3195 switch ($mode)
3196 {
3197 case 'admin':
3198 $sql_ary['log_type'] = LOG_ADMIN;
3199 break;
3200
3201 case 'mod':
3202 $sql_ary += array(
3203 'log_type' => LOG_MOD,
3204 'forum_id' => $forum_id,
3205 'topic_id' => $topic_id
3206 );
3207 break;
3208
3209 case 'user':
3210 $sql_ary += array(
3211 'log_type' => LOG_USERS,
3212 'reportee_id' => $reportee_id
3213 );
3214 break;
3215
3216 case 'critical':
3217 $sql_ary['log_type'] = LOG_CRITICAL;
3218 break;
3219
3220 default:
3221 return false;
3222 }
3223
3224 $db->sql_query('INSERT INTO ' . LOG_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
3225
3226 return $db->sql_nextid();
3227}
3228
3229/**
3230* Return a nicely formatted backtrace (parts from the php manual by diz at ysagoon dot com)
3231*/
3232function get_backtrace()
3233{
3234 global $phpbb_root_path;
3235
3236 $output = '<div style="font-family: monospace;">';
3237 $backtrace = debug_backtrace();
3238 $path = phpbb_realpath($phpbb_root_path);
3239
3240 foreach ($backtrace as $number => $trace)
3241 {
3242 // We skip the first one, because it only shows this file/function
3243 if ($number == 0)
3244 {
3245 continue;
3246 }
3247
3248 // Strip the current directory from path
3249 if (empty($trace['file']))
3250 {
3251 $trace['file'] = '';
3252 }
3253 else
3254 {
3255 $trace['file'] = str_replace(array($path, '\\'), array('', '/'), $trace['file']);
3256 $trace['file'] = substr($trace['file'], 1);
3257 }
3258 $args = array();
3259
3260 // If include/require/include_once is not called, do not show arguments - they may contain sensible information
3261 if (!in_array($trace['function'], array('include', 'require', 'include_once')))
3262 {
3263 unset($trace['args']);
3264 }
3265 else
3266 {
3267 // Path...
3268 if (!empty($trace['args'][0]))
3269 {
3270 $argument = htmlspecialchars($trace['args'][0]);
3271 $argument = str_replace(array($path, '\\'), array('', '/'), $argument);
3272 $argument = substr($argument, 1);
3273 $args[] = "'{$argument}'";
3274 }
3275 }
3276
3277 $trace['class'] = (!isset($trace['class'])) ? '' : $trace['class'];
3278 $trace['type'] = (!isset($trace['type'])) ? '' : $trace['type'];
3279
3280 $output .= '<br />';
3281 $output .= '<b>FILE:</b> ' . htmlspecialchars($trace['file']) . '<br />';
3282 $output .= '<b>LINE:</b> ' . ((!empty($trace['line'])) ? $trace['line'] : '') . '<br />';
3283
3284 $output .= '<b>CALL:</b> ' . htmlspecialchars($trace['class'] . $trace['type'] . $trace['function']) . '(' . ((sizeof($args)) ? implode(', ', $args) : '') . ')<br />';
3285 }
3286 $output .= '</div>';
3287 return $output;
3288}
3289
3290/**
3291* This function returns a regular expression pattern for commonly used expressions
3292* Use with / as delimiter for email mode and # for url modes
3293* mode can be: email|bbcode_htm|url|url_inline|www_url|www_url_inline|relative_url|relative_url_inline|ipv4|ipv6
3294*/
3295function get_preg_expression($mode)
3296{
3297 switch ($mode)
3298 {
3299 case 'email':
3300 return '(?:[a-z0-9\'\.\-_\+\|]++|&amp;)+@[a-z0-9\-]+\.(?:[a-z0-9\-]+\.)*[a-z]+';
3301 break;
3302
3303 case 'bbcode_htm':
3304 return array(
3305 '#<!\-\- e \-\-><a href="mailto:(.*?)">.*?</a><!\-\- e \-\->#',
3306 '#<!\-\- l \-\-><a (?:class="[\w-]+" )?href="(.*?)(?:(&amp;|\?)sid=[0-9a-f]{32})?">.*?</a><!\-\- l \-\->#',
3307 '#<!\-\- ([mw]) \-\-><a (?:class="[\w-]+" )?href="(.*?)">.*?</a><!\-\- \1 \-\->#',
3308 '#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/.*? \/><!\-\- s\1 \-\->#',
3309 '#<!\-\- .*? \-\->#s',
3310 '#<.*?>#s',
3311 );
3312 break;
3313
3314 // Whoa these look impressive!
3315 // The code to generate the following two regular expressions which match valid IPv4/IPv6 addresses
3316 // can be found in the develop directory
3317 case 'ipv4':
3318 return '#^(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$#';
3319 break;
3320
3321 case 'ipv6':
3322 return '#^(?:(?:(?:[\dA-F]{1,4}:){6}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:::(?:[\dA-F]{1,4}:){5}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:):(?:[\dA-F]{1,4}:){4}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,2}:(?:[\dA-F]{1,4}:){3}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,3}:(?:[\dA-F]{1,4}:){2}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,4}:(?:[\dA-F]{1,4}:)(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,5}:(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,6}:[\dA-F]{1,4})|(?:(?:[\dA-F]{1,4}:){1,7}:))$#i';
3323 break;
3324
3325 case 'url':
3326 case 'url_inline':
3327 $inline = ($mode == 'url') ? ')' : '';
3328 $scheme = ($mode == 'url') ? '[a-z\d+\-.]' : '[a-z\d+]'; // avoid automatic parsing of "word" in "last word.http://..."
3329 // generated with regex generation file in the develop folder
3330 return "[a-z]$scheme*:/{2}(?:(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})+|[0-9.]+|\[[a-z0-9.]+:[a-z0-9.]+:[a-z0-9.:]+\])(?::\d*)?(?:/(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?";
3331 break;
3332
3333 case 'www_url':
3334 case 'www_url_inline':
3335 $inline = ($mode == 'www_url') ? ')' : '';
3336 return "www\.(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})+(?::\d*)?(?:/(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?";
3337 break;
3338
3339 case 'relative_url':
3340 case 'relative_url_inline':
3341 $inline = ($mode == 'relative_url') ? ')' : '';
3342 return "(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*(?:/(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?";
3343 break;
3344 }
3345
3346 return '';
3347}
3348
3349/**
3350* Returns the first block of the specified IPv6 address and as many additional
3351* ones as specified in the length paramater.
3352* If length is zero, then an empty string is returned.
3353* If length is greater than 3 the complete IP will be returned
3354*/
3355function short_ipv6($ip, $length)
3356{
3357 if ($length < 1)
3358 {
3359 return '';
3360 }
3361
3362 // extend IPv6 addresses
3363 $blocks = substr_count($ip, ':') + 1;
3364 if ($blocks < 9)
3365 {
3366 $ip = str_replace('::', ':' . str_repeat('0000:', 9 - $blocks), $ip);
3367 }
3368 if ($ip[0] == ':')
3369 {
3370 $ip = '0000' . $ip;
3371 }
3372 if ($length < 4)
3373 {
3374 $ip = implode(':', array_slice(explode(':', $ip), 0, 1 + $length));
3375 }
3376
3377 return $ip;
3378}
3379
3380/**
3381* Wrapper for php's checkdnsrr function.
3382*
3383* The windows failover is from the php manual
3384* Please make sure to check the return value for === true and === false, since NULL could
3385* be returned too.
3386*
3387* @return true if entry found, false if not, NULL if this function is not supported by this environment
3388*/
3389function phpbb_checkdnsrr($host, $type = '')
3390{
3391 $type = (!$type) ? 'MX' : $type;
3392
3393 if (DIRECTORY_SEPARATOR == '\\')
3394 {
3395 if (!function_exists('exec'))
3396 {
3397 return NULL;
3398 }
3399
3400 // @exec('nslookup -retry=1 -timout=1 -type=' . escapeshellarg($type) . ' ' . escapeshellarg($host), $output);
3401 @exec('nslookup -type=' . escapeshellarg($type) . ' ' . escapeshellarg($host) . '.', $output);
3402
3403 // If output is empty, the nslookup failed
3404 if (empty($output))
3405 {
3406 return NULL;
3407 }
3408
3409 foreach ($output as $line)
3410 {
3411 if (!trim($line))
3412 {
3413 continue;
3414 }
3415
3416 // Valid records begin with host name:
3417 if (strpos($line, $host) === 0)
3418 {
3419 return true;
3420 }
3421 }
3422
3423 return false;
3424 }
3425 else if (function_exists('checkdnsrr'))
3426 {
3427 // The dot indicates to search the DNS root (helps those having DNS prefixes on the same domain)
3428 return (checkdnsrr($host . '.', $type)) ? true : false;
3429 }
3430
3431 return NULL;
3432}
3433
3434// Handler, header and footer
3435
3436/**
3437* Error and message handler, call with trigger_error if reqd
3438*/
3439function msg_handler($errno, $msg_text, $errfile, $errline)
3440{
3441 global $cache, $db, $auth, $template, $config, $user;
3442 global $phpEx, $phpbb_root_path, $msg_title, $msg_long_text;
3443
3444 // Do not display notices if we suppress them via @
3445 if (error_reporting() == 0 && $errno != E_USER_ERROR && $errno != E_USER_WARNING && $errno != E_USER_NOTICE)
3446 {
3447 return;
3448 }
3449
3450 // Message handler is stripping text. In case we need it, we are possible to define long text...
3451 if (isset($msg_long_text) && $msg_long_text && !$msg_text)
3452 {
3453 $msg_text = $msg_long_text;
3454 }
3455
3456 if (!defined('E_DEPRECATED'))
3457 {
3458 define('E_DEPRECATED', 8192);
3459 }
3460
3461 switch ($errno)
3462 {
3463 case E_NOTICE:
3464 case E_WARNING:
3465
3466 // Check the error reporting level and return if the error level does not match
3467 // If DEBUG is defined the default level is E_ALL
3468 if (($errno & ((defined('DEBUG')) ? E_ALL : error_reporting())) == 0)
3469 {
3470 return;
3471 }
3472
3473 if (strpos($errfile, 'cache') === false && strpos($errfile, 'template.') === false)
3474 {
3475 // flush the content, else we get a white page if output buffering is on
3476 if ((int) @ini_get('output_buffering') === 1 || strtolower(@ini_get('output_buffering')) === 'on')
3477 {
3478 @ob_flush();
3479 }
3480
3481 // Another quick fix for those having gzip compression enabled, but do not flush if the coder wants to catch "something". ;)
3482 if (!empty($config['gzip_compress']))
3483 {
3484 if (@extension_loaded('zlib') && !headers_sent() && !ob_get_level())
3485 {
3486 @ob_flush();
3487 }
3488 }
3489
3490 // remove complete path to installation, with the risk of changing backslashes meant to be there
3491 $errfile = str_replace(array(phpbb_realpath($phpbb_root_path), '\\'), array('', '/'), $errfile);
3492 $msg_text = str_replace(array(phpbb_realpath($phpbb_root_path), '\\'), array('', '/'), $msg_text);
3493 echo '<b>[phpBB Debug] PHP Notice</b>: in file <b>' . $errfile . '</b> on line <b>' . $errline . '</b>: <b>' . $msg_text . '</b><br />' . "\n";
3494
3495 // we are writing an image - the user won't see the debug, so let's place it in the log
3496 if (defined('IMAGE_OUTPUT') || defined('IN_CRON'))
3497 {
3498 add_log('critical', 'LOG_IMAGE_GENERATION_ERROR', $errfile, $errline, $msg_text);
3499 }
3500 // echo '<br /><br />BACKTRACE<br />' . get_backtrace() . '<br />' . "\n";
3501 }
3502
3503 return;
3504
3505 break;
3506
3507 case E_USER_ERROR:
3508
3509 if (!empty($user) && !empty($user->lang))
3510 {
3511 $msg_text = (!empty($user->lang[$msg_text])) ? $user->lang[$msg_text] : $msg_text;
3512 $msg_title = (!isset($msg_title)) ? $user->lang['GENERAL_ERROR'] : ((!empty($user->lang[$msg_title])) ? $user->lang[$msg_title] : $msg_title);
3513
3514 $l_return_index = sprintf($user->lang['RETURN_INDEX'], '<a href="' . $phpbb_root_path . '">', '</a>');
3515 $l_notify = '';
3516
3517 if (!empty($config['board_contact']))
3518 {
3519 $l_notify = '<p>' . sprintf($user->lang['NOTIFY_ADMIN_EMAIL'], $config['board_contact']) . '</p>';
3520 }
3521 }
3522 else
3523 {
3524 $msg_title = 'General Error';
3525 $l_return_index = '<a href="' . $phpbb_root_path . '">Return to index page</a>';
3526 $l_notify = '';
3527
3528 if (!empty($config['board_contact']))
3529 {
3530 $l_notify = '<p>Please notify the board administrator or webmaster: <a href="mailto:' . $config['board_contact'] . '">' . $config['board_contact'] . '</a></p>';
3531 }
3532 }
3533
3534 if ((defined('DEBUG') || defined('IN_CRON') || defined('IMAGE_OUTPUT')) && isset($db))
3535 {
3536 // let's avoid loops
3537 $db->sql_return_on_error(true);
3538 add_log('critical', 'LOG_GENERAL_ERROR', $msg_title, $msg_text);
3539 $db->sql_return_on_error(false);
3540 }
3541
3542 // Do not send 200 OK, but service unavailable on errors
3543 header('HTTP/1.1 503 Service Unavailable');
3544
3545 garbage_collection();
3546
3547 // Try to not call the adm page data...
3548
3549 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
3550 echo '<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr">';
3551 echo '<head>';
3552 echo '<meta http-equiv="content-type" content="text/html; charset=utf-8" />';
3553 echo '<title>' . $msg_title . '</title>';
3554 echo '<style type="text/css">' . "\n" . '/* <![CDATA[ */' . "\n";
3555 echo '* { margin: 0; padding: 0; } html { font-size: 100%; height: 100%; margin-bottom: 1px; background-color: #E4EDF0; } body { font-family: "Lucida Grande", Verdana, Helvetica, Arial, sans-serif; color: #536482; background: #E4EDF0; font-size: 62.5%; margin: 0; } ';
3556 echo 'a:link, a:active, a:visited { color: #006699; text-decoration: none; } a:hover { color: #DD6900; text-decoration: underline; } ';
3557 echo '#wrap { padding: 0 20px 15px 20px; min-width: 615px; } #page-header { text-align: right; height: 40px; } #page-footer { clear: both; font-size: 1em; text-align: center; } ';
3558 echo '.panel { margin: 4px 0; background-color: #FFFFFF; border: solid 1px #A9B8C2; } ';
3559 echo '#errorpage #page-header a { font-weight: bold; line-height: 6em; } #errorpage #content { padding: 10px; } #errorpage #content h1 { line-height: 1.2em; margin-bottom: 0; color: #DF075C; } ';
3560 echo '#errorpage #content div { margin-top: 20px; margin-bottom: 5px; border-bottom: 1px solid #CCCCCC; padding-bottom: 5px; color: #333333; font: bold 1.2em "Lucida Grande", Arial, Helvetica, sans-serif; text-decoration: none; line-height: 120%; text-align: left; } ';
3561 echo "\n" . '/* ]]> */' . "\n";
3562 echo '</style>';
3563 echo '</head>';
3564 echo '<body id="errorpage">';
3565 echo '<div id="wrap">';
3566 echo ' <div id="page-header">';
3567 echo ' ' . $l_return_index;
3568 echo ' </div>';
3569 echo ' <div id="acp">';
3570 echo ' <div class="panel">';
3571 echo ' <div id="content">';
3572 echo ' <h1>' . $msg_title . '</h1>';
3573
3574 echo ' <div>' . $msg_text . '</div>';
3575
3576 echo $l_notify;
3577
3578 echo ' </div>';
3579 echo ' </div>';
3580 echo ' </div>';
3581 echo ' <div id="page-footer">';
3582 echo ' Powered by phpBB &copy; 2000, 2002, 2005, 2007 <a href="http://www.phpbb.com/">phpBB Group</a>';
3583 echo ' </div>';
3584 echo '</div>';
3585 echo '</body>';
3586 echo '</html>';
3587
3588 exit_handler();
3589
3590 // On a fatal error (and E_USER_ERROR *is* fatal) we never want other scripts to continue and force an exit here.
3591 exit;
3592 break;
3593
3594 case E_USER_WARNING:
3595 case E_USER_NOTICE:
3596
3597 define('IN_ERROR_HANDLER', true);
3598
3599 if (empty($user->data))
3600 {
3601 $user->session_begin();
3602 }
3603
3604 // We re-init the auth array to get correct results on login/logout
3605 $auth->acl($user->data);
3606
3607 if (empty($user->lang))
3608 {
3609 $user->setup();
3610 }
3611
3612 $msg_text = (!empty($user->lang[$msg_text])) ? $user->lang[$msg_text] : $msg_text;
3613 $msg_title = (!isset($msg_title)) ? $user->lang['INFORMATION'] : ((!empty($user->lang[$msg_title])) ? $user->lang[$msg_title] : $msg_title);
3614
3615 if (!defined('HEADER_INC'))
3616 {
3617 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
3618 {
3619 adm_page_header($msg_title);
3620 }
3621 else
3622 {
3623 page_header($msg_title, false);
3624 }
3625 }
3626
3627 $template->set_filenames(array(
3628 'body' => 'message_body.html')
3629 );
3630
3631 $template->assign_vars(array(
3632 'MESSAGE_TITLE' => $msg_title,
3633 'MESSAGE_TEXT' => $msg_text,
3634 'S_USER_WARNING' => ($errno == E_USER_WARNING) ? true : false,
3635 'S_USER_NOTICE' => ($errno == E_USER_NOTICE) ? true : false)
3636 );
3637
3638 // We do not want the cron script to be called on error messages
3639 define('IN_CRON', true);
3640
3641 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
3642 {
3643 adm_page_footer();
3644 }
3645 else
3646 {
3647 page_footer();
3648 }
3649
3650 exit_handler();
3651 break;
3652
3653 // PHP4 compatibility
3654 case E_DEPRECATED:
3655 return true;
3656 break;
3657 }
3658
3659 // If we notice an error not handled here we pass this back to PHP by returning false
3660 // This may not work for all php versions
3661 return false;
3662}
3663
3664/**
3665* Queries the session table to get information about online guests
3666* @param int $item_id Limits the search to the item with this id
3667* @param string $item The name of the item which is stored in the session table as session_{$item}_id
3668* @return int The number of active distinct guest sessions
3669*/
3670function obtain_guest_count($item_id = 0, $item = 'forum')
3671{
3672 global $db, $config;
3673
3674 if ($item_id)
3675 {
3676 $reading_sql = ' AND s.session_' . $item . '_id = ' . (int) $item_id;
3677 }
3678 else
3679 {
3680 $reading_sql = '';
3681 }
3682 $time = (time() - (intval($config['load_online_time']) * 60));
3683
3684 // Get number of online guests
3685
3686 if ($db->sql_layer === 'sqlite')
3687 {
3688 $sql = 'SELECT COUNT(session_ip) as num_guests
3689 FROM (
3690 SELECT DISTINCT s.session_ip
3691 FROM ' . SESSIONS_TABLE . ' s
3692 WHERE s.session_user_id = ' . ANONYMOUS . '
3693 AND s.session_time >= ' . ($time - ((int) ($time % 60))) .
3694 $reading_sql .
3695 ')';
3696 }
3697 else
3698 {
3699 $sql = 'SELECT COUNT(DISTINCT s.session_ip) as num_guests
3700 FROM ' . SESSIONS_TABLE . ' s
3701 WHERE s.session_user_id = ' . ANONYMOUS . '
3702 AND s.session_time >= ' . ($time - ((int) ($time % 60))) .
3703 $reading_sql;
3704 }
3705 $result = $db->sql_query($sql);
3706 $guests_online = (int) $db->sql_fetchfield('num_guests');
3707 $db->sql_freeresult($result);
3708
3709 return $guests_online;
3710}
3711
3712/**
3713* Queries the session table to get information about online users
3714* @param int $item_id Limits the search to the item with this id
3715* @param string $item The name of the item which is stored in the session table as session_{$item}_id
3716* @return array An array containing the ids of online, hidden and visible users, as well as statistical info
3717*/
3718function obtain_users_online($item_id = 0, $item = 'forum')
3719{
3720 global $db, $config, $user;
3721
3722 $reading_sql = '';
3723 if ($item_id !== 0)
3724 {
3725 $reading_sql = ' AND s.session_' . $item . '_id = ' . (int) $item_id;
3726 }
3727
3728 $online_users = array(
3729 'online_users' => array(),
3730 'hidden_users' => array(),
3731 'total_online' => 0,
3732 'visible_online' => 0,
3733 'hidden_online' => 0,
3734 'guests_online' => 0,
3735 );
3736
3737 if ($config['load_online_guests'])
3738 {
3739 $online_users['guests_online'] = obtain_guest_count($item_id, $item);
3740 }
3741
3742 // a little discrete magic to cache this for 30 seconds
3743 $time = (time() - (intval($config['load_online_time']) * 60));
3744
3745 $sql = 'SELECT s.session_user_id, s.session_ip, s.session_viewonline
3746 FROM ' . SESSIONS_TABLE . ' s
3747 WHERE s.session_time >= ' . ($time - ((int) ($time % 30))) .
3748 $reading_sql .
3749 ' AND s.session_user_id <> ' . ANONYMOUS;
3750 $result = $db->sql_query($sql);
3751
3752 while ($row = $db->sql_fetchrow($result))
3753 {
3754 // Skip multiple sessions for one user
3755 if (!isset($online_users['online_users'][$row['session_user_id']]))
3756 {
3757 $online_users['online_users'][$row['session_user_id']] = (int) $row['session_user_id'];
3758 if ($row['session_viewonline'])
3759 {
3760 $online_users['visible_online']++;
3761 }
3762 else
3763 {
3764 $online_users['hidden_users'][$row['session_user_id']] = (int) $row['session_user_id'];
3765 $online_users['hidden_online']++;
3766 }
3767 }
3768 }
3769 $online_users['total_online'] = $online_users['guests_online'] + $online_users['visible_online'] + $online_users['hidden_online'];
3770 $db->sql_freeresult($result);
3771
3772 return $online_users;
3773}
3774
3775/**
3776* Uses the result of obtain_users_online to generate a localized, readable representation.
3777* @param mixed $online_users result of obtain_users_online - array with user_id lists for total, hidden and visible users, and statistics
3778* @param int $item_id Indicate that the data is limited to one item and not global
3779* @param string $item The name of the item which is stored in the session table as session_{$item}_id
3780* @return array An array containing the string for output to the template
3781*/
3782function obtain_users_online_string($online_users, $item_id = 0, $item = 'forum')
3783{
3784 global $config, $db, $user, $auth;
3785
3786 $user_online_link = $online_userlist = '';
3787 // Need caps version of $item for language-strings
3788 $item_caps = strtoupper($item);
3789
3790 if (sizeof($online_users['online_users']))
3791 {
3792 $sql = 'SELECT username, username_clean, user_id, user_type, user_allow_viewonline, user_colour
3793 FROM ' . USERS_TABLE . '
3794 WHERE ' . $db->sql_in_set('user_id', $online_users['online_users']) . '
3795 ORDER BY username_clean ASC';
3796 $result = $db->sql_query($sql);
3797
3798 while ($row = $db->sql_fetchrow($result))
3799 {
3800 // User is logged in and therefore not a guest
3801 if ($row['user_id'] != ANONYMOUS)
3802 {
3803 if (isset($online_users['hidden_users'][$row['user_id']]))
3804 {
3805 $row['username'] = '<em>' . $row['username'] . '</em>';
3806 }
3807
3808 if (!isset($online_users['hidden_users'][$row['user_id']]) || $auth->acl_get('u_viewonline'))
3809 {
3810 $user_online_link = get_username_string(($row['user_type'] <> USER_IGNORE) ? 'full' : 'no_profile', $row['user_id'], $row['username'], $row['user_colour']);
3811 $online_userlist .= ($online_userlist != '') ? ', ' . $user_online_link : $user_online_link;
3812 }
3813 }
3814 }
3815 $db->sql_freeresult($result);
3816 }
3817
3818 if (!$online_userlist)
3819 {
3820 $online_userlist = $user->lang['NO_ONLINE_USERS'];
3821 }
3822
3823 if ($item_id === 0)
3824 {
3825 $online_userlist = $user->lang['REGISTERED_USERS'] . ' ' . $online_userlist;
3826 }
3827 else if ($config['load_online_guests'])
3828 {
3829 $l_online = ($online_users['guests_online'] === 1) ? $user->lang['BROWSING_' . $item_caps . '_GUEST'] : $user->lang['BROWSING_' . $item_caps . '_GUESTS'];
3830 $online_userlist = sprintf($l_online, $online_userlist, $online_users['guests_online']);
3831 }
3832 else
3833 {
3834 $online_userlist = sprintf($user->lang['BROWSING_' . $item_caps], $online_userlist);
3835 }
3836 // Build online listing
3837 $vars_online = array(
3838 'ONLINE' => array('total_online', 'l_t_user_s', 0),
3839 'REG' => array('visible_online', 'l_r_user_s', !$config['load_online_guests']),
3840 'HIDDEN' => array('hidden_online', 'l_h_user_s', $config['load_online_guests']),
3841 'GUEST' => array('guests_online', 'l_g_user_s', 0)
3842 );
3843
3844 foreach ($vars_online as $l_prefix => $var_ary)
3845 {
3846 if ($var_ary[2])
3847 {
3848 $l_suffix = '_AND';
3849 }
3850 else
3851 {
3852 $l_suffix = '';
3853 }
3854 switch ($online_users[$var_ary[0]])
3855 {
3856 case 0:
3857 ${$var_ary[1]} = $user->lang[$l_prefix . '_USERS_ZERO_TOTAL' . $l_suffix];
3858 break;
3859
3860 case 1:
3861 ${$var_ary[1]} = $user->lang[$l_prefix . '_USER_TOTAL' . $l_suffix];
3862 break;
3863
3864 default:
3865 ${$var_ary[1]} = $user->lang[$l_prefix . '_USERS_TOTAL' . $l_suffix];
3866 break;
3867 }
3868 }
3869 unset($vars_online);
3870
3871 $l_online_users = sprintf($l_t_user_s, $online_users['total_online']);
3872 $l_online_users .= sprintf($l_r_user_s, $online_users['visible_online']);
3873 $l_online_users .= sprintf($l_h_user_s, $online_users['hidden_online']);
3874
3875 if ($config['load_online_guests'])
3876 {
3877 $l_online_users .= sprintf($l_g_user_s, $online_users['guests_online']);
3878 }
3879
3880
3881
3882 return array(
3883 'online_userlist' => $online_userlist,
3884 'l_online_users' => $l_online_users,
3885 );
3886}
3887
3888/**
3889* Get option bitfield from custom data
3890*
3891* @param int $bit The bit/value to get
3892* @param int $data Current bitfield to check
3893* @return bool Returns true if value of constant is set in bitfield, else false
3894*/
3895function phpbb_optionget($bit, $data)
3896{
3897 return ($data & 1 << (int) $bit) ? true : false;
3898}
3899
3900/**
3901* Set option bitfield
3902*
3903* @param int $bit The bit/value to set/unset
3904* @param bool $set True if option should be set, false if option should be unset.
3905* @param int $data Current bitfield to change
3906*
3907* @return int The new bitfield
3908*/
3909function phpbb_optionset($bit, $set, $data)
3910{
3911 if ($set && !($data & 1 << $bit))
3912 {
3913 $data += 1 << $bit;
3914 }
3915 else if (!$set && ($data & 1 << $bit))
3916 {
3917 $data -= 1 << $bit;
3918 }
3919
3920 return $data;
3921}
3922
3923/**
3924* Login using http authenticate.
3925*
3926* @param array $param Parameter array, see $param_defaults array.
3927*
3928* @return void
3929*/
3930function phpbb_http_login($param)
3931{
3932 global $auth, $user;
3933 global $config;
3934
3935 $param_defaults = array(
3936 'auth_message' => '',
3937
3938 'autologin' => false,
3939 'viewonline' => true,
3940 'admin' => false,
3941 );
3942
3943 // Overwrite default values with passed values
3944 $param = array_merge($param_defaults, $param);
3945
3946 // User is already logged in
3947 // We will not overwrite his session
3948 if (!empty($user->data['is_registered']))
3949 {
3950 return;
3951 }
3952
3953 // $_SERVER keys to check
3954 $username_keys = array(
3955 'PHP_AUTH_USER',
3956 'Authorization',
3957 'REMOTE_USER', 'REDIRECT_REMOTE_USER',
3958 'HTTP_AUTHORIZATION', 'REDIRECT_HTTP_AUTHORIZATION',
3959 'REMOTE_AUTHORIZATION', 'REDIRECT_REMOTE_AUTHORIZATION',
3960 'AUTH_USER',
3961 );
3962
3963 $password_keys = array(
3964 'PHP_AUTH_PW',
3965 'REMOTE_PASSWORD',
3966 'AUTH_PASSWORD',
3967 );
3968
3969 $username = null;
3970 foreach ($username_keys as $k)
3971 {
3972 if (isset($_SERVER[$k]))
3973 {
3974 $username = $_SERVER[$k];
3975 break;
3976 }
3977 }
3978
3979 $password = null;
3980 foreach ($password_keys as $k)
3981 {
3982 if (isset($_SERVER[$k]))
3983 {
3984 $password = $_SERVER[$k];
3985 break;
3986 }
3987 }
3988
3989 // Decode encoded information (IIS, CGI, FastCGI etc.)
3990 if (!is_null($username) && is_null($password) && strpos($username, 'Basic ') === 0)
3991 {
3992 list($username, $password) = explode(':', base64_decode(substr($username, 6)), 2);
3993 }
3994
3995 if (!is_null($username) && !is_null($password))
3996 {
3997 set_var($username, $username, 'string', true);
3998 set_var($password, $password, 'string', true);
3999
4000 $auth_result = $auth->login($username, $password, $param['autologin'], $param['viewonline'], $param['admin']);
4001
4002 if ($auth_result['status'] == LOGIN_SUCCESS)
4003 {
4004 return;
4005 }
4006 else if ($auth_result['status'] == LOGIN_ERROR_ATTEMPTS)
4007 {
4008 header('HTTP/1.0 401 Unauthorized');
4009 trigger_error('NOT_AUTHORISED');
4010 }
4011 }
4012
4013 // Prepend sitename to auth_message
4014 $param['auth_message'] = ($param['auth_message'] === '') ? $config['sitename'] : $config['sitename'] . ' - ' . $param['auth_message'];
4015
4016 // We should probably filter out non-ASCII characters - RFC2616
4017 $param['auth_message'] = preg_replace('/[\x80-\xFF]/', '?', $param['auth_message']);
4018
4019 header('WWW-Authenticate: Basic realm="' . $param['auth_message'] . '"');
4020 header('HTTP/1.0 401 Unauthorized');
4021
4022 trigger_error('NOT_AUTHORISED');
4023}
4024
4025/**
4026* Generate page header
4027*/
4028function page_header($page_title = '', $display_online_list = true, $item_id = 0, $item = 'forum')
4029{
4030 global $db, $config, $template, $SID, $_SID, $user, $auth, $phpEx, $phpbb_root_path;
4031
4032 if (defined('HEADER_INC'))
4033 {
4034 return;
4035 }
4036
4037 define('HEADER_INC', true);
4038
4039 // gzip_compression
4040 if ($config['gzip_compress'])
4041 {
4042 if (@extension_loaded('zlib') && !headers_sent())
4043 {
4044 ob_start('ob_gzhandler');
4045 }
4046 }
4047
4048 // Generate logged in/logged out status
4049 if ($user->data['user_id'] != ANONYMOUS)
4050 {
4051 $u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=logout', true, $user->session_id);
4052 $l_login_logout = sprintf($user->lang['LOGOUT_USER'], $user->data['username']);
4053 }
4054 else
4055 {
4056 $u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=login');
4057 $l_login_logout = $user->lang['LOGIN'];
4058 }
4059
4060 // Last visit date/time
4061 $s_last_visit = ($user->data['user_id'] != ANONYMOUS) ? $user->format_date($user->data['session_last_visit']) : '';
4062
4063 // Get users online list ... if required
4064 $l_online_users = $online_userlist = $l_online_record = $l_online_time = '';
4065
4066 if ($config['load_online'] && $config['load_online_time'] && $display_online_list)
4067 {
4068 /**
4069 * Load online data:
4070 * For obtaining another session column use $item and $item_id in the function-parameter, whereby the column is session_{$item}_id.
4071 */
4072 $item_id = max($item_id, 0);
4073
4074 $online_users = obtain_users_online($item_id, $item);
4075 $user_online_strings = obtain_users_online_string($online_users, $item_id, $item);
4076
4077 $l_online_users = $user_online_strings['l_online_users'];
4078 $online_userlist = $user_online_strings['online_userlist'];
4079 $total_online_users = $online_users['total_online'];
4080
4081 if ($total_online_users > $config['record_online_users'])
4082 {
4083 set_config('record_online_users', $total_online_users, true);
4084 set_config('record_online_date', time(), true);
4085 }
4086
4087 $l_online_record = sprintf($user->lang['RECORD_ONLINE_USERS'], $config['record_online_users'], $user->format_date($config['record_online_date'], false, true));
4088
4089 $l_online_time = ($config['load_online_time'] == 1) ? 'VIEW_ONLINE_TIME' : 'VIEW_ONLINE_TIMES';
4090 $l_online_time = sprintf($user->lang[$l_online_time], $config['load_online_time']);
4091 }
4092
4093 $l_privmsgs_text = $l_privmsgs_text_unread = '';
4094 $s_privmsg_new = false;
4095
4096 // Obtain number of new private messages if user is logged in
4097 if (!empty($user->data['is_registered']))
4098 {
4099 if ($user->data['user_new_privmsg'])
4100 {
4101 $l_message_new = ($user->data['user_new_privmsg'] == 1) ? $user->lang['NEW_PM'] : $user->lang['NEW_PMS'];
4102 $l_privmsgs_text = sprintf($l_message_new, $user->data['user_new_privmsg']);
4103
4104 if (!$user->data['user_last_privmsg'] || $user->data['user_last_privmsg'] > $user->data['session_last_visit'])
4105 {
4106 $sql = 'UPDATE ' . USERS_TABLE . '
4107 SET user_last_privmsg = ' . $user->data['session_last_visit'] . '
4108 WHERE user_id = ' . $user->data['user_id'];
4109 $db->sql_query($sql);
4110
4111 $s_privmsg_new = true;
4112 }
4113 else
4114 {
4115 $s_privmsg_new = false;
4116 }
4117 }
4118 else
4119 {
4120 $l_privmsgs_text = $user->lang['NO_NEW_PM'];
4121 $s_privmsg_new = false;
4122 }
4123
4124 $l_privmsgs_text_unread = '';
4125
4126 if ($user->data['user_unread_privmsg'] && $user->data['user_unread_privmsg'] != $user->data['user_new_privmsg'])
4127 {
4128 $l_message_unread = ($user->data['user_unread_privmsg'] == 1) ? $user->lang['UNREAD_PM'] : $user->lang['UNREAD_PMS'];
4129 $l_privmsgs_text_unread = sprintf($l_message_unread, $user->data['user_unread_privmsg']);
4130 }
4131 }
4132
4133 $forum_id = request_var('f', 0);
4134 $topic_id = request_var('t', 0);
4135
4136 $s_feed_news = false;
4137
4138 // Get option for news
4139 if ($config['feed_enable'])
4140 {
4141 $sql = 'SELECT forum_id
4142 FROM ' . FORUMS_TABLE . '
4143 WHERE ' . $db->sql_bit_and('forum_options', FORUM_OPTION_FEED_NEWS, '<> 0');
4144 $result = $db->sql_query_limit($sql, 1, 0, 600);
4145 $s_feed_news = (int) $db->sql_fetchfield('forum_id');
4146 $db->sql_freeresult($result);
4147 }
4148
4149 // Determine board url - we may need it later
4150 $board_url = generate_board_url() . '/';
4151 $web_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? $board_url : $phpbb_root_path;
4152
4153 // Which timezone?
4154 $tz = ($user->data['user_id'] != ANONYMOUS) ? strval(doubleval($user->data['user_timezone'])) : strval(doubleval($config['board_timezone']));
4155
4156 // Send a proper content-language to the output
4157 $user_lang = $user->lang['USER_LANG'];
4158 if (strpos($user_lang, '-x-') !== false)
4159 {
4160 $user_lang = substr($user_lang, 0, strpos($user_lang, '-x-'));
4161 }
4162
4163 // The following assigns all _common_ variables that may be used at any point in a template.
4164 $template->assign_vars(array(
4165 'SITENAME' => $config['sitename'],
4166 'SITE_DESCRIPTION' => $config['site_desc'],
4167 'PAGE_TITLE' => $page_title,
4168 'SCRIPT_NAME' => str_replace('.' . $phpEx, '', $user->page['page_name']),
4169 'LAST_VISIT_DATE' => sprintf($user->lang['YOU_LAST_VISIT'], $s_last_visit),
4170 'LAST_VISIT_YOU' => $s_last_visit,
4171 'CURRENT_TIME' => sprintf($user->lang['CURRENT_TIME'], $user->format_date(time(), false, true)),
4172 'TOTAL_USERS_ONLINE' => $l_online_users,
4173 'LOGGED_IN_USER_LIST' => $online_userlist,
4174 'RECORD_USERS' => $l_online_record,
4175 'PRIVATE_MESSAGE_INFO' => $l_privmsgs_text,
4176 'PRIVATE_MESSAGE_INFO_UNREAD' => $l_privmsgs_text_unread,
4177
4178 'S_USER_NEW_PRIVMSG' => $user->data['user_new_privmsg'],
4179 'S_USER_UNREAD_PRIVMSG' => $user->data['user_unread_privmsg'],
4180 'S_USER_NEW' => $user->data['user_new'],
4181
4182 'SID' => $SID,
4183 '_SID' => $_SID,
4184 'SESSION_ID' => $user->session_id,
4185 'ROOT_PATH' => $phpbb_root_path,
4186 'BOARD_URL' => $board_url,
4187
4188 'L_LOGIN_LOGOUT' => $l_login_logout,
4189 'L_INDEX' => $user->lang['FORUM_INDEX'],
4190 'L_ONLINE_EXPLAIN' => $l_online_time,
4191
4192 'U_PRIVATEMSGS' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;folder=inbox'),
4193 'U_RETURN_INBOX' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;folder=inbox'),
4194 'U_POPUP_PM' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;mode=popup'),
4195 'UA_POPUP_PM' => addslashes(append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;mode=popup')),
4196 'U_MEMBERLIST' => append_sid("{$phpbb_root_path}memberlist.$phpEx"),
4197 'U_VIEWONLINE' => ($auth->acl_gets('u_viewprofile', 'a_user', 'a_useradd', 'a_userdel')) ? append_sid("{$phpbb_root_path}viewonline.$phpEx") : '',
4198 'U_LOGIN_LOGOUT' => $u_login_logout,
4199 'U_INDEX' => append_sid("{$phpbb_root_path}index.$phpEx"),
4200 'U_SEARCH' => append_sid("{$phpbb_root_path}search.$phpEx"),
4201 'U_REGISTER' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=register'),
4202 'U_PROFILE' => append_sid("{$phpbb_root_path}ucp.$phpEx"),
4203 'U_MODCP' => append_sid("{$phpbb_root_path}mcp.$phpEx", false, true, $user->session_id),
4204 'U_FAQ' => append_sid("{$phpbb_root_path}faq.$phpEx"),
4205 'U_SEARCH_SELF' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=egosearch'),
4206 'U_SEARCH_NEW' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=newposts'),
4207 'U_SEARCH_UNANSWERED' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=unanswered'),
4208 'U_SEARCH_UNREAD' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=unreadposts'),
4209 'U_SEARCH_ACTIVE_TOPICS'=> append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=active_topics'),
4210 'U_DELETE_COOKIES' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=delete_cookies'),
4211 'U_TEAM' => ($user->data['user_id'] != ANONYMOUS && !$auth->acl_get('u_viewprofile')) ? '' : append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=leaders'),
4212 'U_TERMS_USE' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=terms'),
4213 'U_PRIVACY' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=privacy'),
4214 'U_RESTORE_PERMISSIONS' => ($user->data['user_perm_from'] && $auth->acl_get('a_switchperm')) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=restore_perm') : '',
4215 'U_FEED' => generate_board_url() . "/feed.$phpEx",
4216
4217 'S_USER_LOGGED_IN' => ($user->data['user_id'] != ANONYMOUS) ? true : false,
4218 'S_AUTOLOGIN_ENABLED' => ($config['allow_autologin']) ? true : false,
4219 'S_BOARD_DISABLED' => ($config['board_disable']) ? true : false,
4220 'S_REGISTERED_USER' => (!empty($user->data['is_registered'])) ? true : false,
4221 'S_IS_BOT' => (!empty($user->data['is_bot'])) ? true : false,
4222 'S_USER_PM_POPUP' => $user->optionget('popuppm'),
4223 'S_USER_LANG' => $user_lang,
4224 'S_USER_BROWSER' => (isset($user->data['session_browser'])) ? $user->data['session_browser'] : $user->lang['UNKNOWN_BROWSER'],
4225 'S_USERNAME' => $user->data['username'],
4226 'S_CONTENT_DIRECTION' => $user->lang['DIRECTION'],
4227 'S_CONTENT_FLOW_BEGIN' => ($user->lang['DIRECTION'] == 'ltr') ? 'left' : 'right',
4228 'S_CONTENT_FLOW_END' => ($user->lang['DIRECTION'] == 'ltr') ? 'right' : 'left',
4229 'S_CONTENT_ENCODING' => 'UTF-8',
4230 'S_TIMEZONE' => ($user->data['user_dst'] || ($user->data['user_id'] == ANONYMOUS && $config['board_dst'])) ? sprintf($user->lang['ALL_TIMES'], $user->lang['tz'][$tz], $user->lang['tz']['dst']) : sprintf($user->lang['ALL_TIMES'], $user->lang['tz'][$tz], ''),
4231 'S_DISPLAY_ONLINE_LIST' => ($l_online_time) ? 1 : 0,
4232 'S_DISPLAY_SEARCH' => (!$config['load_search']) ? 0 : (isset($auth) ? ($auth->acl_get('u_search') && $auth->acl_getf_global('f_search')) : 1),
4233 'S_DISPLAY_PM' => ($config['allow_privmsg'] && !empty($user->data['is_registered']) && ($auth->acl_get('u_readpm') || $auth->acl_get('u_sendpm'))) ? true : false,
4234 'S_DISPLAY_MEMBERLIST' => (isset($auth)) ? $auth->acl_get('u_viewprofile') : 0,
4235 'S_NEW_PM' => ($s_privmsg_new) ? 1 : 0,
4236 'S_REGISTER_ENABLED' => ($config['require_activation'] != USER_ACTIVATION_DISABLE) ? true : false,
4237 'S_FORUM_ID' => $forum_id,
4238 'S_TOPIC_ID' => $topic_id,
4239
4240 'S_LOGIN_ACTION' => ((!defined('ADMIN_START')) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=login') : append_sid("index.$phpEx", false, true, $user->session_id)),
4241 'S_LOGIN_REDIRECT' => build_hidden_fields(array('redirect' => str_replace('&amp;', '&', build_url()))),
4242
4243 'S_ENABLE_FEEDS' => ($config['feed_enable']) ? true : false,
4244 'S_ENABLE_FEEDS_OVERALL' => ($config['feed_overall']) ? true : false,
4245 'S_ENABLE_FEEDS_FORUMS' => ($config['feed_overall_forums']) ? true : false,
4246 'S_ENABLE_FEEDS_TOPICS' => ($config['feed_topics_new']) ? true : false,
4247 'S_ENABLE_FEEDS_TOPICS_ACTIVE' => ($config['feed_topics_active']) ? true : false,
4248 'S_ENABLE_FEEDS_NEWS' => ($s_feed_news) ? true : false,
4249
4250 'T_THEME_PATH' => "{$web_path}styles/" . $user->theme['theme_path'] . '/theme',
4251 'T_TEMPLATE_PATH' => "{$web_path}styles/" . $user->theme['template_path'] . '/template',
4252 'T_SUPER_TEMPLATE_PATH' => (isset($user->theme['template_inherit_path']) && $user->theme['template_inherit_path']) ? "{$web_path}styles/" . $user->theme['template_inherit_path'] . '/template' : "{$web_path}styles/" . $user->theme['template_path'] . '/template',
4253 'T_IMAGESET_PATH' => "{$web_path}styles/" . $user->theme['imageset_path'] . '/imageset',
4254 'T_IMAGESET_LANG_PATH' => "{$web_path}styles/" . $user->theme['imageset_path'] . '/imageset/' . $user->data['user_lang'],
4255 'T_IMAGES_PATH' => "{$web_path}images/",
4256 'T_SMILIES_PATH' => "{$web_path}{$config['smilies_path']}/",
4257 'T_AVATAR_PATH' => "{$web_path}{$config['avatar_path']}/",
4258 'T_AVATAR_GALLERY_PATH' => "{$web_path}{$config['avatar_gallery_path']}/",
4259 'T_ICONS_PATH' => "{$web_path}{$config['icons_path']}/",
4260 'T_RANKS_PATH' => "{$web_path}{$config['ranks_path']}/",
4261 'T_UPLOAD_PATH' => "{$web_path}{$config['upload_path']}/",
4262 'T_STYLESHEET_LINK' => (!$user->theme['theme_storedb']) ? "{$web_path}styles/" . $user->theme['theme_path'] . '/theme/stylesheet.css' : append_sid("{$phpbb_root_path}style.$phpEx", 'id=' . $user->theme['style_id'] . '&amp;lang=' . $user->data['user_lang'], true, $user->session_id),
4263 'T_STYLESHEET_NAME' => $user->theme['theme_name'],
4264
4265 'T_THEME_NAME' => $user->theme['theme_path'],
4266 'T_TEMPLATE_NAME' => $user->theme['template_path'],
4267 'T_SUPER_TEMPLATE_NAME' => (isset($user->theme['template_inherit_path']) && $user->theme['template_inherit_path']) ? $user->theme['template_inherit_path'] : $user->theme['template_path'],
4268 'T_IMAGESET_NAME' => $user->theme['imageset_path'],
4269 'T_IMAGESET_LANG_NAME' => $user->data['user_lang'],
4270 'T_IMAGES' => 'images',
4271 'T_SMILIES' => $config['smilies_path'],
4272 'T_AVATAR' => $config['avatar_path'],
4273 'T_AVATAR_GALLERY' => $config['avatar_gallery_path'],
4274 'T_ICONS' => $config['icons_path'],
4275 'T_RANKS' => $config['ranks_path'],
4276 'T_UPLOAD' => $config['upload_path'],
4277
4278 'SITE_LOGO_IMG' => $user->img('site_logo'),
4279
4280 'A_COOKIE_SETTINGS' => addslashes('; path=' . $config['cookie_path'] . ((!$config['cookie_domain'] || $config['cookie_domain'] == 'localhost' || $config['cookie_domain'] == '127.0.0.1') ? '' : '; domain=' . $config['cookie_domain']) . ((!$config['cookie_secure']) ? '' : '; secure')),
4281 ));
4282
4283 // application/xhtml+xml not used because of IE
4284 header('Content-type: text/html; charset=UTF-8');
4285
4286 header('Cache-Control: private, no-cache="set-cookie"');
4287 header('Expires: 0');
4288 header('Pragma: no-cache');
4289
4290 return;
4291}
4292
4293/**
4294* Generate page footer
4295*/
4296function page_footer($run_cron = true)
4297{
4298 global $db, $config, $template, $user, $auth, $cache, $starttime, $phpbb_root_path, $phpEx;
4299
4300 // Output page creation time
4301 if (defined('DEBUG'))
4302 {
4303 $mtime = explode(' ', microtime());
4304 $totaltime = $mtime[0] + $mtime[1] - $starttime;
4305
4306 if (!empty($_REQUEST['explain']) && $auth->acl_get('a_') && defined('DEBUG_EXTRA') && method_exists($db, 'sql_report'))
4307 {
4308 $db->sql_report('display');
4309 }
4310
4311 $debug_output = sprintf('Time : %.3fs | ' . $db->sql_num_queries() . ' Queries | GZIP : ' . (($config['gzip_compress'] && @extension_loaded('zlib')) ? 'On' : 'Off') . (($user->load) ? ' | Load : ' . $user->load : ''), $totaltime);
4312
4313 if ($auth->acl_get('a_') && defined('DEBUG_EXTRA'))
4314 {
4315 if (function_exists('memory_get_usage'))
4316 {
4317 if ($memory_usage = memory_get_usage())
4318 {
4319 global $base_memory_usage;
4320 $memory_usage -= $base_memory_usage;
4321 $memory_usage = get_formatted_filesize($memory_usage);
4322
4323 $debug_output .= ' | Memory Usage: ' . $memory_usage;
4324 }
4325 }
4326
4327 $debug_output .= ' | <a href="' . build_url() . '&amp;explain=1">Explain</a>';
4328 }
4329 }
4330
4331 $template->assign_vars(array(
4332 'DEBUG_OUTPUT' => (defined('DEBUG')) ? $debug_output : '',
4333 'TRANSLATION_INFO' => (!empty($user->lang['TRANSLATION_INFO'])) ? $user->lang['TRANSLATION_INFO'] : '',
4334
4335 'U_ACP' => ($auth->acl_get('a_') && !empty($user->data['is_registered'])) ? append_sid("{$phpbb_root_path}adm/index.$phpEx", false, true, $user->session_id) : '')
4336 );
4337
4338 // Call cron-type script
4339 $call_cron = false;
4340 if (!defined('IN_CRON') && $run_cron && !$config['board_disable'])
4341 {
4342 $call_cron = true;
4343 $time_now = (!empty($user->time_now) && is_int($user->time_now)) ? $user->time_now : time();
4344
4345 // Any old lock present?
4346 if (!empty($config['cron_lock']))
4347 {
4348 $cron_time = explode(' ', $config['cron_lock']);
4349
4350 // If 1 hour lock is present we do not call cron.php
4351 if ($cron_time[0] + 3600 >= $time_now)
4352 {
4353 $call_cron = false;
4354 }
4355 }
4356 }
4357
4358 // Call cron job?
4359 if ($call_cron)
4360 {
4361 $cron_type = '';
4362
4363 if ($time_now - $config['queue_interval'] > $config['last_queue_run'] && !defined('IN_ADMIN') && file_exists($phpbb_root_path . 'cache/queue.' . $phpEx))
4364 {
4365 // Process email queue
4366 $cron_type = 'queue';
4367 }
4368 else if (method_exists($cache, 'tidy') && $time_now - $config['cache_gc'] > $config['cache_last_gc'])
4369 {
4370 // Tidy the cache
4371 $cron_type = 'tidy_cache';
4372 }
4373 else if ($config['warnings_expire_days'] && ($time_now - $config['warnings_gc'] > $config['warnings_last_gc']))
4374 {
4375 $cron_type = 'tidy_warnings';
4376 }
4377 else if ($time_now - $config['database_gc'] > $config['database_last_gc'])
4378 {
4379 // Tidy the database
4380 $cron_type = 'tidy_database';
4381 }
4382 else if ($time_now - $config['search_gc'] > $config['search_last_gc'])
4383 {
4384 // Tidy the search
4385 $cron_type = 'tidy_search';
4386 }
4387 else if ($time_now - $config['session_gc'] > $config['session_last_gc'])
4388 {
4389 $cron_type = 'tidy_sessions';
4390 }
4391
4392 if ($cron_type)
4393 {
4394 $template->assign_var('RUN_CRON_TASK', '<img src="' . append_sid($phpbb_root_path . 'cron.' . $phpEx, 'cron_type=' . $cron_type) . '" width="1" height="1" alt="cron" />');
4395 }
4396 }
4397
4398 $template->display('body');
4399
4400 garbage_collection();
4401 exit_handler();
4402}
4403
4404/**
4405* Closing the cache object and the database
4406* Cool function name, eh? We might want to add operations to it later
4407*/
4408function garbage_collection()
4409{
4410 global $cache, $db;
4411
4412 // Unload cache, must be done before the DB connection if closed
4413 if (!empty($cache))
4414 {
4415 $cache->unload();
4416 }
4417
4418 // Close our DB connection.
4419 if (!empty($db))
4420 {
4421 $db->sql_close();
4422 }
4423}
4424
4425/**
4426* Handler for exit calls in phpBB.
4427* This function supports hooks.
4428*
4429* Note: This function is called after the template has been outputted.
4430*/
4431function exit_handler()
4432{
4433 global $phpbb_hook, $config;
4434
4435 if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__))
4436 {
4437 if ($phpbb_hook->hook_return(__FUNCTION__))
4438 {
4439 return $phpbb_hook->hook_return_result(__FUNCTION__);
4440 }
4441 }
4442
4443 // As a pre-caution... some setups display a blank page if the flush() is not there.
4444 (empty($config['gzip_compress'])) ? @flush() : @ob_flush();
4445
4446 exit;
4447}
4448
4449/**
4450* Handler for init calls in phpBB. This function is called in user::setup();
4451* This function supports hooks.
4452*/
4453function phpbb_user_session_handler()
4454{
4455 global $phpbb_hook;
4456
4457 if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__))
4458 {
4459 if ($phpbb_hook->hook_return(__FUNCTION__))
4460 {
4461 return $phpbb_hook->hook_return_result(__FUNCTION__);
4462 }
4463 }
4464
4465 return;
4466}
4467
4468?>
Note: See TracBrowser for help on using the repository browser.