<?php

/**
  * SquirrelMail Login Manager Plugin
  * Copyright (C) 2003-2006 Paul Lesneiwski <paul@squirrelmail.org>
  * This program is licensed under GPL. See COPYING for details
  *
  * @package plugins
  * @subpackage vlogin
  *
  */



/**
  * Load plugin configuration
  *
  * This function loads the correct configuration file.  If a "meta-
  * configuration" file is present, it is always loaded, along with
  * the group of settings it points to.  If a custom configuration 
  * file is present, it is loaded.  If BOTH of these are present, 
  * note that the custom configuration file settings will override
  * any "meta-coniguration" if there are overlapping settings.
  *
  */
function load_vlogin_config()
{

   global $strip_domain_only, $add_default_domain_only;
   $config_settings_loaded = FALSE;


   // load meta config
   //
   @include_once (SM_PATH . 'plugins/vlogin/data/meta_config.php');


   // strip domain from username only?
   //
   if ($strip_domain_only)
   {
      include_once(SM_PATH . 'plugins/vlogin/data/meta_config.strip_domain.php');
      $config_settings_loaded = TRUE;
   }


   // add default domain to username only?
   //
   if ($add_default_domain_only)
   {
      include_once(SM_PATH . 'plugins/vlogin/data/meta_config.add_default_domain.php');
      $config_settings_loaded = TRUE;
   }


   // load custom config if present, or load typical settings 
   // if none found at all
   //
   if ((!@include_once(SM_PATH . 'plugins/vlogin/data/config.php'))
    && !$config_settings_loaded)
      include_once (SM_PATH . 'plugins/vlogin/data/config.sample.typical.php');

}



/**
  * Override Default Config Values
  *
  * This function looks up new configuration settins for the current
  * user and replaces the default settings with those.  The new values
  * might be retrieved from the virtual domain table (based on the
  * hostname portion of the current URL), a per user file, etc.
  *
  * @param array $args Array of arguments passed to the current hook;
  *                    used primarily to get hook name from first argument
  * @param string $user If given, overrides whatever user name 
  *                     would normally be extracted automatically
  * @param string $hostname If given, overrides whatever 
  *                         host name would normally be 
  *                         extracted automatically
  * @param string $hostname_stripped If given, overrides whatever 
  *                                  host name would normally be 
  *                                  extracted automatically
  * @param string $orig_hostname_stripped If given, overrides whatever 
  *                                       host name would normally be 
  *                                       extracted automatically
  *                     
  */
function overrideSmConfig($args, $user='', $hostname='', $hostname_stripped='', 
                          $orig_hostname_stripped='')
{

   global $allVirtualDomainsAreUnderOneHost, $PHP_SELF,
          $smHostIsDomainThatUserLoggedInWith, $virtualDomainDataDir, 
          $data_dir, $domain, $plugins, $virtualDomains,
          $squirrelmail_plugin_hooks, $useSessionBased, $serviceLevel,
          $usernameDomainIsHost, $at, $stripDomainFromUserSubstitution,
          $SQLDatabaseDomainLookup, $SQLDatabaseServiceLevelLookup,
          $default_org_logo, $notPartOfDomainName, 
          $chopOffDotSectionsFromRight, $chopOffDotSectionsFromLeft, 
          $numberOfDotSections, $checkByExcludeList, $dot, 
          $replacements, $removeFromFront, $translateHostnameTable, 
          $pathToQmail, $reverseDotSectionOrder,
          $always_prepend, $always_append, $IMAPServerRules, 
          $SMTPServerRules;
      
  
  // get global variable for versions of PHP < 4.1 
  //
  if (!check_php_version(4,1)) {
    global $HTTP_SERVER_VARS, $HTTP_SESSION_VARS, $HTTP_POST_VARS;
    $_SERVER = $HTTP_SERVER_VARS;
    $_SESSION = $HTTP_SESSION_VARS;
    $_POST = $HTTP_POST_VARS;
  }


  // make sure the session has started
  //
  sqsession_is_active();


  if (empty($user))
  {
     // try to find username
     //
     $user = '';
     if (isset($_SESSION['username']))
        $user = $_SESSION['username'];
     elseif (isset($_POST['login_username']))
        $user = trim($_POST['login_username']);
  }
   

  // take domain off of username that is used for 
  // substitutions below if needed
  //
  if ($stripDomainFromUserSubstitution && strpos($user, $at) !== FALSE) 
     $userSubstitution = substr($user, 0, strpos($user, $at));
  else
     $userSubstitution = $user;


  // if the host name is already given, don't need to go figure it out
  // 
  if (!empty($hostname))
  {
     global $config_override;
     $checkServiceLevel = FALSE;
  }
  else 
  {

     $checkServiceLevel = TRUE;
     load_vlogin_config();


     // prep: get config settings from session if necessary
     //
     if ($useSessionBased)
     {
        global $config_override;


        // only clean up when first logging in
        //
        if (strpos($PHP_SELF, 'redirect.php') !== FALSE
         || strpos($PHP_SELF, 'login.php') !== FALSE)
        {
           sqsession_unregister('config_override');
   
           // If we don't initialize this, it will work
           // together with the multilogin plugin
           // $config_override = array();
        }
        else
           sqgetGlobalVar('config_override', $config_override, SQ_SESSION);
     }
   

     // grab hostname into local var
     //
     if (isset($_SERVER['HTTP_X_FORWARDED_HOST']) && !empty($_SERVER['HTTP_X_FORWARDED_HOST']))
        $hostname = $_SERVER['HTTP_X_FORWARDED_HOST'];
     else
        $hostname = $_SERVER['HTTP_HOST'];
   
   
     // for sites where virtual host is pegged on the end of the main
     // site's URL (usually for single-certificate SSL hosting), get 
     // the actual host name out of the PHP_SELF portion of the URL
     //
     if ($allVirtualDomainsAreUnderOneHost) {
   
        // old way, static location of domain on end of URI:
        // preg_match('/[\/]*(.*?)(\/|$)/', $_SERVER['REQUEST_URI'], $matches);
        preg_match($allVirtualDomainsAreUnderOneHost, $_SERVER['REQUEST_URI'], $matches);
        $hostname = $matches[1];
   
     }
   
   
     $hostname_stripped = deconstructDomainName($hostname,
                                                $notPartOfDomainName, 
                                                $chopOffDotSectionsFromRight, 
                                                $chopOffDotSectionsFromLeft, 
                                                $numberOfDotSections, 
                                                $checkByExcludeList, 
                                                $at, $dot, $replacements,
                                                $removeFromFront, 
                                                $translateHostnameTable, 
                                                $pathToQmail,
                                                $reverseDotSectionOrder,
                                                $always_prepend, $always_append);
     $orig_hostname_stripped = $hostname_stripped;
   
   
     // set domain if the $smHostIsDomainThatUserLoggedInWith
     // flag is on...
     //
     if ($smHostIsDomainThatUserLoggedInWith)
     {
        if ($useSessionBased)
           $config_override['domain'] = $hostname_stripped;
        else
           $domain = $hostname_stripped; 
     }
      

     // allow domain given in username to override host
     // if needed - yikes
     //
     if ($usernameDomainIsHost && strpos($user, $at) !== FALSE ) 
     {
        $hostname = substr($user, strpos($user, $at) + strlen($at));
        $hostname_stripped = deconstructDomainName($hostname,
                                                   $notPartOfDomainName, 
                                                   $chopOffDotSectionsFromRight, 
                                                   $chopOffDotSectionsFromLeft, 
                                                   $numberOfDotSections, 
                                                   $checkByExcludeList, 
                                                   $at, $dot, $replacements,
                                                   $removeFromFront, 
                                                   $translateHostnameTable, 
                                                   $pathToQmail,
                                                   $reverseDotSectionOrder,
                                                   $always_prepend, $always_append);
     }


     // determine IMAP server address if it should be based 
     // on the user's domain
     //
     if (!empty($IMAPServerRules))
     {

        if (strpos($user, $at) !== FALSE) 
           $imap_domain = substr($user, strpos($user, $at) + strlen($at));
        else 
           $imap_domain = $hostname_stripped;

        $server_addr = deconstructDomainName($imap_domain,
                 (empty($IMAPServerRules['notPartOfDomainName']) 
                  ? array() 
                  : $IMAPServerRules['notPartOfDomainName']), 
                 (empty($IMAPServerRules['chopOffDotSectionsFromRight'])
                  ? 0
                  : $IMAPServerRules['chopOffDotSectionsFromRight']),
                 (empty($IMAPServerRules['chopOffDotSectionsFromLeft'])
                  ? 0
                  : $IMAPServerRules['chopOffDotSectionsFromLeft']),
                 (empty($IMAPServerRules['numberOfDotSections'])
                  ? 0
                  : $IMAPServerRules['numberOfDotSections']),
                 (empty($IMAPServerRules['checkByExcludeList'])
                  ? 0
                  : $IMAPServerRules['checkByExcludeList']),
                 (empty($IMAPServerRules['at'])
                  ? ''
                  : $IMAPServerRules['at']),
                 (empty($IMAPServerRules['dot'])
                  ? ''
                  : $IMAPServerRules['dot']),
                 (empty($IMAPServerRules['replacements'])
                  ? array()
                  : $IMAPServerRules['replacements']),
                 (empty($IMAPServerRules['removeFromFront'])
                  ? 0
                  : $IMAPServerRules['removeFromFront']),
                 (empty($IMAPServerRules['translateHostnameTable'])
                  ? ''
                  : $IMAPServerRules['translateHostnameTable']),
                 (empty($IMAPServerRules['pathToQmail'])
                  ? ''
                  : $IMAPServerRules['pathToQmail']),
                 (empty($IMAPServerRules['reverseDotSectionOrder'])
                  ? 0
                  : $IMAPServerRules['reverseDotSectionOrder']),
                 (empty($IMAPServerRules['always_prepend'])
                  ? ''
                  : $IMAPServerRules['always_prepend']),
                 (empty($IMAPServerRules['always_append'])
                  ? ''
                  : $IMAPServerRules['always_append'])
                                            );

        if ($useSessionBased)
           $config_override['imapServerAddress'] = $server_addr;
        else
        {
           global $imapServerAddress;
           $imapServerAddress = $server_addr;
        }

     }


     // determine SMTP server address if it should be based
     // on the user's domain
     //
     if (!empty($SMTPServerRules))
     {

        if (strpos($user, $at) !== FALSE)
           $smtp_domain = substr($user, strpos($user, $at) + strlen($at));
        else
           $smtp_domain = $hostname_stripped;

        $server_addr = deconstructDomainName($smtp_domain,
                 (empty($SMTPServerRules['notPartOfDomainName'])
                  ? array()
                  : $SMTPServerRules['notPartOfDomainName']),
                 (empty($SMTPServerRules['chopOffDotSectionsFromRight'])
                  ? 0
                  : $SMTPServerRules['chopOffDotSectionsFromRight']),
                 (empty($SMTPServerRules['chopOffDotSectionsFromLeft'])
                  ? 0
                  : $SMTPServerRules['chopOffDotSectionsFromLeft']),
                 (empty($SMTPServerRules['numberOfDotSections'])
                  ? 0
                  : $SMTPServerRules['numberOfDotSections']),
                 (empty($SMTPServerRules['checkByExcludeList'])
                  ? 0
                  : $SMTPServerRules['checkByExcludeList']),
                 (empty($SMTPServerRules['at'])
                  ? ''
                  : $SMTPServerRules['at']),
                 (empty($SMTPServerRules['dot'])
                  ? ''
                  : $SMTPServerRules['dot']),
                 (empty($SMTPServerRules['replacements'])
                  ? array()
                  : $SMTPServerRules['replacements']),
                 (empty($SMTPServerRules['removeFromFront'])
                  ? 0
                  : $SMTPServerRules['removeFromFront']),
                 (empty($SMTPServerRules['translateHostnameTable'])
                  ? ''
                  : $SMTPServerRules['translateHostnameTable']),
                 (empty($SMTPServerRules['pathToQmail'])
                  ? ''
                  : $SMTPServerRules['pathToQmail']),
                 (empty($SMTPServerRules['reverseDotSectionOrder'])
                  ? 0
                  : $SMTPServerRules['reverseDotSectionOrder']),
                 (empty($SMTPServerRules['always_prepend'])
                  ? ''
                  : $SMTPServerRules['always_prepend']),
                 (empty($SMTPServerRules['always_append'])
                  ? ''
                  : $SMTPServerRules['always_append'])
                                            );

        if ($useSessionBased)
           $config_override['smtpServerAddress'] = $server_addr;
        else
        {
           global $smtpServerAddress;
           $smtpServerAddress = $server_addr;
        }

     }


     // override data_dir if the $virtualDomainDataDir setting 
     // has been specified
     //
     if (!empty($virtualDomainDataDir)) 
     {
        $the_data_dir = $virtualDomainDataDir;
        $the_data_dir = str_replace('###VIRTUAL_DOMAIN###', 
                                $hostname_stripped, 
                                $the_data_dir);
        if (!empty($user))
           $the_data_dir = str_replace('###USERNAME###', 
                                   $userSubstitution, 
                                   $the_data_dir);
        if ($useSessionBased)
           $config_override['data_dir'] = $the_data_dir;
        else
           $data_dir = $the_data_dir;
     }
   

     // allow inclusion of external configuration files for each domain
     //
     if (file_exists(SM_PATH . 'plugins/vlogin/data/domains/' . $hostname_stripped . '.vlogin.config.php'))
       include_once(SM_PATH . 'plugins/vlogin/data/domains/' . $hostname_stripped . '.vlogin.config.php');

  }

  $firstTime = 1;


  // override the org_logo and other stuff if we find a match
  //
  // only need to do this stuff when first logging in
  //
  if (!$checkServiceLevel || strpos($PHP_SELF, 'redirect.php') !== FALSE
   || strpos($PHP_SELF, 'login.php') !== FALSE)
//
// NOTE: the following line will solve PHP 4.3 problems when using
//       the session_recall patch, however it will also unfortunately
//       mask errors in the config file.  removing this line could
//       possibly help debug non-functional vlogin installations
//
  if (is_array($virtualDomains))
  foreach (array_keys($virtualDomains) as $virtualDomain) {

    if (stristr($hostname, (string)($virtualDomain)) || $virtualDomain == '*') {


      // limit usage of global domain to first entry only
      //
      if ($virtualDomain == '*' && !$firstTime)
      {
         echo '<html><body><font color="red" size="12pt">';
         echo 'Sorry, please contact your administrator and ';
         echo 'ask them to reconfigure the SquirrelMail ';
         echo 'Login Manager (vlogin) plugin such that the ';
         echo 'global virtual domain is listed first';
         echo '</font></body></html>';
         exit;
      }


      foreach ($virtualDomains[$virtualDomain] as $setting => $value)
      {

        // go ahead and replace the strings ###VIRTUAL_DOMAIN### and ###USERNAME###
        //
        if (!is_array($value))
        {
           if (strpos($value, '###VIRTUAL_DOMAIN###') !== FALSE)
              $value = str_replace('###VIRTUAL_DOMAIN###',
                                   $hostname_stripped,
                                   $value);

           if (strpos($value, '###USERNAME###') !== FALSE && !empty($user))
              $value = str_replace('###USERNAME###', 
                               $userSubstitution, 
                               $value);
        }
         
         
        // enable additional plugins
        //
        if (stristr($setting, 'enable_plugins'))
        {
           if ($useSessionBased)
           {
              $config_override[$setting] = $value;
           }
           else
           {
              foreach ($value as $pluginName)
              {
                 $plugins[] = $pluginName;
                 use_plugin($pluginName);
              }
           }
           continue;
        }


        // disable plugins
        //
        if (stristr($setting, 'disable_plugins'))
        {
           if ($useSessionBased)
           {
              $config_override[$setting] = $value;
           }
           else
           {
              foreach ($value as $pluginName)
              {
                 $pluginKey = array_search($pluginName, $plugins);
                 if (!is_null($pluginKey) && $pluginKey !== FALSE)
                 {
                    unset($plugins[$pluginKey]);
                    foreach (array_keys($squirrelmail_plugin_hooks) as $hookName)
                    {
                       unset($squirrelmail_plugin_hooks[$hookName][$pluginName]);
                    }
                 }
              }
           }
           continue;
        }


        // replace SquirrelMail config values
        //
        if ($useSessionBased)
        {

           // in order to set the org_title, in the browser
           // title bar, have to do it now
           //
           if ($setting == 'org_title')
           {
              global $$setting;

              // embedded PHP in value need to be evaluated?
              //
              if (isset($virtualDomains[$virtualDomain]['settingsWithEmbeddedPHP']) 
               && is_array($virtualDomains[$virtualDomain]['settingsWithEmbeddedPHP'])
               && in_array($setting, $virtualDomains[$virtualDomain]['settingsWithEmbeddedPHP']))
                 eval('$$setting = ' . $value . ';');
              else
                 $$setting = $value;

              $config_override[$setting] = $value;
           }

           // use default org logo if configured and this org_logo non-existent
           //
           else if (!empty($default_org_logo) && $setting == 'org_logo' && !file_exists($value))
              $config_override[$setting] = $default_org_logo;

           else
              $config_override[$setting] = $value;

        }
        else
        {

           global $$setting;

           // embedded PHP in value need to be evaluated?
           //
           if (isset($virtualDomains[$virtualDomain]['settingsWithEmbeddedPHP']) 
            && is_array($virtualDomains[$virtualDomain]['settingsWithEmbeddedPHP']) 
            && in_array($setting, $virtualDomains[$virtualDomain]['settingsWithEmbeddedPHP']))
              eval('$$setting = ' . $value . ';');
/*
           if ($setting == 'org_title')
           {
              global $$setting;
              eval('$$setting = ' . $value . ';');
           }
*/

           // use default org logo if configured and this org_logo non-existent
           //
           else if (!empty($default_org_logo) && $setting == 'org_logo' && !file_exists($value))
              $$setting = $default_org_logo;

           else
              $$setting = $value;

        }

      }


      if ($smHostIsDomainThatUserLoggedInWith)
      {
         if ($useSessionBased)
            $config_override['domain'] = $orig_hostname_stripped;
         else
            $domain = $orig_hostname_stripped; 
      }

      
      // exit this loop, unless this is the global default
      // 
      if( $virtualDomain != '*' ) break;

    }


    $firstTime = 0;


  }


  if ($useSessionBased)
  {

     sqsession_register($config_override, 'config_override');

     include_once (SM_PATH . 'plugins/multilogin/functions.php');

     multilogin_sqoverride_config_do($args);

  }


  // look for SQL overrides for this domain only when not in
  // override of hostname (service level mode)
  //
  if ($checkServiceLevel && !empty($SQLDatabaseDomainLookup))
  {
     include_once(SM_PATH . 'plugins/vlogin/sql_settings_lookup.php');
     SQL_settings_lookup($SQLDatabaseDomainLookup, $hostname_stripped, $args);
  }


  // override settings for different service levels
  //
  if ($checkServiceLevel && serviceLevelOverride($user))
  {
     overrideSmConfig($args, $user, $hostname_stripped, 
                      $orig_hostname_stripped);
  }


  // look for SQL overrides for service level settings
  // if a service level was found and only when in non-
  // hostname override (service level) mode
  //
  if ($checkServiceLevel && !empty($serviceLevel) && !empty($SQLDatabaseServiceLevelLookup))
  {
     include_once(SM_PATH . 'plugins/vlogin/sql_settings_lookup.php');
     SQL_settings_lookup($SQLDatabaseServiceLevelLookup, $serviceLevel, $args);
  }


  // override settings on a per-user basis
// TODO: not clear if this will work with password_forget and/or login_alias...
  //
  perUserOverride($user, $args);


}



/** 
  * Figures out just which hostname is the right one to add to the end 
  * of the username.  This function does *NOT* care whether or not it
  * will in fact be added to the username; it is only responsible for
  * getting the right hostname and no more.
  *
  * @return string The expected host name
  *
  */
function determine_user_hostname()
{

   global $useDomainFromVirtDomainsArray, $virtualDomains, $securePort,
          $allVirtualDomainsAreUnderOneHost, $forceLowercase,
          $useDomainFromServerEnvironment, $vlogin_debug,
          $notPartOfDomainName, $chopOffDotSectionsFromRight, 
          $chopOffDotSectionsFromLeft, $numberOfDotSections, 
          $checkByExcludeList, $at, $dot, $replacements,
          $removeFromFront, $translateHostnameTable, $pathToQmail, 
          $reverseDotSectionOrder, $always_prepend, $always_append;


   // get vlogin config settings
   //
   load_vlogin_config();


   // get global variable for versions of PHP < 4.1
   //
   if (!check_php_version(4,1)) {
      global $HTTP_SERVER_VARS;
      $_SERVER = $HTTP_SERVER_VARS;
   }


   // grab hostname into local var
   //
   if (isset($_SERVER['HTTP_X_FORWARDED_HOST']) && !empty($_SERVER['HTTP_X_FORWARDED_HOST']))
      $hostname = $_SERVER['HTTP_X_FORWARDED_HOST'];
   else
      $hostname = $_SERVER['HTTP_HOST'];



   // grab server port 
   //
//   if (isset($_SERVER['SERVER_PORT']))
//      $serverPort = $_SERVER['SERVER_PORT'];
//   else
//      $serverPort = 0;



   // what's this server's HTTPS port?
   //
//   if (isset($securePort))
//      $httpsPort = $securePort;
//   else
//      $httpsPort = '443';



  // for sites where virtual host is pegged on the end of the main
  // site's URL (usually for single-certificate SSL hosting), get 
  // the actual host name out of the PHP_SELF portion of the URL
  //
// old way:
//   if (isset($_SERVER['HTTPS']) 
// um, why restrict this to only https?
//   if ($serverPort == $httpsPort
//    && $allVirtualDomainsAreUnderOneHost) 
   if ($allVirtualDomainsAreUnderOneHost) 
   {

      // old way, static location of domain on end of URI:
      // preg_match('/[\/]*(.*?)(\/|$)/', $_SERVER['REQUEST_URI'], $matches);
      preg_match($allVirtualDomainsAreUnderOneHost, $_SERVER['REQUEST_URI'], $matches);
      $hostname = $matches[1];

   }



   $found = FALSE;



   // use server name from server environment if necessary
   //
   if (!empty($useDomainFromServerEnvironment))
   {

      $hostname = $_SERVER[$useDomainFromServerEnvironment];
      $found = TRUE;

      if ($vlogin_debug) 
         echo 'Using hostname from SERVER environment... $_SERVER[\'' 
            . $useDomainFromServerEnvironment . '\'] = ' 
            . $hostname . '<br />';
   }


   // go lowercase if needed
   //
   if ($forceLowercase)
      $hostname = strtolower($hostname);



   // allow the use of the domain setting from the $virtualDomains
   // array, which is a heck of a lot simpler...
   //
   if ($useDomainFromVirtDomainsArray)
// TODO: Do this for SQL too??  We might need another query that just gets the domain and use it here.  Yuck.
   {

      if (is_array($virtualDomains))
      foreach (array_keys($virtualDomains) as $virtualDomain)
      {

         if (stristr($hostname, (string)($virtualDomain)) || $virtualDomain == '*')
         {

            if (isset($virtualDomains[$virtualDomain]['domain']))
            {
               $found = TRUE;
               $hostname = $virtualDomains[$virtualDomain]['domain'];
            }


            // exit this loop, unless this is the global default
            //
            if ($virtualDomain != '*') break;

         }

      }

   }
   if (!$found) $hostname = deconstructDomainName($hostname,
                                                  $notPartOfDomainName, 
                                                  $chopOffDotSectionsFromRight, 
                                                  $chopOffDotSectionsFromLeft, 
                                                  $numberOfDotSections, 
                                                  $checkByExcludeList, 
                                                  $at, $dot, $replacements,
                                                  $removeFromFront, 
                                                  $translateHostnameTable, 
                                                  $pathToQmail,
                                                  $reverseDotSectionOrder,
                                                  $always_prepend, $always_append);


   return $hostname;

}



/** 
  * Remap Username When Logging In
  * 
  * Here is the meat of the vlogin magic.  It is used to figure
  * out what the username should really be, possibly based on
  * the hostname portion of the current URL, a sendmail style
  * virtual users table, any number of different combinations
  * of stripping parts of the hostname, etc.
  *
  */
function vlogin_domain_do($args)
{

  global $plugins, $login_username, $$login_username, $vlogin_debug, $data_dir, 
         $foundLoginAlias, $at, $dot, $dontUseHostName, $atConversion,
         $sendmailVirtualUserTable, $putHostNameOnFrontOfUsername,
         $prefs_dsn, $useSessionBased, $removeDomainIfGiven, 
         $alwaysAddHostName, $usernameReplacements, $forceLowercase,
         $prefs_are_cached, $prefs_cache, $vlogin_remap_username_completed,
         $dontUseHostNameUserList;


  // we've already been here.... don't do this twice
  // (used for compatibility/interoperability with
  // other plugins)
  //
  if ($vlogin_remap_username_completed) return;


  // figure out where prefs are stored
  //
  if (isset($prefs_dsn) && !empty($prefs_dsn))
      $prefsInDB = true;
  else
      $prefsInDB = false;


  // get vlogin config settings
  //
  load_vlogin_config();



  if ($useSessionBased)
     overrideSmConfig($args);



  // what username did the user submit to us?
  //
  // someone reported that they were able to log in with spaces
  // in username if this trim() was not here... I cannot reproduce
  // the problem, but this trim() doesn't seem to hurt... if it
  // DOES cause problems for someone, let me know!
  //
  $user = trim($login_username);
  


  // if password_forget is loaded, use the obfuscated name
  //
  if (in_array('password_forget', $plugins)) {
    if (!isset($$login_username)) 
       sqgetGlobalVar($login_username, $$login_username, SQ_FORM);
    if ($$login_username != '')
      $user = trim($$login_username);  // see notes above about this trim()
  }



  // save the name originally used as the login (gets saved in user
  // session on the login_verified hook)... might be useful later
  //
  global $vlogin_original_username;
  $vlogin_original_username = $user;



  // if original username is in the $dontUseHostNameUserList array,
  // make sure $dontUseHostName is turned on
  //
  if (is_array($dontUseHostNameUserList) 
   && in_array($vlogin_original_username, $dontUseHostNameUserList))
     $dontUseHostName = 1;



  // if user logged in with full email address, 
  // chop off domain info if needed
  //
  if ( $removeDomainIfGiven && strpos($user, $at) !== FALSE ) 
  {
     $user = substr($user, 0, strpos($user, $at));
  }



  $hostname = determine_user_hostname();



  // check for login_alias plugin
  //
  if (in_array('login_alias',$plugins)) 
  {


    // check if login alias was already processed
    if (isset($foundLoginAlias)) {

      if ($foundLoginAlias) return;

    }
    else 
    {

      // make sure prefs file exists
      //
      if (!$prefsInDB)
         touch(getHashedFile('login_alias', $data_dir, "login_alias.pref"));


      // check for login alias here and return if found
      // (but only if it is in the domain being used
      // to log in when $dontUseHostName is off)
      //
      $prefs_are_cached = false;
      $prefs_cache = array();
      session_unregister('prefs_are_cached');
      session_unregister('prefs_cache');

      $loginAlias = getPref($data_dir, 'login_alias', $user);

      $prefs_are_cached = false;
      $prefs_cache = array();
      session_unregister('prefs_are_cached');
      session_unregister('prefs_cache');


      if (!empty($loginAlias))
      {
         if (!$dontUseHostName)
         {
            if (strpos($loginAlias, $hostname) !== FALSE)
               return;
         }
         else
         {
            return;
         }

      }

    }

  }



  // convert "at" sign...
  //
  if (is_array($atConversion))
  foreach ($atConversion as $otherAt)
     $user = str_replace($otherAt, $at, $user);



  // go lowercase if needed
  //
  if ($forceLowercase)
     $user = strtolower($user);



  // replace any characters as necessary
  //
  if (is_array($usernameReplacements))
    foreach ($usernameReplacements as $chars => $repl)
      $user = str_replace($chars, $repl, $user);



  // check and see if they decided to insert the host anyway
  // or we don't want to use the host name...
  //
  if( $alwaysAddHostName || (!$dontUseHostName && strpos($user, $at) === FALSE) ) {


    // assign realname using parsed hostname
    //
    if ($putHostNameOnFrontOfUsername)
       $realname = $hostname . $at . $user;
    else
       $realname = $user . $at . $hostname;


  } else {
    $realname = $user;
  }

  
  // remap to correct user account when using sendmail virtual logins...
  //
  if (!empty($sendmailVirtualUserTable))
  {
     $realname = getSendmailVirtualUser($realname, $sendmailVirtualUserTable);
  }


  // override settings on a per-user basis
  //
  perUserOverride($realname, $args);


  // if password_forget is loaded, use the obfuscated name
  if (in_array('password_forget',$plugins) && $$login_username != '')
     $$login_username=$realname;
  else
     $login_username=$realname;


  // tell anyone else who might be looking that we're done
  //
  $vlogin_remap_username_completed = TRUE;


  // when in debug mode, just dump out final login name and quit
  //
  if ($vlogin_debug)
  {

     echo '';
     echo '';
     echo '<html><body><hr><br>';
     echo '<h4>Your IMAP login was resolved to:<br><br>';
     echo $realname;
     echo '</h4><br><hr>';
     // was confusing to people using $dontUseHostName 
     if (!$dontUseHostName) echo '<br>$hostname is ' . $hostname . '<br>';
     echo '<br>PHP_SELF is ' . (isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : 'not globally accessible')
        . '<br>';
global        $imapServerAddress;
echo '<hr>IMAP server: ' . $imapServerAddress . '<hr>';
     echo '<br><hr></body></html>';
     exit;

  }

}



/**
  * Save original username used to log in
  *
  */
function vlogin_save_original_do()
{

   // get vlogin config settings
   //
   load_vlogin_config();


   global $vlogin_original_username, $rawLoginIsOutgoingEmailAddress, 
          $username, $data_dir;
   sqsession_register($vlogin_original_username, 'vlogin_original_username');


   // force outgoing email address?
   //
   if ($rawLoginIsOutgoingEmailAddress)
      setPref($data_dir, $username, 'email_address', $vlogin_original_username);

}



/** 
  * Get Per-User Config Overrides
  *
  * Looks up a user in the per-user config file and, if found,
  * grabs and uses the user's custom settings.
  *
  * @param string $user The username for which to look for custom settings.
  * @param array $args The array of arguments passed to the current hook.
  *
  */
function perUserOverride($user, $args) 
{

   if (empty($user)) return;


   global $perUserSettingsFile, $SQLDatabaseUserLookup, $at, $atConversion;


   if (!empty($perUserSettingsFile))
   {
      include_once(SM_PATH . 'plugins/vlogin/per_user_functions.php');
      perUserOverride_do($user, $args);
   }


   if (!empty($SQLDatabaseUserLookup))
   {
      include_once(SM_PATH . 'plugins/vlogin/sql_settings_lookup.php');

      // convert "at" sign...
      //
      foreach ($atConversion as $otherAt)
         $user = str_replace($otherAt, $at, $user);

      SQL_settings_lookup($SQLDatabaseUserLookup, $user, $args);
   }

}



/** 
  * Get Service Level Config Overrides
  *
  * Looks up a user's service level, and if found, attempts 
  * to find a corresponding set of service level config 
  * overrides.  If found, they will be located after this 
  * function completes in the global scope, primarily in
  * the $virtualDomains array.
  * 
  * The service level name will also be available in the global
  * scope after the function return as the variable $serviceLevel,
  * even when an actual service level settings file was not found.
  *
  * @param string $user The username for which to look for 
  *                     service level overrides.
  *
  * @return boolean TRUE if overrides were found and should 
  *                 be processed (they will be located in the
  *                 global scope, primarily in $virtualDomains),
  *                 FALSE otherwise.  Note that even when FALSE
  *                 is returned, it is still possible that a valid
  *                 service level was found - it will be set in
  *                 the global $serviceLevel variable.
  *
  */
function serviceLevelOverride($user) 
{

   global $serviceLevel, $at, $atConversion, $serviceLevelBackend, $virtualDomains;


   // make sure service level functionality is turned on
   //
   if (empty($serviceLevelBackend))
      return FALSE;


   $backend = '';
   include_once(SM_PATH . 'plugins/vlogin/service_level_functions.php');
   if ($serviceLevelBackend == 1) $backend = 'internal';
   else if ($serviceLevelBackend == 2) $backend = 'sql';


   // convert "at" sign...
   //
   foreach ($atConversion as $otherAt)
      $user = str_replace($otherAt, $at, $user);


   // get user's service level
   //
   $serviceLevel = call_user_func('vlogin_service_level_map_' . 
                                  $backend, $user);


   // override settings if found
   //
   if (file_exists(SM_PATH . 'plugins/vlogin/data/service_levels/' . $serviceLevel . '.php'))
   {
      include_once(SM_PATH . 'plugins/vlogin/data/service_levels/' . $serviceLevel . '.php');
      return TRUE;
   }


   return FALSE;

}



/** 
  * Host Name Deconstruction
  *
  * Takes a host name (usually from URL, but can be
  * anything) and reconstructs it based on the given
  * configuration settings.
  *
  * @param string $hostname The hostname to re/deconstruct.
  * TODO: fix param documentation; see README or config files
  * @param $notPartOfDomainName 
  * @param $chopOffDotSectionsFromRight 
  * @param $chopOffDotSectionsFromLeft 
  * @param $numberOfDotSections
  * @param $checkByExcludeList 
  * @param $at
  * @param $dot
  * @param $replacements
  * @param $removeFromFront
  * @param $translateHostnameTable 
  * @param $pathToQmail
  * @param $reverseDotSectionOrder
  * @param $always_prepend
  * @param $always_append
  *
  * @return string The rebuilt hostname.
  *
  */
function deconstructDomainName($hostname, $notPartOfDomainName, 
                               $chopOffDotSectionsFromRight, 
                               $chopOffDotSectionsFromLeft, 
                               $numberOfDotSections, $checkByExcludeList, 
                               $at, $dot, $replacements,
                               $removeFromFront, $translateHostnameTable, 
                               $pathToQmail, $reverseDotSectionOrder,
                               $always_prepend, $always_append)
{

  load_vlogin_config();



  // if a port number is in the URL, remove it before proceeding
  //
  $hostname = preg_replace('/:\d+/', '', $hostname);



  // replace any characters as necessary
  //
  if (is_array($replacements))
    foreach ($replacements as $chars => $repl)
      $hostname = str_replace($chars, $repl, $hostname);


  // reverse dot section order if necessary
  //
  if ($reverseDotSectionOrder)
    $hostname = implode('.', array_reverse(explode('.', $hostname))); 


  // if enabled, remove "dot sections" until desired size of hostname is reached
  //
  if ($numberOfDotSections > 0) {

     // lop off pieces of hostname until 
     // number of dot sections is same as desired
     //
     while (sizeof(explode('.', $hostname)) > $numberOfDotSections) {

        if ($removeFromFront)
           $hostname = substr($hostname, strpos($hostname, '.') + 1);
        else
           $hostname = substr($hostname, 0, strrpos($hostname, '.'));
        
     }

  }


  // if enabled, remove "dot sections" from the left side...
  //
  for ($i = 0; $i < $chopOffDotSectionsFromLeft; $i++) {

    $hostname = substr($hostname, strpos($hostname, '.') + 1);
     
  }


  // if enabled, remove "dot sections" from the right side...
  //
  for ($i = 0; $i < $chopOffDotSectionsFromRight; $i++) {

    $hostname = substr($hostname, 0, strrpos($hostname, '.'));
     
  }


// TODO: I think it makes more sense to do this BEFORE 
// doing the $numberOfDotSections functionality.  I would
// move it now, but I don't know if I'll break things for
// people out there.... ;>
   // if enabled, extract any of the undesired host name pieces
   //
   if ($checkByExcludeList) {
    
      foreach ($notPartOfDomainName as $dotSection)
      {

         $hostname = preg_replace('/(^|\.)' . $dotSection . '($|\.)/', 
                                  "\$1", $hostname);

      }

   }


   // if resulting hostname begins or ends with a dot, remove it
   //
   $hostname = preg_replace('/^\./', '', $hostname);
   $hostname = preg_replace('/\.$/', '', $hostname);


  // just in case they need a different '.' seperator
  //
  $hostname = str_replace( ".", $dot, $hostname );


  // append or prepend static strings if given
  //
  if (!empty($always_prepend)) $hostname = $always_prepend . $hostname;
  if (!empty($always_append)) $hostname = $hostname . $always_append;


  // if domain name needs to be remapped, do so here
  //
  if (!empty($translateHostnameTable))
     $hostname = translateHostname($hostname, $translateHostnameTable);


  // if qmail/vpopmail domain aliasing is used and we
  // need to translate this domain (if it is an alias),
  // do so here
  //
  if (!empty($pathToQmail))
     $hostname = unaliasQmailDomainAlias($hostname, $pathToQmail);


  return $hostname;

}



/**
  * Unalias any Qmail/Vpopmail Domain Aliases
  *
  * @param string $host Host name to be dealiased.
  * @param string $pathToQmail System path to Qmail install directory.
  *
  * @return string Corrected host name.
  *
  */
function unaliasQmailDomainAlias($host, $pathToQmail)
{
    $tmp = '+'.$host.'-:' ;
    $tlen = strlen($tmp) ;
    $file = fopen($pathToQmail.'/users/assign','r') ;
    while(!feof($file)) {
      $line = fgets($file,256) ;
      if(substr($line,0,$tlen) == $tmp)	{
        $host = substr($line,$tlen) ;
        $tmp = strpos($host,':') ;
        if ($tmp !== FALSE) $host = substr($host,0,$tmp) ;
        break ;
      }
    }
    fclose($file) ;
    return $host;
}



// remap a host name to the one specified in the translate host 
// name table (path given by the $translateHostnameTable)
//
/**
  * Remap Host Name
  *
  * Takes a host name, looks it up in the given lookup table,
  * and, if found therein, returns the lookup value (otherwise,
  * returns the host as is).
  *
  * @param string $host The host name to be looked up.
  * @param string $translateHostnameTable The path to the lookup table.
  *
  * @return string The remapped host name.
  *
  */
function translateHostname($host, $translateHostnameTable)
{

   if ($HOSTTABLE = @fopen ($translateHostnameTable, 'r'))
   {

      while (!feof($HOSTTABLE))
      {

         $line = fgets($HOSTTABLE, 4096);
         $line = trim($line);


         // skip blank lines and comment lines and php
         // open/close tag lines 
         //
         if (strpos($line, '#') === 0 || strlen($line) < 3 
          || strpos($line, '<?php') === 0 || strpos($line, '*/ ?>') === 0)
            continue;


         // parse fields out
         //
         preg_match('/^(\S+)\s+(\S+)/', $line, $matches);


         // if host is found, get remapped hostname and return
         //
         if (preg_match('/^' . str_replace(array('?', '*'), 
                                           array('\w{1}', '.*?'),
                                           strtoupper($matches[1])) 
                             . '$/', strtoupper($host)))
         {
            fclose($HOSTTABLE);
            return $matches[2];
         }

      }


      fclose($HOSTTABLE);
      return $host;


   }


   // otherwise, unaltered hostname is returned
   //
   return $host;

}



/**
  * Remap User Login ala Sendmail Style Virtual User Table
  *
  * Looks up the given username in the given virtual user table
  * and, if found therein, returns the corresponding lookup value
  * (otherwise, returns the username as is).
  *
  * @param string $user The username to be looked up.
  * @param string $sendmailVirtualUserTable The system path to the lookup table.
  *
  * @return string The remapped user name.
  *
  */
function getSendmailVirtualUser($user, $sendmailVirtualUserTable)
{

   global $at, $vlogin_debug;

   $catchallLogin = '';
   $remappedUsername = '';
   $atPos = strpos($user, $at);


   if ($VIRTTABLE = @fopen ($sendmailVirtualUserTable, 'r'))
   {

      // be ready to use catchall address - prepare domain name
      //
      $domainName = '';
      if ($atPos !== FALSE)
         $domainName = substr($user, $atPos);


      while (!feof($VIRTTABLE))
      {

         $line = fgets($VIRTTABLE, 4096);
         $line = trim($line);


         // skip blank lines and comment lines and php
         // open/close tag lines 
         //
         if (strpos($line, '#') === 0 || strlen($line) < 3 
          || strpos($line, '<?php') === 0 || strpos($line, '*/ ?>') === 0)
            continue;


         // parse fields out
         //
         preg_match('/^(\S+)\s+(\S+)/', $line, $matches);


         // grab catchall login
         //
         if ($domainName === $matches[1]) 
            $catchallLogin = $matches[2];


         // if user is found, get remapped login and finish looping
         //
         if ($user === $matches[1]) 
         {
            $remappedUsername = $matches[2];
            break;
         }

      }


      fclose($VIRTTABLE);


   }


   // only show bad fopen info when in debug mode
   //
   else if ($vlogin_debug)
   {
      global $color;
      plain_error_message(sprintf("Could not find %s", $sendmailVirtualUserTable), $color);
   }


   // if we get here without having found a direct match
   // and a catchall login is available, use it
   //
   if (empty($remappedUsername) && !empty($catchallLogin))
   {

      // remove extraneous stuff we don't need off end (begins with plus sign)
      //
      $catchallLogin = preg_replace('/\+%\S+\s*$/', '', $catchallLogin);

      $remappedUsername = $catchallLogin;

   }


   // otherwise, use unaltered username
   //
   else if (empty($remappedUsername))
      $remappedUsername = $user;



   // do variable replacement if needed
   //
   if ($atPos !== FALSE && strpos($remappedUsername, '%1') !== FALSE)
   {
      $usernameOnly = substr($user, 0, $atPos);
      $remappedUsername = str_replace('%1', $usernameOnly, $remappedUsername);
   }


   return $remappedUsername;

}



/**
  * Allows control over which options are displayed on options pages
  *
  */
function vlogin_display_options_do()
{

   global $config_override, $optpage_data, $notPartOfDomainName, 
          $chopOffDotSectionsFromRight, $chopOffDotSectionsFromLeft, 
          $numberOfDotSections, $checkByExcludeList, $at, $dot, 
          $replacements, $removeFromFront, $translateHostnameTable, 
          $pathToQmail, $reverseDotSectionOrder, $always_prepend, 
          $always_append;

   $overrides = array();


   // try to get configuration settings from session, if they're there
   //
   if (!isset($config_override))
   {

      global $virtualDomains, $useSessionBased;
      load_vlogin_config();

      if (!$useSessionBased)
      {

         // get global variable for versions of PHP < 4.1 
         //
         if (!check_php_version(4,1)) 
         {
            global $HTTP_SERVER_VARS;
            $_SERVER = $HTTP_SERVER_VARS;
         }


         // grab hostname into local var
         //
         if (isset($_SERVER['HTTP_X_FORWARDED_HOST']) && !empty($_SERVER['HTTP_X_FORWARDED_HOST']))
            $hostname = $_SERVER['HTTP_X_FORWARDED_HOST'];
         else
            $hostname = $_SERVER['HTTP_HOST'];
         $hostname_stripped = deconstructDomainName($hostname,
                                                    $notPartOfDomainName, 
                                                    $chopOffDotSectionsFromRight, 
                                                    $chopOffDotSectionsFromLeft, 
                                                    $numberOfDotSections, 
                                                    $checkByExcludeList, 
                                                    $at, $dot, $replacements,
                                                    $removeFromFront, 
                                                    $translateHostnameTable, 
                                                    $pathToQmail,
                                                    $reverseDotSectionOrder,
                                                    $always_prepend, $always_append);


         // allow inclusion of external configuration files for each domain
         //
         if (file_exists(SM_PATH . 'plugins/vlogin/data/domains/' . $hostname_stripped . '.vlogin.config.php'))
            include_once(SM_PATH . 'plugins/vlogin/data/domains/' . $hostname_stripped . '.vlogin.config.php');


         // get virtual domain config items
         //
         $overrides = array();
         if (is_array($virtualDomains))
         foreach (array_keys($virtualDomains) as $virtualDomain) 
         {

            if (stristr($hostname, (string)($virtualDomain)) || $virtualDomain == '*') 
            {

               $overrides = $virtualDomains[$virtualDomain];

            }

         }

      }

   }
   else
      $overrides = $config_override;



   // remove any options from list of options as desired
   //
   if (isset($overrides['disable_options']) && is_array($overrides['disable_options']))
      foreach ($overrides['disable_options'] as $removeOptionName)
         foreach ($optpage_data['vals'] as $groupNumber => $optionGroup)
            foreach ($optionGroup as $optionNumber => $optionItem)
               if (isset($optionItem['name']) && $optionItem['name'] == $removeOptionName)
                  unset($optpage_data['vals'][$groupNumber][$optionNumber]);

}



/**
  * Get a database connection
  *
  * If a connection has already been opened, return that,
  * otherwise, open a new connection.
  *
  * @return object The database connection handle.
  *
  */
function vlogin_get_database_connection()
{

   global $vlogin_db_connection, $vlogin_dsn;
   if (!include_once('DB.php'))
   {
      global $color;
      plain_error_message("Could not find Pear DB.php.", $color);
   }


   // make a new connection if needed; exit if failure
   //
   if (empty($vlogin_db_connection))
   {

      $vlogin_db_connection = DB::connect($vlogin_dsn);
      if (DB::isError($vlogin_db_connection))
      {
         global $color;
         plain_error_message("Could not make database connection.", $color);
      }
      $vlogin_db_connection->setFetchMode(DB_FETCHMODE_ORDERED);

   }


   // return connection
   //
   return $vlogin_db_connection;

}



?>
