functions.php 75 KB

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