[ Index ]
 

Code source de Serendipity 1.2

Accédez au Source d'autres logiciels libres

title

Body

[fermer]

/include/ -> functions_config.inc.php (source)

   1  <?php # $Id: functions_config.inc.php 1860 2007-08-21 10:26:07Z garvinhicking $
   2  # Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team)
   3  # All rights reserved.  See LICENSE file for licensing details
   4  
   5  if (IN_serendipity !== true) {
   6      die ("Don't hack!");
   7  }
   8  
   9  if (defined('S9Y_FRAMEWORK_CONFIG')) {
  10      return;
  11  }
  12  @define('S9Y_FRAMEWORK_CONFIG', true);
  13  
  14  /**
  15   * Adds a new author account
  16   *
  17   * @access public
  18   * @param   string  New username
  19   * @param   string  New password
  20   * @param   string  The realname of the user
  21   * @param   string  The email address of the user
  22   * @param   int     The userlevel of a user
  23   * @return  int     The new user ID of the added author
  24   */
  25  function serendipity_addAuthor($username, $password, $realname, $email, $userlevel=0) {
  26      global $serendipity;
  27      $password = md5($password);
  28      $query = "INSERT INTO {$serendipity['dbPrefix']}authors (username, password, realname, email, userlevel)
  29                          VALUES  ('" . serendipity_db_escape_string($username) . "',
  30                                   '" . serendipity_db_escape_String($password) . "',
  31                                   '" . serendipity_db_escape_String($realname) . "',
  32                                   '" . serendipity_db_escape_String($email) . "',
  33                                   '" . serendipity_db_escape_String($userlevel) . "')";
  34      serendipity_db_query($query);
  35      $cid = serendipity_db_insert_id('authors', 'authorid');
  36  
  37      $data = array(
  38          'authorid' => $cid,
  39          'username' => $username,
  40          'realname' => $realname,
  41          'email'    => $email
  42      );
  43  
  44      serendipity_insertPermalink($data, 'author');
  45      return $cid;
  46  }
  47  
  48  /**
  49   * Delete an author account
  50   *
  51   * (Note, this function does not delete entries by an author)
  52   *
  53   * @access public
  54   * @param   int     The author ID to delete
  55   * @return  boolean     True on success, false on error or unsufficient privileges
  56   */
  57  function serendipity_deleteAuthor($authorid) {
  58      global $serendipity;
  59  
  60      if (!serendipity_checkPermission('adminUsersDelete')) {
  61          return false;
  62      }
  63  
  64      if (serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}authors WHERE authorid=" . (int)$authorid)) {
  65          serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}permalinks WHERE entry_id=" . (int)$authorid ." and type='author'");
  66      }
  67      return true;
  68  }
  69  
  70  /**
  71   * Removes a configuration value from the Serendipity Configuration
  72   *
  73   * Global config items have the authorid 0, author-specific configuration items have the corresponding authorid.
  74   *
  75   * @access public
  76   * @param   string      The name of the configuration value
  77   * @param   int         The ID of the owner of the config value (0: global)
  78   * @return null
  79   */
  80  function serendipity_remove_config_var($name, $authorid = 0) {
  81      global $serendipity;
  82      serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}config where name='" . serendipity_db_escape_string($name) . "' AND authorid = " . (int)$authorid);
  83  }
  84  
  85  /**
  86   * Sets a configuration value for the Serendipity Configuration
  87   *
  88   * Global config items have the authorid 0, author-specific configuration items have the corresponding authorid.
  89   *
  90   * @access public
  91   * @param   string      The name of the configuration value
  92   * @param   string      The value of the configuration item
  93   * @param   int         The ID of the owner of the config value (0: global)
  94   * @return  null
  95   */
  96  function serendipity_set_config_var($name, $val, $authorid = 0) {
  97      global $serendipity;
  98  
  99      serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}config where name='" . serendipity_db_escape_string($name) . "' AND authorid = " . (int)$authorid);
 100  
 101      if ($name == 'password' || $name == 'check_password') {
 102          return;
 103      }
 104  
 105      $r = serendipity_db_insert('config', array('name' => $name, 'value' => $val, 'authorid' => $authorid));
 106  
 107      if ($authorid === 0 || $authorid === $serendipity['authorid']) {
 108          if ($val === 'false') {
 109              $serendipity[$name] = false;
 110          } else {
 111              $serendipity[$name] = $val;
 112          }
 113      }
 114  
 115      if (is_string($r)) {
 116          echo $r;
 117      }
 118  }
 119  
 120  /**
 121   * Retrieve a global configuration value for a specific item of the current Serendipity Configuration
 122   *
 123   * @access public
 124   * @param   string      The name of the configuration value
 125   * @param   string      The default value of a configuration item, if not found in the Database
 126   * @param   boolean     If set to true, the default value of a configuration item will be returned if the item is set, but empty. If false, an empty configuration value will be returned empty. This is required for getting default values if you do not want to store/allow empty config values.
 127   * @return  string      The configuration value content
 128   */
 129  function serendipity_get_config_var($name, $defval = false, $empty = false) {
 130      global $serendipity;
 131      if (isset($serendipity[$name])) {
 132          if ($empty && gettype($serendipity[$name]) == 'string' && $serendipity[$name] === '') {
 133              return $defval;
 134          } else {
 135              return $serendipity[$name];
 136          }
 137      } else {
 138          return $defval;
 139      }
 140  }
 141  
 142  /**
 143   * Retrieve an author-specific configuration value for an item of the Serendipity Configuration stored in the DB
 144   *
 145   * Despite the serendipity_get_config_var() function, this will retrieve author-specific values straight from the Database.
 146   *
 147   * @access public
 148   * @param   string      The name of the configuration value
 149   * @param   int         The ID of the owner of the config value (0: global)
 150   * @param   string      The default value of a configuration option, if not set in the DB
 151   * @return  string      The configuration value content
 152   */
 153  function serendipity_get_user_config_var($name, $authorid, $default = '') {
 154      global $serendipity;
 155  
 156      $author_sql = '';
 157      if (!empty($authorid)) {
 158          $author_sql = "authorid = " . (int)$authorid . " AND ";
 159      } elseif (isset($serendipity[$name])) {
 160          return $serendipity[$name];
 161      }
 162  
 163      $r = serendipity_db_query("SELECT value FROM {$serendipity['dbPrefix']}config WHERE $author_sql name = '" . $name . "' LIMIT 1", true);
 164  
 165      if (is_array($r)) {
 166          return $r[0];
 167      } else {
 168          return $default;
 169      }
 170  }
 171  
 172  /**
 173   * Retrieves an author-specific account value
 174   *
 175   * This retrieves specific account data from the user configuration, not from the serendipity configuration.
 176   *
 177   * @access public
 178   * @param   string      The name of the configuration value
 179   * @param   int         The ID of the author to fetch the configuration for
 180   * @param   string      The default value of a configuration option, if not set in the DB
 181   * @return  string      The configuration value content
 182   */
 183  function serendipity_get_user_var($name, $authorid, $default) {
 184      global $serendipity;
 185  
 186      $r = serendipity_db_query("SELECT $name FROM {$serendipity['dbPrefix']}authors WHERE authorid = " . (int)$authorid, true);
 187  
 188      if (is_array($r)) {
 189          return $r[0];
 190      } else {
 191          return $default;
 192      }
 193  }
 194  
 195  /**
 196   * Updates data from the author-specific account
 197   *
 198   * This sets the personal account data of a serendipity user within the 'authors' DB table
 199   *
 200   * @access public
 201   * @param   string      The name of the configuration value
 202   * @param   string      The content of the configuration value
 203   * @param   int         The ID of the author to set the configuration for
 204   * @param   boolean     If set to true, the stored config value will be imported to the Session/current config of the user. This is applied for example when you change your own user's preferences and want it to be immediately reflected in the interface.
 205   * @return null
 206   */
 207  function serendipity_set_user_var($name, $val, $authorid, $copy_to_s9y = true) {
 208      global $serendipity;
 209  
 210      // When inserting a DB value, this array maps the new values to the corresponding s9y variables
 211      static $user_map_array = array(
 212          'username'  => 'serendipityUser',
 213          'email'     => 'serendipityEmail',
 214          'userlevel' => 'serendipityUserlevel'
 215      );
 216  
 217      // Special case for inserting a password
 218      switch($name) {
 219          case 'check_password':
 220              //Skip this field.  It doesn't need to be stored.
 221              return;
 222          case 'password':
 223              if (empty($val)) {
 224                  return;
 225              }
 226  
 227              $val = md5($val);
 228              $copy_to_s9y = false;
 229              break;
 230  
 231          case 'right_publish':
 232          case 'mail_comments':
 233          case 'mail_trackbacks':
 234              $val = (serendipity_db_bool($val) ? 1 : '0');
 235              break;
 236      }
 237  
 238      serendipity_db_query("UPDATE {$serendipity['dbPrefix']}authors SET $name = '" . serendipity_db_escape_string($val) . "' WHERE authorid = " . (int)$authorid);
 239  
 240      if ($copy_to_s9y) {
 241          if (isset($user_map_array[$name])) {
 242              $key = $user_map_array[$name];
 243          } else {
 244              $key = 'serendipity' . ucfirst($name);
 245          }
 246  
 247          $_SESSION[$key] = $serendipity[$key] = $val;
 248      }
 249  }
 250  
 251  /**
 252   * Gets the full filename and path of a template/style/theme file
 253   *
 254   * The returned full path is depending on the second parameter, where you can either fetch a HTTP path, or a realpath.
 255   * The file is searched in the current template, and if it is not found there, it is returned from the default template.
 256   *
 257   * @access public
 258   * @param   string      The filename to search for in the selected template
 259   * @param   string      The path selector that tells whether to return a HTTP or realpath
 260   * @return  string      The full path+filename to the requested file
 261   */
 262  function serendipity_getTemplateFile($file, $key = 'serendipityHTTPPath') {
 263      global $serendipity;
 264  
 265      $directories = array();
 266  
 267      $directories[] = isset($serendipity['template']) ? $serendipity['template'] . '/' : '';
 268      if (isset($serendipity['template_engine']) && (stristr($file, 'admin/') === false || $serendipity['template_engine'] != 'default')) {
 269           $directories[] = $serendipity['template_engine'] . '/';
 270      }
 271  
 272      $directories[] = $serendipity['defaultTemplate'] .'/';
 273      $directories[] = 'default/';
 274  
 275      foreach ($directories as $directory) {
 276          $templateFile = $serendipity['templatePath'] . $directory . $file;
 277          if (file_exists($serendipity['serendipityPath'] . $templateFile)) {
 278              return $serendipity[$key] . $templateFile;
 279          }
 280      }
 281  
 282      if (preg_match('@\.(tpl|css|php)@i', $file) && !stristr($file, 'plugin')) {
 283          return $file;
 284      }
 285  
 286      return false;
 287  }
 288  
 289  /**
 290   * Loads all configuration values and imports them to the $serendipity array
 291   *
 292   * This function may be called twice - once for the global config and once for
 293   * user-specific config
 294   *
 295   * @access public
 296   * @param   int     The Authorid to fetch the configuration from (0: global)
 297   * @return  null
 298   */
 299  function serendipity_load_configuration($author = null) {
 300      global $serendipity;
 301      static $config_loaded = array();
 302  
 303      if (isset($config_loaded[$author])) {
 304          return true;
 305      }
 306  
 307      if (!empty($author)) {
 308          // Replace default configuration directives with user-relevant data
 309          $rows =& serendipity_db_query("SELECT name,value
 310                                          FROM {$serendipity['dbPrefix']}config
 311                                          WHERE authorid = '". (int)$author ."'");
 312      } else {
 313          // Only get default variables, user-independent (frontend)
 314          $rows =& serendipity_db_query("SELECT name, value
 315                                          FROM {$serendipity['dbPrefix']}config
 316                                          WHERE authorid = 0");
 317      }
 318  
 319      if (is_array($rows)) {
 320          foreach ($rows as $row) {
 321              // Convert 'true' and 'false' into booleans
 322              $serendipity[$row['name']] = serendipity_get_bool($row['value']);
 323          }
 324      }
 325      $config_loaded[$author] = true;
 326  
 327      // Store default language
 328      $serendipity['default_lang'] = $serendipity['lang'];
 329  }
 330  
 331  /**
 332   * Perform logout functions (destroys session data)
 333   *
 334   * @access public
 335   * @return null
 336   */
 337  function serendipity_logout() {
 338      $_SESSION['serendipityAuthedUser'] = false;
 339      serendipity_session_destroy();
 340      serendipity_deleteCookie('author_information');
 341      serendipity_deleteCookie('author_token');
 342  }
 343  
 344  /**
 345   * Destroys a session, keeps important stuff intact.
 346   * @access public
 347   * @return null
 348   */
 349  function serendipity_session_destroy() {
 350      $no_smarty = $_SESSION['no_smarty'];
 351      @session_destroy();
 352      session_regenerate_id();
 353      session_start();
 354  
 355      $_SESSION['SERVER_GENERATED_SID'] = true;
 356      $_SESSION['no_smarty']            = $no_smarty;
 357  }
 358  
 359  /**
 360   * Perform login to Serendipity
 361   *
 362   * @access public
 363   * @param   boolean     If set to true, external plugins will be queried for getting a login
 364   * @return  boolean     Return true, if the user is logged in. False if not.
 365   */
 366  function serendipity_login($use_external = true) {
 367      global $serendipity;
 368  
 369      if (serendipity_authenticate_author('', '', false, $use_external)) {
 370          #The session has this data already
 371          #we previously just checked the value of $_SESSION['serendipityAuthedUser'] but
 372          #we need the authorid still, so call serendipity_authenticate_author with blank
 373          #params
 374          return true;
 375      }
 376  
 377      // First try login via POST data. If true, the userinformation will be stored in a cookie (optionally)
 378      if (serendipity_authenticate_author($serendipity['POST']['user'], $serendipity['POST']['pass'], false, $use_external)) {
 379          if (empty($serendipity['POST']['auto'])) {
 380              serendipity_deleteCookie('author_information');
 381              serendipity_deleteCookie('author_information_iv');
 382              return false;
 383          } else {
 384              serendipity_issueAutologin(
 385                  array('username' => $serendipity['POST']['user'],
 386                        'password' => $serendipity['POST']['pass']
 387                  )
 388              );
 389              return true;
 390          }
 391      // Now try login via COOKIE data
 392      } elseif (isset($serendipity['COOKIE']['author_information'])) {
 393          $cookie = serendipity_checkAutologin($serendipity['COOKIE']['author_information'], $serendipity['COOKIE']['author_information_iv']);
 394  
 395          if (is_array($cookie) && serendipity_authenticate_author($cookie['username'], $cookie['password'], false, $use_external)) {
 396              return true;
 397          } else {
 398              serendipity_deleteCookie('author_information');
 399              serendipity_deleteCookie('author_information_iv');
 400              return false;
 401          }
 402      }
 403  }
 404  
 405  /**
 406   * Issue a new auto login cookie
 407   * @param array The input data
 408   */
 409  function serendipity_issueAutologin($array) {
 410      global $serendipity;
 411  
 412      $package = serialize($array);
 413  
 414      if (function_exists('mcrypt_encrypt')) {
 415          // Secure the package data when being stored inside the Database
 416          $iv  = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC), MCRYPT_RAND);
 417          $key = base64_encode($iv);
 418          $package = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $package, MCRYPT_MODE_CBC, $iv);
 419          serendipity_setCookie('author_information_iv', $key);
 420      }
 421      $package = base64_encode($package);
 422  
 423      $rnd = md5(uniqid(time(), true) . $_SERVER['REMOTE_ADDR']);
 424  
 425      // Delete possible current cookie. Also delete any autologin keys that smell like 3-week-old, dead fish.
 426      serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}options 
 427                                  WHERE okey = 'l_" . serendipity_db_escape_string($serendipity['COOKIE']['author_information']) . "'
 428                                     OR (okey LIKE 'l_%' AND name < " . (time() - 1814400) . ")");
 429  
 430      // Issue new autologin cookie
 431      serendipity_db_query("INSERT INTO {$serendipity['dbPrefix']}options (name, value, okey) VALUES ('" . time() . "', '" . serendipity_db_escape_string($package) . "', 'l_" . $rnd . "')");
 432      serendipity_setCookie('author_information', $rnd);
 433  }
 434  
 435  /**
 436   * Checks a new auto login cookie
 437   * @param array The input data
 438   */
 439  function serendipity_checkAutologin($ident, $iv) {
 440      global $serendipity;
 441  
 442      // Fetch login data from DB
 443      $autologin =& serendipity_db_query("SELECT * FROM {$serendipity['dbPrefix']}options WHERE okey = 'l_" . serendipity_db_escape_string($ident) . "' LIMIT 1", true, 'assoc');
 444      if (!is_array($autologin)) {
 445          return false;
 446      }
 447  
 448      if (function_exists('mcrypt_decrypt') && !empty($iv)) {
 449          $key    = $iv;
 450          $iv     = base64_decode($iv);
 451          $cookie = unserialize(mcrypt_decrypt(MCRYPT_BLOWFISH, $key, base64_decode($autologin['value']), MCRYPT_MODE_CBC, $iv));
 452      } else {
 453          $cookie = unserialize(base64_decode($autologin['value']));
 454      }
 455  
 456      if ($autologin['name'] < (time()-86400)) {
 457          // Issued autologin cookie has been issued more than 1 day ago. Re-Issue new cookie, invalidate old one to prevent abuse
 458          if ($serendipity['expose_s9y']) serendipity_header('X-ReIssue-Cookie: +' . (time() - $autologin['name']) . 's');
 459          serendipity_issueAutologin($cookie);
 460      }
 461  
 462      return $cookie;
 463  }
 464  
 465  /**
 466   * Set a session cookie which can identify a user accross http/https boundaries
 467   */
 468  function serendipity_setAuthorToken() {
 469      $hash = sha1(uniqid(rand(), true));
 470      serendipity_setCookie('author_token', $hash);
 471      $_SESSION['author_token'] = $hash;
 472  }
 473  
 474  /**
 475   * Perform user authentication routine
 476   *
 477   * If a user is already authenticated via session data, this bypasses some routines.
 478   * After a user has ben authenticated, several SESSION variables ar set.
 479   * If the authentication fails, the session is destroyed.
 480   *
 481   * @access public
 482   * @param   string      The username to check
 483   * @param   string      The password to check (may contain plaintext or MD5 hash)
 484   * @param   boolean     Indicates whether the input password is already in MD5 format (TRUE) or not (FALSE).
 485   * @param   boolean     Indicates whether to query external plugins for authentication
 486   * @return  boolean     True on success, False on error
 487   */
 488  function serendipity_authenticate_author($username = '', $password = '', $is_md5 = false, $use_external = true) {
 489      global $serendipity;
 490  
 491      if (isset($_SESSION['serendipityUser']) && isset($_SESSION['serendipityPassword']) && isset($_SESSION['serendipityAuthedUser']) && $_SESSION['serendipityAuthedUser'] == true) {
 492          $username = $_SESSION['serendipityUser'];
 493          $password = $_SESSION['serendipityPassword'];
 494          // For safety reasons when multiple blogs are installed on the same host, we need to check the current author each time to not let him log into a different blog with the same sessiondata
 495          $is_md5 = true;
 496      }
 497  
 498      $is_authenticated = false;
 499      serendipity_plugin_api::hook_event('backend_login', $is_authenticated, NULL);
 500      if ($is_authenticated) {
 501          return true;
 502      }
 503  
 504      if ($username != '') {
 505          if ($use_external) {
 506              serendipity_plugin_api::hook_event('backend_auth', $is_md5, array('username' => $username, 'password' => $password));
 507          }
 508  
 509          if ($is_md5 === false && !empty($password)) {
 510              $password = md5($password);
 511          }
 512  
 513          $query = "SELECT DISTINCT
 514                      email, realname, authorid, userlevel, right_publish
 515                    FROM
 516                      {$serendipity['dbPrefix']}authors
 517                    WHERE
 518                      username   = '" . serendipity_db_escape_string($username) . "'
 519                    AND password = '" . serendipity_db_escape_string($password) . "'";
 520          $row =& serendipity_db_query($query, true, 'assoc');
 521  
 522          if (is_array($row)) {
 523              serendipity_setCookie('old_session', session_id(), false);
 524              if (!$is_md5) {
 525                  serendipity_setAuthorToken();
 526              }
 527              $_SESSION['serendipityUser']        = $serendipity['serendipityUser']         = $username;
 528              $_SESSION['serendipityRealname']    = $serendipity['serendipityRealname']     = $row['realname'];
 529              $_SESSION['serendipityPassword']    = $serendipity['serendipityPassword']     = $password;
 530              $_SESSION['serendipityEmail']       = $serendipity['serendipityEmail']        = $row['email'];
 531              $_SESSION['serendipityAuthorid']    = $serendipity['authorid']                = $row['authorid'];
 532              $_SESSION['serendipityUserlevel']   = $serendipity['serendipityUserlevel']    = $row['userlevel'];
 533              $_SESSION['serendipityAuthedUser']  = $serendipity['serendipityAuthedUser']   = true;
 534              $_SESSION['serendipityRightPublish']= $serendipity['serendipityRightPublish'] = $row['right_publish'];
 535              serendipity_load_configuration($serendipity['authorid']);
 536              serendipity_setCookie('userDefLang', $serendipity['lang'], false);
 537              return true;
 538          } else {
 539              $_SESSION['serendipityAuthedUser'] = false;
 540              serendipity_session_destroy();
 541          }
 542      }
 543  
 544      return false;
 545  }
 546  
 547  /**
 548   * Check if a user is logged in
 549   *
 550   * @access public
 551   * @return boolean  TRUE when logged in, FALSE when not.
 552   */
 553  function serendipity_userLoggedIn() {
 554      if ($_SESSION['serendipityAuthedUser'] === true && IS_installed) {
 555          return true;
 556      } else {
 557          return false;
 558      }
 559  }
 560  
 561  /**
 562   * A clone of an ifsetor() function to set a variable conditional on if the target already exists
 563   *
 564   * The function sets the contents of $source into the $target variable, but only if $target is not yet set. Eases up some if...else logic or multiple ternary operators
 565   *
 566   * @access public
 567   * @param   mixed   Source variable that should be set into the target variable (reference call!)
 568   * @param   mixed   Target variable, that should get the contents of the source variable (reference call!)
 569   * @return  boolean True, when $target was not yet set and has been altered. False when no changes where made.
 570   */
 571  function serendipity_restoreVar(&$source, &$target) {
 572      global $serendipity;
 573  
 574      if (isset($source) && !isset($target)) {
 575          $target = $source;
 576          return true;
 577      }
 578  
 579      return false;
 580  }
 581  
 582  /**
 583   * Echo Javascript code to set a cookie variable
 584   *
 585   * This function is useful if your HTTP headers were already sent, but you still want to set a cookie
 586   * Note that contents are echo'd, not return'd.
 587   *
 588   * @access public
 589   * @param   string      The name of the cookie variable
 590   * @param   string      The contents of the cookie variable
 591   * @return  null
 592   */
 593  function serendipity_JSsetCookie($name, $value) {
 594      $name  = htmlentities($name);
 595      $value = urlencode($value);
 596  
 597      echo '<script type="text/javascript">SetCookie("' . $name . '", unescape("' . $value . '"))</script>' . "\n";
 598  }
 599  
 600  /**
 601   * Set a Cookie via HTTP calls, and update $_COOKIE plus $serendipity['COOKIE'] array.
 602   *
 603   * @access public
 604   * @param   string      The name of the cookie variable
 605   * @param   string      The contents of the cookie variable
 606   * @return null
 607   */
 608  function serendipity_setCookie($name, $value, $securebyprot = true) {
 609      global $serendipity;
 610  
 611      $host = $_SERVER['HTTP_HOST'];
 612      if ($securebyprot) {
 613          $secure = (strtolower($_SERVER['HTTPS']) == 'on') ? true : false;
 614          if ($pos = strpos($host, ":")) {
 615              $host = substr($host, 0, $pos);
 616          }
 617      } else {
 618          $secure = false;
 619      }
 620      
 621      // If HTTP-Hosts like "localhost" are used, current browsers reject cookies.
 622      // In this case, we disregard the HTTP host to be able to set that cookie.
 623      if (substr_count($host, '.') < 2) {
 624          $host = '';
 625      }
 626  
 627      setcookie("serendipity[$name]", $value, time()+60*60*24*30, $serendipity['serendipityHTTPPath'], $host, $secure);
 628      $_COOKIE[$name] = $value;
 629      $serendipity['COOKIE'][$name] = $value;
 630  }
 631  
 632  /**
 633   * Deletes an existing cookie value
 634   *
 635   * LONG
 636   *
 637   * @access public
 638   * @param   string      Name of the cookie to delete
 639   * @return
 640   */
 641  function serendipity_deleteCookie($name) {
 642      global $serendipity;
 643  
 644      $host = $_SERVER['HTTP_HOST'];
 645      if ($pos = strpos($host, ":")) {
 646          $host = substr($host, 0, $pos);
 647      }
 648  
 649      setcookie("serendipity[$name]", '', time()-4000, $serendipity['serendipityHTTPPath'], $host);
 650      unset($_COOKIE[$name]);
 651      unset($serendipity['COOKIE'][$name]);
 652  }
 653  
 654  /**
 655   * Performs a check whether an iframe for the admin section shall be emitted
 656   *
 657   * The iframe is used for previewing an entry with the stylesheet of the frontend.
 658   * It fetches its data from the session input data.
 659   *
 660   * @access private
 661   * @return boolean  True, if iframe was requested, false if not.
 662   */
 663  function serendipity_is_iframe() {
 664      global $serendipity;
 665  
 666      if ($serendipity['GET']['is_iframe'] && is_array($_SESSION['save_entry'])) {
 667          include_once  S9Y_INCLUDE_PATH . 'include/functions_entries_admin.inc.php';
 668          // An iframe may NOT contain <html> and </html> tags, that's why we emit different headers here than on serendipity_admin.php
 669  
 670          // We need to restore GET/POST variables to that depending plugins inside the iframe
 671          // can still fetch all that variables; and we also tighten security by not allowing
 672          // to pass any different GET/POST variables to our iframe.
 673          $iframe_mode         = $serendipity['GET']['iframe_mode'];
 674          $serendipity['POST'] = &$_SESSION['save_entry_POST'];
 675          $serendipity['GET']  = &$_SESSION['save_entry_POST']; // GET-Vars are the same as POST to ensure compatibility.
 676          ignore_user_abort(true);
 677          serendipity_iframe($_SESSION['save_entry'], $iframe_mode, true);
 678          return true;
 679      }
 680      return false;
 681  }
 682  
 683  /**
 684   * Prints the content of the iframe.
 685   *
 686   * Called by serendipity_is_iframe, when preview is requested. Fetches data from session.
 687   * An iframe is used so that a single s9y page must not timeout on intensive operations,
 688   * and so that the frontend stylesheet can be embedded without screwing up the backend.
 689   *
 690   * @access private
 691   * @see serendipity_is_iframe()
 692   * @param   mixed   The entry array (comes from session variable)
 693   * @param   string  Indicates whether an entry is previewed or saved. Save performs XML-RPC calls.
 694   * @param   boolean Use smarty templating?
 695   * @return  boolean Indicates whether iframe data was printed
 696   */
 697  function serendipity_iframe(&$entry, $mode = null, $use_smarty = true) {
 698      global $serendipity;
 699  
 700      if (empty($mode) || !is_array($entry)) {
 701          return false;
 702      }
 703  
 704      if ($use_smarty) {
 705          $serendipity['smarty_raw_mode'] = true; // Force output of Smarty stuff in the backend
 706          $serendipity['smarty_preview']  = true;
 707          serendipity_smarty_init();
 708          $serendipity['smarty']->assign('is_preview',  true);
 709          ob_start();
 710      }
 711  
 712      $show = false;
 713      switch ($mode) {
 714          case 'save':
 715              echo '<div style="float: left; height: 75px"></div>';
 716              $res = serendipity_updertEntry($entry);
 717  
 718              if (is_string($res)) {
 719                  echo '<div class="serendipity_msg_error">' . ERROR . ': <b>' . $res . '</b></div>';
 720              } else {
 721                  if (!empty($serendipity['lastSavedEntry'])) {
 722                      // Last saved entry must be propagated to entry form so that if the user re-edits it,
 723                      // it needs to be stored with the new ID.
 724                      echo '<script type="text/javascript">parent.document.forms[\'serendipityEntry\'][\'serendipity[id]\'].value = "' . $serendipity['lastSavedEntry'] . '";</script>';
 725                  }
 726                  $entrylink = serendipity_archiveURL($res, $entry['title'], 'serendipityHTTPPath', true, array('timestamp' => $entry['timestamp']));
 727                  echo '<div class="serendipityAdminMsgSuccess"><img style="height: 22px; width: 22px; border: 0px; padding-right: 4px; vertical-align: middle" src="' . serendipity_getTemplateFile('admin/img/admin_msg_success.png') . '" alt="" />' . ENTRY_SAVED . ' (<a href="' . $entrylink . '" target="_blank">' . VIEW . '</a>)</div>';
 728              }
 729              echo '<br style="clear: both" />';
 730  
 731              $show = true;
 732              break;
 733  
 734          case 'preview':
 735              echo '<div style="float: left; height: 225px"></div>';
 736              serendipity_printEntries(array($entry), ($entry['extended'] != '' ? 1 : 0), true);
 737              echo '<br style="clear: both" />';
 738  
 739              $show = true;
 740              break;
 741      }
 742  
 743      if ($use_smarty) {
 744          $preview = ob_get_contents();
 745          ob_end_clean();
 746          $serendipity['smarty']->assign_by_ref('preview', $preview);
 747          $serendipity['smarty']->display(serendipity_getTemplateFile('preview_iframe.tpl', 'serendipityPath'));
 748      }
 749  
 750      return $show;
 751  }
 752  
 753  /**
 754   * Creates the necessary session data to be used by later iframe calls
 755   *
 756   * This function emits the actual <iframe> call.
 757   *
 758   * @access private
 759   * @see serendipity_is_iframe()
 760   * @param   string  Indicates whether an entry is previewed or saved. Save performs XML-RPC calls.
 761   * @param   mixed   The entry array (comes from HTTP POST request)
 762   * @return  boolean Indicates whether iframe data was stored
 763   */
 764  function serendipity_iframe_create($mode, &$entry) {
 765      global $serendipity;
 766  
 767      if (!empty($serendipity['POST']['no_save'])) {
 768          return true;
 769      }
 770  
 771      $_SESSION['save_entry']      = $entry;
 772      $_SESSION['save_entry_POST'] = $serendipity['POST'];
 773      
 774      $attr = '';
 775      switch($mode) {
 776          case 'save':
 777              $attr = ' height="100" ';
 778              break;
 779  
 780          case 'preview':
 781              $attr = ' height="300" ';
 782              break;
 783      }
 784  
 785      echo '<iframe src="serendipity_admin.php?serendipity[is_iframe]=true&amp;serendipity[iframe_mode]=' . $mode . '" id="serendipity_iframe" name="serendipity_iframe" ' . $attr . ' width="100%" frameborder="0" marginwidth="0" marginheight="0" scrolling="auto" title="Serendipity">'
 786           . IFRAME_WARNING
 787           . '</iframe><br /><br />';
 788  }
 789  
 790  /**
 791   * Pre-Checks certain server environments to indicate available options when installing Serendipity
 792   *
 793   * @access public
 794   * @param   string      The name of the configuration option that needs to be checked for environmental data.
 795   * @return  array       Returns the array of available options for the requested config option
 796   */
 797  function serendipity_probeInstallation($item) {
 798      global $serendipity;
 799      $res = NULL;
 800  
 801      switch ( $item ) {
 802          case 'dbType' :
 803              $res =  array();
 804              if (extension_loaded('mysql')) {
 805                  $res['mysql'] = 'MySQL';
 806              }
 807              if (extension_loaded('PDO') &&
 808                  in_array('pgsql', PDO::getAvailableDrivers())) {
 809                  $res['pdo-postgres'] = 'PDO::PostgreSQL';
 810              }
 811              if (extension_loaded('pgsql')) {
 812                  $res['postgres'] = 'PostgreSQL';
 813              }
 814              if (extension_loaded('mysqli')) {
 815                  $res['mysqli'] = 'MySQLi';
 816              }
 817              if (extension_loaded('sqlite')) {
 818                  $res['sqlite'] = 'SQLite';
 819              }
 820              if (extension_loaded('SQLITE3')) {
 821                  $res['sqlite3'] = 'SQLite3';
 822              }
 823              break;
 824  
 825          case 'rewrite' :
 826              $res = array();
 827              $res['none'] = 'Disable URL Rewriting';
 828              $res['errordocs'] = 'Use Apache errorhandling';
 829              if( !function_exists('apache_get_modules') || in_array('mod_rewrite', apache_get_modules()) ) {
 830                  $res['rewrite'] = 'Use Apache mod_rewrite';
 831              }
 832              break;
 833      }
 834  
 835      return $res;
 836  }
 837  
 838  /**
 839   * Sets a HTTP header
 840   *
 841   * @access public
 842   * @param   string      The HTTP headre to set
 843   * @return null
 844   */
 845  function serendipity_header($header) {
 846      if (!headers_sent()) {
 847          header($header);
 848      }
 849  }
 850  
 851  /**
 852   * Gets the currently selected language. Either from the browser, or the personal configuration, or the global configuration.
 853   *
 854   * This function also sets HTTP Headers and cookies to contain the language for follow-up requests
 855   * TODO:
 856   * This previously was handled inside a plugin with an event hook, but caching
 857   * the event plugins that early in sequence created trouble with plugins not
 858   * having loaded the right language.
 859   * Find a way to let plugins hook into that sequence :-)
 860   *
 861   * @access public
 862   * @return  string      Returns the name of the selected language.
 863   */
 864  function serendipity_getSessionLanguage() {
 865      global $serendipity;
 866  
 867      // DISABLE THIS!
 868  /*
 869      if ($_SESSION['serendipityAuthedUser']) {
 870          serendipity_header('X-Serendipity-InterfaceLangSource: Database');
 871          return $serendipity['lang'];
 872      }
 873  */
 874  
 875      if (isset($_REQUEST['user_language']) && (!empty($serendipity['languages'][$_REQUEST['user_language']])) && !headers_sent()) {
 876          serendipity_setCookie('serendipityLanguage', $_REQUEST['user_language'], false);
 877      }
 878  
 879      if (isset($serendipity['COOKIE']['serendipityLanguage'])) {
 880          if ($serendipity['expose_s9y']) serendipity_header('X-Serendipity-InterfaceLangSource: Cookie');
 881          $lang = $serendipity['COOKIE']['serendipityLanguage'];
 882      } elseif (!empty($serendipity['languages'][$serendipity['GET']['lang_selected']])) {
 883          if ($serendipity['expose_s9y']) serendipity_header('X-Serendipity-InterfaceLangSource: GET');
 884          $lang = $serendipity['GET']['lang_selected'];
 885      } elseif (serendipity_db_bool($serendipity['lang_content_negotiation'])) {
 886          if ($serendipity['expose_s9y']) serendipity_header('X-Serendipity-InterfaceLangSource: Content-Negotiation');
 887          $lang = serendipity_detectLang();
 888      }
 889  
 890      if (isset($lang)) {
 891          $serendipity['detected_lang'] = $lang;
 892      } else {
 893          if (! empty($_SESSION['serendipityLanguage'])) {
 894              $lang = $_SESSION['serendipityLanguage'];
 895          } else {
 896              if (isset($serendipity['COOKIE']['userDefLang']) && ! empty($serendipity['COOKIE']['userDefLang'])) {
 897                  $lang = $serendipity['COOKIE']['userDefLang'];
 898              } else {
 899                  $lang = $serendipity['lang'];
 900                }
 901          }
 902          $serendipity['detected_lang'] = null;
 903      }
 904  
 905      if (!isset($serendipity['languages'][$lang])) {
 906          $serendipity['detected_lang'] = null;
 907          return $serendipity['lang'];
 908      } else {
 909          $_SESSION['serendipityLanguage'] = $lang;
 910          if (!is_null($serendipity['detected_lang'])) {
 911              if ($serendipity['expose_s9y']) serendipity_header('X-Serendipity-InterfaceLang: ' . $lang);
 912          }
 913      }
 914  
 915      return $lang;
 916  }
 917  
 918  /**
 919   * Gets the selected language from personal configuration if needed
 920   *
 921   * This function also sets HTTP Headers and cookies to contain the language for follow-up requests
 922   *
 923   * @access public
 924   * @return  string      Returns the name of the selected language.
 925   */
 926  function serendipity_getPostAuthSessionLanguage() {
 927      global $serendipity;
 928  
 929      if (! is_null($serendipity['detected_lang'])) {
 930          return $serendipity['detected_lang'];
 931      }
 932  
 933      if ($_SESSION['serendipityAuthedUser']) {
 934          if ($serendipity['expose_s9y']) serendipity_header('X-Serendipity-InterfaceLangSource: Database');
 935          $lang = $serendipity['lang'];
 936      } else {
 937          $lang = (isset($_SESSION['serendipityLanguage']))?$_SESSION['serendipityLanguage']:$serendipity['lang'];
 938      }
 939  
 940      if (!isset($serendipity['languages'][$lang])) {
 941          $lang = $serendipity['lang'];
 942      }
 943  
 944      $_SESSION['serendipityLanguage'] = $lang;
 945  
 946      if ($serendipity['expose_s9y']) serendipity_header('X-Serendipity-InterfaceLang: ' . $lang);
 947  
 948      if ($lang != $serendipity['lang']) {
 949          $serendipity['content_lang'] = $lang;
 950      }
 951  
 952      return $lang;
 953  }
 954  
 955  /**
 956   * Retrieves an array of applying permissions to an author
 957   *
 958   * The privileges of each group an author is a member of are aggreated
 959   * and stored in a larger array. So both memberships and all aplying
 960   * privileges are returned.
 961   *
 962   * @access public
 963   * @param   int     The ID of the author to fetch permissions/group memberships for
 964   * @return  array   Multi-dimensional associative array which holds a 'membership' and permission name data
 965   */
 966  function &serendipity_getPermissions($authorid) {
 967      global $serendipity;
 968  
 969          // Get group information
 970          $groups =& serendipity_db_query("SELECT ag.groupid, g.name, gc.property, gc.value
 971                                            FROM {$serendipity['dbPrefix']}authorgroups AS ag
 972                                 LEFT OUTER JOIN {$serendipity['dbPrefix']}groups AS g
 973                                              ON ag.groupid = g.id
 974                                 LEFT OUTER JOIN {$serendipity['dbPrefix']}groupconfig AS gc
 975                                              ON gc.id = g.id
 976                                           WHERE ag.authorid = " . (int)$authorid);
 977          $perm = array('membership' => array());
 978          if (is_array($groups)) {
 979              foreach($groups AS $group) {
 980                  $perm['membership'][$group['groupid']]       = $group['groupid'];
 981                  $perm[$group['groupid']][$group['property']] = $group['value'];
 982              }
 983          }
 984          return $perm;
 985  }
 986  
 987  /**
 988   * Returns the list of available internal Serendipity permission field names
 989   *
 990   * This function also mapps which function was available to which userleves in older
 991   * Serendipity versions. Thus if an author does not have a certain privilege he should
 992   * have because of his userlevel, this can be reverse-mapped.
 993   *
 994   * @access public
 995   * @return  array   Multi-dimensional associative array which the list of all permission items plus their userlevel associations
 996   */
 997  function serendipity_getPermissionNames() {
 998      return array(
 999          'personalConfiguration'
1000              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF, USERLEVEL_EDITOR),
1001          'personalConfigurationUserlevel'
1002              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1003          'personalConfigurationNoCreate'
1004              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1005          'personalConfigurationRightPublish'
1006              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1007          'siteConfiguration'
1008              => array(USERLEVEL_ADMIN),
1009          'blogConfiguration'
1010              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1011  
1012          'adminEntries'
1013              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF, USERLEVEL_EDITOR),
1014          'adminEntriesMaintainOthers'
1015              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1016  
1017          'adminImport'
1018              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1019  
1020          'adminCategories'
1021              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF, USERLEVEL_EDITOR),
1022          'adminCategoriesMaintainOthers'
1023              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1024          'adminCategoriesDelete'
1025              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1026  
1027          'adminUsers'
1028              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1029          'adminUsersDelete'
1030              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1031          'adminUsersEditUserlevel'
1032              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1033          'adminUsersMaintainSame'
1034              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1035          'adminUsersMaintainOthers'
1036              => array(USERLEVEL_ADMIN),
1037          'adminUsersCreateNew'
1038              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1039          'adminUsersGroups'
1040              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1041  
1042          'adminPlugins'
1043              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1044          'adminPluginsMaintainOthers'
1045              => array(USERLEVEL_ADMIN),
1046  
1047          'adminImages'
1048              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF, USERLEVEL_EDITOR),
1049          'adminImagesDirectories'
1050              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1051          'adminImagesAdd'
1052              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF, USERLEVEL_EDITOR),
1053          'adminImagesDelete'
1054              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF, USERLEVEL_EDITOR),
1055          'adminImagesMaintainOthers'
1056              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1057          'adminImagesViewOthers'
1058              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF, USERLEVEL_EDITOR),
1059          'adminImagesView'
1060              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF, USERLEVEL_EDITOR),
1061          'adminImagesSync'
1062              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1063  
1064          'adminComments'
1065              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1066  
1067          'adminTemplates'
1068              => array(USERLEVEL_ADMIN, USERLEVEL_CHIEF),
1069      );
1070  }
1071  
1072  /**
1073   * Checks if a permission is granted to a specific author
1074   *
1075   * This function caches all permission chacks in static function variables to not
1076   * fetch all permissions time and again.
1077   * The permission checks are performed agains the values of each group. If a privilege
1078   * is set in one of the groups the author is a user of, the function returns true.
1079   * If a privilege is not set, the userlevel of an author is checked to act for backwards-compatibility.
1080   *
1081   * @access public
1082   * @see serendipity_getPermissionNames()
1083   * @param   string      The name of the permission to check
1084   * @param   int         The authorid for which the permission check should be performed
1085   * @param   boolean     If set to true, all groups that the requested author is a user of will be returned. This bypasses the permission check and mainly acts as a mean to return cached permissions, since those variables are only available within this function.
1086   * @return  mixed       Either returns true if a permission check is performed, or return an array of group memberships. Depends on the $returnMyGroups variable.
1087   */
1088  function serendipity_checkPermission($permName, $authorid = null, $returnMyGroups = false) {
1089      global $serendipity;
1090  
1091      // Define old serendipity permissions
1092      static $permissions = null;
1093      static $group = null;
1094  
1095      if (IS_installed !== true) {
1096          return true;
1097      }
1098  
1099      if ($permissions === null) {
1100          $permissions = serendipity_getPermissionNames();
1101      }
1102  
1103      if ($group === null) {
1104          $group = array();
1105      }
1106  
1107      if ($authorid === null) {
1108          $authorid = $serendipity['authorid'];
1109      }
1110  
1111      if (!isset($group[$authorid])) {
1112          $group[$authorid] = serendipity_getPermissions($authorid);
1113      }
1114  
1115      if ($returnMyGroups) {
1116          if ($returnMyGroups === 'all') {
1117              return $group[$authorid];
1118          } else {
1119              return $group[$authorid]['membership'];
1120          }
1121      }
1122  
1123      if ($authorid == $serendipity['authorid'] && $serendipity['no_create']) {
1124          // This no_create user privilege overrides other permissions.
1125          return false;
1126      }
1127  
1128      $return = true;
1129  
1130      foreach($group[$authorid] AS $item) {
1131          if (!isset($item[$permName])) {
1132              continue;
1133          }
1134  
1135          if ($item[$permName] === 'true') {
1136              return true;
1137          } else {
1138              $return = false;
1139          }
1140      }
1141  
1142      // If the function did not yet return it means there's a check for a permission which is not defined anywhere.
1143      // Let's use a backwards compatible way.
1144      if ($return && isset($permissions[$permName]) && in_array($serendipity['serendipityUserlevel'], $permissions[$permName])) {
1145          return true;
1146      }
1147  
1148      return false;
1149  }
1150  
1151  /**
1152   * Update author group membership(s)
1153   *
1154   * @access public
1155   * @param   array       The array of groups the author should be a member of. All memberships that were present before and not contained in this array will be removed.
1156   * @param   int         The ID of the author to update
1157   * @param   boolean     If set to true, the groups can only be updated if the user has the adminUsersMaintainOthers privilege. If set to false, group memberships will be changable for any user.
1158   * @return
1159   */
1160  function serendipity_updateGroups($groups, $authorid, $apply_acl = true) {
1161      global $serendipity;
1162  
1163      if ($apply_acl && !serendipity_checkPermission('adminUsersMaintainOthers')) {
1164          return false;
1165      }
1166  
1167      serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}authorgroups WHERE authorid = " . (int)$authorid);
1168  
1169      foreach($groups AS $group) {
1170          serendipity_db_query("INSERT INTO {$serendipity['dbPrefix']}authorgroups (authorid, groupid) VALUES (" . (int)$authorid . ", " . (int)$group . ")");
1171      }
1172      return true;
1173  }
1174  
1175  /**
1176   * Returns all authorgroups that are available
1177   *
1178   * If a groupname is prefixed with "USERLEVEL_" then the constant of that name is used to
1179   * return the name of the group. This allows inserting the special groups Chief Editor, Editor
1180   * and admin and still being able to use multilingual names for these groups.
1181   *
1182   * @access public
1183   * @param   int     If set to an author ID value, only groups are fetched that this author is a member of. If set to false, all groups are returned, also those that the current user has no access to.
1184   * @return  array   An associative array of group names.
1185   */
1186  function &serendipity_getAllGroups($apply_ACL_user = false) {
1187      global $serendipity;
1188  
1189      if ($apply_ACL_user) {
1190          $groups =& serendipity_db_query("SELECT g.id   AS confkey,
1191                                                  g.name AS confvalue,
1192                                                  g.id   AS id,
1193                                                  g.name AS name
1194                                             FROM {$serendipity['dbPrefix']}authorgroups AS ag
1195                                  LEFT OUTER JOIN {$serendipity['dbPrefix']}groups AS g
1196                                               ON g.id = ag.groupid
1197                                            WHERE ag.authorid = " . (int)$apply_ACL_user . "
1198                                         ORDER BY g.name", false, 'assoc');
1199      } else {
1200          $groups =& serendipity_db_query("SELECT g.id   AS confkey,
1201                                                  g.name AS confvalue,
1202                                                  g.id   AS id,
1203                                                  g.name AS name
1204                                            FROM {$serendipity['dbPrefix']}groups AS g
1205                                        ORDER BY  g.name", false, 'assoc');
1206      }
1207      if (is_array($groups)) {
1208          foreach ($groups as $k => $v) {
1209              if ('USERLEVEL_' == substr($v['confvalue'], 0, 10)) {
1210                  $groups[$k]['confvalue'] = $groups[$k]['name'] = constant($v['confvalue']);
1211              }
1212          }
1213      }
1214      return $groups;
1215  }
1216  
1217  /**
1218   * Fetch the permissions of a certain group
1219   *
1220   * @access public
1221   * @param   int     The ID of the group that the permissions are fetched for
1222   * @return  array   The associative array of permissions of a group.
1223   */
1224  function &serendipity_fetchGroup($groupid) {
1225      global $serendipity;
1226  
1227      $conf = array();
1228      $groups =& serendipity_db_query("SELECT g.id        AS confkey,
1229                                              g.name      AS confvalue,
1230                                              g.id        AS id,
1231                                              g.name      AS name,
1232  
1233                                              gc.property AS property,
1234                                              gc.value    AS value
1235                                        FROM {$serendipity['dbPrefix']}groups AS g
1236                             LEFT OUTER JOIN {$serendipity['dbPrefix']}groupconfig AS gc
1237                                          ON g.id = gc.id
1238                                       WHERE g.id = " . (int)$groupid, false, 'assoc');
1239  
1240      if (is_array($groups)) {
1241          foreach($groups AS $group) {
1242              $conf[$group['property']] = $group['value'];
1243          }
1244      }
1245  
1246      // The following are unique
1247      $conf['name']      = $groups[0]['name'];
1248      $conf['id']        = $groups[0]['id'];
1249      $conf['confkey']   = $groups[0]['confkey'];
1250      $conf['confvalue'] = $groups[0]['confvalue'];
1251  
1252      return $conf;
1253  }
1254  
1255  /**
1256   * Gets all groups a user is a member of
1257   *
1258   * @access public
1259   * @param   int         The authorid to fetch groups for
1260   * @param   boolean     Indicate whether the original multi-dimensional DB result array shall be returned (FALSE) or if the array shall be flattened to be 1-dimensional (TRUE).
1261   * @return
1262   */
1263  function &serendipity_getGroups($authorid, $sequence = false) {
1264      global $serendipity;
1265  
1266      $_groups =& serendipity_db_query("SELECT g.id   AS confkey,
1267                                              g.name AS confvalue,
1268                                              g.id   AS id,
1269                                              g.name AS name
1270                                        FROM {$serendipity['dbPrefix']}authorgroups AS ag
1271                             LEFT OUTER JOIN {$serendipity['dbPrefix']}groups AS g
1272                                          ON g.id = ag.groupid
1273                                       WHERE ag.authorid = " . (int)$authorid, false, 'assoc');
1274      if (!is_array($_groups)) {
1275          $groups = array();
1276      } else {
1277          $groups =& $_groups;
1278      }
1279  
1280      if ($sequence) {
1281          $rgroups  = array();
1282          foreach($groups AS $grouprow) {
1283              $rgroups[] = $grouprow['confkey'];
1284          }
1285      } else {
1286          $rgroups =& $groups;
1287      }
1288  
1289      return $rgroups;
1290  }
1291  
1292  /**
1293   * Gets all author IDs of a specific group
1294   *
1295   * @access public
1296   * @param   int     The ID of the group to fetch the authors of
1297   * @return  array   The assotiative array of author IDs and names
1298   */
1299  function &serendipity_getGroupUsers($groupid) {
1300      global $serendipity;
1301  
1302      $groups =& serendipity_db_query("SELECT g.name     AS name,
1303                                              a.realname AS author,
1304                                              a.authorid AS id
1305                                        FROM {$serendipity['dbPrefix']}authorgroups AS ag
1306                             LEFT OUTER JOIN {$serendipity['dbPrefix']}groups AS g
1307                                          ON g.id = ag.groupid
1308                             LEFT OUTER JOIN {$serendipity['dbPrefix']}authors AS a
1309                                          ON ag.authorid = a.authorid
1310                                       WHERE ag.groupid = " . (int)$groupid, false, 'assoc');
1311      return $groups;
1312  }
1313  
1314  /**
1315   * Deletes a specific author group by ID
1316   *
1317   * @access public
1318   * @param   int     The group ID to delete
1319   * @return  boolean Return true if group could be deleted, false if unsufficient privileges.
1320   */
1321  function serendipity_deleteGroup($groupid) {
1322      global $serendipity;
1323  
1324      if (!serendipity_checkPermission('adminUsersGroups')) {
1325          return false;
1326      }
1327  
1328      if (!serendipity_checkPermission('adminUsersMaintainOthers')) {
1329          // Only groups should be accessible where a user has access rights.
1330          $my_groups =& serendipity_getGroups($serendipity['authorid'], true);
1331          if (!in_array($groupid, $my_groups)) {
1332              return false;
1333          }
1334      }
1335  
1336      serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}groups       WHERE id = " . (int)$groupid);
1337      serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}authorgroups WHERE groupid = " . (int)$groupid);
1338  
1339      return true;
1340  }
1341  
1342  /**
1343   * Creates a new author group
1344   *
1345   * @access public
1346   * @param   string      The name of the new group
1347   * @return  int         The id of the created group
1348   */
1349  function serendipity_addGroup($name) {
1350      global $serendipity;
1351  
1352      serendipity_db_query("INSERT INTO {$serendipity['dbPrefix']}groups (name) VALUES ('" . serendipity_db_escape_string($name) . "')");
1353      $gid = serendipity_db_insert_id('groups', 'id');
1354  
1355      return $gid;
1356  }
1357  
1358  /**
1359   * Returns a list of all existing permission names.
1360   *
1361   * Additional plugins might insert specific properties into the groupconfig database to
1362   * handle their own privileges. This call returns an array of all available permission names
1363   * so that it can be intersected with the list of internal permission names (serendipity_getPermissionNames())
1364   * and the be distincted.
1365   *
1366   * @access public
1367   * @see serendipity_getPermissionNames()
1368   * @return  array   associative array of all available permission names
1369   */
1370  function &serendipity_getDBPermissionNames() {
1371      global $serendipity;
1372  
1373      $config =& serendipity_db_query("SELECT property FROM {$serendipity['dbPrefix']}groupconfig GROUP BY property ORDER BY property", false, 'assoc');
1374  
1375      return $config;
1376  }
1377  
1378  /**
1379   * Gets the list of all Permissions and merges the two arrays
1380   *
1381   * The first call will fetch all existing permission names of the database. Then it will
1382   * fetch the list of defined internal permission names, which has an array with extra information
1383   * (like userlevel). This array will be merged with those permission names only found in the
1384   * database. The returned array will then hold as much information about permission names as is
1385   * available.
1386   * TODO Might need further pushing and/or an event hook so that external plugins using the
1387   * permission system can inject specific information into the array
1388   *
1389   * @access public
1390   * @see serendipity_getPermissionNames()
1391   * @see serendipity_getDBPermissionNames()
1392   * @return  array   Returns the array with all information about all permission names
1393   */
1394  function &serendipity_getAllPermissionNames() {
1395      global $serendipity;
1396  
1397      $DBperms =& serendipity_getDBPermissionNames();
1398      $perms   =& serendipity_getPermissionNames();
1399  
1400      foreach($DBperms AS $perm) {
1401          if (!isset($perms[$perm['property']])) {
1402              $perms[$perm['property']] = array();
1403          }
1404      }
1405  
1406      return $perms;
1407  }
1408  
1409  /**
1410   * Checks if two users are members of the same group
1411   *
1412   * This function will retrieve all group memeberships of a
1413   * foreign user ($checkuser) and yourself ($myself). Then it
1414   * will check if there is any group membership that those
1415   * two users have in common.
1416   * It can be used for detecting if a different author should
1417   * be allowed to access your entries, because he's in the same
1418   * group, for example.
1419   *
1420   * @access public
1421   * @param   int     ID of the first author to check group memberships
1422   * @param   int     ID of the second author to check group memberships
1423   * @return  boolea  True if a membership intersects, false if not
1424   */
1425  function serendipity_intersectGroup($checkuser = null, $myself = null) {
1426      global $serendipity;
1427  
1428      if ($myself === null) {
1429          $myself = $serendipity['authorid'];
1430      }
1431  
1432      $my_groups  =& serendipity_getGroups($myself, true);
1433      $his_groups =& serendipity_getGroups($checkuser, true);
1434  
1435      foreach($his_groups AS $his_group) {
1436          if (in_array($his_group, $my_groups)) {
1437              return true;
1438          }
1439      }
1440  
1441      return false;
1442  }
1443  
1444  /**
1445   * Updates the configuration of permissions of a specific group
1446   *
1447   * This function ensures that a group can only be updated from users that have permissions to do so.
1448   * @access public
1449   * @param   int     The ID of the group to update
1450   * @param   array   The associative array of permission names
1451   * @param   array   The associative array of new values for the permissions. Needs the same associative keys like the $perms array.
1452   * @param   bool    Indicates if an all new privilege should be inserted (true) or if an existing privilege is going to be checked
1453   * @param   array   The associative array of plugin permission names
1454   * @param   array   The associative array of plugin permission hooks
1455   * @return true
1456   */
1457  function serendipity_updateGroupConfig($groupid, &$perms, &$values, $isNewPriv = false, $forbidden_plugins = null, $forbidden_hooks = null) {
1458      global $serendipity;
1459  
1460      if (!serendipity_checkPermission('adminUsersGroups')) {
1461          return false;
1462      }
1463  
1464      if (!serendipity_checkPermission('adminUsersMaintainOthers')) {
1465          // Only groups should be accessible where a user has access rights.
1466          $my_groups =& serendipity_getGroups($serendipity['authorid'], true);
1467          if (!in_array($groupid, $my_groups)) {
1468              return false;
1469          }
1470      }
1471  
1472      $storage =& serendipity_fetchGroup($groupid);
1473  
1474      serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}groupconfig WHERE id = " . (int)$groupid);
1475      foreach ($perms AS $perm => $userlevels) {
1476          if (substr($perm, 0, 2) == 'f_') {
1477              continue;
1478          }
1479  
1480          if (isset($values[$perm]) && $values[$perm] == 'true') {
1481              $value = 'true';
1482          } elseif (isset($values[$perm]) && $values[$perm] === 'false') {
1483              $value = 'false';
1484          } elseif (isset($values[$perm])) {
1485              $value = $values[$perm];
1486          } else {
1487              $value = 'false';
1488          }
1489  
1490          if ($isNewPriv == false && !serendipity_checkPermission($perm)) {
1491              if (!isset($storage[$perm])) {
1492                  $value = 'false';
1493              } else {
1494                  $value = $storage[$perm];
1495              }
1496          }
1497  
1498          serendipity_db_query(
1499              sprintf("INSERT INTO {$serendipity['dbPrefix']}groupconfig (id, property, value) VALUES (%d, '%s', '%s')",
1500                  (int)$groupid,
1501                  serendipity_db_escape_string($perm),
1502                  serendipity_db_escape_string($value)
1503              )
1504          );
1505      }
1506  
1507      if (is_array($forbidden_plugins)) {
1508          foreach($forbidden_plugins AS $plugid) {
1509              serendipity_db_query(
1510                  sprintf("INSERT INTO {$serendipity['dbPrefix']}groupconfig (id, property, value) VALUES (%d, '%s', 'true')",
1511                      (int)$groupid,
1512                      serendipity_db_escape_string('f_' . urldecode($plugid))
1513                  )
1514              );
1515          }
1516      }
1517  
1518      if (is_array($forbidden_hooks)) {
1519          foreach($forbidden_hooks AS $hook) {
1520              serendipity_db_query(
1521                  sprintf("INSERT INTO {$serendipity['dbPrefix']}groupconfig (id, property, value) VALUES (%d, '%s', 'true')",
1522                      (int)$groupid,
1523                      serendipity_db_escape_string('f_' . urldecode($hook))
1524                  )
1525              );
1526          }
1527      }
1528  
1529      serendipity_db_query("UPDATE {$serendipity['dbPrefix']}groups SET name = '" . serendipity_db_escape_string($values['name']) . "' WHERE id = " . (int)$groupid);
1530  
1531      if (is_array($values['members'])) {
1532          serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}authorgroups WHERE groupid = " . (int)$groupid);
1533          foreach($values['members'] AS $member) {
1534              serendipity_db_query(
1535                  sprintf("INSERT INTO {$serendipity['dbPrefix']}authorgroups (groupid, authorid) VALUES (%d, %d)",
1536                      (int)$groupid,
1537                      (int)$member
1538                  )
1539              );
1540          }
1541      }
1542  
1543      return true;
1544  }
1545  
1546  /**
1547   * Adds a default internal group (Editor, Chief Editor, Admin)
1548   *
1549   * @access public
1550   * @param   string  The name of the group to insert
1551   * @param   int     The userlevel that represents this group (0|1|255 for Editor/Chief/Admin).
1552   * @return true
1553   */
1554  function serendipity_addDefaultGroup($name, $level) {
1555      global $serendipity;
1556  
1557      static $perms = null;
1558      if ($perms === null) {
1559          $perms = serendipity_getPermissionNames();
1560      }
1561  
1562      serendipity_db_query("INSERT INTO {$serendipity['dbPrefix']}groups (name) VALUES ('" . serendipity_db_escape_string($name) . "')");
1563      $gid = (int)serendipity_db_insert_id('groups', 'id');
1564      serendipity_db_query("INSERT INTO {$serendipity['dbPrefix']}groupconfig (id, property, value) VALUES ($gid, 'userlevel', '" . (int)$level . "')");
1565  
1566      $authors = serendipity_db_query("SELECT * FROM {$serendipity['dbPrefix']}authors WHERE userlevel = " . (int)$level);
1567  
1568      if (is_array($authors)) {
1569          foreach($authors AS $author) {
1570              serendipity_db_query("INSERT INTO {$serendipity['dbPrefix']}authorgroups (authorid, groupid) VALUES ('{$author['authorid']}', '$gid')");
1571          }
1572      }
1573  
1574      foreach($perms AS $permName => $permArray) {
1575          if (in_array($level, $permArray)) {
1576              serendipity_db_query("INSERT INTO {$serendipity['dbPrefix']}groupconfig (id, property, value) VALUES ($gid, '" . serendipity_db_escape_string($permName) . "', 'true')");
1577          } else {
1578              serendipity_db_query("INSERT INTO {$serendipity['dbPrefix']}groupconfig (id, property, value) VALUES ($gid, '" . serendipity_db_escape_string($permName) . "', 'false')");
1579          }
1580      }
1581  
1582      return true;
1583  }
1584  
1585  /**
1586   * Allow access to a specific item (category or entry) for a specific usergroup
1587   *
1588   * ACL are Access Control Lists. They indicate which read/write permissions a
1589   * specific item has for specific usergroups.
1590   * An artifact in terms of Serendipity can be either a category or an entry, or
1591   * anything beyond that for future compatibility.
1592   * This function sets up the ACLs.
1593   *
1594   * @access public
1595   * @param   int     The ID of the artifact to set the access
1596   * @param   string  The type of an artifact (category|entry)
1597   * @param   string  The type of access to grant (read|write)
1598   * @param   array   The ID of the group to grant access to
1599   * @param   string  A variable option for an artifact
1600   * @return  boolean True if ACL was applied, false if not.
1601   */
1602  function serendipity_ACLGrant($artifact_id, $artifact_type, $artifact_mode, $groups, $artifact_index = '') {
1603      global $serendipity;
1604  
1605      if (empty($groups) || !is_array($groups)) {
1606          return false;
1607      }
1608  
1609      // Delete all old existing relations.
1610      serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}access
1611                                  WHERE artifact_id    = " . (int)$artifact_id . "
1612                                    AND artifact_type  = '" . serendipity_db_escape_string($artifact_type) . "'
1613                                    AND artifact_mode  = '" . serendipity_db_escape_string($artifact_mode) . "'
1614                                    AND artifact_index = '" . serendipity_db_escape_string($artifact_index) . "'");
1615  
1616      $data = array(
1617          'artifact_id'    => (int)$artifact_id,
1618          'artifact_type'  => $artifact_type,
1619          'artifact_mode'  => $artifact_mode,
1620          'artifact_index' => $artifact_index
1621      );
1622  
1623      if (count($data) < 1) {
1624          return true;
1625      }
1626  
1627      foreach($groups AS $group) {
1628          $data['groupid'] = $group;
1629          serendipity_db_insert('access', $data);
1630      }
1631  
1632      return true;
1633  }
1634  
1635  /**
1636   * Checks if a specific item (category or entry) can be accessed by a specific usergroup
1637   *
1638   * ACL are Access Control Lists. They indicate which read/write permissions a
1639   * specific item has for specific usergroups.
1640   * An artifact in terms of Serendipity can be either a category or an entry, or
1641   * anything beyond that for future compatibility.
1642   * This function retrieves the ACLs.
1643   *
1644   * @access public
1645   * @param   int     The ID of the artifact to set the access
1646   * @param   string  The type of an artifact (category|entry)
1647   * @param   string  The type of access to check for (read|write)
1648   * @param   string  A variable option for an artifact
1649   * @return  array   Returns an array of all groups that are allowed for this kind of access. You can then check if you are the member of any of the groups returned here.
1650   */
1651  function serendipity_ACLGet($artifact_id, $artifact_type, $artifact_mode, $artifact_index = '') {
1652      global $serendipity;
1653  
1654      $sql = "SELECT groupid, artifact_index FROM {$serendipity['dbPrefix']}access
1655                      WHERE artifact_type  = '" . serendipity_db_escape_string($artifact_type) . "'
1656                        AND artifact_id    = '" . (int)$artifact_id . "'
1657                        AND artifact_mode  = '" . serendipity_db_escape_string($artifact_mode) . "'
1658                        AND artifact_index = '" . serendipity_db_escape_string($artifact_index) . "'";
1659      $rows =& serendipity_db_query($sql, false, 'assoc');
1660  
1661      if (!is_array($rows)) {
1662          return false;
1663      }
1664  
1665      $acl = array();
1666      foreach($rows AS $row) {
1667          $acl[$row['groupid']] = $row['artifact_index'];
1668      }
1669  
1670      return $acl;
1671  }
1672  
1673  /**
1674   * Checks if a specific item (category or entry) can be accessed by a specific Author
1675   *
1676   * ACL are Access Control Lists. They indicate which read/write permissions a
1677   * specific item has for specific usergroups.
1678   * An artifact in terms of Serendipity can be either a category or an entry, or
1679   * anything beyond that for future compatibility.
1680   * This function retrieves the ACLs for a specific user.
1681   *
1682   * @access public
1683   * @param   int     The ID of the author to check against.
1684   * @param   int     The ID of the artifact to set the access
1685   * @param   string  The type of an artifact ('category', more to come)
1686   * @param   string  The type of access to check for (read|write)
1687   * @return  boolean Returns true, if the author has access to this artifact. False if not.
1688   */
1689  function serendipity_ACLCheck($authorid, $artifact_id, $artifact_type, $artifact_mode) {
1690      global $serendipity;
1691  
1692      $artifact_sql = array();
1693  
1694      // TODO: If more artifact_types are available, the JOIN needs to be edited so that the first AND portion is not required, and the join is fully made on that conditiion.
1695      switch($artifact_type) {
1696          default:
1697          case 'category':
1698              $artifact_sql['unique']= "atf.categoryid";
1699              $artifact_sql['cond']  = "atf.categoryid = " . (int)$artifact_id;
1700              $artifact_sql['where'] = "     ag.groupid = a.groupid
1701                                          OR a.groupid  = 0
1702                                          OR (a.artifact_type IS NULL AND (atf.authorid = " . (int)$authorid . " OR atf.authorid = 0 OR atf.authorid IS NULL))";
1703              $artifact_sql['table'] = 'category';
1704      }
1705  
1706      $sql = "SELECT {$artifact_sql['unique']} AS result
1707                FROM {$serendipity['dbPrefix']}{$artifact_sql['table']} AS atf
1708     LEFT OUTER JOIN {$serendipity['dbPrefix']}authorgroups AS ag
1709                  ON ag.authorid = ". (int)$authorid . "
1710     LEFT OUTER JOIN {$serendipity['dbPrefix']}access AS a
1711                  ON (    a.artifact_type = '" . serendipity_db_escape_string($artifact_type) . "'
1712                      AND a.artifact_id   = " . (int)$artifact_id . "
1713                      AND a.artifact_mode = '" . serendipity_db_escape_string($artifact_mode) . "'
1714                     )
1715  
1716               WHERE {$artifact_sql['cond']}
1717                 AND ( {$artifact_sql['where']} )
1718            GROUP BY result";
1719  
1720      $res =& serendipity_db_query($sql, true, 'assoc');
1721      if (is_array($res) && !empty($res['result'])) {
1722          return true;
1723      }
1724  
1725      return false;
1726  }
1727  
1728  /**
1729   * Prepares a SQL statement to be used in queries that should be ACL restricted.
1730   *
1731   * ACL are Access Control Lists. They indicate which read/write permissions a
1732   * specific item has for specific usergroups.
1733   * An artifact in terms of Serendipity can be either a category or an entry, or
1734   * anything beyond that for future compatibility.
1735   * This function evaluates and applies the SQL statements required.
1736   * It is currently only written for retrieving Category ACLs.
1737   * All of the SQL code that will be used in serendipity_fetchEntries will be stored
1738   * within the referenced $cond array.
1739   *
1740   * @access private
1741   * @param   array       Associative array that holds the SQL part array to be used in other functions like serendipity_fetchEntries()
1742   * @param   boolean     Some queries do not need to joins categories. When ACLs need to be applied, this column is required, so if $append_category is set to true it will perform this missing JOIN.
1743   * @param   string      The ACL type ('category', 'directory')
1744   * @param   string      ACL mode
1745   * @return  true        True if ACLs were applied, false if not.
1746   */
1747  function serendipity_ACL_SQL(&$cond, $append_category = false, $type = 'category', $mode = 'read') {
1748      global $serendipity;
1749  
1750      // A global configuration item controls whether the blog should apply ACLs or not!
1751      if (!isset($serendipity['enableACL']) || $serendipity['enableACL'] == true) {
1752  
1753          // If the user is logged in, we retrieve his authorid for the upcoming checks
1754          if ($_SESSION['serendipityAuthedUser'] === true) {
1755              $read_id = (int)$serendipity['authorid'];
1756              $read_id_sql = 'acl_a.groupid OR acl_acc.groupid = 0';
1757          } else {
1758              // "0" as category property counts as "anonymous viewers"
1759              $read_id     = 0;
1760              $read_id_sql = 0;
1761          }
1762  
1763          if ($append_category) {
1764              if ($append_category !== 'limited') {
1765                  $cond['joins'] .= " LEFT JOIN {$serendipity['dbPrefix']}entrycat ec
1766                                             ON e.id = ec.entryid";
1767              }
1768  
1769              $cond['joins'] .= " LEFT JOIN {$serendipity['dbPrefix']}category c
1770                                         ON ec.categoryid = c.categoryid";
1771          }
1772  
1773          switch($type) {
1774              case 'directory':
1775                  $sql_artifact_column = 'i.path IS NULL OR
1776                                          acl_acc.groupid IS NULL';
1777                  $sql_artifact = 'AND acl_acc.artifact_index = i.path';
1778                  break;
1779  
1780              case 'category':
1781                  $sql_artifact_column = 'c.categoryid IS NULL';
1782                  $sql_artifact = 'AND acl_acc.artifact_id   = c.categoryid';
1783                  break;
1784          }
1785  
1786          $cond['joins'] .= " LEFT JOIN {$serendipity['dbPrefix']}authorgroups AS acl_a
1787                                     ON acl_a.authorid = " . $read_id . "
1788                              LEFT JOIN {$serendipity['dbPrefix']}access AS acl_acc
1789                                     ON (    acl_acc.artifact_mode = '" . $mode . "'
1790                                         AND acl_acc.artifact_type = '" . $type . "'
1791                                         " . $sql_artifact . "
1792                                        )";
1793  
1794          if (empty($cond['and'])) {
1795              $cond['and'] .= ' WHERE ';
1796          } else {
1797              $cond['and'] .= ' AND ';
1798          }
1799  
1800          // When in Admin-Mode, apply readership permissions.
1801          $cond['and'] .= "    (
1802                                   " . $sql_artifact_column . "
1803                                   OR ( acl_acc.groupid = " . $read_id_sql . ")
1804                                   OR ( acl_acc.artifact_id IS NULL
1805                                        " . (isset($serendipity['GET']['adminModule']) &&
1806                                             $serendipity['GET']['adminModule'] == 'entries' &&
1807                                             !serendipity_checkPermission('adminEntriesMaintainOthers')
1808                                          ? "AND (c.authorid IS NULL OR c.authorid = 0 OR c.authorid = " . $read_id . ")"
1809                                          : "") . "
1810                                      )
1811                                 )";
1812          return true;
1813      }
1814  
1815      return false;
1816  }
1817  
1818  /**
1819   * Check for Cross-Site-Request-Forgery attacks because of missing HTTP Referer
1820   *
1821   * http://de.wikipedia.org/wiki/XSRF
1822   * This function checks the HTTP referer, and if it is part of the current Admin panel.
1823   *
1824   * @access public
1825   * @return  Returns true if XSRF was detected, false if not. The script should abort, if TRUE is returned.
1826   */
1827  function serendipity_checkXSRF() {
1828      global $serendipity;
1829  
1830      // If no module was requested, the user has just logged in and no action will be performed.
1831      if (empty($serendipity['GET']['adminModule'])) {
1832          return false;
1833      }
1834  
1835      // The referrer was empty. Deny access.
1836      if (empty($_SERVER['HTTP_REFERER'])) {
1837          echo serendipity_reportXSRF(1, true, true);
1838          return false;
1839      }
1840  
1841      // Parse the Referrer host. Abort if not parseable.
1842      $hostinfo = @parse_url($_SERVER['HTTP_REFERER']);
1843      if (!is_array($hostinfo)) {
1844          echo serendipity_reportXSRF(2, true, true);
1845          return true;
1846      }
1847  
1848      // Get the server against we will perform the XSRF check.
1849      $server = '';
1850      if (empty($_SERVER['HTTP_HOST'])) {
1851          $myhost = @parse_url($serendipity['baseURL']);
1852          if (is_array($myhost)) {
1853              $server = $myhost['host'];
1854          }
1855      } else {
1856          $server = $_SERVER['HTTP_HOST'];
1857      }
1858  
1859      // If the current server is different than the referred server, deny access.
1860      if ($hostinfo['host'] != $server) {
1861          echo serendipity_reportXSRF(3, true, true);
1862          return true;
1863      }
1864  
1865      return false;
1866  }
1867  
1868  /**
1869   * Report a XSRF attempt to the Serendipity Interface
1870   *
1871   * http://de.wikipedia.org/wiki/XSRF
1872   *
1873   * LONG
1874   *
1875   * @access public
1876   * @see serendipity_checkXSRF()
1877   * @param   string      The type of XSRF check that got hit. Used for CSS formatting.
1878   * @param   boolean     If true, the XSRF error should be fatal
1879   * @param   boolean     If true, tell Serendipity to check the $serendipity['referrerXSRF'] config option to decide if an error should be reported or not.
1880   * @return  string      Returns the HTML error report
1881   */
1882  function serendipity_reportXSRF($type = 0, $reset = true, $use_config = false) {
1883      global $serendipity;
1884  
1885      // Set this in your serendipity_config_local.inc.php if you want HTTP Referrer blocking:
1886      // $serendipity['referrerXSRF'] = true;
1887  
1888      $string = '<div class="serendipityAdminMsgError XSRF_' . $type . '"><img style="width: 22px; height: 22px; border: 0px; padding-right: 4px; vertical-align: middle" src="' . serendipity_getTemplateFile('admin/img/admin_msg_error.png') . '" alt="" />' . ERROR_XSRF . '</div>';
1889      if ($reset) {
1890          // Config key "referrerXSRF" can be set to enable blocking based on HTTP Referrer. Recommended for Paranoia.
1891          if (($use_config && isset($serendipity['referrerXSRF']) && $serendipity['referrerXSRF']) || $use_config === false) {
1892              $serendipity['GET']['adminModule'] = '';
1893          } else {
1894              // Paranoia not enabled. Do not report XSRF.
1895              $string = '';
1896          }
1897      }
1898      return $string;
1899  }
1900  
1901  /**
1902   * Prevent XSRF attacks by checking for a form token
1903   *
1904   * http://de.wikipedia.org/wiki/XSRF
1905   *
1906   * This function checks, if a valid Form token was posted to the site.
1907   *
1908   * @access public
1909   * @see serendipity_setFormToken()
1910   * @return  boolean     Returns true, if XSRF attempt was found and the token was missing
1911   */
1912  function serendipity_checkFormToken($output = true) {
1913      global $serendipity;
1914  
1915      $token = '';
1916      if (!empty($serendipity['POST']['token'])) {
1917          $token = $serendipity['POST']['token'];
1918      } elseif (!empty($serendipity['GET']['token'])) {
1919          $token = $serendipity['GET']['token'];
1920      }
1921  
1922      if (empty($token)) {
1923          if ($output) echo serendipity_reportXSRF('token', false);
1924          return false;
1925      }
1926  
1927      if ($token != md5(session_id()) &&
1928          $token != md5($serendipity['COOKIE']['old_session'])) {
1929          if ($output) echo serendipity_reportXSRF('token', false);
1930          return false;
1931      }
1932  
1933      return true;
1934  }
1935  
1936  /**
1937   * Prevent XSRF attacks by setting a form token within HTTP Forms
1938   *
1939   * http://de.wikipedia.org/wiki/XSRF
1940   *
1941   * By inserting a unique Form token that holds the session id, all requests
1942   * to serendipity HTTP forms can only be processed if the token is present.
1943   * This effectively makes XSRF attacks impossible. Only bundled with XSS
1944   * attacks it can be bypassed.
1945   *
1946   * 'form' type tokens can be embedded within the <form> script.
1947   * 'url' type token can be embedded within HTTP GET calls.
1948   *
1949   * @access public
1950   * @param   string      The type of token to return (form|url|plain)
1951   * @return  string      Returns the form token to be used in your functions
1952   */
1953  function serendipity_setFormToken($type = 'form') {
1954      global $serendipity;
1955  
1956      if ($type == 'form') {
1957          return '<input type="hidden" name="serendipity[token]" value="' . md5(session_id()) . '" />';
1958      } elseif ($type == 'url') {
1959          return 'serendipity[token]=' . md5(session_id());
1960      } else {
1961          return md5(session_id());
1962      }
1963  }
1964  
1965  /**
1966   * Load available/configured options for a specific theme (through config.inc.php of a template directory)
1967   * into an array.
1968   *
1969   * @param   array   Referenced variable coming from the config.inc.php file, where the config values will be stored in
1970   * @return  array   Final return array with default values
1971   */
1972  function &serendipity_loadThemeOptions(&$template_config, $okey = '') {
1973      global $serendipity;
1974      
1975      if (empty($okey)) {
1976          $okey = $serendipity['template'];
1977      }
1978  
1979      $_template_vars =& serendipity_db_query("SELECT name, value FROM {$serendipity['dbPrefix']}options
1980                                               WHERE okey = 't_" . serendipity_db_escape_string($okey) . "'", false, 'assoc', false, 'name', 'value');
1981      if (!is_array($_template_vars)) {
1982          $template_vars = array();
1983      } else {
1984          $template_vars =& $_template_vars;
1985      }
1986  
1987      foreach($template_config AS $key => $item) {
1988          if (!isset($template_vars[$item['var']])) {
1989              $template_vars[$item['var']] = $item['default'];
1990          }
1991      }
1992  
1993      return $template_vars;
1994  }
1995  
1996  /**
1997   * Check if a member of a group has permissions to execute a plugin
1998   *
1999   * @param string    Pluginname
2000   * @param int       ID of the group of which the members should be checked
2001   * @return boolean
2002   */
2003  function serendipity_hasPluginPermissions($plugin, $groupid = null) {
2004      static $forbidden = null;
2005      global $serendipity;
2006  
2007      if (empty($serendipity['authorid'])) {
2008          return true;
2009      }
2010  
2011      if ($forbidden === null || ($groupid !== null && !isset($forbidden[$groupid]))) {
2012          $forbidden = array();
2013          
2014          if ($groupid === null) {
2015              $groups =& serendipity_checkPermission(null, null, 'all');
2016          } else {
2017              $groups = array($groupid => serendipity_fetchGroup($groupid));
2018          }
2019  
2020          foreach($groups AS $idx => $group) {
2021              if ($idx == 'membership') {
2022                  continue;
2023              }
2024              foreach($group AS $key => $val) {
2025                  if (substr($key, 0, 2) == 'f_') {
2026                      $forbidden[$groupid][$key] = true;
2027                  }
2028              }
2029          }
2030      }
2031  
2032      if (isset($forbidden[$groupid]['f_' . $plugin])) {
2033          return false;
2034      } else {
2035          return true;
2036      }
2037  }
2038  
2039  /* vim: set sts=4 ts=4 expandtab : */


Généré le : Sat Nov 24 09:00:37 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics