From 8b2bf0516a417e517189fa7ea9bd1f45c834a707 Mon Sep 17 00:00:00 2001
From: Andreas Baumann <mail@andreasbaumann.cc>
Date: Thu, 30 Apr 2015 12:01:30 +0200
Subject: introduced an user status for registration and active status

---
 sql/sqlite3.sql | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

(limited to 'sql/sqlite3.sql')

diff --git a/sql/sqlite3.sql b/sql/sqlite3.sql
index beefe6e..ee9abc6 100644
--- a/sql/sqlite3.sql
+++ b/sql/sqlite3.sql
@@ -1,16 +1,32 @@
+PRAGMA foreign_keys = ON;
+
 drop table if exists login;
 drop table if exists user;
+drop table if exists userstatus;
+
+create table userstatus(
+	status char(1) primary key not null,
+	description varchar(32) not null
+);
+
+insert into userstatus values( 'U', 'User unknown' );
+insert into userstatus values( 'R', 'Currently in registration' );
+insert into userstatus values( 'A', 'Registration ok, user active' );
+insert into userstatus values( 'D', 'User disabled' );
 
 create table user(
 	id integer primary key autoincrement not null,
 	username varchar(32) unique not null,
-	password varchar(32) not null
+	password varchar(32) not null,
+	status char(1) references userstatus(status) default 'U',
+	registration_start timestamp,
+	code varchar(32)
 );
 
-insert into user(username, password) values('admin','admin');
+insert into user(username, password, status) values('admin','admin', 'A');
 
 create table login(
 	id integer primary key autoincrement not null,
 	user_id integer references user(id),
-	last_login TIMESTAMP
+	last_login timestamp
 );
-- 
cgit v1.2.3-54-g00ecf


From 14d83c88444cd621860eadf0686f633c8d066d67 Mon Sep 17 00:00:00 2001
From: Andreas Baumann <mail@andreasbaumann.cc>
Date: Thu, 30 Apr 2015 20:18:44 +0200
Subject: basic registration works

---
 CMakeLists.txt  |   1 +
 sql/sqlite3.sql |   1 +
 src/sha1.cpp    | 593 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/sha1.hpp    |  93 +++++++++
 src/user.cpp    | 104 ++++++++--
 src/user.hpp    |   7 +-
 6 files changed, 784 insertions(+), 15 deletions(-)
 create mode 100644 src/sha1.cpp
 create mode 100644 src/sha1.hpp

(limited to 'sql/sqlite3.sql')

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6667fee..16ae05f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -48,6 +48,7 @@ set(SRC
 	src/captcha.cpp
 	src/mail.cpp
 	src/user.cpp
+	src/sha1.cpp
 )
 
 add_custom_command(
diff --git a/sql/sqlite3.sql b/sql/sqlite3.sql
index ee9abc6..97ffcea 100644
--- a/sql/sqlite3.sql
+++ b/sql/sqlite3.sql
@@ -18,6 +18,7 @@ create table user(
 	id integer primary key autoincrement not null,
 	username varchar(32) unique not null,
 	password varchar(32) not null,
+	email varchar(32),
 	status char(1) references userstatus(status) default 'U',
 	registration_start timestamp,
 	code varchar(32)
diff --git a/src/sha1.cpp b/src/sha1.cpp
new file mode 100644
index 0000000..7fe99e0
--- /dev/null
+++ b/src/sha1.cpp
@@ -0,0 +1,593 @@
+/*
+ *  sha1.cpp
+ *
+ *  Copyright (C) 1998, 2009
+ *  Paul E. Jones <paulej@packetizer.com>
+ *  All Rights Reserved.
+ *
+ *****************************************************************************
+ *  $Id: sha1.cpp 12 2009-06-22 19:34:25Z paulej $
+ *****************************************************************************
+ *
+ *  Description:
+ *      This class implements the Secure Hashing Standard as defined
+ *      in FIPS PUB 180-1 published April 17, 1995.
+ *
+ *      The Secure Hashing Standard, which uses the Secure Hashing
+ *      Algorithm (SHA), produces a 160-bit message digest for a
+ *      given data stream.  In theory, it is highly improbable that
+ *      two messages will produce the same message digest.  Therefore,
+ *      this algorithm can serve as a means of providing a "fingerprint"
+ *      for a message.
+ *
+ *  Portability Issues:
+ *      SHA-1 is defined in terms of 32-bit "words".  This code was
+ *      written with the expectation that the processor has at least
+ *      a 32-bit machine word size.  If the machine word size is larger,
+ *      the code should still function properly.  One caveat to that
+ *      is that the input functions taking characters and character arrays
+ *      assume that only 8 bits of information are stored in each character.
+ *
+ *  Caveats:
+ *      SHA-1 is designed to work with messages less than 2^64 bits long.
+ *      Although SHA-1 allows a message digest to be generated for
+ *      messages of any number of bits less than 2^64, this implementation
+ *      only works with messages with a length that is a multiple of 8
+ *      bits.
+ *
+ */
+
+
+#include "sha1.hpp"
+
+namespace sha {
+
+/*  
+ *  SHA1
+ *
+ *  Description:
+ *      This is the constructor for the sha1 class.
+ *
+ *  Parameters:
+ *      None.
+ *
+ *  Returns:
+ *      Nothing.
+ *
+ *  Comments:
+ *
+ */
+SHA1::SHA1()
+{
+    Reset();
+}
+
+/*  
+ *  ~SHA1
+ *
+ *  Description:
+ *      This is the destructor for the sha1 class
+ *
+ *  Parameters:
+ *      None.
+ *
+ *  Returns:
+ *      Nothing.
+ *
+ *  Comments:
+ *
+ */
+SHA1::~SHA1()
+{
+    // The destructor does nothing
+}
+
+/*  
+ *  Reset
+ *
+ *  Description:
+ *      This function will initialize the sha1 class member variables
+ *      in preparation for computing a new message digest.
+ *
+ *  Parameters:
+ *      None.
+ *
+ *  Returns:
+ *      Nothing.
+ *
+ *  Comments:
+ *
+ */
+void SHA1::Reset()
+{
+    Length_Low          = 0;
+    Length_High         = 0;
+    Message_Block_Index = 0;
+
+    H[0]        = 0x67452301;
+    H[1]        = 0xEFCDAB89;
+    H[2]        = 0x98BADCFE;
+    H[3]        = 0x10325476;
+    H[4]        = 0xC3D2E1F0;
+
+    Computed    = false;
+    Corrupted   = false;
+}
+
+/*  
+ *  Result
+ *
+ *  Description:
+ *      This function will return the 160-bit message digest into the
+ *      array provided.
+ *
+ *  Parameters:
+ *      message_digest_array: [out]
+ *          This is an array of five unsigned integers which will be filled
+ *          with the message digest that has been computed.
+ *
+ *  Returns:
+ *      True if successful, false if it failed.
+ *
+ *  Comments:
+ *
+ */
+bool SHA1::Result(unsigned *message_digest_array)
+{
+    int i;                                  // Counter
+
+    if (Corrupted)
+    {
+        return false;
+    }
+
+    if (!Computed)
+    {
+        PadMessage();
+        Computed = true;
+    }
+
+    for(i = 0; i < 5; i++)
+    {
+        message_digest_array[i] = H[i];
+    }
+
+    return true;
+}
+
+/*  
+ *  Input
+ *
+ *  Description:
+ *      This function accepts an array of octets as the next portion of
+ *      the message.
+ *
+ *  Parameters:
+ *      message_array: [in]
+ *          An array of characters representing the next portion of the
+ *          message.
+ *
+ *  Returns:
+ *      Nothing.
+ *
+ *  Comments:
+ *
+ */
+void SHA1::Input(   const unsigned char *message_array,
+                    unsigned            length)
+{
+    if (!length)
+    {
+        return;
+    }
+
+    if (Computed || Corrupted)
+    {
+        Corrupted = true;
+        return;
+    }
+
+    while(length-- && !Corrupted)
+    {
+        Message_Block[Message_Block_Index++] = (*message_array & 0xFF);
+
+        Length_Low += 8;
+        Length_Low &= 0xFFFFFFFF;               // Force it to 32 bits
+        if (Length_Low == 0)
+        {
+            Length_High++;
+            Length_High &= 0xFFFFFFFF;          // Force it to 32 bits
+            if (Length_High == 0)
+            {
+                Corrupted = true;               // Message is too long
+            }
+        }
+
+        if (Message_Block_Index == 64)
+        {
+            ProcessMessageBlock();
+        }
+
+        message_array++;
+    }
+}
+
+/*  
+ *  Input
+ *
+ *  Description:
+ *      This function accepts an array of octets as the next portion of
+ *      the message.
+ *
+ *  Parameters:
+ *      message_array: [in]
+ *          An array of characters representing the next portion of the
+ *          message.
+ *      length: [in]
+ *          The length of the message_array
+ *
+ *  Returns:
+ *      Nothing.
+ *
+ *  Comments:
+ *
+ */
+void SHA1::Input(   const char  *message_array,
+                    unsigned    length)
+{
+    Input((unsigned char *) message_array, length);
+}
+
+/*  
+ *  Input
+ *
+ *  Description:
+ *      This function accepts a single octets as the next message element.
+ *
+ *  Parameters:
+ *      message_element: [in]
+ *          The next octet in the message.
+ *
+ *  Returns:
+ *      Nothing.
+ *
+ *  Comments:
+ *
+ */
+void SHA1::Input(unsigned char message_element)
+{
+    Input(&message_element, 1);
+}
+
+/*  
+ *  Input
+ *
+ *  Description:
+ *      This function accepts a single octet as the next message element.
+ *
+ *  Parameters:
+ *      message_element: [in]
+ *          The next octet in the message.
+ *
+ *  Returns:
+ *      Nothing.
+ *
+ *  Comments:
+ *
+ */
+void SHA1::Input(char message_element)
+{
+    Input((unsigned char *) &message_element, 1);
+}
+
+/*  
+ *  operator<<
+ *
+ *  Description:
+ *      This operator makes it convenient to provide character strings to
+ *      the SHA1 object for processing.
+ *
+ *  Parameters:
+ *      message_array: [in]
+ *          The character array to take as input.
+ *
+ *  Returns:
+ *      A reference to the SHA1 object.
+ *
+ *  Comments:
+ *      Each character is assumed to hold 8 bits of information.
+ *
+ */
+SHA1& SHA1::operator<<(const char *message_array)
+{
+    const char *p = message_array;
+
+    while(*p)
+    {
+        Input(*p);
+        p++;
+    }
+
+    return *this;
+}
+
+/*  
+ *  operator<<
+ *
+ *  Description:
+ *      This operator makes it convenient to provide character strings to
+ *      the SHA1 object for processing.
+ *
+ *  Parameters:
+ *      message_array: [in]
+ *          The character array to take as input.
+ *
+ *  Returns:
+ *      A reference to the SHA1 object.
+ *
+ *  Comments:
+ *      Each character is assumed to hold 8 bits of information.
+ *
+ */
+SHA1& SHA1::operator<<(const unsigned char *message_array)
+{
+    const unsigned char *p = message_array;
+
+    while(*p)
+    {
+        Input(*p);
+        p++;
+    }
+
+    return *this;
+}
+
+/*  
+ *  operator<<
+ *
+ *  Description:
+ *      This function provides the next octet in the message.
+ *
+ *  Parameters:
+ *      message_element: [in]
+ *          The next octet in the message
+ *
+ *  Returns:
+ *      A reference to the SHA1 object.
+ *
+ *  Comments:
+ *      The character is assumed to hold 8 bits of information.
+ *
+ */
+SHA1& SHA1::operator<<(const char message_element)
+{
+    Input((unsigned char *) &message_element, 1);
+
+    return *this;
+}
+
+/*  
+ *  operator<<
+ *
+ *  Description:
+ *      This function provides the next octet in the message.
+ *
+ *  Parameters:
+ *      message_element: [in]
+ *          The next octet in the message
+ *
+ *  Returns:
+ *      A reference to the SHA1 object.
+ *
+ *  Comments:
+ *      The character is assumed to hold 8 bits of information.
+ *
+ */
+SHA1& SHA1::operator<<(const unsigned char message_element)
+{
+    Input(&message_element, 1);
+
+    return *this;
+}
+
+/*  
+ *  ProcessMessageBlock
+ *
+ *  Description:
+ *      This function will process the next 512 bits of the message
+ *      stored in the Message_Block array.
+ *
+ *  Parameters:
+ *      None.
+ *
+ *  Returns:
+ *      Nothing.
+ *
+ *  Comments:
+ *      Many of the variable names in this function, especially the single
+ *      character names, were used because those were the names used
+ *      in the publication.
+ *
+ */
+void SHA1::ProcessMessageBlock()
+{
+    const unsigned K[] =    {               // Constants defined for SHA-1
+                                0x5A827999,
+                                0x6ED9EBA1,
+                                0x8F1BBCDC,
+                                0xCA62C1D6
+                            };
+    int         t;                          // Loop counter
+    unsigned    temp;                       // Temporary word value
+    unsigned    W[80];                      // Word sequence
+    unsigned    A, B, C, D, E;              // Word buffers
+
+    /*
+     *  Initialize the first 16 words in the array W
+     */
+    for(t = 0; t < 16; t++)
+    {
+        W[t] = ((unsigned) Message_Block[t * 4]) << 24;
+        W[t] |= ((unsigned) Message_Block[t * 4 + 1]) << 16;
+        W[t] |= ((unsigned) Message_Block[t * 4 + 2]) << 8;
+        W[t] |= ((unsigned) Message_Block[t * 4 + 3]);
+    }
+
+    for(t = 16; t < 80; t++)
+    {
+       W[t] = CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+    }
+
+    A = H[0];
+    B = H[1];
+    C = H[2];
+    D = H[3];
+    E = H[4];
+
+    for(t = 0; t < 20; t++)
+    {
+        temp = CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
+        temp &= 0xFFFFFFFF;
+        E = D;
+        D = C;
+        C = CircularShift(30,B);
+        B = A;
+        A = temp;
+    }
+
+    for(t = 20; t < 40; t++)
+    {
+        temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
+        temp &= 0xFFFFFFFF;
+        E = D;
+        D = C;
+        C = CircularShift(30,B);
+        B = A;
+        A = temp;
+    }
+
+    for(t = 40; t < 60; t++)
+    {
+        temp = CircularShift(5,A) +
+               ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
+        temp &= 0xFFFFFFFF;
+        E = D;
+        D = C;
+        C = CircularShift(30,B);
+        B = A;
+        A = temp;
+    }
+
+    for(t = 60; t < 80; t++)
+    {
+        temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
+        temp &= 0xFFFFFFFF;
+        E = D;
+        D = C;
+        C = CircularShift(30,B);
+        B = A;
+        A = temp;
+    }
+
+    H[0] = (H[0] + A) & 0xFFFFFFFF;
+    H[1] = (H[1] + B) & 0xFFFFFFFF;
+    H[2] = (H[2] + C) & 0xFFFFFFFF;
+    H[3] = (H[3] + D) & 0xFFFFFFFF;
+    H[4] = (H[4] + E) & 0xFFFFFFFF;
+
+    Message_Block_Index = 0;
+}
+
+/*  
+ *  PadMessage
+ *
+ *  Description:
+ *      According to the standard, the message must be padded to an even
+ *      512 bits.  The first padding bit must be a '1'.  The last 64 bits
+ *      represent the length of the original message.  All bits in between
+ *      should be 0.  This function will pad the message according to those
+ *      rules by filling the message_block array accordingly.  It will also
+ *      call ProcessMessageBlock() appropriately.  When it returns, it
+ *      can be assumed that the message digest has been computed.
+ *
+ *  Parameters:
+ *      None.
+ *
+ *  Returns:
+ *      Nothing.
+ *
+ *  Comments:
+ *
+ */
+void SHA1::PadMessage()
+{
+    /*
+     *  Check to see if the current message block is too small to hold
+     *  the initial padding bits and length.  If so, we will pad the
+     *  block, process it, and then continue padding into a second block.
+     */
+    if (Message_Block_Index > 55)
+    {
+        Message_Block[Message_Block_Index++] = 0x80;
+        while(Message_Block_Index < 64)
+        {
+            Message_Block[Message_Block_Index++] = 0;
+        }
+
+        ProcessMessageBlock();
+
+        while(Message_Block_Index < 56)
+        {
+            Message_Block[Message_Block_Index++] = 0;
+        }
+    }
+    else
+    {
+        Message_Block[Message_Block_Index++] = 0x80;
+        while(Message_Block_Index < 56)
+        {
+            Message_Block[Message_Block_Index++] = 0;
+        }
+
+    }
+
+    /*
+     *  Store the message length as the last 8 octets
+     */
+    Message_Block[56] = (Length_High >> 24) & 0xFF;
+    Message_Block[57] = (Length_High >> 16) & 0xFF;
+    Message_Block[58] = (Length_High >> 8) & 0xFF;
+    Message_Block[59] = (Length_High) & 0xFF;
+    Message_Block[60] = (Length_Low >> 24) & 0xFF;
+    Message_Block[61] = (Length_Low >> 16) & 0xFF;
+    Message_Block[62] = (Length_Low >> 8) & 0xFF;
+    Message_Block[63] = (Length_Low) & 0xFF;
+
+    ProcessMessageBlock();
+}
+
+
+/*  
+ *  CircularShift
+ *
+ *  Description:
+ *      This member function will perform a circular shifting operation.
+ *
+ *  Parameters:
+ *      bits: [in]
+ *          The number of bits to shift (1-31)
+ *      word: [in]
+ *          The value to shift (assumes a 32-bit integer)
+ *
+ *  Returns:
+ *      The shifted value.
+ *
+ *  Comments:
+ *
+ */
+unsigned SHA1::CircularShift(int bits, unsigned word)
+{
+    return ((word << bits) & 0xFFFFFFFF) | ((word & 0xFFFFFFFF) >> (32-bits));
+}
+
+}
\ No newline at end of file
diff --git a/src/sha1.hpp b/src/sha1.hpp
new file mode 100644
index 0000000..7f50827
--- /dev/null
+++ b/src/sha1.hpp
@@ -0,0 +1,93 @@
+/*
+ *  sha1.h
+ *
+ *  Copyright (C) 1998, 2009
+ *  Paul E. Jones <paulej@packetizer.com>
+ *  All Rights Reserved.
+ *
+ *****************************************************************************
+ *  $Id: sha1.h 12 2009-06-22 19:34:25Z paulej $
+ *****************************************************************************
+ *
+ *  Description:
+ *      This class implements the Secure Hashing Standard as defined
+ *      in FIPS PUB 180-1 published April 17, 1995.
+ *
+ *      Many of the variable names in this class, especially the single
+ *      character names, were used because those were the names used
+ *      in the publication.
+ *
+ *      Please read the file sha1.cpp for more information.
+ *
+ */
+
+#ifndef _SHA1_H_
+#define _SHA1_H_
+
+namespace sha
+{
+
+class SHA1
+{
+
+    public:
+
+        SHA1();
+        virtual ~SHA1();
+
+        /*
+         *  Re-initialize the class
+         */
+        void Reset();
+
+        /*
+         *  Returns the message digest
+         */
+        bool Result(unsigned *message_digest_array);
+
+        /*
+         *  Provide input to SHA1
+         */
+        void Input( const unsigned char *message_array,
+                    unsigned            length);
+        void Input( const char  *message_array,
+                    unsigned    length);
+        void Input(unsigned char message_element);
+        void Input(char message_element);
+        SHA1& operator<<(const char *message_array);
+        SHA1& operator<<(const unsigned char *message_array);
+        SHA1& operator<<(const char message_element);
+        SHA1& operator<<(const unsigned char message_element);
+
+    private:
+
+        /*
+         *  Process the next 512 bits of the message
+         */
+        void ProcessMessageBlock();
+
+        /*
+         *  Pads the current message block to 512 bits
+         */
+        void PadMessage();
+
+        /*
+         *  Performs a circular left shift operation
+         */
+        inline unsigned CircularShift(int bits, unsigned word);
+
+        unsigned H[5];                      // Message digest buffers
+
+        unsigned Length_Low;                // Message length in bits
+        unsigned Length_High;               // Message length in bits
+
+        unsigned char Message_Block[64];    // 512-bit message blocks
+        int Message_Block_Index;            // Index into message block array
+
+        bool Computed;                      // Is the digest computed?
+        bool Corrupted;                     // Is the message digest corruped?
+    
+};
+}
+
+#endif
diff --git a/src/user.cpp b/src/user.cpp
index d98d0f3..9cef242 100644
--- a/src/user.cpp
+++ b/src/user.cpp
@@ -2,6 +2,7 @@
 #include "user.hpp"
 #include "strusCms.hpp"
 #include "captcha.hpp"
+#include "sha1.hpp"
 
 #include <cppcms/url_dispatcher.h>  
 #include <cppcms/url_mapper.h>   
@@ -9,6 +10,10 @@
 #include <cppcms/session_interface.h>
 #include <booster/posix_time.h>
 
+#include <cstdlib>
+#include <sstream>
+#include <iomanip>
+
 namespace apps {
 
 // user
@@ -63,14 +68,22 @@ void user::register_user( )
 	if( request( ).request_method( ) == "POST" ) {
 		c.register_user.load( context( ) );
 		if( c.register_user.validate( ) ) {
+			std::string code = registration_start( c.register_user.username.value( ),
+				c.register_user.password.value( ), c.register_user.email.value( ) );
+
 			cms.mail.subject = "Registration request";
-			cms.mail.body = "Your registration code is: CODE\n";
+			
+			std::ostringstream oss;
+			oss << "Your registration code is: " << code << "\n";
+			
+			cms.mail.body = oss.str( );
 			cms.mail.to = c.register_user.email.value( );
 			cms.mail.send( );
 			if( cms.mail.hasError( ) ) {
 				c.register_user.email.valid( false );
 				c.register_user.email.error_message( "Can't send email to this address" );
 				booster::ptime::sleep( booster::ptime( 5, 0 ) );
+				delete_user( c.register_user.username.value( ) );
 				std::cerr << "SEND MAIL ERROR: " << cms.mail.getLastError( ) << std::endl;
 			} else {
 				response( ).set_redirect_header( cms.root( ) + "/confirm_register" );
@@ -88,14 +101,19 @@ void user::confirm_register( )
 	if( request( ).request_method( ) == "POST" ) {
 		c.confirm_register.load( context( ) );
 		if( c.confirm_register.validate( ) ) {
-			response( ).set_redirect_header( cms.root( ) + "/login" );
+			if( cms.user.verify_registration_code( c.confirm_register.code.value( ) ) ) {
+				response( ).set_redirect_header( cms.root( ) + "/login" );
+			} else {
+				c.confirm_register.code.valid( false );
+			}			
 		}
 	}
+
 	render( "confirm_register", c );
 }
 	
 // TODO: make this a salted hash
-bool user::check_login( std::string user, std::string password )
+bool user::check_login( const std::string user, const std::string password )
 {
 	if( user.empty( ) || password.empty( ) ) {
 		return false;
@@ -128,7 +146,7 @@ bool user::check_login( std::string user, std::string password )
 	return true;
 }
 
-bool user::user_exists( std::string user )
+bool user::user_exists( const std::string user )
 {
 	if( user.empty( ) ) {
 		return false;
@@ -144,6 +162,75 @@ bool user::user_exists( std::string user )
 	return true;
 }
 
+namespace {
+	
+std::string generate_token( )
+{
+	char chars[] = "ABCDEF1234567890";
+	std::string token;
+
+	for( int i = 0; i < 12; i++ ) {
+		token += chars[rand( ) % 16];
+	}
+
+	return token;
+}
+
+std::string compute_token_hash( const std::string user, const std::string token )
+{
+	sha::SHA1 sha;
+	unsigned int res[5];
+	sha.Reset( );
+	sha << user.c_str( ) << token.c_str( );
+
+	std::ostringstream oss;
+	for( int i = 0; i < 5; i++ ) {
+		oss << std::uppercase << std::hex << std::setw( 8 ) << std::setfill( '0' ) << res[i];
+		if( i < 4 ) oss << ' ';
+	}
+
+	return oss.str( );
+}
+
+}
+
+std::string user::registration_start( const std::string user, const std::string password, const std::string email )
+{
+	std::time_t now_time = std::time( 0 );
+	std::tm now = *std::localtime( &now_time );
+	std::string token = generate_token( );
+	std::string code = compute_token_hash( user, token );
+
+	cppdb::session sql( cms.conn );	
+	cppdb::statement stmt;
+	stmt = sql << "INSERT INTO user(username, password, email, status, registration_start, code ) VALUES( ?, ?, ?, 'R', ?, ? )" 
+		<< user << password << email << now << code;
+	stmt.exec( );
+	
+	return code;
+}
+
+bool user::verify_registration_code( std::string code )
+{
+	cppdb::session sql( cms.conn );
+	cppdb::statement stmt;
+	stmt = sql << "UPDATE user set status='A' WHERE code=?" << code;
+	stmt.exec( );
+	if( stmt.affected( ) == 1 ) {
+		return true;
+	}
+			
+	return false;
+}
+
+void user::delete_user( std::string user )
+{
+	cppdb::session sql( cms.conn );
+	cppdb::statement stmt;
+	stmt = sql << "DELETE FROM user WHERE username=?" << user;
+	stmt.exec( );
+}
+
 void user::ini( content::user &c )
 {
 	master::ini( c );
@@ -286,15 +373,6 @@ bool confirm_register_form::validate( )
 	if( !form::validate( ) ) {
 		return false;
 	}
-
-	// TODO: check code supplied against code in the DB, this is a
-	// method in the user class
-	
-	//~ if( !cms.user.check_code( code.value( ) ) ) {
-		//~ code.valid( false );
-		//~ booster::ptime::sleep( booster::ptime( 5, 0 ) );
-		//~ return false;
-	//~ }
 		
 	return true;
 }
diff --git a/src/user.hpp b/src/user.hpp
index 633af60..e6a4e35 100644
--- a/src/user.hpp
+++ b/src/user.hpp
@@ -10,8 +10,11 @@ namespace apps {
 class user : public master {
 	public:
 		user( strusCms &cms );
-		bool check_login( std::string user, std::string password );
-		bool user_exists( std::string user );
+		bool check_login( const std::string user, const std::string password );
+		bool user_exists( const std::string user );
+		void delete_user( const std::string user );
+		std::string registration_start( const std::string user, const std::string password, const std::string email );
+		bool verify_registration_code( const std::string code );
 
 	public:
 		std::string last_captcha;
-- 
cgit v1.2.3-54-g00ecf

