Page 1 of 1

Login script

Posted: Fri May 27, 2011 9:40 am
by pritam79
I got the following code in a book.

This is login.php

Code: Select all

<?php

if($state == $_GET['cu'])
 {
  setcookie("login[username]", "", time()-360);
  setcookie("login[password]", "", time()-360);
  
  header('Location:login.php');
  exit;
 }
else if($state == $_GET['so'])
 {
  setcookie("login[password]", "", time()-3600);
  header('Location:login.php');
  exit;
 }
if(isset($_COOKIE['login']['username']) && isset($_COOKIE['login']['password']))
 {
  header('Location:authormaster.php');
 }
?>

<html>
<body>
<form action="setme.php" method="post" name="frmLogin">
Username:<input name="txtusername" type="text" value="<?php if(isset($_COOKIE['login']['username'])) echo $_COOKIE['login']['username']; ?>"><br><br>
Password:<input name="txtpassword" type="password"><br><br>
<input name="chkrem" type="checkbox" value="REMEMBER">Remember<br>
<input name="submit" type="submit" value="Sign In">
</form>
</body>
</html> 
This is setme.php

Code: Select all

<?php
$result = mysql_pconnect("localhost","root","");
mysql_select_db("users");
$qry="SELECT * FROM Users WHERE Username='$_POST[txtusername]' AND Password='$_POST[txtpassword]'";
$res=mysql_query($qry);
$numrows=mysql_numrows($res);

if($numrows == 1)
 {
   if($_POST['chkrem']=="REMEMBER")
    {
      setcookie("login[Username]",$_POST['txtusername'], time()+360);
      setcookie("login[Password]",$_POST['txtpassword'], time()+360);
    }
   header('Location:authormaster.php');
 }    
else
 {
  $msg = "Invalid Username/Password";
  header("Location:login.php?msg=".msg);
 }
?>
This is authormaster.php

Code: Select all

<html>
<head>
<title>Untitled 1</title>
</head>
<body>
<a href="login.php?state=so">Sign Out</a>&nbsp;&nbsp;&nbsp;<a href="login.php?state=cu">Change user</a>
</body>
</html>
When i load login.php i get two errors which say- Notice: Undefined variable: state in C:\wamp\www\KKHSOU\login\login.php on line 3

Notice: Undefined variable: state in C:\wamp\www\KKHSOU\login\login.php on line 11

The above scripts should perform the following-

1. Accept a valid username and password.
2. Authenticate the user against the database and serve the ‘authormaster.php’ page if validated.
3. If the REMEMBER ME option is selected on the login page, then the next time the user logs-in the server should directly serve the user with ‘authormaster.php’.
4. The ‘authormaster.php’ page should allow users to do the following-
a) Sign Out – brings the user back to login.php and populates the ‘username’ field with the last username used to login.
b) Change User – allows the user to login as a different user by bringing back the login page with nothing populated in the text boxes.

-- Please help...

Re: Login script

Posted: Fri May 27, 2011 12:27 pm
by oscardog
Notices won't affect the running of the script at all. The most common form of notice is given twice in your post, without even looking at the code it probably uses a variable (i.e. echo $var;) that hasn't been instantiated. Change your php.ini file so that it doesn't show errors.

For testing leaving it to show errors is useful though, but when you make the website live make sure errors are not shown.

Re: Login script

Posted: Fri May 27, 2011 2:01 pm
by califdon
I take it you are just beginning to learn how to use PHP. Perhaps the most valuable thing you can learn is how to interpret error messages.

Consider those 2 messages:
Notice: Undefined variable: state in C:\wamp\www\KKHSOU\login\login.php on line 3
Notice: Undefined variable: state in C:\wamp\www\KKHSOU\login\login.php on line 11
First of all, they tell you that these are Notices (not Warnings or Fatal Errors). This is significant. Learn what the differences are between these messages. See http://www.onlinehowto.net/Tutorials/PH ... tices/1361.

Next, they tell you what kind of an error it is, in this case it is an undefined variable. Again, significant, because it rules out other issues such as syntax errors, etc. It means that you have tried to use the value stored in a variable that hasn't been defined previously (obviously impossible to do).

And it tells you very specifically WHICH variable hasn't been defined, in this case "state".

Finally, they tell you precisely where the problem is, both the name of the script and the path to find it, and the line number.

If you just read the error messages with that understanding, you should see that the very first line of actual PHP code in your script,
if($state == $_GET['cu'])
tries to define and test for the value of the variable $state. But what happens if there is no URL parameter passed? For example, if you just call the script in your browser, without any "?cu=1 2 3" at the end, $state won't be defined. Thus, PHP kindly sends you a Notice of that fact.

As oscardog advised you, Notices are helpful while you're getting a script to run, but you don't want them popping up when page goes into production. You can control this in your scripts, as discussed in http://php.about.com/od/troubleshooting ... orting.htm and many other tutorials.

Re: Login script

Posted: Sat May 28, 2011 1:06 am
by Pazuzu156
This is a very simple way to make a login script and shouldn't take too long to make as it took me 10 minutes.

This script calls usernames and passwords from a database, so create a database of your choice, and add the following query into the database for the login script.

Code: Select all

CREATE TABLE IF NOT EXISTS `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) NOT NULL,
  `password` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
)
Next, create a dummy user with an md5 hashed password. For the md5 version of "password" use: 5f4dcc3b5aa765d61d8327deb882cf99

Now, just use the following scripts and then you should have a simple login script.

connect.php

Code: Select all

<?php
// connect to database
$connect = mysql_connect("localhost","root","") or die (mysql_error());
$seldb = mysql_select_db("test") or die (mysql_error());
?>
index.php

Code: Select all

<?php
// require connect.php and begin session
require 'connect.php';
session_start()
?>
<h1>Welcome
<?php
// if user is logged in, display their name. if not, display guest
if(isset($_SESSION['user'])) {
	echo $_SESSION['user'];
} else { 
	?> Guest<?php } ?>!</h1>
<?php
if(isset($_SESSION['user'])) {
	echo 'You see this because you are logged in. <a href="./logout.php">Logout</a>';
} else { ?>
You are not currently logged in. Please use the form bellow to login.
<form action="login.php" method="post">
	Username: <input type="text" name="username">
	Password: <input type="password" name="password"> <input type="submit" name="login" value="Login">
</form>
<?php } ?>
login.php

Code: Select all

<?php
// require connect.php and begin session
require 'connect.php';
session_start();

// set username and password values based on values sent over from the login form
$username = $_POST['username'];
$password = $_POST['password'];

// check if the user has entered in both the username and password
// if not, throw an error telling them they need to do so
if($username&&$password) {
	// hash password
	$password = md5($password);
	// pull the username and password from db based on the info given from the login form
	$login = sprintf("SELECT * FROM users WHERE username = '%s' AND password = '%s'", mysql_real_escape_string($username), mysql_real_escape_string($password));
	$query = mysql_query($login) or die (mysql_error());
	//get the number of rows
	$numrows = mysql_num_rows($query);
	// if for = 1, then user type in matching username and password if so, run code
	// else tell them they entered in an invalid username and password combo
	if($numrows==1) {
		// set the session to the username provided
		$_SESSION['user'] = $username;
		// give a personal hello and allow the user to return home
		echo 'Hello, ' . $_SESSION['user'] . '. You have successfully logged in! <a href="./">Return Home</a>';
	 } else {
		echo 'Incorrect username and password combination!';
	 }
} else {
	echo 'You need to type in your username and password!';
}
?>
logout.php

Code: Select all

<?php
session_start();

// destory session so the user can be logged out
session_destroy();

// allow user to return home with a success message
echo 'You have successfully logged out. <a href="./">Return home</a>.';
?>
Thus we have a simple login/logout script. This script uses sessions instead of cookies like your example, but I hope you learn from this example.

Re: Login script

Posted: Wed Jun 29, 2011 1:40 am
by Gopesh
Thanks a lot Pazuzu156 for this wonderful login script using sessions.Surely it will help all the beginners to develop a login form with enhanced security....

Re: Login script

Posted: Sat Jul 02, 2011 3:31 pm
by Pazuzu156
No problem at all. Glad to help someone.

Re: Login script

Posted: Sat Jul 02, 2011 8:28 pm
by flying_circus
Gopesh wrote:Thanks a lot Pazuzu156 for this wonderful login script using sessions.Surely it will help all the beginners to develop a login form with enhanced security....
Login scripts always seem to cause the most questions on this board, why, because login scripts are HARD!

I don't like Pazuzu's script. While it seemingly accomplishes the task, it's not done well. It's not doing you any favors by using it, if you are concerned with security. I picked 3 points about his script to focus on:
1) Use the MySQLi extenstion.
2) NEVER EVER EVER use a hash algorithm like md5 for storing passwords, that goes for SHA1 as well. Never store a password hash that has not been salted.
3) ALWAYS validate your input.
4) Pay attention to Character Encoding

I spent some time writing this script in reply to your post. It's not the be-all end-all, in fact, the contrary. It contains only the bare minimum functionality, and it is expected that you will customize it and improve it for your needs. It consists of 3 files, 2 of which are template files. It's kind of long, but it's important to understand why certain steps are done.

I hope this helps, good luck.

index.php

Code: Select all

<?php declare(encoding = 'UTF-8');
/*
 * Schema
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `user_username` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
  `user_password` char(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  `user_password_salt` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  `user_email` varchar(128) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
*/

  # Configuration
    # Debugging - To be turned off before relase
      error_reporting(E_ALL);
      ini_set('display_errors', 'On');
      
    # URI's
      define('WWW_ROOT', 'www.example.org');
      
    # Encoding
      define('DEFAULT_CHARACTER_SET', 'utf8');
      define('DEFAULT_COLLATION', 'utf8_unicode_ci');
      
    # Database Credentials
      define('DB_HOSTNAME', 'localhost');
      define('DB_USERNAME', 'myUsername');
      define('DB_PASSWORD', 'myPassword');
      define('DB_DATABASE', 'myDatabase');
      define('DB_PORT', '3306');
      
    # Hash Pepper
      define("HASH_PEPPER", "OTEyNjY1MDg0NzY3Y2E2M2ZmMzA0ZDE4NzQ0NGFjNjY3NDlkOGFkYThkZWE0ZDQ4YzAyNGE1ZjYzNDcxMmI4YjRjM2UxMmVjYmNjNDcxNDA1OGVlODlmZTExOGE5YmNkOGZjZGI2NWMxOTg5MjM1OGIwNDE1MzY4OTI4ZWQ4OTY=");
    
    # Validation Constraints
      define("CONSTR_EMAIL_MAX_LENGTH", 128);
      define("CONSTR_EMAIL_MIN_LENGTH", 6);
      define("CONSTR_PASSWORD_MIN_LENGTH", 6);
      define("CONSTR_USERNAME_MAX_LENGTH", 32);
      define("CONSTR_USERNAME_MIN_LENGTH", 3);
  
    # Regular Expressions
      define('REGEX_EMAIL_ADDRESS','/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/ui');
      define('REGEX_USERNAME','/^[A-Z][A-Z0-9_]{' . (CONSTR_USERNAME_MIN_LENGTH - 1) . ',' . (CONSTR_USERNAME_MAX_LENGTH - 1) . '}$/ui');
    
  # Start Session
    session_start();
    
  # Database Connection
    $database = new mysqli(DB_HOSTNAME, DB_USERNAME, DB_PASSWORD, DB_DATABASE, DB_PORT);
    if($database->connect_error)
      exit('Connect Error (' . $database->connect_errno . '): ' . $database->connect_error);
      
  # Set Default Character Set and Collation
    $database->set_charset(DEFAULT_CHARACTER_SET);
    $database->query("SET NAMES '" . DEFAULT_CHARACTER_SET . "' COLLATE '". DEFAULT_COLLATION ."';");
    
  # Basic Functions
    function is_https() {
    # A hack for an example.
      return true;
      
      if(isset($_SERVER['HTTPS']) && mb_strtolower($_SERVER['HTTPS']) == "on")
        return true;
      else if(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 1)
        return true;
      else if(isset($_SERVER['SERVER_PORT']) && $_SERVER["SERVER_PORT"] == 443)
        return true;
      else
        return false;
    }
    
    function is_postback() {
      return (isset($_SERVER['REQUEST_METHOD']) && mb_strtolower($_SERVER['REQUEST_METHOD']) == "post");
    }
    
    function redirect($location, $replace = true, $code = 302) {
      header("location:" . $location, $replace, $code);
      exit();
    }
    
    function generate_salt($length = 32) {
      $fp = @fopen('/dev/urandom', 'rb');
      if($fp) {
        $random = fread($fp, $length);
        fclose($fp);
        return bin2hex($random);
      }
      
      return false;
    }
    
    function hash_password($salt, $pepper, $password, $algorithm = 'SHA512') {
      return hash($algorithm, $salt . $pepper . $password);
    }
    
    function is_logged_in() {
      if(isset($_SESSION['user_id']) && $_SESSION['user_id'] > 0)
        return true;
      else
        return false;
    }
  
  # Process Request
    $request = isset($_GET['request']) ? $_GET['request'] : '';
    
  # Route Request - Whitelist
    switch($request) {
      case 'register':
      # Is this a secure POST request?
        if(is_https() && is_postback()) {
        # Verify the POST vars exist before referencing them
          $username = isset($_POST['username']) ? $_POST['username'] : '';
          $password = isset($_POST['password']) ? $_POST['password'] : '';
          $password2 = isset($_POST['password2']) ? $_POST['password2'] : '';
          $email = isset($_POST['email']) ? $_POST['email'] : '';
          $email2 = isset($_POST['email2']) ? $_POST['email2'] : '';
          
        # Verify that $username, $password, and $email are not empty
          if(!empty($username) && !empty($password) && !empty($email)) {
          # Compare passwords
            if($password !== $password2)
              redirect(WWW_ROOT . '/index.php?register_error=' . urlencode('Passwords do not match')); // Passwords don't match - Redirect
            else
              unset($password2);
              
          # Compare emails
            if($email !== $email2)
              redirect(WWW_ROOT . '/index.php?register_error=' . urlencode('Email Addresses do not match')); // Emails don't match - Redirect
            else
              unset($email2);
              
          # Validate the username here.
          # Check for min/max length requirements, invalid characters, proper format
            if(!preg_match(REGEX_USERNAME, $username))
              redirect(WWW_ROOT . '/index.php?register_error=' . urlencode('Invalid Username')); // Invalid Username - Redirect
              
          # You should validate the password here.
          # Check for min length requirements
            if(mb_strlen($password) < CONSTR_PASSWORD_MIN_LENGTH)
              redirect(WWW_ROOT . '/index.php?register_error=' . urlencode('Password not long enough')); // Password insufficient length - Redirect
              
          # Validate the email address here.
          # Check for min/max length requirements, invalid characters, proper format
            if(mb_strlen($email) < CONSTR_EMAIL_MIN_LENGTH || mb_strlen($email) > CONSTR_EMAIL_MAX_LENGTH)
              redirect(WWW_ROOT . '/index.php?register_error=' . urlencode('Invalid Email Address')); // Invalid Email - Redirect
              
            if(!preg_match(REGEX_EMAIL_ADDRESS, $email))
              redirect(WWW_ROOT . '/index.php?register_error=' . urlencode('Invalid Email Address')); // Invalid Email - Redirect
              
          # Verify username does not already exist in the database
            $records = $database->query(sprintf("SELECT `user_id` FROM `users` WHERE `user_username` = '%s';",
                                        $database->real_escape_string($username)));
                                        
            if($records->num_rows > 0) {
            # Username Already Exists
              redirect(WWW_ROOT . '/index.php?register_error=' . urlencode('Username already taken')); // Username already exists - Redirect
            } else {
            # Username Available
              $salt = generate_salt();
              $password = hash_password($salt, HASH_PEPPER, $password);
              
            # Add New User
              $database->query(sprintf("INSERT INTO `users` (`user_username`, `user_password`, `user_password_salt`, `user_email`) VALUES ('%s', '%s', '%s', '%s');",
                                       $database->real_escape_string($username),
                                       $database->real_escape_string($password),
                                       $database->real_escape_string($salt),
                                       $database->real_escape_string($email)));
                                        
            # Verify Success
              $user_id = $database->insert_id;
              
              if($user_id) {
              # Log User In
                $_SESSION['user_id'] = $user_id;
                
              # Take the user to their homepage
                redirect(WWW_ROOT . '/index.php'); // Login Sucessful - Redirect
              } else {
              # Unsuccessful - possible query error
                redirect(WWW_ROOT . '/index.php?register_error=' . urlencode('Unable to process your request.  Try again later.')); // We have a problem with our database server, don't inform the user - Redirect
              }
            }
          } else {
          # Required Form Fields Missing
            redirect(WWW_ROOT . '/index.php?register_error=' . urlencode('Please fill in all form fields')); // Redirect
          }
        }
        
      # Make sure this page is not rendered
        redirect(WWW_ROOT . '/index.php'); // Redirect to Homepage
        break;
      case 'login':
      # Is this a secure POST request?
        if(is_https() && is_postback()) {
        # Verify the POST vars exist before referencing them
          $username = isset($_POST['username']) ? $_POST['username'] : '';
          $password = isset($_POST['password']) ? $_POST['password'] : '';
          
        # Verify that $username and $password are not empty
          if(!empty($username) && !empty($password)) {
          # Validate the username here.
          # Check for min/max length requirements, invalid characters, proper format
            if(!preg_match(REGEX_USERNAME, $username))
              redirect(WWW_ROOT . '/index.php?login_error=' . urlencode('Invalid Username')); // Invalid Username - Redirect
              
          # Fetch Records From the Database that Match $username
            $records = $database->query(sprintf("SELECT `user_id`, `user_password`, `user_password_salt` FROM `users` WHERE `user_username` = '%s';",
                                        $database->real_escape_string($username)));
                                        
          # Verify Number of Records
            if($records->num_rows == 1) {
            # Found 1 record matching username
              $record = $records->fetch_assoc();
              
            # Check that the supplied password matches whats stored in the database
              if(hash_password($record['user_password_salt'], HASH_PEPPER, $password) == $record['user_password']) {
              # Do not store user credentials in sessions or cookies!!!
                $_SESSION['user_id'] = $record['user_id'];
                
              # Take the user to their homepage
                redirect(WWW_ROOT . '/index.php'); // Login Sucessful - Redirect
              } else {
              # Incorrect Password
                redirect(WWW_ROOT . '/index.php?login_error=' . urlencode('Incorrect Username/Password')); // Username not found - Redirect
              }
            } else if($records->num_rows > 1) {
            # Too many users found - Problem with database
              redirect(WWW_ROOT . '/index.php?login_error=' . urlencode('Unable to process your request.  Try again later.')); // We have a problem with our database server, don't inform the user - Redirect
            } else {
            # Username not found - try again
            redirect(WWW_ROOT . '/index.php?login_error=' . urlencode('Incorrect Username/Password')); // Username not found - Redirect
            }
          }
        }
        
      # Make sure this page is not rendered
        redirect(WWW_ROOT . '/index.php'); // Redirect to Homepage
        break;
      case 'logout':
      # Is this a secure request and is the user logged in?
        if(is_https() && is_logged_in())
          unset($_SESSION['user_id']);
          
      # Take the user to the homepage
        redirect(WWW_ROOT . '/index.php'); // Take the user to the home page - Redirect
        break;
      default:
      # Display Homepage
        if(is_logged_in()) {
          include_once('includes/my_account.php');
        } else {
          include_once('includes/homepage.php');
        }
        break;
    }
    
  # Close database connection
    $database->close();
?>

includes/homepage.php

Code: Select all

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Better Registration/Login Example</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="Author" content="Devnetwork: Flying_Circus" />
  </head>
  <body>
    <h1>Login</h1>
    <form action="index.php?request=login" method="post" accept-charset="UTF-8">
    <div>
<?php
  $login_error = isset($_GET['login_error']) ? $_GET['login_error'] : '';
  
  if(!empty($login_error))
    print "<p>Error: {$login_error}</p>";
?>
      Username: <input type="text" name="username" value="" /><br />
      Password: <input type="password" name="password" value="" /><br />
      <input type="submit" value="Login" />
    </div>
    </form>
    <br />
    <br />
    <hr />
    <br />
    <h1>Register</h1>
    <form action="index.php?request=register" method="post" accept-charset="UTF-8">
    <div>
<?php
  $register_error = isset($_GET['register_error']) ? $_GET['register_error'] : '';
  
  if(!empty($register_error))
    print "<p>Error: {$register_error}</p>";
?>
      Username: <input type="text" name="username" value="" /><br />
      Password: <input type="password" name="password" value="" /><br />
      Confirm Password: <input type="password" name="password2" value="" /><br />
      Email: <input type="text" name="email" value="" /><br />
      Confirm Email: <input type="text" name="email2" value="" /><br />
      <input type="submit" value="Register" />
    </div>
    </form>
  </body>
</html>

includes/my_account.php

Code: Select all

<?php
  # Fetch User Info
    $records = $database->query(sprintf("SELECT `user_username` FROM `users` WHERE `user_id`='%s';",
                                        $database->real_escape_string($_SESSION['user_id'])));
                                        
    if($records->num_rows == 1) {
      $user = $records->fetch_assoc();
    }
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Better Registration/Login Example</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="Author" content="Devnetwork: Flying_Circus" />
  </head>
  <body>
    <h1>My Account</h1>
    <br />
    <p>Hi, <?php print htmlentities($user['user_username']); ?>!</p>
    <br />
    <a href="index.php?request=logout">Logout</a>
  </body>
</html>

Re: Login script

Posted: Sun Jul 03, 2011 6:22 pm
by Pazuzu156
I love your script. I agree, logins, registrations, and password resets are very complicating, and mysqli is best in doing this by far. But my script is a very quick and simple script and is suppose to be a generalized idea on a bare structure of a login. Something like my script is for new php developers who haven't fully dived into php. But anyhow, your script is really good, and most logins are hundreds of lines long if done correctly checking every single area of processing, injections, etc. :D

Re: Login script

Posted: Mon Jul 04, 2011 1:34 am
by flying_circus
Pazuzu156 wrote:Something like my script is for new php developers who haven't fully dived into php.
Thanks Pazuzu. I'm glad you posted your script, it prompted me to post as well. The more discussion there is, the better off everyone is. When information is available, we all learn something. Hopefully someone will comment on my code to make it better, I like improvements. :)

I want to caution, especially when replying to beginners, to be careful of the information we offer up. I know it's tough to sink a chunk of time into a reply here, it doesnt pay anything, and often the OP doesnt even come back and say thanks... but we also have to make sure we offer the best advice we can. My main problem was with the md5 hashed password, especially since it doesnt take much more effort to use a stronger algorithm. When we coach beginners, we want to set them off on the right foot, ya know? Who knows, maybe this post provided me an opportunity to share an improvement with you? Either way, thanks for the comment!

Re: Login script

Posted: Thu Jul 07, 2011 3:31 pm
by Pazuzu156
[quote=flying_circus]Who knows, maybe this post provided me an opportunity to share an improvement with you? Either way, thanks for the comment![/quote]

You are quite welcome. And i agree, using a salt to help with the hashing is a better way to do so. I even go through the trouble of randomizing the hash once it is done. and for the salt I normally use

Code: Select all

define(PW_SALT, '(+3%_');
Anyhow, good discussions lead to good outcomes :D