functions.php 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091
  1. <?php
  2. /**
  3. * Copyright (C) 2008-2012 FluxBB
  4. * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  5. * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  6. */
  7. //
  8. // Cookie stuff!
  9. //
  10. function check_cookie(&$pun_user)
  11. {
  12. global $container, $pun_config;
  13. $db = $container->get('DB');
  14. $userCookie = $container->get('UserCookie');
  15. $now = time();
  16. if (($userId = $userCookie->id()) === false) {
  17. return set_default_user();
  18. }
  19. // Кто в этой теме - , o.witt_data - Visman
  20. // Check if there's a user with the user ID and password hash from the cookie
  21. $result = $db->query('SELECT u.*, g.*, o.logged, o.idle, o.witt_data FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON u.group_id=g.g_id LEFT JOIN '.$db->prefix.'online AS o ON o.user_id=u.id WHERE u.id='.$userId) or error('Unable to fetch user information', __FILE__, __LINE__, $db->error());
  22. $pun_user = $db->fetch_assoc($result);
  23. if (empty($pun_user['id']) || ! $userCookie->verifyHash($pun_user['id'], $pun_user['password'])) {
  24. return set_default_user();
  25. }
  26. // проверка ip админа и модератора - Visman
  27. if ($pun_config['o_check_ip'] == '1' && ($pun_user['g_id'] == PUN_ADMIN || $pun_user['g_moderator'] == '1') && $pun_user['registration_ip'] != get_remote_address())
  28. {
  29. return set_default_user();
  30. }
  31. $userCookie->setUserCookie($pun_user['id'], $pun_user['password']);
  32. // Set a default language if the user selected language no longer exists
  33. if (!file_exists(PUN_ROOT.'lang/'.$pun_user['language']))
  34. $pun_user['language'] = $pun_config['o_default_lang'];
  35. // Set a default style if the user selected style no longer exists
  36. if (!file_exists(PUN_ROOT.'style/'.$pun_user['style'].'.css'))
  37. $pun_user['style'] = $pun_config['o_default_style'];
  38. if (!$pun_user['disp_topics'])
  39. $pun_user['disp_topics'] = $pun_config['o_disp_topics_default'];
  40. if (!$pun_user['disp_posts'])
  41. $pun_user['disp_posts'] = $pun_config['o_disp_posts_default'];
  42. // Define this if you want this visit to affect the online list and the users last visit data
  43. if (!defined('PUN_QUIET_VISIT'))
  44. {
  45. // Update the online list
  46. if (!$pun_user['logged'])
  47. {
  48. $pun_user['logged'] = $now;
  49. // With MySQL/MySQLi/SQLite, REPLACE INTO avoids a user having two rows in the online table
  50. switch ($container->getParameter('DB_TYPE'))
  51. {
  52. case 'mysql':
  53. case 'mysqli':
  54. case 'mysql_innodb':
  55. case 'mysqli_innodb':
  56. case 'sqlite':
  57. witt_query('REPLACE INTO '.$db->prefix.'online (user_id, ident, logged:?comma?::?column?:) VALUES('.$pun_user['id'].', \''.$db->escape($pun_user['username']).'\', '.$pun_user['logged'].':?comma?::?value?:)'); // MOD Кто в этой теме - Visman
  58. break;
  59. default:
  60. witt_query('INSERT INTO '.$db->prefix.'online (user_id, ident, logged:?comma?::?column?:) SELECT '.$pun_user['id'].', \''.$db->escape($pun_user['username']).'\', '.$pun_user['logged'].':?comma?::?value?: WHERE NOT EXISTS (SELECT 1 FROM '.$db->prefix.'online WHERE user_id='.$pun_user['id'].')'); // MOD Кто в этой теме - Visman
  61. break;
  62. }
  63. // Reset tracked topics
  64. set_tracked_topics(null);
  65. }
  66. else
  67. {
  68. // Special case: We've timed out, but no other user has browsed the forums since we timed out
  69. if ($pun_user['logged'] < ($now-$pun_config['o_timeout_visit']))
  70. {
  71. $db->query('UPDATE '.$db->prefix.'users SET last_visit='.$pun_user['logged'].' WHERE id='.$pun_user['id']) or error('Unable to update user visit data', __FILE__, __LINE__, $db->error());
  72. $pun_user['last_visit'] = $pun_user['logged'];
  73. }
  74. $idle_sql = ($pun_user['idle'] == '1') ? ', idle=0' : '';
  75. witt_query('UPDATE '.$db->prefix.'online SET logged='.$now.$idle_sql.':?comma?::?column?::?equal?::?value?: WHERE user_id='.$pun_user['id']); // MOD Кто в этой теме - Visman
  76. $cookie = $container->get('Cookie');
  77. $track = $cookie->get('track');
  78. // Update tracked topics with the current expire time
  79. if (isset($track)) {
  80. $cookie->set('track', $track, $now + $pun_config['o_timeout_visit']);
  81. }
  82. }
  83. }
  84. else
  85. {
  86. if (!$pun_user['logged'])
  87. $pun_user['logged'] = $pun_user['last_visit'];
  88. }
  89. $pun_user['is_guest'] = false;
  90. $pun_user['is_admmod'] = $pun_user['g_id'] == PUN_ADMIN || $pun_user['g_moderator'] == '1';
  91. $pun_user['is_bot'] = false; // MOD определения ботов - Visman
  92. }
  93. //
  94. // Converts the CDATA end sequence ]]> into ]]&gt;
  95. //
  96. function escape_cdata($str)
  97. {
  98. return str_replace(']]>', ']]&gt;', $str);
  99. }
  100. //
  101. // Authenticates the provided username and password against the user database
  102. // $user can be either a user ID (integer) or a username (string)
  103. // $password can be either a plaintext password or a password hash including salt ($password_is_hash must be set accordingly)
  104. //
  105. function authenticate_user($user, $password, $password_is_hash = false)
  106. {
  107. global $container, $pun_user;
  108. $db = $container->get('DB');
  109. // Check if there's a user matching $user and $password
  110. $result = $db->query('SELECT u.*, g.*, o.logged, o.idle FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON g.g_id=u.group_id LEFT JOIN '.$db->prefix.'online AS o ON o.user_id=u.id WHERE '.(is_int($user) ? 'u.id='.intval($user) : 'u.username=\''.$db->escape($user).'\'')) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
  111. $pun_user = $db->fetch_assoc($result);
  112. $is_password_authorized = hash_equals($password, $pun_user['password']);
  113. $is_hash_authorized = hash_equals(pun_hash($password), $pun_user['password']);
  114. if (!isset($pun_user['id']) ||
  115. ($password_is_hash && !$is_password_authorized ||
  116. (!$password_is_hash && !$is_hash_authorized)))
  117. set_default_user();
  118. else
  119. $pun_user['is_guest'] = false;
  120. }
  121. //
  122. // Try to determine the current URL
  123. //
  124. function get_current_url($max_length = 0)
  125. {
  126. $protocol = get_current_protocol();
  127. $port = (isset($_SERVER['SERVER_PORT']) && (($_SERVER['SERVER_PORT'] != '80' && $protocol == 'http') || ($_SERVER['SERVER_PORT'] != '443' && $protocol == 'https')) && strpos($_SERVER['HTTP_HOST'], ':') === false) ? ':'.$_SERVER['SERVER_PORT'] : '';
  128. $url = urldecode($protocol.'://'.$_SERVER['HTTP_HOST'].$port.$_SERVER['REQUEST_URI']);
  129. if (strlen($url) <= $max_length || $max_length == 0)
  130. return $url;
  131. // We can't find a short enough url
  132. return null;
  133. }
  134. //
  135. // Fetch the current protocol in use - http or https
  136. //
  137. function get_current_protocol()
  138. {
  139. $protocol = 'http';
  140. // Check if the server is claiming to using HTTPS
  141. if (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) != 'off')
  142. $protocol = 'https';
  143. // If we are behind a reverse proxy try to decide which protocol it is using
  144. if (defined('FORUM_BEHIND_REVERSE_PROXY'))
  145. {
  146. // Check if we are behind a Microsoft based reverse proxy
  147. if (!empty($_SERVER['HTTP_FRONT_END_HTTPS']) && strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) != 'off')
  148. $protocol = 'https';
  149. // Check if we're behind a "proper" reverse proxy, and what protocol it's using
  150. if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']))
  151. $protocol = strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']);
  152. }
  153. return $protocol;
  154. }
  155. //
  156. // Fetch the base_url, optionally support HTTPS and HTTP
  157. //
  158. function get_base_url($support_https = false)
  159. {
  160. global $container;
  161. static $base_url;
  162. if (!$support_https)
  163. return $container->getParameter('BASE_URL');
  164. if (!isset($base_url))
  165. {
  166. // Make sure we are using the correct protocol
  167. $base_url = str_replace(array('http://', 'https://'), get_current_protocol().'://', $container->getParameter('BASE_URL'));
  168. }
  169. return $base_url;
  170. }
  171. //
  172. // Fill $pun_user with default values (for guests)
  173. //
  174. function set_default_user()
  175. {
  176. global $container, $pun_user, $pun_config, $languages;
  177. $db = $container->get('DB');
  178. $container->get('UserCookie')->deleteUserCookie();
  179. $remote_addr = get_remote_address();
  180. // MOD определения ботов - Visman
  181. if (!defined('FORUM_BOT_FUNCTIONS_LOADED'))
  182. include PUN_ROOT.'include/bots.inc.php';
  183. $remote_addr = ua_isbotex($remote_addr);
  184. // Кто в этой теме - , o.witt_data - Visman
  185. // Fetch guest user
  186. $result = $db->query('SELECT u.*, g.*, o.logged, o.last_post, o.last_search, o.witt_data FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON u.group_id=g.g_id LEFT JOIN '.$db->prefix.'online AS o ON o.ident=\''.$db->escape($remote_addr).'\' WHERE u.id=1') or error('Unable to fetch guest information', __FILE__, __LINE__, $db->error());
  187. if (!$db->num_rows($result))
  188. exit('Unable to fetch guest information. Your database must contain both a guest user and a guest user group.');
  189. $pun_user = $db->fetch_assoc($result);
  190. // Update online list
  191. if (!$pun_user['logged'])
  192. {
  193. $pun_user['logged'] = time();
  194. // With MySQL/MySQLi/SQLite, REPLACE INTO avoids a user having two rows in the online table
  195. switch ($container->getParameter('DB_TYPE'))
  196. {
  197. case 'mysql':
  198. case 'mysqli':
  199. case 'mysql_innodb':
  200. case 'mysqli_innodb':
  201. case 'sqlite':
  202. witt_query('REPLACE INTO '.$db->prefix.'online (user_id, ident, logged:?comma?::?column?:) VALUES(1, \''.$db->escape($remote_addr).'\', '.$pun_user['logged'].':?comma?::?value?:)'); // MOD Кто в этой теме - Visman
  203. break;
  204. default:
  205. witt_query('INSERT INTO '.$db->prefix.'online (user_id, ident, logged:?comma?::?column?:) SELECT 1, \''.$db->escape($remote_addr).'\', '.$pun_user['logged'].':?comma?::?value?: WHERE NOT EXISTS (SELECT 1 FROM '.$db->prefix.'online WHERE ident=\''.$db->escape($remote_addr).'\')'); // MOD Кто в этой теме - Visman
  206. break;
  207. }
  208. }
  209. else
  210. witt_query('UPDATE '.$db->prefix.'online SET logged='.time().':?comma?::?column?::?equal?::?value?: WHERE ident=\''.$db->escape($remote_addr).'\''); // MOD Кто в этой теме - Visman
  211. $pun_user['disp_topics'] = $pun_config['o_disp_topics_default'];
  212. $pun_user['disp_posts'] = $pun_config['o_disp_posts_default'];
  213. $pun_user['timezone'] = $pun_config['o_default_timezone'];
  214. $pun_user['dst'] = $pun_config['o_default_dst'];
  215. $pun_user['language'] = $pun_config['o_default_lang'];
  216. $pun_user['style'] = $pun_config['o_default_style'];
  217. $pun_user['is_guest'] = true;
  218. $pun_user['is_admmod'] = false;
  219. $pun_user['is_bot'] = (strpos($remote_addr, '[Bot]') !== false); // MOD определения ботов - Visman
  220. $pun_user['ident'] = $remote_addr; // Кто в этой теме - Visman
  221. // быстрое переключение языка - Visman
  222. $language = $container->get('Cookie')->get('glang');
  223. if (null !== $language)
  224. {
  225. $language = preg_replace('%[^\w]%', '', $language);
  226. $languages = forum_list_langs();
  227. if (in_array($language, $languages))
  228. $pun_user['language'] = $language;
  229. }
  230. }
  231. //
  232. // Check username
  233. //
  234. function check_username($username, $exclude_id = null)
  235. {
  236. global $container, $pun_config, $errors, $lang_prof_reg, $lang_register, $lang_common;
  237. $db = $container->get('DB');
  238. $bans = $container->get('bans');
  239. // Convert multiple whitespace characters into one (to prevent people from registering with indistinguishable usernames)
  240. $username = preg_replace('%\s+%s', ' ', $username);
  241. // Validate username
  242. if (mb_strlen($username) < 2)
  243. $errors[] = $lang_prof_reg['Username too short'];
  244. else if (mb_strlen($username) > 25) // This usually doesn't happen since the form element only accepts 25 characters
  245. $errors[] = $lang_prof_reg['Username too long'];
  246. else if (!preg_match('%^\p{L}[\p{L}\p{N}_ ]+$%uD', $username)) // строгая проверка имени пользователя - Visman
  247. $errors[] = $lang_prof_reg['Username Error'];
  248. else if (!strcasecmp($username, 'Guest') || !strcmp(mb_strtolower($username), mb_strtolower($lang_common['Guest'])))
  249. $errors[] = $lang_prof_reg['Username guest'];
  250. else if (preg_match('%[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}%', $username) || preg_match('%((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))%', $username))
  251. $errors[] = $lang_prof_reg['Username IP'];
  252. else if ((strpos($username, '[') !== false || strpos($username, ']') !== false) && strpos($username, '\'') !== false && strpos($username, '"') !== false)
  253. $errors[] = $lang_prof_reg['Username reserved chars'];
  254. else if (preg_match('%(?:\[/?(?:b|u|s|ins|del|em|i|h|colou?r|quote|code|img|url|email|list|\*|topic|post|forum|user)\]|\[(?:img|url|quote|list)=)%i', $username))
  255. $errors[] = $lang_prof_reg['Username BBCode'];
  256. // Check username for any censored words
  257. if ($pun_config['o_censoring'] == '1' && censor_words($username) != $username)
  258. $errors[] = $lang_register['Username censor'];
  259. // Check that the username (or a too similar username) is not already registered
  260. $query = (!is_null($exclude_id)) ? ' AND id!='.$exclude_id : '';
  261. $result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE (UPPER(username)=UPPER(\''.$db->escape($username).'\') OR UPPER(username)=UPPER(\''.$db->escape(ucp_preg_replace('%[^\p{L}\p{N}]%u', '', $username)).'\')) AND id>1'.$query) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
  262. if ($db->num_rows($result))
  263. {
  264. $busy = $db->result($result);
  265. $errors[] = $lang_register['Username dupe 1'].' '.pun_htmlspecialchars($busy).'. '.$lang_register['Username dupe 2'];
  266. }
  267. // Check username for any banned usernames
  268. foreach ($bans as $cur_ban)
  269. {
  270. if ($cur_ban['username'] != '' && mb_strtolower($username) == mb_strtolower($cur_ban['username']))
  271. {
  272. $errors[] = $lang_prof_reg['Banned username'];
  273. break;
  274. }
  275. }
  276. }
  277. //
  278. // Update "Users online"
  279. // ф-ия переписана - Visman
  280. function update_users_online($tid = 0, &$witt_us = array())
  281. {
  282. global $container, $pun_config, $onl_u, $onl_g, $onl_s;
  283. $db = $container->get('DB');
  284. $now = time();
  285. $nn1 = $now-$pun_config['o_timeout_online'];
  286. $nn2 = $now-$pun_config['o_timeout_visit'];
  287. $kol = 0;
  288. // Fetch all online list entries that are older than "o_timeout_online"
  289. $result = $db->query('SELECT * FROM '.$db->prefix.'online') or error('Unable to fetch old entries from online list', __FILE__, __LINE__, $db->error());
  290. while ($cu = $db->fetch_assoc($result))
  291. {
  292. if ($cu['logged'] < $nn1)
  293. {
  294. // If the entry is a guest, delete it
  295. if ($cu['user_id'] == 1)
  296. $db->query('DELETE FROM '.$db->prefix.'online WHERE ident=\''.$db->escape($cu['ident']).'\'') or error('Unable to delete from online list', __FILE__, __LINE__, $db->error());
  297. else
  298. {
  299. // If the entry is older than "o_timeout_visit", update last_visit for the user in question, then delete him/her from the online list
  300. if ($cu['logged'] < $nn2)
  301. {
  302. $db->query('UPDATE '.$db->prefix.'users SET last_visit='.$cu['logged'].' WHERE id='.$cu['user_id']) or error('Unable to update user visit data', __FILE__, __LINE__, $db->error());
  303. $db->query('DELETE FROM '.$db->prefix.'online WHERE user_id='.$cu['user_id']) or error('Unable to delete from online list', __FILE__, __LINE__, $db->error());
  304. }
  305. else if ($cu['idle'] == '0')
  306. {
  307. $db->query('UPDATE '.$db->prefix.'online SET idle=1 WHERE user_id='.$cu['user_id']) or error('Unable to insert into online list', __FILE__, __LINE__, $db->error());
  308. $onl_s[] = $cu['ident'];
  309. }
  310. else
  311. $onl_s[] = $cu['ident'];
  312. }
  313. }
  314. else
  315. {
  316. $kol++;
  317. $onl_s[] = $cu['ident'];
  318. if ($cu['user_id'] == 1)
  319. $onl_g[] = $cu['ident'];
  320. else
  321. $onl_u[$cu['user_id']] = $cu['ident'];
  322. if ($tid > 0 && !empty($cu['witt_data']))
  323. {
  324. $witt_ar = unserialize($cu['witt_data']);
  325. if (isset($witt_ar[$tid]) && $witt_ar[$tid] > $now - WITT_ENABLE)
  326. {
  327. if ($cu['user_id'] == 1)
  328. $witt_us[1][] = $cu['ident'];
  329. else
  330. $witt_us[$cu['user_id']] = $cu['ident'];
  331. }
  332. }
  333. }
  334. }
  335. if ($pun_config['st_max_users'] < $kol)
  336. {
  337. $db->query('UPDATE '.$db->prefix.'config SET conf_value=\''.$kol.'\' WHERE conf_name=\'st_max_users\'') or error('Unable to update config value \'st_max_users\'', __FILE__, __LINE__, $db->error());
  338. $db->query('UPDATE '.$db->prefix.'config SET conf_value=\''.$now.'\' WHERE conf_name=\'st_max_users_time\'') or error('Unable to update config value \'st_max_users_time\'', __FILE__, __LINE__, $db->error());
  339. $container->get('config update');
  340. }
  341. }
  342. //
  343. // Display the profile navigation menu
  344. //
  345. function generate_profile_menu($page = '')
  346. {
  347. global $lang_profile, $pun_config, $pun_user, $id;
  348. ?>
  349. <div id="profile" class="block2col">
  350. <div class="blockmenu">
  351. <h2><span><?php echo $lang_profile['Profile menu'] ?></span></h2>
  352. <div class="box">
  353. <div class="inbox">
  354. <ul>
  355. <li<?php if ($page == 'essentials') echo ' class="isactive"'; ?>><a href="profile.php?section=essentials&amp;id=<?php echo $id ?>"><?php echo $lang_profile['Section essentials'] ?></a></li>
  356. <li<?php if ($page == 'personal') echo ' class="isactive"'; ?>><a href="profile.php?section=personal&amp;id=<?php echo $id ?>"><?php echo $lang_profile['Section personal'] ?></a></li>
  357. <li<?php if ($page == 'messaging') echo ' class="isactive"'; ?>><a href="profile.php?section=messaging&amp;id=<?php echo $id ?>"><?php echo $lang_profile['Section messaging'] ?></a></li>
  358. <?php if ($pun_config['o_avatars'] == '1' || $pun_config['o_signatures'] == '1'): ?> <li<?php if ($page == 'personality') echo ' class="isactive"'; ?>><a href="profile.php?section=personality&amp;id=<?php echo $id ?>"><?php echo $lang_profile['Section personality'] ?></a></li>
  359. <?php endif; ?> <li<?php if ($page == 'display') echo ' class="isactive"'; ?>><a href="profile.php?section=display&amp;id=<?php echo $id ?>"><?php echo $lang_profile['Section display'] ?></a></li>
  360. <li<?php if ($page == 'privacy') echo ' class="isactive"'; ?>><a href="profile.php?section=privacy&amp;id=<?php echo $id ?>"><?php echo $lang_profile['Section privacy'] ?></a></li>
  361. <?php require PUN_ROOT.'include/uploadp.php'; ?>
  362. <?php if ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && $pun_user['g_mod_ban_users'] == '1')): ?> <li<?php if ($page == 'admin') echo ' class="isactive"'; ?>><a href="profile.php?section=admin&amp;id=<?php echo $id ?>"><?php echo $lang_profile['Section admin'] ?></a></li>
  363. <?php endif; ?> </ul>
  364. </div>
  365. </div>
  366. </div>
  367. <?php
  368. }
  369. //
  370. // Outputs markup to display a user's avatar
  371. //
  372. function generate_avatar_markup($user_id)
  373. {
  374. global $pun_config;
  375. $filetypes = array('jpg', 'gif', 'png');
  376. $avatar_markup = '';
  377. foreach ($filetypes as $cur_type)
  378. {
  379. $path = $pun_config['o_avatars_dir'].'/'.$user_id.'.'.$cur_type;
  380. if (file_exists(PUN_ROOT.$path) && $img_size = getimagesize(PUN_ROOT.$path))
  381. {
  382. $avatar_markup = '<img src="'.pun_htmlspecialchars(get_base_url(true).'/'.$path.'?m='.filemtime(PUN_ROOT.$path)).'" '.$img_size[3].' alt="" />';
  383. break;
  384. }
  385. }
  386. return $avatar_markup;
  387. }
  388. //
  389. // Generate browser's title
  390. //
  391. function generate_page_title($page_title, $p = null)
  392. {
  393. global $lang_common;
  394. if (!is_array($page_title))
  395. $page_title = array($page_title);
  396. $page_title = array_reverse($page_title);
  397. if ($p > 1)
  398. $page_title[0] .= ' ('.sprintf($lang_common['Page'], forum_number_format($p)).')';
  399. $crumbs = implode($lang_common['Title separator'], $page_title);
  400. return $crumbs;
  401. }
  402. //
  403. // Save array of tracked topics in cookie
  404. //
  405. function set_tracked_topics($tracked_topics)
  406. {
  407. global $container, $pun_config;
  408. $cookie_data = '';
  409. if (!empty($tracked_topics))
  410. {
  411. // Sort the arrays (latest read first)
  412. arsort($tracked_topics['topics'], SORT_NUMERIC);
  413. arsort($tracked_topics['forums'], SORT_NUMERIC);
  414. // Homebrew serialization (to avoid having to run unserialize() on cookie data)
  415. foreach ($tracked_topics['topics'] as $id => $timestamp)
  416. $cookie_data .= 't'.$id.'='.$timestamp.';';
  417. foreach ($tracked_topics['forums'] as $id => $timestamp)
  418. $cookie_data .= 'f'.$id.'='.$timestamp.';';
  419. // Enforce a byte size limit (4096 minus some space for the cookie name - defaults to 4048)
  420. if (strlen($cookie_data) > FORUM_MAX_COOKIE_SIZE)
  421. {
  422. $cookie_data = substr($cookie_data, 0, FORUM_MAX_COOKIE_SIZE);
  423. $cookie_data = substr($cookie_data, 0, strrpos($cookie_data, ';')).';';
  424. }
  425. }
  426. $container->get('Cookie')->set('track', $cookie_data, time() + $pun_config['o_timeout_visit']);
  427. }
  428. //
  429. // Extract array of tracked topics from cookie
  430. //
  431. function get_tracked_topics()
  432. {
  433. global $container;
  434. $cookie_data = $container->get('Cookie')->get('track');
  435. if (!$cookie_data)
  436. return array('topics' => array(), 'forums' => array());
  437. if (strlen($cookie_data) > FORUM_MAX_COOKIE_SIZE)
  438. return array('topics' => array(), 'forums' => array());
  439. // Unserialize data from cookie
  440. $tracked_topics = array('topics' => array(), 'forums' => array());
  441. $temp = explode(';', $cookie_data);
  442. foreach ($temp as $t)
  443. {
  444. $type = substr($t, 0, 1) == 'f' ? 'forums' : 'topics';
  445. $id = intval(substr($t, 1));
  446. $timestamp = intval(substr($t, strpos($t, '=') + 1));
  447. if ($id > 0 && $timestamp > 0)
  448. $tracked_topics[$type][$id] = $timestamp;
  449. }
  450. return $tracked_topics;
  451. }
  452. //
  453. // Shortcut method for executing all callbacks registered with the addon manager for the given hook
  454. //
  455. function flux_hook($name)
  456. {
  457. global $flux_addons;
  458. $flux_addons->hook($name);
  459. }
  460. //
  461. // Update posts, topics, last_post, last_post_id and last_poster for a forum
  462. //
  463. function update_forum($forum_id)
  464. {
  465. global $container;
  466. $db = $container->get('DB');
  467. $result = $db->query('SELECT COUNT(id), SUM(num_replies) FROM '.$db->prefix.'topics WHERE forum_id='.$forum_id) or error('Unable to fetch forum topic count', __FILE__, __LINE__, $db->error());
  468. list($num_topics, $num_posts) = $db->fetch_row($result);
  469. $num_posts = $num_posts + $num_topics; // $num_posts is only the sum of all replies (we have to add the topic posts)
  470. $result = $db->query('SELECT last_post, last_post_id, last_poster, subject FROM '.$db->prefix.'topics WHERE forum_id='.$forum_id.' AND moved_to IS NULL ORDER BY last_post DESC LIMIT 1') or error('Unable to fetch last_post/last_post_id/last_poster', __FILE__, __LINE__, $db->error()); // last topic on index - Visman
  471. if ($db->num_rows($result)) // There are topics in the forum
  472. {
  473. list($last_post, $last_post_id, $last_poster, $last_topic) = $db->fetch_row($result);
  474. $db->query('UPDATE '.$db->prefix.'forums SET num_topics='.$num_topics.', num_posts='.$num_posts.', last_post='.$last_post.', last_post_id='.$last_post_id.', last_poster=\''.$db->escape($last_poster).'\', last_topic=\''.$db->escape($last_topic).'\' WHERE id='.$forum_id) or error('Unable to update last_post/last_post_id/last_poster', __FILE__, __LINE__, $db->error()); // last topic on index - Visman
  475. }
  476. else // There are no topics
  477. $db->query('UPDATE '.$db->prefix.'forums SET num_topics='.$num_topics.', num_posts='.$num_posts.', last_post=NULL, last_post_id=NULL, last_poster=NULL, last_topic=NULL WHERE id='.$forum_id) or error('Unable to update last_post/last_post_id/last_poster', __FILE__, __LINE__, $db->error()); // last topic on index - Visman
  478. }
  479. //
  480. // Deletes any avatars owned by the specified user ID
  481. //
  482. function delete_avatar($user_id)
  483. {
  484. global $pun_config;
  485. $filetypes = array('jpg', 'gif', 'png');
  486. // Delete user avatar
  487. foreach ($filetypes as $cur_type)
  488. {
  489. if (file_exists(PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$user_id.'.'.$cur_type))
  490. @unlink(PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$user_id.'.'.$cur_type);
  491. }
  492. }
  493. //
  494. // Delete a topic and all of it's posts
  495. //
  496. function delete_topic($topic_id, $flag_f = 1) // not sum - Visman
  497. {
  498. global $container;
  499. $db = $container->get('DB');
  500. // Delete the topic and any redirect topics
  501. $db->query('DELETE FROM '.$db->prefix.'topics WHERE id='.$topic_id.' OR moved_to='.$topic_id) or error('Unable to delete topic', __FILE__, __LINE__, $db->error());
  502. if ($flag_f == 0) // уменьшение постов у юзеров и not sum - Visman
  503. {
  504. $result = $db->query('SELECT COUNT(id), poster_id FROM '.$db->prefix.'posts WHERE topic_id='.$topic_id.' GROUP BY poster_id') or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
  505. while ($row = $db->fetch_row($result))
  506. {
  507. if ($row[1] > 1)
  508. $db->query('UPDATE '.$db->prefix.'users SET num_posts = num_posts-'.$row[0].' WHERE id='.$row[1]) or error('Unable to update posters messages count', __FILE__, __LINE__, $db->error());
  509. }
  510. }
  511. // Create a list of the post IDs in this topic
  512. $post_ids = '';
  513. $result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id='.$topic_id) or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
  514. while ($row = $db->fetch_row($result))
  515. $post_ids .= ($post_ids != '') ? ','.$row[0] : $row[0];
  516. // Make sure we have a list of post IDs
  517. if ($post_ids != '')
  518. {
  519. // MOD warnings - Visman
  520. $db->query('DELETE FROM '.$db->prefix.'warnings WHERE id IN ('.$post_ids.')') or error('Unable to delete warnings', __FILE__, __LINE__, $db->error());
  521. strip_search_index($post_ids);
  522. // Delete posts in topic
  523. $db->query('DELETE FROM '.$db->prefix.'posts WHERE topic_id='.$topic_id) or error('Unable to delete posts', __FILE__, __LINE__, $db->error());
  524. }
  525. // Delete any subscriptions for this topic
  526. $db->query('DELETE FROM '.$db->prefix.'topic_subscriptions WHERE topic_id='.$topic_id) or error('Unable to delete subscriptions', __FILE__, __LINE__, $db->error());
  527. global $pun_user;
  528. require PUN_ROOT.'include/poll.php';
  529. poll_delete($topic_id);
  530. }
  531. //
  532. // Delete a single post
  533. //
  534. function delete_post($post_id, $topic_id)
  535. {
  536. global $container;
  537. $db = $container->get('DB');
  538. $result = $db->query('SELECT id, poster, posted FROM '.$db->prefix.'posts WHERE topic_id='.$topic_id.' ORDER BY id DESC LIMIT 2') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
  539. list($last_id, ,) = $db->fetch_row($result);
  540. list($second_last_id, $second_poster, $second_posted) = $db->fetch_row($result);
  541. // Delete the post
  542. $db->query('DELETE FROM '.$db->prefix.'posts WHERE id='.$post_id) or error('Unable to delete post', __FILE__, __LINE__, $db->error());
  543. // MOD warnings - Visman
  544. $db->query('DELETE FROM '.$db->prefix.'warnings WHERE id='.$post_id) or error('Unable to delete warnings', __FILE__, __LINE__, $db->error());
  545. strip_search_index($post_id);
  546. // Count number of replies in the topic
  547. $result = $db->query('SELECT COUNT(id) FROM '.$db->prefix.'posts WHERE topic_id='.$topic_id) or error('Unable to fetch post count for topic', __FILE__, __LINE__, $db->error());
  548. $num_replies = $db->result($result, 0) - 1;
  549. // If the message we deleted is the most recent in the topic (at the end of the topic)
  550. if ($last_id == $post_id)
  551. {
  552. // If there is a $second_last_id there is more than 1 reply to the topic
  553. if (!empty($second_last_id))
  554. $db->query('UPDATE '.$db->prefix.'topics SET last_post='.$second_posted.', last_post_id='.$second_last_id.', last_poster=\''.$db->escape($second_poster).'\', num_replies='.$num_replies.' WHERE id='.$topic_id) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
  555. else
  556. // We deleted the only reply, so now last_post/last_post_id/last_poster is posted/id/poster from the topic itself
  557. $db->query('UPDATE '.$db->prefix.'topics SET last_post=posted, last_post_id=id, last_poster=poster, num_replies='.$num_replies.' WHERE id='.$topic_id) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
  558. }
  559. else
  560. // Otherwise we just decrement the reply counter
  561. $db->query('UPDATE '.$db->prefix.'topics SET num_replies='.$num_replies.' WHERE id='.$topic_id) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
  562. }
  563. //
  564. // Replace censored words in $text
  565. //
  566. function censor_words($text)
  567. {
  568. global $container;
  569. static $search_for, $replace_with;
  570. // If not already built in a previous call, build an array of censor words and their replacement text
  571. if (!isset($search_for)) {
  572. list($search_for, $replace_with) = $container->get('censoring');
  573. }
  574. if (!empty($search_for)) {
  575. $text = substr(ucp_preg_replace($search_for, $replace_with, ' '.$text.' '), 1, -1);
  576. }
  577. return $text;
  578. }
  579. //
  580. // Determines the correct title for $user
  581. // $user must contain the elements 'username', 'title', 'posts', 'g_id' and 'g_user_title'
  582. //
  583. function get_title($user)
  584. {
  585. global $container, $lang_common;
  586. static $ban_list;
  587. $bans = $container->get('bans');
  588. // If not already built in a previous call, build an array of lowercase banned usernames
  589. if (empty($ban_list))
  590. {
  591. $ban_list = array();
  592. foreach ($bans as $cur_ban)
  593. $ban_list[] = mb_strtolower($cur_ban['username']);
  594. }
  595. // If the user is banned
  596. if (in_array(mb_strtolower($user['username']), $ban_list))
  597. $user_title = $lang_common['Banned'];
  598. // If the user has a custom title
  599. else if ($user['title'] != '')
  600. $user_title = pun_htmlspecialchars($user['title']);
  601. // If the user group has a default user title
  602. else if ($user['g_user_title'] != '')
  603. $user_title = pun_htmlspecialchars($user['g_user_title']);
  604. // If the user is a guest
  605. else if ($user['g_id'] == PUN_GUEST)
  606. $user_title = $lang_common['Guest'];
  607. // If nothing else helps, we assign the default
  608. else
  609. $user_title = $lang_common['Member'];
  610. return $user_title;
  611. }
  612. //
  613. // Generate a string with numbered links (for multipage scripts)
  614. //
  615. function paginate($num_pages, $cur_page, $link)
  616. {
  617. global $lang_common;
  618. $pages = array();
  619. $link_to_all = false;
  620. // If $cur_page == -1, we link to all pages (used in viewforum.php)
  621. if ($cur_page == -1)
  622. {
  623. $cur_page = 1;
  624. $link_to_all = true;
  625. }
  626. if ($num_pages <= 1)
  627. $pages = array('<strong class="item1">1</strong>');
  628. else
  629. {
  630. // Add a previous page link
  631. if ($num_pages > 1 && $cur_page > 1)
  632. $pages[] = '<a rel="prev"'.(empty($pages) ? ' class="item1"' : '').' href="'.$link.($cur_page == 2 ? '' : '&amp;p='.($cur_page - 1)).'">'.$lang_common['Previous'].'</a>';
  633. if ($cur_page > 3)
  634. {
  635. $pages[] = '<a'.(empty($pages) ? ' class="item1"' : '').' href="'.$link.'">1</a>';
  636. if ($cur_page > 5)
  637. $pages[] = '<span class="spacer">'.$lang_common['Spacer'].'</span>';
  638. }
  639. // Don't ask me how the following works. It just does, OK? :-)
  640. for ($current = ($cur_page == 5) ? $cur_page - 3 : $cur_page - 2, $stop = ($cur_page + 4 == $num_pages) ? $cur_page + 4 : $cur_page + 3; $current < $stop; ++$current)
  641. {
  642. if ($current < 1 || $current > $num_pages)
  643. continue;
  644. else if ($current != $cur_page || $link_to_all)
  645. $pages[] = '<a'.(empty($pages) ? ' class="item1"' : '').' href="'.$link.($current == 1 ? '' : '&amp;p='.$current).'">'.forum_number_format($current).'</a>';
  646. else
  647. $pages[] = '<strong'.(empty($pages) ? ' class="item1"' : '').'>'.forum_number_format($current).'</strong>';
  648. }
  649. if ($cur_page <= ($num_pages-3))
  650. {
  651. if ($cur_page != ($num_pages-3) && $cur_page != ($num_pages-4))
  652. $pages[] = '<span class="spacer">'.$lang_common['Spacer'].'</span>';
  653. $pages[] = '<a'.(empty($pages) ? ' class="item1"' : '').' href="'.$link.'&amp;p='.$num_pages.'">'.forum_number_format($num_pages).'</a>';
  654. }
  655. // Add a next page link
  656. if ($num_pages > 1 && !$link_to_all && $cur_page < $num_pages)
  657. $pages[] = '<a rel="next"'.(empty($pages) ? ' class="item1"' : '').' href="'.$link.'&amp;p='.($cur_page +1).'">'.$lang_common['Next'].'</a>';
  658. }
  659. return implode(' ', $pages);
  660. }
  661. //
  662. // Display a message
  663. //
  664. function message($message, $no_back_link = false, $http_status = null)
  665. {
  666. global $container, $lang_common, $pun_config, $pun_start, $tpl_main, $pun_user, $page_js;
  667. witt_query(); // MOD Кто в этой теме - Visman
  668. // Did we receive a custom header?
  669. if(!is_null($http_status)) {
  670. header('HTTP/1.1 ' . $http_status);
  671. }
  672. if (!defined('PUN_HEADER'))
  673. {
  674. $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Info']);
  675. define('PUN_ACTIVE_PAGE', 'index');
  676. require PUN_ROOT.'header.php';
  677. }
  678. ?>
  679. <div id="msg" class="block">
  680. <h2><span><?php echo $lang_common['Info'] ?></span></h2>
  681. <div class="box">
  682. <div class="inbox">
  683. <p><?php echo $message ?></p>
  684. <?php if (!$no_back_link): ?> <p><a href="javascript: history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
  685. <?php endif; ?> </div>
  686. </div>
  687. </div>
  688. <?php
  689. require PUN_ROOT.'footer.php';
  690. }
  691. //
  692. // Format a time string according to $time_format and time zones
  693. //
  694. function format_time($timestamp, $date_only = false, $date_format = null, $time_format = null, $time_only = false, $no_text = false)
  695. {
  696. global $lang_common, $pun_user, $forum_date_formats, $forum_time_formats;
  697. if ($timestamp == '')
  698. return $lang_common['Never'];
  699. $diff = ($pun_user['timezone'] + $pun_user['dst']) * 3600;
  700. $timestamp += $diff;
  701. $now = time();
  702. if(is_null($date_format))
  703. $date_format = $forum_date_formats[$pun_user['date_format']];
  704. if(is_null($time_format))
  705. $time_format = $forum_time_formats[$pun_user['time_format']];
  706. $date = gmdate($date_format, $timestamp);
  707. $today = gmdate($date_format, $now+$diff);
  708. $yesterday = gmdate($date_format, $now+$diff-86400);
  709. if(!$no_text)
  710. {
  711. if ($date == $today)
  712. $date = $lang_common['Today'];
  713. else if ($date == $yesterday)
  714. $date = $lang_common['Yesterday'];
  715. }
  716. if ($date_only)
  717. return $date;
  718. else if ($time_only)
  719. return gmdate($time_format, $timestamp);
  720. else
  721. return $date.' '.gmdate($time_format, $timestamp);
  722. }
  723. //
  724. // A wrapper for PHP's number_format function
  725. //
  726. function forum_number_format($number, $decimals = 0)
  727. {
  728. global $lang_common;
  729. return is_numeric($number) ? number_format($number, $decimals, $lang_common['lang_decimal_point'], $lang_common['lang_thousands_sep']) : $number;
  730. }
  731. //
  732. // Make sure that HTTP_REFERER matches base_url/script
  733. // ********* новые ф-ии проверки подлинности - Visman
  734. // rev 59 - Variant of functions not depending on Base URL
  735. function confirm_message($error_msg = false)
  736. {
  737. global $lang_common, $errors;
  738. if (isset($errors) && is_array($errors))
  739. $errors[] = $error_msg ? $error_msg : $lang_common['Bad referrer'];
  740. else
  741. message($error_msg ? $error_msg : $lang_common['Bad referrer']);
  742. }
  743. function confirm_referrer($script, $error_msg = false, $use_ip = true)
  744. {
  745. global $container;
  746. $hash = $container->get('Request')->requestStr('csrf_hash', '');
  747. if (empty($hash) || !hash_equals(csrf_hash($script, $use_ip), $hash))
  748. confirm_message($error_msg);
  749. }
  750. function csrf_hash($script = false, $use_ip = true, $user = false)
  751. {
  752. global $pun_config, $pun_user;
  753. static $arr = array();
  754. $script = $script ? $script : basename($_SERVER['SCRIPT_NAME']);
  755. $ip = $use_ip ? get_remote_address() : '';
  756. $user = is_array($user) ? $user : $pun_user;
  757. $key = $script.$ip.$user['id'];
  758. if (!isset($arr[$key]))
  759. $arr[$key] = pun_hash(PUN_ROOT.$script.$user['id'].pun_hash($ip.$user['password'].$pun_config['o_crypto_pas']));
  760. return $arr[$key];
  761. }
  762. // ********* новые ф-ии проверки подлинности - Visman
  763. //
  764. // Validate the given redirect URL, use the fallback otherwise
  765. //
  766. function validate_redirect($redirect_url, $fallback_url)
  767. {
  768. $referrer = parse_url(strtolower($redirect_url));
  769. // Make sure the host component exists
  770. if (!isset($referrer['host']))
  771. $referrer['host'] = '';
  772. // Remove www subdomain if it exists
  773. if (strpos($referrer['host'], 'www.') === 0)
  774. $referrer['host'] = substr($referrer['host'], 4);
  775. // Make sure the path component exists
  776. if (!isset($referrer['path']))
  777. $referrer['path'] = '';
  778. $valid = parse_url(strtolower(get_base_url()));
  779. // Remove www subdomain if it exists
  780. if (strpos($valid['host'], 'www.') === 0)
  781. $valid['host'] = substr($valid['host'], 4);
  782. // Make sure the path component exists
  783. if (!isset($valid['path']))
  784. $valid['path'] = '';
  785. if ($referrer['host'] == $valid['host'] && preg_match('%^'.preg_quote($valid['path'], '%').'/(.*?)\.php%i', $referrer['path']))
  786. return $redirect_url;
  787. else
  788. return $fallback_url;
  789. }
  790. //
  791. // Compute a hash of $str
  792. //
  793. function pun_hash($str)
  794. {
  795. return sha1($str);
  796. }
  797. //
  798. // Try to determine the correct remote IP-address
  799. //
  800. function get_remote_address()
  801. {
  802. $remote_addr = $_SERVER['REMOTE_ADDR'];
  803. // If we are behind a reverse proxy try to find the real users IP
  804. if (defined('FORUM_BEHIND_REVERSE_PROXY'))
  805. {
  806. if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
  807. {
  808. // The general format of the field is:
  809. // X-Forwarded-For: client1, proxy1, proxy2
  810. // where the value is a comma+space separated list of IP addresses, the left-most being the farthest downstream client,
  811. // and each successive proxy that passed the request adding the IP address where it received the request from.
  812. $forwarded_for = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
  813. $forwarded_for = trim($forwarded_for[0]);
  814. if (@preg_match('%^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$%', $forwarded_for) || @preg_match('%^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$%', $forwarded_for))
  815. $remote_addr = $forwarded_for;
  816. }
  817. }
  818. return $remote_addr;
  819. }
  820. //
  821. // Calls htmlspecialchars with a few options already set
  822. //
  823. function pun_htmlspecialchars($str)
  824. {
  825. return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
  826. }
  827. //
  828. // Calls htmlspecialchars_decode with a few options already set
  829. //
  830. function pun_htmlspecialchars_decode($str)
  831. {
  832. if (function_exists('htmlspecialchars_decode'))
  833. return htmlspecialchars_decode($str, ENT_QUOTES);
  834. static $translations;
  835. if (!isset($translations))
  836. {
  837. $translations = get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES);
  838. $translations['&#039;'] = '\''; // get_html_translation_table doesn't include &#039; which is what htmlspecialchars translates ' to, but apparently that is okay?! http://bugs.php.net/bug.php?id=25927
  839. $translations = array_flip($translations);
  840. }
  841. return strtr($str, $translations);
  842. }
  843. //
  844. // Convert \r\n and \r to \n
  845. //
  846. function pun_linebreaks($str)
  847. {
  848. return str_replace(array("\r\n", "\r"), "\n", $str);
  849. }
  850. //
  851. // Checks if a string is in all uppercase
  852. //
  853. function is_all_uppercase($string)
  854. {
  855. return mb_strtoupper($string) == $string && mb_strtolower($string) != $string;
  856. }
  857. //
  858. // Inserts $element into $input at $offset
  859. // $offset can be either a numerical offset to insert at (eg: 0 inserts at the beginning of the array)
  860. // or a string, which is the key that the new element should be inserted before
  861. // $key is optional: it's used when inserting a new key/value pair into an associative array
  862. //
  863. function array_insert(&$input, $offset, $element, $key = null)
  864. {
  865. if (is_null($key))
  866. $key = $offset;
  867. // Determine the proper offset if we're using a string
  868. if (!is_int($offset))
  869. $offset = array_search($offset, array_keys($input), true);
  870. // Out of bounds checks
  871. if ($offset > count($input))
  872. $offset = count($input);
  873. else if ($offset < 0)
  874. $offset = 0;
  875. $input = array_merge(array_slice($input, 0, $offset), array($key => $element), array_slice($input, $offset));
  876. }
  877. //
  878. // Display a message when board is in maintenance mode
  879. //
  880. function maintenance_message()
  881. {
  882. global $container, $pun_config, $lang_common, $pun_user;
  883. // Send no-cache headers
  884. header('Expires: Thu, 21 Jul 1977 07:30:00 GMT'); // When yours truly first set eyes on this world! :)
  885. header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
  886. header('Cache-Control: post-check=0, pre-check=0', false);
  887. header('Pragma: no-cache'); // For HTTP/1.0 compatibility
  888. // Send the Content-type header in case the web server is setup to send something else
  889. header('Content-type: text/html; charset=utf-8');
  890. // Deal with newlines, tabs and multiple spaces
  891. $pattern = array("\t", ' ', ' ');
  892. $replace = array('&#160; &#160; ', '&#160; ', ' &#160;');
  893. $message = str_replace($pattern, $replace, $pun_config['o_maintenance_message']);
  894. if (file_exists(PUN_ROOT.'style/'.$pun_user['style'].'/maintenance.tpl'))
  895. {
  896. $tpl_file = PUN_ROOT.'style/'.$pun_user['style'].'/maintenance.tpl';
  897. $tpl_inc_dir = PUN_ROOT.'style/'.$pun_user['style'].'/';
  898. }
  899. else
  900. {
  901. $tpl_file = PUN_ROOT.'include/template/maintenance.tpl';
  902. $tpl_inc_dir = PUN_ROOT.'include/user/';
  903. }
  904. $tpl_maint = file_get_contents($tpl_file);
  905. // START SUBST - <pun_include "*">
  906. preg_match_all('%<pun_include "([^/\\\\]*?)\.(php[45]?|inc|html?|txt)">%i', $tpl_maint, $pun_includes, PREG_SET_ORDER);
  907. foreach ($pun_includes as $cur_include)
  908. {
  909. ob_start();
  910. // Allow for overriding user includes, too.
  911. if (file_exists($tpl_inc_dir.$cur_include[1].'.'.$cur_include[2]))
  912. require $tpl_inc_dir.$cur_include[1].'.'.$cur_include[2];
  913. else if (file_exists(PUN_ROOT.'include/user/'.$cur_include[1].'.'.$cur_include[2]))
  914. require PUN_ROOT.'include/user/'.$cur_include[1].'.'.$cur_include[2];
  915. else
  916. error(sprintf($lang_common['Pun include error'], htmlspecialchars($cur_include[0]), basename($tpl_file)));
  917. $tpl_temp = ob_get_contents();
  918. $tpl_maint = str_replace($cur_include[0], $tpl_temp, $tpl_maint);
  919. ob_end_clean();
  920. }
  921. // END SUBST - <pun_include "*">
  922. // START SUBST - <pun_language>
  923. $tpl_maint = str_replace('<pun_language>', $lang_common['lang_identifier'], $tpl_maint);
  924. // END SUBST - <pun_language>
  925. // START SUBST - <pun_content_direction>
  926. $tpl_maint = str_replace('<pun_content_direction>', $lang_common['lang_direction'], $tpl_maint);
  927. // END SUBST - <pun_content_direction>
  928. // START SUBST - <pun_head>
  929. ob_start();
  930. $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Maintenance']);
  931. ?>
  932. <title><?php echo generate_page_title($page_title) ?></title>
  933. <link rel="stylesheet" type="text/css" href="style/<?php echo $pun_user['style'].'.css' ?>" />
  934. <?php
  935. $tpl_temp = trim(ob_get_contents());
  936. $tpl_maint = str_replace('<pun_head>', $tpl_temp, $tpl_maint);
  937. ob_end_clean();
  938. // END SUBST - <pun_head>
  939. // START SUBST - <pun_maint_main>
  940. ob_start();
  941. ?>
  942. <div class="block">
  943. <h2><?php echo $lang_common['Maintenance'] ?></h2>
  944. <div class="box">
  945. <div class="inbox">
  946. <p><?php echo $message ?></p>
  947. </div>
  948. </div>
  949. </div>
  950. <?php
  951. $tpl_temp = trim(ob_get_contents());
  952. $tpl_maint = str_replace('<pun_maint_main>', $tpl_temp, $tpl_maint);
  953. ob_end_clean();
  954. // END SUBST - <pun_maint_main>
  955. // End the transaction
  956. $container->get('DB')->end_transaction();
  957. // Close the db connection (and free up any result data)
  958. $container->get('DB')->close();
  959. exit($tpl_maint);
  960. }
  961. //
  962. // Display $message and redirect user to $destination_url
  963. //
  964. function redirect($destination_url, $message)
  965. {
  966. global $container, $pun_config, $lang_common, $pun_user;
  967. // Prefix with base_url (unless there's already a valid URI)
  968. if (strpos($destination_url, 'http://') !== 0 && strpos($destination_url, 'https://') !== 0 && strpos($destination_url, '/') !== 0)
  969. $destination_url = get_base_url(true).'/'.$destination_url;
  970. // Do a little spring cleaning
  971. $destination_url = preg_replace('%([\r\n])|(\%0[ad])|(;\s*data\s*:)%i', '', $destination_url);
  972. // If the delay is 0 seconds, we might as well skip the redirect all together
  973. if ($pun_config['o_redirect_delay'] == '0')
  974. {
  975. $container->get('DB')->end_transaction();
  976. $container->get('DB')->close();
  977. header('Location: '.str_replace('&amp;', '&', $destination_url));
  978. exit;
  979. }
  980. // Send no-cache headers
  981. header('Expires: Thu, 21 Jul 1977 07:30:00 GMT'); // When yours truly first set eyes on this world! :)
  982. header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
  983. header('Cache-Control: post-check=0, pre-check=0', false);
  984. header('Pragma: no-cache'); // For HTTP/1.0 compatibility
  985. // Send the Content-type header in case the web server is setup to send something else
  986. header('Content-type: text/html; charset=utf-8');
  987. if (file_exists(PUN_ROOT.'style/'.$pun_user['style'].'/redirect.tpl'))
  988. {
  989. $tpl_file = PUN_ROOT.'style/'.$pun_user['style'].'/redirect.tpl';
  990. $tpl_inc_dir = PUN_ROOT.'style/'.$pun_user['style'].'/';
  991. }
  992. else
  993. {
  994. $tpl_file = PUN_ROOT.'include/template/redirect.tpl';
  995. $tpl_inc_dir = PUN_ROOT.'include/user/';
  996. }
  997. $tpl_redir = file_get_contents($tpl_file);
  998. // START SUBST - <pun_include "*">
  999. preg_match_all('%<pun_include "([^/\\\\]*?)\.(php[45]?|inc|html?|txt)">%i', $tpl_redir, $pun_includes, PREG_SET_ORDER);
  1000. foreach ($pun_includes as $cur_include)
  1001. {
  1002. ob_start();
  1003. // Allow for overriding user includes, too.
  1004. if (file_exists($tpl_inc_dir.$cur_include[1].'.'.$cur_include[2]))
  1005. require $tpl_inc_dir.$cur_include[1].'.'.$cur_include[2];
  1006. else if (file_exists(PUN_ROOT.'include/user/'.$cur_include[1].'.'.$cur_include[2]))
  1007. require PUN_ROOT.'include/user/'.$cur_include[1].'.'.$cur_include[2];
  1008. else
  1009. error(sprintf($lang_common['Pun include error'], htmlspecialchars($cur_include[0]), basename($tpl_file)));
  1010. $tpl_temp = ob_get_contents();
  1011. $tpl_redir = str_replace($cur_include[0], $tpl_temp, $tpl_redir);
  1012. ob_end_clean();
  1013. }
  1014. // END SUBST - <pun_include "*">
  1015. // START SUBST - <pun_language>
  1016. $tpl_redir = str_replace('<pun_language>', $lang_common['lang_identifier'], $tpl_redir);
  1017. // END SUBST - <pun_language>
  1018. // START SUBST - <pun_content_direction>
  1019. $tpl_redir = str_replace('<pun_content_direction>', $lang_common['lang_direction'], $tpl_redir);
  1020. // END SUBST - <pun_content_direction>
  1021. // START SUBST - <pun_head>
  1022. ob_start();
  1023. $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Redirecting']);
  1024. ?>
  1025. <meta http-equiv="refresh" content="<?php echo $pun_config['o_redirect_delay'] ?>;URL=<?php echo $destination_url ?>" />
  1026. <title><?php echo generate_page_title($page_title) ?></title>
  1027. <link rel="stylesheet" type="text/css" href="style/<?php echo $pun_user['style'].'.css' ?>" />
  1028. <?php
  1029. $tpl_temp = trim(ob_get_contents());
  1030. $tpl_redir = str_replace('<pun_head>', $tpl_temp, $tpl_redir);
  1031. ob_end_clean();
  1032. // END SUBST - <pun_head>
  1033. // START SUBST - <pun_redir_main>
  1034. ob_start();
  1035. ?>
  1036. <div class="block">
  1037. <h2><?php echo $lang_common['Redirecting'] ?></h2>
  1038. <div class="box">
  1039. <div class="inbox">
  1040. <p><?php echo $message.'<br /><br /><a href="'.$destination_url.'">'.$lang_common['Click redirect'].'</a>' ?></p>
  1041. </div>
  1042. </div>
  1043. </div>
  1044. <?php
  1045. $tpl_temp = trim(ob_get_contents());
  1046. $tpl_redir = str_replace('<pun_redir_main>', $tpl_temp, $tpl_redir);
  1047. ob_end_clean();
  1048. // END SUBST - <pun_redir_main>
  1049. // START SUBST - <pun_footer>
  1050. ob_start();
  1051. // End the transaction
  1052. $container->get('DB')->end_transaction();
  1053. // Display executed queries (if enabled)
  1054. if (defined('PUN_SHOW_QUERIES'))
  1055. display_saved_queries();
  1056. $tpl_temp = trim(ob_get_contents());
  1057. $tpl_redir = str_replace('<pun_footer>', $tpl_temp, $tpl_redir);
  1058. ob_end_clean();
  1059. // END SUBST - <pun_footer>
  1060. // Close the db connection (and free up any result data)
  1061. $container->get('DB')->close();
  1062. exit($tpl_redir);
  1063. }
  1064. //
  1065. // Display a simple error message
  1066. //
  1067. function error($message, $file = null, $line = null, $db_error = false)
  1068. {
  1069. global $pun_config, $lang_common;
  1070. // Set some default settings if the script failed before $pun_config could be populated
  1071. if (empty($pun_config))
  1072. {
  1073. $pun_config = array(
  1074. 'o_board_title' => 'FluxBB',
  1075. 'o_gzip' => '0'
  1076. );
  1077. }
  1078. // Set some default translations if the script failed before $lang_common could be populated
  1079. if (empty($lang_common))
  1080. {
  1081. $lang_common = array(
  1082. 'Title separator' => ' / ',
  1083. 'Page' => 'Page %s'
  1084. );
  1085. }
  1086. // Empty all output buffers and stop buffering
  1087. while (@ob_end_clean());
  1088. // "Restart" output buffering if we are using ob_gzhandler (since the gzip header is already sent)
  1089. if ($pun_config['o_gzip'] && extension_loaded('zlib'))
  1090. ob_start('ob_gzhandler');
  1091. // Send no-cache headers
  1092. header('Expires: Thu, 21 Jul 1977 07:30:00 GMT'); // When yours truly first set eyes on this world! :)
  1093. header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
  1094. header('Cache-Control: post-check=0, pre-check=0', false);
  1095. header('Pragma: no-cache'); // For HTTP/1.0 compatibility
  1096. // Send the Content-type header in case the web server is setup to send something else
  1097. header('Content-type: text/html; charset=utf-8');
  1098. ?>
  1099. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  1100. <html xmlns="http://www.w3.org/1999/xhtml" dir="ltr">
  1101. <head>
  1102. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  1103. <?php $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), 'Error') ?>
  1104. <title><?php echo generate_page_title($page_title) ?></title>
  1105. <style type="text/css">
  1106. <!--
  1107. BODY {MARGIN: 10% 20% auto 20%; font: 10px Verdana, Arial, Helvetica, sans-serif}
  1108. #errorbox {BORDER: 1px solid #B84623}
  1109. H2 {MARGIN: 0; COLOR: #FFFFFF; BACKGROUND-COLOR: #B84623; FONT-SIZE: 1.1em; PADDING: 5px 4px}
  1110. #errorbox DIV {PADDING: 6px 5px; BACKGROUND-COLOR: #F1F1F1}
  1111. -->
  1112. </style>
  1113. </head>
  1114. <body>
  1115. <div id="errorbox">
  1116. <h2>An error was encountered</h2>
  1117. <div>
  1118. <?php
  1119. if (defined('PUN_DEBUG') && !is_null($file) && !is_null($line))
  1120. {
  1121. $file = str_replace(realpath(PUN_ROOT), '', $file);
  1122. echo "\t\t".'<strong>File:</strong> '.$file.'<br />'."\n\t\t".'<strong>Line:</strong> '.$line.'<br /><br />'."\n\t\t".'<strong>FluxBB reported</strong>: '.$message."\n";
  1123. if ($db_error)
  1124. {
  1125. echo "\t\t".'<br /><br /><strong>Database reported:</strong> '.pun_htmlspecialchars($db_error['error_msg']).(($db_error['error_no']) ? ' (Errno: '.$db_error['error_no'].')' : '')."\n";
  1126. if ($db_error['error_sql'] != '')
  1127. echo "\t\t".'<br /><br /><strong>Failed query:</strong> '.pun_htmlspecialchars($db_error['error_sql'])."\n";
  1128. }
  1129. }
  1130. else
  1131. echo "\t\t".'Error: <strong>'.$message.'.</strong>'."\n";
  1132. ?>
  1133. </div>
  1134. </div>
  1135. </body>
  1136. </html>
  1137. <?php
  1138. // If a database connection was established (before this error) we close it
  1139. if ($db_error)
  1140. $GLOBALS['db']->close();
  1141. exit;
  1142. }
  1143. //
  1144. // Removes any "bad" characters (characters which mess with the display of a page, are invisible, etc) from user input
  1145. //
  1146. function forum_remove_bad_characters() //????
  1147. {
  1148. $_GET = remove_bad_characters($_GET);
  1149. $_POST = remove_bad_characters($_POST);
  1150. $_COOKIE = remove_bad_characters($_COOKIE);
  1151. $_REQUEST = remove_bad_characters($_REQUEST);
  1152. }
  1153. //
  1154. // Removes any "bad" characters (characters which mess with the display of a page, are invisible, etc) from the given string
  1155. // See: http://kb.mozillazine.org/Network.IDN.blacklist_chars
  1156. //
  1157. function remove_bad_characters($array) //????
  1158. {
  1159. static $bad_utf8_chars;
  1160. if (!isset($bad_utf8_chars))
  1161. {
  1162. $bad_utf8_chars = array(
  1163. "\xcc\xb7" => '', // COMBINING SHORT SOLIDUS OVERLAY 0337 *
  1164. "\xcc\xb8" => '', // COMBINING LONG SOLIDUS OVERLAY 0338 *
  1165. "\xe1\x85\x9F" => '', // HANGUL CHOSEONG FILLER 115F *
  1166. "\xe1\x85\xA0" => '', // HANGUL JUNGSEONG FILLER 1160 *
  1167. "\xe2\x80\x8b" => '', // ZERO WIDTH SPACE 200B *
  1168. "\xe2\x80\x8c" => '', // ZERO WIDTH NON-JOINER 200C
  1169. "\xe2\x80\x8d" => '', // ZERO WIDTH JOINER 200D
  1170. "\xe2\x80\x8e" => '', // LEFT-TO-RIGHT MARK 200E
  1171. "\xe2\x80\x8f" => '', // RIGHT-TO-LEFT MARK 200F
  1172. "\xe2\x80\xaa" => '', // LEFT-TO-RIGHT EMBEDDING 202A
  1173. "\xe2\x80\xab" => '', // RIGHT-TO-LEFT EMBEDDING 202B
  1174. "\xe2\x80\xac" => '', // POP DIRECTIONAL FORMATTING 202C
  1175. "\xe2\x80\xad" => '', // LEFT-TO-RIGHT OVERRIDE 202D
  1176. "\xe2\x80\xae" => '', // RIGHT-TO-LEFT OVERRIDE 202E
  1177. "\xe2\x80\xaf" => '', // NARROW NO-BREAK SPACE 202F *
  1178. "\xe2\x81\x9f" => '', // MEDIUM MATHEMATICAL SPACE 205F *
  1179. "\xe2\x81\xa0" => '', // WORD JOINER 2060
  1180. "\xe3\x85\xa4" => '', // HANGUL FILLER 3164 *
  1181. "\xef\xbb\xbf" => '', // ZERO WIDTH NO-BREAK SPACE FEFF
  1182. "\xef\xbe\xa0" => '', // HALFWIDTH HANGUL FILLER FFA0 *
  1183. "\xef\xbf\xb9" => '', // INTERLINEAR ANNOTATION ANCHOR FFF9 *
  1184. "\xef\xbf\xba" => '', // INTERLINEAR ANNOTATION SEPARATOR FFFA *
  1185. "\xef\xbf\xbb" => '', // INTERLINEAR ANNOTATION TERMINATOR FFFB *
  1186. "\xef\xbf\xbc" => '', // OBJECT REPLACEMENT CHARACTER FFFC *
  1187. "\xef\xbf\xbd" => '', // REPLACEMENT CHARACTER FFFD *
  1188. "\xe2\x80\x80" => ' ', // EN QUAD 2000 *
  1189. "\xe2\x80\x81" => ' ', // EM QUAD 2001 *
  1190. "\xe2\x80\x82" => ' ', // EN SPACE 2002 *
  1191. "\xe2\x80\x83" => ' ', // EM SPACE 2003 *
  1192. "\xe2\x80\x84" => ' ', // THREE-PER-EM SPACE 2004 *
  1193. "\xe2\x80\x85" => ' ', // FOUR-PER-EM SPACE 2005 *
  1194. "\xe2\x80\x86" => ' ', // SIX-PER-EM SPACE 2006 *
  1195. "\xe2\x80\x87" => ' ', // FIGURE SPACE 2007 *
  1196. "\xe2\x80\x88" => ' ', // PUNCTUATION SPACE 2008 *
  1197. "\xe2\x80\x89" => ' ', // THIN SPACE 2009 *
  1198. "\xe2\x80\x8a" => ' ', // HAIR SPACE 200A *
  1199. "\xE3\x80\x80" => ' ', // IDEOGRAPHIC SPACE 3000 *
  1200. );
  1201. }
  1202. if (is_array($array))
  1203. return array_map('remove_bad_characters', $array);
  1204. // Strip out any invalid characters
  1205. $array = utf8_bad_strip($array);
  1206. // Remove control characters
  1207. $array = preg_replace('%[\x00-\x08\x0b-\x0c\x0e-\x1f]%', '', $array);
  1208. // Replace some "bad" characters
  1209. $array = str_replace(array_keys($bad_utf8_chars), array_values($bad_utf8_chars), $array);
  1210. return $array;
  1211. }
  1212. //
  1213. // Converts the file size in bytes to a human readable file size
  1214. //
  1215. function file_size($size)
  1216. {
  1217. global $lang_common;
  1218. $units = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB');
  1219. for ($i = 0; $size > 1024; $i++)
  1220. $size /= 1024;
  1221. return sprintf($lang_common['Size unit '.$units[$i]], round($size, 2));
  1222. }
  1223. //
  1224. // Fetch a list of available styles
  1225. //
  1226. function forum_list_styles()
  1227. {
  1228. $styles = array();
  1229. $d = dir(PUN_ROOT.'style');
  1230. while (($entry = $d->read()) !== false)
  1231. {
  1232. if ($entry{0} == '.')
  1233. continue;
  1234. if (substr($entry, -4) == '.css')
  1235. $styles[] = substr($entry, 0, -4);
  1236. }
  1237. $d->close();
  1238. natcasesort($styles);
  1239. return $styles;
  1240. }
  1241. //
  1242. // Fetch a list of available language packs
  1243. //
  1244. function forum_list_langs()
  1245. {
  1246. $languages = array();
  1247. $d = dir(PUN_ROOT.'lang');
  1248. while (($entry = $d->read()) !== false)
  1249. {
  1250. if ($entry{0} == '.')
  1251. continue;
  1252. if (is_dir(PUN_ROOT.'lang/'.$entry) && file_exists(PUN_ROOT.'lang/'.$entry.'/common.php'))
  1253. $languages[] = $entry;
  1254. }
  1255. $d->close();
  1256. natcasesort($languages);
  1257. return $languages;
  1258. }
  1259. //
  1260. // Split text into chunks ($inside contains all text inside $start and $end, and $outside contains all text outside)
  1261. //
  1262. function split_text($text, $start, $end, $retab = true)
  1263. {
  1264. global $pun_config;
  1265. $result = array(0 => array(), 1 => array()); // 0 = inside, 1 = outside
  1266. // split the text into parts
  1267. $parts = preg_split('%'.preg_quote($start, '%').'(.*)'.preg_quote($end, '%').'%Us', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
  1268. $num_parts = count($parts);
  1269. // preg_split results in outside parts having even indices, inside parts having odd
  1270. for ($i = 0;$i < $num_parts;$i++)
  1271. $result[1 - ($i % 2)][] = $parts[$i];
  1272. if ($pun_config['o_indent_num_spaces'] != 8 && $retab)
  1273. {
  1274. $spaces = str_repeat(' ', $pun_config['o_indent_num_spaces']);
  1275. $result[1] = str_replace("\t", $spaces, $result[1]);
  1276. }
  1277. return $result;
  1278. }
  1279. //
  1280. // Extract blocks from a text with a starting and ending string
  1281. // This function always matches the most outer block so nesting is possible
  1282. //
  1283. function extract_blocks($text, $start, $end, $retab = true)
  1284. {
  1285. global $pun_config;
  1286. $code = array();
  1287. $start_len = strlen($start);
  1288. $end_len = strlen($end);
  1289. $regex = '%(?:'.preg_quote($start, '%').'|'.preg_quote($end, '%').')%';
  1290. $matches = array();
  1291. if (preg_match_all($regex, $text, $matches))
  1292. {
  1293. $counter = $offset = 0;
  1294. $start_pos = $end_pos = false;
  1295. foreach ($matches[0] as $match)
  1296. {
  1297. if ($match == $start)
  1298. {
  1299. if ($counter == 0)
  1300. $start_pos = strpos($text, $start);
  1301. $counter++;
  1302. }
  1303. elseif ($match == $end)
  1304. {
  1305. $counter--;
  1306. if ($counter == 0)
  1307. $end_pos = strpos($text, $end, $offset + 1);
  1308. $offset = strpos($text, $end, $offset + 1);
  1309. }
  1310. if ($start_pos !== false && $end_pos !== false)
  1311. {
  1312. $code[] = substr($text, $start_pos + $start_len,
  1313. $end_pos - $start_pos - $start_len);
  1314. $text = substr_replace($text, "\1", $start_pos,
  1315. $end_pos - $start_pos + $end_len);
  1316. $start_pos = $end_pos = false;
  1317. $offset = 0;
  1318. }
  1319. }
  1320. }
  1321. if ($pun_config['o_indent_num_spaces'] != 8 && $retab)
  1322. {
  1323. $spaces = str_repeat(' ', $pun_config['o_indent_num_spaces']);
  1324. $text = str_replace("\t", $spaces, $text);
  1325. }
  1326. return array($code, $text);
  1327. }
  1328. //
  1329. // function url_valid($url) {
  1330. //
  1331. // Return associative array of valid URI components, or FALSE if $url is not
  1332. // RFC-3986 compliant. If the passed URL begins with: "www." or "ftp.", then
  1333. // "http://" or "ftp://" is prepended and the corrected full-url is stored in
  1334. // the return array with a key name "url". This value should be used by the caller.
  1335. //
  1336. // Return value: FALSE if $url is not valid, otherwise array of URI components:
  1337. // e.g.
  1338. // Given: "http://www.jmrware.com:80/articles?height=10&width=75#fragone"
  1339. // Array(
  1340. // [scheme] => http
  1341. // [authority] => www.jmrware.com:80
  1342. // [userinfo] =>
  1343. // [host] => www.jmrware.com
  1344. // [IP_literal] =>
  1345. // [IPV6address] =>
  1346. // [ls32] =>
  1347. // [IPvFuture] =>
  1348. // [IPv4address] =>
  1349. // [regname] => www.jmrware.com
  1350. // [port] => 80
  1351. // [path_abempty] => /articles
  1352. // [query] => height=10&width=75
  1353. // [fragment] => fragone
  1354. // [url] => http://www.jmrware.com:80/articles?height=10&width=75#fragone
  1355. // )
  1356. function url_valid($url)
  1357. {
  1358. if (strpos($url, 'www.') === 0) $url = 'http://'. $url;
  1359. if (strpos($url, 'ftp.') === 0) $url = 'ftp://'. $url;
  1360. if (!preg_match('/# Valid absolute URI having a non-empty, valid DNS host.
  1361. ^
  1362. (?P<scheme>[A-Za-z][A-Za-z0-9+\-.]*):\/\/
  1363. (?P<authority>
  1364. (?:(?P<userinfo>(?:[A-Za-z0-9\-._~!$&\'()*+,;=:]|%[0-9A-Fa-f]{2})*)@)?
  1365. (?P<host>
  1366. (?P<IP_literal>
  1367. \[
  1368. (?:
  1369. (?P<IPV6address>
  1370. (?: (?:[0-9A-Fa-f]{1,4}:){6}
  1371. | ::(?:[0-9A-Fa-f]{1,4}:){5}
  1372. | (?: [0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){4}
  1373. | (?:(?:[0-9A-Fa-f]{1,4}:){0,1}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){3}
  1374. | (?:(?:[0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){2}
  1375. | (?:(?:[0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})?:: [0-9A-Fa-f]{1,4}:
  1376. | (?:(?:[0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})?::
  1377. )
  1378. (?P<ls32>[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}
  1379. | (?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}
  1380. (?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)
  1381. )
  1382. | (?:(?:[0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})?:: [0-9A-Fa-f]{1,4}
  1383. | (?:(?:[0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})?::
  1384. )
  1385. | (?P<IPvFuture>[Vv][0-9A-Fa-f]+\.[A-Za-z0-9\-._~!$&\'()*+,;=:]+)
  1386. )
  1387. \]
  1388. )
  1389. | (?P<IPv4address>(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}
  1390. (?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))
  1391. | (?P<regname>(?:[A-Za-z0-9\-._~!$&\'()*+,;=]|%[0-9A-Fa-f]{2})+)
  1392. )
  1393. (?::(?P<port>[0-9]*))?
  1394. )
  1395. (?P<path_abempty>(?:\/(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})*)*)
  1396. (?:\?(?P<query> (?:[A-Za-z0-9\-._~!$&\'()*+,;=:@\\/?]|%[0-9A-Fa-f]{2})*))?
  1397. (?:\#(?P<fragment> (?:[A-Za-z0-9\-._~!$&\'()*+,;=:@\\/?]|%[0-9A-Fa-f]{2})*))?
  1398. $
  1399. /mx', $url, $m)) return FALSE;
  1400. switch ($m['scheme'])
  1401. {
  1402. case 'https':
  1403. case 'http':
  1404. if ($m['userinfo']) return FALSE; // HTTP scheme does not allow userinfo.
  1405. break;
  1406. case 'ftps':
  1407. case 'ftp':
  1408. break;
  1409. default:
  1410. return FALSE; // Unrecognised URI scheme. Default to FALSE.
  1411. }
  1412. // Validate host name conforms to DNS "dot-separated-parts".
  1413. if ($m{'regname'}) // If host regname specified, check for DNS conformance.
  1414. {
  1415. if (!preg_match('/# HTTP DNS host name.
  1416. ^ # Anchor to beginning of string.
  1417. (?!.{256}) # Overall host length is less than 256 chars.
  1418. (?: # Group dot separated host part alternatives.
  1419. [0-9A-Za-z]\. # Either a single alphanum followed by dot
  1420. | # or... part has more than one char (63 chars max).
  1421. [0-9A-Za-z] # Part first char is alphanum (no dash).
  1422. [\-0-9A-Za-z]{0,61} # Internal chars are alphanum plus dash.
  1423. [0-9A-Za-z] # Part last char is alphanum (no dash).
  1424. \. # Each part followed by literal dot.
  1425. )* # One or more parts before top level domain.
  1426. (?: # Top level domains
  1427. [A-Za-z]{2,63}| # Country codes are exactly two alpha chars.
  1428. xn--[0-9A-Za-z]{4,59}) # Internationalized Domain Name (IDN)
  1429. $ # Anchor to end of string.
  1430. /ix', $m['host'])) return FALSE;
  1431. }
  1432. $m['url'] = $url;
  1433. for ($i = 0; isset($m[$i]); ++$i) unset($m[$i]);
  1434. return $m; // return TRUE == array of useful named $matches plus the valid $url.
  1435. }
  1436. //
  1437. // Replace string matching regular expression
  1438. //
  1439. // This function takes care of possibly disabled unicode properties in PCRE builds
  1440. //
  1441. function ucp_preg_replace($pattern, $replace, $subject, $callback = false)
  1442. {
  1443. if($callback)
  1444. $replaced = preg_replace_callback($pattern, create_function('$matches', 'return '.$replace.';'), $subject);
  1445. else
  1446. $replaced = preg_replace($pattern, $replace, $subject);
  1447. // If preg_replace() returns false, this probably means unicode support is not built-in, so we need to modify the pattern a little
  1448. if ($replaced === false)
  1449. {
  1450. if (is_array($pattern))
  1451. {
  1452. foreach ($pattern as $cur_key => $cur_pattern)
  1453. $pattern[$cur_key] = str_replace('\p{L}\p{N}', '\w', $cur_pattern);
  1454. $replaced = preg_replace($pattern, $replace, $subject);
  1455. }
  1456. else
  1457. $replaced = preg_replace(str_replace('\p{L}\p{N}', '\w', $pattern), $replace, $subject);
  1458. }
  1459. return $replaced;
  1460. }
  1461. //
  1462. // A wrapper for ucp_preg_replace
  1463. //
  1464. function ucp_preg_replace_callback($pattern, $replace, $subject)
  1465. {
  1466. return ucp_preg_replace($pattern, $replace, $subject, true);
  1467. }
  1468. //
  1469. // Replace four-byte characters with a question mark
  1470. //
  1471. // As MySQL cannot properly handle four-byte characters with the default utf-8
  1472. // charset up until version 5.5.3 (where a special charset has to be used), they
  1473. // need to be replaced, by question marks in this case.
  1474. //
  1475. function strip_bad_multibyte_chars($str)
  1476. {
  1477. $result = '';
  1478. $length = strlen($str);
  1479. for ($i = 0; $i < $length; $i++)
  1480. {
  1481. // Replace four-byte characters (11110www 10zzzzzz 10yyyyyy 10xxxxxx)
  1482. $ord = ord($str[$i]);
  1483. if ($ord >= 240 && $ord <= 244)
  1484. {
  1485. $result .= '?';
  1486. $i += 3;
  1487. }
  1488. else
  1489. {
  1490. $result .= $str[$i];
  1491. }
  1492. }
  1493. return $result;
  1494. }
  1495. //
  1496. // Check whether a file/folder is writable.
  1497. //
  1498. // This function also works on Windows Server where ACLs seem to be ignored.
  1499. //
  1500. function forum_is_writable($path)
  1501. {
  1502. if (is_dir($path))
  1503. {
  1504. $path = rtrim($path, '/').'/';
  1505. return forum_is_writable($path.uniqid(mt_rand()).'.tmp');
  1506. }
  1507. // Check temporary file for read/write capabilities
  1508. $rm = file_exists($path);
  1509. $f = @fopen($path, 'a');
  1510. if ($f === false)
  1511. return false;
  1512. fclose($f);
  1513. if (!$rm)
  1514. @unlink($path);
  1515. return true;
  1516. }
  1517. // DEBUG FUNCTIONS BELOW
  1518. //
  1519. // Display executed queries (if enabled)
  1520. //
  1521. function display_saved_queries()
  1522. {
  1523. global $container, $lang_common;
  1524. // Get the queries so that we can print them out
  1525. $saved_queries = $container->get('DB')->get_saved_queries();
  1526. ?>
  1527. <div id="debug" class="blocktable">
  1528. <h2><span><?php echo $lang_common['Debug table'] ?></span></h2>
  1529. <div class="box">
  1530. <div class="inbox">
  1531. <table>
  1532. <thead>
  1533. <tr>
  1534. <th class="tcl" scope="col"><?php echo $lang_common['Query times'] ?></th>
  1535. <th class="tcr" scope="col"><?php echo $lang_common['Query'] ?></th>
  1536. </tr>
  1537. </thead>
  1538. <tbody>
  1539. <?php
  1540. $query_time_total = 0.0;
  1541. foreach ($saved_queries as $cur_query)
  1542. {
  1543. $query_time_total += $cur_query[1];
  1544. ?>
  1545. <tr>
  1546. <td class="tcl"><?php echo ($cur_query[1] != 0) ? $cur_query[1] : '&#160;' ?></td>
  1547. <td class="tcr"><?php echo pun_htmlspecialchars($cur_query[0]) ?></td>
  1548. </tr>
  1549. <?php
  1550. }
  1551. ?>
  1552. <tr>
  1553. <td class="tcl" colspan="2"><?php printf($lang_common['Total query time'], $query_time_total.' s') ?></td>
  1554. </tr>
  1555. </tbody>
  1556. </table>
  1557. </div>
  1558. </div>
  1559. </div>
  1560. <?php
  1561. }
  1562. //
  1563. // Dump contents of variable(s)
  1564. //
  1565. function dump()
  1566. {
  1567. echo '<pre>';
  1568. $num_args = func_num_args();
  1569. for ($i = 0; $i < $num_args; ++$i)
  1570. {
  1571. print_r(func_get_arg($i));
  1572. echo "\n\n";
  1573. }
  1574. echo '</pre>';
  1575. exit;
  1576. }
  1577. // *** Visman ***
  1578. //
  1579. // генерация javascript в футере
  1580. //
  1581. function generation_js($arr)
  1582. {
  1583. global $container;
  1584. $res = '';
  1585. if (!empty($arr['j']))
  1586. $res.= '<script type="text/javascript" src="'.$container->getParameter('JQUERY_LINK').'"></script>'."\n";
  1587. if (!empty($arr['f']))
  1588. $res.= '<script type="text/javascript" src="'.implode('"></script>'."\n".'<script type="text/javascript" src="', $arr['f']).'"></script>'."\n";
  1589. if (!empty($arr['c']))
  1590. $res.= '<script type="text/javascript">'."\n".'/* <![CDATA[ */'."\n".implode("\n", $arr['c'])."\n".'/* ]]> */'."\n".'</script>'."\n";
  1591. return $res;
  1592. }
  1593. //
  1594. // MOD Кто в этой теме
  1595. // Отложенное выполнение запроса
  1596. //
  1597. function witt_query($var = NULL)
  1598. {
  1599. global $container;
  1600. static $query;
  1601. $db = $container->get('DB');
  1602. if (!defined('WITT_ENABLE'))
  1603. {
  1604. if (is_string($var))
  1605. $db->query(str_replace(array(':?comma?:', ':?equal?:', ':?column?:', ':?value?:'), '', $var)) or error('Unable to insert/update into online list', __FILE__, __LINE__, $db->error());
  1606. }
  1607. else
  1608. {
  1609. if (is_string($var))
  1610. $query = $var;
  1611. elseif (!empty($query))
  1612. {
  1613. if (is_array($var) && isset($var['column']) && isset($var['value']))
  1614. $query = str_replace(array(':?comma?:', ':?equal?:', ':?column?:', ':?value?:'), array(', ', '=', $var['column'], '\''.$db->escape($var['value']).'\''), $query);
  1615. else
  1616. $query = str_replace(array(':?comma?:', ':?equal?:', ':?column?:', ':?value?:'), '', $query);
  1617. $db->query($query) or error('Unable to insert/update into online list', __FILE__, __LINE__, $db->error());
  1618. $query = NULL;
  1619. }
  1620. }
  1621. }
  1622. //
  1623. // MOD Subforums
  1624. //
  1625. function sf_crumbs($id)
  1626. {
  1627. global $sf_array_desc;
  1628. $str = '';
  1629. while (isset($sf_array_desc[$id]) && isset($sf_array_desc[$id][0]))
  1630. {
  1631. $id = $sf_array_desc[$id][0];
  1632. $str = "\t\t\t".'<li><span>»&#160;</span><strong><a href="viewforum.php?id='.$id.'">'.pun_htmlspecialchars($sf_array_desc[$id]['forum_name']).'</a></strong></li>'."\n".$str;
  1633. }
  1634. return $str;
  1635. }
  1636. /******************************************************************************/
  1637. /*
  1638. function __($data, ...$args)
  1639. {
  1640. static $tr = [];
  1641. static $loaded = [];
  1642. if ($data instanceof \R2\DependencyInjection\ContainerInterface) {
  1643. if (isset($loaded[$args[0]])) {
  1644. return;
  1645. }
  1646. $dir = $data->getParameter('DIR_LANG');
  1647. $lang = isset($args[1]) ? $args[1] : $data->get('user')['language'];
  1648. if (file_exists($dir . $lang . '/' . $args[0] . '.php')) {
  1649. $tr = (include $dir . $lang . '/' . $args[0] . '.php') + $tr;
  1650. } elseif (file_exists($dir . 'English/' . $args[0] . '.php')) {
  1651. $tr = (include $dir . 'English/' . $args[0] . '.php') + $tr;
  1652. }
  1653. $loaded[$args[0]] = true;
  1654. } else {
  1655. if (! isset($tr[$data])) {
  1656. return $data;
  1657. } elseif (empty($args)) {
  1658. return $tr[$data];
  1659. } else {
  1660. return sprintf($tr[$data], ...$args);
  1661. }
  1662. }
  1663. }
  1664. */
  1665. function __($data, ...$args)
  1666. {
  1667. static $lang;
  1668. if (empty($lang)) {
  1669. $lang = $data;
  1670. return;
  1671. }
  1672. $tr = $lang->get($data);
  1673. if (is_array($tr)) {
  1674. if (isset($args[0]) && is_numeric($args[0])) {
  1675. $n = array_shift($args);
  1676. eval('$n = (int) ' . $tr['plural']);
  1677. $tr = $tr[$n];
  1678. } else {
  1679. $tr = $tr[0];
  1680. }
  1681. }
  1682. if (empty($args)) {
  1683. return $tr;
  1684. } elseif (is_array($args[0])) {
  1685. return strtr($tr, $args[0]);
  1686. } else {
  1687. return sprintf($tr, ...$args);
  1688. }
  1689. }