plugins/SpecialUserFuncs.php
changeset 1132 05fe0039d952
parent 1119 2e045975fc65
child 1158 e733f984c990
equal deleted inserted replaced
1131:adfbe522c95f 1132:05fe0039d952
    46 $__login_status = '';
    46 $__login_status = '';
    47 
    47 
    48 function page_Special_Login()
    48 function page_Special_Login()
    49 {
    49 {
    50   global $db, $session, $paths, $template, $plugins; // Common objects
    50   global $db, $session, $paths, $template, $plugins; // Common objects
    51   global $__login_status;
    51   global $login_result;
    52   global $lang;
    52   global $lang, $output;
    53   
    53   
    54   require_once(ENANO_ROOT . '/includes/math.php');
    54   // Determine which level we're going up to
    55   require_once(ENANO_ROOT . '/includes/diffiehellman.php');
       
    56   global $dh_supported;
       
    57   
       
    58   $locked_out = false;
       
    59   // are we locked out?
       
    60   $threshold = ( $_ = getConfig('lockout_threshold') ) ? intval($_) : 5;
       
    61   $duration  = ( $_ = getConfig('lockout_duration') ) ? intval($_) : 15;
       
    62   // convert to minutes
       
    63   $duration  = $duration * 60;
       
    64   $policy = ( $x = getConfig('lockout_policy') && in_array(getConfig('lockout_policy'), array('lockout', 'disable', 'captcha')) ) ? getConfig('lockout_policy') : 'lockout';
       
    65   if ( $policy != 'disable' )
       
    66   {
       
    67     $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
       
    68     $timestamp_cutoff = time() - $duration;
       
    69     $q = $session->sql('SELECT timestamp FROM '.table_prefix.'lockout WHERE timestamp > ' . $timestamp_cutoff . ' AND ipaddr = \'' . $ipaddr . '\' ORDER BY timestamp DESC;');
       
    70     $fails = $db->numrows();
       
    71     if ( $fails >= $threshold )
       
    72     {
       
    73       $row = $db->fetchrow();
       
    74       $locked_out = true;
       
    75       $lockdata = array(
       
    76           'locked_out' => true,
       
    77           'lockout_threshold' => $threshold,
       
    78           'lockout_duration' => ( $duration / 60 ),
       
    79           'lockout_fails' => $fails,
       
    80           'lockout_policy' => $policy,
       
    81           'lockout_last_time' => $row['timestamp'],
       
    82           'time_rem' => ( $duration / 60 ) - round( ( time() - $row['timestamp'] ) / 60 ),
       
    83           'captcha' => ''
       
    84         );
       
    85       if ( $policy == 'captcha' )
       
    86       {
       
    87         $lockdata['captcha'] = $session->make_captcha();
       
    88       }
       
    89     }
       
    90     $db->free_result();
       
    91   }
       
    92   
       
    93   if ( isset($_GET['act']) && $_GET['act'] == 'getkey' )
       
    94   {
       
    95     header('Content-type: text/javascript');
       
    96     $username = ( $session->user_logged_in ) ? $session->username : false;
       
    97     $response = Array(
       
    98       'username' => $username,
       
    99       'key' => $pubkey,
       
   100       'challenge' => $challenge,
       
   101       'locked_out' => false
       
   102       );
       
   103     
       
   104     if ( $locked_out )
       
   105     {
       
   106       foreach ( $lockdata as $x => $y )
       
   107       {
       
   108         $response[$x] = $y;
       
   109       }
       
   110       unset($x, $y);
       
   111     }
       
   112     
       
   113     // 1.1.3: generate diffie hellman key
       
   114     $response['dh_supported'] = $dh_supported;
       
   115     if ( $dh_supported )
       
   116     {
       
   117       $dh_key_priv = dh_gen_private();
       
   118       $dh_key_pub = dh_gen_public($dh_key_priv);
       
   119       $dh_key_priv = $_math->str($dh_key_priv);
       
   120       $dh_key_pub = $_math->str($dh_key_pub);
       
   121       $response['dh_public_key'] = $dh_key_pub;
       
   122       // store the keys in the DB
       
   123       $q = $db->sql_query('INSERT INTO ' . table_prefix . "diffiehellman( public_key, private_key ) VALUES ( '$dh_key_pub', '$dh_key_priv' );");
       
   124       if ( !$q )
       
   125         $db->die_json();
       
   126     }
       
   127     
       
   128     $response = enano_json_encode($response);
       
   129     echo $response;
       
   130     return null;
       
   131   }
       
   132   
       
   133   $level = ( isset($_GET['level']) && in_array($_GET['level'], array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') ) ) ? intval($_GET['level']) : USER_LEVEL_MEMBER;
    55   $level = ( isset($_GET['level']) && in_array($_GET['level'], array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') ) ) ? intval($_GET['level']) : USER_LEVEL_MEMBER;
   134   if ( isset($_POST['login']) )
    56   if ( isset($_POST['login']) )
   135   {
    57   {
   136     if ( in_array($_POST['auth_level'], array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') ) )
    58     if ( in_array($_POST['level'], array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') ) )
   137     {
    59     {
   138       $level = intval($_POST['auth_level']);
    60       $level = intval($_POST['level']);
   139     }
    61     }
   140   }
    62   }
   141   
    63   // Don't allow going from guest straight to elevated
       
    64   // FIXME do we want to allow this with a CSRF check?
   142   if ( $level > USER_LEVEL_MEMBER && !$session->user_logged_in )
    65   if ( $level > USER_LEVEL_MEMBER && !$session->user_logged_in )
   143   {
    66   {
   144     $level = USER_LEVEL_MEMBER;
    67     $level = USER_LEVEL_MEMBER;
   145   }
    68   }
       
    69   
       
    70   // If we're already at or above this level, redirect to the target page or, if no target
       
    71   // specified, back to the main page.
   146   if ( $level <= USER_LEVEL_MEMBER && $session->user_logged_in )
    72   if ( $level <= USER_LEVEL_MEMBER && $session->user_logged_in )
   147   {
    73   {
   148     if ( $target = $paths->getAllParams() )
    74     if ( $target = $paths->getAllParams() )
   149     {
    75     {
   150       redirect(makeUrl($target), '', '', 0);
    76       redirect(makeUrl($target), '', '', 0);
   151     }
    77     }
   152     $paths->main_page();
    78     $paths->main_page();
   153   }
    79   }
   154   
    80   
   155   $template->header();
    81   // Lockout aliasing
   156   echo '<form action="'.makeUrl($paths->nslist['Special'].'Login').'" method="post" name="loginform" onsubmit="try{runEncryption();}catch(e){};">';
    82   $lockout =& $login_result['lockout'];
   157   $header = ( $level > USER_LEVEL_MEMBER ) ? $lang->get('user_login_message_short_elev') : $lang->get('user_login_message_short');
    83   
   158   if ( isset($_POST['login']) )
    84   $output->header();
   159   {
    85   echo '<form action="' . makeUrl($paths->nslist['Special'].'Login') . '" method="post" name="loginform" onsubmit="try { return runEncryption(); } catch(e) { console.error(e); };">';
   160     $errstring = $__login_status['error'];
    86   
   161     switch($__login_status['error'])
       
   162     {
       
   163       case 'key_not_found':
       
   164         $errstring = $lang->get('user_err_key_not_found');
       
   165         break;
       
   166       case 'ERR_DH_KEY_NOT_FOUND':
       
   167         $errstring = $lang->get('user_err_dh_key_not_found'); // . " -- {$__login_status['debug']}";
       
   168         break;
       
   169       case 'ERR_DH_KEY_NOT_INTEGER':
       
   170         $errstring = $lang->get('user_err_dh_key_not_numeric');
       
   171         break;
       
   172       case 'key_wrong_length':
       
   173         $errstring = $lang->get('user_err_key_wrong_length');
       
   174         break;
       
   175       case 'too_big_for_britches':
       
   176         $errstring = $lang->get('user_err_too_big_for_britches');
       
   177         break;
       
   178       case 'invalid_credentials':
       
   179         $errstring = $lang->get('user_err_invalid_credentials');
       
   180         if ( getConfig('lockout_policy', 'lockout') == 'lockout' )
       
   181         {
       
   182           $errstring .= $lang->get('user_err_invalid_credentials_lockout', array('fails' => $__login_status['lockout_fails']));
       
   183         }
       
   184         else if ( getConfig('lockout_policy', 'lockout') == 'captcha' )
       
   185         {
       
   186           $errstring .= $lang->get('user_err_invalid_credentials_lockout_captcha', array('fails' => $__login_status['lockout_fails']));
       
   187         }
       
   188         break;
       
   189       case 'backend_fail':
       
   190         $errstring = $lang->get('user_err_backend_fail');
       
   191         break;
       
   192       case 'locked_out':
       
   193         $attempts = intval($__login_status['lockout_fails']);
       
   194         if ( $attempts > $__login_status['lockout_threshold'])
       
   195           $attempts = $__login_status['lockout_threshold'];
       
   196         
       
   197         $server_time = time();
       
   198         $time_rem = ( intval(@$__login_status['lockout_last_time']) == time() ) ? $__login_status['lockout_duration'] : $__login_status['lockout_duration'] - round( ( $server_time - $__login_status['lockout_last_time'] ) / 60 );
       
   199         if ( $time_rem < 1 )
       
   200           $time_rem = $__login_status['lockout_duration'];
       
   201         
       
   202         $s = ( $time_rem == 1 ) ? '' : $lang->get('meta_plural');
       
   203         
       
   204         $captcha_string = ( $__login_status['lockout_policy'] == 'captcha' ) ? $lang->get('user_err_locked_out_captcha_blurb') : '';
       
   205         $errstring = $lang->get('user_err_locked_out', array('plural' => $s, 'captcha_blurb' => $captcha_string, 'time_rem' => $time_rem));
       
   206         
       
   207         break;
       
   208       default:
       
   209         $errstring = $lang->get($errstring);
       
   210         break;
       
   211     }
       
   212     echo '<div class="error-box-mini">'.$errstring.'</div>';
       
   213   }
       
   214   if ( $p = $paths->getAllParams() )
    87   if ( $p = $paths->getAllParams() )
   215   {
    88   {
   216     echo '<input type="hidden" name="return_to" value="'.$p.'" />';
    89     echo '<input type="hidden" name="return_to" value="' . htmlspecialchars($p) . '" />';
   217   }
    90   }
   218   else if ( isset($_POST['login']) && isset($_POST['return_to']) )
    91   else if ( isset($_POST['login']) && isset($_POST['return_to']) )
   219   {
    92   {
   220     echo '<input type="hidden" name="return_to" value="'.htmlspecialchars($_POST['return_to']).'" />';
    93     echo '<input type="hidden" name="return_to" value="' . htmlspecialchars($_POST['return_to']) . '" />';
   221   }
    94   }
       
    95   
       
    96   // determine what the "remember me" checkbox should say
       
    97   $session_time = intval(getConfig('session_remember_time', '30'));
       
    98   if ( $session_time === 0 )
       
    99   {
       
   100     // sessions are infinite
       
   101     $text_remember = $lang->get('user_login_check_remember_infinite');
       
   102   }
       
   103   else
       
   104   {
       
   105     // is the number of days evenly divisible by 7? if so, use weeks
       
   106     if ( $session_time % 7 == 0 )
       
   107     {
       
   108       $session_time = $session_time / 7;
       
   109       $unit = 'week';
       
   110     }
       
   111     else
       
   112     {
       
   113       $unit = 'day';
       
   114     }
       
   115     // if it's not equal to 1, pluralize it
       
   116     if ( $session_time != 1 )
       
   117     {
       
   118       $unit .= $lang->get('meta_plural');
       
   119     }
       
   120     $text_remember = $lang->get('user_login_check_remember', array(
       
   121         'session_length' => $session_time,
       
   122         'length_units' => $lang->get("etc_unit_$unit")
       
   123       ));
       
   124   }
       
   125   
       
   126   if ( $error_text = login_get_error($login_result) )
       
   127   {
       
   128     echo '<div class="error-box-mini">' . htmlspecialchars($error_text) . '</div>';
       
   129   }
       
   130   
       
   131   //
       
   132   // START FORM
       
   133   //
   222   ?>
   134   ?>
   223     <div class="tblholder">
   135     <div class="tblholder">
   224       <table border="0" style="width: 100%;" cellspacing="1" cellpadding="4">
   136       <table border="0" style="width: 100%;" cellspacing="1" cellpadding="4">
   225         <tr>
   137         <tr>
   226           <th colspan="3"><?php echo $header; ?></th>
   138           <th colspan="3">
       
   139             <!-- Table header: "Please enter..." -->
       
   140             <?php echo ( $level > USER_LEVEL_MEMBER ) ? $lang->get('user_login_message_short_elev') : $lang->get('user_login_message_short'); ?>
       
   141           </th>
   227         </tr>
   142         </tr>
   228         <tr>
   143         <tr>
   229           <td colspan="3" class="row1">
   144           <td colspan="3" class="row1">
       
   145             <!-- Introduction text -->
   230             <?php
   146             <?php
   231             if ( $level <= USER_LEVEL_MEMBER )
   147             if ( $level <= USER_LEVEL_MEMBER )
   232             {
       
   233               echo '<p>' . $lang->get('user_login_body', array('reg_link' => makeUrlNS('Special', 'Register'))) . '</p>';
   148               echo '<p>' . $lang->get('user_login_body', array('reg_link' => makeUrlNS('Special', 'Register'))) . '</p>';
   234             }
       
   235             else
   149             else
   236             {
       
   237               echo '<p>' . $lang->get('user_login_body_elev') . '</p>';
   150               echo '<p>' . $lang->get('user_login_body_elev') . '</p>';
   238             }
       
   239             ?>
   151             ?>
   240           </td>
   152           </td>
   241         </tr>
   153         </tr>
   242         <tr>
   154         <tr>
       
   155           <!-- Username field -->
   243           <td class="row2">
   156           <td class="row2">
   244             <?php echo $lang->get('user_login_field_username'); ?>:
   157             <?php echo $lang->get('user_login_field_username'); ?>:
   245           </td>
   158           </td>
   246           <td class="row1">
   159           <td class="row1">
   247             <input name="username" size="25" type="text" <?php
   160             <input name="username" size="25" type="text" value="<?php echo $session->user_logged_in ? htmlspecialchars($session->username) : ''; ?>" />
   248               if ( $level <= USER_LEVEL_MEMBER )
       
   249               {
       
   250                 echo 'tabindex="1" ';
       
   251               }
       
   252               else
       
   253               {
       
   254                 echo 'tabindex="3" ';
       
   255               }
       
   256               if ( $session->user_logged_in )
       
   257               {
       
   258                 echo 'value="' . $session->username . '"';
       
   259               }
       
   260               ?> />
       
   261           </td>
   161           </td>
   262           <?php if ( $level <= USER_LEVEL_MEMBER ) { ?>
   162           <?php if ( $level <= USER_LEVEL_MEMBER ): ?>
   263           <td rowspan="<?php echo ( ( $locked_out && $lockdata['lockout_policy'] == 'captcha' ) ) ? '4' : '2'; ?>" class="row3">
   163           <!-- Forgot password / create account links -->
       
   164           <td rowspan="<?php echo ( ( $lockout['active'] && $lockout['policy'] == 'captcha' ) ) ? '4' : '2'; ?>" class="row3">
   264             <small><?php echo $lang->get('user_login_forgotpass_blurb', array('forgotpass_link' => makeUrlNS('Special', 'PasswordReset'))); ?><br />
   165             <small><?php echo $lang->get('user_login_forgotpass_blurb', array('forgotpass_link' => makeUrlNS('Special', 'PasswordReset'))); ?><br />
   265             <?php echo $lang->get('user_login_createaccount_blurb', array('reg_link' => makeUrlNS('Special', 'Register'))); ?></small>
   166             <?php echo $lang->get('user_login_createaccount_blurb', array('reg_link' => makeUrlNS('Special', 'Register'))); ?></small>
   266           </td>
   167           </td>
   267           <?php } ?>
   168           <?php endif; ?>
   268         </tr>
   169         </tr>
   269         <tr>
   170         <tr>
       
   171           <!-- Password field -->
   270           <td class="row2">
   172           <td class="row2">
   271             <?php echo $lang->get('user_login_field_password'); ?>:
   173             <?php echo $lang->get('user_login_field_password'); ?>:
   272           </td><td class="row1"><input name="pass" size="25" type="password" tabindex="<?php echo ( $level <= USER_LEVEL_MEMBER ) ? '2' : '1'; ?>" /></td>
   174           </td><td class="row1"><input name="password" size="25" type="password" /></td>
   273          </tr>
   175          </tr>
       
   176          
   274          <?php
   177          <?php
   275          if ( $locked_out && $lockdata['lockout_policy'] == 'captcha' )
   178          // CAPTCHA?
       
   179          if ( $lockout['active'] && $lockout['policy'] == 'captcha' )
   276          {
   180          {
   277            ?>
   181            ?>
       
   182            <!-- CAPTCHA -->
   278            <tr>
   183            <tr>
   279              <td class="row2" rowspan="2"><?php echo $lang->get('user_login_field_captcha'); ?>:<br /></td><td class="row1"><input type="hidden" name="captcha_hash" value="<?php echo $lockdata['captcha']; ?>" /><input name="captcha_code" size="25" type="text" tabindex="<?php echo ( $level <= USER_LEVEL_MEMBER ) ? '3' : '4'; ?>" /></td>
   184              <td class="row2" rowspan="2">
       
   185                <?php echo $lang->get('user_login_field_captcha'); ?>:
       
   186                <br />
       
   187              </td>
       
   188              <td class="row1">
       
   189                <input type="hidden" name="captcha_hash" value="<?php echo $lockout['captcha']; ?>" />
       
   190                <input name="captcha_code" size="25" type="text" tabindex="<?php echo ( $level <= USER_LEVEL_MEMBER ) ? '3' : '4'; ?>" />
       
   191              </td>
   280            </tr>
   192            </tr>
   281            <tr>
   193            <tr>
   282              <td class="row3">
   194              <td class="row3">
   283                <img src="<?php echo makeUrlNS('Special', 'Captcha/' . $lockdata['captcha']) ?>" onclick="this.src=this.src+'/a';" style="cursor: pointer;" />
   195                <img src="<?php echo makeUrlNS('Special', 'Captcha/' . $lockout['captcha']) ?>" onclick="this.src=this.src+'/a';" style="cursor: pointer;" />
   284              </td>
   196              </td>
   285            </tr>
   197            </tr>
   286            <?php
   198            <?php
   287          }
   199          }
       
   200          
       
   201          // Run hooks
   288          $code = $plugins->setHook('login_form_html');
   202          $code = $plugins->setHook('login_form_html');
   289          foreach ( $code as $cmd )
   203          foreach ( $code as $cmd )
   290          {
   204          {
   291            eval($cmd);
   205            eval($cmd);
   292          }
   206          }
       
   207          
       
   208          // level-2 only: "Remember me" switch
   293          if ( $level <= USER_LEVEL_MEMBER )
   209          if ( $level <= USER_LEVEL_MEMBER )
   294          {
   210          {
   295            // "remember me" switch
       
   296            // first order of business is to determine what the checkbox should say
       
   297            $session_time = intval(getConfig('session_remember_time', '30'));
       
   298            if ( $session_time === 0 )
       
   299            {
       
   300              // sessions are infinite
       
   301              $text_remember = $lang->get('user_login_check_remember_infinite');
       
   302            }
       
   303            else
       
   304            {
       
   305              // is the number of days evenly divisible by 7? if so, use weeks
       
   306              if ( $session_time % 7 == 0 )
       
   307              {
       
   308                $session_time = $session_time / 7;
       
   309                $unit = 'week';
       
   310              }
       
   311              else
       
   312              {
       
   313                $unit = 'day';
       
   314              }
       
   315              // if it's not equal to 1, pluralize it
       
   316              if ( $session_time != 1 )
       
   317              {
       
   318                $unit .= 's';
       
   319              }
       
   320              $text_remember = $lang->get('user_login_check_remember', array(
       
   321                  'session_length' => $session_time,
       
   322                  'length_units' => $lang->get("etc_unit_$unit")
       
   323                ));
       
   324            }
       
   325            ?>
   211            ?>
   326            <tr>
   212            <tr>
   327              <td class="row2">
   213              <td class="row2">
   328                <?php echo $lang->get('user_login_field_remember'); ?>
   214                <?php echo $lang->get('user_login_field_remember'); ?>
   329              </td>
   215              </td>
   332                  <input type="checkbox" name="remember" tabindex="3" />
   218                  <input type="checkbox" name="remember" tabindex="3" />
   333                  <?php echo $text_remember; ?>
   219                  <?php echo $text_remember; ?>
   334                </label>
   220                </label>
   335              </td>
   221              </td>
   336            </tr>
   222            </tr>
       
   223            
       
   224          <!-- Crypto notice -->
   337            <?php
   225            <?php
   338          }
   226          }
   339          if ( $level <= USER_LEVEL_MEMBER && ( !isset($_GET['use_crypt']) || ( isset($_GET['use_crypt']) && $_GET['use_crypt']!='0' ) ) )
   227          
       
   228          // lol DeMorgan'd
       
   229          $crypto_disable = ( isset($_GET['use_crypt']) && $_GET['use_crypt'] == '0' );
       
   230          
       
   231          // Crypto disable: crypto on, normal login
       
   232          if ( $level <= USER_LEVEL_MEMBER && !$crypto_disable )
   340          {
   233          {
   341            echo '<tr>
   234            echo '<tr>
   342              <td class="row3" colspan="3">';
   235              <td class="row3" colspan="3">';
   343              
   236              
   344            $returnpage_link = ( $return = $paths->getAllParams() ) ? '/' . $return : '';
   237            $returnpage_link = ( $return = $paths->getAllParams() ) ? '/' . $return : '';
   347            echo '<p>' . $lang->get('user_login_nocrypt_countrylist') . '</p>';
   240            echo '<p>' . $lang->get('user_login_nocrypt_countrylist') . '</p>';
   348            
   241            
   349            echo '  </td>
   242            echo '  </td>
   350            </tr>';
   243            </tr>';
   351          }
   244          }
   352          else if ( $level <= USER_LEVEL_MEMBER && ( isset($_GET['use_crypt']) && $_GET['use_crypt']=='0' ) )
   245          // Crypto disable: crypto OFF, normal login
       
   246          else if ( $level <= USER_LEVEL_MEMBER && $crypto_disable )
   353          {
   247          {
   354            echo '<tr>
   248            echo '<tr>
   355              <td class="row3" colspan="3">';
   249              <td class="row3" colspan="3">';
   356              
   250              
   357            $returnpage_link = ( $return = $paths->getAllParams() ) ? '/' . $return : '';
   251            $returnpage_link = ( $return = $paths->getAllParams() ) ? '/' . $return : '';
   360            echo '<p>' . $lang->get('user_login_usecrypt_countrylist') . '</p>';
   254            echo '<p>' . $lang->get('user_login_usecrypt_countrylist') . '</p>';
   361            
   255            
   362            echo '  </td>
   256            echo '  </td>
   363            </tr>';
   257            </tr>';
   364          }
   258          }
   365          else if ( $level > USER_LEVEL_MEMBER && !strstr($_SERVER['HTTP_USER_AGENT'], 'iPhone') && $dh_supported )
   259          // Crypto disable: crypto on, ELEV login
       
   260          else if ( $level > USER_LEVEL_MEMBER && $GLOBALS['dh_supported'] )
   366          {
   261          {
   367            echo '<tr>';
   262            echo '<tr>';
   368            echo '<td class="row3" colspan="3">';
   263            echo '<td class="row3" colspan="3">';
   369            echo '<p>' . $lang->get('user_login_dh_notice') . '</p>';
   264            echo '<p>' . $lang->get('user_login_dh_notice') . '</p>';
   370            echo '</td>';
   265            echo '</td>';
   371            echo '</tr>';
   266            echo '</tr>';
   372          }
   267          }
   373          ?>
   268          ?>
   374          
   269          
       
   270          <!-- Submit button -->
   375          <tr>
   271          <tr>
   376            <th colspan="3" style="text-align: center" class="subhead">
   272            <th colspan="3" style="text-align: center" class="subhead">
   377              <input type="hidden" name="login" value="true" />
   273              <input type="hidden" name="login" value="true" />
   378              <input type="submit" value="Log in" tabindex="<?php echo ( $level <= USER_LEVEL_MEMBER ) ? '4' : '2'; ?>" />
   274              <input type="submit" value="<?php echo $lang->get('user_login_btn_log_in'); ?>" />
   379            </th>
   275            </th>
   380          </tr>
   276          </tr>
   381       </table>
   277       </table>
   382     </div>
   278     </div>
   383       <input type="hidden" name="auth_level" value="<?php echo (string)$level; ?>" />
   279     
       
   280       <input type="hidden" name="level" value="<?php echo (string)$level; ?>" />
   384       <?php if ( $level <= USER_LEVEL_MEMBER ): ?>
   281       <?php if ( $level <= USER_LEVEL_MEMBER ): ?>
   385       <script type="text/javascript">
   282       <script type="text/javascript">
   386         document.forms.loginform.username.focus();
   283         document.forms.loginform.username.focus();
   387       </script>
   284       </script>
   388       <?php else: ?>
   285       <?php else: ?>
   420         echo '<input type="hidden" name="get_fwd" value="' . htmlspecialchars($_POST['get_fwd']) . '" />';
   317         echo '<input type="hidden" name="get_fwd" value="' . htmlspecialchars($_POST['get_fwd']) . '" />';
   421       }
   318       }
   422       ?>
   319       ?>
   423     </form>
   320     </form>
   424     <?php
   321     <?php
   425       echo $session->aes_javascript('loginform', 'pass');
   322       if ( !$crypto_disable )
       
   323         echo $session->aes_javascript('loginform', 'password');
   426     ?>
   324     ?>
   427   <?php
   325   <?php
   428   $template->footer();
   326   $output->footer();
   429 }
   327 }
   430 
   328 
   431 function page_Special_Login_preloader() // adding _preloader to the end of the function name calls the function before $session and $paths setup routines are called
   329 function page_Special_Login_preloader() // adding _preloader to the end of the function name calls the function before $session and $paths setup routines are called
   432 {
   330 {
   433   global $db, $session, $paths, $template, $plugins; // Common objects
   331   global $db, $session, $paths, $template, $plugins; // Common objects
   434   global $__login_status;
   332   global $login_result;
   435   global $lang;
   333   global $lang;
   436   require_once( ENANO_ROOT . '/includes/math.php' );
   334   
   437   
   335   // Are we calling the JSON interface?
   438   $paths->fullpage = $GLOBALS['urlname'];
   336   $paths->fullpage = $GLOBALS['urlname'];
   439   if ( $paths->getParam(0) === 'action.json' )
   337   if ( $paths->getParam(0) === 'action.json' )
   440   {
   338   {
   441     if ( !isset($_POST['r']) )
   339     if ( !isset($_POST['r']) )
   442       die('No request.');
   340       die('No request.');
   457     echo enano_json_encode($session->process_login_request($request));
   355     echo enano_json_encode($session->process_login_request($request));
   458     
   356     
   459     $db->close();
   357     $db->close();
   460     exit;
   358     exit;
   461   }
   359   }
   462   if ( isset($_GET['act']) && $_GET['act'] == 'ajaxlogin' )
   360   
   463   {
   361   // No. Process incoming results from the HTML version.
   464     echo 'This version of the Enano LoginAPI is deprecated. Please clear your browser\'s cache and try your login again. Developers, please use the action.json method instead.';
   362   if ( isset($_POST['login']) )
   465     return true;
   363   {
   466   }
   364     $_POST['password'] = $session->get_aes_post();
   467   if(isset($_POST['login']))
   365     
   468   {
   366     $result = $session->process_login_request(array(
   469     $captcha_hash = ( isset($_POST['captcha_hash']) ) ? $_POST['captcha_hash'] : false;
   367         'mode' => 'login_pt',
   470     $captcha_code = ( isset($_POST['captcha_code']) ) ? $_POST['captcha_code'] : false;
   368         'userinfo' => $_POST,
   471     
   369         'level' => $_POST['level'],
   472     try
   370         'captcha_hash' => isset($_POST['captcha_hash']) ? $_POST['captcha_hash'] : false,
   473     {
   371         'captcha_code' => isset($_POST['captcha_code']) ? $_POST['captcha_code'] : false
   474       $password = $session->get_aes_post('pass');
   372       ));
   475     }
   373     
   476     catch ( Exception $e )
   374     if ( $result['mode'] === 'login_success' )
   477     {
   375     {
   478       $__login_status = array(
   376       //
   479         'mode' => 'error',
   377       // LOGIN SUCCESS.
   480         'error' => $e->getMessage()
   378       // Redirect as necessary.
   481       );
   379       //
   482       return false;
   380       
   483     }
   381       // Load our preferences
   484     
       
   485     // These are to allow auth plugins to work universally between JSON and HTML login forms
       
   486     $userinfo =& $_POST;
       
   487     $userinfo['password'] =& $password;
       
   488     $req = array(
       
   489       'level' => intval($_POST['auth_level']),
       
   490       'remember' => isset($_POST['remember'])
       
   491     );
       
   492     
       
   493     // At this point if any extra fields were injected into the login form, we need to let plugins process it
       
   494     
       
   495     /**
       
   496      * Called upon processing an incoming login request from the plain HTML login form.. If you added anything to the form,
       
   497      * that will be in the $userinfo array here and on $_POST. Expected return values are: true if your plugin has
       
   498      * not only succeeded but ALSO issued a session key (bypass the whole Enano builtin login process) and an associative array
       
   499      * with "mode" set to "error" and an error string in "error" to send an error back to the client. Any return value other
       
   500      * than these will be ignored.
       
   501      * @hook login_process_userdata_json
       
   502      */
       
   503      
       
   504     $skip_normal_login = false;
       
   505     
       
   506     $code = $plugins->setHook('login_process_userdata_json');
       
   507     foreach ( $code as $cmd )
       
   508     {
       
   509       $result = eval($cmd);
       
   510       if ( $result === true )
       
   511       {
       
   512         $skip_normal_login = true;
       
   513         $result = array('success' => true);
       
   514         break;
       
   515       }
       
   516       else if ( is_array($result) )
       
   517       {
       
   518         if ( isset($result['mode']) && $result['mode'] === 'error' && isset($result['error']) )
       
   519         {
       
   520           $__login_status = array(
       
   521             'mode' => 'error',
       
   522             'error' => $result['error']
       
   523           );
       
   524           return false;
       
   525         }
       
   526       }
       
   527     }
       
   528     
       
   529     if ( !$skip_normal_login )
       
   530     {
       
   531       $result = $session->login_without_crypto($_POST['username'], $password, false, intval($_POST['auth_level']), $captcha_hash, $captcha_code, isset($_POST['remember']));
       
   532     }
       
   533     
       
   534     if($result['success'])
       
   535     {
       
   536       $session->start();
   382       $session->start();
   537       
   383       
       
   384       // Decode get_add
   538       $get_add = false;
   385       $get_add = false;
   539       if ( isset($_POST['get_fwd']) )
   386       if ( isset($_POST['get_fwd']) )
   540       {
   387       {
   541         try
   388         try
   542         {
   389         {
   551         catch ( Exception $e )
   398         catch ( Exception $e )
   552         {
   399         {
   553         }
   400         }
   554       }
   401       }
   555       
   402       
   556       $template->load_theme($session->theme, $session->style);
   403       // Going to a user-specified page?
   557       if(isset($_POST['return_to']))
   404       if ( isset($_POST['return_to']) )
   558       {
   405       {
       
   406         // yea
   559         $name = get_page_title($_POST['return_to']);
   407         $name = get_page_title($_POST['return_to']);
   560         $subst = array(
   408         $subst = array(
   561             'username' => $session->username,
   409             'username' => $session->username,
   562             'redir_target' => $name
   410             'redir_target' => $name
   563           );
   411           );
   564         redirect( makeUrl($_POST['return_to'], $get_add), $lang->get('user_login_success_title'), $lang->get('user_login_success_body', $subst) );
   412         redirect( makeUrl($_POST['return_to'], $get_add), $lang->get('user_login_success_title'), $lang->get('user_login_success_body', $subst) );
   565       }
   413       }
   566       else
   414       else
   567       {
   415       {
       
   416         // No, redirect them to the main page
   568         $subst = array(
   417         $subst = array(
   569             'username' => $session->username,
   418             'username' => $session->username,
   570             'redir_target' => $lang->get('user_login_success_body_mainpage')
   419             'redir_target' => $lang->get('user_login_success_body_mainpage')
   571           );
   420           );
   572         redirect( makeUrl(get_main_page(), $get_add), $lang->get('user_login_success_title'), $lang->get('user_login_success_body', $subst) );
   421         redirect( makeUrl(get_main_page(), $get_add), $lang->get('user_login_success_title'), $lang->get('user_login_success_body', $subst) );
   573       }
   422       }
   574     }
   423     }
   575     else
   424     else if ( $result['mode'] === 'login_success_reset' )
   576     {
   425     {
   577       if ( $result['error'] === 'valid_reset' )
   426       // They logged in with a temporary password; send them to the reset form
   578       {
   427       redirect($result['redirect_url'], '', '', 0);
   579         header('HTTP/1.1 302 Temporary Redirect');
   428     }
   580         header('Location: ' . $result['redirect_url']);
   429     // Otherwise, the result is likely an error.
   581         
   430     $login_result = $result;
   582         $db->close();
   431   }
   583         exit();
   432   else
   584       }
   433   {
   585       $GLOBALS['__login_status'] = $result;
   434     $login_result = $session->process_login_request(array(
   586     }
   435         'mode' => 'getkey'
       
   436       ));
       
   437   }
       
   438   
       
   439   // This is a bit of a hack. The login form generates AES and DiffieHellman keys on its
       
   440   // own, so we need to clean up the ones from the login request API.
       
   441   if ( !empty($login_result['crypto']) )
       
   442   {
       
   443     $session->process_login_request(array(
       
   444         'mode' => 'clean_key',
       
   445         'key_aes' => $login_result['crypto']['aes_key'],
       
   446         'key_dh' => $login_result['crypto']['dh_public_key'],
       
   447       ));
   587   }
   448   }
   588 }
   449 }
   589 
   450 
   590 function SpecialLogin_SendResponse_PasswordReset($user_id, $passkey)
   451 /**
       
   452  * Given a Login API response, find the appropriate error text, if any.
       
   453  * @param array LoginAPI response
       
   454  * @return mixed Error string, or bool(false) if no error.
       
   455  */
       
   456 
       
   457 function login_get_error($response)
   591 {
   458 {
   592   $response = Array(
   459   global $lang;
   593       'result' => 'success_reset',
   460   
   594       'user_id' => $user_id,
   461   if ( !empty($response['lockout']) )
   595       'temppass' => $passkey
   462   {
   596     );
   463     // set this pluralality thing
   597   
   464     $response['lockout']['plural'] = $response['lockout']['time_rem'] == 1 ? '' : $lang->get('meta_plural');
   598   $response = enano_json_encode($response);
   465   }
   599   echo $response;
   466   
   600   
   467   if ( $response['mode'] == 'initial' )
   601   $db->close();
   468   {
   602   exit;
   469     // Just showing the box for the first time. If there's an error now, it's based on a preexisting lockout.
       
   470     if ( $response['lockout']['active'] )
       
   471     {
       
   472       return $lang->get('user_err_locked_out_initial_' . $response['lockout']['policy'], $response['lockout']);
       
   473     }
       
   474     return false;
       
   475   }
       
   476   else
       
   477   {
       
   478     // An attempt was made.
       
   479     switch($response['mode'])
       
   480     {
       
   481       case 'login_failure':
       
   482         // Generic login user error.
       
   483         $error = '';
       
   484         if ( ($x = $lang->get($response['error'])) != $response['error'] )
       
   485           $error = $x;
       
   486         else
       
   487           $error = $lang->get('user_err_' . $response['error']);
       
   488         if ( $response['lockout']['active'] && $response['lockout']['policy'] == 'lockout' )
       
   489         {
       
   490           // Lockout enforcement was just activated.
       
   491           return $lang->get('user_err_locked_out_initial_' . $response['lockout']['policy'], $response['lockout']);
       
   492         }
       
   493         else if ( $response['lockout']['policy'] != 'disable' && !$response['lockout']['active'] && $response['lockout']['fails'] > 0 )
       
   494         {
       
   495           // Lockout is in a warning state.
       
   496           $error .= ' ' . $lang->get('user_err_invalid_credentials_' . $response['lockout']['policy'], $response['lockout']);
       
   497         }
       
   498         return $error;
       
   499         break;
       
   500       case 'api_error':
       
   501         // Error in the API.
       
   502         return $lang->get('user_err_login_generic_title') + ': ' + $lang->get('user_' . strtolower($response['error']));
       
   503         break;
       
   504     }
       
   505   }
       
   506   
       
   507   return is_string($response['error']) ? $response['error'] : false;
   603 }
   508 }
   604 
   509 
   605 function page_Special_Logout()
   510 function page_Special_Logout()
   606 {
   511 {
   607   global $db, $session, $paths, $template, $plugins; // Common objects
   512   global $db, $session, $paths, $template, $plugins; // Common objects