Spike PHPCoverage Details: user.module

Line #FrequencySource Line
1 <?php
2 // $Id: user.module,v 1.898 2008/03/31 20:50:05 dries Exp $
3 
4 /**
5  * @file
6  * Enables the user registration and login system.
7  */
8 
9 define('USERNAME_MAX_LENGTH', 60);
10 define('EMAIL_MAX_LENGTH', 64);
11 
12 /**
13  * Invokes hook_user() in every module.
14  *
15  * We cannot use module_invoke() for this, because the arguments need to
16  * be passed by reference.
17  */
18 function user_module_invoke($type, &$array, &$user, $category = NULL) {
191  foreach (module_list() as $module) {
201    $function = $module .'_user';
211    if (function_exists($function)) {
221      $function($type, $array, $user, $category);
23     }
24   }
25 }
26 
27 /**
28  * Implementation of hook_theme().
29  */
30 function user_theme() {
31   return array(
32     'user_picture' => array(
33       'arguments' => array('account' => NULL),
34       'template' => 'user-picture',
35     ),
36     'user_profile' => array(
37       'arguments' => array('account' => NULL),
38       'template' => 'user-profile',
39       'file' => 'user.pages.inc',
40     ),
41     'user_profile_category' => array(
42       'arguments' => array('element' => NULL),
43       'template' => 'user-profile-category',
44       'file' => 'user.pages.inc',
45     ),
46     'user_profile_item' => array(
47       'arguments' => array('element' => NULL),
48       'template' => 'user-profile-item',
49       'file' => 'user.pages.inc',
50     ),
51     'user_list' => array(
52       'arguments' => array('users' => NULL, 'title' => NULL),
53     ),
54     'user_admin_perm' => array(
55       'arguments' => array('form' => NULL),
56       'file' => 'user.admin.inc',
57     ),
58     'user_admin_new_role' => array(
59       'arguments' => array('form' => NULL),
60       'file' => 'user.admin.inc',
61     ),
62     'user_admin_account' => array(
63       'arguments' => array('form' => NULL),
64       'file' => 'user.admin.inc',
65     ),
66     'user_filter_form' => array(
67       'arguments' => array('form' => NULL),
68       'file' => 'user.admin.inc',
69     ),
70     'user_filters' => array(
71       'arguments' => array('form' => NULL),
72       'file' => 'user.admin.inc',
73     ),
74     'user_signature' => array(
75       'arguments' => array('signature' => NULL),
76     ),
77   );
78 }
79 
80 function user_external_load($authname) {
81   $result = db_query("SELECT uid FROM {authmap} WHERE authname = '%s'", $authname);
82 
83   if ($user = db_fetch_array($result)) {
84     return user_load($user);
85   }
86   else {
87     return 0;
88   }
89 }
90 
91 /**
92  * Perform standard Drupal login operations for a user object.
93  *
94  * The user object must already be authenticated. This function verifies
95  * that the user account is not blocked/denied and then performs the login,
96  * updates the login timestamp in the database, invokes hook_user('login'),
97  * and regenerates the session.
98  *
99  * @param $account
100  *    An authenticated user object to be set as the currently logged
101  *    in user.
102  * @param $edit
103  *    The array of form values submitted by the user, if any.
104  *    This array is passed to hook_user op login.
105  * @return boolean
106  *    TRUE if the login succeeds, FALSE otherwise.
107  */
108 function user_external_login($account, $edit = array()) {
109   $form = drupal_get_form('user_login');
110 
111   $state['values'] = $edit;
112   if (empty($state['values']['name'])) {
113     $state['values']['name'] = $account->name;
114   }
115 
116   // Check if user is blocked or denied by access rules.
117   user_login_name_validate($form, $state, (array)$account);
118   if (form_get_errors()) {
119     // Invalid login.
120     return FALSE;
121   }
122 
123   // Valid login.
124   global $user;
125   $user = $account;
126   user_authenticate_finalize($state['values']);
127   return TRUE;
128 }
129 
130 /**
131  * Fetch a user object.
132  *
133  * @param $array
134  *   An associative array of attributes to search for in selecting the
135  *   user, such as user name or e-mail address.
136  *
137  * @return
138  *   A fully-loaded $user object upon successful user load or FALSE if user
139  *   cannot be loaded.
140  */
141 function user_load($array = array()) {
142   // Dynamically compose a SQL query:
143   $query = array();
144   $params = array();
145 
1461  if (is_numeric($array)) {
147     $array = array('uid' => $array);
148   }
1491  elseif (!is_array($array)) {
150     return FALSE;
151   }
152 
1531  foreach ($array as $key => $value) {
1541    if ($key == 'uid' || $key == 'status') {
1551      $query[] = "$key = %d";
1561      $params[] = $value;
157     }
158     else if ($key == 'pass') {
159       $query[] = "pass = '%s'";
160       $params[] = $value;
161     }
162     else {
163       $query[]= "LOWER($key) = LOWER('%s')";
164       $params[] = $value;
165     }
166   }
1671  $result = db_query('SELECT * FROM {users} u WHERE '. implode(' AND ', $query), $params);
168 
1691  if ($user = db_fetch_object($result)) {
1701    $user = drupal_unpack($user);
171 
1721    $user->roles = array();
1731    if ($user->uid) {
1741      $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
175     }
176     else {
177       $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
178     }
1791    $result = db_query('SELECT r.rid, r.name FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid = %d', $user->uid);
1801    while ($role = db_fetch_object($result)) {
1811      $user->roles[$role->rid] = $role->name;
182     }
1831    user_module_invoke('load', $array, $user);
184   }
185   else {
186     $user = FALSE;
187   }
188 
1891  return $user;
190 }
191 
192 /**
193  * Save changes to a user account or add a new user.
194  *
195  * @param $account
196  *   The $user object for the user to modify or add. If $user->uid is
197  *   omitted, a new user will be added.
198  *
199  * @param $array
200  *   An array of fields and values to save. For example array('name'
201  *   => 'My name').  Keys that do not belong to columns in the user-related
202  *   tables are added to the a serialized array in the 'data' column
203  *   and will be loaded in the $user->data array by user_load().
204  *   Setting a field to NULL deletes it from the data column.
205  *
206  * @param $category
207  *   (optional) The category for storing profile information in.
208  *
209  * @return
210  *   A fully-loaded $user object upon successful save or FALSE if the save failed.
211  */
212 function user_save($account, $array = array(), $category = 'account') {
2131  $table = drupal_get_schema('users');
2141  $user_fields = $table['fields'];
215 
2161  if (!empty($array['pass'])) {
217     // Allow alternate password hashing schemes.
218     require_once variable_get('password_inc', './includes/password.inc');
2191    $array['pass'] = user_hash_password(trim($array['pass']));
220     // Abort if the hashing failed and returned FALSE.
2211    if (!$array['pass']) {
222       return FALSE;
223     }
224   }
225   else {
226     // Avoid overwriting an existing password with a blank password.
227     unset($array['pass']);
228   }
229 
2301  if (is_object($account) && $account->uid) {
231     user_module_invoke('update', $array, $account, $category);
232     $data = unserialize(db_result(db_query('SELECT data FROM {users} WHERE uid = %d', $account->uid)));
233     // Consider users edited by an administrator as logged in, if they haven't
234     // already, so anonymous users can view the profile (if allowed).
235     if (empty($array['access']) && empty($account->access) && user_access('administer users')) {
236       $array['access'] = time();
237     }
238     foreach ($array as $key => $value) {
239       // Fields that don't pertain to the users or user_roles
240       // automatically serialized into the users.data column.
241       if ($key != 'roles' && empty($user_fields[$key])) {
242         if ($value === NULL) {
243           unset($data[$key]);
244         }
245         else {
246           $data[$key] = $value;
247         }
248       }
249     }
250 
251     $array['data'] = $data;
252     $array['uid'] = $account->uid;
253     // Save changes to the users table.
254     $success = drupal_write_record('users', $array, 'uid');
255     if (!$success) {
256       // The query failed - better to abort the save than risk further data loss.
257       return FALSE;
258     }
259 
260     // Reload user roles if provided.
261     if (isset($array['roles']) && is_array($array['roles'])) {
262       db_query('DELETE FROM {users_roles} WHERE uid = %d', $account->uid);
263 
264       foreach (array_keys($array['roles']) as $rid) {
265         if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
266           db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $account->uid, $rid);
267         }
268       }
269     }
270 
271     // Delete a blocked user's sessions to kick them if they are online.
272     if (isset($array['status']) && $array['status'] == 0) {
273       sess_destroy_uid($account->uid);
274     }
275 
276     // If the password changed, delete all open sessions and recreate
277     // the current one.
278     if (!empty($array['pass'])) {
279       sess_destroy_uid($account->uid);
280       sess_regenerate();
281     }
282 
283     // Refresh user object.
284     $user = user_load(array('uid' => $account->uid));
285 
286     // Send emails after we have the new user object.
287     if (isset($array['status']) && $array['status'] != $account->status) {
288       // The user's status is changing; conditionally send notification email.
289       $op = $array['status'] == 1 ? 'status_activated' : 'status_blocked';
290       _user_mail_notify($op, $user);
291     }
292 
293     user_module_invoke('after_update', $array, $user, $category);
294   }
295   else {
296     // Allow 'created' to be set by the caller.
2971    if (!isset($array['created'])) {
2981      $array['created'] = time();
299     }
300     // Consider users created by an administrator as already logged in, so
301     // anonymous users can view the profile (if allowed).
3021    if (empty($array['access']) && user_access('administer users')) {
3031      $array['access'] = time();
304     }
305 
3061    $success = drupal_write_record('users', $array);
3071    if (!$success) {
308       // On a failed INSERT some other existing user's uid may be returned.
309       // We must abort to avoid overwriting their account.
310       return FALSE;
311     }
312 
313     // Build the initial user object.
3141    $user = user_load(array('uid' => $array['uid']));
315 
3161    user_module_invoke('insert', $array, $user, $category);
317 
318     // Note, we wait with saving the data column to prevent module-handled
319     // fields from being saved there.
320     $data = array();
3211    foreach ($array as $key => $value) {
3221      if (($key != 'roles') && (empty($user_fields[$key])) && ($value !== NULL)) {
323         $data[$key] = $value;
324       }
325     }
3261    if (!empty($data)) {
327       $data_array = array('uid' => $user->uid, 'data' => $data);
328       drupal_write_record('users', $data_array, 'uid');
329     }
330 
331     // Save user roles (delete just to be safe).
3321    if (isset($array['roles']) && is_array($array['roles'])) {
3331      db_query('DELETE FROM {users_roles} WHERE uid = %d', $array['uid']);
3341      foreach (array_keys($array['roles']) as $rid) {
3351        if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
3361          db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $array['uid'], $rid);
337         }
338       }
339     }
340 
341     // Build the finished user object.
3421    $user = user_load(array('uid' => $array['uid']));
343   }
344 
3451  return $user;
346 }
347 
348 /**
349  * Verify the syntax of the given name.
350  */
351 function user_validate_name($name) {
352   if (!strlen($name)) return t('You must enter a username.');
353   if (substr($name, 0, 1) == ' ') return t('The username cannot begin with a space.');
354   if (substr($name, -1) == ' ') return t('The username cannot end with a space.');
355   if (strpos($name, '  ') !== FALSE) return t('The username cannot contain multiple spaces in a row.');
356   if (ereg("[^\x80-\xF7 [:alnum:]@_.-]", $name)) return t('The username contains an illegal character.');
357   if (preg_match('/[\x{80}-\x{A0}'.          // Non-printable ISO-8859-1 + NBSP
358                    '\x{AD}'.                 // Soft-hyphen
359                    '\x{2000}-\x{200F}'.      // Various space characters
360                    '\x{2028}-\x{202F}'.      // Bidirectional text overrides
361                    '\x{205F}-\x{206F}'.      // Various text hinting characters
362                    '\x{FEFF}'.               // Byte order mark
363                    '\x{FF01}-\x{FF60}'.      // Full-width latin
364                    '\x{FFF9}-\x{FFFD}'.      // Replacement characters
365                    '\x{0}]/u',               // NULL byte
366                    $name)) {
367     return t('The username contains an illegal character.');
368   }
369   if (strpos($name, '@') !== FALSE && !eregi('@([0-9a-z](-?[0-9a-z])*.)+[a-z]{2}([zmuvtg]|fo|me)?$', $name)) return t('The username is not a valid authentication ID.');
370   if (strlen($name) > USERNAME_MAX_LENGTH) return t('The username %name is too long: it must be %max characters or less.', array('%name' => $name, '%max' => USERNAME_MAX_LENGTH));
371 }
372 
373 function user_validate_mail($mail) {
374   if (!$mail) return t('You must enter an e-mail address.');
375   if (!valid_email_address($mail)) {
376     return t('The e-mail address %mail is not valid.', array('%mail' => $mail));
377   }
378 }
379 
380 function user_validate_picture(&$form, &$form_state) {
381   // If required, validate the uploaded picture.
382   $validators = array(
383     'file_validate_is_image' => array(),
384     'file_validate_image_resolution' => array(variable_get('user_picture_dimensions', '85x85')),
385     'file_validate_size' => array(variable_get('user_picture_file_size', '30') * 1024),
386   );
387   if ($file = file_save_upload('picture_upload', $validators)) {
388     // Remove the old picture.
389     if (isset($form_state['values']['_account']->picture) && file_exists($form_state['values']['_account']->picture)) {
390       file_delete($form_state['values']['_account']->picture);
391     }
392 
393     // The image was saved using file_save_upload() and was added to the
394     // files table as a temporary file. We'll make a copy and let the garbage
395     // collector delete the original upload.
396     $info = image_get_info($file->filepath);
397     $destination = variable_get('user_picture_path', 'pictures') .'/picture-'. $form['#uid'] .'.'. $info['extension'];
398     if (file_copy($file, $destination, FILE_EXISTS_REPLACE)) {
399       $form_state['values']['picture'] = $file->filepath;
400     }
401     else {
402       form_set_error('picture_upload', t("Failed to upload the picture image; the %directory directory doesn't exist or is not writable.", array('%directory' => variable_get('user_picture_path', 'pictures'))));
403     }
404   }
405 }
406 
407 /**
408  * Generate a random alphanumeric password.
409  */
410 function user_password($length = 10) {
411   // This variable contains the list of allowable characters for the
412   // password. Note that the number 0 and the letter 'O' have been
413   // removed to avoid confusion between the two. The same is true
414   // of 'I', 1, and 'l'.
4151  $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
416 
417   // Zero-based count of characters in the allowable list:
4181  $len = strlen($allowable_characters) - 1;
419 
420   // Declare the password as a blank string.
4211  $pass = '';
422 
423   // Loop the number of times specified by $length.
4241  for ($i = 0; $i < $length; $i++) {
425 
426     // Each iteration, pick a random character from the
427     // allowable string and append it to the password:
4281    $pass .= $allowable_characters[mt_rand(0, $len)];
429   }
430 
4311  return $pass;
432 }
433 
434 /**
435  * Determine whether the user has a given privilege.
436  *
437  * @param $string
438  *   The permission, such as "administer nodes", being checked for.
439  * @param $account
440  *   (optional) The account to check, if not given use currently logged in user.
441  * @param $reset
442  *   (optional) Resets the user's permissions cache, which will result in a
443  *   recalculation of the user's permissions. This is necessary to support
444  *   dynamically added user roles.
445  *
446  * @return
447  *   Boolean TRUE if the current user has the requested permission.
448  *
449  * All permission checks in Drupal should go through this function. This
450  * way, we guarantee consistent behavior, and ensure that the superuser
451  * can perform all actions.
452  */
453 function user_access($string, $account = NULL, $reset = FALSE) {
4541  global $user;
455   static $perm = array();
456 
4571  if ($reset) {
458     unset($perm);
459   }
460 
4611  if (is_null($account)) {
4621    $account = $user;
463   }
464 
465   // User #1 has all privileges:
4661  if ($account->uid == 1) {
4671    return TRUE;
468   }
469 
470   // To reduce the number of SQL queries, we cache the user's permissions
471   // in a static variable.
472   if (!isset($perm[$account->uid])) {
473     $result = db_query("SELECT p.perm FROM {role} r INNER JOIN {permission} p ON p.rid = r.rid WHERE r.rid IN (". db_placeholders($account->roles) .")", array_keys($account->roles));
474 
475     $perms = array();
476     while ($row = db_fetch_object($result)) {
477       $perms += array_flip(explode(', ', $row->perm));
478     }
479     $perm[$account->uid] = $perms;
480   }
481 
482   return isset($perm[$account->uid][$string]);
483 }
484 
485 /**
486  * Checks for usernames blocked by user administration.
487  *
488  * @return boolean TRUE for blocked users, FALSE for active.
489  */
490 function user_is_blocked($name) {
491   $deny = db_fetch_object(db_query("SELECT name FROM {users} WHERE status = 0 AND name = LOWER('%s')", $name));
492 
493   return $deny;
494 }
495 
496