Wrox Programmer Forums

Need to download code?

View our list of code downloads.

Register | FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read
BOOK: Professional PHP 5 ISBN: 978-0-7645-7282-1
This is the forum to discuss the Wrox book Professional PHP5 by Ed Lecky-Thompson, Heow Eide-Goodman, Steven D. Nowicki, Alec Cove; ISBN: 9780764572821
Welcome to the p2p.wrox.com Forums.

You are currently viewing the BOOK: Professional PHP 5 ISBN: 978-0-7645-7282-1 section of the Wrox Programmer to Programmer discussions. This is a community of tens of thousands of software programmers and website developers including Wrox book authors and readers. As a guest, you can read any forum posting. By joining today you can post your own programming questions, respond to other developers’ questions, and eliminate the ads that are displayed to guests. Registration is fast, simple and absolutely free .
DRM-free e-books 300x50
Reply
 
Thread Tools Display Modes
  #1 (permalink)  
Old March 3rd, 2008, 01:07 AM
Registered User
 
Join Date: Mar 2008
Location: , , .
Posts: 1
Thanks: 0
Thanked 3 Times in 1 Post
Default Working Code - Ch15

Hey Guys,

   One of my frustrations have been the lack of working code samples.  Which, I'm not sure if I've learned more from reading the book or hunting for the solutions to the downright terrible code samples.  Maybe we can start a repository if someone has web space?

If you are stuck, here is complete code for the usersession.phpm file for chapter 15.  I found that the test files in the book's code download work fine with no modification.  Also, I've made a couple tweaks to make garbage collection actually work as well as eliminate redundant session variables.

Sorry for the funky formatted code.  I use dreamweaver which I've set to understand tab's as 2 spaces.

About this code -
O/S: FreeBSD 6.3
Webserver: Apache 2.2.8
PHP Version: 5.2.5
MySQL: 5.0.45


MySQL DB Script:
Code:
use php5ch15;

CREATE TABLE user_session (
  id int NOT NULL AUTO_INCREMENT,
  ascii_session_id varchar(32),
  logged_in bool,
  user_id int(4),
  last_impression timestamp,
  created timestamp,  
  user_agent varchar(256),
    PRIMARY KEY (id)
);

CREATE TABLE user (
  id int NOT NULL AUTO_INCREMENT,
  username varchar(32),
  md5_pw varchar(32),
  first_name varchar(64),  
  last_name varchar(64),
    PRIMARY KEY (id)
);

CREATE TABLE session_variable (
  id int NOT NULL AUTO_INCREMENT,
  session_id int(4),
  variable_name varchar(64),
  variable_value text,
    PRIMARY KEY (id)
);

INSERT INTO user (username,md5_pw,first_name,last_name) VALUES ('ed','827ccb0eea8a706c4c34a16891f84e7b','Ed','Lecky-Thompson');


usersession.phpm:
Code:
<?php
  define("DB_USER_NAME", "php5");
  define("DB_PASSWORD", "php5password");
  define("DB_SERVER", "localhost");
  define("DB_DATABASE", "php5ch15");

  class UserSession {
    private $php_session_id;
    private $native_session_id;
    private $dbhandle;
    private $logged_in;
    private $user_id;
    private $session_timeout = 600;      # 10 minute inactivity timeout
    private $session_lifespan = 3600;    # 1 hour session duration

    public function __construct() {
      # Connect to database
        $this->dbhandle = mysqli_connect(DB_SERVER,DB_USER_NAME,DB_PASSWORD,DB_DATABASE) or die ("MySQL Error: --> " . mysqli_error($this->dbhandle));
      # Set up the handler
        session_set_save_handler(
            array(&$this, '_session_open_method'), 
            array(&$this, '_session_close_method'), 
            array(&$this, '_session_read_method'), 
            array(&$this, '_session_write_method'), 
            array(&$this, '_session_destroy_method'), 
            array(&$this, '_session_gc_method')
        );
      # Check the cookie passed - if one is - if it looks wrong we'll scrub it right away      
        $strUserAgent = $_SERVER["HTTP_USER_AGENT"];
        if ($_COOKIE["PHPSESSID"]) {
          # Security and age check
            $this->php_session_id = $_COOKIE["PHPSESSID"];
            $stmt = "SELECT id FROM user_session WHERE ascii_session_id = '" . $this->php_session_id . "' AND ((UNIX_TIMESTAMP(now()) - UNIX_TIMESTAMP(created)) < '" . $this->session_lifespan . "') AND user_agent='" . $strUserAgent . "' AND ((UNIX_TIMESTAMP(now()) - UNIX_TIMESTAMP(last_impression)) <= '" . $this->session_timeout . "' OR last_impression IS NULL)";
            $result = mysqli_query($this->dbhandle,$stmt);
            if (mysqli_num_rows($result) == 0) {
              # Set failed flag
                $failed = 1;
              # Delete from database - we do garbage cleanup at the same time
                $result = mysqli_query($this->dbhandle,"DELETE FROM user_session WHERE (ascii_session_id = '" . $this->php_session_id . "') OR (UNIX_TIMESTAMP(now()) - UNIX_TIMESTAMP(created)) > '" . $this->session_lifespan . "'");
              # Clean up stray session variables
                $result = mysqli_query($this->dbhandle,"DELETE FROM session_variable WHERE session_id NOT IN (SELECT id FROM user_session)");
              # Get rid of this one... this will force PHP to give us another
                unset($_COOKIE["PHPSESSID"]);
            };
        };
      # Set the life time for the cookie
        session_set_cookie_params($this->session_lifespan);
      # Call the session_start method to get things started
        session_start();
    }

    public function Impress() {
      if ($this->native_session_id) {
        $result = mysqli_query($this->dbhandle,"UPDATE user_session SET last_impression = now() WHERE id = " . $this->native_session_id);
      };
    }

    public function IsLoggedIn() {
      return($this->logged_in);
    }

    public function GetUserID() {
      if ($this->logged_in) {
        return($this->user_id);
      } else {
        return(false);
      };
    }

    public function GetUserObject() {
      if ($this->logged_in) {
        if (class_exists("user")) {
          $objUser = new User($this->user_id);
          return($objUser);
        } else {
          return(false);
        };
      };
    }

    public function GetSessionIdentifier() {
      return($this->php_session_id);
    }

    public function Login($strUsername, $strPlainPassword) {
      $strMD5Password = md5($strPlainPassword);
      $stmt = "SELECT id FROM user WHERE username = '$strUsername' AND md5_pw = '$strMD5Password'";
      $result = mysqli_query($this->dbhandle,$stmt);
      if (mysqli_num_rows($result)>0) {
        $row = mysqli_fetch_array($result);
        $this->user_id = $row["id"];
        $this->logged_in = true;
        $result = mysqli_query($this->dbhandle,"UPDATE user_session SET logged_in = true, user_id = " . $this->user_id . " WHERE id = " . $this->native_session_id);
        return(true);
      } else {
        return(false);
      };
    }  

    public function LogOut() {
      if ($this->logged_in == true) {
        $result = mysqli_query($this->dbhandle,"UPDATE user_session SET logged_in = false, user_id = 0 WHERE id = " . $this->native_session_id);
        $this->logged_in = false;
        $this->user_id = 0;
        return(true);
      } else {
        return(false);
      };
    }

    public function __get($nm) {
      $result = mysqli_query($this->dbhandle,"SELECT variable_value FROM session_variable WHERE session_id = " . $this->native_session_id . " AND variable_name = '" . $nm . "'");
      if (mysqli_num_rows($result)>0) {
        $row = mysqli_fetch_array($result);
        return(unserialize($row["variable_value"]));
      } else {
        return(false);
      };
    }


    public function __set($nm, $val) {
      if(!$this->__get($nm)) {  // Session Variable doesn't exist: Create it
        $strSer = serialize($val);
        $stmt = "INSERT INTO session_variable (session_id, variable_name, variable_value) VALUES (" . $this->native_session_id . ", '$nm', '$strSer')";
        $result = mysqli_query($this->dbhandle,$stmt);
      } else {  // Session Variable exists: Update it
        $strSer = serialize($val);
        $stmt = "UPDATE session_variable SET variable_value = '$strSer' WHERE session_id = '" . $this->native_session_id . "' AND variable_name = '$nm'";
        $result = mysqli_query($this->dbhandle,$stmt);
      }
    }

    private function _session_open_method($save_path, $session_name) {
      # Do nothing
      return(true);
    }

    private function _session_close_method() {
      mysqli_close($this->dbhandle);
      return(true);
    }

    private function _session_read_method($id) {
      # We use this to determine whether or not our session actually exists.
        $strUserAgent = $_SERVER["HTTP_USER_AGENT"];
        $this->php_session_id = $id;
      # Set failed flag to 1 for now
        $failed = 1;
      # See if this exists in the database or not.
        $result = mysqli_query($this->dbhandle,"SELECT id, logged_in, user_id FROM user_session WHERE ascii_session_id = '$id'");
        if (mysqli_num_rows($result)>0) {
          $row = mysqli_fetch_array($result);
          $this->native_session_id = $row["id"];
          if ($row["logged_in"] == "t") {
            $this->logged_in = true;
            $this->user_id = $row["user_id"];
          } else {
            $this->logged_in = false;
          };
        } else {
          $this->logged_in = false;
          # We need to create an entry in the database
            $result = mysqli_query($this->dbhandle,"INSERT INTO user_session (ascii_session_id, logged_in, user_id, created, user_agent) VALUES ('$id','f',0,now(),'$strUserAgent')");
          # Now get the true ID
            $result = mysqli_query($this->dbhandle,"SELECT id FROM user_session WHERE ascii_session_id = '$id'");
            $row = mysqli_fetch_array($result);
            $this->native_session_id = $row["id"];
        };
      # Just return empty string
      return("");
    }

    private function _session_write_method($id, $sess_data) {
      return(true);
    }

    private function _session_destroy_method($id) {
      $result = mysqli_query($this->dbhandle,"DELETE FROM user_session WHERE ascii_session_id = '$id'");
      return($result);
    }

    private function _session_gc_method($maxlifetime) {
      # Garbage collection for expired sessions
        $result = mysqli_query($this->dbhandle,"DELETE FROM user_session WHERE (UNIX_TIMESTAMP(now()) - UNIX_TIMESTAMP(last_impression)) > '$maxlifetime'");
      # Clean up expired session variables
        $result = mysqli_query($this->dbhandle,"DELETE FROM session_variable WHERE session_id NOT IN (SELECT id FROM user_session)");
      return(true);
    }

    public function __destruct() {
      $this->_session_gc_method($this->session_lifespan);
      session_write_close();
    }
  }
?>
Reply With Quote
The Following 3 Users Say Thank You to slurp56 For This Useful Post:
johnny.s (March 19th, 2010), pawel (June 16th, 2009), shegxzyl (December 2nd, 2009)
  #2 (permalink)  
Old June 16th, 2009, 04:55 AM
Registered User
 
Join Date: Jun 2009
Posts: 1
Thanks: 1
Thanked 0 Times in 0 Posts
Default misprint

Hay,

little misprint: "logged_in" in table user_session is tinyint, it should be 1 instead of "t";

Big thanks for your work;)

Quote:
Originally Posted by slurp56 View Post
usersession.phpm:
Code:
...

    private function _session_read_method($id) {
      # We use this to determine whether or not our session actually exists.
        $strUserAgent = $_SERVER["HTTP_USER_AGENT"];
        $this->php_session_id = $id;
      # Set failed flag to 1 for now
        $failed = 1;
      # See if this exists in the database or not.
        $result = mysqli_query($this->dbhandle,"SELECT id, logged_in, user_id FROM user_session WHERE ascii_session_id = '$id'");
        if (mysqli_num_rows($result)>0) {
          $row = mysqli_fetch_array($result);
          $this->native_session_id = $row["id"];
          if ($row["logged_in"] == "t") {
            $this->logged_in = true;
            $this->user_id = $row["user_id"];
          } else {
            $this->logged_in = false;
          };
      ....
Reply With Quote
  #3 (permalink)  
Old March 19th, 2010, 06:54 AM
Registered User
 
Join Date: Mar 2010
Posts: 1
Thanks: 1
Thanked 0 Times in 0 Posts
Default

Here's a little misunderstanding with original __set method used in this code:
Quote:
Originally Posted by slurp56 View Post
Code:
    public function __set($nm, $val) {
      if(!$this->__get($nm)) {  // Session Variable doesn't exist: Create it
        $strSer = serialize($val);
        $stmt = "INSERT INTO session_variable (session_id, variable_name, variable_value) VALUES (" . $this->native_session_id . ", '$nm', '$strSer')";
        $result = mysqli_query($this->dbhandle,$stmt);
      } else {  // Session Variable exists: Update it
        $strSer = serialize($val);
        $stmt = "UPDATE session_variable SET variable_value = '$strSer' WHERE session_id = '" . $this->native_session_id . "' AND variable_name = '$nm'";
        $result = mysqli_query($this->dbhandle,$stmt);
      }
    }
If you save in session variables with values like:
empty string '', int 0, boolean false etc
and then will try to update variable value - the __set method will insert new variable with duplicated name in database instead of updating existing one.

That's happening because of returned __get unserialized value:
Code:
if (!$this->__get($nm)) etc...
To avoid this you'd better use this "handmade" validation:
Code:
    public function __set($nm, $val) {
      $strSer = serialize($val);
      $result = mysqli_query($this->dbhandle,"SELECT id FROM session_variable WHERE session_id = " . $this->native_session_id . " AND variable_name = '" . $nm . "'");
      if (mysqli_num_rows($result)>0) {  // Session Variable exists: Update it
          $row = mysqli_fetch_array($result);
          $id = $row["variable_value"];
          $stmt = "UPDATE session_variable SET variable_value = '$strSer' WHERE id = '$id'";
      } else {// Session Variable doesn't exist: Create it
        $stmt = "INSERT INTO session_variable (session_id, variable_name, variable_value) VALUES (" . $this->native_session_id . ", '$nm', '$strSer')";
      };
      $result = mysqli_query($this->dbhandle,$stmt);
    }

Last edited by johnny.s; March 19th, 2010 at 06:56 AM.
Reply With Quote
  #4 (permalink)  
Old April 24th, 2013, 10:32 AM
Registered User
Points: 69, Level: 1
Points: 69, Level: 1 Points: 69, Level: 1 Points: 69, Level: 1
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Feb 2013
Posts: 5
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Quote:
Originally Posted by slurp56 View Post
Hey Guys,

   One of my frustrations have been the lack of working code samples.  Which, I'm not sure if I've learned more from reading the book or hunting for the solutions to the downright terrible code samples.  Maybe we can start a repository if someone has web space?

If you are stuck, here is complete code for the usersession.phpm file for chapter 15.  I found that the test files in the book's code download work fine with no modification.  Also, I've made a couple tweaks to make garbage collection actually work as well as eliminate redundant session variables.

Sorry for the funky formatted code.  I use dreamweaver which I've set to understand tab's as 2 spaces.

About this code -
O/S: FreeBSD 6.3
Webserver: Apache 2.2.8
PHP Version: 5.2.5
MySQL: 5.0.45


MySQL DB Script:
Code:
use php5ch15;

CREATE TABLE user_session (
  id int NOT NULL AUTO_INCREMENT,
  ascii_session_id varchar(32),
  logged_in bool,
  user_id int(4),
  last_impression timestamp,
  created timestamp,  
  user_agent varchar(256),
    PRIMARY KEY (id)
);

CREATE TABLE user (
  id int NOT NULL AUTO_INCREMENT,
  username varchar(32),
  md5_pw varchar(32),
  first_name varchar(64),  
  last_name varchar(64),
    PRIMARY KEY (id)
);

CREATE TABLE session_variable (
  id int NOT NULL AUTO_INCREMENT,
  session_id int(4),
  variable_name varchar(64),
  variable_value text,
    PRIMARY KEY (id)
);

INSERT INTO user (username,md5_pw,first_name,last_name) VALUES ('ed','827ccb0eea8a706c4c34a16891f84e7b','Ed','Lecky-Thompson');


usersession.phpm:
Code:
<?php
  define("DB_USER_NAME", "php5");
  define("DB_PASSWORD", "php5password");
  define("DB_SERVER", "localhost");
  define("DB_DATABASE", "php5ch15");

  class UserSession {
    private $php_session_id;
    private $native_session_id;
    private $dbhandle;
    private $logged_in;
    private $user_id;
    private $session_timeout = 600;      # 10 minute inactivity timeout
    private $session_lifespan = 3600;    # 1 hour session duration

    public function __construct() {
      # Connect to database
        $this->dbhandle = mysqli_connect(DB_SERVER,DB_USER_NAME,DB_PASSWORD,DB_DATABASE) or die ("MySQL Error: --> " . mysqli_error($this->dbhandle));
      # Set up the handler
        session_set_save_handler(
            array(&$this, '_session_open_method'), 
            array(&$this, '_session_close_method'), 
            array(&$this, '_session_read_method'), 
            array(&$this, '_session_write_method'), 
            array(&$this, '_session_destroy_method'), 
            array(&$this, '_session_gc_method')
        );
      # Check the cookie passed - if one is - if it looks wrong we'll scrub it right away      
        $strUserAgent = $_SERVER["HTTP_USER_AGENT"];
        if ($_COOKIE["PHPSESSID"]) {
          # Security and age check
            $this->php_session_id = $_COOKIE["PHPSESSID"];
            $stmt = "SELECT id FROM user_session WHERE ascii_session_id = '" . $this->php_session_id . "' AND ((UNIX_TIMESTAMP(now()) - UNIX_TIMESTAMP(created)) < '" . $this->session_lifespan . "') AND user_agent='" . $strUserAgent . "' AND ((UNIX_TIMESTAMP(now()) - UNIX_TIMESTAMP(last_impression)) <= '" . $this->session_timeout . "' OR last_impression IS NULL)";
            $result = mysqli_query($this->dbhandle,$stmt);
            if (mysqli_num_rows($result) == 0) {
              # Set failed flag
                $failed = 1;
              # Delete from database - we do garbage cleanup at the same time
                $result = mysqli_query($this->dbhandle,"DELETE FROM user_session WHERE (ascii_session_id = '" . $this->php_session_id . "') OR (UNIX_TIMESTAMP(now()) - UNIX_TIMESTAMP(created)) > '" . $this->session_lifespan . "'");
              # Clean up stray session variables
                $result = mysqli_query($this->dbhandle,"DELETE FROM session_variable WHERE session_id NOT IN (SELECT id FROM user_session)");
              # Get rid of this one... this will force PHP to give us another
                unset($_COOKIE["PHPSESSID"]);
            };
        };
      # Set the life time for the cookie
        session_set_cookie_params($this->session_lifespan);
      # Call the session_start method to get things started
        session_start();
    }

    public function Impress() {
      if ($this->native_session_id) {
        $result = mysqli_query($this->dbhandle,"UPDATE user_session SET last_impression = now() WHERE id = " . $this->native_session_id);
      };
    }

    public function IsLoggedIn() {
      return($this->logged_in);
    }

    public function GetUserID() {
      if ($this->logged_in) {
        return($this->user_id);
      } else {
        return(false);
      };
    }

    public function GetUserObject() {
      if ($this->logged_in) {
        if (class_exists("user")) {
          $objUser = new User($this->user_id);
          return($objUser);
        } else {
          return(false);
        };
      };
    }

    public function GetSessionIdentifier() {
      return($this->php_session_id);
    }

    public function Login($strUsername, $strPlainPassword) {
      $strMD5Password = md5($strPlainPassword);
      $stmt = "SELECT id FROM user WHERE username = '$strUsername' AND md5_pw = '$strMD5Password'";
      $result = mysqli_query($this->dbhandle,$stmt);
      if (mysqli_num_rows($result)>0) {
        $row = mysqli_fetch_array($result);
        $this->user_id = $row["id"];
        $this->logged_in = true;
        $result = mysqli_query($this->dbhandle,"UPDATE user_session SET logged_in = true, user_id = " . $this->user_id . " WHERE id = " . $this->native_session_id);
        return(true);
      } else {
        return(false);
      };
    }  

    public function LogOut() {
      if ($this->logged_in == true) {
        $result = mysqli_query($this->dbhandle,"UPDATE user_session SET logged_in = false, user_id = 0 WHERE id = " . $this->native_session_id);
        $this->logged_in = false;
        $this->user_id = 0;
        return(true);
      } else {
        return(false);
      };
    }

    public function __get($nm) {
      $result = mysqli_query($this->dbhandle,"SELECT variable_value FROM session_variable WHERE session_id = " . $this->native_session_id . " AND variable_name = '" . $nm . "'");
      if (mysqli_num_rows($result)>0) {
        $row = mysqli_fetch_array($result);
        return(unserialize($row["variable_value"]));
      } else {
        return(false);
      };
    }


    public function __set($nm, $val) {
      if(!$this->__get($nm)) {  // Session Variable doesn't exist: Create it
        $strSer = serialize($val);
        $stmt = "INSERT INTO session_variable (session_id, variable_name, variable_value) VALUES (" . $this->native_session_id . ", '$nm', '$strSer')";
        $result = mysqli_query($this->dbhandle,$stmt);
      } else {  // Session Variable exists: Update it
        $strSer = serialize($val);
        $stmt = "UPDATE session_variable SET variable_value = '$strSer' WHERE session_id = '" . $this->native_session_id . "' AND variable_name = '$nm'";
        $result = mysqli_query($this->dbhandle,$stmt);
      }
    }

    private function _session_open_method($save_path, $session_name) {
      # Do nothing
      return(true);
    }

    private function _session_close_method() {
      mysqli_close($this->dbhandle);
      return(true);
    }

    private function _session_read_method($id) {
      # We use this to determine whether or not our session actually exists.
        $strUserAgent = $_SERVER["HTTP_USER_AGENT"];
        $this->php_session_id = $id;
      # Set failed flag to 1 for now
        $failed = 1;
      # See if this exists in the database or not.
        $result = mysqli_query($this->dbhandle,"SELECT id, logged_in, user_id FROM user_session WHERE ascii_session_id = '$id'");
        if (mysqli_num_rows($result)>0) {
          $row = mysqli_fetch_array($result);
          $this->native_session_id = $row["id"];
          if ($row["logged_in"] == "t") {
            $this->logged_in = true;
            $this->user_id = $row["user_id"];
          } else {
            $this->logged_in = false;
          };
        } else {
          $this->logged_in = false;
          # We need to create an entry in the database
            $result = mysqli_query($this->dbhandle,"INSERT INTO user_session (ascii_session_id, logged_in, user_id, created, user_agent) VALUES ('$id','f',0,now(),'$strUserAgent')");
          # Now get the true ID
            $result = mysqli_query($this->dbhandle,"SELECT id FROM user_session WHERE ascii_session_id = '$id'");
            $row = mysqli_fetch_array($result);
            $this->native_session_id = $row["id"];
        };
      # Just return empty string
      return("");
    }

    private function _session_write_method($id, $sess_data) {
      return(true);
    }

    private function _session_destroy_method($id) {
      $result = mysqli_query($this->dbhandle,"DELETE FROM user_session WHERE ascii_session_id = '$id'");
      return($result);
    }

    private function _session_gc_method($maxlifetime) {
      # Garbage collection for expired sessions
        $result = mysqli_query($this->dbhandle,"DELETE FROM user_session WHERE (UNIX_TIMESTAMP(now()) - UNIX_TIMESTAMP(last_impression)) > '$maxlifetime'");
      # Clean up expired session variables
        $result = mysqli_query($this->dbhandle,"DELETE FROM session_variable WHERE session_id NOT IN (SELECT id FROM user_session)");
      return(true);
    }

    public function __destruct() {
      $this->_session_gc_method($this->session_lifespan);
      session_write_close();
    }
  }
?>
Thank you! I am working with it in 2013! And my book is 2nd edition!
Reply With Quote
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off
Trackbacks are Off
Pingbacks are On
Refbacks are Off

Similar Threads
Thread Thread Starter Forum Replies Last Post
Code not working billytikky88 C# 1 February 17th, 2008 02:20 PM
Page 811; Ch15; First para after code Nick Y BOOK: Ivor Horton's Beginning Visual C++ 2005 0 July 24th, 2006 02:26 PM
Code not working well vinish Beginning PHP 2 November 11th, 2005 07:02 PM
Working with code behind - How To pinkuisadear ASP.NET 1.0 and 1.1 Basics 7 March 11th, 2005 12:13 AM
code not working Toka1 Javascript How-To 12 December 17th, 2003 04:08 AM



All times are GMT -4. The time now is 09:41 PM.


Powered by vBulletin® Version 3.7.0
Copyright ©2000 - 2014, Jelsoft Enterprises Ltd.
© 2013 John Wiley & Sons, Inc.