PageRenderTime 62ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/php/Sources/Subs-Auth.php

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