PageRenderTime 51ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/forum/Sources/Subs-Auth.php

https://github.com/leftnode/nooges.com
PHP | 837 lines | 500 code | 139 blank | 198 comment | 95 complexity | e5a5c9c13694ae6debe800462be18b45 MD5 | raw file
  1. <?php
  2. /**********************************************************************************
  3. * Subs-Auth.php *
  4. ***********************************************************************************
  5. * SMF: Simple Machines Forum *
  6. * Open-Source Project Inspired by Zef Hemel (zef@zefhemel.com) *
  7. * =============================================================================== *
  8. * Software Version: SMF 2.0 RC2 *
  9. * Software by: Simple Machines (http://www.simplemachines.org) *
  10. * Copyright 2006-2009 by: Simple Machines LLC (http://www.simplemachines.org) *
  11. * 2001-2006 by: Lewis Media (http://www.lewismedia.com) *
  12. * Support, News, Updates at: http://www.simplemachines.org *
  13. ***********************************************************************************
  14. * This program is free software; you may redistribute it and/or modify it under *
  15. * the terms of the provided license as published by Simple Machines LLC. *
  16. * *
  17. * This program is distributed in the hope that it is and will be useful, but *
  18. * WITHOUT ANY WARRANTIES; without even any implied warranty of MERCHANTABILITY *
  19. * or FITNESS FOR A PARTICULAR PURPOSE. *
  20. * *
  21. * See the "license.txt" file for details of the Simple Machines license. *
  22. * The latest version can always be found at http://www.simplemachines.org. *
  23. **********************************************************************************/
  24. if (!defined('SMF'))
  25. die('Hacking attempt...');
  26. /* This file has functions in it to do with authentication, user handling,
  27. and the like. It provides these functions:
  28. void setLoginCookie(int cookie_length, int id_member, string password = '')
  29. - sets the SMF-style login cookie and session based on the id_member
  30. and password passed.
  31. - password should be already encrypted with the cookie salt.
  32. - logs the user out if id_member is zero.
  33. - sets the cookie and session to last the number of seconds specified
  34. by cookie_length.
  35. - when logging out, if the globalCookies setting is enabled, attempts
  36. to clear the subdomain's cookie too.
  37. array url_parts(bool local, bool global)
  38. - returns the path and domain to set the cookie on.
  39. - normally, local and global should be the localCookies and
  40. globalCookies settings, respectively.
  41. - uses boardurl to determine these two things.
  42. - returns an array with domain and path in it, in that order.
  43. void KickGuest()
  44. - throws guests out to the login screen when guest access is off.
  45. - sets $_SESSION['login_url'] to $_SERVER['REQUEST_URL'].
  46. - uses the 'kick_guest' sub template found in Login.template.php.
  47. void InMaintenance()
  48. - display a message about being in maintenance mode.
  49. - display a login screen with sub template 'maintenance'.
  50. void adminLogin()
  51. - double check the verity of the admin by asking for his or her
  52. password.
  53. - loads Login.template.php and uses the admin_login sub template.
  54. - sends data to template so the admin is sent on to the page they
  55. wanted if their password is correct, otherwise they can try
  56. again.
  57. string adminLogin_outputPostVars(string key, string value)
  58. - used by the adminLogin() function.
  59. - returns 'hidden' HTML form fields, containing key-value-pairs.
  60. - if 'value' is an array, the function is called recursively.
  61. void show_db_error(bool loadavg = false)
  62. - called by db_fatal_error() function (in Errors.php.)
  63. - shows a complete page independent of language files or themes.
  64. - used only if there's no way to connect to the database or the
  65. load averages are too high to do so.
  66. - loadavg means this is a load average problem, not a database error.
  67. - stops further execution of the script.
  68. array findMembers(array names, bool use_wildcards = false,
  69. bool buddies_only = false, int max = 500)
  70. - searches for members whose username, display name, or e-mail address
  71. match the given pattern of array names.
  72. - accepts wildcards ? and * in the patern if use_wildcards is set.
  73. - retrieves a maximum of max members, if passed.
  74. - searches only buddies if buddies_only is set.
  75. - returns an array containing information about the matching members.
  76. void JSMembers()
  77. - called by index.php?action=findmember.
  78. - is used as a popup for searching members.
  79. - uses sub template find_members of the Help template.
  80. - also used to add members for PM's sent using wap2/imode protocol.
  81. void RequestMembers()
  82. - used by javascript to find members matching the request.
  83. - outputs each member name on its own line.
  84. void resetPassword(int id_member, string username = null)
  85. - called by Profile.php when changing someone's username.
  86. - checks the validity of the new username.
  87. - generates and sets a new password for the given user.
  88. - mails the new password to the email address of the user.
  89. - if username is not set, only a new password is generated and sent.
  90. string validateUsername(int memID, string username)
  91. - checks a username obeys a load of rules. Returns null if fine.
  92. string validatePassword(string password, string username,
  93. array restrict_in = none)
  94. - called when registering/choosing a password.
  95. - checks the password obeys the current forum settings for password
  96. strength.
  97. - if password checking is enabled, will check that none of the words
  98. in restrict_in appear in the password.
  99. - returns an error identifier if the password is invalid, or null.
  100. void rebuildModCache()
  101. - stores some useful information on the current users moderation powers in the session.
  102. */
  103. // Actually set the login cookie...
  104. function setLoginCookie($cookie_length, $id, $password = '')
  105. {
  106. global $cookiename, $boardurl, $modSettings;
  107. // If changing state force them to re-address some permission caching.
  108. $_SESSION['mc']['time'] = 0;
  109. // The cookie may already exist, and have been set with different options.
  110. $cookie_state = (empty($modSettings['localCookies']) ? 0 : 1) | (empty($modSettings['globalCookies']) ? 0 : 2);
  111. if (isset($_COOKIE[$cookiename]) && preg_match('~^a:[34]:\{i:0;(i:\d{1,6}|s:[1-8]:"\d{1,8}");i:1;s:(0|40):"([a-fA-F0-9]{40})?";i:2;[id]:\d{1,14};(i:3;i:\d;)?\}$~', $_COOKIE[$cookiename]) === 1)
  112. {
  113. $array = @unserialize($_COOKIE[$cookiename]);
  114. // Out with the old, in with the new!
  115. if (isset($array[3]) && $array[3] != $cookie_state)
  116. {
  117. $cookie_url = url_parts($array[3] & 1 > 0, $array[3] & 2 > 0);
  118. setcookie($cookiename, serialize(array(0, '', 0)), time() - 3600, $cookie_url[1], $cookie_url[0], !empty($modSettings['secureCookies']));
  119. }
  120. }
  121. // Get the data and path to set it on.
  122. $data = serialize(empty($id) ? array(0, '', 0) : array($id, $password, time() + $cookie_length, $cookie_state));
  123. $cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies']));
  124. // Set the cookie, $_COOKIE, and session variable.
  125. setcookie($cookiename, $data, time() + $cookie_length, $cookie_url[1], $cookie_url[0], !empty($modSettings['secureCookies']));
  126. // If subdomain-independent cookies are on, unset the subdomain-dependent cookie too.
  127. if (empty($id) && !empty($modSettings['globalCookies']))
  128. setcookie($cookiename, $data, time() + $cookie_length, $cookie_url[1], '', !empty($modSettings['secureCookies']));
  129. // Any alias URLs? This is mainly for use with frames, etc.
  130. if (!empty($modSettings['forum_alias_urls']))
  131. {
  132. $aliases = explode(',', $modSettings['forum_alias_urls']);
  133. $temp = $boardurl;
  134. foreach ($aliases as $alias)
  135. {
  136. // Fake the $boardurl so we can set a different cookie.
  137. $alias = strtr(trim($alias), array('http://' => '', 'https://' => ''));
  138. $boardurl = 'http://' . $alias;
  139. $cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies']));
  140. if ($cookie_url[0] == '')
  141. $cookie_url[0] = strtok($alias, '/');
  142. setcookie($cookiename, $data, time() + $cookie_length, $cookie_url[1], $cookie_url[0], !empty($modSettings['secureCookies']));
  143. }
  144. $boardurl = $temp;
  145. }
  146. $_COOKIE[$cookiename] = $data;
  147. // Make sure the user logs in with a new session ID.
  148. if (!isset($_SESSION['login_' . $cookiename]) || $_SESSION['login_' . $cookiename] !== $data)
  149. {
  150. // Backup and remove the old session.
  151. $oldSessionData = $_SESSION;
  152. $_SESSION = array();
  153. session_destroy();
  154. // Recreate and restore the new session.
  155. loadSession();
  156. session_regenerate_id();
  157. $_SESSION = $oldSessionData;
  158. // Version 4.3.2 didn't store the cookie of the new session.
  159. if (version_compare(PHP_VERSION, '4.3.2') === 0 || (isset($_COOKIE[session_name()]) && $_COOKIE[session_name()] != session_id()))
  160. setcookie(session_name(), session_id(), time() + $cookie_length, $cookie_url[1], '', !empty($modSettings['secureCookies']));
  161. $_SESSION['login_' . $cookiename] = $data;
  162. }
  163. }
  164. // PHP < 4.3.2 doesn't have this function
  165. if (!function_exists('session_regenerate_id'))
  166. {
  167. function session_regenerate_id()
  168. {
  169. // Too late to change the session now.
  170. if (headers_sent())
  171. return false;
  172. session_id(strtolower(md5(uniqid(mt_rand(), true))));
  173. return true;
  174. }
  175. }
  176. // Get the domain and path for the cookie...
  177. function url_parts($local, $global)
  178. {
  179. global $boardurl;
  180. // Parse the URL with PHP to make life easier.
  181. $parsed_url = parse_url($boardurl);
  182. // Is local cookies off?
  183. if (empty($parsed_url['path']) || !$local)
  184. $parsed_url['path'] = '';
  185. // Globalize cookies across domains (filter out IP-addresses)?
  186. if ($global && preg_match('~^\d{1,3}(\.\d{1,3}){3}$~', $parsed_url['host']) == 0 && preg_match('~(?:[^\.]+\.)?([^\.]{2,}\..+)\z~i', $parsed_url['host'], $parts) == 1)
  187. $parsed_url['host'] = '.' . $parts[1];
  188. // We shouldn't use a host at all if both options are off.
  189. elseif (!$local && !$global)
  190. $parsed_url['host'] = '';
  191. // The host also shouldn't be set if there aren't any dots in it.
  192. elseif (!isset($parsed_url['host']) || strpos($parsed_url['host'], '.') === false)
  193. $parsed_url['host'] = '';
  194. return array($parsed_url['host'], $parsed_url['path'] . '/');
  195. }
  196. // Kick out a guest when guest access is off...
  197. function KickGuest()
  198. {
  199. global $txt, $context;
  200. loadLanguage('Login');
  201. loadTemplate('Login');
  202. // Never redirect to an attachment
  203. if (strpos($_SERVER['REQUEST_URL'], 'dlattach') === false)
  204. $_SESSION['login_url'] = $_SERVER['REQUEST_URL'];
  205. $context['sub_template'] = 'kick_guest';
  206. $context['page_title'] = $txt['login'];
  207. }
  208. // Display a message about the forum being in maintenance mode, etc.
  209. function InMaintenance()
  210. {
  211. global $txt, $mtitle, $mmessage, $context;
  212. loadLanguage('Login');
  213. loadTemplate('Login');
  214. // Send a 503 header, so search engines don't bother indexing while we're in maintenance mode.
  215. header('HTTP/1.1 503 Service Temporarily Unavailable');
  216. // Basic template stuff..
  217. $context['sub_template'] = 'maintenance';
  218. $context['title'] = &$mtitle;
  219. $context['description'] = &$mmessage;
  220. $context['page_title'] = $txt['maintain_mode'];
  221. }
  222. function adminLogin()
  223. {
  224. global $context, $scripturl, $txt, $user_info, $user_settings;
  225. loadLanguage('Admin');
  226. loadTemplate('Login');
  227. // They used a wrong password, log it and unset that.
  228. if (isset($_POST['admin_hash_pass']) || isset($_POST['admin_pass']))
  229. {
  230. $txt['security_wrong'] = sprintf($txt['security_wrong'], isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : $txt['unknown'], $_SERVER['HTTP_USER_AGENT'], $user_info['ip']);
  231. log_error($txt['security_wrong'], 'critical');
  232. if (isset($_POST['admin_hash_pass']))
  233. unset($_POST['admin_hash_pass']);
  234. if (isset($_POST['admin_pass']))
  235. unset($_POST['admin_pass']);
  236. $context['incorrect_password'] = true;
  237. }
  238. // Figure out the get data and post data.
  239. $context['get_data'] = '?' . construct_query_string($_GET);
  240. $context['post_data'] = '';
  241. // Now go through $_POST. Make sure the session hash is sent.
  242. $_POST[$context['session_var']] = $context['session_id'];
  243. foreach ($_POST as $k => $v)
  244. $context['post_data'] .= adminLogin_outputPostVars($k, $v);
  245. // Now we'll use the admin_login sub template of the Login template.
  246. $context['sub_template'] = 'admin_login';
  247. // And title the page something like "Login".
  248. if (!isset($context['page_title']))
  249. $context['page_title'] = $txt['login'];
  250. obExit();
  251. // We MUST exit at this point, because otherwise we CANNOT KNOW that the user is privileged.
  252. trigger_error('Hacking attempt...', E_USER_ERROR);
  253. }
  254. function adminLogin_outputPostVars($k, $v)
  255. {
  256. global $smcFunc;
  257. if (!is_array($v))
  258. return '
  259. <input type="hidden" name="' . $k . '" value="' . strtr($v, array('"' => '&quot;', '<' => '&lt;', '>' => '&gt;')) . '" />';
  260. else
  261. {
  262. $ret = '';
  263. foreach ($v as $k2 => $v2)
  264. $ret .= adminLogin_outputPostVars($k . '[' . $k2 . ']', $v2);
  265. return $ret;
  266. }
  267. }
  268. function construct_query_string($get)
  269. {
  270. global $scripturl;
  271. $query_string = '';
  272. // Awww, darn. The $scripturl contains GET stuff!
  273. $q = strpos($scripturl, '?');
  274. if ($q !== false)
  275. {
  276. parse_str(preg_replace('/&(\w+)(?=&|$)/', '&$1=', strtr(substr($scripturl, $q + 1), ';', '&')), $temp);
  277. foreach ($get as $k => $v)
  278. {
  279. // Only if it's not already in the $scripturl!
  280. if (!isset($temp[$k]))
  281. $query_string .= urlencode($k) . '=' . urlencode($v) . ';';
  282. // If it changed, put it out there, but with an ampersand.
  283. elseif ($temp[$k] != $get[$k])
  284. $query_string .= urlencode($k) . '=' . urlencode($v) . '&amp;';
  285. }
  286. }
  287. else
  288. {
  289. // Add up all the data from $_GET into get_data.
  290. foreach ($get as $k => $v)
  291. $query_string .= urlencode($k) . '=' . urlencode($v) . ';';
  292. }
  293. $query_string = substr($query_string, 0, -1);
  294. return $query_string;
  295. }
  296. // Show an error message for the connection problems.
  297. function show_db_error($loadavg = false)
  298. {
  299. global $sourcedir, $mbname, $maintenance, $mtitle, $mmessage, $modSettings;
  300. global $db_connection, $webmaster_email, $db_last_error, $db_error_send, $smcFunc;
  301. // Don't cache this page!
  302. header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
  303. header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
  304. header('Cache-Control: no-cache');
  305. // Send the right error codes.
  306. header('HTTP/1.1 503 Service Temporarily Unavailable');
  307. header('Status: 503 Service Temporarily Unavailable');
  308. header('Retry-After: 3600');
  309. if ($loadavg == false)
  310. {
  311. // For our purposes, we're gonna want this on if at all possible.
  312. $modSettings['cache_enable'] = '1';
  313. if (($temp = cache_get_data('db_last_error', 600)) !== null)
  314. $db_last_error = max($db_last_error, $temp);
  315. if ($db_last_error < time() - 3600 * 24 * 3 && empty($maintenance) && !empty($db_error_send))
  316. {
  317. require_once($sourcedir . '/Subs-Admin.php');
  318. // Avoid writing to the Settings.php file if at all possible; use shared memory instead.
  319. cache_put_data('db_last_error', time(), 600);
  320. if (($temp = cache_get_data('db_last_error', 600)) === null)
  321. updateSettingsFile(array('db_last_error' => time()));
  322. // Language files aren't loaded yet :(.
  323. $db_error = @$smcFunc['db_error']($db_connection);
  324. @mail($webmaster_email, $mbname . ': SMF Database Error!', 'There has been a problem with the database!' . ($db_error == '' ? '' : "\n" . $smcFunc['db_title'] . ' reported:' . "\n" . $db_error) . "\n\n" . 'This is a notice email to let you know that SMF could not connect to the database, contact your host if this continues.');
  325. }
  326. }
  327. if (!empty($maintenance))
  328. echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  329. <html xmlns="http://www.w3.org/1999/xhtml">
  330. <head>
  331. <meta name="robots" content="noindex" />
  332. <title>', $mtitle, '</title>
  333. </head>
  334. <body>
  335. <h3>', $mtitle, '</h3>
  336. ', $mmessage, '
  337. </body>
  338. </html>';
  339. // If this is a load average problem, display an appropriate message (but we still don't have language files!)
  340. elseif ($loadavg)
  341. echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  342. <html xmlns="http://www.w3.org/1999/xhtml">
  343. <head>
  344. <meta name="robots" content="noindex" />
  345. <title>Temporarily Unavailable</title>
  346. </head>
  347. <body>
  348. <h3>Temporarily Unavailable</h3>
  349. Due to high stress on the server the forum is temporarily unavailable. Please try again later.
  350. </body>
  351. </html>';
  352. // What to do? Language files haven't and can't be loaded yet...
  353. else
  354. echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  355. <html xmlns="http://www.w3.org/1999/xhtml">
  356. <head>
  357. <meta name="robots" content="noindex" />
  358. <title>Connection Problems</title>
  359. </head>
  360. <body>
  361. <h3>Connection Problems</h3>
  362. Sorry, SMF was unable to connect to the database. This may be caused by the server being busy. Please try again later.
  363. </body>
  364. </html>';
  365. die;
  366. }
  367. // Find members by email address, username, or real name.
  368. function findMembers($names, $use_wildcards = false, $buddies_only = false, $max = 500)
  369. {
  370. global $scripturl, $user_info, $modSettings, $smcFunc;
  371. // If it's not already an array, make it one.
  372. if (!is_array($names))
  373. $names = explode(',', $names);
  374. $maybe_email = false;
  375. foreach ($names as $i => $name)
  376. {
  377. // Trim, and fix wildcards for each name.
  378. $names[$i] = trim($smcFunc['strtolower']($name));
  379. $maybe_email |= strpos($name, '@') !== false;
  380. // Make it so standard wildcards will work. (* and ?)
  381. if ($use_wildcards)
  382. $names[$i] = strtr($names[$i], array('%' => '\%', '_' => '\_', '*' => '%', '?' => '_', '\'' => '&#039;'));
  383. else
  384. $names[$i] = strtr($names[$i], array('\'' => '&#039;'));
  385. }
  386. // What are we using to compare?
  387. $comparison = $use_wildcards ? 'LIKE' : '=';
  388. // Nothing found yet.
  389. $results = array();
  390. // This ensures you can't search someones email address if you can't see it.
  391. $email_condition = allowedTo('moderate_forum') ? '' : 'hide_email = 0 AND ';
  392. if ($use_wildcards || $maybe_email)
  393. $email_condition = '
  394. OR (' . $email_condition . 'email_address ' . $comparison . ' \'' . implode( '\') OR (' . $email_condition . ' email_address ' . $comparison . ' \'', $names) . '\')';
  395. else
  396. $email_condition = '';
  397. // Get the case of the columns right - but only if we need to as things like MySQL will go slow needlessly otherwise.
  398. $member_name = $smcFunc['db_case_sensitive'] ? 'LOWER(member_name)' : 'member_name';
  399. $real_name = $smcFunc['db_case_sensitive'] ? 'LOWER(real_name)' : 'real_name';
  400. // Search by username, display name, and email address.
  401. $request = $smcFunc['db_query']('', '
  402. SELECT id_member, member_name, real_name, email_address, hide_email
  403. FROM {db_prefix}members
  404. WHERE ({raw:member_name_search}
  405. OR {raw:real_name_search} {raw:email_condition})
  406. ' . ($buddies_only ? 'AND id_member IN ({array_int:buddy_list})' : '') . '
  407. AND is_activated IN (1, 11)
  408. LIMIT {int:limit}',
  409. array(
  410. 'buddy_list' => $user_info['buddies'],
  411. 'member_name_search' => $member_name . ' ' . $comparison . ' \'' . implode( '\' OR ' . $member_name . ' ' . $comparison . ' \'', $names) . '\'',
  412. 'real_name_search' => $real_name . ' ' . $comparison . ' \'' . implode( '\' OR ' . $real_name . ' ' . $comparison . ' \'', $names) . '\'',
  413. 'email_condition' => $email_condition,
  414. 'limit' => $max,
  415. )
  416. );
  417. while ($row = $smcFunc['db_fetch_assoc']($request))
  418. {
  419. $results[$row['id_member']] = array(
  420. 'id' => $row['id_member'],
  421. 'name' => $row['real_name'],
  422. 'username' => $row['member_name'],
  423. 'email' => in_array(showEmailAddress(!empty($row['hide_email']), $row['id_member']), array('yes', 'yes_permission_override')) ? $row['email_address'] : '',
  424. 'href' => $scripturl . '?action=profile;u=' . $row['id_member'],
  425. 'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>'
  426. );
  427. }
  428. $smcFunc['db_free_result']($request);
  429. // Return all the results.
  430. return $results;
  431. }
  432. function JSMembers()
  433. {
  434. global $context, $scripturl, $user_info, $smcFunc;
  435. checkSession('get');
  436. if (WIRELESS)
  437. $context['sub_template'] = WIRELESS_PROTOCOL . '_pm';
  438. else
  439. {
  440. // Why is this in the Help template, you ask? Well, erm... it helps you. Does that work?
  441. loadTemplate('Help');
  442. $context['template_layers'] = array();
  443. $context['sub_template'] = 'find_members';
  444. }
  445. if (isset($_REQUEST['search']))
  446. $context['last_search'] = $smcFunc['htmlspecialchars']($_REQUEST['search'], ENT_QUOTES);
  447. else
  448. $_REQUEST['start'] = 0;
  449. // Allow the user to pass the input to be added to to the box.
  450. $context['input_box_name'] = isset($_REQUEST['input']) && preg_match('~^[\w-]+$~', $_REQUEST['input']) === 1 ? $_REQUEST['input'] : 'to';
  451. // Take the delimiter over GET in case it's \n or something.
  452. $context['delimiter'] = isset($_REQUEST['delim']) ? ($_REQUEST['delim'] == 'LB' ? '\\n' : $smcFunc['htmlspecialchars']($_REQUEST['delim'])) : ', ';
  453. $context['quote_results'] = !empty($_REQUEST['quote']);
  454. // List all the results.
  455. $context['results'] = array();
  456. // Some buddy related settings ;)
  457. $context['show_buddies'] = !empty($user_info['buddies']);
  458. $context['buddy_search'] = isset($_REQUEST['buddies']);
  459. // If the user has done a search, well - search.
  460. if (isset($_REQUEST['search']))
  461. {
  462. $_REQUEST['search'] = $smcFunc['htmlspecialchars']($_REQUEST['search'], ENT_QUOTES);
  463. $context['results'] = findMembers(array($_REQUEST['search']), true, $context['buddy_search']);
  464. $total_results = count($context['results']);
  465. $context['page_index'] = constructPageIndex($scripturl . '?action=findmember;search=' . $context['last_search'] . ';' . $context['session_var'] . '=' . $context['session_id'] . ';input=' . $context['input_box_name'] . ($context['quote_results'] ? ';quote=1' : '') . ($context['buddy_search'] ? ';buddies' : ''), $_REQUEST['start'], $total_results, 7);
  466. // Determine the navigation context (especially useful for the wireless template).
  467. $base_url = $scripturl . '?action=findmember;search=' . urlencode($context['last_search']) . (empty($_REQUEST['u']) ? '' : ';u=' . $_REQUEST['u']) . ';' . $context['session_var'] . '=' . $context['session_id'];
  468. $context['links'] = array(
  469. 'first' => $_REQUEST['start'] >= 7 ? $base_url . ';start=0' : '',
  470. 'prev' => $_REQUEST['start'] >= 7 ? $base_url . ';start=' . ($_REQUEST['start'] - 7) : '',
  471. 'next' => $_REQUEST['start'] + 7 < $total_results ? $base_url . ';start=' . ($_REQUEST['start'] + 7) : '',
  472. 'last' => $_REQUEST['start'] + 7 < $total_results ? $base_url . ';start=' . (floor(($total_results - 1) / 7) * 7) : '',
  473. 'up' => $scripturl . '?action=pm;sa=send' . (empty($_REQUEST['u']) ? '' : ';u=' . $_REQUEST['u']),
  474. );
  475. $context['page_info'] = array(
  476. 'current_page' => $_REQUEST['start'] / 7 + 1,
  477. 'num_pages' => floor(($total_results - 1) / 7) + 1
  478. );
  479. $context['results'] = array_slice($context['results'], $_REQUEST['start'], 7);
  480. }
  481. else
  482. $context['links']['up'] = $scripturl . '?action=pm;sa=send' . (empty($_REQUEST['u']) ? '' : ';u=' . $_REQUEST['u']);
  483. }
  484. function RequestMembers()
  485. {
  486. global $user_info, $txt, $smcFunc;
  487. checkSession('get');
  488. $_REQUEST['search'] = $smcFunc['htmlspecialchars']($_REQUEST['search']) . '*';
  489. $_REQUEST['search'] = trim($smcFunc['strtolower']($_REQUEST['search']));
  490. $_REQUEST['search'] = strtr($_REQUEST['search'], array('%' => '\%', '_' => '\_', '*' => '%', '?' => '_', '&#038;' => '&amp;'));
  491. if (function_exists('iconv'))
  492. header('Content-Type: text/plain; charset=UTF-8');
  493. $request = $smcFunc['db_query']('', '
  494. SELECT real_name
  495. FROM {db_prefix}members
  496. WHERE real_name LIKE {string:search}' . (isset($_REQUEST['buddies']) ? '
  497. AND id_member IN ({array_int:buddy_list})' : '') . '
  498. AND is_activated IN (1, 11)
  499. LIMIT ' . (strlen($_REQUEST['search']) <= 2 ? '100' : '800'),
  500. array(
  501. 'buddy_list' => $user_info['buddies'],
  502. 'search' => $_REQUEST['search'],
  503. )
  504. );
  505. while ($row = $smcFunc['db_fetch_assoc']($request))
  506. {
  507. if (function_exists('iconv'))
  508. {
  509. $utf8 = iconv($txt['lang_character_set'], 'UTF-8', $row['real_name']);
  510. if ($utf8)
  511. $row['real_name'] = $utf8;
  512. }
  513. $row['real_name'] = strtr($row['real_name'], array('&amp;' => '&#038;', '&lt;' => '&#060;', '&gt;' => '&#062;', '&quot;' => '&#034;'));
  514. if (preg_match('~&#\d+;~', $row['real_name']) != 0)
  515. {
  516. $fixchar = create_function('$n', '
  517. if ($n < 128)
  518. return chr($n);
  519. elseif ($n < 2048)
  520. return chr(192 | $n >> 6) . chr(128 | $n & 63);
  521. elseif ($n < 65536)
  522. return chr(224 | $n >> 12) . chr(128 | $n >> 6 & 63) . chr(128 | $n & 63);
  523. else
  524. return chr(240 | $n >> 18) . chr(128 | $n >> 12 & 63) . chr(128 | $n >> 6 & 63) . chr(128 | $n & 63);');
  525. $row['real_name'] = preg_replace('~&#(\d+);~e', '$fixchar(\'$1\')', $row['real_name']);
  526. }
  527. echo $row['real_name'], "\n";
  528. }
  529. $smcFunc['db_free_result']($request);
  530. obExit(false);
  531. }
  532. // This function generates a random password for a user and emails it to them.
  533. function resetPassword($memID, $username = null)
  534. {
  535. global $scripturl, $context, $txt, $sourcedir, $modSettings, $smcFunc, $language;
  536. // Language... and a required file.
  537. loadLanguage('Login');
  538. require_once($sourcedir . '/Subs-Post.php');
  539. // Get some important details.
  540. $request = $smcFunc['db_query']('', '
  541. SELECT member_name, email_address, lngfile
  542. FROM {db_prefix}members
  543. WHERE id_member = {int:id_member}',
  544. array(
  545. 'id_member' => $memID,
  546. )
  547. );
  548. list ($user, $email, $lngfile) = $smcFunc['db_fetch_row']($request);
  549. $smcFunc['db_free_result']($request);
  550. if ($username !== null)
  551. {
  552. $old_user = $user;
  553. $user = trim($username);
  554. }
  555. // Generate a random password.
  556. $newPassword = substr(preg_replace('/\W/', '', md5(mt_rand())), 0, 10);
  557. $newPassword_sha1 = sha1(strtolower($user) . $newPassword);
  558. // Do some checks on the username if needed.
  559. if ($username !== null)
  560. {
  561. validateUsername($memID, $user);
  562. // Update the database...
  563. updateMemberData($memID, array('member_name' => $user, 'passwd' => $newPassword_sha1));
  564. }
  565. else
  566. updateMemberData($memID, array('passwd' => $newPassword_sha1));
  567. if (isset($modSettings['integrate_reset_pass']) && function_exists($modSettings['integrate_reset_pass']))
  568. call_user_func($modSettings['integrate_reset_pass'], $old_user, $user, $newPassword);
  569. $replacements = array(
  570. 'USERNAME' => $user,
  571. 'PASSWORD' => $newPassword,
  572. );
  573. $emaildata = loadEmailTemplate('change_password', $replacements, empty($lngfile) || empty($modSettings['userLanguage']) ? $language : $lngfile);
  574. // Send them the email informing them of the change - then we're done!
  575. sendmail($email, $emaildata['subject'], $emaildata['body'], null, null, false, 0);
  576. }
  577. // Is this a valid username?
  578. function validateUsername($memID, $username)
  579. {
  580. global $sourcedir, $txt;
  581. // No name?! How can you register with no name?
  582. if ($username == '')
  583. fatal_lang_error('need_username', false);
  584. // Only these characters are permitted.
  585. if (in_array($username, array('_', '|')) || preg_match('~[<>&"\'=\\\\]~', preg_replace('~&#(?:\\d{1,7}|x[0-9a-fA-F]{1,6});~', '', $username)) != 0 || strpos($username, '[code') !== false || strpos($username, '[/code') !== false)
  586. fatal_lang_error('error_invalid_characters_username', false);
  587. if (stristr($username, $txt['guest_title']) !== false)
  588. fatal_lang_error('username_reserved', true, array($txt['guest_title']));
  589. require_once($sourcedir . '/Subs-Members.php');
  590. if (isReservedName($username, $memID, false))
  591. fatal_error('(' . htmlspecialchars($username) . ') ' . $txt['name_in_use'], false);
  592. return null;
  593. }
  594. // This function simply checks whether a password meets the current forum rules.
  595. function validatePassword($password, $username, $restrict_in = array())
  596. {
  597. global $modSettings, $smcFunc;
  598. // Perform basic requirements first.
  599. if (strlen($password) < (empty($modSettings['password_strength']) ? 4 : 8))
  600. return 'short';
  601. // Is this enough?
  602. if (empty($modSettings['password_strength']))
  603. return null;
  604. // Otherwise, perform the medium strength test - checking if password appears in the restricted string.
  605. if (preg_match('~\b' . preg_quote($password, '~') . '\b~', implode(' ', $restrict_in)) != 0)
  606. return 'restricted_words';
  607. elseif ($smcFunc['strpos']($password, $username) !== false)
  608. return 'restricted_words';
  609. // !!! If pspell is available, use it on the word, and return restricted_words if it doesn't give "bad spelling"?
  610. // If just medium, we're done.
  611. if ($modSettings['password_strength'] == 1)
  612. return null;
  613. // Otherwise, hard test next, check for numbers and letters, uppercase too.
  614. $good = preg_match('~(\D\d|\d\D)~', $password) != 0;
  615. $good &= $smcFunc['strtolower']($password) != $password;
  616. return $good ? null : 'chars';
  617. }
  618. // Quickly find out what this user can and cannot do.
  619. function rebuildModCache()
  620. {
  621. global $user_info, $smcFunc;
  622. // What groups can they moderate?
  623. $group_query = allowedTo('manage_membergroups') ? '1=1' : '0=1';
  624. if ($group_query == '0=1')
  625. {
  626. $request = $smcFunc['db_query']('', '
  627. SELECT id_group
  628. FROM {db_prefix}group_moderators
  629. WHERE id_member = {int:current_member}',
  630. array(
  631. 'current_member' => $user_info['id'],
  632. )
  633. );
  634. $groups = array();
  635. while ($row = $smcFunc['db_fetch_assoc']($request))
  636. $groups[] = $row['id_group'];
  637. $smcFunc['db_free_result']($request);
  638. if (empty($groups))
  639. $group_query = '0=1';
  640. else
  641. $group_query = 'id_group IN (' . implode(',', $groups) . ')';
  642. }
  643. // Then, same again, just the boards this time!
  644. $board_query = allowedTo('moderate_forum') ? '1=1' : '0=1';
  645. if ($board_query == '0=1')
  646. {
  647. $boards = boardsAllowedTo(array('moderate_board', 'approve_posts'), true);
  648. if (empty($boards))
  649. $board_query = '0=1';
  650. else
  651. $board_query = 'id_board IN (' . implode(',', $boards) . ')';
  652. }
  653. // What boards are they the moderator of?
  654. $boards_mod = array();
  655. if (!$user_info['is_guest'])
  656. {
  657. $request = $smcFunc['db_query']('', '
  658. SELECT id_board
  659. FROM {db_prefix}moderators
  660. WHERE id_member = {int:current_member}',
  661. array(
  662. 'current_member' => $user_info['id'],
  663. )
  664. );
  665. while ($row = $smcFunc['db_fetch_assoc']($request))
  666. $boards_mod[] = $row['id_board'];
  667. $smcFunc['db_free_result']($request);
  668. }
  669. $mod_query = empty($boards_mod) ? '0=1' : 'b.id_board IN (' . implode(',', $boards_mod) . ')';
  670. $_SESSION['mc'] = array(
  671. 'time' => time(),
  672. // This looks a bit funny but protects against the login redirect.
  673. 'id' => $user_info['id'] && $user_info['name'] ? $user_info['id'] : 0,
  674. // If you change the format of 'gq' and/or 'bq' make sure to adjust 'can_mod' in Load.php.
  675. 'gq' => $group_query,
  676. 'bq' => $board_query,
  677. 'ap' => boardsAllowedTo('approve_posts'),
  678. 'mb' => $boards_mod,
  679. 'mq' => $mod_query,
  680. );
  681. $user_info['mod_cache'] = $_SESSION['mc'];
  682. }
  683. ?>