source: branches/2.5/include/functions_user.inc.php @ 23075

Last change on this file since 23075 was 23075, checked in by plg, 11 years ago

bug 2907 fixed (only on 2.5, rvelices did a deeper rewrite for 2.6): wrong number of sub-albums

  • Property svn:eol-style set to LF
File size: 39.2 KB
Line 
1<?php
2// +-----------------------------------------------------------------------+
3// | Piwigo - a PHP based photo gallery                                    |
4// +-----------------------------------------------------------------------+
5// | Copyright(C) 2008-2013 Piwigo Team                  http://piwigo.org |
6// | Copyright(C) 2003-2008 PhpWebGallery Team    http://phpwebgallery.net |
7// | Copyright(C) 2002-2003 Pierrick LE GALL   http://le-gall.net/pierrick |
8// +-----------------------------------------------------------------------+
9// | This program is free software; you can redistribute it and/or modify  |
10// | it under the terms of the GNU General Public License as published by  |
11// | the Free Software Foundation                                          |
12// |                                                                       |
13// | This program is distributed in the hope that it will be useful, but   |
14// | WITHOUT ANY WARRANTY; without even the implied warranty of            |
15// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
16// | General Public License for more details.                              |
17// |                                                                       |
18// | You should have received a copy of the GNU General Public License     |
19// | along with this program; if not, write to the Free Software           |
20// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
21// | USA.                                                                  |
22// +-----------------------------------------------------------------------+
23
24// validate_mail_address:
25//   o verifies whether the given mail address has the
26//     right format. ie someone@domain.com "someone" can contain ".", "-" or
27//     even "_". Exactly as "domain". The extension doesn't have to be
28//     "com". The mail address can also be empty.
29//   o check if address could be empty
30//   o check if address is not used by a other user
31// If the mail address doesn't correspond, an error message is returned.
32//
33function validate_mail_address($user_id, $mail_address)
34{
35  global $conf;
36
37  if (empty($mail_address) and
38      !($conf['obligatory_user_mail_address'] and
39      in_array(script_basename(), array('register', 'profile'))))
40  {
41    return '';
42  }
43
44  if ( !email_check_format($mail_address) )
45  {
46    return l10n('mail address must be like xxx@yyy.eee (example : jack@altern.org)');
47  }
48
49  if (defined("PHPWG_INSTALLED") and !empty($mail_address))
50  {
51    $query = '
52SELECT count(*)
53FROM '.USERS_TABLE.'
54WHERE upper('.$conf['user_fields']['email'].') = upper(\''.$mail_address.'\')
55'.(is_numeric($user_id) ? 'AND '.$conf['user_fields']['id'].' != \''.$user_id.'\'' : '').'
56;';
57    list($count) = pwg_db_fetch_row(pwg_query($query));
58    if ($count != 0)
59    {
60      return l10n('this email address is already in use');
61    }
62  }
63}
64
65// validate_login_case:
66//   o check if login is not used by a other user
67// If the login doesn't correspond, an error message is returned.
68//
69function validate_login_case($login)
70{
71  global $conf;
72
73  if (defined("PHPWG_INSTALLED"))
74  {
75    $query = "
76SELECT ".$conf['user_fields']['username']."
77FROM ".USERS_TABLE."
78WHERE LOWER(".stripslashes($conf['user_fields']['username']).") = '".strtolower($login)."'
79;";
80
81    $count = pwg_db_num_rows(pwg_query($query));
82
83    if ($count > 0)
84    {
85      return l10n('this login is already used');
86    }
87  }
88}
89/**
90 * For test on username case sensitivity
91 *
92 * @param : $username typed in by user for identification
93 *
94 * @return : $username found in database
95 *
96 */
97function search_case_username($username)
98{
99  global $conf;
100
101  $username_lo = strtolower($username);
102
103  $SCU_users = array();
104
105  $q = pwg_query("
106    SELECT ".$conf['user_fields']['username']." AS username
107    FROM `".USERS_TABLE."`;
108  ");
109  while ($r = pwg_db_fetch_assoc($q))
110   $SCU_users[$r['username']] = strtolower($r['username']);
111   // $SCU_users is now an associative table where the key is the account as
112   // registered in the DB, and the value is this same account, in lower case
113
114  $users_found = array_keys($SCU_users, $username_lo);
115  // $users_found is now a table of which the values are all the accounts
116  // which can be written in lowercase the same way as $username
117  if (count($users_found) != 1) // If ambiguous, don't allow lowercase writing
118   return $username; // but normal writing will work
119  else
120   return $users_found[0];
121}
122function register_user($login, $password, $mail_address,
123  $with_notification = true, $errors = array())
124{
125  global $conf;
126
127  if ($login == '')
128  {
129    $errors[] = l10n('Please, enter a login');
130  }
131  if (preg_match('/^.* $/', $login))
132  {
133    $errors[] = l10n('login mustn\'t end with a space character');
134  }
135  if (preg_match('/^ .*$/', $login))
136  {
137    $errors[] = l10n('login mustn\'t start with a space character');
138  }
139  if (get_userid($login))
140  {
141    $errors[] = l10n('this login is already used');
142  }
143  if ($login != strip_tags($login))
144  {
145    $errors[] = l10n('html tags are not allowed in login');
146  }
147  $mail_error = validate_mail_address(null, $mail_address);
148  if ('' != $mail_error)
149  {
150    $errors[] = $mail_error;
151  }
152
153  if ($conf['insensitive_case_logon'] == true)
154  {
155    $login_error = validate_login_case($login);
156    if ($login_error != '')
157    {
158      $errors[] = $login_error;
159    }
160  }
161
162  $errors = trigger_event('register_user_check',
163              $errors,
164              array(
165                'username'=>$login,
166                'password'=>$password,
167                'email'=>$mail_address,
168              )
169            );
170
171  // if no error until here, registration of the user
172  if (count($errors) == 0)
173  {
174    // what will be the inserted id ?
175    $query = '
176SELECT MAX('.$conf['user_fields']['id'].') + 1
177  FROM '.USERS_TABLE.'
178;';
179    list($next_id) = pwg_db_fetch_row(pwg_query($query));
180
181    $insert =
182      array(
183        $conf['user_fields']['id'] => $next_id,
184        $conf['user_fields']['username'] => pwg_db_real_escape_string($login),
185        $conf['user_fields']['password'] => $conf['password_hash']($password),
186        $conf['user_fields']['email'] => $mail_address
187        );
188
189    mass_inserts(USERS_TABLE, array_keys($insert), array($insert));
190
191    // Assign by default groups
192    {
193      $query = '
194SELECT id
195  FROM '.GROUPS_TABLE.'
196  WHERE is_default = \''.boolean_to_string(true).'\'
197  ORDER BY id ASC
198;';
199      $result = pwg_query($query);
200
201      $inserts = array();
202      while ($row = pwg_db_fetch_assoc($result))
203      {
204          $inserts[] = array(
205            'user_id' => $next_id,
206            'group_id' => $row['id']
207          );
208      }
209    }
210
211    if (count($inserts) != 0)
212    {
213      mass_inserts(USER_GROUP_TABLE, array('user_id', 'group_id'), $inserts);
214    }
215
216    $override = null;
217    if ($with_notification and $conf['browser_language'])
218    {
219      if ( !get_browser_language($override['language']) )
220        $override=null;
221    }
222    create_user_infos($next_id, $override);
223
224    if ($with_notification and $conf['email_admin_on_new_user'])
225    {
226      include_once(PHPWG_ROOT_PATH.'include/functions_mail.inc.php');
227      $admin_url = get_absolute_root_url()
228                   .'admin.php?page=user_list&username='.$login;
229
230      $keyargs_content = array
231      (
232        get_l10n_args('User: %s', stripslashes($login)),
233        get_l10n_args('Email: %s', $_POST['mail_address']),
234        get_l10n_args('', ''),
235        get_l10n_args('Admin: %s', $admin_url)
236      );
237
238      pwg_mail_notification_admins
239      (
240        get_l10n_args('Registration of %s', stripslashes($login)),
241        $keyargs_content
242      );
243    }
244
245    trigger_action('register_user',
246      array(
247        'id'=>$next_id,
248        'username'=>$login,
249        'email'=>$mail_address,
250       )
251      );
252  }
253
254  return $errors;
255}
256
257function build_user( $user_id, $use_cache )
258{
259  global $conf;
260
261  $user['id'] = $user_id;
262  $user = array_merge( $user, getuserdata($user_id, $use_cache) );
263
264  if ($user['id'] == $conf['guest_id'] and $user['status'] <> 'guest')
265  {
266    $user['status'] = 'guest';
267    $user['internal_status']['guest_must_be_guest'] = true;
268  }
269
270  // Check user theme
271  if (!isset($user['theme_name']))
272  {
273    $user['theme'] = get_default_theme();
274  }
275
276  return $user;
277}
278
279/**
280 * find informations related to the user identifier
281 *
282 * @param int user identifier
283 * @param boolean use_cache
284 * @param array
285 */
286function getuserdata($user_id, $use_cache)
287{
288  global $conf;
289
290  // retrieve basic user data
291  $query = '
292SELECT ';
293  $is_first = true;
294  foreach ($conf['user_fields'] as $pwgfield => $dbfield)
295  {
296    if ($is_first)
297    {
298      $is_first = false;
299    }
300    else
301    {
302      $query.= '
303     , ';
304    }
305    $query.= $dbfield.' AS '.$pwgfield;
306  }
307  $query.= '
308  FROM '.USERS_TABLE.'
309  WHERE '.$conf['user_fields']['id'].' = \''.$user_id.'\'';
310
311  $row = pwg_db_fetch_assoc(pwg_query($query));
312
313  // retrieve additional user data ?
314  if ($conf['external_authentification'])
315  {
316    $query = '
317SELECT
318    COUNT(1) AS counter
319  FROM '.USER_INFOS_TABLE.' AS ui
320    LEFT JOIN '.USER_CACHE_TABLE.' AS uc ON ui.user_id = uc.user_id
321    LEFT JOIN '.THEMES_TABLE.' AS t ON t.id = ui.theme
322  WHERE ui.user_id = '.$user_id.'
323  GROUP BY ui.user_id
324;';
325    list($counter) = pwg_db_fetch_row(pwg_query($query));
326    if ($counter != 1)
327    {
328      create_user_infos($user_id);
329    }
330  }
331
332  // retrieve user info
333  $query = '
334SELECT
335    ui.*,
336    uc.*,
337    t.name AS theme_name
338  FROM '.USER_INFOS_TABLE.' AS ui
339    LEFT JOIN '.USER_CACHE_TABLE.' AS uc ON ui.user_id = uc.user_id
340    LEFT JOIN '.THEMES_TABLE.' AS t ON t.id = ui.theme
341  WHERE ui.user_id = '.$user_id.'
342;';
343
344  $result = pwg_query($query);
345  $user_infos_row = pwg_db_fetch_assoc($result);
346
347  // then merge basic + additional user data
348  $userdata = array_merge($row, $user_infos_row);
349
350  foreach ($userdata as &$value)
351  {
352      // If the field is true or false, the variable is transformed into a boolean value.
353      if ($value == 'true')
354      {
355        $value = true;
356      }
357      elseif ($value == 'false')
358      {
359        $value = false;
360      }
361  }
362  unset($value);
363
364  if ($use_cache)
365  {
366    if (!isset($userdata['need_update'])
367        or !is_bool($userdata['need_update'])
368        or $userdata['need_update'] == true)
369    {
370      $userdata['cache_update_time'] = time();
371
372      // Set need update are done
373      $userdata['need_update'] = false;
374
375      $userdata['forbidden_categories'] =
376        calculate_permissions($userdata['id'], $userdata['status']);
377
378      /* now we build the list of forbidden images (this list does not contain
379      images that are not in at least an authorized category)*/
380      $query = '
381SELECT DISTINCT(id)
382  FROM '.IMAGES_TABLE.' INNER JOIN '.IMAGE_CATEGORY_TABLE.' ON id=image_id
383  WHERE category_id NOT IN ('.$userdata['forbidden_categories'].')
384    AND level>'.$userdata['level'];
385      $forbidden_ids = array_from_query($query, 'id');
386
387      if ( empty($forbidden_ids) )
388      {
389        $forbidden_ids[] = 0;
390      }
391      $userdata['image_access_type'] = 'NOT IN'; //TODO maybe later
392      $userdata['image_access_list'] = implode(',',$forbidden_ids);
393
394
395      $query = '
396SELECT COUNT(DISTINCT(image_id)) as total
397  FROM '.IMAGE_CATEGORY_TABLE.'
398  WHERE category_id NOT IN ('.$userdata['forbidden_categories'].')
399    AND image_id '.$userdata['image_access_type'].' ('.$userdata['image_access_list'].')';
400      list($userdata['nb_total_images']) = pwg_db_fetch_row(pwg_query($query));
401
402
403      // now we update user cache categories
404      $user_cache_cats = get_computed_categories($userdata, null);
405      if ( !is_admin($userdata['status']) )
406      { // for non admins we forbid categories with no image (feature 1053)
407        $uppercats_of = null;
408        $forbidden_ids = array();
409        foreach ($user_cache_cats as $cat)
410        {
411          if ($cat['count_images']==0)
412          {
413            $forbidden_ids[] = $cat['cat_id'];
414            unset( $user_cache_cats[$cat['cat_id']] );
415
416            if (empty($uppercats_of))
417            {
418              $query = '
419SELECT
420    id,
421    uppercats
422  FROM '.CATEGORIES_TABLE.'
423;';
424              $uppercats_of = simple_hash_from_query($query, 'id', 'uppercats');
425            }
426
427            // if child album is removed, we must decrease the number of
428            // sub-albums of all parent albums (to the root)
429            if (isset($uppercats_of[$cat['cat_id']]))
430            {
431              foreach (explode(',', $uppercats_of[$cat['cat_id']]) as $id)
432              {
433                // the last $id is the current album removed, it will always
434                // fail the following condition because it was removed a few
435                // lines before.
436                if (isset($user_cache_cats[$id]))
437                {
438                  $user_cache_cats[$id]['count_categories']--;
439                }
440              }
441            }
442          }
443        }
444        if ( !empty($forbidden_ids) )
445        {
446          if ( empty($userdata['forbidden_categories']) )
447          {
448            $userdata['forbidden_categories'] = implode(',', $forbidden_ids);
449          }
450          else
451          {
452            $userdata['forbidden_categories'] .= ','.implode(',', $forbidden_ids);
453          }
454        }
455      }
456
457      // delete user cache
458      $query = '
459DELETE FROM '.USER_CACHE_CATEGORIES_TABLE.'
460  WHERE user_id = '.$userdata['id'];
461      pwg_query($query);
462
463      // Due to concurrency issues, we ask MySQL to ignore errors on
464      // insert. This may happen when cache needs refresh and that Piwigo is
465      // called "very simultaneously".
466      mass_inserts
467      (
468        USER_CACHE_CATEGORIES_TABLE,
469        array
470        (
471          'user_id', 'cat_id',
472          'date_last', 'max_date_last', 'nb_images', 'count_images', 'count_categories'
473        ),
474        $user_cache_cats,
475        array('ignore' => true)
476      );
477
478
479      // update user cache
480      $query = '
481DELETE FROM '.USER_CACHE_TABLE.'
482  WHERE user_id = '.$userdata['id'];
483      pwg_query($query);
484
485      // for the same reason as user_cache_categories, we ignore error on
486      // this insert
487      $query = '
488INSERT IGNORE INTO '.USER_CACHE_TABLE.'
489  (user_id, need_update, cache_update_time, forbidden_categories, nb_total_images,
490    image_access_type, image_access_list)
491  VALUES
492  ('.$userdata['id'].',\''.boolean_to_string($userdata['need_update']).'\','
493  .$userdata['cache_update_time'].',\''
494  .$userdata['forbidden_categories'].'\','.$userdata['nb_total_images'].',\''
495  .$userdata['image_access_type'].'\',\''.$userdata['image_access_list'].'\')';
496      pwg_query($query);
497    }
498  }
499
500  return $userdata;
501}
502
503/*
504 * deletes favorites of the current user if he's not allowed to see them
505 *
506 * @return void
507 */
508function check_user_favorites()
509{
510  global $user;
511
512  if ($user['forbidden_categories'] == '')
513  {
514    return;
515  }
516
517  // $filter['visible_categories'] and $filter['visible_images']
518  // must be not used because filter <> restriction
519  // retrieving images allowed : belonging to at least one authorized
520  // category
521  $query = '
522SELECT DISTINCT f.image_id
523  FROM '.FAVORITES_TABLE.' AS f INNER JOIN '.IMAGE_CATEGORY_TABLE.' AS ic
524    ON f.image_id = ic.image_id
525  WHERE f.user_id = '.$user['id'].'
526'.get_sql_condition_FandF
527  (
528    array
529      (
530        'forbidden_categories' => 'ic.category_id',
531      ),
532    'AND'
533  ).'
534;';
535  $authorizeds = array_from_query($query, 'image_id');
536
537  $query = '
538SELECT image_id
539  FROM '.FAVORITES_TABLE.'
540  WHERE user_id = '.$user['id'].'
541;';
542  $favorites = array_from_query($query, 'image_id');
543
544  $to_deletes = array_diff($favorites, $authorizeds);
545  if (count($to_deletes) > 0)
546  {
547    $query = '
548DELETE FROM '.FAVORITES_TABLE.'
549  WHERE image_id IN ('.implode(',', $to_deletes).')
550    AND user_id = '.$user['id'].'
551;';
552    pwg_query($query);
553  }
554}
555
556/**
557 * calculates the list of forbidden categories for a given user
558 *
559 * Calculation is based on private categories minus categories authorized to
560 * the groups the user belongs to minus the categories directly authorized
561 * to the user. The list contains at least -1 to be compliant with queries
562 * such as "WHERE category_id NOT IN ($forbidden_categories)"
563 *
564 * @param int user_id
565 * @param string user_status
566 * @return string forbidden_categories
567 */
568function calculate_permissions($user_id, $user_status)
569{
570  $query = '
571SELECT id
572  FROM '.CATEGORIES_TABLE.'
573  WHERE status = \'private\'
574;';
575  $private_array = array_from_query($query, 'id');
576
577  // retrieve category ids directly authorized to the user
578  $query = '
579SELECT cat_id
580  FROM '.USER_ACCESS_TABLE.'
581  WHERE user_id = '.$user_id.'
582;';
583  $authorized_array = array_from_query($query, 'cat_id');
584
585  // retrieve category ids authorized to the groups the user belongs to
586  $query = '
587SELECT cat_id
588  FROM '.USER_GROUP_TABLE.' AS ug INNER JOIN '.GROUP_ACCESS_TABLE.' AS ga
589    ON ug.group_id = ga.group_id
590  WHERE ug.user_id = '.$user_id.'
591;';
592  $authorized_array =
593    array_merge(
594      $authorized_array,
595      array_from_query($query, 'cat_id')
596      );
597
598  // uniquify ids : some private categories might be authorized for the
599  // groups and for the user
600  $authorized_array = array_unique($authorized_array);
601
602  // only unauthorized private categories are forbidden
603  $forbidden_array = array_diff($private_array, $authorized_array);
604
605  // if user is not an admin, locked categories are forbidden
606  if (!is_admin($user_status))
607  {
608    $query = '
609SELECT id
610  FROM '.CATEGORIES_TABLE.'
611  WHERE visible = \'false\'
612;';
613    $result = pwg_query($query);
614    while ($row = pwg_db_fetch_assoc($result))
615    {
616      $forbidden_array[] = $row['id'];
617    }
618    $forbidden_array = array_unique($forbidden_array);
619  }
620
621  if ( empty($forbidden_array) )
622  {// at least, the list contains 0 value. This category does not exists so
623   // where clauses such as "WHERE category_id NOT IN(0)" will always be
624   // true.
625    $forbidden_array[] = 0;
626  }
627
628  return implode(',', $forbidden_array);
629}
630
631/**
632 * compute data of categories branches (one branch only)
633 */
634function compute_branch_cat_data(&$cats, &$list_cat_id, &$level, &$ref_level)
635{
636  $date = '';
637  $count_images = 0;
638  $count_categories = 0;
639  do
640  {
641    $cat_id = array_pop($list_cat_id);
642    if (!is_null($cat_id))
643    {
644      // Count images and categories
645      $cats[$cat_id]['count_images'] += $count_images;
646      $cats[$cat_id]['count_categories'] += $count_categories;
647      $count_images = $cats[$cat_id]['count_images'];
648      $count_categories = $cats[$cat_id]['count_categories'] + 1;
649
650      if ((empty($cats[$cat_id]['max_date_last'])) or ($cats[$cat_id]['max_date_last'] < $date))
651      {
652        $cats[$cat_id]['max_date_last'] = $date;
653      }
654      else
655      {
656        $date = $cats[$cat_id]['max_date_last'];
657      }
658      $ref_level = substr_count($cats[$cat_id]['global_rank'], '.') + 1;
659    }
660    else
661    {
662      $ref_level = 0;
663    }
664  } while ($level <= $ref_level);
665
666  // Last cat updating must be added to list for next branch
667  if ($ref_level <> 0)
668  {
669    $list_cat_id[] = $cat_id;
670  }
671}
672
673/**
674 * compute data of categories branches
675 */
676function compute_categories_data(&$cats)
677{
678  $ref_level = 0;
679  $level = 0;
680  $list_cat_id = array();
681
682  foreach ($cats as $id => $category)
683  {
684    // Compute
685    $level = substr_count($category['global_rank'], '.') + 1;
686    if ($level > $ref_level)
687    {
688      $list_cat_id[] = $id;
689    }
690    else
691    {
692      compute_branch_cat_data($cats, $list_cat_id, $level, $ref_level);
693      $list_cat_id[] = $id;
694    }
695    $ref_level = $level;
696  }
697
698  $level = 1;
699  compute_branch_cat_data($cats, $list_cat_id, $level, $ref_level);
700}
701
702/**
703 * get computed array of categories
704 *
705 * @param array userdata
706 * @param int filter_days number of recent days to filter on or null
707 * @return array
708 */
709function get_computed_categories($userdata, $filter_days=null)
710{
711  $query = 'SELECT c.id AS cat_id, global_rank';
712  // Count by date_available to avoid count null
713  $query .= ',
714  MAX(date_available) AS date_last, COUNT(date_available) AS nb_images
715FROM '.CATEGORIES_TABLE.' as c
716  LEFT JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON ic.category_id = c.id
717  LEFT JOIN '.IMAGES_TABLE.' AS i
718    ON ic.image_id = i.id
719      AND i.level<='.$userdata['level'];
720
721  if ( isset($filter_days) )
722  {
723    $query .= ' AND i.date_available > '.pwg_db_get_recent_period_expression($filter_days);
724  }
725
726  if ( !empty($userdata['forbidden_categories']) )
727  {
728    $query.= '
729  WHERE c.id NOT IN ('.$userdata['forbidden_categories'].')';
730  }
731
732  $query.= '
733  GROUP BY c.id, c.global_rank';
734
735  $result = pwg_query($query);
736
737  $cats = array();
738  while ($row = pwg_db_fetch_assoc($result))
739  {
740    $row['user_id'] = $userdata['id'];
741    $row['count_categories'] = 0;
742    $row['count_images'] = (int)$row['nb_images'];
743    $row['max_date_last'] = $row['date_last'];
744
745    $cats += array($row['cat_id'] => $row);
746  }
747  uasort($cats, 'global_rank_compare');
748
749  compute_categories_data($cats);
750
751  if ( isset($filter_days) )
752  {
753    $cat_tmp = $cats;
754    $cats = array();
755
756    foreach ($cat_tmp as $category)
757    {
758      if (!empty($category['max_date_last']))
759      {
760        // Re-init counters
761        $category['count_categories'] = 0;
762        $category['count_images'] = (int)$category['nb_images'];
763        // Keep category
764        $cats[$category['cat_id']] = $category;
765      }
766    }
767    // Compute a second time
768    compute_categories_data($cats);
769  }
770  return $cats;
771}
772
773/**
774 * returns user identifier thanks to his name, false if not found
775 *
776 * @param string username
777 * @param int user identifier
778 */
779function get_userid($username)
780{
781  global $conf;
782
783  $username = pwg_db_real_escape_string($username);
784
785  $query = '
786SELECT '.$conf['user_fields']['id'].'
787  FROM '.USERS_TABLE.'
788  WHERE '.$conf['user_fields']['username'].' = \''.$username.'\'
789;';
790  $result = pwg_query($query);
791
792  if (pwg_db_num_rows($result) == 0)
793  {
794    return false;
795  }
796  else
797  {
798    list($user_id) = pwg_db_fetch_row($result);
799    return $user_id;
800  }
801}
802
803function get_userid_by_email($email)
804{
805  global $conf;
806
807  $email = pwg_db_real_escape_string($email);
808
809  $query = '
810SELECT
811    '.$conf['user_fields']['id'].'
812  FROM '.USERS_TABLE.'
813  WHERE UPPER('.$conf['user_fields']['email'].') = UPPER(\''.$email.'\')
814;';
815  $result = pwg_query($query);
816
817  if (pwg_db_num_rows($result) == 0)
818  {
819    return false;
820  }
821  else
822  {
823    list($user_id) = pwg_db_fetch_row($result);
824    return $user_id;
825  }
826}
827
828/*
829 * Returns a array with default user value
830 *
831 * @param convert_str allows to convert string value if necessary
832 */
833function get_default_user_info($convert_str = true)
834{
835  global $cache, $conf;
836
837  if (!isset($cache['default_user']))
838  {
839    $query = '
840SELECT *
841  FROM '.USER_INFOS_TABLE.'
842  WHERE user_id = '.$conf['default_user_id'].'
843;';
844
845    $result = pwg_query($query);
846
847    if (pwg_db_num_rows($result) > 0)
848    {
849      $cache['default_user'] = pwg_db_fetch_assoc($result);
850     
851      unset($cache['default_user']['user_id']);
852      unset($cache['default_user']['status']);
853      unset($cache['default_user']['registration_date']);
854    }
855    else
856    {
857      $cache['default_user'] = false;
858    }
859  }
860
861  if (is_array($cache['default_user']) and $convert_str)
862  {
863    $default_user = $cache['default_user'];
864    foreach ($default_user as &$value)
865    {
866      // If the field is true or false, the variable is transformed into a boolean value.
867      if ($value == 'true')
868      {
869        $value = true;
870      }
871      elseif ($value == 'false')
872      {
873        $value = false;
874      }
875    }
876    return $default_user;
877  }
878  else
879  {
880    return $cache['default_user'];
881  }
882}
883
884/*
885 * Returns a default user value
886 *
887 * @param value_name: name of value
888 * @param sos_value: value used if don't exist value
889 */
890function get_default_user_value($value_name, $sos_value)
891{
892  $default_user = get_default_user_info(true);
893  if ($default_user === false or empty($default_user[$value_name]))
894  {
895    return $sos_value;
896  }
897  else
898  {
899   return $default_user[$value_name];
900  }
901}
902
903/*
904 * Returns the default template value
905 *
906 */
907function get_default_theme()
908{
909  $theme = get_default_user_value('theme', PHPWG_DEFAULT_TEMPLATE);
910  if (check_theme_installed($theme))
911  {
912    return $theme;
913  }
914
915  // let's find the first available theme
916  $active_themes = get_pwg_themes();
917  foreach (array_keys(get_pwg_themes()) as $theme_id)
918  {
919    return $theme_id;
920  }
921}
922
923/*
924 * Returns the default language value
925 *
926 */
927function get_default_language()
928{
929  return get_default_user_value('language', PHPWG_DEFAULT_LANGUAGE);
930}
931
932/**
933  * Returns true if the browser language value is set into param $lang
934  *
935  */
936function get_browser_language(&$lang)
937{
938  $browser_language = substr(@$_SERVER["HTTP_ACCEPT_LANGUAGE"], 0, 2);
939  foreach (get_languages() as $language_code => $language_name)
940  {
941    if (substr($language_code, 0, 2) == $browser_language)
942    {
943      $lang = $language_code;
944      return true;
945    }
946  }
947  return false;
948}
949
950/**
951 * add user informations based on default values
952 *
953 * @param int user_id / array of user_if
954 * @param array of values used to override default user values
955 */
956function create_user_infos($arg_id, $override_values = null)
957{
958  global $conf;
959
960  if (is_array($arg_id))
961  {
962    $user_ids = $arg_id;
963  }
964  else
965  {
966    $user_ids = array();
967    if (is_numeric($arg_id))
968    {
969      $user_ids[] = $arg_id;
970    }
971  }
972
973  if (!empty($user_ids))
974  {
975    $inserts = array();
976    list($dbnow) = pwg_db_fetch_row(pwg_query('SELECT NOW();'));
977
978    $default_user = get_default_user_info(false);
979    if ($default_user === false)
980    {
981      // Default on structure are used
982      $default_user = array();
983    }
984
985    if (!is_null($override_values))
986    {
987      $default_user = array_merge($default_user, $override_values);
988    }
989
990    foreach ($user_ids as $user_id)
991    {
992      $level= isset($default_user['level']) ? $default_user['level'] : 0;
993      if ($user_id == $conf['webmaster_id'])
994      {
995        $status = 'webmaster';
996        $level = max( $conf['available_permission_levels'] );
997      }
998      else if (($user_id == $conf['guest_id']) or
999               ($user_id == $conf['default_user_id']))
1000      {
1001        $status = 'guest';
1002      }
1003      else
1004      {
1005        $status = 'normal';
1006      }
1007
1008      $insert = array_merge(
1009        $default_user,
1010        array(
1011          'user_id' => $user_id,
1012          'status' => $status,
1013          'registration_date' => $dbnow,
1014          'level' => $level
1015          ));
1016
1017      $inserts[] = $insert;
1018    }
1019
1020    mass_inserts(USER_INFOS_TABLE, array_keys($inserts[0]), $inserts);
1021  }
1022}
1023
1024/**
1025 * returns the auto login key or false on error
1026 * @param int user_id
1027 * @param time_t time
1028 * @param string [out] username
1029*/
1030function calculate_auto_login_key($user_id, $time, &$username)
1031{
1032  global $conf;
1033  $query = '
1034SELECT '.$conf['user_fields']['username'].' AS username
1035  , '.$conf['user_fields']['password'].' AS password
1036FROM '.USERS_TABLE.'
1037WHERE '.$conf['user_fields']['id'].' = '.$user_id;
1038  $result = pwg_query($query);
1039  if (pwg_db_num_rows($result) > 0)
1040  {
1041    $row = pwg_db_fetch_assoc($result);
1042    $username = stripslashes($row['username']);
1043    $data = $time.$user_id.$username;
1044    $key = base64_encode( hash_hmac('sha1', $data, $conf['secret_key'].$row['password'],true) );
1045    return $key;
1046  }
1047  return false;
1048}
1049
1050/*
1051 * Performs all required actions for user login
1052 * @param int user_id
1053 * @param bool remember_me
1054 * @return void
1055*/
1056function log_user($user_id, $remember_me)
1057{
1058  global $conf, $user;
1059
1060  if ($remember_me and $conf['authorize_remembering'])
1061  {
1062    $now = time();
1063    $key = calculate_auto_login_key($user_id, $now, $username);
1064    if ($key!==false)
1065    {
1066      $cookie = $user_id.'-'.$now.'-'.$key;
1067      if (version_compare(PHP_VERSION, '5.2', '>=') )
1068      {
1069        setcookie($conf['remember_me_name'],
1070            $cookie,
1071            time()+$conf['remember_me_length'],
1072            cookie_path(),ini_get('session.cookie_domain'),ini_get('session.cookie_secure'),
1073            ini_get('session.cookie_httponly')
1074          );
1075      }
1076      else
1077      {
1078        setcookie($conf['remember_me_name'],
1079            $cookie,
1080            time()+$conf['remember_me_length'],
1081            cookie_path(),ini_get('session.cookie_domain'),ini_get('session.cookie_secure')
1082          );
1083      }
1084    }
1085  }
1086  else
1087  { // make sure we clean any remember me ...
1088    setcookie($conf['remember_me_name'], '', 0, cookie_path(),ini_get('session.cookie_domain'));
1089  }
1090  if ( session_id()!="" )
1091  { // we regenerate the session for security reasons
1092    // see http://www.acros.si/papers/session_fixation.pdf
1093    session_regenerate_id(true);
1094  }
1095  else
1096  {
1097    session_start();
1098  }
1099  $_SESSION['pwg_uid'] = (int)$user_id;
1100
1101  $user['id'] = $_SESSION['pwg_uid'];
1102  trigger_action('user_login', $user['id']);
1103}
1104
1105/*
1106 * Performs auto-connexion when cookie remember_me exists
1107 * @return true/false
1108*/
1109function auto_login() {
1110  global $conf;
1111
1112  if ( isset( $_COOKIE[$conf['remember_me_name']] ) )
1113  {
1114    $cookie = explode('-', stripslashes($_COOKIE[$conf['remember_me_name']]));
1115    if ( count($cookie)===3
1116        and is_numeric(@$cookie[0]) /*user id*/
1117        and is_numeric(@$cookie[1]) /*time*/
1118        and time()-$conf['remember_me_length']<=@$cookie[1]
1119        and time()>=@$cookie[1] /*cookie generated in the past*/ )
1120    {
1121      $key = calculate_auto_login_key( $cookie[0], $cookie[1], $username );
1122      if ($key!==false and $key===$cookie[2])
1123      {
1124        log_user($cookie[0], true);
1125        trigger_action('login_success', stripslashes($username));
1126        return true;
1127      }
1128    }
1129    setcookie($conf['remember_me_name'], '', 0, cookie_path(),ini_get('session.cookie_domain'));
1130  }
1131  return false;
1132}
1133
1134/**
1135 * hashes a password, with the PasswordHash class from phpass security
1136 * library. We use an "pwg_" prefix because function password_hash is
1137 * planned for PHP 5.5. Code inspired from Wordpress.
1138 *
1139 * @param string $password Plain text user password to hash
1140 * @return string The hash string of the password
1141 */
1142function pwg_password_hash($password)
1143{
1144  global $pwg_hasher;
1145
1146  if (empty($pwg_hasher))
1147  {
1148    require_once(PHPWG_ROOT_PATH.'include/passwordhash.class.php');
1149   
1150    // We use the portable hash feature from phpass because we can't be sure
1151    // Piwigo runs on PHP 5.3+ (and won't run on an older version in the
1152    // future)
1153    $pwg_hasher = new PasswordHash(13, true);
1154  }
1155 
1156  return $pwg_hasher->HashPassword($password);
1157}
1158
1159/**
1160 * Verifies a password, with the PasswordHash class from phpass security
1161 * library. We use an "pwg_" prefix because function password_verify is
1162 * planned for PHP 5.5. Code inspired from Wordpress.
1163 *
1164 * @param string $password Plain text user password to hash
1165 * @param string $hash may be md5 or phpass hashed password
1166 * @param integer $account_id only useful to update password hash from md5 to phpass
1167 * @return string The hash string of the password
1168 */
1169function pwg_password_verify($password, $hash, $user_id=null)
1170{
1171  global $conf, $pwg_hasher;
1172
1173  // If the password has not been hashed with the current algorithm.
1174  if (strpos('$P', $hash) !== 0)
1175  {
1176    if (!empty($conf['pass_convert']))
1177    {
1178      $check = ($hash == $conf['pass_convert']($password));
1179    }
1180    else
1181    {
1182      $check = ($hash == md5($password));
1183    }
1184   
1185    if ($check)
1186    {
1187      if (!isset($user_id) or $conf['external_authentification'])
1188      {
1189        return true;
1190      }
1191     
1192      // Rehash using new hash.
1193      $hash = pwg_password_hash($password);
1194
1195      single_update(
1196        USERS_TABLE,
1197        array('password' => $hash),
1198        array('id' => $user_id)
1199        );
1200    }
1201  }
1202
1203  // If the stored hash is longer than an MD5, presume the
1204  // new style phpass portable hash.
1205  if (empty($pwg_hasher))
1206  {
1207    require_once(PHPWG_ROOT_PATH.'include/passwordhash.class.php');
1208   
1209    // We use the portable hash feature
1210    $pwg_hasher = new PasswordHash(13, true);
1211  }
1212
1213  return $pwg_hasher->CheckPassword($password, $hash);
1214}
1215
1216/**
1217 * Tries to login a user given username and password (must be MySql escaped)
1218 * return true on success
1219 */
1220function try_log_user($username, $password, $remember_me)
1221{
1222  return trigger_event('try_log_user', false, $username, $password, $remember_me);
1223}
1224
1225add_event_handler('try_log_user', 'pwg_login', EVENT_HANDLER_PRIORITY_NEUTRAL, 4);
1226
1227function pwg_login($success, $username, $password, $remember_me)
1228{
1229  if ($success===true) 
1230  {
1231    return true;
1232  }
1233 
1234  // we force the session table to be clean
1235  pwg_session_gc();
1236
1237  global $conf;
1238  // retrieving the encrypted password of the login submitted
1239  $query = '
1240SELECT '.$conf['user_fields']['id'].' AS id,
1241       '.$conf['user_fields']['password'].' AS password
1242  FROM '.USERS_TABLE.'
1243  WHERE '.$conf['user_fields']['username'].' = \''.pwg_db_real_escape_string($username).'\'
1244;';
1245  $row = pwg_db_fetch_assoc(pwg_query($query));
1246  if ($conf['password_verify']($password, $row['password'], $row['id']))
1247  {
1248    log_user($row['id'], $remember_me);
1249    trigger_action('login_success', stripslashes($username));
1250    return true;
1251  }
1252  trigger_action('login_failure', stripslashes($username));
1253  return false;
1254}
1255
1256/** Performs all the cleanup on user logout */
1257function logout_user()
1258{
1259  global $conf;
1260 
1261  trigger_action('user_logout', @$_SESSION['pwg_uid']);
1262 
1263  $_SESSION = array();
1264  session_unset();
1265  session_destroy();
1266  setcookie(session_name(),'',0,
1267      ini_get('session.cookie_path'),
1268      ini_get('session.cookie_domain')
1269    );
1270  setcookie($conf['remember_me_name'], '', 0, cookie_path(),ini_get('session.cookie_domain'));
1271}
1272
1273/*
1274 * Return user status used in this library
1275 * @return string
1276*/
1277function get_user_status($user_status)
1278{
1279  global $user;
1280
1281  if (empty($user_status))
1282  {
1283    if (isset($user['status']))
1284    {
1285      $user_status = $user['status'];
1286    }
1287    else
1288    {
1289      // swicth to default value
1290      $user_status = '';
1291    }
1292  }
1293  return $user_status;
1294}
1295
1296/*
1297 * Return access_type definition of user
1298 * Test does with user status
1299 * @return bool
1300*/
1301function get_access_type_status($user_status='')
1302{
1303  global $conf;
1304
1305  switch (get_user_status($user_status))
1306  {
1307    case 'guest':
1308    {
1309      $access_type_status =
1310        ($conf['guest_access'] ? ACCESS_GUEST : ACCESS_FREE);
1311      break;
1312    }
1313    case 'generic':
1314    {
1315      $access_type_status = ACCESS_GUEST;
1316      break;
1317    }
1318    case 'normal':
1319    {
1320      $access_type_status = ACCESS_CLASSIC;
1321      break;
1322    }
1323    case 'admin':
1324    {
1325      $access_type_status = ACCESS_ADMINISTRATOR;
1326      break;
1327    }
1328    case 'webmaster':
1329    {
1330      $access_type_status = ACCESS_WEBMASTER;
1331      break;
1332    }
1333    default:
1334    {
1335      $access_type_status = ACCESS_FREE;
1336      break;
1337    }
1338  }
1339
1340  return $access_type_status;
1341}
1342
1343/*
1344 * Return if user have access to access_type definition
1345 * Test does with user status
1346 * @return bool
1347*/
1348function is_autorize_status($access_type, $user_status = '')
1349{
1350  return (get_access_type_status($user_status) >= $access_type);
1351}
1352
1353/*
1354 * Check if user have access to access_type definition
1355 * Stop action if there are not access
1356 * Test does with user status
1357 * @return none
1358*/
1359function check_status($access_type, $user_status = '')
1360{
1361  if (!is_autorize_status($access_type, $user_status))
1362  {
1363    access_denied();
1364  }
1365}
1366
1367/*
1368 * Return if user is generic
1369 * @return bool
1370*/
1371 function is_generic($user_status = '')
1372{
1373  return get_user_status($user_status) == 'generic';
1374}
1375
1376/*
1377 * Return if user is only a guest
1378 * @return bool
1379*/
1380 function is_a_guest($user_status = '')
1381{
1382  return get_user_status($user_status) == 'guest';
1383}
1384
1385/*
1386 * Return if user is, at least, a classic user
1387 * @return bool
1388*/
1389 function is_classic_user($user_status = '')
1390{
1391  return is_autorize_status(ACCESS_CLASSIC, $user_status);
1392}
1393
1394/*
1395 * Return if user is, at least, an administrator
1396 * @return bool
1397*/
1398 function is_admin($user_status = '')
1399{
1400  return is_autorize_status(ACCESS_ADMINISTRATOR, $user_status);
1401}
1402
1403/*
1404 * Return if user is, at least, a webmaster
1405 * @return bool
1406*/
1407 function is_webmaster($user_status = '')
1408{
1409  return is_autorize_status(ACCESS_WEBMASTER, $user_status);
1410}
1411
1412/*
1413 * Adviser status is depreciated from piwigo 2.2
1414 * @return false
1415*/
1416function is_adviser()
1417{
1418  // TODO for Piwigo 2.4 : trigger a warning. We don't do it on Piwigo 2.3
1419  // to avoid changes for plugin contributors
1420  // trigger_error('call to obsolete function is_adviser', E_USER_WARNING);
1421  return false;
1422}
1423
1424/*
1425 * Return if current user can edit/delete/validate a comment
1426 * @param action edit/delete/validate
1427 * @return bool
1428 */
1429function can_manage_comment($action, $comment_author_id)
1430{
1431  global $user, $conf;
1432
1433  if (is_a_guest())
1434  {
1435    return false;
1436  }
1437
1438  if (!in_array($action, array('delete','edit', 'validate')))
1439  {
1440    return false;
1441  }
1442
1443  if (is_admin())
1444  {
1445    return true;
1446  }
1447
1448  if ('edit' == $action and $conf['user_can_edit_comment'])
1449  {
1450    if ($comment_author_id == $user['id']) {
1451      return true;
1452    }
1453  }
1454
1455  if ('delete' == $action and $conf['user_can_delete_comment'])
1456  {
1457    if ($comment_author_id == $user['id']) {
1458      return true;
1459    }
1460  }
1461
1462  return false;
1463}
1464
1465/*
1466 * Return mail address as display text
1467 * @return string
1468*/
1469function get_email_address_as_display_text($email_address)
1470{
1471  global $conf;
1472
1473  if (!isset($email_address) or (trim($email_address) == ''))
1474  {
1475    return '';
1476  }
1477  else
1478  {
1479    return $email_address;
1480  }
1481}
1482
1483/*
1484 * Compute sql where condition with restrict and filter data. "FandF" means
1485 * Forbidden and Filters.
1486 *
1487 * @param array condition_fields: read function body
1488 * @param string prefix_condition: prefixes sql if condition is not empty
1489 * @param boolean force_one_condition: use at least "1 = 1"
1490 *
1491 * @return string sql where/conditions
1492 */
1493function get_sql_condition_FandF(
1494  $condition_fields,
1495  $prefix_condition = null,
1496  $force_one_condition = false
1497  )
1498{
1499  global $user, $filter;
1500
1501  $sql_list = array();
1502
1503  foreach ($condition_fields as $condition => $field_name)
1504  {
1505    switch($condition)
1506    {
1507      case 'forbidden_categories':
1508      {
1509        if (!empty($user['forbidden_categories']))
1510        {
1511          $sql_list[] =
1512            $field_name.' NOT IN ('.$user['forbidden_categories'].')';
1513        }
1514        break;
1515      }
1516      case 'visible_categories':
1517      {
1518        if (!empty($filter['visible_categories']))
1519        {
1520          $sql_list[] =
1521            $field_name.' IN ('.$filter['visible_categories'].')';
1522        }
1523        break;
1524      }
1525      case 'visible_images':
1526        if (!empty($filter['visible_images']))
1527        {
1528          $sql_list[] =
1529            $field_name.' IN ('.$filter['visible_images'].')';
1530        }
1531        // note there is no break - visible include forbidden
1532      case 'forbidden_images':
1533        if (
1534            !empty($user['image_access_list'])
1535            or $user['image_access_type']!='NOT IN'
1536            )
1537        {
1538          $table_prefix=null;
1539          if ($field_name=='id')
1540          {
1541            $table_prefix = '';
1542          }
1543          elseif ($field_name=='i.id')
1544          {
1545            $table_prefix = 'i.';
1546          }
1547          if ( isset($table_prefix) )
1548          {
1549            $sql_list[]=$table_prefix.'level<='.$user['level'];
1550          }
1551          else if ( !empty($user['image_access_list']) and !empty($user['image_access_type']) )
1552          {
1553            $sql_list[]=$field_name.' '.$user['image_access_type']
1554                .' ('.$user['image_access_list'].')';
1555          }
1556        }
1557        break;
1558      default:
1559      {
1560        die('Unknow condition');
1561        break;
1562      }
1563    }
1564  }
1565
1566  if (count($sql_list) > 0)
1567  {
1568    $sql = '('.implode(' AND ', $sql_list).')';
1569  }
1570  else
1571  {
1572    $sql = $force_one_condition ? '1 = 1' : '';
1573  }
1574
1575  if (isset($prefix_condition) and !empty($sql))
1576  {
1577    $sql = $prefix_condition.' '.$sql;
1578  }
1579
1580  return $sql;
1581}
1582
1583/**
1584 * search an available activation_key
1585 *
1586 * @return string
1587 */
1588function get_user_activation_key()
1589{
1590  while (true)
1591  {
1592    $key = generate_key(20);
1593    $query = '
1594SELECT COUNT(*)
1595  FROM '.USER_INFOS_TABLE.'
1596  WHERE activation_key = \''.$key.'\'
1597;';
1598    list($count) = pwg_db_fetch_row(pwg_query($query));
1599    if (0 == $count)
1600    {
1601      return $key;
1602    }
1603  }
1604}
1605
1606?>
Note: See TracBrowser for help on using the repository browser.