Load.php 121 KB

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