PageRenderTime 63ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/include/api/user.php

https://bitbucket.org/webop/webop-forum
PHP | 3295 lines | 1158 code | 274 blank | 1863 comment | 281 complexity | b5ac9d94d404681b080f4d9f563fea24 MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php
  2. ////////////////////////////////////////////////////////////////////////////////
  3. // //
  4. // Copyright (C) 2010 Phorum Development Team //
  5. // http://www.phorum.org //
  6. // //
  7. // This program is free software. You can redistribute it and/or modify //
  8. // it under the terms of either the current Phorum License (viewable at //
  9. // phorum.org) or the Phorum License that was distributed with this file //
  10. // //
  11. // This program is distributed in the hope that it will be useful, //
  12. // but WITHOUT ANY WARRANTY, without even the implied warranty of //
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. //
  14. // //
  15. // You should have received a copy of the Phorum License //
  16. // along with this program. //
  17. // //
  18. ////////////////////////////////////////////////////////////////////////////////
  19. /**
  20. * This script implements the Phorum user API.
  21. *
  22. * The user API is used for managing users and user related data. The API
  23. * does also implement the Phorum session system, which is used for
  24. * remembering authenticated users. See the documentation for the function
  25. * {@link phorum_api_user_session_create()} for more information on
  26. * Phorum user sessions.
  27. *
  28. * The Phorum user API supports modules which can override Phorum's
  29. * authentication and session handling. And example module is provided
  30. * with the user API documentation.
  31. *
  32. * @package PhorumAPI
  33. * @subpackage UserAPI
  34. * @copyright 2010, Phorum Development Team
  35. * @license Phorum License, http://www.phorum.org/license.txt
  36. *
  37. * @example user_auth_module.php Authentication override module example
  38. *
  39. * @todo Document what fields are in a user record.
  40. *
  41. * @todo Create in line docs for all hook calls.
  42. *
  43. */
  44. if (!defined('PHORUM')) return;
  45. // {{{ Constant and variable definitions
  46. /**
  47. * If a user API is written as a replacement for the standard Phorum user API,
  48. * where the replacement API is incompatible with the standard API, then this
  49. * define should be set to FALSE. That will disable the user management
  50. * functions in the admin interface.
  51. */
  52. define("PHORUM_ORIGINAL_USER_CODE", TRUE);
  53. /**
  54. * Used for identifying long term sessions. The value is used as
  55. * the name for the session cookie for long term sessions.
  56. */
  57. define( 'PHORUM_SESSION_LONG_TERM' , 'phorum_session_v5' );
  58. /**
  59. * Used for identifying short term sessions. The value is used as
  60. * the name for the session cookie for short term sessions
  61. * (this is used by the tighter authentication scheme).
  62. */
  63. define( 'PHORUM_SESSION_SHORT_TERM', 'phorum_session_st' );
  64. /**
  65. * Used for identifying admin sessions. The value is used as
  66. * the name for the session cookie for admin sessions.
  67. */
  68. define( 'PHORUM_SESSION_ADMIN', 'phorum_admin_session' );
  69. /**
  70. * Function call parameter, which tells various functions that
  71. * a front end forum session has to be handled.
  72. */
  73. define('PHORUM_FORUM_SESSION', 1);
  74. /**
  75. * Function call parameter, which tells various functions that
  76. * an admin back end session has to be handled.
  77. */
  78. define('PHORUM_ADMIN_SESSION', 2);
  79. /**
  80. * Function call flag, which tells {@link phorum_api_user_set_active_user()}
  81. * that the short term forum session has to be activated.
  82. */
  83. define('PHORUM_FLAG_SESSION_ST', 1);
  84. /**
  85. * Function call flag, which tells {@link phorum_api_user_save()} that the
  86. * password field should be stored as is.
  87. * This can be used to feed Phorum MD5 encrypted passwords. Normally,
  88. * the password field would be MD5 encrypted by the function. This will
  89. * keep the phorum_api_user_save() function from double encrypting the password.
  90. */
  91. define('PHORUM_FLAG_RAW_PASSWORD', 1);
  92. /**
  93. * Function call flag, which tells {@link phorum_api_user_get_display_name()}
  94. * that the returned display names have to be HTML formatted, so they can be
  95. * used for showing the name in HTML pages.
  96. */
  97. define('PHORUM_FLAG_HTML', 1);
  98. /**
  99. * Function call flag, which tells {@link phorum_api_user_get_display_name()}
  100. * that the returned display names should be stripped down to plain text
  101. * format, so they can be used for showing the name in things like mail
  102. * messages and message quoting.
  103. */
  104. define('PHORUM_FLAG_PLAINTEXT', 2);
  105. /**
  106. * Function call parameter, which tells {@link phorum_api_user_session_create()}
  107. * that session ids have to be reset to new values as far as that is sensible
  108. * for a newly logged in user.
  109. */
  110. define('PHORUM_SESSID_RESET_LOGIN', 1);
  111. /**
  112. * Function call parameter, which tells {@link phorum_api_user_session_create()}
  113. * that all session ids have to be reset to new values. This is for example
  114. * appropriate after a user changed the password (so active sessions on
  115. * other computers or browsers will be ended).
  116. */
  117. define('PHORUM_SESSID_RESET_ALL', 2);
  118. /**
  119. * Function call parameter, which tells {@link phorum_api_user_get_list()}
  120. * that all users have to be returned.
  121. */
  122. define('PHORUM_GET_ALL', 0);
  123. /**
  124. * Function call parameter, which tells {@link phorum_api_user_get_list()}
  125. * that all active users have to be returned.
  126. */
  127. define('PHORUM_GET_ACTIVE', 1);
  128. /**
  129. * Function call parameter, which tells {@link phorum_api_user_get_list()}
  130. * that all inactive users have to be returned.
  131. */
  132. define('PHORUM_GET_INACTIVE', 2);
  133. /**
  134. * Function call parameter, which tells {@link phorum_api_user_check_access()}
  135. * and {@link phorum_api_user_check_group_access()} to return an array
  136. * of respectively forums or groups for which a user is granted access.
  137. */
  138. define('PHORUM_ACCESS_LIST', -1);
  139. /**
  140. * Function call parameter, which tells {@link phorum_api_user_check_access()}
  141. * and {@link phorum_api_user_check_group_access()} to check if the user
  142. * is granted access for respectively any forum or group.
  143. */
  144. define('PHORUM_ACCESS_ANY', -2);
  145. /**
  146. * User status, indicating that the user has not yet confirmed the registration
  147. * by email and that a user moderator will have to approve the registration
  148. * as well.
  149. */
  150. define("PHORUM_USER_PENDING_BOTH", -3);
  151. /**
  152. * User status, indicating that the user has not yet confirmed the registration
  153. * by email.
  154. */
  155. define("PHORUM_USER_PENDING_EMAIL", -2);
  156. /**
  157. * User status, indicating that the registration has not yet been approved
  158. * by a user moderator.
  159. */
  160. define("PHORUM_USER_PENDING_MOD", -1);
  161. /**
  162. * User status, indicating that the user has been deactivated.
  163. */
  164. define("PHORUM_USER_INACTIVE", 0);
  165. /**
  166. * User status, indicating that the registration has been completed and that
  167. * the user can access the forums.
  168. */
  169. define("PHORUM_USER_ACTIVE", 1);
  170. /**
  171. * Permission flag which allows users to read forum messages.
  172. */
  173. define('PHORUM_USER_ALLOW_READ', 1);
  174. /**
  175. * Permission flag which allows users to reply to forum messages.
  176. */
  177. define('PHORUM_USER_ALLOW_REPLY', 2);
  178. /**
  179. * Permission flag which allows users to edit their own forum messages.
  180. */
  181. define('PHORUM_USER_ALLOW_EDIT', 4);
  182. /**
  183. * Permission flag which allows users to start new forum topics.
  184. */
  185. define('PHORUM_USER_ALLOW_NEW_TOPIC', 8);
  186. /**
  187. * Permission flag which allows users to attach files to their forum messages.
  188. */
  189. define('PHORUM_USER_ALLOW_ATTACH', 32);
  190. /**
  191. * Permission flag which allows users to edit other users' messages.
  192. */
  193. define('PHORUM_USER_ALLOW_MODERATE_MESSAGES', 64);
  194. /**
  195. * Permission flag which allows users to moderate user signup
  196. * requests within the vroot.
  197. */
  198. define('PHORUM_USER_ALLOW_MODERATE_USERS', 128);
  199. /**
  200. * Group permission flag for users which are suspended by a group moderator.
  201. */
  202. define('PHORUM_USER_GROUP_SUSPENDED', -1);
  203. /**
  204. * Group permission flag for users which are not yet approved by
  205. * a group moderator.
  206. */
  207. define('PHORUM_USER_GROUP_UNAPPROVED', 0);
  208. /**
  209. * Group permission flag for users which are active approved group members.
  210. */
  211. define('PHORUM_USER_GROUP_APPROVED', 1);
  212. /**
  213. * Group permission flag for users which are group moderator.
  214. */
  215. define('PHORUM_USER_GROUP_MODERATOR', 2);
  216. /**
  217. * Subscription type, which tells Phorum explicitly that the user
  218. * does not have a subscription of any kind for the forum or thread.
  219. */
  220. define("PHORUM_SUBSCRIPTION_NONE", -1);
  221. /**
  222. * Subscription type, which tells Phorum to send out a mail message for
  223. * every new forum or thread that a user is subscribed to.
  224. */
  225. define("PHORUM_SUBSCRIPTION_MESSAGE", 0);
  226. /**
  227. * Subscription type, which tells Phorum to periodially send a mail message,
  228. * containing a list of new messages in forums or threads that a user is
  229. * subscribed to. There is currently no support for this type of subscription
  230. * in the Phorum core code.
  231. */
  232. define("PHORUM_SUBSCRIPTION_DIGEST", 1);
  233. /**
  234. * Subscription type, which tells Phorum to make the forums or threads that
  235. * a user is subscribed to accessible from the followed threads interface in
  236. * the control center. No mail is sent for new messages, but the user can
  237. * check for new messages using that interface.
  238. */
  239. define("PHORUM_SUBSCRIPTION_BOOKMARK", 2);
  240. /**
  241. * This array describes user data fields. It is mainly used internally
  242. * for configuring how to handle the fields and for doing checks on them.
  243. */
  244. $GLOBALS['PHORUM']['API']['user_fields'] = array
  245. (
  246. // Fields that are really in the Phorum users table.
  247. 'user_id' => 'int',
  248. 'username' => 'string',
  249. 'real_name' => 'string',
  250. 'display_name' => 'string',
  251. 'password' => 'string',
  252. 'password_temp' => 'string',
  253. 'sessid_lt' => 'string',
  254. 'sessid_st' => 'string',
  255. 'sessid_st_timeout' => 'string',
  256. 'email' => 'string',
  257. 'email_temp' => 'string',
  258. 'hide_email' => 'bool',
  259. 'active' => 'int',
  260. 'admin' => 'bool',
  261. 'signature' => 'string',
  262. 'posts' => 'int',
  263. 'date_added' => 'int',
  264. 'date_last_active' => 'int',
  265. 'last_active_forum' => 'int',
  266. 'threaded_list' => 'int',
  267. 'threaded_read' => 'int',
  268. 'hide_activity' => 'bool',
  269. 'show_signature' => 'bool',
  270. 'email_notify' => 'int',
  271. 'pm_email_notify' => 'bool',
  272. 'tz_offset' => 'float',
  273. 'is_dst' => 'bool',
  274. 'user_language' => 'string',
  275. 'user_template' => 'string',
  276. 'moderation_email' => 'bool',
  277. 'moderator_data' => 'array',
  278. 'settings_data' => 'array',
  279. // Fields that are used for passing on information about user related,
  280. // data, which is not stored in a standard user table field.
  281. 'forum_permissions' => 'array',
  282. // Fields that we do not use for saving data (yet?), but which might
  283. // be in the user data (e.g. if we store a user data array like it was
  284. // returned by phorum_api_user_get()).
  285. 'groups' => NULL,
  286. 'group_permissions' => NULL,
  287. 'permissions' => NULL,
  288. );
  289. // }}}
  290. // ----------------------------------------------------------------------
  291. // Handling user data.
  292. // ----------------------------------------------------------------------
  293. // {{{ Function: phorum_api_user_save()
  294. /**
  295. * Create or update Phorum users.
  296. *
  297. * This function can be used for both creating and updating Phorum users.
  298. * If the user_id in the user data is NULL, a new user will be created.
  299. * If a user_id is provided, then the existing user will be updated or a
  300. * new user with that user_id is created.
  301. *
  302. * Often when calling this function yourself, you will be doing that for
  303. * synchronizing a user from some external system with the Phorum database.
  304. * For those cases, the most basic use of this API function can be found
  305. * in the examples below.
  306. * <code>
  307. * $user = array(
  308. * "user_id" => 1234,
  309. * "username" => 'johndoe',
  310. * "password" => '#barbar#',
  311. * "email" => 'john.doe@example.com',
  312. * "admin" => 0,
  313. * "active" => PHORUM_USER_ACTIVE
  314. * );
  315. * phorum_api_user_save($user);
  316. * </code>
  317. *
  318. * If you do not have the plain text password available, but only an MD5
  319. * hash for the password, then you can use the following code instead.
  320. * <code>
  321. * $user = array(
  322. * "user_id" => 1234,
  323. * "username" => 'johndoe',
  324. * "password" => '5d61ed116ffdecf2d29cd1ed9bd9d4cb',
  325. * "email" => 'john.doe@example.com',
  326. * "admin" => 0,
  327. * "active" => PHORUM_USER_ACTIVE
  328. * );
  329. * phorum_api_user_save($user, PHORUM_FLAG_RAW_PASSWORD);
  330. * </code>
  331. *
  332. * @param array $user
  333. * An array containing user data. This array should at least contain
  334. * a field "user_id". This field can be NULL to create a new user
  335. * with an automatically assigned user_id. It can also be set to a
  336. * user_id to either update an existing user or to create a new user
  337. * with the provided user_id.
  338. * If a new user is created, then all user fields must be provided
  339. * in the user data.
  340. *
  341. * @param int $flags
  342. * If the flag {@link PHORUM_FLAG_RAW_PASSWORD} is set, then the
  343. * password fields ("password" and "password_temp") are considered to be
  344. * MD5 encrypted already. So this can be used to feed Phorum existing MD5
  345. * encrypted passwords.
  346. *
  347. * @return int
  348. * The user_id of the user. For new users, the newly assigned user_id
  349. * will be returned.
  350. */
  351. function phorum_api_user_save($user, $flags = 0)
  352. {
  353. global $PHORUM;
  354. include_once('./include/api/custom_profile_fields.php');
  355. // $user must be an array.
  356. if (!is_array($user)) {
  357. trigger_error(
  358. 'phorum_api_user_save(): $user argument is not an array',
  359. E_USER_ERROR
  360. );
  361. return NULL;
  362. }
  363. // We need at least the user_id field.
  364. if (!array_key_exists('user_id', $user)) {
  365. trigger_error(
  366. 'phorum_api_user_save(): missing field "user_id" in user data array',
  367. E_USER_ERROR
  368. );
  369. return NULL;
  370. }
  371. if ($user['user_id'] !== NULL && !is_numeric($user['user_id'])) {
  372. trigger_error(
  373. 'phorum_api_user_save(): field "user_id" not NULL or numerical',
  374. E_USER_ERROR
  375. );
  376. return NULL;
  377. }
  378. // Check if we are handling an existing or new user.
  379. $existing = NULL;
  380. if ($user['user_id'] !== NULL) {
  381. $existing = phorum_api_user_get($user['user_id'], TRUE, TRUE, TRUE);
  382. }
  383. // Create a user data array that is understood by the database layer.
  384. // We start out with the existing record, if we have one.
  385. $dbuser = $existing === NULL ? array() : $existing;
  386. // Merge in the fields from the $user argument.
  387. foreach ($user as $fld => $val) {
  388. $dbuser[$fld] = $val;
  389. }
  390. // Initialize storage for custom profile field data.
  391. $user_data = array();
  392. // Check and format the user data fields.
  393. foreach ($dbuser as $fld => $val)
  394. {
  395. // Determine the field type that we are handling.
  396. $fldtype = NULL;
  397. $custom = NULL;
  398. // Check if we are handling a custom profile field. We asume that any
  399. // field that is not in the user_fields array is a custom profile
  400. // field. If we find that it isn't such field, then we will ignore
  401. // the field (it's either a field that was manually added to the
  402. // user table or a custom profile field that was just deleted).
  403. if (!array_key_exists($fld, $PHORUM['API']['user_fields'])) {
  404. $custom = phorum_api_custom_profile_field_byname($fld);
  405. if ($custom === NULL) {
  406. $fldtype = 'ignore_field';
  407. } else {
  408. $fldtype = 'custom_profile_field';
  409. }
  410. } else {
  411. $fldtype = $PHORUM['API']['user_fields'][$fld];
  412. }
  413. switch ($fldtype)
  414. {
  415. // A field that has to be fully ignored.
  416. case NULL:
  417. break;
  418. case 'int':
  419. $dbuser[$fld] = $val === NULL ? NULL : (int) $val;
  420. break;
  421. case 'float':
  422. $dbuser[$fld] = $val === NULL ? NULL : (float) $val;
  423. break;
  424. case 'string':
  425. $dbuser[$fld] = $val === NULL ? NULL : trim($val);
  426. break;
  427. case 'bool':
  428. $dbuser[$fld] = $val ? 1 : 0;
  429. break;
  430. case 'array':
  431. // TODO: maybe check for real arrays here?
  432. $dbuser[$fld] = $val;
  433. break;
  434. case 'custom_profile_field':
  435. // Arrays and NULL values are left untouched.
  436. // Other values are truncated to their configured field length.
  437. if ($val !== NULL && !is_array($val)) {
  438. $val = substr($val, 0, $custom['length']);
  439. }
  440. $user_data[$custom['id']] = $val;
  441. unset($dbuser[$fld]);
  442. break;
  443. case 'ignore_field':
  444. unset($dbuser[$fld]);
  445. break;
  446. default:
  447. trigger_error(
  448. 'phorum_api_user_save(): Illegal field type used: ' .
  449. htmlspecialchars($fldtype),
  450. E_USER_ERROR
  451. );
  452. return NULL;
  453. break;
  454. }
  455. }
  456. // Add the custom profile field data to the user data.
  457. $dbuser['user_data'] = $user_data;
  458. // At this point, we should have a couple of mandatory fields available
  459. // in our data. Without these fields, the user record is not sane
  460. // enough to continue with.
  461. // We really need a username, so we can always generate a display name.
  462. if (!isset($dbuser['username']) || $dbuser['username'] == '') {
  463. trigger_error(
  464. 'phorum_api_user_save(): the username field for a user record ' .
  465. 'cannot be empty',
  466. E_USER_ERROR
  467. );
  468. return NULL;
  469. }
  470. // Phorum sends out mail messages on several occasions. So we need a
  471. // mail address for the user.
  472. if (!isset($dbuser['email']) || $dbuser['email'] == '') {
  473. trigger_error(
  474. 'phorum_api_user_save(): the email field for a user record ' .
  475. 'cannot be empty',
  476. E_USER_ERROR
  477. );
  478. return NULL;
  479. }
  480. // For new accounts only.
  481. if (!$existing)
  482. {
  483. if (empty($dbuser['date_added']))
  484. $dbuser['date_added'] = time();
  485. if (empty($dbuser['date_last_active']))
  486. $dbuser['date_last_active'] = time();
  487. }
  488. // Handle password encryption.
  489. foreach (array('password', 'password_temp') as $fld)
  490. {
  491. // Sometimes, this function is (accidentally) called with existing
  492. // passwords in the data. Prevent duplicate encryption.
  493. if ($existing && strlen($existing[$fld]) == 32 &&
  494. $existing[$fld] == $dbuser[$fld]) {
  495. continue;
  496. }
  497. // If the password field is empty, we should never store the MD5 sum
  498. // of an empty string as a safety precaution. Instead we store a
  499. // string which will never work as a password. This could happen in
  500. // case of bugs in the code or in case external user auth is used
  501. // (in which case Phorum can have empty passwords, since the Phorum
  502. // passwords are not used at all).
  503. if (!isset($dbuser[$fld]) || $dbuser[$fld] === NULL ||
  504. $dbuser[$fld] == '' || $dbuser[$fld] == '*NO PASSWORD SET*') {
  505. $dbuser[$fld] = '*NO PASSWORD SET*';
  506. continue;
  507. }
  508. // Only crypt the password using MD5, if the PHORUM_FLAG_RAW_PASSWORD
  509. // flag is not set.
  510. if (!($flags & PHORUM_FLAG_RAW_PASSWORD)) {
  511. $dbuser[$fld] = md5($dbuser[$fld]);
  512. }
  513. }
  514. // Determine the display name to use for the user. If the setting
  515. // $PHORUM["custom_display_name"] is enabled (a "secret" setting which
  516. // cannot be changed through the admin settings, but only through
  517. // modules that consciously set it), then Phorum expects that the display
  518. // name is a HTML formatted display_name field, which is provided by
  519. // 3rd party software. Otherwise, the username or real_name is used
  520. // (depending on the $PHORUM["display_name_source"] Phorum setting).
  521. if (empty($PHORUM['custom_display_name'])) {
  522. $display_name = $dbuser['username'];
  523. if ($PHORUM['display_name_source'] == 'real_name' &&
  524. isset($dbuser['real_name']) &&
  525. trim($dbuser['real_name']) != '') {
  526. $display_name = $dbuser['real_name'];
  527. }
  528. $dbuser['display_name'] = $display_name;
  529. }
  530. // If the 3rd party software provided no or an empty display_name,
  531. // then we save the day by using the username (just so users won't show up
  532. // empty on screen, this should not happen at all in the first place).
  533. // We HTML encode the username, because custom display names are supposed
  534. // to be provided in escaped HTML format.
  535. elseif (!isset($dbuser['display_name']) ||
  536. trim($dbuser['display_name']) == '') {
  537. $dbuser['display_name'] = htmlspecialchars($dbuser['username'], ENT_COMPAT, $PHORUM['DATA']['HCHARSET']);
  538. }
  539. /**
  540. * [hook]
  541. * user_save
  542. *
  543. * [description]
  544. * This hook can be used to handle the data that is going to be
  545. * stored in the database for a user. Modules can do some last
  546. * minute change on the data or keep some external system in sync
  547. * with the Phorum user data.<sbr/>
  548. * <sbr/>
  549. * In combination with the <hook>user_get</hook> hook, this hook
  550. * could also be used to store and retrieve some of the Phorum
  551. * user fields using some external system.
  552. *
  553. * [category]
  554. * User data handling
  555. *
  556. * [when]
  557. * Just before user data is stored in the database.
  558. *
  559. * [input]
  560. * An array containing user data that will be sent to the database.
  561. *
  562. * [output]
  563. * The same array as the one that was used for the hook call
  564. * argument, possibly with some updated fields in it.
  565. *
  566. * [example]
  567. * <hookcode>
  568. * function phorum_mod_foo_user_save($user)
  569. * {
  570. * // Add "[A]" in front of admin user real_name fields.
  571. * $A = $user["admin"] ? "[A]" : "";
  572. * $real_name = preg_replace('/^\[A\]/', $A, $user["real_name"]);
  573. * $user['real_name'] = $real_name;
  574. *
  575. * // Some fictional external system to keep in sync.
  576. * include("../coolsys.php");
  577. * coolsys_save($user);
  578. *
  579. * return $user;
  580. * }
  581. * </hookcode>
  582. */
  583. if (isset($PHORUM['hooks']['user_save'])) {
  584. $dbuser = phorum_hook('user_save', $dbuser);
  585. }
  586. /**
  587. * [hook]
  588. * user_register
  589. *
  590. * [description]
  591. * This hook is called when a user registration is completed by
  592. * setting the status for the user to PHORUM_USER_ACTIVE.
  593. * This hook will not be called right after filling in the
  594. * registration form (unless of course, the registration has been
  595. * setup to require no verification at all in which case the user
  596. * becomes active right away).
  597. *
  598. * [category]
  599. * User data handling
  600. *
  601. * [when]
  602. * Right after a new user registration becomes active.
  603. *
  604. * [input]
  605. * An array containing user data for the registered user.
  606. *
  607. * [output]
  608. * The same array as the one that was used for the hook call
  609. * argument, possibly with some updated fields in it.
  610. *
  611. * [example]
  612. * <hookcode>
  613. * function phorum_mod_foo_user_register($user)
  614. * {
  615. * // Log user registrations through syslog.
  616. * openlog("Phorum", LOG_PID | LOG_PERROR, LOG_LOCAL0);
  617. * syslog(LOG_NOTICE, "New user registration: $user[username]");
  618. *
  619. * return $user;
  620. * }
  621. * </hookcode>
  622. */
  623. if (isset($PHORUM['hooks']['user_register']))
  624. {
  625. // Only fire this hook if a user goes from some pending state
  626. // to the active state.
  627. if ($dbuser['active'] == PHORUM_USER_ACTIVE)
  628. {
  629. // For new users we have no existing status. For those we asume
  630. // a pending status, so below a switch to active status will mean
  631. // that the user registration is activated.
  632. $orig_status = $existing
  633. ? $existing['active']
  634. : PHORUM_USER_PENDING_MOD;
  635. if ($orig_status == PHORUM_USER_PENDING_BOTH ||
  636. $orig_status == PHORUM_USER_PENDING_EMAIL ||
  637. $orig_status == PHORUM_USER_PENDING_MOD) {
  638. $dbuser = phorum_hook('user_register', $dbuser);
  639. }
  640. }
  641. }
  642. // Add or update the user in the database.
  643. if ($existing) {
  644. phorum_db_user_save($dbuser);
  645. } else {
  646. $dbuser['user_id'] = phorum_db_user_add($dbuser);
  647. }
  648. // If the display name changed for the user, then we do need to run
  649. // updates throughout the Phorum database to make references to this
  650. // user to show up correctly.
  651. if ($existing && $existing['display_name'] != $dbuser['display_name']) {
  652. phorum_db_user_display_name_updates($dbuser);
  653. }
  654. // If user caching is enabled, we invalidate the cache for this user.
  655. if (!empty($PHORUM['cache_users'])) {
  656. phorum_cache_remove('user', $dbuser['user_id']);
  657. }
  658. // Are we handling the active Phorum user? Then refresh the user data.
  659. if (isset($PHORUM['user']) &&
  660. $PHORUM['user']['user_id'] == $dbuser['user_id']) {
  661. $PHORUM['user'] = phorum_api_user_get($user['user_id'], TRUE, TRUE);
  662. }
  663. return $dbuser['user_id'];
  664. }
  665. // }}}
  666. // {{{ Function: phorum_api_user_save_raw()
  667. /**
  668. * This function quickly updates the Phorum users table, using all fields in
  669. * the user data as real user table fields.
  670. *
  671. * This is the quickest way to update the user table. Care has to be taken
  672. * by the calling function though, to provide the information exactly as the
  673. * Phorum users table expects it. Only use this function if speed is really
  674. * an issue.
  675. *
  676. * @param array $user
  677. * An array containing user data. This array should at least contain
  678. * a field "user_id", pointing to the user_id of an existing user to
  679. * update. Besides "user_id", this array can contain other fields,
  680. * which should all be valid fields from the users table.
  681. */
  682. function phorum_api_user_save_raw($user)
  683. {
  684. if (empty($user['user_id'])) {
  685. trigger_error(
  686. 'phorum_api_user_save_raw(): the user_id field cannot be empty',
  687. E_USER_ERROR
  688. );
  689. return NULL;
  690. }
  691. // This hook is documented in phorum_api_user_save().
  692. if (isset($PHORUM['hooks']['user_save'])) {
  693. $user = phorum_hook('user_save', $user);
  694. }
  695. // Store the data in the database.
  696. phorum_db_user_save($user);
  697. // Invalidate the cache for the user, unless we are only updating
  698. // user activity tracking fields.
  699. if (!empty($GLOBALS['PHORUM']['cache_users']))
  700. {
  701. // Count the number of activity tracking fields in the data.
  702. $count = 1; // count user_id as an activity tracking field
  703. if (array_key_exists('date_last_active', $user)) $count ++;
  704. if (array_key_exists('last_active_forum', $user)) $count ++;
  705. // Invalidate the cache, if there are non-activity tracking fields.
  706. if ($count != count($user)) {
  707. phorum_cache_remove('user', $user['user_id']);
  708. }
  709. }
  710. }
  711. // }}}
  712. // {{{ Function: phorum_api_user_save_settings()
  713. /**
  714. * Create or update user settings for the active Phorum user.
  715. *
  716. * This function can be used to store arbitrairy settings for the active
  717. * Phorum user in the database. The main goal for this function is to store
  718. * user settings which are not available as a Phorum user table field in
  719. * the database. These are settings which do not really belong to the Phorum
  720. * core, but which are for example used for remembering some kind of state
  721. * in a user interface (templates). Since each user interface might require
  722. * different settings, a dynamic settings storage like this is required.
  723. *
  724. * If you are writing modules that need to store data for a user, then please
  725. * do not use this function. Instead, use custom profile fields. The data
  726. * that is stored using this function can be best looked at as if it were
  727. * session data.
  728. *
  729. * @param array $settings
  730. * An array of setting name => value pairs to store as user
  731. * settings in the database.
  732. */
  733. function phorum_api_user_save_settings($settings)
  734. {
  735. global $PHORUM;
  736. // Get the active user's user_id.
  737. if (empty($PHORUM['user']['user_id'])) return;
  738. $user_id = $PHORUM['user']['user_id'];
  739. // The settings data must always be an array.
  740. if (empty($PHORUM['user']['settings_data'])) {
  741. $PHORUM['user']['settings_data'] = array();
  742. }
  743. // Merge the setting with the existing settings.
  744. if (is_array($settings)) {
  745. foreach ($settings as $name => $value) {
  746. if ($value === NULL) {
  747. unset($PHORUM['user']['settings_data'][$name]);
  748. } else {
  749. $PHORUM['user']['settings_data'][$name] = $value;
  750. }
  751. }
  752. }
  753. // Save the settings in the database.
  754. phorum_db_user_save(array(
  755. 'user_id' => $user_id,
  756. 'settings_data' => $PHORUM['user']['settings_data']
  757. ));
  758. // If user caching is enabled, we remove the user from the cache.
  759. if (!empty($GLOBALS['PHORUM']['cache_users'])) {
  760. phorum_cache_remove('user', $user_id);
  761. }
  762. }
  763. // }}}
  764. // {{{ Function: phorum_api_user_get()
  765. /**
  766. * Retrieve data for Phorum users.
  767. *
  768. * @param mixed $user_id
  769. * Either a single user_id or an array of user_ids.
  770. *
  771. * @param boolean $detailed
  772. * If this parameter is TRUE (default is FALSE), then the user's
  773. * groups and permissions are included in the user data.
  774. *
  775. * @param boolean $use_write_server
  776. * This parameter is for internal use only. It is used to flag that
  777. * the database layer has to run the query against the master database
  778. * server (known as the "write server"; only applicable if the database
  779. * system is setup as a replicated master/slave environment). When you
  780. * are using this API call in your own code, then you most probably do
  781. * not need to use this parameter.
  782. *
  783. * @return mixed
  784. * If the $user_id parameter is a single user_id, then either an array
  785. * containing user data is returned or NULL if the user was not found.
  786. * If the $user_id parameter is an array of user_ids, then an array
  787. * of user data arrays is returned, indexed by the user_id.
  788. * Users for user_ids that are not found are not included in the
  789. * returned array.
  790. */
  791. function phorum_api_user_get($user_id, $detailed = FALSE, $use_write_server = FALSE, $raw_data = FALSE)
  792. {
  793. $PHORUM = $GLOBALS['PHORUM'];
  794. if (!is_array($user_id)) {
  795. $user_ids = array($user_id);
  796. } else {
  797. $user_ids = $user_id;
  798. }
  799. // Prepare the return data array. For each requested user_id,
  800. // a slot is prepared in this array. Also, turn the user id array
  801. // into an array which has the user_id as both the key and value.
  802. $users = array();
  803. $new_user_ids = array();
  804. foreach ($user_ids as $id) {
  805. $users[$id] = NULL;
  806. $new_user_ids[$id] = $id;
  807. }
  808. $user_ids = $new_user_ids;
  809. // First, try to retrieve user data from the user cache,
  810. // if user caching is enabled.
  811. if ($raw_data === FALSE && !empty($PHORUM['cache_users']))
  812. {
  813. $cached_users = phorum_cache_get('user', $user_ids);
  814. if (is_array($cached_users))
  815. {
  816. foreach ($cached_users as $id => $user) {
  817. $users[$id] = $user;
  818. unset($user_ids[$id]);
  819. }
  820. // We need to retrieve the data for some dynamic fields
  821. // from the database.
  822. $dynamic_data = phorum_db_user_get_fields(
  823. array_keys($cached_users),
  824. array('date_last_active','last_active_forum','posts')
  825. );
  826. // Store the results in the users array.
  827. foreach ($dynamic_data as $id => $data) {
  828. $users[$id] = array_merge($users[$id],$data);
  829. }
  830. }
  831. }
  832. // Retrieve user data for the users for which no data was
  833. // retrieved from the cache.
  834. if (count($user_ids))
  835. {
  836. $db_users = phorum_db_user_get($user_ids, $detailed, $use_write_server, $raw_data);
  837. foreach ($db_users as $id => $user)
  838. {
  839. // Merge the group and forum permissions into a final
  840. // permission value per forum. Forum permissions that are
  841. // assigned to a user directly override any group based
  842. // permission.
  843. if (!$user['admin']) {
  844. if (!empty($user['group_permissions'])) {
  845. foreach ($user['group_permissions'] as $fid => $perm) {
  846. if (!isset($user['permissions'][$fid])) {
  847. $user['permissions'][$fid] = $perm;
  848. } else {
  849. $user['permissions'][$fid] |= $perm;
  850. }
  851. }
  852. }
  853. if (!empty($user['forum_permissions'])) {
  854. foreach ($user['forum_permissions'] as $fid => $perm) {
  855. $user['permissions'][$fid] = $perm;
  856. }
  857. }
  858. }
  859. // If detailed information was requested, we store the data in
  860. // the cache. For non-detailed information, we do not cache the
  861. // data, because there is not much to gain there by caching.
  862. if ($detailed && !empty($PHORUM['cache_users']) && $raw_data === FALSE) {
  863. phorum_cache_put('user', $id, $user);
  864. }
  865. // Store the results in the users array.
  866. $users[$id] = $user;
  867. }
  868. }
  869. // Remove the users for which we did not find data from the array.
  870. foreach ($users as $id => $user) {
  871. if ($user === NULL) {
  872. unset($users[$id]);
  873. }
  874. }
  875. /**
  876. * [hook]
  877. * user_get
  878. *
  879. * [description]
  880. * This hook can be used to handle the data that was retrieved
  881. * from the database for a user. Modules can add and modify the
  882. * user data.<sbr/>
  883. * <sbr/>
  884. * In combination with the <hook>user_save</hook> hook, this hook
  885. * could also be used to store and retrieve some of the Phorum
  886. * user fields in some external system
  887. *
  888. * [category]
  889. * User data handling
  890. *
  891. * [when]
  892. * Just after user data has been retrieved from the database.
  893. *
  894. * [input]
  895. * This hook receives two arguments.<sbr/>
  896. * The first argument contains an array of users.
  897. * Each item in this array is an array containing data for
  898. * a single user, which can be updated.<sbr/>
  899. * The second argument contains a boolean that indicates whether
  900. * detailed information (i.e. including group info) is retrieved.
  901. *
  902. * [output]
  903. * The array that was used as the first argument for the hook call,
  904. * possibly with some updated users in it.
  905. *
  906. * [example]
  907. * <hookcode>
  908. * function phorum_mod_foo_user_get($user, $detailed)
  909. * {
  910. * // Let's asume that our usernames are based on the
  911. * // system users on a UNIX system. We could merge some
  912. * // info from the password file with the Phorum info here.
  913. *
  914. * // First try to lookup the password file entry.
  915. * // Return if this lookup fails.
  916. * $pw = posix_getpwnam($user['username']);
  917. * if (empty($pw)) return $user;
  918. *
  919. * // On a lot of systems, the "gecos" field contains
  920. * // the real name for the user.
  921. * $user['real_name'] = $pw["gecos"] != ''
  922. * ? $pw["gecos"]
  923. * : $user["real_name"];
  924. *
  925. * // If a custom profile field "shell" was created, then
  926. * // we could also put the user's shell in the data.
  927. * $user['shell'] = $pw['shell'];
  928. *
  929. * return $user;
  930. * }
  931. * </hookcode>
  932. */
  933. if (isset($PHORUM['hooks']['user_get'])) {
  934. $users = phorum_hook('user_get', $users, $detailed);
  935. }
  936. // Return the results.
  937. if (is_array($user_id)) {
  938. return $users;
  939. } else {
  940. return isset($users[$user_id]) ? $users[$user_id] : NULL;
  941. }
  942. }
  943. // }}}
  944. // {{{ Function: phorum_api_user_get_setting()
  945. /**
  946. * This function can be used to retrieve the value for a user setting
  947. * that was stored by the {@link phorum_api_user_save_settings()} function
  948. * for the active Phorum user.
  949. *
  950. * @param string $name
  951. * The name of the setting for which to retrieve the setting value.
  952. *
  953. * @return mixed
  954. * The value of the setting or NULL if it is not available.
  955. */
  956. function phorum_api_user_get_setting($name)
  957. {
  958. $PHORUM = $GLOBALS['PHORUM'];
  959. // No settings available at all?
  960. if (empty($PHORUM['user']['settings_data'])) return NULL;
  961. // The setting is available.
  962. if (array_key_exists($name, $PHORUM['user']['settings_data'])) {
  963. return $PHORUM['user']['settings_data'][$name];
  964. } else {
  965. return NULL;
  966. }
  967. }
  968. // }}}
  969. // {{{ Function: phorum_api_user_get_display_name()
  970. /**
  971. * Retrieve the display name to use for one or more users.
  972. *
  973. * The name to use depends on the "display_name_source" setting. This
  974. * one points to either the username or the real_name field of the
  975. * user. If the display_name is requested for an unknown user, then
  976. * a fallback name will be used.
  977. *
  978. * @param mixed $user_id
  979. * Either a single user_id, an array of user_ids or NULL to use the
  980. * user_id of the active Phorum user.
  981. *
  982. * @param mixed $fallback
  983. * The fallback display name to use in case the user is unknown or NULL
  984. * to use the "AnonymousUser" language string.
  985. *
  986. * @param mixed $flags
  987. * One of {@link PHORUM_FLAG_HTML} (the default) or
  988. * {@link PHORUM_FLAG_PLAINTEXT}. These determine what output format
  989. * is used for the display names.
  990. *
  991. * @return mixed
  992. * If the $user_id parameter was NULL or a single user_id, then this
  993. * function will return a single display name. If it was an array,
  994. * then this function will return an array of display names, indexed
  995. * by user_id.
  996. */
  997. function phorum_api_user_get_display_name($user_id = NULL, $fallback = NULL, $flags = PHORUM_FLAG_HTML)
  998. {
  999. $PHORUM = $GLOBALS['PHORUM'];
  1000. if ($fallback === NULL) {
  1001. $fallback = $PHORUM['DATA']['LANG']['AnonymousUser'];
  1002. }
  1003. // Use the user_id for the active user.
  1004. if ($user_id === NULL) {
  1005. $user_id = $PHORUM['user']['user_id'];
  1006. }
  1007. // From here on, we need an array of user_ids to lookup.
  1008. $user_ids = is_array($user_id) ? $user_id : array((int)$user_id);
  1009. // Lookup the users.
  1010. $users = phorum_api_user_get($user_ids, FALSE);
  1011. // Determine the display names.
  1012. $display_names = array();
  1013. foreach ($user_ids as $id)
  1014. {
  1015. $display_name = empty($users[$id])
  1016. ? $fallback
  1017. : $users[$id]['display_name'];
  1018. // Generate HTML based display names.
  1019. if ($flags == PHORUM_FLAG_HTML)
  1020. {
  1021. // If the setting $PHORUM["custom_display_name"] is enabled,
  1022. // then Phorum expects that the display name is a HTML
  1023. // formatted display_name field, which is provided by
  1024. // 3rd party software. So those do not have to be HTML escaped.
  1025. // Other names do have to be escaped.
  1026. if (empty($users[$id]) || empty($PHORUM['custom_display_name']))
  1027. {
  1028. $display_name = htmlspecialchars($display_name, ENT_COMPAT, $PHORUM['DATA']['HCHARSET']);
  1029. }
  1030. }
  1031. // Generate a plain text version of the display name. This is the
  1032. // display name as it can be found in the database. Only for
  1033. // custom_display_name cases, we need to strip HTML code.
  1034. elseif ($flags == PHORUM_FLAG_PLAINTEXT)
  1035. {
  1036. // Strip tags from the name. These might be in the
  1037. // name if the custom_display_name feature is enabled.
  1038. // So for custom display names we strip the HTML from the
  1039. // display name that we found above.
  1040. if (!empty($PHORUM['custom_display_name']))
  1041. {
  1042. $display_name = trim(strip_tags($display_name));
  1043. // If the name was 100% HTML code (so empty after stripping),
  1044. // then fallback to the default display_name that Phorum
  1045. // would use without the custom display name feature.
  1046. if ($display_name == '') {
  1047. if (empty($users[$id])) {
  1048. $display_name = $fallback;
  1049. } else {
  1050. $display_name = $users[$id]['username'];
  1051. if ($PHORUM['display_name_source'] == 'real_name' &&
  1052. trim($users[$id]['real_name']) != '') {
  1053. $display_name = $users[$id]['real_name'];
  1054. }
  1055. }
  1056. }
  1057. }
  1058. }
  1059. $display_names[$id] = $display_name;
  1060. }
  1061. if (is_array($user_id)) {
  1062. return $display_names;
  1063. } else {
  1064. return $display_names[$user_id];
  1065. }
  1066. }
  1067. // }}}
  1068. // {{{ Function: phorum_api_user_search()
  1069. /**
  1070. * Search for users, based on simple search conditions, which act on
  1071. * fields in the user table.
  1072. *
  1073. * The parameters $field, $value and $operator (which are used for defining
  1074. * the search condition) can be arrays or single values. If arrays are used,
  1075. * then all three parameter arrays must contain the same number of elements
  1076. * and the keys in the arrays must be the same.
  1077. *
  1078. * @param mixed $field
  1079. * The user table field (string) or fields (array) to search on.
  1080. *
  1081. * @param mixed $value
  1082. * The value (string) or values (array) to search for.
  1083. *
  1084. * @param mixed $operator
  1085. * The operator (string) or operators (array) to use. Valid operators are
  1086. * "=", "!=", "<>", "<", ">", ">=" and "<=", "*". The
  1087. * "*" operator is for executing a "LIKE '%value%'" matching query.
  1088. *
  1089. * @param boolean $return_array
  1090. * If this parameter has a true value, then an array of all matching
  1091. * user_ids will be returned. Else, a single user_id will be returned.
  1092. *
  1093. * @param string $type
  1094. * The type of search to perform. This can be one of:
  1095. * - AND match against all fields
  1096. * - OR match against any of the fields
  1097. *
  1098. * @param mixed $sort
  1099. * The field (string) or fields (array) to sort the results by. For
  1100. * ascending sort, "fieldname" or "+fieldname" can be used. For
  1101. * descending sort, "-fieldname" can be used. By default, the results
  1102. * will be sorted by user_id.
  1103. *
  1104. * @param integer $offset
  1105. * The result page offset starting with 0.
  1106. *
  1107. * @param integer $length
  1108. * The result page length (nr. of results per page)
  1109. * or 0 (zero, the default) to return all results.
  1110. *
  1111. * @param boolean $count_only
  1112. * Tells the function to just return the count of results for this
  1113. * search query.
  1114. *
  1115. * @return mixed
  1116. * An array of matching user_ids or a single user_id (based on the
  1117. * $return_array parameter) or a count of results (based on $count_only).
  1118. * If no user_ids can be found at all, then 0 (zero) will be returned.
  1119. */
  1120. function phorum_api_user_search($field, $value, $operator = '=', $return_array = FALSE, $type = 'AND', $sort = NULL, $offset = 0, $length = 0,$count_only=false)
  1121. {
  1122. return phorum_db_user_search($field, $value, $operator, $return_array, $type, $sort, $offset, $length,$count_only);
  1123. }
  1124. // }}}
  1125. // {{{ Function: phorum_api_user_search_custom_profile_field()
  1126. /**
  1127. * Search for users, based on a simple search condition,
  1128. * which can be used to search on custom profile fields.
  1129. *
  1130. * The parameters $field_id, $value and $operator (which are used for defining
  1131. * the search condition) can be arrays or single values. If arrays are used,
  1132. * then all three parameter arrays must contain the same number of elements
  1133. * and the keys in the arrays must be the same.
  1134. *
  1135. * @param mixed $field_id
  1136. * The custom profile field id (integer) or ids (array) to search on.
  1137. *
  1138. * @param mixed $value
  1139. * The value (string) or values (array) to search for.
  1140. *
  1141. * @param mixed $operator
  1142. * The operator (string) or operators (array) to use. Valid operators are
  1143. * "=", "!=", "<>", "<", ">", ">=" and "<=", "*". The
  1144. * "*" operator is for executing a "LIKE '%value%'" matching query.
  1145. *
  1146. * @param boolean $return_array
  1147. * If this parameter has a true value, then an array of all matching
  1148. * user_ids will be returned. Else, a single user_id will be returned.
  1149. *
  1150. * @param string $type
  1151. * The type of search to perform. This can be one of:
  1152. * - AND match against all fields
  1153. * - OR match against any of the fields
  1154. *
  1155. * @param integer $offset
  1156. * The result page offset starting with 0.
  1157. *
  1158. * @param integer $length
  1159. * The result page length (nr. of results per page)
  1160. * or 0 (zero, the default) to return all results.
  1161. *
  1162. * @return mixed
  1163. * An array of matching user_ids or a single user_id (based on the
  1164. * $return_array parameter). If no user_ids can be found at all,
  1165. * then 0 (zero) will be returned.
  1166. */
  1167. function phorum_api_user_search_custom_profile_field($field_id, $value, $operator = '=', $return_array = FALSE, $type = 'AND', $offset = 0, $length = 0)
  1168. {
  1169. return phorum_db_user_search_custom_profile_field($field_id, $value, $operator, $return_array, $type, $offset, $length);
  1170. }
  1171. // }}}
  1172. // {{{ Function: phorum_api_user_list()
  1173. /**
  1174. * Retrieve a list of Phorum users.
  1175. *
  1176. * @param int $type
  1177. * One of:
  1178. * - {@link PHORUM_GET_ALL}: retrieve a list of all users (the default)
  1179. * - {@link PHORUM_GET_ACTIVE}: retrieve a list of all active users
  1180. * - {@link PHORUM_GET_INACTIVE}: retrieve a list of all inactive users
  1181. *
  1182. * @return array
  1183. * An array of users, indexed by user_id. Each element in the array
  1184. * is an array, containing the fields "user_id", "username" and
  1185. * "display_name".
  1186. *
  1187. * @todo Do we really need phorum_api_user_list() or could we use the
  1188. * phorum_api_user_search() functionality in combination with
  1189. * phorum_api_user_get() instead?
  1190. */
  1191. function phorum_api_user_list($type = PHORUM_GET_ALL)
  1192. {
  1193. // Retrieve a list of users from the database.
  1194. $list = phorum_db_user_get_list($type);
  1195. /**
  1196. * [hook]
  1197. * user_list
  1198. *
  1199. * [description]
  1200. *
  1201. * This hook can be used for reformatting the list of users that
  1202. * is returned by the phorum_api_user_list() function. Reformatting
  1203. * could mean things like changing the sort order or modifying the
  1204. * fields in the user arrays.
  1205. *
  1206. * [category]
  1207. * User data handling
  1208. *
  1209. * [when]
  1210. * Each time the phorum_api_user_list() function is called. The core
  1211. * Phorum code calls the function for creating user drop down lists
  1212. * (if those are enabled in the Phorum general settings) for the
  1213. * group moderation interface in the control center and for sending
  1214. * private messages.
  1215. *
  1216. * [input]
  1217. * An array of user info arrays. Each user info array contains the
  1218. * fields "user_id", "username" and "display_name". The hook function
  1219. * is allowed to update the "username" and "display_name" fields.
  1220. *
  1221. * [output]
  1222. * The same array as was used for the hook call argument,
  1223. * possibly with some updated fields in it.
  1224. *
  1225. * [example]
  1226. * <hookcode>
  1227. * function phorum_mod_foo_user_list($users)
  1228. * {
  1229. * // Only run this hook code for authenticated users.
  1230. * if (empty($PHORUM["user"]["user_id"])) return $users;
  1231. *
  1232. * // Retrieve a list of buddies for the active user.
  1233. * // If there are no buddies, then no work is needed.
  1234. * $buddies = phorum_db_pm_buddy_list();
  1235. * if (empty($buddies)) return $users;
  1236. *
  1237. * // Flag buddies in the user list.
  1238. * $langstr = $GLOBALS["PHORUM"]["DATA"]["LANG"]["Buddy"];
  1239. * foreach ($buddies as $user_id => $info) {
  1240. * $users[$user_id]["display_name"] .= " ($langstr)";
  1241. * }
  1242. *
  1243. * return $users;
  1244. * }
  1245. * </hookcode>
  1246. */
  1247. if (isset($GLOBALS['PHORUM']['hooks']['user_list'])) {
  1248. $list = phorum_hook('user_list', $list);
  1249. }
  1250. return $list;
  1251. }
  1252. // }}}
  1253. // {{{ Function: phorum_api_user_increment_posts()
  1254. /**
  1255. * Increment the posts counter for a user.
  1256. *
  1257. * @param mixed $user_id
  1258. * The user_id for which to increment the posts counter
  1259. * or NULL (the default) to increment the posts counter for the
  1260. * active Phorum user.
  1261. */
  1262. function phorum_api_user_increment_posts($user_id = NULL)
  1263. {
  1264. if (empty($user_id)) {
  1265. $user_id = $GLOBALS["PHORUM"]["user"]["user_id"];
  1266. }
  1267. settype($user_id, "int");
  1268. phorum_db_user_increment_posts($user_id);
  1269. }
  1270. // }}}
  1271. // {{{ Function: phorum_api_user_delete()
  1272. /**
  1273. * Delete a Phorum user.
  1274. *
  1275. * @param integer $user_id
  1276. * The user_id of the user that has to be deleted.
  1277. */
  1278. function phorum_api_user_delete($user_id)
  1279. {
  1280. settype($user_id, "int");
  1281. /**
  1282. * [hook]
  1283. * user_delete
  1284. *
  1285. * [description]
  1286. * Modules can use this hook to run some additional user cleanup
  1287. * tasks or or to keep some external system in sync with the Phorum
  1288. * user data.
  1289. *
  1290. * [category]
  1291. * User data handling
  1292. *
  1293. * [when]
  1294. * Just before a user is deleted.
  1295. *
  1296. * [input]
  1297. * The user_id of the user that will be deleted.
  1298. *
  1299. * [output]
  1300. * The same user_id as the one that was used for the hook
  1301. * call argument.
  1302. *
  1303. * [example]
  1304. * <hookcode>
  1305. * function phorum_mod_foo_user_delete($user_id)
  1306. * {
  1307. * // Get user info
  1308. * $user = phorum_api_user_get($user_id);
  1309. *
  1310. * // Log user delete through syslog.
  1311. * openlog("Phorum", LOG_PID | LOG_PERROR, LOG_LOCAL0);
  1312. * syslog(LOG_NOTICE, "Delete user registration: $user[username]");
  1313. *
  1314. * return $user_id;
  1315. * }
  1316. * </hookcode>
  1317. */
  1318. if (isset($GLOBALS['PHORUM']['hooks']['user_delete'])) {
  1319. phorum_hook('user_delete', $user_id);
  1320. }
  1321. // If user caching is enabled, we remove the user from the cache.
  1322. if (!empty($GLOBALS['PHORUM']['cache_users'])) {
  1323. phorum_cache_remove('user', $user_id);
  1324. }
  1325. // Remove the user and user related data from the database.
  1326. phorum_db_user_delete($user_id);
  1327. // Delete the personal user files for this user.
  1328. require_once(dirname(__FILE__).'/file_storage.php');
  1329. $files = phorum_api_file_list(PHORUM_LINK_USER, $user_id, 0);
  1330. foreach ($files as $file_id => $file) {
  1331. phorum_api_file_delete($file_id);
  1332. }
  1333. }
  1334. // }}}
  1335. // {{{ Function: phorum_api_user_format()
  1336. /*
  1337. * This function handles preparing user data * for use in the templates.
  1338. *
  1339. * @param mixed $users
  1340. * An array of user data records to format.
  1341. *
  1342. * @return array
  1343. * The same as the $users argument array, with formatting applied.
  1344. */
  1345. function phorum_api_user_format($users)
  1346. {
  1347. global $PHORUM;
  1348. foreach ($users as $id => $user)
  1349. {
  1350. foreach (array(
  1351. 'username', 'real_name', 'display_name',
  1352. 'email', 'signature'
  1353. ) as $field) {
  1354. if (isset($user[$field])) {
  1355. $users[$id][$field] = htmlspecialchars(
  1356. $user[$field], ENT_COMPAT, $PHORUM["DATA"]["HCHARSET"]
  1357. );
  1358. }
  1359. }
  1360. }
  1361. return $users;
  1362. }
  1363. // }}}
  1364. // ----------------------------------------------------------------------
  1365. // Authentication and session management.
  1366. // ----------------------------------------------------------------------
  1367. // {{{ Function: phorum_api_user_authenticate()
  1368. /**
  1369. * Check the authentication credentials for a user.
  1370. *
  1371. * @example user_login.php Handle a user forum login
  1372. *
  1373. * @param string $type
  1374. * The type of session for which authentication is run. This must be
  1375. * one of {@link PHORUM_FORUM_SESSION} or {@link PHORUM_ADMIN_SESSION}.
  1376. *
  1377. * This parameter is mostly used for logging purposes and for giving
  1378. * mods a chance to handle user authentication for only a certain type
  1379. * of session. It is not used for denying authentication if for example
  1380. * a standard user tries to authenticate for the admin interface. Those
  1381. * restrictions are handled in a different part of the user API.
  1382. *
  1383. * See the documentation for {@link phorum_api_user_session_create()}
  1384. * for more information on Phorum user sessions.
  1385. *
  1386. * @param string $username
  1387. * The username for the user.
  1388. *
  1389. * @param string $password
  1390. * The password for the user.
  1391. *
  1392. * @return mixed
  1393. * If the authentication credentials are correct, this function returns
  1394. * the user_id of the authenticated user. Otherwise, FALSE is returned.
  1395. */
  1396. function phorum_api_user_authenticate($type, $username, $password)
  1397. {
  1398. $PHORUM = $GLOBALS['PHORUM'];
  1399. $user_id = NULL;
  1400. /**
  1401. * [hook]
  1402. * user_authenticate
  1403. *
  1404. * [description]
  1405. * This hooks gives modules a chance to handle the user
  1406. * authentication (for example to authenticate against an
  1407. * external source like an LDAP server).
  1408. *
  1409. * [category]
  1410. * User authentication and session handling
  1411. *
  1412. * [when]
  1413. * Just before Phorum runs its own user authentication.
  1414. *
  1415. * [input]
  1416. * An array containing the following fields:
  1417. * <ul>
  1418. * <li>type:
  1419. * either PHORUM_FORUM_SESSION or PHORUM_ADMIN_SESSION;</li>
  1420. * <li>username:
  1421. * the username of the user to authenticate;</li>
  1422. * <li>password:
  1423. * the password of the user to authenticate;</li>
  1424. * <li>user_id:
  1425. * Always NULL on input. This field implements the
  1426. * authentication state.</li>
  1427. * </ul>
  1428. *
  1429. * [output]
  1430. * The same array as the one that was used for the hook call
  1431. * argument, possibly with the user_id field updated. This field
  1432. * can be set to one of the following values by a module:
  1433. *
  1434. * <ul>
  1435. * <li>NULL: let Phorum handle the authentication</li>
  1436. * <li>FALSE: the authentication credentials are rejected</li>
  1437. * <li>1234: the numerical user_id of the authenticated user</li>
  1438. * </ul>
  1439. *
  1440. * [example]
  1441. * <hookcode>
  1442. * function phorum_mod_foo_user_authenticate($auth)
  1443. * {
  1444. * // Only trust admin logins from IP addresses in 10.1.2.0/24.
  1445. * if ($auth["type"] == PHORUM_ADMIN_SESSION) {
  1446. * if (substr($_SERVER['REMOTE_ADDR'],0,7) != '10.1.2.') {
  1447. * $auth["user_id"] = FALSE;
  1448. * return $auth;
  1449. * }
  1450. * }
  1451. *
  1452. * // Let Phorum handle autentication for all users that
  1453. * // have a username starting with "bar" (not a really
  1454. * // useful feature, but it shows the use of the NULL
  1455. * // return value ;-).
  1456. * if (substr($auth["username"], 0, 3) == "bar") {
  1457. * $auth["user_id"] = NULL;
  1458. * return $auth;
  1459. * }
  1460. *
  1461. * // Authenticate other logins against an external source. Here
  1462. * // we call some made up function for checking the password,
  1463. * // which returns the user_id for the authenticated user.
  1464. * $user_id = some_func_that_checks_pw(
  1465. * $auth["username"],
  1466. * $auth["password"]
  1467. * );
  1468. * $auth["user_id"] = empty($user_id) ? FALSE : $user_id;
  1469. * return $auth;
  1470. * }
  1471. * </hookcode>
  1472. */
  1473. if (isset($PHORUM['hooks']['user_authenticate']))
  1474. {
  1475. // Run the hook.
  1476. $authinfo = phorum_hook('user_authenticate', array(
  1477. 'type' => $type,
  1478. 'username' => $username,
  1479. 'password' => $password,
  1480. 'user_id' => NULL
  1481. ));
  1482. // Authentication rejected by module.
  1483. if ($authinfo['user_id'] === FALSE) {
  1484. return FALSE;
  1485. }
  1486. // Check if the returned user_id is numerical, if the the module
  1487. // did return a user_id.
  1488. if ($authinfo['user_id']!==NULL && !is_numeric($authinfo['user_id'])) {
  1489. trigger_error(
  1490. 'Hook user_check_login returned a non-numerical user_id "' .
  1491. htmlspecialchars($authinfo['user_id']) .
  1492. '" for the authenticated user. Phorum only supports numerical ' .
  1493. 'user_id values.',
  1494. E_USER_ERROR
  1495. );
  1496. return NULL;
  1497. }
  1498. $user_id = $authinfo['user_id'];
  1499. }
  1500. // No module handled the authentication?
  1501. // Then we have to run the Phorum authentication.
  1502. if ($user_id === NULL)
  1503. {
  1504. // Check the password.
  1505. $user_id = phorum_db_user_check_login($username, md5($password));
  1506. // Password check failed? Then try the temporary password (used for
  1507. // the password reminder feature).
  1508. $temporary_matched = FALSE;
  1509. if ($user_id == 0) {
  1510. $user_id = phorum_db_user_check_login($username, md5($password), TRUE);
  1511. if ($user_id != 0) {
  1512. $temporary_matched = TRUE;
  1513. }
  1514. }
  1515. // If the temporary password matched, then synchronize the main
  1516. // password with the temporary password. The temporary password
  1517. // is kept the same. We also reset the long term session id, so
  1518. // sessions in other browsers are reset along with the pasword
  1519. // reset. For the active browser, a new session id will be generated
  1520. // by the {@link phorum_api_user_session_create()} function.
  1521. if ($temporary_matched) {
  1522. phorum_api_user_save(array(
  1523. 'user_id' => $user_id,
  1524. 'password' => $password,
  1525. 'sessid_lt' => ''
  1526. ));
  1527. }
  1528. }
  1529. return $user_id ? $user_id : FALSE;
  1530. }
  1531. // }}}
  1532. // {{{ Function: phorum_api_user_set_active_user()
  1533. /**
  1534. * Set the active Phorum user.
  1535. *
  1536. * This function can be used to setup the Phorum data in $PHORUM['user']
  1537. * to indicate which user is logged in or to setup the anonymous user.
  1538. * Calling this function is all that is needed to tell Phorum which user
  1539. * is logged in (or to tell that no user is logged in by setting up the
  1540. * anonymous user in $PHORUM['user']).
  1541. *
  1542. * Next to setting up the user data, the function will handle user activity
  1543. * tracking (based on the "track_user_activity" setting) and setup some
  1544. * special (template) variables:
  1545. *
  1546. * The variabe $PHORUM["DATA"]["ADMINISTRATOR"] will be set to TRUE if
  1547. * the active user is an administrator, FALSE otherwise.
  1548. *
  1549. * For type {@link PHORUM_FORUM_SESSION}, the following extra variables
  1550. * will be filled:
  1551. *
  1552. * - $PHORUM["DATA"]["LOGGEDIN"]:
  1553. * TRUE if the user is logged in, FALSE otherwise.
  1554. *
  1555. * - $PHORUM["DATA"]["FULLY_LOGGEDIN"]:
  1556. * TRUE if a short term session is active (by setting the
  1557. * {@link PHORUM_FLAG_SESSION_ST} flag for the $flags parameter),
  1558. * FALSE otherwise.
  1559. *
  1560. * @example user_login.php Handle a user forum login
  1561. *
  1562. * @param string $type
  1563. * The type of session for which to set the active user. This must be
  1564. * one of {@link PHORUM_FORUM_SESSION} or {@link PHORUM_ADMIN_SESSION}.
  1565. * See the documentation for {@link phorum_api_user_session_create()}
  1566. * for more information on Phorum user sessions.
  1567. *
  1568. * @param mixed $user
  1569. * The user_id or the full user data array for the user that has to be
  1570. * the active user or NULL if the active user has to be set to the
  1571. * anonymous user (the default).
  1572. *
  1573. * @param integer $flags
  1574. * If the flag {@link PHORUM_FLAG_SESSION_ST} is set, then the short
  1575. * term session will be enabled for {@link PHORUM_FORUM_SESSION}
  1576. * based sessions.
  1577. *
  1578. * @return boolean
  1579. * TRUE if a real user was set as the active user successfully
  1580. * or FALSE if the anonymous user was set (either because that was
  1581. * requested or because setting the real user failed). If setting a
  1582. * real user as the active user failed, the functions
  1583. * {@link phorum_api_strerror()} and {@link phorum_api_errno()} can be
  1584. * used to retrieve information about the error which occurred.
  1585. */
  1586. function phorum_api_user_set_active_user($type, $user = NULL, $flags = 0)
  1587. {
  1588. global $PHORUM;
  1589. // Reset error storage.
  1590. $GLOBALS['PHORUM']['API']['errno'] = NULL;
  1591. $GLOBALS['PHORUM']['API']['error'] = NULL;
  1592. // Determine what user to use.
  1593. if ($user !== NULL)
  1594. {
  1595. // Use a full user array.
  1596. if (is_array($user)) {
  1597. // Some really basic checks on the user data. If something's
  1598. // missing, then we fall back to the anonymous user.
  1599. if (!isset($user['user_id']) ||
  1600. !isset($user['active'])) {
  1601. phorum_api_error_set(
  1602. PHORUM_ERRNO_ERROR,
  1603. 'phorum_api_user_set_active_user(): ' .
  1604. 'user record seems incomplete'
  1605. );
  1606. $user = NULL;
  1607. }
  1608. }
  1609. // Retrieve the user by its user_id.
  1610. elseif (is_numeric($user)) {
  1611. $user = phorum_api_user_get($user, TRUE);
  1612. }
  1613. // Bogus $user parameter.
  1614. else {
  1615. trigger_error(
  1616. 'phorum_api_user_set_active_user(): $user argument should be ' .
  1617. 'one of NULL, array or integer',
  1618. E_USER_ERROR
  1619. );
  1620. return NULL;
  1621. }
  1622. // Fall back to the anonymous user if the user is not activated.
  1623. if ($user && $user['active'] != PHORUM_USER_ACTIVE) {
  1624. phorum_api_error_set(
  1625. PHORUM_ERRNO_ERROR,
  1626. 'phorum_api_user_set_active_user(): ' .
  1627. 'the user is not active'
  1628. );
  1629. $user = NULL;
  1630. }
  1631. // Fall back to the anonymous user if the user does not have
  1632. // admin rights, while an admin setup was requested.
  1633. if ($type == PHORUM_ADMIN_SESSION && $user && empty($user['admin'])) {
  1634. phorum_api_error_set(
  1635. PHORUM_ERRNO_ERROR,
  1636. 'phorum_api_user_set_active_user(): ' .
  1637. 'the user is not an administrator'
  1638. );
  1639. $user = NULL;
  1640. }
  1641. }
  1642. // Clear the special variables.
  1643. $PHORUM['DATA']['LOGGEDIN'] = FALSE;
  1644. $PHORUM['DATA']['FULLY_LOGGEDIN'] = FALSE;
  1645. $PHORUM['DATA']['ADMINISTRATOR'] = FALSE;
  1646. // ----------------------------------------------------------------------
  1647. // Set the anonymous user.
  1648. // ----------------------------------------------------------------------
  1649. if (! $user)
  1650. {
  1651. // Fill the Phorum user with anonymous user data.
  1652. $PHORUM['user'] = array(
  1653. 'user_id' => 0,
  1654. 'username' => '',
  1655. 'real_name' => '',
  1656. 'admin' => false,
  1657. 'newinfo' => array(),
  1658. 'tz_offset' => -99
  1659. );
  1660. return FALSE;
  1661. }
  1662. // ----------------------------------------------------------------------
  1663. // Set the active Phorum user and handle activity tracking.
  1664. // ----------------------------------------------------------------------
  1665. $PHORUM['user'] = $user;
  1666. if (!empty($user['admin'])) {
  1667. $PHORUM['DATA']['ADMINISTRATOR'] = TRUE;
  1668. }
  1669. if ($type == PHORUM_FORUM_SESSION) {
  1670. $PHORUM['DATA']['LOGGEDIN'] = TRUE;
  1671. if ($flags & PHORUM_FLAG_SESSION_ST) {
  1672. $PHORUM['DATA']['FULLY_LOGGEDIN'] = TRUE;
  1673. }
  1674. }
  1675. // Handle tracking user activity. The "track_user_activity" setting
  1676. // specifies the user activity update interval in seconds (the lower
  1677. // this setting is, the more often the database will be updated).
  1678. if ($PHORUM['track_user_activity'] &&
  1679. (empty($user['date_last_active']) ||
  1680. $user['date_last_active'] < time() - $PHORUM['track_user_activity']))
  1681. {
  1682. $date_last_active = time();
  1683. $last_active_forum = empty($PHORUM['forum_id'])
  1684. ? 0 : $PHORUM['forum_id'];
  1685. // Update the user data in the database.
  1686. phorum_api_user_save_raw(array(
  1687. 'user_id' => $user['user_id'],
  1688. 'date_last_active' => $date_last_active,
  1689. 'last_active_forum' => $last_active_forum
  1690. ));
  1691. // Update the live user data.
  1692. $PHORUM['user']['date_last_active'] = $date_last_active;
  1693. $PHORUM['user']['last_active_forum'] = $last_active_forum;
  1694. }
  1695. return TRUE;
  1696. }
  1697. // }}}
  1698. // {{{ Function: phorum_api_user_get_active_user()
  1699. /**
  1700. * Retrieve the active Phorum user.
  1701. *
  1702. * This function was added in Phorum 5.2.16.
  1703. *
  1704. * @return NULL|array
  1705. * This method will return the data for the active Phorum user or
  1706. * NULL when this user is an anonymous user (i.e. not logged in.)
  1707. */
  1708. function phorum_api_user_get_active_user()
  1709. {
  1710. global $PHORUM;
  1711. return empty($PHORUM['user']['user_id']) ? NULL : $PHORUM['user'];
  1712. }
  1713. // }}}
  1714. // {{{ Function: phorum_api_user_session_create()
  1715. /**
  1716. * Create a Phorum user session.
  1717. *
  1718. * Before calling this function, the variable $PHORUM['use_cookies']
  1719. * should be set to one of {@link PHORUM_NO_COOKIES},
  1720. * {@link PHORUM_USE_COOKIES} or {@link PHORUM_REQUIRE_COOKIES}.
  1721. *
  1722. * Phorum does not use PHP sessions. Instead, it uses its own session
  1723. * management system for remembering logged in users. There are
  1724. * multiple reasons for that, amongst which are:
  1725. *
  1726. * - the lack of session support (on some PHP installs);
  1727. * - missing out of the box load balancing support (sessions are normally
  1728. * written to local session state files, so multiple machines would not
  1729. * work well together);
  1730. * - file I/O problems (both performance and file system permissions can
  1731. * be a problem);
  1732. * - the amount of unneeded overhead that is caused by the PHP session system;
  1733. * - the fact that Phorum also supports URI based sessions (without cookie).
  1734. *
  1735. * This function can be used to create or maintain a login session for a
  1736. * Phorum user. A prerequisite is that an active Phorum user is set through
  1737. * the {@link phorum_api_user_set_active_user()} function, before calling
  1738. * this function.
  1739. *
  1740. * There are two session types available: {@link PHORUM_FORUM_SESSION}
  1741. * (used for the front end application) and {@link PHORUM_ADMIN_SESSION}
  1742. * (used for the administrative back end).
  1743. *
  1744. * Admin sessions are used for the administrative back end system. For
  1745. * security reasons, the back end does not share the front end session,
  1746. * but uses a fully separate session instead. This session does not
  1747. * have a timeout restriction, but it does not survive closing the
  1748. * browser. It is always tracked using a cookie, never using URI
  1749. * authentication (for security reasons).
  1750. *
  1751. * The forum sessions can be split up into long term and short term sessions:
  1752. *
  1753. * - Long term session:
  1754. * The standard Phorum user session. This session is long lasting and will
  1755. * survive after closing the browser (unless the long term session timeout
  1756. * is set to zero). If tighter security is not enabled, then this session
  1757. * is all a user needs to fully use all forum options. This session is
  1758. * tracked using either a cookie or URI authentication.
  1759. *
  1760. * - Short term session:
  1761. * This session has a limited life time and will not survive closing the
  1762. * browser. If tighter security is enabled, then the user will not be able
  1763. * to use all forum functions, unless there is a short term session active
  1764. * (e.g. posting forum messages and reading/writing private messages are
  1765. * restricted). This session is tracked using a cookie. If URI authentication
  1766. * is in use (because of admin config or cookie-less browsers) Phorum will
  1767. * only look at the long term session (even in tighter security mode), since
  1768. * URI authentication can be considered to be short term by nature.
  1769. *
  1770. * @example user_login.php Handle a user forum login
  1771. *
  1772. * @param string $type
  1773. * The type of session to initialize. This must be one of
  1774. * {@link PHORUM_FORUM_SESSION} or {@link PHORUM_ADMIN_SESSION}.
  1775. *
  1776. * @param integer $reset
  1777. * If it is set to 0 (zero, the default), then existing session_ids
  1778. * will be reused if possible.
  1779. *
  1780. * If this parameter is set to PHORUM_SESSID_RESET_LOGIN, then a new
  1781. * session id will be generated for short term forum sessions and if
  1782. * cookies are disabled for some reason, for long term forum sessions
  1783. * as well (to prevent accidental distribution of URLs with auth info
  1784. * in them). This is the type of session id reset that is appropriate
  1785. * after handling a login action.
  1786. *
  1787. * If this parameter is set to PHORUM_SESSID_RESET_ALL, then all session
  1788. * ids will be reset to new values. This is for example
  1789. * appropriate after a user changed the password (so active sessions on
  1790. * other computers or browsers will be ended).
  1791. *
  1792. * @return boolean
  1793. * TRUE in case the session was initialized successfully.
  1794. * Otherwise, FALSE will be returned. The functions
  1795. * {@link phorum_api_strerror()} and {@link phorum_api_errno()} can be
  1796. * used to retrieve information about the error which occurred.
  1797. */
  1798. function phorum_api_user_session_create($type, $reset = 0)
  1799. {
  1800. global $PHORUM;
  1801. /**
  1802. * [hook]
  1803. * user_session_create
  1804. *
  1805. * [description]
  1806. * Allow modules to override Phorum's session create management or
  1807. * to even fully omit creating a session (for example useful
  1808. * if the hook <hook>user_session_restore</hook> is used
  1809. * to inherit an external session from some 3rd party application).
  1810. *
  1811. * [category]
  1812. * User authentication and session handling
  1813. *
  1814. * [when]
  1815. * Just before Phorum runs its own session initialization code
  1816. * in the user API function
  1817. * <literal>phorum_api_user_session_create()</literal>.
  1818. *
  1819. * [input]
  1820. * The session type for which a session must be created.
  1821. * This can be either <literal>PHORUM_FORUM_SESSION</literal>
  1822. * or <literal>PHORUM_ADMIN_SESSION</literal>.
  1823. *
  1824. * [output]
  1825. * Same as input if Phorum has to run its standard session
  1826. * initialization code or NULL if that code should be fully skipped.
  1827. *
  1828. * [example]
  1829. * <hookcode>
  1830. * function phorum_mod_foo_user_session_create($type)
  1831. * {
  1832. * // Let Phorum handle admin sessions on its own.
  1833. * if ($type == PHORUM_ADMIN_SESSION) return $type;
  1834. *
  1835. * // Override the session handling for front end forum sessions.
  1836. * // We could for example put the session in a standard PHP
  1837. * // session by first starting a PHP session if that was
  1838. * // not done yet...
  1839. * if (!session_id()) session_start();
  1840. *
  1841. * // ...and then storing the user_id of the current user in the
  1842. * // PHP session data. The user_id is really the only thing
  1843. * // that needs to be remembered for a Phorum session, because
  1844. * // all other data for the user is stored in the database.
  1845. * $phorum_user_id = $GLOBALS["PHORUM"]["user"]["user_id"];
  1846. * $_SESSION['phorum_user_id'] = $phorum_user_id;
  1847. *
  1848. * // Tell Phorum not to run its own session initialization code.
  1849. * return NULL;
  1850. * }
  1851. * </hookcode>
  1852. *
  1853. * See the <hook>user_session_restore</hook> hook for an example
  1854. * of how to let Phorum pick up this PHP based session.
  1855. */
  1856. if (isset($PHORUM['hooks']['user_session_create'])) {
  1857. if (phorum_hook('user_session_create', $type) === NULL) {
  1858. return TRUE;
  1859. }
  1860. }
  1861. // Reset error storage.
  1862. $PHORUM['API']['errno'] = NULL;
  1863. $PHORUM['API']['error'] = NULL;
  1864. // Check if we have a valid session type.
  1865. if ($type != PHORUM_FORUM_SESSION &&
  1866. $type != PHORUM_ADMIN_SESSION) {
  1867. trigger_error(
  1868. 'phorum_api_user_session_create(): Illegal session type: ' .
  1869. htmlspecialchars($type),
  1870. E_USER_ERROR
  1871. );
  1872. return NULL;
  1873. }
  1874. // Check if the active Phorum user was set.
  1875. if (empty($PHORUM['user']) ||
  1876. empty($PHORUM['user']['user_id'])) {
  1877. trigger_error(
  1878. 'phorum_api_user_session_create(): Missing user in environment',
  1879. E_USER_ERROR
  1880. );
  1881. return NULL;
  1882. }
  1883. // Check if the user is activated.
  1884. if ($PHORUM['user']['active'] != PHORUM_USER_ACTIVE) {
  1885. return phorum_api_error_set(
  1886. PHORUM_ERRNO_NOACCESS,
  1887. 'The user is not (yet) activated (user id '.$PHORUM['user']['user_id'].')'
  1888. );
  1889. }
  1890. // For admin sessions, check if the user has administrator rights.
  1891. // This is also checked from phorum_api_user_set_active_user(), but
  1892. // one can never be too sure about this.
  1893. if ($type == PHORUM_ADMIN_SESSION &&
  1894. empty($PHORUM['user']['admin'])) {
  1895. return phorum_api_error_set(
  1896. PHORUM_ERRNO_NOACCESS,
  1897. 'The user is not an administrator (user id '.$PHORUM['user']['user_id'].')'
  1898. );
  1899. }
  1900. // Shortcut for checking if session ids are stored in cookies.
  1901. // Note that the software that uses this function is responsible for
  1902. // setting $PHORUM["use_cookies"] to PHORUM_NO_COOKIES if the client
  1903. // does not support cookies.
  1904. $use_cookies = isset($PHORUM['use_cookies']) &&
  1905. $PHORUM['use_cookies'] > PHORUM_NO_COOKIES;
  1906. // ----------------------------------------------------------------------
  1907. // Retrieve or generate required session id(s).
  1908. // ----------------------------------------------------------------------
  1909. $user = $PHORUM['user'];
  1910. // Generate a long term session id. This one is used by all session types.
  1911. // Create a new long term session id if no session id is available yet or
  1912. // if a refresh was requested and cookies are disabled (with cookies
  1913. // enabled, we always reuse the existing long term session, so the session
  1914. // can be remembered and shared between multiple browsers / computers).
  1915. $refresh_sessid_lt =
  1916. empty($user['sessid_lt']) ||
  1917. (!$use_cookies && $reset == PHORUM_SESSID_RESET_LOGIN) ||
  1918. $reset == PHORUM_SESSID_RESET_ALL;
  1919. if ($refresh_sessid_lt) {
  1920. $sessid_lt = md5($user['username'].microtime().$user['password']);
  1921. phorum_api_user_save_raw(array(
  1922. 'user_id' => $user['user_id'],
  1923. 'sessid_lt' => $sessid_lt,
  1924. ));
  1925. $PHORUM['user']['sessid_lt'] = $sessid_lt;
  1926. } else {
  1927. $sessid_lt = $user['sessid_lt'];
  1928. }
  1929. // For forum sessions, generate a short term session id if tight
  1930. // security is enabled in the configuration and cookies are enabled
  1931. // (with URI authentication, the tight security system is bypassed
  1932. // since the user will have to login on every visit already).
  1933. $refresh_sessid_st = FALSE;
  1934. if ($type == PHORUM_FORUM_SESSION &&
  1935. !empty($PHORUM['tight_security']) &&
  1936. $use_cookies)
  1937. {
  1938. // How much longer is the existing short term session id valid?
  1939. $timeleft = empty($user['sessid_st_timeout'])
  1940. ? 0 : $user['sessid_st_timeout'] - time();
  1941. // Create a new short term session id if ..
  1942. if (empty($user['sessid_st']) || // .. no session id is available yet
  1943. $reset) { // .. any type of reset was requested
  1944. $sessid_st = md5($user['username'].microtime().$user['password']);
  1945. $refresh_sessid_st = TRUE;
  1946. } else {
  1947. // Reuse the existing short term session id
  1948. $sessid_st = $user['sessid_st'];
  1949. // Have the session timeout reset if more than one third of the
  1950. // session's life time has passed and if the session has not
  1951. // yet expired.
  1952. if ($timeleft > 0 &&
  1953. $timeleft < $PHORUM['short_session_timeout']*60/2) {
  1954. $refresh_sessid_st = TRUE;
  1955. }
  1956. }
  1957. // The session data needs updating.
  1958. if ($refresh_sessid_st) {
  1959. $timeout = time() + $PHORUM['short_session_timeout']*60;
  1960. phorum_api_user_save_raw(array(
  1961. 'user_id' => $user['user_id'],
  1962. 'sessid_st' => $sessid_st,
  1963. 'sessid_st_timeout' => $timeout
  1964. ));
  1965. $PHORUM['user']['sessid_st'] = $sessid_st;
  1966. $PHORUM['user']['sessid_st_timeout'] = $timeout;
  1967. }
  1968. }
  1969. // For admin sessions, the session id is computed using the long term
  1970. // session id and some random data that was generated at install time.
  1971. if ($type == PHORUM_ADMIN_SESSION) {
  1972. $sessid_admin = md5($sessid_lt . $PHORUM['admin_session_salt']);
  1973. }
  1974. // ----------------------------------------------------------------------
  1975. // Route the required session id(s) to the user.
  1976. // ----------------------------------------------------------------------
  1977. $user = $PHORUM['user'];
  1978. if ($type == PHORUM_FORUM_SESSION)
  1979. {
  1980. // The long term session can be stored in either a cookie or
  1981. // URL / form posting data.
  1982. if ($use_cookies) {
  1983. $timeout = empty($PHORUM['session_timeout'])
  1984. ? 0 : time() + 86400 * $PHORUM['session_timeout'];
  1985. setcookie(
  1986. PHORUM_SESSION_LONG_TERM,
  1987. $user['user_id'].':'.$sessid_lt,
  1988. $timeout,
  1989. $PHORUM['session_path'], $PHORUM['session_domain']
  1990. );
  1991. } else {
  1992. // Add the session id to the URL building GET variables.
  1993. $PHORUM['DATA']['GET_VARS'][PHORUM_SESSION_LONG_TERM] =
  1994. PHORUM_SESSION_LONG_TERM . '=' .
  1995. urlencode($user['user_id'].':'.$sessid_lt);
  1996. // Add the session id to the form POST variables.
  1997. $PHORUM['DATA']['POST_VARS'] .=
  1998. '<input type="hidden" name="'.PHORUM_SESSION_LONG_TERM.'" ' .
  1999. 'value="'.$user['user_id'].':'.$sessid_lt.'" />';
  2000. }
  2001. // The short term session id is always put in a cookie.
  2002. if ($refresh_sessid_st) {
  2003. setcookie(
  2004. PHORUM_SESSION_SHORT_TERM,
  2005. $user['user_id'].':'.$user['sessid_st'],
  2006. $user['sessid_st_timeout'],
  2007. $PHORUM['session_path'], $PHORUM['session_domain']
  2008. );
  2009. }
  2010. }
  2011. // The admin session id is always put in a cookie.
  2012. elseif ($type == PHORUM_ADMIN_SESSION) {
  2013. setcookie(
  2014. PHORUM_SESSION_ADMIN,
  2015. $user['user_id'].':'.$sessid_admin,
  2016. 0, // admin sessions are destroyed as soon as the browser closes
  2017. $PHORUM['session_path'], $PHORUM['session_domain']
  2018. );
  2019. }
  2020. return TRUE;
  2021. }
  2022. // }}}
  2023. // {{{ Function: phorum_api_user_session_restore()
  2024. /**
  2025. * Restore a Phorum user session.
  2026. *
  2027. * This function will check for a valid user session for either the
  2028. * forum or the admin interface (based on the $type parameter). If a valid
  2029. * session is found, then the user session will be restored.
  2030. *
  2031. * Before calling this function, the variable $PHORUM['use_cookies']
  2032. * should be set to one of {@link PHORUM_NO_COOKIES},
  2033. * {@link PHORUM_USE_COOKIES} or {@link PHORUM_REQUIRE_COOKIES}.
  2034. *
  2035. * @param string $type
  2036. * The type of session to check for. This must be one of
  2037. * {@link PHORUM_FORUM_SESSION} or {@link PHORUM_ADMIN_SESSION}.
  2038. * See the documentation for {@link phorum_api_user_session_create()}
  2039. * for more information on Phorum user sessions.
  2040. *
  2041. * @return boolean
  2042. * TRUE in case a valid session is detected, otherwise FALSE.
  2043. * Note that a {@link PHORUM_FORUM_SESSION} will return TRUE if
  2044. * a long term session is detected and that the sort term session
  2045. * might be missing. Code which depends on short term sessions should
  2046. * investigate the $PHORUM["DATA"]["FULLY_LOGGEDIN"] variable.
  2047. */
  2048. function phorum_api_user_session_restore($type)
  2049. {
  2050. $PHORUM = $GLOBALS['PHORUM'];
  2051. // ----------------------------------------------------------------------
  2052. // Determine which session cookie(s) to check.
  2053. // ----------------------------------------------------------------------
  2054. // A list of session cookies to lookup.
  2055. // The possible values for the items in this array are:
  2056. //
  2057. // 0: this cookie does not have to be checked
  2058. // 1: a check has to be done or failed for this cookie
  2059. // 2: the check for this cookie was successful
  2060. //
  2061. $check_session = array(
  2062. PHORUM_SESSION_LONG_TERM => 0,
  2063. PHORUM_SESSION_SHORT_TERM => 0,
  2064. PHORUM_SESSION_ADMIN => 0
  2065. );
  2066. if ($type == PHORUM_FORUM_SESSION)
  2067. {
  2068. // Lookup the long term cookie.
  2069. $check_session[PHORUM_SESSION_LONG_TERM] = 1;
  2070. // Lookup the short term cookie if tight security is enabled.
  2071. if (!empty($PHORUM['tight_security'])) {
  2072. $check_session[PHORUM_SESSION_SHORT_TERM] = 1;
  2073. }
  2074. }
  2075. elseif ($type == PHORUM_ADMIN_SESSION)
  2076. {
  2077. // Lookup the admin cookie.
  2078. $check_session[PHORUM_SESSION_ADMIN] = 1;
  2079. }
  2080. else {
  2081. trigger_error(
  2082. 'phorum_api_user_session_restore(): Illegal session type: ' .
  2083. htmlspecialchars($type),
  2084. E_USER_ERROR
  2085. );
  2086. return NULL;
  2087. }
  2088. // ----------------------------------------------------------------------
  2089. // Check the session cookie(s).
  2090. // ----------------------------------------------------------------------
  2091. // Now we decided what session cookie(s) we want to check, we allow
  2092. // modules to hook into the session system to do the check for us.
  2093. // This can for example be used to let Phorum inherit an already
  2094. // running authenticated session in some external system.
  2095. /**
  2096. * [hook]
  2097. * user_session_restore
  2098. *
  2099. * [description]
  2100. * Allow modules to override Phorum's session restore management.
  2101. * This hook is the designated hook if you need to let Phorum
  2102. * inherit an authenticated session from some external system.<sbr/>
  2103. * <sbr/>
  2104. * The array that is passed to this hook,
  2105. * contains a key for each of the Phorum session types:
  2106. * <ul>
  2107. * <li>PHORUM_SESSION_LONG_TERM</li>
  2108. * <li>PHORUM_SESSION_SHORT_TERM</li>
  2109. * <li>PHORUM_SESSION_ADMIN</li>
  2110. * </ul>
  2111. * What the module has to do, is fill the values for each of these
  2112. * keys with the user_id of the Phorum user for which the session that
  2113. * the key represents should be considered active. Other options
  2114. * are FALSE to indicate that no session is active and NULL to
  2115. * tell Phorum to handle session restore on its own.<sbr/>
  2116. * <sbr/>
  2117. * Note that the user for which a user_id is provided through this
  2118. * hook must exist in the Phorum system before returning from this
  2119. * hook. One option to take care of that constraint is letting
  2120. * this hook create the user on-the-fly if needed. A cleaner way
  2121. * would be to synchronize the user data from the main system at those
  2122. * times when the user data changes (create, update and delete user).
  2123. * Of course it is highly dependent on the other system whether
  2124. * you can implement that kind of Phorum user management in the main
  2125. * application.<sbr/>
  2126. * <sbr/>
  2127. * Hint: Creating users can be done using the
  2128. * <literal> phorum_api_user_save()</literal> user API function.
  2129. *
  2130. * [category]
  2131. * User authentication and session handling
  2132. *
  2133. * [when]
  2134. * Just before Phorum runs its own session restore code
  2135. * in the user API function
  2136. * <literal>phorum_api_user_session_restore()</literal>.
  2137. *
  2138. * [input]
  2139. * An array containing three keys:
  2140. * <ul>
  2141. * <li>PHORUM_SESSION_LONG_TERM</li>
  2142. * <li>PHORUM_SESSION_SHORT_TERM</li>
  2143. * <li>PHORUM_SESSION_ADMIN</li>
  2144. * </ul>
  2145. * By default, all values for these keys are NULL.
  2146. *
  2147. * [output]
  2148. * Same as input, possibly with updated array values.
  2149. *
  2150. * [example]
  2151. * See the <hook>user_session_create</hook> hook for an example
  2152. * of how to let Phorum setup the PHP session that is picked up
  2153. * in this example hook.
  2154. * <hookcode>
  2155. * function phorum_mod_foo_user_session_restore($sessions)
  2156. * {
  2157. * // Override the session handling for front end forum sessions.
  2158. * // We could for example retrieve a session from a standard PHP
  2159. * // session by first starting a PHP session if that was
  2160. * // not done yet...
  2161. * if (!session_id()) session_start();
  2162. *
  2163. * // ...and then retrieving the user_id of the current user
  2164. * // from the PHP session data. The user_id is really the
  2165. * // only thing that needs to be remembered for a Phorum
  2166. * // session, because all other data for the user is stored
  2167. * // in the database. If no user id was set in the session,
  2168. * // then use FALSE to flag this to Phorum.
  2169. * $phorum_user_id = empty($_SESSION['phorum_user_id'])
  2170. * ? FALSE : $_SESSION['phorum_user_id'];
  2171. *
  2172. * // If we only use session inheritance for the front end
  2173. * // forum session (highly recommended for security), then
  2174. * // We keep PHORUM_SESSION_ADMIN at NULL (default value).
  2175. * // The other two need to be updated. If the main system does
  2176. * // not use the concept of one long and one short term cookie
  2177. * // (named "tight security" by Phorum), then simply assign
  2178. * // the user_id to both PHORUM_SESSION_LONG_TERM and
  2179. * // PHORUM_SESSION_SHORT_TERM.
  2180. * $sessions[PHORUM_SESSION_SHORT_TERM] = $phorum_user_id;
  2181. * $sessions[PHORUM_SESSION_LONG_TERM] = $phorum_user_id;
  2182. *
  2183. * return $sessions;
  2184. * }
  2185. * </hookcode>
  2186. */
  2187. $hook_sessions = array(
  2188. PHORUM_SESSION_LONG_TERM => NULL,
  2189. PHORUM_SESSION_SHORT_TERM => NULL,
  2190. PHORUM_SESSION_ADMIN => NULL
  2191. );
  2192. if (isset($PHORUM['hooks']['user_session_restore'])) {
  2193. $hook_sessions = phorum_hook('user_session_restore', $hook_sessions);
  2194. }
  2195. $real_cookie = FALSE;
  2196. $session_user = NULL;
  2197. foreach ($check_session as $cookie => $do_check)
  2198. {
  2199. if (!$do_check) continue;
  2200. // Check if a module did provide a user_id for the checked session.
  2201. $user_id_from_hook_session = FALSE;
  2202. if ($hook_sessions[$cookie] !== NULL) {
  2203. // Continue with the next cookie, if a module specified the
  2204. // session cookie as invalid.
  2205. if ($hook_sessions[$cookie] === FALSE) continue;
  2206. // Pass on the user_id that was set by the module.
  2207. // We add a fake a session id to the user_id here,
  2208. // to make the split from below work.
  2209. $value = $hook_sessions[$cookie] . ':dummy';
  2210. $user_id_from_hook_session = TRUE;
  2211. // To not let Phorum fall back to URI authentication.
  2212. $real_cookie = TRUE;
  2213. }
  2214. // Check for a real cookie, which can always be expected for
  2215. // short term and admin sessions and for long term sessions if
  2216. // cookies are enabled.
  2217. elseif (($cookie != PHORUM_SESSION_LONG_TERM ||
  2218. (isset($PHORUM['use_cookies']) &&
  2219. $PHORUM['use_cookies'] > PHORUM_NO_COOKIES)) &&
  2220. isset($_COOKIE[$cookie])) {
  2221. $value = $_COOKIE[$cookie];
  2222. $real_cookie = TRUE;
  2223. }
  2224. // Check for URI based authentication.
  2225. elseif ($PHORUM['use_cookies'] < PHORUM_REQUIRE_COOKIES &&
  2226. isset($PHORUM['args'][$cookie])) {
  2227. $value = urldecode($PHORUM['args'][$cookie]);
  2228. }
  2229. // Check for session id in form POST data.
  2230. elseif ($PHORUM['use_cookies'] < PHORUM_REQUIRE_COOKIES &&
  2231. isset($_POST[$cookie])) {
  2232. $value = $_POST[$cookie];
  2233. }
  2234. // Check for session id in form GET data (should rarely happen, but
  2235. // it helps sometimes).
  2236. elseif ($PHORUM['use_cookies'] < PHORUM_REQUIRE_COOKIES &&
  2237. isset($_GET[$cookie])) {
  2238. $value = $_GET[$cookie];
  2239. }
  2240. // Cookie not found. Continue with the next one.
  2241. else {
  2242. continue;
  2243. }
  2244. // Cookie incorrectly formatted. Continue with the next one.
  2245. if (strstr($value, ':') === FALSE) continue;
  2246. // The cookie value is formatted as <user id>:<session id>.
  2247. // Split these into separate parts.
  2248. list($user_id, $sessid) = explode(':', $value, 2);
  2249. // The user_id should be numerical at all times.
  2250. if (!is_numeric($user_id)) continue;
  2251. // Find the data for the session user by its user_id. If the user
  2252. // cannot be found, then the session is destroyed and the
  2253. // anonymous user is setup.
  2254. if ($session_user === NULL) {
  2255. $session_user = phorum_api_user_get($user_id, TRUE);
  2256. if (empty($session_user) ||
  2257. $session_user['active'] != PHORUM_USER_ACTIVE) {
  2258. phorum_api_user_session_destroy($type);
  2259. return FALSE;
  2260. }
  2261. } else {
  2262. // The user_id should be the same for all cookies.
  2263. // If a different user_id is found, then the cookie
  2264. // that we're currently looking at is ignored. It could
  2265. // be an old cookie for a different user.
  2266. if ($session_user['user_id'] != $user_id) {
  2267. continue;
  2268. }
  2269. }
  2270. // Check if the session id from the cookie is valid for the user.
  2271. $valid_session =
  2272. $user_id_from_hook_session ||
  2273. ($cookie == PHORUM_SESSION_LONG_TERM &&
  2274. !empty($session_user['sessid_lt']) &&
  2275. $session_user['sessid_lt'] == $sessid) ||
  2276. ($cookie == PHORUM_SESSION_SHORT_TERM &&
  2277. !empty($session_user['sessid_st']) &&
  2278. $session_user['sessid_st'] == $sessid) ||
  2279. ($cookie == PHORUM_SESSION_ADMIN &&
  2280. !empty($session_user['sessid_lt']) &&
  2281. md5($session_user['sessid_lt'].$PHORUM['admin_session_salt']) == $sessid);
  2282. // Keep track of valid session cookies.
  2283. if ($valid_session) {
  2284. $check_session[$cookie] = 2;
  2285. }
  2286. }
  2287. // No real cookie found for a long term session? Then we will ignore
  2288. // short term sessions (short term sessions are not implemented for URI
  2289. // authentication) and update the "use_cookies" setting accordingly.
  2290. if ($check_session[PHORUM_SESSION_LONG_TERM] == 2 && ! $real_cookie) {
  2291. $check_session[PHORUM_SESSION_SHORT_TERM] = 0;
  2292. $GLOBALS['PHORUM']['use_cookies'] = PHORUM_NO_COOKIES;
  2293. }
  2294. // ----------------------------------------------------------------------
  2295. // Check if a user session needs to be restored.
  2296. // ----------------------------------------------------------------------
  2297. $do_restore_session = FALSE;
  2298. $do_restore_short_term_session = FALSE;
  2299. if ($type == PHORUM_FORUM_SESSION)
  2300. {
  2301. // Valid long term forum session found.
  2302. if ($check_session[PHORUM_SESSION_LONG_TERM] == 2)
  2303. {
  2304. $do_restore_session = TRUE;
  2305. if ($check_session[PHORUM_SESSION_SHORT_TERM] == 1) {
  2306. // Checked short term session, but no valid session found.
  2307. $do_restore_short_term_session = FALSE;
  2308. } else {
  2309. // Short term session not checked (0) or valid (2).
  2310. $do_restore_short_term_session = TRUE;
  2311. }
  2312. }
  2313. }
  2314. elseif ($type == PHORUM_ADMIN_SESSION)
  2315. {
  2316. // Valid admin session found. Note that the function
  2317. // phorum_api_user_set_active_user() might still reject the user
  2318. // if it's not an admin user (anymore).
  2319. if ($check_session[PHORUM_SESSION_ADMIN] == 2) {
  2320. $do_restore_session = TRUE;
  2321. }
  2322. }
  2323. // ----------------------------------------------------------------------
  2324. // Restore the user session.
  2325. // ----------------------------------------------------------------------
  2326. // No session to restore? Then destroy the session
  2327. // and setup the anonymous user.
  2328. if (! $do_restore_session)
  2329. {
  2330. phorum_api_user_session_destroy($type);
  2331. return FALSE;
  2332. }
  2333. // Restore a user's session.
  2334. else
  2335. {
  2336. // Setup the Phorum user.
  2337. $flags = 0;
  2338. if ($do_restore_short_term_session) $flags |= PHORUM_FLAG_SESSION_ST;
  2339. phorum_api_user_set_active_user($type, $session_user, $flags);
  2340. // Refresh and keep the session alive for the user.
  2341. phorum_api_user_session_create($type);
  2342. return TRUE;
  2343. }
  2344. }
  2345. // }}}
  2346. // {{{ Function: phorum_api_user_session_destroy()
  2347. /**
  2348. * Destroy a Phorum user session.
  2349. *
  2350. * This will destroy a Phorum user session and set the active
  2351. * Phorum user to the anonymous user.
  2352. *
  2353. * @param string $type
  2354. * The type of session to destroy. This must be one of
  2355. * {@link PHORUM_FORUM_SESSION} or {@link PHORUM_ADMIN_SESSION}.
  2356. * See the documentation for {@link phorum_api_user_session_create()}
  2357. * for more information on Phorum user sessions.
  2358. */
  2359. function phorum_api_user_session_destroy($type)
  2360. {
  2361. $PHORUM = $GLOBALS['PHORUM'];
  2362. /**
  2363. * [hook]
  2364. * user_session_destroy
  2365. *
  2366. * [description]
  2367. * Allow modules to override Phorum's session destroy management or
  2368. * to even fully omit destroying a session (for example useful
  2369. * if the hook <hook>user_session_restore</hook> is used
  2370. * to inherit an external session from some 3rd party application).
  2371. *
  2372. * [category]
  2373. * User authentication and session handling
  2374. *
  2375. * [when]
  2376. * Just before Phorum runs its own session destroy code
  2377. * in the user API function
  2378. * <literal>phorum_api_user_session_destroy()</literal>.
  2379. *
  2380. * [input]
  2381. * The session type for which a session must be destroyed.
  2382. * This can be either <literal>PHORUM_FORUM_SESSION</literal>
  2383. * or <literal>PHORUM_ADMIN_SESSION</literal>.
  2384. *
  2385. * [output]
  2386. * Same as input if Phorum has to run its standard session
  2387. * destroy code or NULL if that code should be fully skipped.
  2388. *
  2389. * [example]
  2390. * See the <hook>user_session_create</hook> hook for an example
  2391. * of how to let Phorum setup the PHP session that is destroyed
  2392. * in this example hook.
  2393. * <hookcode>
  2394. * function phorum_mod_foo_user_session_destroy($type)
  2395. * {
  2396. * // Let Phorum handle destroying of admin sessions on its own.
  2397. * if ($type == PHORUM_ADMIN_SESSION) return $type;
  2398. *
  2399. * // Override the session handling for front end forum sessions.
  2400. * // We could for example have stored the session in a standard
  2401. * // PHP session. First, we start a PHP session if that was
  2402. * // not done yet.
  2403. * if (!session_id()) session_start();
  2404. *
  2405. * // After starting the PHP session, we can clear the session
  2406. * // data for the Phorum user. In the user_session_create hook
  2407. * // example code, we stored the user_id for the active user
  2408. * // in the session. Here we clear that data. We could also
  2409. * // have destroyed the full PHP session, but in that case we
  2410. * // would risk destroying session data that was setup by
  2411. * // other PHP scripts.
  2412. * unset($_SESSION['phorum_user_id']);
  2413. *
  2414. * // Tell Phorum not to run its own session destroy code.
  2415. * return NULL;
  2416. * }
  2417. * </hookcode>
  2418. */
  2419. $do_phorum_destroy_session = TRUE;
  2420. if (isset($PHORUM['hooks']['user_session_destroy'])) {
  2421. if (phorum_hook('user_session_destroy', $type) === NULL) {
  2422. $do_phorum_destroy_session = FALSE;
  2423. }
  2424. }
  2425. if ($do_phorum_destroy_session)
  2426. {
  2427. // Destroy session cookie(s). We do not care here if use_cookies is
  2428. // enabled or not. We just want to clean out all that we have here.
  2429. if ($type == PHORUM_FORUM_SESSION) {
  2430. setcookie(
  2431. PHORUM_SESSION_SHORT_TERM, '', time()-86400,
  2432. $PHORUM['session_path'], $PHORUM['session_domain']
  2433. );
  2434. setcookie(
  2435. PHORUM_SESSION_LONG_TERM, '', time()-86400,
  2436. $PHORUM['session_path'], $PHORUM['session_domain']
  2437. );
  2438. } elseif ($type == PHORUM_ADMIN_SESSION) {
  2439. setcookie(
  2440. PHORUM_SESSION_ADMIN, '', time()-86400,
  2441. $PHORUM['session_path'], $PHORUM['session_domain']
  2442. );
  2443. } else {
  2444. trigger_error(
  2445. 'phorum_api_user_session_destroy(): Illegal session type: ' .
  2446. htmlspecialchars($type),
  2447. E_USER_ERROR
  2448. );
  2449. return NULL;
  2450. }
  2451. // If cookies are not in use, then the long term session is reset
  2452. // to a new value. That way we fully invalidate URI authentication
  2453. // data, so that old URL's won't work anymore. We can only do this
  2454. // if we have an active Phorum user.
  2455. if ($PHORUM['use_cookies'] == PHORUM_NO_COOKIES &&
  2456. $type == PHORUM_FORUM_SESSION &&
  2457. !empty($PHORUM['user']) && !empty($PHORUM['user']['user_id'])) {
  2458. $user = $PHORUM['user'];
  2459. $sessid_lt = md5($user['username'].microtime().$user['password']);
  2460. phorum_api_user_save_raw(array(
  2461. 'user_id' => $user['user_id'],
  2462. 'sessid_lt' => $sessid_lt,
  2463. ));
  2464. }
  2465. }
  2466. // Force Phorum to see the anonymous user from here on.
  2467. phorum_api_user_set_active_user(PHORUM_FORUM_SESSION, NULL);
  2468. }
  2469. // }}}
  2470. // ----------------------------------------------------------------------
  2471. // Authorization management.
  2472. // ----------------------------------------------------------------------
  2473. // {{{ Function: phorum_api_user_get_groups()
  2474. /**
  2475. * Retrieve the groups and their subscription statuses for a user.
  2476. *
  2477. * @param integer $user_id
  2478. * The user_id of the user for which to retrieve the groups.
  2479. *
  2480. * @return array
  2481. * An array of groups for the user. The keys are the group ids.
  2482. * The values are group information arrays.
  2483. */
  2484. function phorum_api_user_get_groups($user_id)
  2485. {
  2486. // Retrieve information for all the groups for the user.
  2487. $groups = phorum_api_user_check_group_access(
  2488. PHORUM_USER_GROUP_SUSPENDED,
  2489. PHORUM_ACCESS_LIST,
  2490. $user_id
  2491. );
  2492. return $groups;
  2493. }
  2494. // }}}
  2495. // {{{ Function: phorum_api_user_save_groups()
  2496. /**
  2497. * Save the groups and group permissions for a user.
  2498. *
  2499. * @param integer $user_id
  2500. * The user_id of the user for which to store the group permissions.
  2501. *
  2502. * @param array $groups
  2503. * An array of groups and their permissions. The keys in this array are
  2504. * group ids. The values are either group permission values or arrays
  2505. * containing at least the key "user_status" (which has the group
  2506. * permission as its value) in them. The group permission value must be
  2507. * one of the PHORUM_USER_GROUP_* constants.
  2508. */
  2509. function phorum_api_user_save_groups($user_id, $groups)
  2510. {
  2511. if (!empty($GLOBALS["PHORUM"]['cache_users'])) {
  2512. phorum_cache_remove('user', $user_id);
  2513. }
  2514. $dbgroups = array();
  2515. foreach ($groups as $id => $perm)
  2516. {
  2517. if (is_array($perm) && isset($perm['user_status'])) {
  2518. $perm = $perm['user_status'];
  2519. }
  2520. if ($perm != PHORUM_USER_GROUP_SUSPENDED &&
  2521. $perm != PHORUM_USER_GROUP_UNAPPROVED &&
  2522. $perm != PHORUM_USER_GROUP_APPROVED &&
  2523. $perm != PHORUM_USER_GROUP_MODERATOR) {
  2524. trigger_error(
  2525. 'phorum_api_user_save_groups(): Illegal group permission for ' .
  2526. 'group id '.htmlspecialchars($id).': '.htmlspecialchars($perm),
  2527. E_USER_ERROR
  2528. );
  2529. return NULL;
  2530. }
  2531. $dbgroups[$id] = $perm;
  2532. }
  2533. /**
  2534. * [hook]
  2535. * user_save_groups
  2536. *
  2537. * [description]
  2538. * This hook can be used to handle the groups data that is going to be
  2539. * stored in the database for a user. Modules can do some last
  2540. * minute change on the data or keep some external system in sync
  2541. * with the Phorum user data.
  2542. *
  2543. * [category]
  2544. * User data handling
  2545. *
  2546. * [when]
  2547. * Just before the groups for a user are stored in the database.
  2548. *
  2549. * [input]
  2550. * An array containing user_id and groups-data as another array.
  2551. *
  2552. * [output]
  2553. * The same array as the one that was used for the hook call
  2554. * argument, possibly with some updated fields in it.
  2555. *
  2556. * [example]
  2557. * <hookcode>
  2558. * function phorum_mod_foo_user_save_groups($data)
  2559. * {
  2560. * list($user_id,$groups) = $data;
  2561. * foreach($groups as $group_id => $group_permission) {
  2562. * // do something with the groups permissions
  2563. * }
  2564. *
  2565. * return array($user_id,$groups);
  2566. * }
  2567. * </hookcode>
  2568. */
  2569. if (isset($GLOBALS['PHORUM']['hooks']['user_save_groups'])) {
  2570. list($user_id,$dbgroups) = phorum_hook('user_save_groups', array($user_id,$dbgroups));
  2571. }
  2572. return phorum_db_user_save_groups($user_id, $dbgroups);
  2573. }
  2574. // }}}
  2575. // {{{ Function: phorum_api_user_check_access()
  2576. /**
  2577. * Check if a user has certain access right for forum(s).
  2578. *
  2579. * @param integer $permission
  2580. * The permission to check for. Multiple permissions can be OR-ed
  2581. * together. The available permissions are:
  2582. * - {@link PHORUM_USER_ALLOW_READ}
  2583. * - {@link PHORUM_USER_ALLOW_REPLY}
  2584. * - {@link PHORUM_USER_ALLOW_EDIT}
  2585. * - {@link PHORUM_USER_ALLOW_NEW_TOPIC}
  2586. * - {@link PHORUM_USER_ALLOW_ATTACH}
  2587. * - {@link PHORUM_USER_ALLOW_MODERATE_MESSAGES}
  2588. * - {@link PHORUM_USER_ALLOW_MODERATE_USERS}
  2589. *
  2590. * @param mixed $forum_id
  2591. * Specifies the forum(s) to look at. Available options are:
  2592. * - The id of the forum for which to check the access
  2593. * - 0 (zero, the default) to check access for the active forum
  2594. * - An array of forum_ids to check
  2595. * - {@link PHORUM_ACCESS_ANY} to check if the user has access rights
  2596. * for any of the available forums
  2597. * - {@link PHORUM_ACCESS_LIST} to return a list of forum_ids for which the
  2598. * user has access rights
  2599. *
  2600. * @param mixed $user
  2601. * Specifies the user to look at. Available options are:
  2602. * - 0 (zero, the default) to look at the active Phorum user.
  2603. * - A full user data array.
  2604. * - A single user_id.
  2605. *
  2606. * @return mixed
  2607. * The return value depends on the $forum_id argument that was used:
  2608. *
  2609. * - Single forum_id , 0 (zero) or {@link PHORUM_ACCESS_ANY}:
  2610. * return either TRUE (access granted) or FALSE (access denied).
  2611. *
  2612. * - An array of forum_ids or {@link PHORUM_ACCESS_LIST}:
  2613. * return an array, containing all forum_ids for which
  2614. * permission was granted (both keys and values are forum_ids
  2615. * in this array).
  2616. */
  2617. function phorum_api_user_check_access($permission, $forum_id = 0, $user = 0)
  2618. {
  2619. $PHORUM = $GLOBALS['PHORUM'];
  2620. // Prepare the array of forum ids to check.
  2621. $forum_access = array();
  2622. $forums = NULL;
  2623. $single_forum_id = NULL;
  2624. // An array of forum ids.
  2625. if (is_array($forum_id)) {
  2626. foreach ($forum_id as $id) $forum_access[$id] = FALSE;
  2627. // Forum id 0 (zero).
  2628. } elseif (empty($forum_id)) {
  2629. $single_forum_id = $PHORUM['forum_id'];
  2630. $forum_access[$PHORUM['forum_id']] = FALSE;
  2631. $forums = array(
  2632. $PHORUM['forum_id'] => array(
  2633. 'reg_perms' => $PHORUM['reg_perms'],
  2634. 'pub_perms' => $PHORUM['pub_perms']
  2635. )
  2636. );
  2637. // Retrieve a forum access list or access-rights-in-any-forum.
  2638. } elseif ($forum_id == PHORUM_ACCESS_LIST ||
  2639. $forum_id == PHORUM_ACCESS_ANY) {
  2640. $forums = phorum_db_get_forums(0, NULL, $PHORUM['vroot']);
  2641. foreach ($forums as $id => $data) $forum_access[$id] = FALSE;
  2642. // A single forum id.
  2643. } else {
  2644. $single_forum_id = $forum_id;
  2645. $forum_access[$forum_id] = FALSE;
  2646. }
  2647. // Prepare the user to check the access for.
  2648. if (empty($user)) {
  2649. $user = $PHORUM['user'];
  2650. } elseif (!is_array($user)) {
  2651. $user = phorum_api_user_get($user);
  2652. }
  2653. // Inactive users have no permissions at all.
  2654. if (!empty($user['user_id']) && empty($user['active']))
  2655. {
  2656. if ($forum_id == PHORUM_ACCESS_ANY) return FALSE;
  2657. // No further code required. We'll just keep all forum
  2658. // permissions set to FALSE here.
  2659. }
  2660. // Administrators always have permission.
  2661. elseif (!empty($user['user_id']) && !empty($user['admin']))
  2662. {
  2663. if ($forum_id == PHORUM_ACCESS_ANY) return TRUE;
  2664. foreach ($forum_access as $id => $data) {
  2665. $forum_access[$id] = TRUE;
  2666. }
  2667. }
  2668. // For other users, we have to do a full permission lookup.
  2669. else
  2670. {
  2671. // Fetch data for the forums, unless we already have that
  2672. // data available.
  2673. if ($forums === NULL) {
  2674. $forums = phorum_db_get_forums(array_keys($forum_access));
  2675. }
  2676. // Check the access rights for each forum.
  2677. foreach ($forum_access as $id => $data)
  2678. {
  2679. // Access to folders is always granted.
  2680. if (!empty($forums[$id]['folder_flag'])) {
  2681. $forum_access[$id] = TRUE;
  2682. continue;
  2683. }
  2684. $perm = NULL;
  2685. // Authenticated user with specific access rights.
  2686. if (!empty($user['user_id']) &&
  2687. isset($user['permissions'][$id])) {
  2688. $perm = $user['permissions'][$id];
  2689. }
  2690. // User for which to use the forum permissions.
  2691. else {
  2692. $key = empty($user['user_id']) ? 'pub_perms' : 'reg_perms';
  2693. if (isset($forums[$id][$key])) {
  2694. $perm = $forums[$id][$key];
  2695. }
  2696. }
  2697. // Check if the user has the requested permission for the forum.
  2698. if (!empty($perm) && ($perm & $permission) == $permission)
  2699. {
  2700. if ($forum_id == PHORUM_ACCESS_ANY) {
  2701. return TRUE;
  2702. } else {
  2703. $forum_access[$id] = TRUE;
  2704. }
  2705. }
  2706. }
  2707. }
  2708. // If we reach this code, then we did not find any forum for the user.
  2709. if ($forum_id == PHORUM_ACCESS_ANY) return FALSE;
  2710. // Return the results.
  2711. if ($single_forum_id !== NULL) {
  2712. // Return either TRUE or FALSE.
  2713. return empty($forum_access[$single_forum_id]) ? FALSE : TRUE;
  2714. } else {
  2715. // Return an array of forums for which permission is granted.
  2716. // Both the keys and values are the forum ids.
  2717. $return = array();
  2718. foreach ($forum_access as $id => $has_permission) {
  2719. if ($has_permission) $return[$id] = $id;
  2720. }
  2721. return $return;
  2722. }
  2723. }
  2724. // }}}
  2725. // {{{ Function: phorum_api_user_check_group_access()
  2726. /**
  2727. * @param integer $permission
  2728. * The permission level to check for. The function will check if the
  2729. * user has equal or higher permissions for the group(s). The available
  2730. * permission levels in low-to-high level order are:
  2731. * - {@link PHORUM_USER_GROUP_SUSPENDED}
  2732. * - {@link PHORUM_USER_GROUP_UNAPPROVED}
  2733. * - {@link PHORUM_USER_GROUP_APPROVED}
  2734. * - {@link PHORUM_USER_GROUP_MODERATOR}
  2735. *
  2736. * @param mixed $group_id
  2737. * Specifies the group(s) to look at. Available options are:
  2738. * - The id of the group for which to check the access.
  2739. * - An array of group_ids to check.
  2740. * - {@link PHORUM_ACCESS_ANY} to check if the user has access rights
  2741. * for any of the available groups.
  2742. * - {@link PHORUM_ACCESS_LIST} to return a list of group_ids for which the
  2743. * user has access rights.
  2744. *
  2745. * @param mixed $user
  2746. * Specifies the user to look at. Available options are:
  2747. * - 0 (zero, the default) to look at the active Phorum user.
  2748. * - A full user data array.
  2749. * - A single user_id.
  2750. *
  2751. * @return mixed
  2752. * The return value depends on the $group_id argument that was used:
  2753. *
  2754. * - Single group_id or {@link PHORUM_ACCESS_ANY}:
  2755. * return either TRUE (access granted) or FALSE (access denied).
  2756. *
  2757. * - An array of group_ids or {@link PHORUM_ACCESS_LIST}:
  2758. * return an array, containing all groups for which permission was
  2759. * granted. The keys in this array are group_ids and the values are
  2760. * group info arrays. These arrays contain the fields "group_id",
  2761. * "name", "open", "permissions" (which contains an array of
  2762. * forum permissions, indexed by forum_id), "user_status" (which contains
  2763. * the group status for the user, i.e. one of the PHORUM_USER_GROUP_*
  2764. * constants).
  2765. */
  2766. function phorum_api_user_check_group_access($permission, $group_id, $user = 0)
  2767. {
  2768. $PHORUM = $GLOBALS['PHORUM'];
  2769. // Prepare the user to check the access for.
  2770. if (empty($user)) {
  2771. $user = $PHORUM['user'];
  2772. } elseif (!is_array($user)) {
  2773. $user = phorum_api_user_get($user);
  2774. }
  2775. // Retrieve all the groups for the current user. Admins get all groups.
  2776. if (!empty($user['user_id']) && !empty($user['admin'])) {
  2777. $groups = phorum_db_get_groups(0, TRUE);
  2778. } else {
  2779. $usergroups = phorum_db_user_get_groups($user['user_id']);
  2780. $groups = empty($usergroups)
  2781. ? array()
  2782. : phorum_db_get_groups(array_keys($usergroups), TRUE);
  2783. }
  2784. // Prepare the array of group_ids to check.
  2785. $group_access = array();
  2786. $single_group_id = NULL;
  2787. // An array of group ids.
  2788. if (is_array($group_id)) {
  2789. foreach ($group_id as $id) $group_access[$id] = FALSE;
  2790. // Retrieve a group access list or access-rights-in-any-group.
  2791. } elseif ($group_id == PHORUM_ACCESS_LIST ||
  2792. $group_id == PHORUM_ACCESS_ANY) {
  2793. foreach ($groups as $id => $data) $group_access[$id] = FALSE;
  2794. // A single group id.
  2795. } else {
  2796. $single_group_id = $group_id;
  2797. $group_access[$group_id] = FALSE;
  2798. }
  2799. // Inactive users have no group permissions at all.
  2800. if (!empty($user['user_id']) && empty($user['active']))
  2801. {
  2802. if ($group_id == PHORUM_ACCESS_ANY) return FALSE;
  2803. // No further code required. We'll just keep all group
  2804. // permissions set to FALSE here.
  2805. }
  2806. // Administrators always have full group permission. This will include
  2807. // restrictive access groups for the administrator as well. That is no
  2808. // problem, because the admin status for the user will override any
  2809. // group permission restriction.
  2810. elseif (!empty($user['user_id']) && !empty($user['admin']))
  2811. {
  2812. if ($group_id == PHORUM_ACCESS_ANY) return TRUE;
  2813. foreach ($group_access as $id => $data) {
  2814. $group_access[$id] = $groups[$id];
  2815. $group_access[$id]['user_status'] = PHORUM_USER_GROUP_MODERATOR;
  2816. }
  2817. }
  2818. // For other users, we have to do a full permission lookup.
  2819. else
  2820. {
  2821. foreach ($group_access as $id => $data)
  2822. {
  2823. if (!isset($groups[$id])) continue;
  2824. if ($usergroups[$id] >= $permission)
  2825. {
  2826. if ($group_id == PHORUM_ACCESS_ANY) return TRUE;
  2827. $group_access[$id] = $groups[$id];
  2828. $group_access[$id]['user_status'] = $usergroups[$id];
  2829. continue;
  2830. }
  2831. }
  2832. }
  2833. // If we reach this code, then we did not find any group for the user.
  2834. if ($group_id == PHORUM_ACCESS_ANY) return FALSE;
  2835. // Return the results.
  2836. if ($single_group_id !== NULL) {
  2837. // Return either TRUE or FALSE.
  2838. return empty($group_access[$single_group_id]) ? FALSE : TRUE;
  2839. } else {
  2840. // Return an array of groups for which permission is granted.
  2841. // The keys are group_ids and the values the user's permissions
  2842. // for the groups.
  2843. $return = array();
  2844. foreach ($group_access as $id => $group) {
  2845. if ($group !== FALSE) $return[$id] = $group;
  2846. }
  2847. return $return;
  2848. }
  2849. }
  2850. // }}}
  2851. // {{{ Function: phorum_api_user_list_moderators()
  2852. /**
  2853. * Retrieve a list of moderators.
  2854. *
  2855. * @param integer $forum_id
  2856. * The id of the forum for which to retrieve a list of moderators or
  2857. * 0 (zero, the default) to use the active forum.
  2858. *
  2859. * @param boolean $exclude_admin
  2860. * If TRUE, then the admin users are kept out of the list. The default
  2861. * is to include admin users.
  2862. *
  2863. * @param boolean $for_mail
  2864. * If TRUE, then a list of moderators is created for sending moderator
  2865. * mail messages. Moderators which have disabled the moderation_email
  2866. * option will be excluded from the list in this case. The default
  2867. * is to include all moderators.
  2868. *
  2869. * @return array
  2870. * An array of moderator users, indexed by user_id.
  2871. */
  2872. function phorum_api_user_list_moderators($forum_id = 0, $exclude_admin = FALSE, $for_mail = FALSE)
  2873. {
  2874. $PHORUM = $GLOBALS['PHORUM'];
  2875. if (empty($forum_id)) $forum_id = $PHORUM['forum_id'];
  2876. return phorum_db_user_get_moderators($forum_id, $exclude_admin, $for_mail);
  2877. }
  2878. // }}}
  2879. // ----------------------------------------------------------------------
  2880. // Subscription management.
  2881. // ----------------------------------------------------------------------
  2882. // {{{ Function: phorum_api_user_subscribe()
  2883. /**
  2884. * Subscribe a user to a thread.
  2885. *
  2886. * Remark: Currently, there is no active support for subscribing to forums
  2887. * using subscription type PHORUM_SUBSCRIPTION_DIGEST in the Phorum core.
  2888. *
  2889. * @param integer $user_id
  2890. * The id of the user to create the subscription for.
  2891. *
  2892. * @param integer $thread
  2893. * The id of the thread to describe to.
  2894. *
  2895. * @param integer $forum_id
  2896. * The id of the forum in which the thread to subscribe to resides.
  2897. *
  2898. * @param integer $type
  2899. * The type of subscription. Available types are:
  2900. * - {@link PHORUM_SUBSCRIPTION_NONE}
  2901. * - {@link PHORUM_SUBSCRIPTION_MESSAGE}
  2902. * - {@link PHORUM_SUBSCRIPTION_BOOKMARK}
  2903. * - {@link PHORUM_SUBSCRIPTION_DIGEST}
  2904. */
  2905. function phorum_api_user_subscribe($user_id, $thread, $forum_id, $type)
  2906. {
  2907. // Check if the user is allowed to read the forum.
  2908. if (! phorum_api_user_check_access(PHORUM_USER_ALLOW_READ, $forum_id)) {
  2909. return;
  2910. }
  2911. // Setup the subscription.
  2912. phorum_db_user_subscribe($user_id, $thread, $forum_id, $type);
  2913. }
  2914. // }}}
  2915. // {{{ Function: phorum_api_user_unsubscribe()
  2916. /**
  2917. * Unsubscribe a user from a thread.
  2918. *
  2919. * @param integer $user_id
  2920. * The id of the user to remove the subscription for.
  2921. *
  2922. * @param integer $thread
  2923. * The id of the thread to describe from.
  2924. *
  2925. * @param integer $forum_id
  2926. * The id of the forum in which the thread to unsubscribe from resides.
  2927. * This parameter can be 0 (zero) to simply unsubscribe by thread id alone.
  2928. */
  2929. function phorum_api_user_unsubscribe($user_id, $thread, $forum_id = 0)
  2930. {
  2931. // Remove the subscription.
  2932. phorum_db_user_unsubscribe($user_id, $thread, $forum_id);
  2933. }
  2934. // }}}
  2935. // {{{ Function: phorum_api_user_get_subscription()
  2936. /**
  2937. * Retrieve the type of a single subscription.
  2938. *
  2939. * @param integer $user_id
  2940. * The id of the user to retrieve a subscription for.
  2941. *
  2942. * @param integer $thread
  2943. * The id of the thread to retrieve a subscription for.
  2944. *
  2945. * @param integer $forum_id
  2946. * The id of the forum to retrieve a subscription for.
  2947. *
  2948. * @return mixed
  2949. * The type of subscription if there is a subscription available or
  2950. * NULL in case no subscription was found. Available types are:
  2951. * - {@link PHORUM_SUBSCRIPTION_NONE}
  2952. * - {@link PHORUM_SUBSCRIPTION_MESSAGE}
  2953. * - {@link PHORUM_SUBSCRIPTION_BOOKMARK}
  2954. * - {@link PHORUM_SUBSCRIPTION_DIGEST}
  2955. */
  2956. function phorum_api_user_get_subscription($user_id, $forum_id, $thread)
  2957. {
  2958. return phorum_db_user_get_subscription($user_id, $forum_id, $thread);
  2959. }
  2960. // }}}
  2961. // {{{ Function: phorum_api_user_list_subscriptions()
  2962. /**
  2963. * Retrieve a list of threads to which a user is subscribed. The list can be
  2964. * limited to those threads which did receive contributions recently.
  2965. *
  2966. * @param integer $user_id
  2967. * The id of the user for which to retrieve the subscribed threads.
  2968. *
  2969. * @param integer $days
  2970. * If set to 0 (zero), then all subscriptions will be returned. If set to
  2971. * a different value, then only threads which have received contributions
  2972. * within the last $days days will be included in the list.
  2973. *
  2974. * @param integer $forum_ids
  2975. * If this parameter is NULL, then subscriptions from all forums will
  2976. * be included. This parameter can also be an array of forum_ids, in
  2977. * which case the search will be limited to the forums in this array.
  2978. *
  2979. * @return array $threads
  2980. * An array of matching threads, indexed by thread id. One special key
  2981. * "forum_ids" is set too. This one contains an array of all involved
  2982. * forum_ids.
  2983. */
  2984. function phorum_api_user_list_subscriptions($user_id, $days=0, $forum_ids=NULL)
  2985. {
  2986. return phorum_db_user_list_subscriptions($user_id, $days, $forum_ids);
  2987. }
  2988. // }}}
  2989. // {{{ Function: phorum_api_user_list_subscribers()
  2990. /**
  2991. * Retrieve the email addresses of the users that are subscribed to a
  2992. * forum/thread, grouped by the preferred language for these users.
  2993. *
  2994. * @param integer $forum_id
  2995. * The forum_id to check on.
  2996. *
  2997. * @param integer $thread
  2998. * The thread id to check on.
  2999. *
  3000. * @param integer $type
  3001. * The type of subscription. Available types are:
  3002. * - {@link PHORUM_SUBSCRIPTION_MESSAGE}
  3003. * - {@link PHORUM_SUBSCRIPTION_BOOKMARK}
  3004. * - {@link PHORUM_SUBSCRIPTION_DIGEST}
  3005. *
  3006. * @param boolean $ignore_active_user
  3007. * If this parameter is set to FALSE (it is TRUE by default), then the
  3008. * active Phorum user will be excluded from the list.
  3009. *
  3010. * @return array $addresses
  3011. * An array containing the subscriber email addresses. The keys in the
  3012. * result array are language names. The values are arrays. Each array
  3013. * contains a list of email addresses of users which are using the
  3014. * language from the key field.
  3015. */
  3016. function phorum_api_user_list_subscribers($forum_id, $thread, $type, $ignore_active_user = TRUE)
  3017. {
  3018. return phorum_db_user_list_subscribers($forum_id, $thread, $type, $ignore_active_user);
  3019. }
  3020. // }}}
  3021. ?>