includes/sessions.php
changeset 286 b2f985e4cef3
parent 270 5bcdee999015
child 289 2b60c89dc27f
equal deleted inserted replaced
285:7846d45bd250 286:b2f985e4cef3
   148   /**
   148   /**
   149    * Regex that defines a valid username, minus the ^ and $, these are added later
   149    * Regex that defines a valid username, minus the ^ and $, these are added later
   150    * @var string
   150    * @var string
   151    */
   151    */
   152    
   152    
   153   //var $valid_username = '([A-Za-z0-9 \!\@\(\)-]+)';
       
   154   var $valid_username = '([^<>&\?\'"%\n\r\t\a\/]+)';
   153   var $valid_username = '([^<>&\?\'"%\n\r\t\a\/]+)';
   155    
   154    
   156   /**
   155   /**
   157    * What we're allowed to do as far as permissions go. This changes based on the value of the "auth" URI param.
   156    * What we're allowed to do as far as permissions go. This changes based on the value of the "auth" URI param.
   158    * @var string
   157    * @var string
   280     {
   279     {
   281       if(is_writable(ENANO_ROOT.'/config.php'))
   280       if(is_writable(ENANO_ROOT.'/config.php'))
   282       {
   281       {
   283         // Generate and stash a private key
   282         // Generate and stash a private key
   284         // This should only happen during an automated silent gradual migration to the new encryption platform.
   283         // This should only happen during an automated silent gradual migration to the new encryption platform.
   285         $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
   284         $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
   286         $this->private_key = $aes->gen_readymade_key();
   285         $this->private_key = $aes->gen_readymade_key();
   287         
   286         
   288         $config = file_get_contents(ENANO_ROOT.'/config.php');
   287         $config = file_get_contents(ENANO_ROOT.'/config.php');
   289         if(!$config)
   288         if(!$config)
   290         {
   289         {
   498             $super = $this->compat_validate_session($key);
   497             $super = $this->compat_validate_session($key);
   499           }
   498           }
   500           else
   499           else
   501           {
   500           {
   502             $key = strrev($_REQUEST['auth']);
   501             $key = strrev($_REQUEST['auth']);
   503             $super = $this->validate_session($key);
   502             if ( !empty($key) && ( strlen($key) / 2 ) % 4 == 0 )
       
   503             {
       
   504               $super = $this->validate_session($key);
       
   505             }
   504           }
   506           }
   505           if(is_array($super))
   507           if(is_array($super))
   506           {
   508           {
   507             $this->auth_level = intval($super['auth_level']);
   509             $this->auth_level = intval($super['auth_level']);
   508             $this->sid_super = $_REQUEST['auth'];
   510             $this->sid_super = $_REQUEST['auth'];
   516       $this->register_guest_session();
   518       $this->register_guest_session();
   517     }
   519     }
   518     if(!$this->compat)
   520     if(!$this->compat)
   519     {
   521     {
   520       // init groups
   522       // init groups
   521       $q = $this->sql('SELECT g.group_name,g.group_id,m.is_mod FROM '.table_prefix.'groups AS g
   523       $q = $this->sql('SELECT g.group_name,g.group_id,m.is_mod FROM '.table_prefix.'groups AS g' . "\n"
   522           LEFT JOIN '.table_prefix.'group_members AS m
   524         . '  LEFT JOIN '.table_prefix.'group_members AS m' . "\n"
   523             ON g.group_id=m.group_id
   525         . '    ON g.group_id=m.group_id' . "\n"
   524           WHERE ( m.user_id='.$this->user_id.' 
   526         . '  WHERE ( m.user_id='.$this->user_id.'' . "\n" 
   525             OR g.group_name=\'Everyone\')
   527         . '    OR g.group_name=\'Everyone\')' . "\n"
   526             ' . ( enano_version() == '1.0RC1' ? '' : 'AND ( m.pending != 1 OR m.pending IS NULL )' ) . '
   528         . '    ' . ( enano_version() == '1.0RC1' ? '' : 'AND ( m.pending != 1 OR m.pending IS NULL )' ) . '' . "\n"
   527           ORDER BY group_id ASC;'); // Make sure "Everyone" comes first so the permissions can be overridden
   529         . '  ORDER BY group_id ASC;'); // Make sure "Everyone" comes first so the permissions can be overridden
   528       if($row = $db->fetchrow())
   530       if($row = $db->fetchrow())
   529       {
   531       {
   530         do {
   532         do {
   531           $this->groups[$row['group_id']] = $row['group_name'];
   533           $this->groups[$row['group_id']] = $row['group_name'];
   532           $this->group_mod[$row['group_id']] = ( intval($row['is_mod']) == 1 );
   534           $this->group_mod[$row['group_id']] = ( intval($row['is_mod']) == 1 );
   564     global $db, $session, $paths, $template, $plugins; // Common objects
   566     global $db, $session, $paths, $template, $plugins; // Common objects
   565     
   567     
   566     $privcache = $this->private_key;
   568     $privcache = $this->private_key;
   567     
   569     
   568     // Instanciate the Rijndael encryption object
   570     // Instanciate the Rijndael encryption object
   569     $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
   571     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
   570     
   572     
   571     // Fetch our decryption key
   573     // Fetch our decryption key
   572     
   574     
   573     $aes_key = $this->fetch_public_key($aes_key_id);
   575     $aes_key = $this->fetch_public_key($aes_key_id);
   574     if(!$aes_key)
   576     if(!$aes_key)
   712     {
   714     {
   713       return $this->login_compat($username, $pass_hashed, $level);
   715       return $this->login_compat($username, $pass_hashed, $level);
   714     }
   716     }
   715     
   717     
   716     // Instanciate the Rijndael encryption object
   718     // Instanciate the Rijndael encryption object
   717     $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
   719     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
   718     
   720     
   719     // Initialize our success switch
   721     // Initialize our success switch
   720     $success = false;
   722     $success = false;
   721     
   723     
   722     // Retrieve the real password from the database
   724     // Retrieve the real password from the database
   860     
   862     
   861     // Unencrypted session key
   863     // Unencrypted session key
   862     $session_key = "u=$username;p=$passha1;s=$salt";
   864     $session_key = "u=$username;p=$passha1;s=$salt";
   863     
   865     
   864     // Encrypt the key
   866     // Encrypt the key
   865     $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
   867     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
   866     $session_key = $aes->encrypt($session_key, $this->private_key, ENC_HEX);
   868     $session_key = $aes->encrypt($session_key, $this->private_key, ENC_HEX);
   867     
   869     
   868     // If we're registering an elevated-privilege key, it needs to be on GET
   870     // If we're registering an elevated-privilege key, it needs to be on GET
   869     if($level > USER_LEVEL_MEMBER)
   871     if($level > USER_LEVEL_MEMBER)
   870     {
   872     {
   966     $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE, true);
   968     $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE, true);
   967     $decrypted_key = $aes->decrypt($key, $this->private_key, ENC_HEX);
   969     $decrypted_key = $aes->decrypt($key, $this->private_key, ENC_HEX);
   968     
   970     
   969     if ( !$decrypted_key )
   971     if ( !$decrypted_key )
   970     {
   972     {
   971       die_semicritical('AES encryption error', '<p>Something went wrong during the AES decryption process.</p><pre>'.print_r($decrypted_key, true).'</pre>');
   973       // die_semicritical('AES encryption error', '<p>Something went wrong during the AES decryption process.</p><pre>'.print_r($decrypted_key, true).'</pre>');
       
   974       return false;
   972     }
   975     }
   973     
   976     
   974     $n = preg_match('/^u='.$this->valid_username.';p=([A-Fa-f0-9]+?);s=([A-Fa-f0-9]+?)$/', $decrypted_key, $keydata);
   977     $n = preg_match('/^u='.$this->valid_username.';p=([A-Fa-f0-9]+?);s=([A-Fa-f0-9]+?)$/', $decrypted_key, $keydata);
   975     if($n < 1)
   978     if($n < 1)
   976     {
   979     {
   977       // echo '(debug) $session->validate_session: Key does not match regex<br />Decrypted key: '.$decrypted_key;
   980       // echo '(debug) $session->validate_session: Key does not match regex<br />Decrypted key: '.$decrypted_key;
   978       return false;
   981       return false;
   979     }
   982     }
   980     $keyhash = md5($key);
   983     $keyhash = md5($key);
   981     $salt = $db->escape($keydata[3]);
   984     $salt = $db->escape($keydata[3]);
   982     $query = $db->sql_query('SELECT u.user_id AS uid,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms,x.* FROM '.table_prefix.'session_keys AS k
   985     // using a normal call to $db->sql_query to avoid failing on errors here
   983                                LEFT JOIN '.table_prefix.'users AS u
   986     $query = $db->sql_query('SELECT u.user_id AS uid,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,' . "\n"
   984                                  ON ( u.user_id=k.user_id )
   987                              . '    u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms,' . "\n"
   985                                LEFT JOIN '.table_prefix.'users_extra AS x
   988                              . '    x.* FROM '.table_prefix.'session_keys AS k' . "\n"
   986                                  ON ( u.user_id=x.user_id OR x.user_id IS NULL )
   989                              . '  LEFT JOIN '.table_prefix.'users AS u' . "\n"
   987                                LEFT JOIN '.table_prefix.'privmsgs AS p
   990                              . '    ON ( u.user_id=k.user_id )' . "\n"
   988                                  ON ( p.message_to=u.username AND p.message_read=0 )
   991                              . '  LEFT JOIN '.table_prefix.'users_extra AS x' . "\n"
   989                                WHERE k.session_key=\''.$keyhash.'\'
   992                              . '    ON ( u.user_id=x.user_id OR x.user_id IS NULL )' . "\n"
   990                                  AND k.salt=\''.$salt.'\'
   993                              . '  LEFT JOIN '.table_prefix.'privmsgs AS p' . "\n"
   991                                GROUP BY u.user_id;');
   994                              . '    ON ( p.message_to=u.username AND p.message_read=0 )' . "\n"
       
   995                              . '  WHERE k.session_key=\''.$keyhash.'\'' . "\n"
       
   996                              . '    AND k.salt=\''.$salt.'\'' . "\n"
       
   997                              . '  GROUP BY u.user_id;');
   992     if ( !$query )
   998     if ( !$query )
   993     {
   999     {
   994       $query = $this->sql('SELECT u.user_id AS uid,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms FROM '.table_prefix.'session_keys AS k
  1000       $query = $this->sql('SELECT u.user_id AS uid,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms FROM '.table_prefix.'session_keys AS k
   995                              LEFT JOIN '.table_prefix.'users AS u
  1001                              LEFT JOIN '.table_prefix.'users AS u
   996                                ON ( u.user_id=k.user_id )
  1002                                ON ( u.user_id=k.user_id )
  1131     global $db, $session, $paths, $template, $plugins; // Common objects
  1137     global $db, $session, $paths, $template, $plugins; // Common objects
  1132     $ou = $this->username;
  1138     $ou = $this->username;
  1133     $oid = $this->user_id;
  1139     $oid = $this->user_id;
  1134     if($level > USER_LEVEL_CHPREF)
  1140     if($level > USER_LEVEL_CHPREF)
  1135     {
  1141     {
  1136       $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
  1142       $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  1137       if(!$this->user_logged_in || $this->auth_level < USER_LEVEL_MOD) return 'success';
  1143       if(!$this->user_logged_in || $this->auth_level < USER_LEVEL_MOD) return 'success';
  1138       // Destroy elevated privileges
  1144       // Destroy elevated privileges
  1139       $keyhash = md5(strrev($this->sid_super));
  1145       $keyhash = md5(strrev($this->sid_super));
  1140       $this->sql('DELETE FROM '.table_prefix.'session_keys WHERE session_key=\''.$keyhash.'\' AND user_id=\'' . $this->user_id . '\';');
  1146       $this->sql('DELETE FROM '.table_prefix.'session_keys WHERE session_key=\''.$keyhash.'\' AND user_id=\'' . $this->user_id . '\';');
  1141       $this->sid_super = false;
  1147       $this->sid_super = false;
  1207    * @return string Hex-encoded AES key
  1213    * @return string Hex-encoded AES key
  1208    */
  1214    */
  1209    
  1215    
  1210   function rijndael_genkey()
  1216   function rijndael_genkey()
  1211   {
  1217   {
  1212     $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
  1218     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  1213     $key = $aes->gen_readymade_key();
  1219     $key = $aes->gen_readymade_key();
  1214     $keys = getConfig('login_key_cache');
  1220     $keys = getConfig('login_key_cache');
  1215     if(is_string($keys))
  1221     if(is_string($keys))
  1216       $keys .= $key;
  1222       $keys .= $key;
  1217     else
  1223     else
  1225    * @return string
  1231    * @return string
  1226    */
  1232    */
  1227    
  1233    
  1228   function dss_rand()
  1234   function dss_rand()
  1229   {
  1235   {
  1230     $aes = new AESCrypt();
  1236     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  1231     $random = $aes->randkey(128);
  1237     $random = $aes->randkey(128);
  1232     unset($aes);
  1238     unset($aes);
  1233     return md5(microtime() . $random);
  1239     return md5(microtime() . $random);
  1234   }
  1240   }
  1235   
  1241   
  1342    
  1348    
  1343   function check_banlist()
  1349   function check_banlist()
  1344   {
  1350   {
  1345     global $db, $session, $paths, $template, $plugins; // Common objects
  1351     global $db, $session, $paths, $template, $plugins; // Common objects
  1346     $col_reason = ( $this->compat ) ? '"No reason entered (session manager is in compatibility mode)" AS reason' : 'reason';
  1352     $col_reason = ( $this->compat ) ? '"No reason entered (session manager is in compatibility mode)" AS reason' : 'reason';
  1347     $is_banned = false;
  1353     $banned = false;
  1348     if ( $this->user_logged_in )
  1354     if ( $this->user_logged_in )
  1349     {
  1355     {
  1350       // check by IP, email, and username
  1356       // check by IP, email, and username
  1351       $sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE \n"
  1357       $sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE \n"
  1352             . "    ( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR \n"
  1358             . "    ( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR \n"
  1437   function create_user($username, $password, $email, $real_name = '', $coppa = false)
  1443   function create_user($username, $password, $email, $real_name = '', $coppa = false)
  1438   {
  1444   {
  1439     global $db, $session, $paths, $template, $plugins; // Common objects
  1445     global $db, $session, $paths, $template, $plugins; // Common objects
  1440     
  1446     
  1441     // Initialize AES
  1447     // Initialize AES
  1442     $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
  1448     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  1443     
  1449     
  1444     if(!preg_match('#^'.$this->valid_username.'$#', $username)) return 'The username you chose contains invalid characters.';
  1450     if(!preg_match('#^'.$this->valid_username.'$#', $username)) return 'The username you chose contains invalid characters.';
  1445     $username = str_replace('_', ' ', $username);
  1451     $username = str_replace('_', ' ', $username);
  1446     $user_orig = $username;
  1452     $user_orig = $username;
  1447     $username = $this->prepare_text($username);
  1453     $username = $this->prepare_text($username);
  1793    * @return bool
  1799    * @return bool
  1794    */
  1800    */
  1795    
  1801    
  1796   function register_temp_password($user_id, $password)
  1802   function register_temp_password($user_id, $password)
  1797   {
  1803   {
  1798     $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
  1804     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  1799     $temp_pass = $aes->encrypt($password, $this->private_key, ENC_HEX);
  1805     $temp_pass = $aes->encrypt($password, $this->private_key, ENC_HEX);
  1800     $this->sql('UPDATE '.table_prefix.'users SET temp_password=\'' . $temp_pass . '\',temp_password_time='.time().' WHERE user_id='.intval($user_id).';');
  1806     $this->sql('UPDATE '.table_prefix.'users SET temp_password=\'' . $temp_pass . '\',temp_password_time='.time().' WHERE user_id='.intval($user_id).';');
  1801   }
  1807   }
  1802   
  1808   
  1803   /**
  1809   /**
  1904     
  1910     
  1905     // Scan the user ID for problems
  1911     // Scan the user ID for problems
  1906     if(intval($user_id) < 1) $errors[] = 'SQL injection attempt';
  1912     if(intval($user_id) < 1) $errors[] = 'SQL injection attempt';
  1907     
  1913     
  1908     // Instanciate the AES encryption class
  1914     // Instanciate the AES encryption class
  1909     $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
  1915     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  1910     
  1916     
  1911     // If all of our input vars are false, then we've effectively done our job so get out of here
  1917     // If all of our input vars are false, then we've effectively done our job so get out of here
  1912     if($username === false && $password === false && $email === false && $realname === false && $signature === false && $user_level === false)
  1918     if($username === false && $password === false && $email === false && $realname === false && $signature === false && $user_level === false)
  1913     {
  1919     {
  1914    // echo 'debug: $session->update_user(): success (no changes requested)';
  1920    // echo 'debug: $session->update_user(): success (no changes requested)';
  2198     // Initialize the permissions list with some defaults
  2204     // Initialize the permissions list with some defaults
  2199     $this->perms = $this->acl_types;
  2205     $this->perms = $this->acl_types;
  2200     $this->acl_defaults_used = $this->perms;
  2206     $this->acl_defaults_used = $this->perms;
  2201     
  2207     
  2202     // Fetch sitewide defaults from the permissions table
  2208     // Fetch sitewide defaults from the permissions table
  2203     $bs = 'SELECT rules FROM '.table_prefix.'acl WHERE page_id IS NULL AND namespace IS NULL AND ( ';
  2209     $bs = 'SELECT rules, target_type, target_id FROM '.table_prefix.'acl' . "\n"
       
  2210              . '  WHERE page_id IS NULL AND namespace IS NULL AND' . "\n"
       
  2211              . '  ( ';
  2204     
  2212     
  2205     $q = Array();
  2213     $q = Array();
  2206     $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$this->user_id.' )';
  2214     $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$this->user_id.' )';
  2207     if(count($this->groups) > 0)
  2215     if(count($this->groups) > 0)
  2208     {
  2216     {
  2209       foreach($this->groups as $g_id => $g_name)
  2217       foreach($this->groups as $g_id => $g_name)
  2210       {
  2218       {
  2211         $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )';
  2219         $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )';
  2212       }
  2220       }
  2213     }
  2221     }
  2214     $bs .= implode(' OR ', $q) . ' ) ORDER BY target_type ASC, target_id ASC;';
  2222     $bs .= implode(" OR \n    ", $q) . " ) \n  ORDER BY target_type ASC, target_id ASC;";
  2215     $q = $this->sql($bs);
  2223     $q = $this->sql($bs);
  2216     if ( $row = $db->fetchrow() )
  2224     if ( $row = $db->fetchrow() )
  2217     {
  2225     {
  2218       do {
  2226       do {
  2219         $rules = $this->string_to_perm($row['rules']);
  2227         $rules = $this->string_to_perm($row['rules']);
  2253         $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )';
  2261         $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )';
  2254       }
  2262       }
  2255     }
  2263     }
  2256     // The reason we're using an ORDER BY statement here is because ACL_TYPE_GROUP is less than ACL_TYPE_USER, causing the user's individual
  2264     // The reason we're using an ORDER BY statement here is because ACL_TYPE_GROUP is less than ACL_TYPE_USER, causing the user's individual
  2257     // permissions to override group permissions.
  2265     // permissions to override group permissions.
  2258     $bs .= implode(' OR ', $q) . ' ) AND (' . $pg_info . ' ( page_id=\''.$db->escape($paths->cpage['urlname_nons']).'\' AND namespace=\''.$db->escape($paths->namespace).'\' ) )     
  2266     $bs .= implode(" OR\n    ", $q) . " )\n  AND (" . $pg_info . ' ( page_id=\''.$db->escape($paths->cpage['urlname_nons']).'\' AND namespace=\''.$db->escape($paths->namespace).'\' ) )     
  2259       ORDER BY target_type ASC, page_id ASC, namespace ASC;';
  2267       ORDER BY target_type ASC, page_id ASC, namespace ASC;';
  2260     $q = $this->sql($bs);
  2268     $q = $this->sql($bs);
  2261     if ( $row = $db->fetchrow() )
  2269     if ( $row = $db->fetchrow() )
  2262     {
  2270     {
  2263       do {
  2271       do {
  2695     {
  2703     {
  2696       $pg_info .= ' ( page_id=\'' . $g_id . '\' AND namespace=\'__PageGroup\' ) OR';
  2704       $pg_info .= ' ( page_id=\'' . $g_id . '\' AND namespace=\'__PageGroup\' ) OR';
  2697     }
  2705     }
  2698     
  2706     
  2699     // Build a query to grab ACL info
  2707     // Build a query to grab ACL info
  2700     $bs = 'SELECT rules FROM '.table_prefix.'acl WHERE ( ';
  2708     $bs = 'SELECT rules FROM '.table_prefix.'acl WHERE ' . "\n"
       
  2709           . '  ( ';
  2701     $q = Array();
  2710     $q = Array();
  2702     $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$session->user_id.' )';
  2711     $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$session->user_id.' )';
  2703     if(count($session->groups) > 0)
  2712     if(count($session->groups) > 0)
  2704     {
  2713     {
  2705       foreach($session->groups as $g_id => $g_name)
  2714       foreach($session->groups as $g_id => $g_name)
  2707         $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )';
  2716         $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )';
  2708       }
  2717       }
  2709     }
  2718     }
  2710     // The reason we're using an ORDER BY statement here is because ACL_TYPE_GROUP is less than ACL_TYPE_USER, causing the user's individual
  2719     // The reason we're using an ORDER BY statement here is because ACL_TYPE_GROUP is less than ACL_TYPE_USER, causing the user's individual
  2711     // permissions to override group permissions.
  2720     // permissions to override group permissions.
  2712     $bs .= implode(' OR ', $q) . ' ) AND (' . $pg_info . ' page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\' )     
  2721     $bs .= implode(" OR\n    ", $q) . ' ) AND (' . $pg_info . ' page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\' )     
  2713       ORDER BY target_type ASC, page_id ASC, namespace ASC;';
  2722       ORDER BY target_type ASC, page_id ASC, namespace ASC;';
  2714     $q = $session->sql($bs);
  2723     $q = $session->sql($bs);
  2715     if ( $row = $db->fetchrow() )
  2724     if ( $row = $db->fetchrow() )
  2716     {
  2725     {
  2717       do {
  2726       do {