Index: Commands.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Commands.cpp,v
retrieving revision 1.227
diff -u -p -b -r1.227 Commands.cpp
--- Commands.cpp	16 Jan 2005 00:58:16 -0000	1.227
+++ Commands.cpp	10 Apr 2005 22:43:18 -0000
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////
-// $Id: Commands.cpp,v 1.227 2005/01/16 00:58:16 klas Exp $
+// $Id: Commands.cpp,v 1.248 2005/04/05 12:45:06 jockelundin Exp $
 //
 // Usage: Ftp commands and data transfer functions
 //
@@ -7,202 +7,69 @@
 //    LICENSE for complete information.
 /////////////////////////////////////////////////////////////////////
 
-#define __COMMANDS_CPP__
-#define __NETINCLS__
-#define _STREAM_COMPAT
+#define COMMANDS_CPP
+#define NETINCLS
 #include "Commands.h"
 
-bool Commands::ftp_send(User *user, string message)
-{
-  int ret;
-  fd_set wfds;
-  struct timeval tv;
-
-  if(user->socket_id < 0)
-  {
-    user->logged_in = LOGGED_OUT;
-    return(false);
-  }
-  FD_ZERO(&wfds);
-  FD_SET(user->socket_id, &wfds);
-  tv.tv_sec = 1;
-  tv.tv_usec = 0;
-
-  sem_wait(&user->user_lock);
-  if(!select(user->socket_id + 1, NULL, &wfds, NULL, &tv))
-  {
-    user->logged_in = LOGGED_OUT;
-    sem_post(&user->user_lock);
-    return(false);
-  }
-  message += "\r\n";
-#ifdef USE_TLS
-  if(user->settings.test(TLS_ENABLED))
-    ret = gnutls_record_send(user->ctrl_session, static_cast<const void*>(message.c_str()), message.size());
-  else
-#endif // USE_TLS
-    ret = write(user->socket_id, static_cast<const void*>(message.c_str()), message.size());
-
-  sem_post(&user->user_lock);
-  if(ret < 0)
-  {
-    logging->log_this(3, TYPE_INFO, "write to socket " + util.itos(user->socket_id) + " failed: %m"); // I think %m should work here as well.. maybe?
-    return(false);
-  }
-  return(true);
-}
-
-bool Commands::ftp_receive(User *user, string &message)
-{
-  fd_set rfd;
-  struct timeval time;
-
-  time.tv_sec = config->get_user(user->user_name).idle;
-  time.tv_usec = 0;
-
-  // We _really_ want to pass rfd as a reference...
-  FD_ZERO(&rfd);
-  FD_SET(user->socket_id, &rfd);
-  
-  if(!(select(user->socket_id + 1, &rfd, NULL, NULL, &time)))
-  {
-    if(user->data_vec.size())
-      return(false);
-    Commands::ftp_send(user, "421 Connection timed out(" + util.itos(config->get_user(user->user_name).idle) + " seconds). Disconnected.");
-    if(user->logged_in == LOGGED_IN)
-      logging->log_this(3, TYPE_AUTH, "User " + user->user_name + ", disconnected due to timeout.");
-
-    sem_wait(&user->user_lock);
-    user->logged_in = LOGGED_OUT;
-    sem_post(&user->user_lock);
-    close(user->socket_id);
-    return(false);
-  }
-  
-  char tmp_buf[210] = { 0 };
-  
-#ifdef USE_TLS
-  if(user->settings.test(TLS_ENABLED))
-    gnutls_record_recv(user->ctrl_session, static_cast<void*>(tmp_buf), 200);
-  else
-#endif // USE_TLS
-    read(user->socket_id, static_cast<void*>(tmp_buf), 200);
-  message.assign(tmp_buf, (strlen(tmp_buf)));
-  
-  if(message.size() == 2) // no need to try parsing just "\r\n"
-    return(false);
-  if(message.size() == 0)
-  {
-    // this code makes sure that we don't get stuck in an endless loop if the connection just dissapears...
-    close(user->socket_id);
-    sem_wait(&user->user_lock);
-    user->logged_in = LOGGED_OUT;
-    sem_post(&user->user_lock);
-    return(false);
-  }
-  else // everything is normal and we've got a string to play with
-    return(true);
-}
+class Commands comm;
 
 void *data_wrapper(void *void_data)
 {
   class Data *data = static_cast<class Data*>(void_data);
-  extern class Commands comm;
   string message;
-  size_t read_size = READ_SIZE; // for lack of finding a good define that fits here.
-  fd_set set; // no, it isn't a bad name, but we'll use it for both RETR and STOR
-  struct timeval timeout;
   time_t start_time = time(NULL);
   size_t trans_size = 0; // will hold the total number of sent bytes
 
-  timeout.tv_sec = 5; // same timeout for data as control? no, 5 seconds is more than enough.
-  timeout.tv_usec = 0;
-
+  data->thread_id(pthread_self());
 
   pthread_setspecific(my_data, void_data);
   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
 
-  data->host_name = inet_ntoa(data->data_addr.sin_addr);
+  data->host_name(inet_ntoa(data->addr().sin_addr));
 
   // I don't like it, but we need to drop permissions again...
+  //   not true for new threads, need to look deeper into this.
   struct passwd pwd, *pwd_ptr;
   char pwdata[PWBUF_SIZE];
-  getpwnam_r(data->user->user_name.c_str(), &pwd, pwdata, sizeof(pwdata), &pwd_ptr);
+  getpwnam_r(data->user->user_name().c_str(), &pwd, pwdata, sizeof(pwdata),
+      &pwd_ptr);
   uid_t uid = pwd.pw_uid;
   gid_t gid = pwd.pw_gid;
   setuid(uid);
   setgid(gid);
 
-  FD_ZERO(&set);
-  FD_SET(data->data_id, &set);
-
-  sem_wait(&data->data_lock);
-  data->thread_id = pthread_self();
-  // do a dns lookup here.
-  sem_post(&data->data_lock);
-
-  if(!(select(data->data_id + 1, &set, &set, NULL, &timeout)))
-  {
-    message = "451 Data connection timed out(" + util.itos(config->get_user(data->user->user_name).idle) + " seconds).";
-    logging->log_this(3, TYPE_INFO, "User " + data->user->user_name + ", lost a data connection due to timeout.");
-  }
 #ifdef USE_TLS // handshakes etc
-  else if(data->settings.test(TLS_INITED))
-  {
-    sem_wait(&data->data_lock);
-    int ret = gnutls_handshake(data->data_session);
-    sem_post(&data->data_lock);
-    if(ret < 0) // failed handshake
-    {
-      message = "451 Failed to negotiate TLS as was asked. Closing data connection.";
-      gnutls_perror(ret);
-    }
-    else // handshake is aok
-    {
-      // tls is picky about how much in one send/recv
-      read_size = gnutls_record_get_max_size(data->data_session);
-      sem_wait(&data->data_lock);
-      data->settings.set(TLS_ENABLED);
-      sem_post(&data->data_lock);
-    }
-  }
+  if(data->settings(TLS_PROT_D))
+    if(!data->engage_tls()) // failed handshake
+      message =
+        "451 Failed to negotiate TLS as was asked. Closing data connection.";
 #endif // USE_TLS
-  while(data->data_id && message.size() == 0)
+  while(message.empty())
   {
-    char read_buf[read_size];
-    size_t size, size2;
-    if(data->settings.test(RETR_TRANS))
-      size2 = read(data->file_id, read_buf, read_size);
-
-#ifdef USE_TLS
-    if(data->settings.test(TLS_ENABLED))
-    {
-      if(data->settings.test(RETR_TRANS))
-        size = gnutls_record_send(data->data_session, read_buf, size2);
-      else // STOR_TRANS
-        size = gnutls_record_recv(data->data_session, read_buf, read_size);
+    char *read_buf = new char[data->packet_size()];
+    size_t size = 0, size2 = 0;
+    if(data->settings(RETR_TRANS))
+    {
+      size2 = read(data->file_id(), read_buf, data->packet_size());
+      if(size2 > 0)
+        size = data->send(read_buf, size2);
     }
-    else
-    {
-#endif
-      if(data->settings.test(RETR_TRANS))
-        size = write(data->data_id, read_buf, size2);
       else // STOR_STRANS
-        size = recv(data->data_id, read_buf, read_size, MSG_WAITALL);
-#ifdef USE_TLS
-    } // the else just before last #endif
-#endif
+      size = data->receive(read_buf, data->packet_size() - 1);
+
     trans_size += size;
-    if(!data->settings.test(RETR_TRANS))
+    if(!data->settings(RETR_TRANS))
     {
-      if(size != 0 && (write(data->file_id, read_buf, size) == -1))
+      if(size != 0 && (write(data->file_id(), read_buf, size) == -1))
       {
-        logging->log_this(3, TYPE_DEBUG, "Write failed to file: " + data->file_name + " from host: " + data->host_name + " with error %m");
+        logging->log_this(3, TYPE_DEBUG, "Write failed to file: " +
+            data->file_name() + " from host: " + data->host_name() +
+            " with error %m");
         message = "451 File transfer failed.";
       }
       else if(size == 0)
       {
-        if(close(data->file_id) != 0) // it's only important for STOR that the closing is proper.
+        if(close(data->file_id()) != 0)
           message = "550 Failed saving file.";
         else
           message = "226 Transfer complete.";
@@ -212,41 +79,43 @@ void *data_wrapper(void *void_data)
     {
       if(size != size2)
         message = "550 Transfer failed.";
-      else if(size2 != read_size)
+      else if(size2 != data->packet_size())
         message = "226 Transfer complete.";
     }
+    delete[] read_buf;
   }
 
-  comm.ftp_send(data->user, message);
+  *data->user << message << "\r\n";
   if(config->logger.xferlog)
   {
-    const time_t timep = time(NULL);
-    struct tm now;
-    char time_string[30] = {0};
+    string time_string;
+    util.time_string(time_string, "%a %b %d %H:%M:%S %Y ");
 
-    gmtime_r(&timep, &now);
-    strftime(time_string, 28, "%a %b %d %H:%M:%S %Y ", &now);
+    // as ' ' separates fields in the xferlog we convert them to '_' here
+    string temp_name = data->file_name();
+    for(string::iterator name_it = temp_name.begin();
+        name_it != temp_name.end(); name_it++)
+      if(isspace(*name_it))
+        *name_it = '_';
 
     // let us build a string!
     string xferlog = time_string;
     xferlog += util.itos(time(NULL) - start_time);
-    xferlog += " " + data->host_name;
+    xferlog += " " + data->host_name();
     xferlog += " " + util.itos(trans_size);
-    xferlog += " " + data->file_name + " ";
-    xferlog += (data->settings.test(BINARY)?"b":"a");
-    xferlog += " _ "; // we don't support any special-action to flag here.. or is encryption special? have to make up a flag I guess ;)
-    xferlog += (data->settings.test(RETR_TRANS)?"o":"i");
+    xferlog += " " + temp_name + " ";
+    xferlog += (data->settings(BINARY)?"b":"a");
+    xferlog += " _ "; // we don't support any special-action to flag here.
+    xferlog += (data->settings(RETR_TRANS)?"o":"i");
     xferlog += " r"; // same thing again, we only support real users so far.. 
-    xferlog += " " + data->user->user_name;
-    xferlog += " FTP"; // maybe ftps for encrypted?
+    xferlog += " " + data->user->user_name();
+    xferlog += " FTP"; // maybe ftps for encrypted? no. IANA.
     xferlog += " 0"; // there should be more auth methods available here?
     xferlog += " *"; // or should this be uid?
 
     logging->log_this(1, TYPE_XFER, xferlog);
   }
-  sem_wait(&data->user->user_lock);
-  data->user->total_transfered += trans_size;
-  sem_post(&data->user->user_lock);
+  data->user->total_transfered(data->user->total_transfered() + trans_size);
 
   pthread_exit(NULL);
 }
@@ -259,7 +128,7 @@ string Commands::parsedir(User *user, st
   string new_string;
 
   if(orig_string[0] != '/')
-    orig_string = user->current_dir + '/' + orig_string;
+    orig_string = user->current_dir() + '/' + orig_string;
 
   class StringTokenizer *token = new StringTokenizer(orig_string, "/", false);
   vector<string> dir_array;
@@ -286,58 +155,39 @@ string Commands::parsedir(User *user, st
   if(dir_array.empty())
     new_string = '/';
   else
-    for(vector<string>::iterator dir_it = dir_array.begin(); dir_it != dir_array.end(); dir_it++)
+    for(vector<string>::iterator dir_it = dir_array.begin();
+        dir_it != dir_array.end(); dir_it++)
       new_string += '/' + *dir_it;
 
-  if(user->root_dir.size() > 1)
-    if(new_string.find(user->root_dir) != 0)
-      new_string = user->root_dir + new_string;
+  if(user->root_dir().size() > 1)
+    if(new_string.find(user->root_dir()) != 0)
+      new_string = user->root_dir() + new_string;
 
 
   return(new_string);
 }
 
-#ifdef USE_TLS
-
-void Commands::init_tls(gnutls_session &session)
-{
-  gnutls_init(&session, GNUTLS_SERVER);
-
-  gnutls_set_default_priority(session);   
-
-  gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
-
-  gnutls_certificate_server_set_request(session, GNUTLS_CERT_REQUEST);
-
-  gnutls_dh_set_prime_bits(session, 1024);
-
-  gnutls_db_set_retrieve_function(session, tls_db_fetch);
-  gnutls_db_set_remove_function(session, tls_db_delete);
-  gnutls_db_set_store_function(session, tls_db_store);
-  gnutls_db_set_ptr(session, NULL);
-}
-#endif // USE_TLS
-
 int pam_conversate(int num_msg, const struct pam_message **msg,
     struct pam_response **resp, void *appdata_ptr)
 {
-  struct pam_response *reply = new struct pam_response[0];
+  struct pam_response *reply = NULL;
   int count = 0;
   struct pam_data *auth_data = (static_cast<struct pam_data*>(appdata_ptr));
 
   for(int i = 0; i < num_msg; i++)
   {
+    struct pam_response *reply_tmp;
     switch(msg[i]->msg_style)
     {
       case PAM_PROMPT_ECHO_ON:
       case PAM_PROMPT_ECHO_OFF:
-        if(count != 0)
-        {
-          struct pam_response *reply_tmp = new struct pam_response[count + 1];
+        reply_tmp = (static_cast<struct pam_response*>
+            (malloc(sizeof(struct pam_response) * (count + 1))));
           memcpy(reply_tmp, reply, count);
-          delete[] reply;
+        if(reply)
+          free(reply);
           reply = reply_tmp;
-        }
+
         // is this really how it should be done?
         if(msg[i]->msg_style == PAM_PROMPT_ECHO_ON)
           reply[i].resp = strndup(auth_data->user_name.c_str(),
@@ -354,7 +204,8 @@ int pam_conversate(int num_msg, const st
 
       case PAM_ERROR_MSG:
       default:
-        delete[] reply;
+        if(reply)
+          free(reply);
         return PAM_CONV_ERR;
     }
   }
Index: Commands.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Commands.h,v
retrieving revision 1.80
diff -u -p -b -r1.80 Commands.h
--- Commands.h	22 Jan 2005 17:18:50 -0000	1.80
+++ Commands.h	10 Apr 2005 22:43:18 -0000
@@ -1,117 +1,113 @@
 /////////////////////////////////////////////////////////////////////
-//  File: Commands.h
+// $Id: Commands.h,v 1.95 2005/04/05 12:45:07 jockelundin Exp $
 // Usage: Ftp commands and data transfer functions
 //
 //  A part of babyftpd, licensed under the GNU GPL, see the file
 //    LICENSE for complete information.
 /////////////////////////////////////////////////////////////////////
 
-#ifndef __COMMANDS_H__
-#define __COMMANDS_H__
+#ifndef COMMANDS_H
+#define COMMANDS_H
 
-#if (defined(__COMMANDS_CPP__) || defined(__COMMANDS2_CPP__))
+#if (defined(COMMANDS_CPP) || defined(COMMANDS2_CPP))
 #include "generic.h"
-#include "User.h"
 #include "Utilities.h"
 #include "Configuration.h"
+#include "Log.h"
+#include "Connection.h"
+#include "Data.h"
+#include "User.h"
 #include "Handler.h"
 #include "StringTokenizer.h"
-#include "Log.h"
 #endif
 
+#define BABY_COM(com)  bool com(User *user, string argument, unsigned int flag)
+
 class Commands
 {
   private:
     string parsedir(User*, string);
-    string mod_time(struct stat);
     string mlsx(User*, string);
     string list_file(string);
     string mlst_string(User*);
   public:
-    bool user(User*, string, unsigned int);
-    bool pass(User*, string, unsigned int);
-    bool retr(User*, string, unsigned int);
-    bool stor(User*, string, unsigned int);
-    bool port(User*, string, unsigned int);
-    bool quit(User*, string, unsigned int);
-    bool list(User*, string, unsigned int);
-    bool help(User*, string, unsigned int);
-    bool mkd(User*, string, unsigned int);
-    bool rmd(User*, string, unsigned int);
-    bool cwd(User*, string, unsigned int);
-    bool dele(User*, string, unsigned int);
-    bool pasv(User*, string, unsigned int);
-    bool ftp_send(User*, string);
-    bool ftp_receive(User*, string&);
-    bool rname(User*, string, unsigned int);
-    bool opts(User*, string, unsigned int);
-    bool rest(User*, string, unsigned int);
-    bool abor(User*, string, unsigned int);
+    BABY_COM(user);
+    BABY_COM(pass);
+    BABY_COM(retr);
+    BABY_COM(stor);
+    BABY_COM(port);
+    BABY_COM(quit);
+    BABY_COM(list);
+    BABY_COM(help);
+    BABY_COM(mkd);
+    BABY_COM(rmd);
+    BABY_COM(cwd);
+    BABY_COM(dele);
+    BABY_COM(pasv);
+    BABY_COM(rname);
+    BABY_COM(opts);
+    BABY_COM(rest);
+    BABY_COM(abor);
     // functions for simple commands that don't justify an own function
-    bool sim1(User*, string, unsigned int);
-    bool sim2(User*, string, unsigned int);
+    BABY_COM(sim1);
+    BABY_COM(sim2);
     sem_t pam_lock; // pam is not threadsafe.
 #ifdef USE_TLS
     // collection of encryption commands
-    bool auth(User*, string, unsigned int);
-    void init_tls(gnutls_session&);
+    BABY_COM(auth);
 #endif // USE_TLS
 };
 
+extern class Commands comm;
+
 // the extra flags we can play with...
-#define COM_LONG (1 << 0)  // for list()
-#define COM_SHORT (1 << 1) 
-#define COM_MLSD (1 << 2)
-
-#define COM_FROM (1 << 0) // for rname()
-#define COM_TO (1 << 1)
-
-#define COM_STOR (1 << 0) // for stor()
-#define COM_APPE (1 << 1)
-
-#define COM_CDUP (1 << 0)
-#define COM_CWD  (1 << 1)
-
-#define COM_NOOP (1 << 0) // for sim1()
-#define COM_PWD  (1 << 1)
-#define COM_SYST (1 << 2)
-#define COM_FEAT (1 << 3)
-#define COM_TYPE (1 << 4)
-#define COM_MODE (1 << 5)
-#define COM_STRU (1 << 6)
+const int COM_LONG = (1 << 0);  // for list()
+const int COM_SHORT = (1 << 1);
+const int COM_MLSD = (1 << 2);
+
+const int COM_FROM = (1 << 0); // for rname()
+const int COM_TO = (1 << 1);
+
+const int COM_STOR = (1 << 0); // for stor()
+const int COM_APPE = (1 << 1);
+
+const int COM_CDUP = (1 << 0);
+const int COM_CWD  = (1 << 1);
+
+const int COM_NOOP = (1 << 0); // for sim1()
+const int COM_PWD  = (1 << 1);
+const int COM_SYST = (1 << 2);
+const int COM_FEAT = (1 << 3);
+const int COM_TYPE = (1 << 4);
+const int COM_MODE = (1 << 5);
+const int COM_STRU = (1 << 6);
 #ifdef USE_TLS
-#define COM_PBSZ (1 << 7)
-#define COM_PROT (1 << 8)
-#define COM_CCC  (1 << 9)
-#define COM_CONF (1 << 10)
-#define COM_MIC (1 << 11)
-#define COM_ENC (1 << 12)
+const int COM_PBSZ = (1 << 7);
+const int COM_PROT = (1 << 8);
+const int COM_CCC  = (1 << 9);
+const int COM_CONF = (1 << 10);
+const int COM_MIC = (1 << 11);
+const int COM_ENC = (1 << 12);
 #endif // USE_TLS
 
-#define COM_SIZE (1 << 0) // for sim2()
-#define COM_MDTM (1 << 1)
-#define COM_MLST (1 << 2)
-
-#define COM_PORT (1 << 0) // port and pasv
-#define COM_EPRT (1 << 1)
-#define COM_PASV (1 << 2)
-#define COM_EPSV (1 << 3)
-
-#define BEFORE_USER (1 << 31) // remember that these are used in conunctions with those above..
-#define LAST_COM (1 << 30)
+const int COM_SIZE = (1 << 0); // for sim2()
+const int COM_MDTM = (1 << 1);
+const int COM_MLST = (1 << 2);
+
+const int COM_PORT = (1 << 0); // port and pasv
+const int COM_EPRT = (1 << 1);
+const int COM_PASV = (1 << 2);
+const int COM_EPSV = (1 << 3);
 
-#define PROT_IPV4 1 // EPRT and EPSV
-#define PROT_IPV6 2
+// remember that these are used in conunctions with those above..
+const int BEFORE_USER = (1 << 31);
 
-#define READ_SIZE 16384
+const int PROT_IPV4 = 1;// EPRT and EPSV
+const int PROT_IPV6 = 2;
 
-#define PWBUF_SIZE 100
 
 void *data_wrapper(void*) __attribute__ ((noreturn));
 
-extern class Configuration *config;
-extern class Utilities util;
-extern class Handler handler;
 extern vector<User*> user_list;
 
 #ifdef USE_TLS
Index: Commands2.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Commands2.cpp,v
retrieving revision 1.119
diff -u -p -b -r1.119 Commands2.cpp
--- Commands2.cpp	23 Jan 2005 01:34:53 -0000	1.119
+++ Commands2.cpp	10 Apr 2005 22:43:18 -0000
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////
-// $Id: Commands2.cpp,v 1.119 2005/01/23 01:34:53 klas Exp $
+// $Id: Commands2.cpp,v 1.156 2005/04/10 21:57:36 jockelundin Exp $
 //
 // Usage: Ftp commands and data transfer functions
 //
@@ -7,89 +7,116 @@
 //    LICENSE for complete information.
 /////////////////////////////////////////////////////////////////////
 
-#define __COMMANDS2_CPP__
-#define _STREAM_COMPAT
-#define __NETINCLS__
+#define COMMANDS2_CPP
+#define NETINCLS
 #include "Commands.h"
 
-bool Commands::quit(User *user, string argument, unsigned int flag)
+BABY_COM(Commands::quit)
 {
-  Commands::ftp_send(user, "221 Goodbye. You transfered a total of " + util.itos(user->total_transfered) + " bytes.");
+  *user << "221 Goodbye. You transfered a total of "
+    << user->total_transfered() << " bytes." << "\r\n";
 
-  sem_wait(&user->user_lock);
-  user->logged_in = LOGGED_OUT;
-  sem_post(&user->user_lock);
-  close(user->socket_id);
+  user->settings(FAILURE, B_SET);
   return(true);
 }
 
-bool Commands::user(User *user, string user_name, unsigned int flag)
+BABY_COM(Commands::user)
 {
-  if(user->logged_in == LOGGED_IN)
+  string message;
+  bool ret = true, bad = false;
+  if(user->logged_in() == LOGGED_IN)
   {
-    Commands::ftp_send(user, "503 You are already logged in!");
-    return(false);
+    message = "503 You are already logged in!";
+    ret = false;
   }
+  else
+  {
+    int current_connections = 0;
+    for(vector<User*>::iterator user_it = user_list.begin();
+        user_it != user_list.end(); user_it++)
+      if((*user_it)->user_name() == argument)
+        current_connections++;
+
+    user->user_name(argument);
+    user->logged_in(USER_SUPPLIED);
 
+    if(config->get_user(argument).noftp == true)
+    {
+      message = "421 Sorry, you are not allowed to use ftp. disconnecting.";
+      bad = true;
+    }
+    else if(current_connections >= config->get_user(argument).user_limit){
+      message =
+        "421 You have reached your maximum number of control connections.";
+      bad = true;
+    }
 #ifdef USE_TLS
-  string failure;
-  if(config->get_user(user_name).encryption == FORCE &&
-      !user->settings.test(TLS_ENABLED))
-    failure = "421 You muse enable encryption before logging in, disconnecting.";
-  else if(config->get_user(user_name).encryption == DENY &&
-      user->settings.test(TLS_ENABLED))
-    failure =  "421 Sorry, you are not allowed to connect with encryption enabled, disconnecting.";
-
-  if(!failure.empty())
-  {
-    Commands::ftp_send(user, failure);
-    sem_wait(&user->user_lock);
-    user->logged_in = LOGGED_OUT;
-    sem_post(&user->user_lock);
-    return(false);
+    else if(config->get_user(argument).encryption == DENY &&
+        user->settings(TLS_ENABLED))
+    {
+      message =  "421 Sorry, you are not allowed to connect with"
+        "encryption enabled, disconnecting.";
+      bad = true;
+    }
+    else if(user->settings(TLS_ENABLED) && user->verify_cert())
+    {
+      message = "230 Logged in as " + argument + ".";
+      user->login();
+    }
+    else if(config->get_user(argument).encryption == FORCE_CERT)
+    {
+      message = "421 You must supply a valid certificate to login.";
+      bad = true;
+    }
+    else if(config->get_user(argument).encryption == FORCE &&
+        !user->settings(TLS_ENABLED))
+    {
+      message =
+        "421 You must enable encryption before logging in, disconnecting.";
+      bad = true;
   }
 #endif // USE_TLS
-  int current_connections = 0;
-  for(vector<User*>::iterator user_it = user_list.begin();
-      user_it != user_list.end(); user_it++){
-    if((*user_it)->user_name == user_name)
-      current_connections++;
+    else
+      message = "331 Password required for " + argument + ".";
   }
-  if(current_connections >= config->get_user(user_name).user_limit){
-     Commands::ftp_send(user, "421 You have reached your maximum number of control connections.");
-     return(false);
+  *user << message << "\r\n";
+  if(bad)
+  {
+    user->settings(FAILURE, B_SET);
+    ret = false;
   }
-
-  sem_wait(&user->user_lock);
-  user->user_name = user_name;
-  user->logged_in = USER_SUPPLIED;
-  sem_post(&user->user_lock);
-  Commands::ftp_send(user, "331 Password required for " + user_name + ".");
-
-  return(true);
+  return(ret);
 }
 
-bool Commands::abor(User *user, string argument, unsigned int flag) // FIXME please?
+BABY_COM(Commands::abor)
 {
-  ftp_send(user, "502 Command not implemented.");
+  string message = "226 ";
+  if(user->data_vec.empty())
+    message += "No Data Connections to cancel";
+  else
+  {
+    for(vector<class Data*>::iterator data_it = user->data_vec.begin();
+        data_it != user->data_vec.end(); data_it++)
+      if(*data_it)
+        pthread_cancel((*data_it)->thread_id());
+    message += "All Data Connections Successfully closed.";
+  }
+  *user << message << ".\r\n";
   return(true);
 }
 
-bool Commands::pass(User *user, string passwd, unsigned int flag)
+BABY_COM(Commands::pass)
 {
-  char pwdata[PWBUF_SIZE];
-  struct passwd pwd, *pwd_ptr;
-
   // before everything else, lets check if it is really time for pass...
-  if(user->logged_in != USER_SUPPLIED)
+  if(user->logged_in() != USER_SUPPLIED)
   {
     string message;
-    if(user->logged_in == NOT_LOGGED_IN)
+    if(user->logged_in() == NOT_LOGGED_IN)
       message = "503 Login with 'USER' first.";
-    else if(user->logged_in == LOGGED_IN)
+    else if(user->logged_in() == LOGGED_IN)
       message = "503 You are already logged in!";
 
-    ftp_send(user, message);
+    *user << message << "\r\n";
     return(false);
   }
 
@@ -98,27 +125,30 @@ bool Commands::pass(User *user, string p
   struct pam_data *auth_data = new struct pam_data;
   struct pam_conv pamconv = { &pam_conversate, auth_data };
   int pam_error = 0;
+  pam_handle_t *tmp_pamh = user->pamh();
 
-  auth_data->user_name = user->user_name;
-  auth_data->password = passwd;
+  auth_data->user_name = user->user_name();
+  auth_data->password = argument;
+  argument.clear();
 
   sem_wait(&comm.pam_lock);
-  pam_error = pam_start("babyftpd", user->user_name.c_str(),
-      &pamconv, &user->pamh);
+  pam_error = pam_start("babyftpd", user->user_name().c_str(),
+      &pamconv, &tmp_pamh);
+  user->pamh(tmp_pamh);
 
   if(pam_error == 0)
   {
-    pam_error = pam_authenticate(user->pamh, 0);
+    pam_error = pam_authenticate(user->pamh(), 0);
     if(pam_error == 0)
     {
-      pam_error = pam_acct_mgmt(user->pamh, 0);
+      pam_error = pam_acct_mgmt(user->pamh(), 0);
       if(pam_error == 0)
-        pam_error = pam_setcred(user->pamh, PAM_ESTABLISH_CRED);
+        pam_error = pam_setcred(user->pamh(), PAM_ESTABLISH_CRED);
     }
   }
   if(pam_error != 0)
   {
-    pam_end(user->pamh, pam_error);
+    pam_end(user->pamh(), pam_error);
     sem_post(&pam_lock);
     seteuid(config->general.user); // and back to some other user..
   }
@@ -127,91 +157,85 @@ bool Commands::pass(User *user, string p
 
   if(pam_error == 0)
   {
-    pam_open_session(user->pamh, PAM_SILENT);
-    user->settings.set(PAM_DONE);
+    pam_open_session(user->pamh(), PAM_SILENT);
+    user->settings(PAM_DONE, B_SET);
 
     const char *pam_username;
-    pam_get_item(user->pamh, PAM_USER,
+    pam_get_item(user->pamh(), PAM_USER,
         (reinterpret_cast<const void**>(&pam_username)));
     sem_post(&pam_lock);
-    if(user->user_name != pam_username)
+    if(user->user_name() != pam_username)
     {
-      logging->log_this(4, LOG_INFO, "PAM changed username of " +
-          user->user_name + " to " + pam_username);
-      user->user_name = pam_username;
-    }
-
-    Commands::ftp_send(user, "230 Password accepted, proceeding.");
-    sem_wait(&user->user_lock);
-    user->logged_in = LOGGED_IN;
-    sem_post(&user->user_lock);
-    getpwnam_r(user->user_name.c_str(), &pwd, pwdata, sizeof(pwdata), &pwd_ptr);
-    uid_t uid = pwd.pw_uid;
-    gid_t gid = pwd.pw_gid;
-    sem_wait(&user->user_lock);
-    if(config->get_user(user->user_name).only_home)
-      user->root_dir = pwd.pw_dir;
-    user->current_dir = pwd.pw_dir;
-    sem_post(&user->user_lock);
-    setgid(gid);
-    seteuid(uid); // dropping permissions...
+      logging->log_this(3, LOG_INFO, "PAM changed username of " +
+          user->user_name() + " to " + pam_username);
+      user->user_name(pam_username);
+    }
+
+      *user << "230 Password accepted, proceeding.\r\n";
+    user->login();
     return(true);
   }
-  sem_post(&user->user_lock);
-  ftp_send(user, "530 Login denied.");
+  *user << "530 Login denied.\r\n";
   return(false);
 }
 
-bool Commands::help(User *user, string argument, unsigned int flag)
+BABY_COM(Commands::help)
 {
   string message;
   if(!argument.size())
   {
     int cmd = 0;
     message = "214-The following commands are understood:\r\n ";
-    while(handler.cmd_info[cmd].command != "\n")
+    for(hash_map<const char*, struct command_info, hash<const char*>,
+        Utilities::eqstr>::iterator cmd_it = handler->cmd_info.begin();
+        cmd_it != handler->cmd_info.end(); cmd_it++)
     {
       if(cmd != 0 && cmd % 8 == 0)
         message += "\r\n ";
-      if(handler.cmd_info[cmd].hlp_info.size() != 0)
-        message += handler.cmd_info[cmd].command + "\t";
+      if(!(*(cmd_it)).second.hlp_info.empty())
+      {
+        message += (*(cmd_it)).first;
+        message += '\t';
       cmd++;
     }
-    message += "\r\n214 if you have any trouble please contact the administrator at " + config->general.admin_email + ".";
+    }
+    message +=
+      "\r\n214 if you have any trouble please contact the administrator at "
+      + config->general.admin_email + ".";
   }
   else
   {
     // other help stuff here...
     message  = "214 Syntax: ";
-    transform(argument.begin(), argument.end(), argument.begin(), (int(*)(int))toupper);
-    int cmd = 0;
-    for(; handler.cmd_info[cmd].command != "\n"; cmd++)
-      if(handler.cmd_info[cmd].command == argument && handler.cmd_info[cmd].hlp_info.size() != 0)
-      {
-        message += handler.cmd_info[cmd].command + handler.cmd_info[cmd].hlp_info;
-        break;
-      }
-
-    if(handler.cmd_info[cmd].command == "\n")
+    transform(argument.begin(), argument.end(), argument.begin(),
+        (int(*)(int))toupper);
+    hash_map<const char*, struct command_info, hash<const char*>, Utilities::
+      eqstr>::iterator cmd_it = handler->cmd_info.find(argument.c_str());
+
+    if(cmd_it != handler->cmd_info.end() &&
+        !(*(cmd_it)).second.hlp_info.empty())
+      message += ((*(cmd_it)).first + (*(cmd_it)).second.hlp_info);
+    else
       message = "500 No help available for '" + argument + "'";
   }
-  ftp_send(user, message);
+  *user << message << "\r\n";
   return(true);
 }
 
-bool Commands::cwd(User *user, string dir, unsigned int flag)
+BABY_COM(Commands::cwd)
 {
   struct stat stt;
   string message, new_dir;
   DIR *tmp_dir;
 
   if(util.is_set(flag, COM_CDUP))
-    dir = "..";
+    argument = "..";
 
-  if((user->root_dir.size() > 1 && (user->current_dir.size() == user->root_dir.size())) && (dir == ".." || dir == "."))
-    new_dir = user->root_dir;
+  if((user->root_dir().size() > 1 && (user->current_dir().size() ==
+          user->root_dir().size())) && (argument == ".." || argument == "."))
+    new_dir = user->root_dir();
   else
-    new_dir = Commands::parsedir(user, dir);
+    new_dir = this->parsedir(user, argument);
 
 
   lstat(new_dir.c_str(), &stt);
@@ -224,7 +248,7 @@ bool Commands::cwd(User *user, string di
     len = readlink(new_dir.c_str(), real_name, 60);
     real_name[len] = '\0';
 
-    new_dir = Commands::parsedir(user, real_name);
+    new_dir = this->parsedir(user, real_name);
   }
 
   if(!(tmp_dir = opendir(new_dir.c_str())))
@@ -241,72 +265,65 @@ bool Commands::cwd(User *user, string di
     else // COM_CWD
       message = "250 'CWD' command successful.";
 
-    sem_wait(&user->user_lock);
-    user->current_dir = new_dir;
-    sem_post(&user->user_lock);
+    user->current_dir(new_dir);
   }
   closedir(tmp_dir);
-  ftp_send(user, message);
+  *user << message << "\r\n";
   return(true);
 }
 
-bool Commands::retr(User *user, string file, unsigned int flag)
+BABY_COM(Commands::retr)
 {
   struct stat stt;
 
-  string file_name = parsedir(user, file);
+  string file_name = parsedir(user, argument);
   stat(file_name.c_str(), &stt);
   if(!S_ISREG(stt.st_mode))
   {
-    ftp_send(user, "550 \"" + file + "\" is not a regular file.");
+    *user << "550 \"" << argument << "\" is not a regular file.\r\n";
     return(false);
   }
 
   int file_id = open(file_name.c_str(), O_RDONLY);
-  if(user->rest_pos > 0)
+  if(user->rest_pos() > 0)
   {
-    lseek(file_id, user->rest_pos, SEEK_SET);
-    sem_wait(&user->user_lock);
-    user->rest_pos = 0;
-    sem_post(&user->user_lock);
+    lseek(file_id, user->rest_pos(), SEEK_SET);
+    user->rest_pos(0);
   }
 
-  ftp_send(user, "150 Opening " + static_cast<string>(user->settings.test(BINARY)?"BINARY":"ASCII") + " mode data connection for \"" + file + "\" (" + util.itos(stt.st_size) + " bytes).");
+  *user << "150 Opening " << (user->settings(BINARY)?"BINARY":"ASCII")
+      << " mode data connection for \"" << argument << "\" ("
+      << util.itos(stt.st_size) << " bytes).\r\n";
 
   class Data *data = new Data(user, file_id, file_name, 1);
-  if(!data->isOpen() || user->data_vec_full())
+  if(!data->settings(DATA_OPEN) || user->data_vec_full())
   {
-    ftp_send(user, "425 Can't open data connection.");
+    *user << "425 Can't open data connection.\r\n";
     delete data;
     return(false);
   }
 
-  sem_wait(&user->user_lock);
-  if(user->rest_pos > 0)
-  {
-    sem_wait(&data->data_lock);
-    data->rest_pos = user->rest_pos;
-    sem_post(&data->data_lock);
-    user->rest_pos = 0;
-  }
-
+  sem_wait(&user->lock);
   user->data_vec.push_back(data);
-  sem_post(&user->user_lock);
-  pthread_create(&data->thread_id, NULL, data_wrapper, static_cast<void*>(data));
-  pthread_detach(data->thread_id);
+  sem_post(&user->lock);
+  pthread_t temp_id;
+  pthread_create(&temp_id, NULL, data_wrapper,
+      static_cast<void*>(data));
   return(true);
 }
 
-bool Commands::stor(User *user, string file, unsigned int flag)
+BABY_COM(Commands::stor)
 {
   int file_id;
 
-  string file_name = parsedir(user, file);
+  string file_name = parsedir(user, argument);
 
   if(util.is_set(flag, COM_STOR))
-    file_id = open(file_name.c_str(), O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
+    file_id = open(file_name.c_str(), O_WRONLY|O_CREAT|O_TRUNC,
+        S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
   else // com_type == COM_APPE
-    file_id = open(file_name.c_str(), O_WRONLY|O_APPEND|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
+    file_id = open(file_name.c_str(), O_WRONLY|O_APPEND|O_TRUNC,
+        S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
 
   if(file_id == -1)
   {
@@ -319,42 +336,34 @@ bool Commands::stor(User *user, string f
       message = "550 Access to " + file_name + " denied.";
     else
       message = "550 File operation failed.";
-    ftp_send(user, message);
+    *user << message << "\r\n";
     return false;
   }
 
-  if(user->rest_pos > 0 && util.is_set(flag, COM_STOR)) // shouldn't try to restart an appending :p
+  // shouldn't try to restart an appending :p
+  if(user->rest_pos() > 0 && util.is_set(flag, COM_STOR))
   {
-    lseek(file_id, user->rest_pos, SEEK_SET);
-    sem_wait(&user->user_lock);
-    user->rest_pos = 0;
-    sem_post(&user->user_lock);
+    lseek(file_id, user->rest_pos(), SEEK_SET);
+    user->rest_pos(0);
   }
 
-  ftp_send(user, "150 Opening " + static_cast<string>(user->settings.test(BINARY)?"BINARY":"ASCII") + " mode data connection for \"" + file + "\".");
+  *user << "150 Opening " << (user->settings(BINARY)?"BINARY":"ASCII")
+      << " mode data connection for \"" << argument << "\".\r\n";
 
   class Data *data = new Data(user, file_id, file_name, 0);
-  if(!data->isOpen() || user->data_vec_full())
+  if(!data->settings(DATA_OPEN) || user->data_vec_full())
   {
-    ftp_send(user, "425 Can't open data connection.");
+    *user << "425 Can't open data connection.\r\n";
     delete data;
     return(false);
   }
 
-  sem_wait(&user->user_lock);
-  if(user->rest_pos > 0 && util.is_set(flag, COM_STOR)) // shouldn't try to restart an appending :p
-  {
-    lseek(file_id, user->rest_pos, SEEK_SET);
-    sem_wait(&data->data_lock);
-    data->rest_pos = user->rest_pos;
-    sem_post(&data->data_lock);
-    user->rest_pos = 0;
-  }
-
+  sem_wait(&user->lock);
   user->data_vec.push_back(data);
-  sem_post(&user->user_lock);
-  pthread_create(&data->thread_id, NULL, data_wrapper, static_cast<void*>(data));
-  pthread_detach(data->thread_id);
+  sem_post(&user->lock);
+  pthread_t temp_id;
+  pthread_create(&temp_id, NULL, data_wrapper,
+      static_cast<void*>(data));
   return(true);
 }
 
@@ -363,56 +372,64 @@ bool Commands::rest(User *user, string a
   char *endptr;
   string message;
 
-  unsigned long fpos = strtoul(argument.c_str(), &endptr, 0);
+  off_t fpos = strtoul(argument.c_str(), &endptr, 0);
   if(!*endptr)
   {
-    sem_wait(&user->user_lock);
-    user->rest_pos = fpos;
-    sem_post(&user->user_lock);
+    user->rest_pos(fpos);
     message = "350 Restarting at " + argument + ". Send STOR or RETR.";
   }
   else
     message = "501 Restart point not in numeric format.";
 
-  ftp_send(user, message);
+  *user << message << "\r\n";
   return(true);
 }
 
-bool Commands::list(User *user, string argument, unsigned int flag)
+BABY_COM(Commands::list)
 {
   struct stat stt;
   string failure;
 
   if(argument.size() == 0)
-    argument = user->current_dir;
+    argument = user->current_dir();
 
   string file = parsedir(user, argument);
-  int stat_err = lstat(file.c_str(), &stt);
 
-  if(stat_err == -1 && ((util.is_set(flag, COM_LONG) || util.is_set(flag, COM_MLSD)) && argument[0] == '-'))
+  if((lstat(file.c_str(), &stt)) == -1 && ((util.is_set(flag, COM_LONG) ||
+          util.is_set(flag, COM_MLSD)) && argument[0] == '-'))
   {
     class StringTokenizer *token = new StringTokenizer(argument, " ", false);
     token->nextToken();
     argument = token->restTokens();
     delete token;
     file = parsedir(user, argument);
-    stat_err = lstat(file.c_str(), &stt);
+    lstat(file.c_str(), &stt);
   }
 
   if(!S_ISDIR(stt.st_mode) && util.is_set(flag, COM_MLSD))
   {
-    ftp_send(user, "501 Argument to MLSD has to be a directory.");
+    *user << "501 Argument to MLSD has to be a directory.\r\n";
     return false;
   }
 
   class Data *data = new Data(user);
 
-  if(!data->isOpen())
+  if(!data->settings(DATA_OPEN))
     failure = "425 Can't open data connection.";
   else
   {
-    string message;
-    size_t write_size = READ_SIZE;
+    *user << "150 Opening ";
+#ifdef USE_TLS
+    if(data->settings(TLS_PROT_D))
+    {
+      *user << "PROTECTED ASCII mode data connection for file list.\r\n";
+      if(!data->engage_tls())
+        failure =
+          "451 Failed to negotiate TLS as was asked. Closing data connection.";
+    }
+    else
+#endif // USE_TLS
+      *user << " ASCII mode data connection for file list.\r\n";
     if(S_ISDIR(stt.st_mode))
     {
       struct dirent dirstr, *dirptr;
@@ -430,115 +447,83 @@ bool Commands::list(User *user, string a
 
       closedir(dirp);
 
-      sort(dirlist.begin(), dirlist.end()); // sorting the directory so we output first things first
-      for(vector<string>::iterator dir_it = dirlist.begin(); dir_it != dirlist.end(); dir_it++)
+      // sorting the directory so we output first things first
+      sort(dirlist.begin(), dirlist.end());
+      for(vector<string>::iterator dir_it = dirlist.begin();
+          dir_it != dirlist.end(); dir_it++)
       {
         if(util.is_set(flag, COM_LONG))
-          message += list_file(file + "/" + *dir_it);
+          *data << list_file(file + "/" + *dir_it);
         else if(util.is_set(flag, COM_MLSD))
-          message += mlsx(user, file + "/" + *dir_it);
+          *data << mlsx(user, file + "/" + *dir_it);
         else
-          message += *dir_it; // short list.
+          *data << *dir_it; // short list.
 
-        message += "\r\n"; // and newline.
+        *data << "\r\n";
+        if(data->settings(FAILURE))
+          break;
       }
     }
     else
     {
       if(util.is_set(flag, COM_LONG))
-        message += list_file(file);
-      else
-        message += argument;
-      message += "\r\n";
+        *data << list_file(file);
+      else // NLST
+        *data << argument;
+      *data << "\r\n";
     }
-    sem_wait(&data->data_lock);
-#ifdef USE_TLS
-    if(user->settings.test(TLS_PROT_D))
-    {
-      ftp_send(user, "150 Opening PROTECTED ASCII mode data connection for file list.");
-      if(gnutls_handshake(data->data_session) < 0) // handshake failed
-        failure =  "451 Failed to negotiate TLS as was asked. Closing data connection.";
-      else
-        write_size = gnutls_record_get_max_size(data->data_session);
-    }
-    else
-#endif
-      ftp_send(user, "150 Opening ASCII mode data connection for file list.");
-
-
-    size_t written = 0;
-    struct timeval tv = { 1,0 };
-    fd_set wfds;
-    FD_ZERO(&wfds);
-    FD_SET(data->data_id, &wfds);
-
-    if(!select(data->data_id + 1, NULL, &wfds, NULL, &tv))
-      failure = "425 Data connection lost.";
-
-    while(written < message.size() && !failure.size())
-    {
-      string writing;
-
-      writing.assign(message, written, write_size);
-#ifdef USE_TLS
-      if(user->settings.test(TLS_PROT_D))
-      {
-        if(failure.size() == 0)
-          written += gnutls_record_send(data->data_session, static_cast<const void*>(writing.c_str()), writing.size());
-      } else
-#endif // USE_TLS
-        written += write(data->data_id, static_cast<const void*>(writing.c_str()), writing.size());
-    }
-    close(data->data_id);
-    sem_post(&data->data_lock);
-    sem_wait(&user->user_lock);
-    user->total_transfered += written;
-    sem_post(&user->user_lock);
+    if(data->settings(FAILURE))
+      failure = "426 LIST transfer failed.";
   }
   if(failure.size() > 0)
-    ftp_send(user, failure);
+    *user << failure << "\r\n";
   else
-    ftp_send(user, "226 Transfer complete.");
+    *user << "226 Transfer complete.\r\n";
   delete data;
-  return(true);
+  return(data->settings(FAILURE));
 }
 
 string Commands::list_file(string file_name)
 {
-  char size[20], nlink[6], time_string[25], pwdbuf[PWBUF_SIZE], grpbuf[PWBUF_SIZE];
-  string user_name, group_name;
+  char size[20], nlink[6], pwdbuf[PWBUF_SIZE], grpbuf[PWBUF_SIZE];
+  string user_name, group_name, time_string;
   struct stat file_stat;
   struct passwd pwd, *pwd_ptr;
   struct group grp, *grp_ptr;
-  struct tm file_time;
 
   lstat(file_name.c_str(), &file_stat);
 
-  snprintf(nlink, 5, "%3d ", static_cast<int>(file_stat.st_nlink)); // number of hard links
+  // number of hard links
+  snprintf(nlink, 5, "%3d ", static_cast<int>(file_stat.st_nlink));
 
-  if((getpwuid_r(file_stat.st_uid, &pwd, pwdbuf, sizeof(pwdbuf), &pwd_ptr)) == 0)
+  if((getpwuid_r(file_stat.st_uid, &pwd, pwdbuf, sizeof(pwdbuf),
+          &pwd_ptr)) == 0)
     user_name = pwd.pw_name;
   else
     user_name = util.itos(file_stat.st_uid);
 
-  if((getgrgid_r(file_stat.st_gid, &grp, grpbuf, sizeof(grpbuf), &grp_ptr)) == 0)
+  if((getgrgid_r(file_stat.st_gid, &grp, grpbuf, sizeof(grpbuf),
+          &grp_ptr)) == 0)
     group_name = grp.gr_name;
   else
     group_name = util.itos(file_stat.st_gid);
 
   snprintf(size, 19, "%9d ", static_cast<int>(file_stat.st_size)); // filesize
 
-  gmtime_r(&file_stat.st_mtime, &file_time);
   // modification time, either in new ls format, or old..
 #ifdef USE_NEWLS
-  strftime(time_string, 24, "%Y-%m-%d %H:%M ", &file_time);
+  string time_format = "%Y-%m-%d %H:%M ";
 #else
-  if((time(NULL) - file_stat.st_mtime) > (60 * 60 * 24 * 365)) // is the file over a year old?
-    strftime(time_string, 24, "%b %e  %Y ", &file_time);
+  string time_format;
+  // is the file over a year old?
+  if((time(NULL) - file_stat.st_mtime) > (60 * 60 * 24 * 365))
+    time_format = "%b %e  %Y ";
   else // if it's not, we display the time it was created as well.
-    strftime(time_string, 24, "%b %e %H:%M ", &file_time);
+    time_format = "%b %e %H:%M ";
 #endif // USE_NEWLS
 
+  util.time_string(time_string, time_format, 25, file_stat.st_mtime);
+
   string message = (util.parse_perms_ls(file_stat.st_mode) + ' ')
     + nlink + user_name + '\t' + group_name + '\t' + size + time_string
     + basename(file_name.c_str()); // parsing everything on one line is neater.
@@ -567,19 +552,19 @@ string Commands::mlsx(User *user, string
   lstat(file_name.c_str(), &stt);
 
   // type.
-  if(user->settings.test(FACT_TYPE))
+  if(user->settings(FACT_TYPE))
   {
     string type;
     if(S_ISREG(stt.st_mode))
       type = "file";
     else if(S_ISDIR(stt.st_mode))
     {
-      if(file_name == user->current_dir)
+      if(file_name == user->current_dir())
       {
         type = "cdir";
         file_name += "/.";
       }
-      else if(file_name == parsedir(user, user->current_dir + "/.."))
+      else if(file_name == parsedir(user, user->current_dir() + "/.."))
         type = "pdir";
       else
         type = "dir";
@@ -598,17 +583,23 @@ string Commands::mlsx(User *user, string
     message =  "Type=" + type + ";";
   }
 
-  if(user->settings.test(FACT_SIZE))
+  if(user->settings(FACT_SIZE))
     message += "Size=" + util.itos(stt.st_size) + ";";
-  if(user->settings.test(FACT_MODIFY))
-    message += "Modify=" + mod_time(stt) + ";"; // mod time...
-  if(user->settings.test(FACT_UNIQUE))
-    message += "Unique=" + util.itos(stt.st_dev) + "g" + util.itos(stt.st_ino) + ";";
-  if(user->settings.test(FACT_PERM))
+  if(user->settings(FACT_MODIFY))
+  {
+    string tmp_time;
+    util.time_string(tmp_time, "Modify=%Y%m%d%H%M%S;", 23, stt.st_mtime);
+    message += tmp_time;
+  }
+  if(user->settings(FACT_UNIQUE))
+    message += "Unique=" + util.itos(stt.st_dev) + "g" +
+      util.itos(stt.st_ino) + ";";
+  if(user->settings(FACT_PERM))
   {
     bool owner = false, group = false;
 
-    if(getpwnam_r(user->user_name.c_str(), &pwd, pwdbuf, sizeof(pwdbuf), &pwd_ptr))
+    if(getpwnam_r(user->user_name().c_str(), &pwd, pwdbuf, sizeof(pwdbuf),
+          &pwd_ptr))
     {
       if(pwd.pw_uid == stt.st_uid)
         owner = true;
@@ -620,7 +611,7 @@ string Commands::mlsx(User *user, string
     {
       if(getgrgid_r(stt.st_gid, &grp, grpbuf, sizeof(grpbuf), &grp_ptr))
         for(int i = 0; grp.gr_mem[i] != NULL; i++)
-          if(user->user_name == grp.gr_mem[i])
+          if(user->user_name() == grp.gr_mem[i])
             group = true;
     }
 
@@ -629,56 +620,55 @@ string Commands::mlsx(User *user, string
     {
       if(S_ISDIR(stt.st_mode))
       {
-        if(util.test(stt.st_mode, S_IRUSR)) message += "l";
-        if(util.test(stt.st_mode, S_IWUSR)) message += "cmp";
-        if(util.test(stt.st_mode, S_IXUSR)) message += "e";
+        if(util.is_set(stt.st_mode, S_IRUSR)) message += "l";
+        if(util.is_set(stt.st_mode, S_IWUSR)) message += "cmp";
+        if(util.is_set(stt.st_mode, S_IXUSR)) message += "e";
       }
-      if(util.test(stt.st_mode, S_IRUSR)) message += "r";
-      if(util.test(stt.st_mode, S_IWUSR)) message += "adfw";
+      if(util.is_set(stt.st_mode, S_IRUSR)) message += "r";
+      if(util.is_set(stt.st_mode, S_IWUSR)) message += "adfw";
     }
     else if(group)
     {
       if(S_ISDIR(stt.st_mode))
       {
-        if(util.test(stt.st_mode, S_IRGRP)) message += "l";
-        if(util.test(stt.st_mode, S_IWGRP)) message += "cmp";
-        if(util.test(stt.st_mode, S_IXGRP)) message += "e";
+        if(util.is_set(stt.st_mode, S_IRGRP)) message += "l";
+        if(util.is_set(stt.st_mode, S_IWGRP)) message += "cmp";
+        if(util.is_set(stt.st_mode, S_IXGRP)) message += "e";
       }
-      if(util.test(stt.st_mode, S_IRGRP)) message += "r";
-      if(util.test(stt.st_mode, S_IWGRP)) message += "adfw";
+      if(util.is_set(stt.st_mode, S_IRGRP)) message += "r";
+      if(util.is_set(stt.st_mode, S_IWGRP)) message += "adfw";
     }
     else
     {
       if(S_ISDIR(stt.st_mode))
       {
-        if(util.test(stt.st_mode, S_IROTH)) message += "l";
-        if(util.test(stt.st_mode, S_IWOTH)) message += "cmp";
-        if(util.test(stt.st_mode, S_IXOTH)) message += "e";
+        if(util.is_set(stt.st_mode, S_IROTH)) message += "l";
+        if(util.is_set(stt.st_mode, S_IWOTH)) message += "cmp";
+        if(util.is_set(stt.st_mode, S_IXOTH)) message += "e";
       }
-      if(util.test(stt.st_mode, S_IROTH)) message += "r";
-      if(util.test(stt.st_mode, S_IWOTH)) message += "adfw";
+      if(util.is_set(stt.st_mode, S_IROTH)) message += "r";
+      if(util.is_set(stt.st_mode, S_IWOTH)) message += "adfw";
     }
     message += ";";
   }
-  if(user->settings.test(FACT_U_MODE))
+  if(user->settings(FACT_U_MODE))
     message += "Unix.mode=" + util.parse_perms_num(stt.st_mode) + ";";
-  if(user->settings.test(FACT_U_UID))
+  if(user->settings(FACT_U_UID))
     message += "Unix.uid=" + util.itos(stt.st_uid) + ";";
-  if(user->settings.test(FACT_U_GID))
+  if(user->settings(FACT_U_GID))
     message += "Unix.gid=" + util.itos(stt.st_gid) + ";";
-  if(user->settings.test(FACT_U_LINK))
+  if(user->settings(FACT_U_LINK))
     message += "Unix.link=" + util.itos(stt.st_nlink) + ";";
-  if(user->root_dir.size() > 1)
-    file_name.erase(0, user->root_dir.size());
+  if(user->root_dir().size() > 1)
+    file_name.erase(0, user->root_dir().size());
   message += " " + file_name;
 
   return(message);
 }
 
-
-bool Commands::mkd(User *user, string dir, unsigned int flag)
+BABY_COM(Commands::mkd)
 {
-  string message, new_dir = parsedir(user, dir);
+  string message, new_dir = parsedir(user, argument);
 
   if(mkdir(new_dir.c_str(), 0755))
   {
@@ -688,20 +678,20 @@ bool Commands::mkd(User *user, string di
     else if(errno == EACCES)
       message += "Write access denied.";
     else if(errno == (ENOTDIR || ENOENT))
-      message += "Faulty pathname, directory not created.\r\n";
+      message += "Faulty pathname, directory not created.";
     else
       message += "Directory not created.";
   }
   else
-    message = "257 \"" + dir + "\" created.\r\n";
+    message = "257 \"" + argument + "\" created.";
 
-  ftp_send(user, message);
+  *user << message << "\r\n";
   return(true);
 }
 
-bool Commands::rmd(User *user, string dir, unsigned int flag)
+BABY_COM(Commands::rmd)
 {
-  string message, new_dir = parsedir(user, dir);
+  string message, new_dir = parsedir(user, argument);
 
   if(rmdir(new_dir.c_str()))
   {
@@ -714,15 +704,15 @@ bool Commands::rmd(User *user, string di
       message += "Directory not removed.";
   }
   else
-    message = "250 \"" + dir + "\" removed.";
+    message = "250 \"" + argument + "\" removed.";
 
-  ftp_send(user, message);
+  *user << message << "\r\n";
   return(true);
 }
 
-bool Commands::dele(User *user, string file, unsigned int flag)
+BABY_COM(Commands::dele)
 {
-  string message, new_file = parsedir(user, file);
+  string message, new_file = parsedir(user, argument);
 
   if(unlink(new_file.c_str()))
   {
@@ -739,22 +729,21 @@ bool Commands::dele(User *user, string f
   else
     message = "250 'DELE' command successful.";
 
-  ftp_send(user, message);
+  *user << message << "\r\n";
   return(true);
 }
 
-bool Commands::port(User *user, string argument, unsigned int flag)
+BABY_COM(Commands::port)
 {
   string tmp_adress, message;
-  int port;
-
-  memset(&user->data_addr, 0, sizeof(user->data_addr));
+  int port = -1;
 
-  if(user->settings.test(EPSV_ONLY))
+  if(user->settings(EPSV_ONLY))
     message = "501 Only 'EPSV' may be used after the issuing of 'EPSV ALL'";
 
-  else if((config->get_user(user->user_name).only_passive))
-    message = "501 Sorry, active data transfers are disabled for your account.";
+  else if((config->get_user(user->user_name()).only_passive))
+    message =
+      "501 Sorry, active data transfers are disabled for your account.";
 
   else if(util.is_set(flag, COM_PORT))
   {
@@ -770,17 +759,18 @@ bool Commands::port(User *user, string a
       tmp_adress += token->nextToken() + "."; // the wrong direction, it's
       tmp_adress += token->nextToken();       // weird.
 
-      port = ntohs(atoi((token->nextToken()).c_str()) << 8 | atoi((token->nextToken()).c_str()));
+      port = atoi((token->nextToken()).c_str()) * 256 +
+          atoi((token->nextToken()).c_str());
     }
     delete token;
   }
   else if(util.is_set(flag, COM_EPRT))
   {
-    char separator[1];
-    separator[0] = static_cast<char>(argument[0]);
-    class StringTokenizer *token = new StringTokenizer(argument, separator, false);
+    class StringTokenizer *token =
+      new StringTokenizer(argument, argument.substr(0, 1), false);
 
-    if(token->countTokens() != 3 || argument[argument.size() - 1] != argument[0])
+    if(token->countTokens() != 3 ||
+        argument[argument.size() - 1] != argument[0])
       message = "501 Illegal 'EPRT' command.";
 
     else
@@ -788,7 +778,7 @@ bool Commands::port(User *user, string a
       if(token->nextToken() == util.itos(PROT_IPV4))
       {
         tmp_adress = token->nextToken();
-        port = htons(atoi(token->nextToken().c_str()));
+        port = atoi(token->nextToken().c_str());
       }
       else
         message = "522 Network protocol not supported, use (1)";
@@ -796,10 +786,10 @@ bool Commands::port(User *user, string a
     delete token;
   }
 
-  if(message.size() == 0)
+  if(message.empty())
   {
-    sem_wait(&user->user_lock);
-    if((inet_aton(tmp_adress.c_str(), &user->data_addr.sin_addr)) == 0)
+    struct sockaddr_in temp_addr;
+    if((inet_aton(tmp_adress.c_str(), &temp_addr.sin_addr)) == 0)
     {
       message = "501 Illegal '";
       message += (util.is_set(flag, COM_PORT) ? "PORT" : "EPRT");
@@ -807,32 +797,31 @@ bool Commands::port(User *user, string a
     }
     else
     {
-      user->data_addr.sin_port = port;
-      user->data_addr.sin_family = AF_INET;
-      user->settings.reset(PASSIVE);
+      temp_addr.sin_port = htons(port);
+      temp_addr.sin_family = AF_INET;
+      user->settings(PASSIVE, B_RESET);
       message += "200 '";
       message += (util.is_set(flag, COM_PORT) ? "PORT" : "EPRT");
       message += "' command successful.";
     }
-    sem_post(&user->user_lock);
+    user->data_addr(temp_addr);
   }
 
-  ftp_send(user, message);
+  *user << message << "\r\n";
   return(true);
 }
 
-bool Commands::pasv(User *user, string argument, unsigned int flag)
+BABY_COM(Commands::pasv)
 {
   string message;
 
   if(util.is_set(flag, COM_EPSV) && argument.size() > 0)
   {
-    transform(argument.begin(), argument.end(), argument.begin(), (int(*)(int))toupper);
+    transform(argument.begin(), argument.end(), argument.begin(),
+        (int(*)(int))toupper);
     if(argument == "ALL" && util.is_set(flag, COM_EPSV))
     {
-      sem_wait(&user->user_lock);
-      user->settings.set(EPSV_ONLY);
-      sem_post(&user->user_lock);
+      user->settings(EPSV_ONLY, B_SET);
       message = "200 Now only accepting 'EPSV'";
     }
     else
@@ -840,45 +829,45 @@ bool Commands::pasv(User *user, string a
   }
   else
   {
-    struct sockaddr_in tmp_addr, tmp_addr2;
+    struct sockaddr_in tmp_addr = {0}, tmp_addr1 = {0}, tmp_addr2 = {0};
     socklen_t len = sizeof(tmp_addr);
 
-    memset(&tmp_addr, 0, len);
-    memset(&tmp_addr2, 0, len);
+    if(user->data_id())
+      close(user->data_id());
 
-    sem_wait(&user->user_lock);
-    memset(&user->data_addr, 0, sizeof(user->data_addr));
-    if(user->data_id)
-      close(user->data_id);
-
-    user->data_id = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-    user->data_addr.sin_family = AF_INET;
-    user->data_addr.sin_addr.s_addr = INADDR_ANY;
-    bind(user->data_id, reinterpret_cast<struct sockaddr*>(&user->data_addr), sizeof(user->data_addr));
-    listen(user->data_id, 1);
-    getsockname(user->data_id, reinterpret_cast<struct sockaddr*>(&tmp_addr), &len);
-    getsockname(user->socket_id, reinterpret_cast<struct sockaddr*>(&tmp_addr2), &len);
-    user->settings.set(PASSIVE);
-    sem_post(&user->user_lock);
+    user->data_id(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
+    tmp_addr.sin_family = AF_INET;
+    tmp_addr.sin_addr.s_addr = INADDR_ANY;
+    bind(user->data_id(), reinterpret_cast<struct sockaddr*>
+        (&tmp_addr), sizeof(tmp_addr));
+    user->data_addr(tmp_addr);
+    listen(user->data_id(), 1);
+    getsockname(user->data_id(), reinterpret_cast<struct sockaddr*>
+        (&tmp_addr1), &len);
+    getsockname(user->socket_id(), reinterpret_cast<struct sockaddr*>
+        (&tmp_addr2), &len);
+    user->settings(PASSIVE, B_SET);
 
-    unsigned int p = ntohs(tmp_addr.sin_port);
+    unsigned int p = ntohs(tmp_addr1.sin_port);
 
     if(util.is_set(flag, COM_PASV))
     {
-      if(user->settings.test(EPSV_ONLY))
-        message = "501 Only 'EPSV' may be used after the issuing of 'EPSV ALL'";
+      if(user->settings(EPSV_ONLY))
+        message =
+          "501 Only 'EPSV' may be used after the issuing of 'EPSV ALL'";
 
       else
       {
-        class StringTokenizer *token = new StringTokenizer(inet_ntoa(tmp_addr2.sin_addr), ".", false);
+        class StringTokenizer *token =
+          new StringTokenizer(inet_ntoa(tmp_addr2.sin_addr), ".", false);
 
         message = "227 Entering Passive Mode (";
         message += token->nextToken() + ",";
         message += token->nextToken() + ",";
         message += token->nextToken() + ",";
         message += token->nextToken() + ",";
-        message += util.itos((p >> 8) & 255) + ","
-          + util.itos(p & 255) + ").";
+        message += util.itos(p / 256) + ","
+          + util.itos(p % 256) + ").";
 
         delete token;
       }
@@ -890,15 +879,16 @@ bool Commands::pasv(User *user, string a
           message = "522 Network protocol not supported, use (1)";
 
       if(message.size() == 0)
-        message = "229 Entering Extended Passive Mode (|||" + util.itos(p) + "|)";
+        message = "229 Entering Extended Passive Mode (|||" +
+          util.itos(p) + "|)";
     }
   }
-  ftp_send(user, message);
+  *user << message << "\r\n";
   return(true);
 }
 
 // rnfr and rnto
-bool Commands::rname(User *user, string argument, unsigned int flag)
+BABY_COM(Commands::rname)
 {
   string file_name, message;
 
@@ -906,31 +896,29 @@ bool Commands::rname(User *user, string 
 
   if(util.is_set(flag, COM_FROM))
   {
-    sem_wait(&user->user_lock);
-    user->temp_buf = file_name;
-    sem_post(&user->user_lock);
+    user->temp_buf(file_name);
     message = "350 Command OK, waiting for RNTO.";
   }
   else if(util.is_set(flag, COM_TO))
   {
-    if(user->temp_buf.size() == 0)
+    if(user->temp_buf().size() == 0)
       message = "503 RNTO must come after RNFR.";
     else
     {
-      if(!(rename(user->temp_buf.c_str(), file_name.c_str())))
-        message = "250 " + user->temp_buf + " renamed to " + file_name + ".";
+      if(!(rename(user->temp_buf().c_str(), file_name.c_str())))
+        message = "250 " + user->temp_buf() + " renamed to " + file_name + ".";
       else if(errno == EACCES)
         message = "550 Access denied.";
       else
         message = "553 Rename failed.";
-      user->temp_buf.erase();
+      user->temp_buf("");
     }
   }
-  ftp_send(user, message);
+  *user << message << "\r\n";
   return(true);
 }
 
-bool Commands::opts(User *user, string argument, unsigned int flag)
+BABY_COM(Commands::opts)
 {
   string command, message;
   class StringTokenizer *st;
@@ -943,8 +931,9 @@ bool Commands::opts(User *user, string a
   }
   else
   {
-    transform(argument.begin(), argument.end(), argument.begin(), (int(*)(int))toupper);
-    st = new StringTokenizer(argument, " ", true);
+    transform(argument.begin(), argument.end(), argument.begin(),
+        (int(*)(int))toupper);
+    st = new StringTokenizer(argument, " ", false);
     command = st->nextToken();
     argument = st->restTokens();
     delete st;
@@ -954,23 +943,20 @@ bool Commands::opts(User *user, string a
       string fact;
       st = new StringTokenizer(argument, ";", false);
 
-      sem_wait(&user->user_lock);
       while(st->hasMoreTokens())
       {
         fact = st->nextToken();
-        if(fact == "TYPE") user->settings.flip(FACT_TYPE);
-        else if(fact == "SIZE") user->settings.flip(FACT_SIZE);
-        else if(fact == "MODIFY") user->settings.flip(FACT_MODIFY);
-        else if(fact == "UNIQUE") user->settings.flip(FACT_UNIQUE);
-        else if(fact == "PERM") user->settings.flip(FACT_PERM);
-        else if(fact == "UNIX.MODE") user->settings.flip(FACT_U_MODE);
-        else if(fact == "UNIX.UID") user->settings.flip(FACT_U_UID);
-        else if(fact == "UNIX.GID") user->settings.flip(FACT_U_GID);
-        else if(fact == "UNIX.LINK") user->settings.flip(FACT_U_LINK);
+        if(fact == "TYPE") user->settings(FACT_TYPE, B_FLIP);
+        else if(fact == "SIZE") user->settings(FACT_SIZE, B_FLIP);
+        else if(fact == "MODIFY") user->settings(FACT_MODIFY, B_FLIP);
+        else if(fact == "UNIQUE") user->settings(FACT_UNIQUE, B_FLIP);
+        else if(fact == "PERM") user->settings(FACT_PERM, B_FLIP);
+        else if(fact == "UNIX.MODE") user->settings(FACT_U_MODE, B_FLIP);
+        else if(fact == "UNIX.UID") user->settings(FACT_U_UID, B_FLIP);
+        else if(fact == "UNIX.GID") user->settings(FACT_U_GID, B_FLIP);
+        else if(fact == "UNIX.LINK") user->settings(FACT_U_LINK, B_FLIP);
       };
 
-      sem_post(&user->user_lock);
-
       message = "200 MLST string now showing: " + mlst_string(user);
       delete st;
     }
@@ -978,7 +964,7 @@ bool Commands::opts(User *user, string a
       message = "501 " + command + " not known as argument to OPTS.";
   }
 
-  ftp_send(user, message);
+  *user << message << "\r\n";
 
   return(true);
 }
@@ -987,33 +973,43 @@ string Commands::mlst_string(User *user)
 {
   string message;
 
-  message += ("Type"); message += (user->settings.test(FACT_TYPE) ? "*;" : ";");
-  message += ("Size"); message += (user->settings.test(FACT_SIZE) ? "*;" : ";");
-  message += ("Modify"); message += (user->settings.test(FACT_MODIFY) ? "*;" : ";");
-  message += ("Unique"); message += (user->settings.test(FACT_UNIQUE) ? "*;" : ";");
-  message += ("Perm"); message += (user->settings.test(FACT_PERM) ? "*;" : ";");
-  message += ("Unix.mode"); message += (user->settings.test(FACT_U_MODE) ? "*;" : ";");
-  message += ("Unix.uid"); message += (user->settings.test(FACT_U_UID) ? "*;" : ";");
-  message += ("Unix.gid"); message += (user->settings.test(FACT_U_GID) ? "*;" : ";");
-  message += ("Unix.link"); message += (user->settings.test(FACT_U_LINK) ? "*;" : ";");
+  message += ("Type"); message +=
+    (user->settings(FACT_TYPE) ? "*;" : ";");
+  message += ("Size"); message +=
+    (user->settings(FACT_SIZE) ? "*;" : ";");
+  message += ("Modify"); message +=
+    (user->settings(FACT_MODIFY) ? "*;" : ";");
+  message += ("Unique"); message +=
+    (user->settings(FACT_UNIQUE) ? "*;" : ";");
+  message += ("Perm"); message +=
+    (user->settings(FACT_PERM) ? "*;" : ";");
+  message += ("Unix.mode"); message +=
+    (user->settings(FACT_U_MODE) ? "*;" : ";");
+  message += ("Unix.uid"); message +=
+    (user->settings(FACT_U_UID) ? "*;" : ";");
+  message += ("Unix.gid"); message +=
+    (user->settings(FACT_U_GID) ? "*;" : ";");
+  message += ("Unix.link"); message +=
+    (user->settings(FACT_U_LINK) ? "*;" : ";");
   return(message);
 }
 
 // functions for small left over commands (noop, pwd, etc...)
-bool Commands::sim1(User *user, string argument, unsigned int flag)
+BABY_COM(Commands::sim1)
 {
   string message;
 
-  transform(argument.begin(), argument.end(), argument.begin(), (int(*)(int))toupper);
+  transform(argument.begin(), argument.end(), argument.begin(),
+      (int(*)(int))toupper);
 
   if(util.is_set(flag, COM_NOOP))
     message = "200 'NOOP': Command successful.";
 
   else if(util.is_set(flag, COM_PWD))
   {
-    string dir = user->current_dir;
-    if(user->root_dir.size() > 1)
-      dir.erase(0, user->root_dir.size());
+    string dir = user->current_dir();
+    if(user->root_dir().size() > 1)
+      dir.erase(0, user->root_dir().size());
     if(dir == "")
       dir = "/";
     message = "257 \"" + dir + "\" is current directory.";
@@ -1045,16 +1041,12 @@ bool Commands::sim1(User *user, string a
     if(argument == "A" || argument == "A N")
     {
       message = "200 Type set to " + argument + ".";
-      sem_wait(&user->user_lock);
-      user->settings.reset(BINARY);
-      sem_post(&user->user_lock);
+      user->settings(BINARY, B_RESET);
     }
     else if(argument == "I" || argument == "L 8")
     {
       message = "200 Type set to " + argument + ".";
-      sem_wait(&user->user_lock);
-      user->settings.reset(BINARY);
-      sem_post(&user->user_lock);
+      user->settings(BINARY, B_SET);
     }
     else
       message = "504 'TYPE " + argument + "' not understood.";
@@ -1080,34 +1072,36 @@ bool Commands::sim1(User *user, string a
 #ifdef USE_TLS
   else if(util.is_set(flag, COM_PBSZ))
   {
-    if(user->settings.test(TLS_ENABLED))
+    if(user->settings(TLS_ENABLED))
     {
       if(argument == "0")
       {
         message = "200 PBSZ set for stream transfer.";
-        user->settings.set(TLS_PBSZ);
+        user->settings(TLS_PBSZ, B_SET);
       }
       else
         message = "501 PBSZ has to be set to 0 for use with TLS.";
     }
     else
-      message = "503 AUTH must be issued and negotiated before PBSZ can be used.";
+      message =
+        "503 AUTH must be issued and negotiated before PBSZ can be used.";
   }
 
   else if(util.is_set(flag, COM_PROT))
   {
-    if(!user->settings.test(TLS_ENABLED) || !user->settings.test(TLS_PBSZ))
-      message = "503 AUTH and PBSZ has to be issued and negotiated before PROT.";
+    if(!user->settings(TLS_ENABLED) || !user->settings(TLS_PBSZ))
+      message =
+        "503 AUTH and PBSZ has to be issued and negotiated before PROT.";
     else
       switch(argument[0])
       {
         case 'C':
           message = "200 Data connection set to CLEAR level.";
-          user->settings.reset(TLS_PROT_D);
+          user->settings(TLS_PROT_D, B_RESET);
           break;
         case 'P':
           message = "200 Data connection set to PRIVATE level.";
-          user->settings.set(TLS_PROT_D);
+          user->settings(TLS_PROT_D, B_SET);
           break;
         case 'S':
         case 'E':
@@ -1119,13 +1113,13 @@ bool Commands::sim1(User *user, string a
   }
   else if(util.is_set(flag, COM_CCC)) // Clear Command Channel
   {
-    if(!user->settings.test(TLS_ENABLED))
-      message = "533 CCC can only be issued on a protected control connection.";
+    if(!user->settings(TLS_ENABLED))
+      message =
+        "533 CCC can only be issued on a protected control connection.";
     else // code for disallowing CCC should be above, containing a 534
     {
-      ftp_send(user, "200 Disabling TLS, control connection is now CLEAR.");
-      gnutls_bye(user->ctrl_session, GNUTLS_SHUT_RDWR);
-      user->settings.reset(TLS_ENABLED);
+      *user << "200 Disabling TLS, control connection is now CLEAR.\r\n";
+      user->disengage_tls();
     }
   }
   else if(util.is_set(flag, COM_CONF | COM_MIC | COM_ENC))
@@ -1141,7 +1135,7 @@ bool Commands::sim1(User *user, string a
     else
       command += "ENC";
 
-    if(!user->settings.test(TLS_ENABLED))
+    if(!user->settings(TLS_ENABLED))
       message = "503 Command " + command
         + " can not be issued before a valid security exchange.";
     else
@@ -1150,11 +1144,11 @@ bool Commands::sim1(User *user, string a
 #endif // USE_TLS
 
   if(message.size() > 0)
-    ftp_send(user, message);
+    *user << message << "\r\n";
   return(true);
 }
 
-bool Commands::sim2(User *user, string argument, unsigned int flag)
+BABY_COM(Commands::sim2)
 {
   string file_name, message;
   struct stat stt;
@@ -1166,7 +1160,7 @@ bool Commands::sim2(User *user, string a
       message = "213 " + util.itos(stt.st_size);
 
     else if(util.is_set(flag, COM_MDTM))
-      message = "213 " + mod_time(stt);
+      util.time_string(message, "213 %Y%m%d%H%M%S", 19, stt.st_mtime);
 
     else if(util.is_set(flag, COM_MLST))
     {
@@ -1178,61 +1172,41 @@ bool Commands::sim2(User *user, string a
   else
     message = "550 \"" + argument + "\": No such file or directory.";
 
-  ftp_send(user, message);
+  *user << message << "\r\n";
   return(true);
 }
 
-string Commands::mod_time(struct stat stt)
-{
-  char date[16] = {0};
-  struct tm m;
-
-  gmtime_r(&(stt.st_mtime), &m);
-  strftime(date, 15, "%Y%m%d%H%M%S", &m);
-  string message = date;
-  return(message);
-}
-
 #ifdef USE_TLS
 
-bool Commands::auth(User *user, string argument, unsigned int flag)
+BABY_COM(Commands::auth)
 {
-  transform(argument.begin(), argument.end(), argument.begin(), (int(*)(int))toupper);
+  transform(argument.begin(), argument.end(), argument.begin(),
+      (int(*)(int))toupper);
 
   if(argument == "TLS" || argument == "TLS-C" || argument == "SSL")
   {
-    if(config->get_user(user->user_name).encryption == DENY)
+    if(config->get_user(user->user_name()).encryption == DENY)
     {
-      ftp_send(user, "421 Sorry, you are not allowed to use encryption, disconnecting.");
-      sem_wait(&user->user_lock);
-      user->logged_in = LOGGED_OUT;
-      sem_post(&user->user_lock);
-    }
-    sem_wait(&user->user_lock);
-    Commands::init_tls(user->ctrl_session);
-    user->settings.set(TLS_INITED);
-
-    gnutls_transport_set_ptr(user->ctrl_session, reinterpret_cast<gnutls_transport_ptr>(user->socket_id));
-    sem_post(&user->user_lock);
-    ftp_send(user, "234 Now starting encryption using TLS.");
-    sem_wait(&user->user_lock);
-
-    if(gnutls_handshake(user->ctrl_session) < 0)
+      *user <<
+        "421 Sorry, you are not allowed to use encryption, disconnecting.\r\n";
+      user->settings(FAILURE, B_SET);
+      return false;
+    }
+    *user << "234 Now starting encryption using TLS.\r\n";
+    if(!user->engage_tls())
     { // failure =(
-      user->logged_in = LOGGED_OUT;
-      sem_post(&user->user_lock);
-      ftp_send(user, "421 Handshake failed. Disconnecting.");
+      user->settings(FAILURE, B_SET);
+      *user << "421 Handshake failed. Disconnecting.\r\n";
     }
     else
     { // wee!
-      user->settings.set(TLS_ENABLED);
       if(argument == "SSL")
-        user->settings.set(TLS_PROT_D);
-      sem_post(&user->user_lock);
+        user->settings(TLS_PROT_D, B_SET);
     }
   }
   else
-    ftp_send(user, "504 Sorry, I don't understand the mechanism '" + argument + "'.");
+    *user << "504 Sorry, I don't understand the mechanism '"
+      << argument << "'.\r\n";
 
   return true;
 }
Index: Configuration.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Configuration.cpp,v
retrieving revision 1.57
diff -u -p -b -r1.57 Configuration.cpp
--- Configuration.cpp	23 Jan 2005 00:23:45 -0000	1.57
+++ Configuration.cpp	10 Apr 2005 22:43:19 -0000
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////
-//  $Id: Configuration.cpp,v 1.57 2005/01/23 00:23:45 klas Exp $
+//  $Id: Configuration.cpp,v 1.96 2005/04/02 09:41:46 klas Exp $
 //
 //  Usage: Functions and stuff for Configure class, holding and
 //           reading config stuff.
@@ -8,20 +8,20 @@
 //    LICENSE for complete information.                                         
 /////////////////////////////////////////////////////////////////////
 
-#define __CONFIGURATION_CPP__
+#define CONFIGURATION_CPP
 
 #include "Configuration.h"
 
+class Configuration *config;
+
 Configuration::Configuration()
 {
   conf_file = "babyftpd.conf";
   parsed = false;
 
-  general.id = "general";
   general.connection_limit = INT_MAX; // ain't this a bit high?
   general.port = 21; // default port
-  general.default_type = 0;
-  general.verbose = 1;
+  general.data_port = 20; // default outgoing data port
   general.no_daemon = false; // daemon mode or not?
   general.os = "UNIX"; // what OS is this?
   general.admin_email = ""; // admin email?
@@ -33,13 +33,15 @@ Configuration::Configuration()
   general.welcome_msg += " Server [";
   general.welcome_msg += tmp;
   general.welcome_msg += "]";
+  general.user = 0; // by default when run all as root
+  general.group = 0;
 #ifdef USE_TLS
-  general.tls_key = "/root/tls_key.pem";
-  general.tls_cert = "/root/tls_cert.pem";
-  general.tls_ca = "/root/tls_ca.pem";
-  general.tls_crl = "/root/tls_crl.pem";
+  general.tls_key = "/etc/babyftpd/tls_key.pem";
+  general.tls_cert = "/etc/babyftpd/tls_cert.pem";
+  general.tls_ca = "/etc/babyftpd/tls_ca.pem";
+  general.tls_crl = "/etc/babyftpd/tls_crl.pem";
   general.dh_regenerate = 3600; // seconds between diffie-hellman regenerations
-  general.user = 0; // by default when run all as root
+  general.verify_client = "/etc/babyftpd/verify_client.pl";
 
   general_user.encryption = ALLOW;
 #endif
@@ -47,15 +49,17 @@ Configuration::Configuration()
   logger.xferlog = true; // by default all transfers are logged
   logger.xferlog_file = "/var/log/xferlog"; // here
   logger.thread_id = false; // we don't want thread ids all over the logs
+  logger.time_format = "%b %e %H:%M:%S "; // time format
 
-  // these options end up as default for all users, that is, those that don't occur in the conf file get these values
+  // these options end up as default for all users, that is,
+  //  those that don't occur in the conf file get these values
   general_user.id = "user_general";
   general_user.sim_data = INT_MAX;
   general_user.idle = 900; // idle timeout after how long?
   general_user.noftp = 0; // can this user login with ftp or not?
-  general_user.only_home = 0; // can the user traverse anything but his/her homedir
-  general_user.only_passive = 0; // is the user limited to passive data transfers?
-  general_user.user_limit = INT_MAX; // maximum control connections from this user
+  general_user.only_home = 0; // false chroot in homedir?
+  general_user.only_passive = 0;
+  general_user.user_limit = INT_MAX; // maximum control connections
 }
 
 Configuration::~Configuration()
@@ -65,16 +69,12 @@ Configuration::~Configuration()
 bool Configuration::parse_config_file()
 {
   vector<string> file_buff;
-  fill_buff(file_buff, conf_file);
-
-  string Sgeneral = get_variables(file_buff, "general", 1);
-  set_general(Sgeneral);
-
-  string Suser = get_variables(file_buff, "general", 2);
-  general_user = set_users(Suser);
+  if(!fill_buff(file_buff, conf_file))
+    return(false);
 
-  string Slog = get_variables(file_buff, "log", 1);
-  set_logger(Slog);
+  set_structs(get_variables(file_buff, "general", 1), GENERAL, &general);
+  set_structs(get_variables(file_buff, "general", 2), USER, &general_user);
+  set_structs(get_variables(file_buff, "log", 1), LOG, &logger);
 
   string user_names;
   num_users = count_users(file_buff, user_names) - 1; // first user is 0.
@@ -82,11 +82,16 @@ bool Configuration::parse_config_file()
   class StringTokenizer *names = new StringTokenizer(user_names, ",", false);
   names->nextToken(); // not nice, but nicer.
 
+  if(!users.empty()) // that is, on SIGHUP
+    users.clear();
+
   users.reserve(num_users + 1);
   for(int i = 0; i < num_users; i++)
   {
-    string Susers = get_variables(file_buff, names->nextToken().c_str(), 2);
-    users.push_back(set_users(Susers));
+    struct users_stru user_tmp = general_user;
+    set_structs(get_variables(file_buff, names->nextToken().c_str(), 2),
+        USER, &user_tmp);
+    users.push_back(user_tmp);
   }
 
   parsed = true;
@@ -94,67 +99,287 @@ bool Configuration::parse_config_file()
   return true;
 }
 
-struct users_stru Configuration::set_users(string variables)
+bool Configuration::set_structs(string variables, int section, void *this_struct)
 {
-  struct users_stru usr = general_user;
+  struct users_stru *usr_tmp = NULL;
+  struct general_stru *gen_tmp = NULL;
+  struct log_stru *log_tmp = NULL;
+  vector<config_entry> this_entry;
+  string for_log = "Configuration for ";
+
+  switch(section)
+  {
+    case USER:
+      {
+        usr_tmp = (static_cast<struct users_stru*>(this_struct));
+
+        this_entry.push_back(config_entry(
+              "id", CONF_STRING, &usr_tmp->id));
+        this_entry.push_back(config_entry(
+              "sim_data", CONF_INT, &usr_tmp->sim_data));
+        this_entry.push_back(config_entry(
+              "idle", CONF_TIME, &usr_tmp->idle));
+        this_entry.push_back(config_entry(
+              "noftp", CONF_BOOL, &usr_tmp->noftp));
+        this_entry.push_back(config_entry(
+              "only_home", CONF_BOOL, &usr_tmp->only_home));
+        this_entry.push_back(config_entry(
+              "only_passive", CONF_BOOL, &usr_tmp->only_passive));
+        this_entry.push_back(config_entry(
+              "user_limit", CONF_INT, &usr_tmp->user_limit));
+#ifdef USE_TLS
+        this_entry.push_back(config_entry(
+              "encryption", CONF_ENC, &usr_tmp->encryption));
+#endif // USE_TLS
+        for_log += "user section ";
+      }
+      break;
 
+    case LOG:
+
+      {
+        log_tmp = (static_cast<struct log_stru*>(this_struct));
+
+        this_entry.push_back(config_entry(
+              "log_level", CONF_INT, &log_tmp->log_level));
+        this_entry.push_back(config_entry(
+              "xferlog", CONF_BOOL, &log_tmp->xferlog));
+        this_entry.push_back(config_entry(
+              "xferlog_file", CONF_LOGF, &log_tmp->xferlog_file));
+        this_entry.push_back(config_entry(
+              "authlog", CONF_LOGF, &log_tmp->authlog));
+        this_entry.push_back(config_entry(
+              "infolog", CONF_LOGF, &log_tmp->infolog));
+        this_entry.push_back(config_entry(
+              "debuglog", CONF_LOGF, &log_tmp->debuglog));
+        this_entry.push_back(config_entry(
+              "thread_id", CONF_BOOL, &log_tmp->thread_id));
+        this_entry.push_back(config_entry(
+              "time_format", CONF_TIMEF, &log_tmp->time_format));
+        for_log += "log section ";
+      }
+      break;
+
+    case GENERAL:
+      {
+        gen_tmp = (static_cast<struct general_stru*>(this_struct));
+
+        this_entry.push_back(config_entry(
+              "connection_limit", CONF_INT, &gen_tmp->connection_limit));
+        this_entry.push_back(config_entry(
+              "port", CONF_INT, &gen_tmp->port));
+        this_entry.push_back(config_entry(
+              "data_port", CONF_INT, &gen_tmp->data_port));
+        this_entry.push_back(config_entry(
+              "no_daemon", CONF_BOOL, &gen_tmp->no_daemon));
+        this_entry.push_back(config_entry(
+              "admin_email", CONF_STRING, &gen_tmp->admin_email));
+        this_entry.push_back(config_entry(
+              "welcome_msg", CONF_STRING, &gen_tmp->welcome_msg));
+        this_entry.push_back(config_entry(
+              "lifebeat_tick", CONF_TIME, &gen_tmp->lifebeat_tick));
+        this_entry.push_back(config_entry(
+              "user", CONF_USER, NULL));
+#ifdef USE_TLS
+        this_entry.push_back(config_entry(
+              "tls_key", CONF_STRING, &gen_tmp->tls_key));
+        this_entry.push_back(config_entry(
+              "tls_cert", CONF_STRING, &gen_tmp->tls_cert));
+        this_entry.push_back(config_entry(
+              "tls_ca", CONF_STRING, &gen_tmp->tls_ca));
+        this_entry.push_back(config_entry(
+              "tls_crl", CONF_STRING, &gen_tmp->tls_crl));
+        this_entry.push_back(config_entry(
+              "dh_regenerate", CONF_TIME, &gen_tmp->dh_regenerate));
+        this_entry.push_back(config_entry(
+              "verify_client", CONF_STRING, &gen_tmp->verify_client));
+#endif // USE_TLS
+        for_log += "general section ";
+      }
+      break;
+  }
   class StringTokenizer *var = new StringTokenizer(variables, "\n", false);
-  usr.id = var->nextToken();
+  string id = var->nextToken();
+  if(section == USER)
+  {
+    *(string*)this_entry[0].variabel = id;
+    for_log += "for " + id + " ";
+  }
 
-  while(var->countTokens() != 0)
+  while(var->hasMoreTokens())
   {
-    string str = var->nextToken();
+    string log_me, str = var->nextToken();
     class StringTokenizer *st = new StringTokenizer(str, " ", false);
     string op = st->nextToken();
-    string arg = st->nextToken();
-    if(op == users_op[0])
-      usr.sim_data = atoi(arg.c_str());
-    else if(op == users_op[1])
-      usr.idle = atoi(arg.c_str());
-    else if(op == users_op[2])
-      usr.noftp = atoi(arg.c_str());
-    else if(op == users_op[3])
-      usr.only_home = atoi(arg.c_str());
-    else if(op == users_op[4])
-      usr.only_passive = atoi(arg.c_str());
-    else if(op == users_op[5])
-      usr.user_limit = atoi(arg.c_str());
+    string arg = st->restTokens();
+    vector<config_entry>::iterator this_it = this_entry.begin();
+
+    for(; this_it != this_entry.end(); this_it++)
+      if(op == (*this_it).name)
+        break;
+
+    if(this_it == this_entry.end())
+      log_me = "Parameter " + op + " doesn't exist, check your spelling.";
+    else
+    {
+      switch((*this_it).type)
+      {
+        case CONF_TIME:
 #ifdef USE_TLS
-    else if(op == users_op[6])
+        case CONF_ENC:
+          if((*this_it).type == CONF_ENC)
     {
       if(arg == "allow")
-        usr.encryption = ALLOW;
+              arg = util.itos(ALLOW);
       else if(arg == "deny")
-        usr.encryption = DENY;
+              arg = util.itos(DENY);
       else if(arg == "force")
-        usr.encryption = FORCE;
+              arg = util.itos(FORCE);
+            else if(arg == "force_cert")
+              arg = util.itos(FORCE_CERT);
+            else
+              log_me = "only allow, force, foce_cert and deny are legal "
+                "parameters to encryption, " + arg + " isn't one of them.";
     }
+          else
 #endif // USE_TLS
-    delete st;
+            if((*this_it).type == CONF_TIME)
+            {
+              int multi = 1;
+              string::iterator st_it = arg.begin();
+              for(; st_it != arg.end(); st_it++)
+              {
+                if(isdigit(*st_it) || isspace(*st_it))
+                  continue;
+                else
+                  break;
   }
-  delete var;
+              if(st_it != arg.end())
+              {
+                switch(*st_it)
+                {
+                  case 's':
+                    multi = 1;
+                    break;
+                  case 'm':
+                    multi = 60;
+                    break;
+                  case 'h':
+                    multi = 3600;
+                    break;
+                  case 'd':
+                    multi = 86400;
+                    break;
+                  default:
+                    log_me = "I don't understand your time unit \'";
+                    log_me += *st_it;
+                    log_me += "\' to paramater " + op + " I will ignore it.";
+                }
+                if(!log_me.empty())
+                  break;
 
-  string for_logging =
-     "user conf for: " + usr.id
-     + " sim_data=" + util.itos(usr.sim_data)
-     + " idle=" + util.itos(usr.idle)
-     + " noftp=" + util.itos(usr.noftp)
-     + " only_home=" + util.itos(usr.only_home)
-     + " only_passive=" + util.itos(usr.only_passive)
-     + " user_limit=" + util.itos(usr.user_limit)
-#ifdef USE_TLS
-     + " encryption=";
-  if(usr.encryption == ALLOW)
-    for_logging += "allow";
-  else if(usr.encryption == DENY)
-    for_logging += "deny";
-  else if(usr.encryption == FORCE)
-    for_logging += "force"
-#endif // USE_TLS
-     ;
-  logging->log_this(3, TYPE_INFO, for_logging);
+                arg.erase(st_it, arg.end());
+                int temp_num = atoi(arg.c_str()) * multi;
+                arg = util.itos(temp_num);
+              }
+            }
+        case CONF_INT:
+          {
+            int i_value = 0;
+            bool error = false;
+            for(string::iterator it = arg.begin(); it != arg.end(); it++)
+              if(!isdigit(*it))
+              {
+                error = true;
+                break;
+              }
+            if(!error)
+              i_value = atoi(arg.c_str());
+            else
+              if(log_me.empty())
+                log_me = "Non int value supplied to " + op +
+                  " value supplied was " + arg + ".";
+
+            if(log_me.empty())
+            {
+              *(static_cast<int*>((*this_it).variabel)) = i_value;
+              for_log += (*this_it).name + "=" + util.itos(i_value) + "; ";
+            }
+          }
+          break;
+
+        case CONF_BOOL:
+          {
+            bool b_value = true;
+            if(arg == "true" || arg == "t" || arg == "1" || arg == "y")
+              b_value = true;
+            else if(arg == "false" || arg == "f" || arg == "0" || arg == "n")
+              b_value = false;
+            else
+              log_me = arg + " not recognised as a boolean value supplied to "
+                + op + ".";
+
+            if(log_me.empty())
+            {
+              *(static_cast<bool*>((*this_it).variabel)) = b_value;
+              for_log += (*this_it).name + (b_value ? "=true; " : "=false; ");
+            }
+          }
+          break;
+
+        case CONF_USER:
+          {
+            struct passwd pwd, *pwd_ptr;
+            struct group grp, *grp_ptr;
+            char pwdata[PWBUF_SIZE], grpdata[PWBUF_SIZE];
+            int delim = arg.find_first_of(':');
+            getpwnam_r(arg.substr(0, delim).c_str(),
+                &pwd, pwdata, sizeof(pwdata), &pwd_ptr);
+            getgrnam_r(arg.substr(delim + 1).c_str(),
+                &grp, grpdata, sizeof(grpdata), &grp_ptr);
+            if(pwd_ptr == NULL)
+            {
+              log_me = "Tried to use nonexistent user " + arg.substr(0,
+                  delim) + " to run the daemon under. using root instead.";
+              pwd.pw_uid = 0;
+              grp.gr_gid = 0;
+            }
+            else if(grp_ptr == NULL)
+            {
+              log_me = "Tried to use nonexistent group " + arg.substr(delim +1)
+                + " to run the daemon under. using root instead.";
+              grp.gr_gid = 0;
+            }
+            general.user = pwd.pw_uid;
+            general.group = grp.gr_gid;
+            for_log += (*this_it).name + "=" +
+              util.itos(pwd.pw_uid) + ":" + util.itos(grp.gr_gid) + "; ";
+          }
+          break;
+
+        case CONF_LOGF:
+          if(arg == "syslog")
+            arg.clear();
+        case CONF_TIMEF:
+          if((*this_it).type == CONF_TIMEF)
+            arg += " ";
+        case CONF_STRING:
+          *(static_cast<string*>((*this_it).variabel)) = arg;
+          for_log += (*this_it).name + "=" + arg + "; ";
+          break;
 
-  return(usr);
+        default:
+          log_me = "Parameter " + op + " doesn't exist, check your spelling.";
+          break;
+      }
+    }
+    delete st;
+    if(!log_me.empty())
+      logging->log_this(2, TYPE_INFO, log_me);
+  }
+  logging->log_this(3, TYPE_INFO, for_log);
+  delete var;
+  return true;
 }
 
 struct users_stru &Configuration::get_user(string name)
@@ -171,7 +396,8 @@ int Configuration::count_users(vector<st
   int ant = 0;
   int deep = 1, curr_deep = 0;
   string name = "user", section = "";
-  for(vector<string>::iterator buff_it = buff.begin(); buff_it != buff.end(); buff_it++)
+  for(vector<string>::iterator buff_it = buff.begin(); buff_it != buff.end();
+      buff_it++)
   {
     if(*buff_it == "{")
     {
@@ -194,154 +420,14 @@ int Configuration::count_users(vector<st
   return ant;
 }
 
-bool Configuration::set_general(string variables)
-{
-  class StringTokenizer *var = new StringTokenizer(variables, "\n", false);
-  general.id = var->nextToken();
-
-  while(var->hasMoreTokens())
-  {
-    string str = var->nextToken();
-    class StringTokenizer *st = new StringTokenizer(str, " ", false);
-    string op = st->nextToken();
-    string arg = st->restTokens();
-    if(op == general_op[0])
-      general.connection_limit = atoi(arg.c_str());
-    else if(op == general_op[1])
-      general.port = atoi(arg.c_str());
-    else if(op == general_op[2])
-      general.default_type = atoi(arg.c_str());
-    else if(op == general_op[3])
-      general.no_daemon = atoi(arg.c_str());
-    else if(op == general_op[4])
-      general.verbose = atoi(arg.c_str());
-    else if(op == general_op[5])
-      general.admin_email = arg;
-    else if(op == general_op[6])
-      general.welcome_msg = arg;
-    else if(op == general_op[7])
-      general.lifebeat_tick = atoi(arg.c_str());
-    else if(op == general_op[8])
-    {
-      struct passwd *pwd = getpwnam(arg.c_str());
-      if(pwd == NULL)
-        general.user = 0;
-      else
-        general.user = pwd->pw_uid;
-    }
-#ifdef USE_TLS
-    else if(op == general_op[9])
-      general.tls_key = arg;
-    else if(op == general_op[10])
-      general.tls_cert = arg;
-    else if(op == general_op[11])
-      general.tls_ca = arg;
-    else if(op == general_op[12])
-      general.tls_crl = arg;
-    else if(op == general_op[13])
-      general.dh_regenerate = atoi(arg.c_str());
-#endif
-    delete st;
-  }
-  delete var;
-
-  string for_logging =
-    "general configuration: \
-    connection_limit=" + util.itos(general.connection_limit)
-    + " port=" + util.itos(general.port)
-    + " default_type=" + util.itos(general.default_type)
-    + " verbose=" + util.itos(general.verbose)
-    + " no_daemon=" + util.itos(general.no_daemon)
-    + " os=" + general.os
-    + " admin_email=" + general.admin_email
-    + " welcome_msg=" + general.welcome_msg
-    + " lifebeat_tick=" + util.itos(general.lifebeat_tick)
-    + " user=" + util.itos(general.user)
-#ifdef USE_TLS
-    + " tls_key=" + general.tls_key
-    + " tls_cert=" + general.tls_cert
-    + " tls_ca=" + general.tls_ca
-    + " tls_crl=" + general.tls_crl
-    + " dh_regenerate=" + util.itos(general.dh_regenerate)
-#endif // USE_TLS
-    ;
-  logging->log_this(3, TYPE_INFO, for_logging);
-
-  return(true);
-}
-
-bool Configuration::set_logger(string variables)
-{
-  class StringTokenizer *var = new StringTokenizer(variables, "\n", false);
-
-  while(var->hasMoreTokens())
-  {
-    string str = var->nextToken();
-    class StringTokenizer *st = new StringTokenizer(str, " ", false);
-    string op = st->nextToken();
-    string arg = st->restTokens();
-    if(op == log_op[0])
-      logger.log_level = atoi(arg.c_str());
-    else if(op == log_op[1])
-    {
-      if(!(atoi(arg.c_str())))
-        logger.xferlog = false;
-    }
-
-    else if(op == log_op[2])
-      logger.xferlog_file = arg;
-
-    else if(op == log_op[3])
-    {
-      if(arg != "syslog")
-        logger.authlog = arg;
-    }
-
-    else if(op == log_op[4])
-    {
-      if(arg != "syslog")
-        logger.infolog = arg;
-    }
-
-    else if(op == log_op[5])
-    {
-      if(arg != "syslog")
-        logger.debuglog = arg;
-    }
-
-    else if(op == log_op[6])
-    {
-      if((atoi(arg.c_str())))
-        logger.thread_id = true;
-    }
-
-    delete st;
-  }
-  delete var;
-
-  string for_logging =
-    "logging configuration: \
-    log_level=" + util.itos(logger.log_level)
-    + " xferlog_enable=" + util.itos(logger.xferlog)
-    + " xferlog_file=" + logger.xferlog_file + " "
-    + log_op[3] + (logger.authlog.empty() ?
-        "=syslog " : "=" + logger.authlog + " ")
-    + log_op[4] + (logger.infolog.empty() ?
-        "=syslog " : "=" + logger.infolog + " ")
-    + log_op[5] + (logger.debuglog.empty() ?
-        "=syslog " : "=" + logger.debuglog)
-    + " thread_id=" + util.itos(logger.thread_id)
-    ;
-  logging->log_this(3, TYPE_INFO, for_logging);
 
-  return(true);
-}
-
-string Configuration::get_variables(vector<string> &buff, const char *key, int deep)
+string Configuration::get_variables(vector<string> &buff, const char *key,
+    int deep)
 {
   int curr_deep = 0;
   string section, str;
-  for(vector<string>::iterator buff_it = buff.begin(); buff_it != buff.end(); buff_it++)
+  for(vector<string>::iterator buff_it = buff.begin(); buff_it != buff.end();
+      buff_it++)
   {
     if((*buff_it).empty())
       continue;
@@ -361,13 +447,13 @@ string Configuration::get_variables(vect
         break;
       curr_deep--;
     }
-    else if(curr_deep == deep && section == key && *(buff_it + 1) != "{")
+    else if(curr_deep == deep && section == key)
       str += *buff_it + "\n";
   }
   return(str);
 }
 
-void Configuration::fill_buff(vector<string> &buff, string filename)
+bool Configuration::fill_buff(vector<string> &buff, string filename)
 {
   ifstream fin;
   fin.open(filename.c_str(), ifstream::in);
@@ -383,4 +469,77 @@ void Configuration::fill_buff(vector<str
   }
 
   fin.close();
+  if(buff.empty())
+    return(false);
+  else
+    return(true);
 }
+
+
+bool Configuration::parse_command_line(int argc, char **argv)
+{
+  int opt = 0;
+  const char *cmdopts = "hc:nVl:p:";
+
+  // struct for the long options... neat, huh?
+  struct option longopts[] =
+  {
+    { "help",      0, 0, 'h' },
+    { "conf-file", 1, 0, 'c' },
+    { "no-daemon", 0, 0, 'd' },
+    { "version",   0, 0, 'V' },
+    { "log-level", 1, 0, 'l' },
+    { "port",      1, 0, 'p' },
+    { 0,           0, 0, 0   }
+  };
+
+  while((opt = getopt_long(argc, argv, cmdopts, longopts, NULL)) != -1)
+  {
+    switch(opt)
+    {
+      case 'h':
+        cout << "Usage: " << argv[0] << " [OPTION]...\n\n"
+          << "  -h, --help            display this help and exit\n"
+          << "  -c, --conf-file=FILE  supply another conf file instead "
+          << "of the default\n"
+          << "  -n, --no-daemon       do not fork into the background\n"
+          << "  -V, --version         display version information and exit\n"
+          << "  -l, --log-level=LEVEL set the log level, 1-10, default is 5\n"
+          << "  -p, --port=PORT       specify an alternate port\n\n"
+          << "Please, report bugs, either directly to the developers\n"
+          << "or via sourceforge (http://www.sf.net/projects/babyftpd).\n";
+
+          exit_baby(0, EXIT_EARLY);
+
+      case 'c':
+        config->conf_file = optarg;
+        break;
+
+      case 'n':
+        config->general.no_daemon = true;
+        break;
+
+      case 'V':
+        cout << "BabyFTPd version " << VERSION << "\nBuilt on " << __DATE__
+          << " " << " " << __TIME__ << endl;
+        exit_baby(0, EXIT_EARLY);
+
+      case 'l':
+        // Log level code...
+        if(atoi(optarg) > 10)
+          logger.log_level = 10;
+        else if(atoi(optarg) < 0)
+          logger.log_level = 0;
+        else
+          logger.log_level = atoi(optarg);
+        break;
+
+      case 'p':
+        // alternate port...
+        config->general.port = atoi(optarg);
+        break;
+    }
+  }
+  return true;
+}
+
Index: Configuration.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Configuration.h,v
retrieving revision 1.33
diff -u -p -b -r1.33 Configuration.h
--- Configuration.h	22 Jan 2005 23:06:05 -0000	1.33
+++ Configuration.h	10 Apr 2005 22:43:19 -0000
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////
-//  $Id: Configuration.h,v 1.33 2005/01/22 23:06:05 klas Exp $
+//  $Id: Configuration.h,v 1.50 2005/03/28 17:06:44 klas Exp $
 //
 //  Usage: Configure class, reading the config file and holding
 //           configurish variables.
@@ -9,14 +9,16 @@
 /////////////////////////////////////////////////////////////////////
 
 
-#ifndef __CONFIGURATION_H__
-#define __CONFIGURATION_H__
+#ifndef CONFIGURATION_H
+#define CONFIGURATION_H
 
-#ifdef __CONFIGURATION_CPP__
+#ifdef CONFIGURATION_CPP
 #include "generic.h"
 #include "StringTokenizer.h"
 #include "Utilities.h"
 #include "Log.h"
+extern void exit_baby(int, int);
+const char *VERSION = "CVS: "__DATE__; // remember to change on release.
 #endif
 
 struct log_stru
@@ -28,6 +30,7 @@ struct log_stru
   string infolog;
   string debuglog;
   bool thread_id;
+  string time_format;
 };
 
 struct users_stru
@@ -35,7 +38,7 @@ struct users_stru
   string id;
   int sim_data;
   int idle;
-  int noftp;
+  bool noftp;
   int only_home;
   int only_passive;
   int user_limit;
@@ -46,23 +49,23 @@ struct users_stru
 
 struct general_stru
 {
-  string id;
   int connection_limit;
   int port;
-  int default_type;
-  int verbose;
+  int data_port;
   bool no_daemon;
   string os;
   string admin_email;
   string welcome_msg;
   int lifebeat_tick;
   uid_t user;
+  gid_t group;
 #ifdef USE_TLS  
   string tls_key;
   string tls_cert;
   string tls_ca;
   string tls_crl;
   int dh_regenerate;
+  string verify_client;
 #endif
 };
 
@@ -73,13 +76,12 @@ class Configuration
     int num_users;
     bool parsed;
     int count_users(vector<string>, string&);
-    void fill_buff(vector<string>&, string);
+    bool fill_buff(vector<string>&, string);
     string get_variables(vector<string>&, const char*, int);
-    bool set_general(string variables);
-    struct users_stru set_users(string);
-    bool set_logger(string variables);
+    bool set_structs(string, int, void*);
   public:
     bool parse_config_file();
+    bool parse_command_line(int argc, char **argv);
     string conf_file;
     struct log_stru logger;
     struct general_stru general;
@@ -89,63 +91,41 @@ class Configuration
     ~Configuration(void);
 };
 
-#ifdef USE_TLS
-const string general_op[14] =
-#else
-const string general_op[9] =
-#endif // USE_TLS
-{
-  "connection_limit",
-  "port",
-  "default:type",
-  "no_daemon",
-  "verbose",
-  "admin_email",
-  "welcome_msg",
-  "lifebeat_tick",
-  "user"
-#ifdef USE_TLS
-  ,"tls_key",
-  "tls_cert",
-  "tls_ca",
-  "tls_crl",
-  "dh_regenerate"
-#endif
-};
+extern class Configuration *config;
 
-const string log_op[7] =
+struct config_entry
 {
-  "log_level",
-  "xferlog",
-  "xferlog_file",
-  "authlog",
-  "infolog",
-  "debuglog",
-  "thread_id"
-};
-
-#ifdef USE_TLS
-const string users_op[7] =
-#else
-const string users_op[6] =
-#endif // USE_TLS
-{
-  "sim_data",
-  "idle",
-  "noftp",
-  "only_home",
-  "only_passive",
-  "user_limit"
-#ifdef USE_TLS
-  ,"encryption"
+  string name;
+  int type;
+  void *variabel;
+  config_entry(const string t_name, const int t_type, void *t_vari)
+  {
+    name = t_name;
+    type = t_type;
+    variabel = t_vari;
+  }
+};
+
+typedef struct config_entry config_entry;
+
+const int CONF_INT = 1;
+const int CONF_BOOL = 2;
+const int CONF_STRING = 3;
+const int CONF_LOGF = 4;
+const int CONF_ENC = 5;
+const int CONF_USER = 6;
+const int CONF_TIMEF = 7;
+const int CONF_TIME = 8;
+
+const int USER = 1;
+const int LOG = 2;
+const int GENERAL = 3;
+
+#ifdef USE_TLS
+const int ALLOW = 1;
+const int FORCE = 2;
+const int DENY = 3;
+const int FORCE_CERT = 4;
 #endif // USE_TLS
-};
-
-extern class Utilities util;
 
-#ifdef USE_TLS
-#define ALLOW 1
-#define FORCE 2
-#define DENY 3
-#endif // USE_TLS
 #endif
Index: Connection.cpp
===================================================================
RCS file: Connection.cpp
diff -N Connection.cpp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Connection.cpp	10 Apr 2005 22:43:19 -0000
@@ -0,0 +1,185 @@
+//////////////////////////////////////////////////////////////////////////
+// $Id: Connection.cpp,v 1.13 2005/04/10 22:03:08 klas Exp $
+//
+// Usage: Data class. inherited from Connection, everything pertaining to
+//        data connection goes here.
+//
+//  A part of babyftpd, licensed under the GNU GPL, see the file
+//    LICENSE for complete information.
+//////////////////////////////////////////////////////////////////////////
+
+#define CONNECTION_CPP
+#define NETINCLS
+
+#include "Connection.h"
+
+Connection::Connection()
+{
+  sem_init(&this->lock, 0, 1);
+  this->_settings.reset();
+  this->_packet_size = READ_SIZE;
+}
+
+Connection::~Connection()
+{
+  if(this->_socket_id)
+    close(this->_socket_id);
+
+  sem_destroy(&this->lock);
+
+#ifdef USE_TLS
+  // TLS cleanup here.
+  if(this->settings(TLS_INITED))
+  {
+    gnutls_db_remove_session(this->session);
+    gnutls_deinit(this->session);
+  }
+#endif
+  if(this->thread_id())
+  {
+    sem_wait(&cleanup_list_lock);
+    cleanup_list.push_back(this->thread_id());
+    sem_post(&cleanup_list_lock);
+  }
+}
+
+ssize_t Connection::send(const char *message, size_t len)
+{
+  assert(len <= this->_packet_size && len > 0);
+  ssize_t ret = -1;
+
+  sem_wait(&this->lock);
+#ifdef USE_TLS
+  if(this->settings(TLS_ENABLED))
+  {
+    ret = gnutls_record_send(this->session, message, len);
+    if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
+      ret = gnutls_record_send(this->session, message, len);
+  }
+  else
+#endif // USE_TLS
+    ret = write(this->_socket_id, message, len);
+
+  sem_post(&this->lock);
+  if(ret < 0)
+    logging->log_this(3, TYPE_INFO, "write to socket " +
+        util.itos(this->socket_id()) + " failed: %m");
+
+  return ret;
+}
+
+ssize_t Connection::receive(char *message, size_t len)
+{
+  assert(len <= this->_packet_size && len > 0);
+  ssize_t ret = -1;
+
+#ifdef USE_TLS
+  if(this->settings(TLS_ENABLED))
+    ret = gnutls_record_recv(this->session, message, len);
+  else
+#endif
+    ret = read(this->socket_id(), message, len);
+
+  return ret;
+}
+
+Connection &Connection::operator<<(long long int num)
+{
+  return (*this << util.itos(num));
+}
+
+Connection &Connection::operator<<(string message)
+{
+  if(this->send(message.c_str(), message.size()) < 0)
+  this->settings(FAILURE, B_SET);
+  return *this;
+}
+
+#ifdef USE_TLS
+bool Connection::engage_tls(void)
+{
+  bool ret = false;
+  gnutls_init(&session, GNUTLS_SERVER);
+
+  gnutls_set_default_priority(this->session);
+
+  gnutls_credentials_set(this->session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+  gnutls_certificate_server_set_request(this->session, GNUTLS_CERT_REQUEST);
+
+  gnutls_dh_set_prime_bits(this->session, 1024);
+
+  // these are for the connection cache
+  gnutls_db_set_retrieve_function(this->session, tls_db_fetch);
+  gnutls_db_set_remove_function(this->session, tls_db_delete);
+  gnutls_db_set_store_function(this->session, tls_db_store);
+  gnutls_db_set_ptr(this->session, NULL);
+
+  // associating the session with the socket.
+  gnutls_transport_set_ptr(this->session,
+      reinterpret_cast<gnutls_transport_ptr>(this->socket_id()));
+  this->settings(TLS_INITED, B_SET);
+
+  if(gnutls_handshake(this->session) >= 0)
+  {
+    this->settings(TLS_ENABLED, B_SET);
+    this->packet_size(gnutls_record_get_max_size(this->session));
+    ret = true;
+  }
+
+  return ret;
+}
+
+bool Connection::disengage_tls(void)
+{
+  gnutls_bye(this->session, GNUTLS_SHUT_RDWR);
+  this->settings(TLS_ENABLED, B_RESET);
+  return true;
+}
+
+bool Connection::verify_cert(void)
+{
+  bool ret = false;
+  if (gnutls_certificate_type_get(this->session) == GNUTLS_CRT_X509)
+  {
+    int verified = gnutls_certificate_verify_peers(this->session);
+    if(util.is_set(verified, GNUTLS_CERT_REVOKED))
+      logging->log_this(2, TYPE_AUTH, this->user_name() +
+          + " tried to login with a revoked certificate.");
+    else if(!util.is_set(verified, GNUTLS_CERT_INVALID) &&
+        !util.is_set(verified, GNUTLS_CERT_SIGNER_NOT_FOUND))
+    {
+      size_t cert_list_size = 0;
+      // I really hope gnutls has their locking code in shape. get_peers just
+      // gives us some memory.. *shudder*                     
+      const gnutls_datum *cert_list = gnutls_certificate_get_peers(
+          this->session, &cert_list_size);
+      if(cert_list_size > 0)
+      {
+        gnutls_x509_crt peer_cert;
+        gnutls_x509_crt_init(&peer_cert);
+        gnutls_x509_crt_import(peer_cert, &cert_list[0], GNUTLS_X509_FMT_DER);
+        char dn_buf[200] = {0}, ca_buf[200] = {0};
+        size_t buf_size = 199, ca_size = 199;
+        gnutls_x509_crt_get_dn(peer_cert, dn_buf, &buf_size);
+        gnutls_x509_crt_get_issuer_dn(peer_cert, ca_buf, &ca_size);
+        if(buf_size > 0 && ca_size > 0)
+        {
+          // here we call an external program to ask if this user may login
+          // with this cert.
+          //  format is "<user> <host/ip> <ca-dn> <cert-dn>"
+          string call = config->general.verify_client + " " +
+            this->user_name() + " " + this->host_name() +
+            " \"" + ca_buf + "\" \"" + dn_buf + "\"";
+          logging->log_this(5, TYPE_DEBUG, "running:" + call);
+          int approved = system(call.c_str());
+          if(WEXITSTATUS(approved) == 0)
+            ret = true;
+        }
+        gnutls_x509_crt_deinit(peer_cert);
+      }
+    }
+  }
+  return ret;
+}
+#endif // USE_TLS
Index: Connection.h
===================================================================
RCS file: Connection.h
diff -N Connection.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Connection.h	10 Apr 2005 22:43:19 -0000
@@ -0,0 +1,130 @@
+//////////////////////////////////////////////////////////////////////////
+// $Id: Connection.h,v 1.10 2005/04/10 21:00:45 klas Exp $
+//
+// Usage: Connection class. is inherited to user and data.
+//
+//  A part of babyftpd, licensed under the GNU GPL, see the file
+//    LICENSE for complete information.
+//////////////////////////////////////////////////////////////////////////
+
+#ifndef CONNECTION_H
+#define CONNECTION_H
+
+#ifdef CONNECTION_CPP
+#include "generic.h"
+#include "Utilities.h"
+#include "Configuration.h"
+#include "Log.h"
+#endif // CONNECTION_CPP
+
+const int B_SET = 1;
+const int B_RESET = 2;
+const int B_FLIP = 3;
+
+const int FACT_TYPE = 0; // for mlsx
+const int FACT_SIZE = 1;
+const int FACT_MODIFY = 2;
+const int FACT_UNIQUE = 3;
+const int FACT_U_MODE = 4;
+const int FACT_U_UID = 5;
+const int FACT_U_GID = 6;
+const int FACT_U_LINK = 7;
+const int FACT_PERM = 8;
+const int LAST_FACT = 8;
+
+const int DATA_ACCT = 9;
+const int BINARY = 10;
+const int COMPRESSED = 11;
+const int PASSIVE = 12;
+const int ANONYMOUS = 13;
+const int DATA_OPEN = 14;
+const int EPSV_ONLY = 15;
+const int RETR_TRANS = 16;
+const int PAM_DONE = 17;
+const int FAILURE = 18;
+#ifdef USE_TLS
+const int TLS_ENABLED = 19;
+const int TLS_INITED = 20;
+const int TLS_PBSZ = 21;
+const int TLS_PROT_D = 22;
+
+// keep an eye on these.. different depending on TLS or not...
+const int TOTAL_U_BIT = 23;
+#else
+const int TOTAL_U_BIT = 19;
+#endif // USE_TLS
+
+// macro that builds get and set functions for us.
+#define MEMBER(func, type, var)  private: type var; \
+  public: type func(void)const{return var;} \
+  public: void func(type var){ \
+          sem_wait(&this->lock); \
+          this->var = var; \
+          sem_post(&this->lock); \
+        }
+
+class Connection
+{
+  public:
+    sem_t lock;
+    ssize_t send(const char*, size_t);
+    ssize_t receive(char*, size_t);
+    Connection &operator<<(long long int num);
+    Connection &operator<<(string message);
+#ifdef USE_TLS
+    bool engage_tls();
+    bool disengage_tls();
+    bool verify_cert();
+#endif // USE_TLS
+    bitset<TOTAL_U_BIT> settings() const
+    {
+      return _settings;
+    }
+    bool settings(int bit) const
+    {
+      assert(bit < TOTAL_U_BIT);
+      return(this->_settings.test(bit));
+    }
+    void settings(int bit, int way)
+    {
+      assert(way < 4 && way > 0);
+      sem_wait(&this->lock);
+      if(way == B_SET)
+        this->_settings.set(bit);
+      else if(way == B_RESET)
+        this->_settings.reset(bit);
+      else if(way == B_FLIP)
+        this->_settings.flip(bit);
+      sem_post(&this->lock);
+    }
+  protected:
+    Connection();
+    ~Connection();
+    bitset<TOTAL_U_BIT> _settings;
+    MEMBER(thread_id, pthread_t, _thread_id);
+    MEMBER(host_name, string, _host_name);
+    MEMBER(socket_id, int, _socket_id);
+    MEMBER(addr, struct sockaddr_in, _addr);
+    MEMBER(packet_size, size_t, _packet_size);
+    MEMBER(timeout, struct timeval, _timeout);
+    MEMBER(user_name, string, _user_name);
+  private:
+#ifdef USE_TLS
+    gnutls_session session;
+#endif // USE_TLS
+};
+
+#undef MEMBER
+
+const int READ_SIZE = 16384;
+
+extern vector<pthread_t> cleanup_list;
+extern sem_t cleanup_list_lock;
+#ifdef USE_TLS
+extern gnutls_certificate_credentials x509_cred;
+extern int tls_db_store(void*, gnutls_datum, gnutls_datum);
+extern gnutls_datum tls_db_fetch(void*, gnutls_datum);
+extern int tls_db_delete(void*, gnutls_datum);
+#endif // USE_TLS
+
+#endif // CONNECTION_H
Index: Data.cpp
===================================================================
RCS file: Data.cpp
diff -N Data.cpp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Data.cpp	10 Apr 2005 22:43:19 -0000
@@ -0,0 +1,158 @@
+//////////////////////////////////////////////////////////////////////////
+// $Id: Data.cpp,v 1.6 2005/04/02 12:15:21 klas Exp $
+//
+// Usage: Data class. inherited from Connection, everything pertaining to
+//        data connection goes here.
+//
+//  A part of babyftpd, licensed under the GNU GPL, see the file
+//    LICENSE for complete information.
+//////////////////////////////////////////////////////////////////////////
+
+#define DATA_CPP
+#define NETINCLS
+
+#include "Data.h"
+
+Data::Data(User *user, int file_id, string file_name, int trans_type)
+  : Connection()
+{
+  this->thread_id(0); // data_wrapper sets this to it's own.
+  this->_file_id = file_id;
+  this->_file_name = file_name;
+  this->user = user;
+  this->_settings = user->settings();
+  this->addr(user->data_addr());
+  struct timeval tv = {1, 0};
+  this->timeout(tv);
+  if(trans_type == 1)
+    this->settings(RETR_TRANS, B_SET);
+
+  this->settings(DATA_OPEN, B_SET);
+#ifdef USE_TLS
+  this->settings(TLS_INITED, B_RESET);
+  this->settings(TLS_ENABLED, B_RESET);
+#endif // USE_TLS
+
+  if(this->settings(PASSIVE))
+    this->open_passive();
+  else // ACTIVE
+    this->open_active();
+
+  // if the data connection comes from another host than the
+  // ctrl connection we drop it.. unless the user is tls protected.
+  if(this->addr().sin_addr.s_addr != user->addr().sin_addr.s_addr)
+  {
+#ifdef USE_TLS
+    if(!user->settings(TLS_ENABLED))
+    {
+#endif // USE_TLS
+      close(this->socket_id());
+      this->settings(DATA_OPEN, B_RESET);
+    }
+#ifdef USE_TLS
+  }
+#endif //USE_TLS
+}
+
+void Data::open_passive()
+{
+  socklen_t len = sizeof(this->addr());
+  struct sockaddr_in temp_addr = this->addr();
+
+  fd_set acc_set;
+  FD_ZERO(&acc_set);
+  FD_SET(user->data_id(), &acc_set);
+  struct timeval tv = this->timeout();
+
+  if(select(user->data_id() + 1, &acc_set, NULL, NULL, &tv) > 0)
+  {
+    int ret = accept(user->data_id(),
+        reinterpret_cast<struct sockaddr*>(&temp_addr), &len);
+    this->socket_id(ret);
+    this->addr(temp_addr);
+  }
+  else
+    this->settings(DATA_OPEN, B_RESET);
+
+  close(user->data_id());
+}
+
+void Data::open_active()
+{
+  this->socket_id(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
+
+  int on = 1;
+  setsockopt(this->socket_id(), SOL_SOCKET, SO_REUSEADDR,
+      (static_cast<void*>(&on)), sizeof(on));
+  struct sockaddr_in my_addr;
+  my_addr.sin_port = htons(config->general.data_port);
+  my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+  uid_t old_uid = 0;
+  // if dataport is below 1024 we need root.
+  if(config->general.data_port <= 1024)
+  {
+    old_uid = geteuid();
+    seteuid(0);
+  }
+  int ret = bind(this->socket_id(), reinterpret_cast<struct sockaddr*>
+      (&my_addr), sizeof(my_addr));
+  if(config->general.data_port <= 1024)
+    seteuid(old_uid);
+
+  if(ret != 0)
+    logging->log_this(3, LOG_INFO,
+        "Failed to bind to data port " + util.itos(config->general.data_port)
+        + ", falling back to arbitrary port.");
+
+  struct sockaddr_in tmp_addr = this->addr();
+
+  // go into nonblocking
+  long sock_arg = fcntl(this->socket_id(), F_GETFL, NULL);
+  sock_arg |= O_NONBLOCK;
+  fcntl(this->socket_id(), F_SETFL, sock_arg);
+
+  int sock_err = connect(this->socket_id(), reinterpret_cast<struct sockaddr*>
+        (&tmp_addr), sizeof(tmp_addr));
+  this->addr(tmp_addr);
+
+  if(sock_err < 0)
+    if(errno == EINPROGRESS)
+    {
+      struct timeval tv = this->timeout();
+      fd_set con_set;
+      FD_ZERO(&con_set);
+      FD_SET(this->socket_id(), &con_set);
+      if(select(this->socket_id() + 1, NULL, &con_set, NULL, &tv) < 0)
+        this->settings(DATA_OPEN, B_RESET);
+    }
+    else
+      this->settings(DATA_OPEN, B_RESET);
+
+  // leave non blocking
+  long sock_arg2 = fcntl(this->socket_id(), F_GETFL, NULL);
+  sock_arg2 &= (~O_NONBLOCK);
+  fcntl(this->socket_id(), F_SETFL, sock_arg2);
+}
+
+Data::~Data()
+{
+  if(this->_file_id)
+    close(this->_file_id);
+
+  if(this->thread_id())
+  {
+    sem_wait(&user->lock);
+    user->data_vec.erase(user->get_data_it(this->thread_id()));
+    sem_post(&user->lock);
+    logging->log_this(4, TYPE_INFO, "data thread " +
+        util.itos(this->thread_id()) +
+        " exiting. Remaining data connections for " + user->user_name() +
+        " is: " + util.itos(user->data_vec.size()));
+  }
+}
+
+void kill_data(void *data)
+{
+  delete static_cast<Data*>(data);
+}
Index: Data.h
===================================================================
RCS file: Data.h
diff -N Data.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Data.h	10 Apr 2005 22:43:19 -0000
@@ -0,0 +1,55 @@
+//////////////////////////////////////////////////////////////////////////
+// $Id: Data.h,v 1.6 2005/03/29 20:13:57 klas Exp $
+//
+// Usage: Data class. inherited from Connection, everything pertaining to
+//        data connection goes here.
+//
+//  A part of babyftpd, licensed under the GNU GPL, see the file
+//    LICENSE for complete information.
+//////////////////////////////////////////////////////////////////////////
+
+#ifndef DATA_H
+#define DATA_H
+
+#ifdef DATA_CPP
+#include "generic.h"
+#include "Utilities.h"
+#include "Configuration.h"
+#include "Log.h"
+#include "Connection.h"
+#include "User.h"
+#endif // DATA_CPP
+
+// macro that builds get and set functions for us.
+#define MEMBER(func, type, var)  private: type var; \
+  public: type func(void)const{return var;} \
+  public: void func(type var){ \
+          sem_wait(&this->lock); \
+          this->var = var; \
+          sem_post(&this->lock); \
+        }
+
+class Data : public Connection
+{
+  friend class User;
+  friend void *data_wrapper(void*);
+  private:
+    User *user;
+    void open_active();
+    void open_passive();
+  public:
+    Data(User *user, int file_id = -1, string file_name = "",
+        int trans_type = 0);
+    ~Data();
+    MEMBER(trans_type, int, _trans_type);
+    MEMBER(file_id, int, _file_id);
+    MEMBER(file_name, string, _file_name);
+};
+
+#undef MEMBER
+
+void kill_data(void*);
+
+extern pthread_key_t my_data;
+
+#endif // DATA_H
Index: Handler.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Handler.cpp,v
retrieving revision 1.143
diff -u -p -b -r1.143 Handler.cpp
--- Handler.cpp	22 Jan 2005 17:18:51 -0000	1.143
+++ Handler.cpp	10 Apr 2005 22:43:19 -0000
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////
-// $Id: Handler.cpp,v 1.143 2005/01/22 17:18:51 klas Exp $
+// $Id: Handler.cpp,v 1.157 2005/04/10 21:57:36 jockelundin Exp $
 //
 // Usage: the main loop for connected clients and interpreter for
 //        ftp commands.
@@ -8,67 +8,82 @@
 //    LICENSE for complete information.
 /////////////////////////////////////////////////////////////////////
 
-#define __HANDLER_CPP__
-#define __NETINCLS__
+#define HANDLER_CPP
+#define NETINCLS
 #include "Handler.h"
 #include "commands.hlp"
 
-struct command_info Handler::cmd_info[] = {
-  { "SYST",  0, &Commands::sim1, COM_SYST,  HLP_SYST }, //0
-  { "HELP", -1, &Commands::help, BEFORE_USER, HLP_HELP },
-  { "RETR", -1, &Commands::retr, 0,         HLP_RETR },
-  { "STOR", -1, &Commands::stor, COM_STOR,  HLP_STOR },
-  { "PORT",  1, &Commands::port, COM_PORT,  HLP_PORT },
-  { "EPRT",  1, &Commands::port, COM_EPRT,  HLP_EPRT }, //5
-  { "PASV",  0, &Commands::pasv, COM_PASV,  HLP_PASV },
-  { "EPSV", -1, &Commands::pasv, COM_EPSV,  HLP_EPSV },
-  { "PWD",   0, &Commands::sim1, COM_PWD,   HLP_PWD  },
-  { "CWD",  -1, &Commands::cwd,  COM_CWD,   HLP_CWD  },
-  { "CDUP",  0, &Commands::cwd,  COM_CDUP,  HLP_CDUP }, //10
-  { "MKD",  -1, &Commands::mkd,  0,         HLP_MKD  },
-  { "RMD",  -1, &Commands::rmd,  0,         HLP_RMD  },
-  { "DELE", -1, &Commands::dele, 0,         HLP_DELE },
-  { "LIST", -1, &Commands::list, COM_LONG,  HLP_LIST },
-  { "NLST", -1, &Commands::list, COM_SHORT, HLP_NLST }, //15
-  { "TYPE", -1, &Commands::sim1, COM_TYPE,  HLP_TYPE },
-  { "MODE",  1, &Commands::sim1, COM_MODE,  HLP_MODE },
-  { "STRU",  1, &Commands::sim1, COM_STRU,  HLP_STRU },
-  { "SIZE", -1, &Commands::sim2, COM_SIZE,  HLP_SIZE },
-  { "USER",  1, &Commands::user, BEFORE_USER, HLP_USER }, //20
-  { "PASS",  1, &Commands::pass, BEFORE_USER, HLP_PASS },
-  { "QUIT", -1, &Commands::quit, BEFORE_USER, HLP_QUIT },
-  { "NOOP",  0, &Commands::sim1, COM_NOOP,  HLP_NOOP },
-  { "MDTM", -1, &Commands::sim2, COM_MDTM,  HLP_MDTM },
-  { "FEAT",  0, &Commands::sim1, COM_FEAT | BEFORE_USER, HLP_FEAT }, //25
-  { "RNFR", -1, &Commands::rname, COM_FROM, HLP_RNFR },
-  { "RNTO", -1, &Commands::rname, COM_TO,   HLP_RNTO },
-  { "MLST", -1, &Commands::sim2, COM_MLST,  HLP_MLST },
-  { "MLSD", -1, &Commands::list, COM_MLSD,  HLP_MLSD },
-  { "APPE", -1, &Commands::stor, COM_APPE,  HLP_APPE }, //30
-  { "OPTS", -1, &Commands::opts, 0,         HLP_OPTS },
-  { "REST",  1, &Commands::rest, 0,         HLP_REST },
-  { "XCWD", -1, &Commands::cwd,  COM_CWD,   HLP_CWD  }, // as of rfc 1123
-  { "XPWD",  0, &Commands::sim1, COM_PWD,   HLP_PWD  },
-  { "XCUP",  0, &Commands::sim1, COM_CDUP,  HLP_CDUP }, //35
-  { "XMKD", -1, &Commands::mkd,  0,         HLP_MKD  },
-  { "XRMD", -1, &Commands::rmd,  0,         HLP_RMD  },
+class Handler *handler;
+
+Handler::Handler()
+{
+  // first argument is number of arguments to the command, they should
+  //  be self explaining except the negative ones. -1 means any number,
+  //  -2 means anything but 0.
+  cmd_info["SYST"] = command_info(0, &Commands::sim1, COM_SYST, HLP_SYST);
+  cmd_info["HELP"] = command_info(-1, &Commands::help, BEFORE_USER, HLP_HELP);
+  cmd_info["RETR"] = command_info(-2, &Commands::retr, 0, HLP_RETR);
+  cmd_info["STOR"] = command_info(-2, &Commands::stor, COM_STOR, HLP_STOR);
+  cmd_info["PORT"] = command_info(1, &Commands::port, COM_PORT, HLP_PORT);
+  cmd_info["EPRT"] = command_info(1, &Commands::port, COM_EPRT, HLP_EPRT);
+  cmd_info["PASV"] = command_info(0, &Commands::pasv, COM_PASV, HLP_PASV);
+  cmd_info["EPSV"] = command_info(-1, &Commands::pasv, COM_EPSV, HLP_EPSV);
+  cmd_info["PWD"] = command_info(0, &Commands::sim1, COM_PWD, HLP_PWD);
+  cmd_info["CWD"] = command_info(-2, &Commands::cwd,  COM_CWD, HLP_CWD);
+  cmd_info["CDUP"] = command_info(0, &Commands::cwd,  COM_CDUP, HLP_CDUP);
+  cmd_info["MKD"] = command_info(-2, &Commands::mkd, 0, HLP_MKD);
+  cmd_info["RMD"] = command_info(-2, &Commands::rmd, 0, HLP_RMD);
+  cmd_info["DELE"] = command_info(-2, &Commands::dele, 0, HLP_DELE);
+  cmd_info["LIST"] = command_info(-1, &Commands::list, COM_LONG, HLP_LIST);
+  cmd_info["NLST"] = command_info(-1, &Commands::list, COM_SHORT, HLP_NLST);
+  cmd_info["TYPE"] = command_info(-2, &Commands::sim1, COM_TYPE, HLP_TYPE);
+  cmd_info["MODE"] = command_info(1, &Commands::sim1, COM_MODE, HLP_MODE);
+  cmd_info["STRU"] = command_info(1, &Commands::sim1, COM_STRU, HLP_STRU);
+  cmd_info["SIZE"] = command_info(-2, &Commands::sim2, COM_SIZE, HLP_SIZE);
+  cmd_info["USER"] = command_info(1, &Commands::user, BEFORE_USER, HLP_USER);
+  cmd_info["PASS"] = command_info(-2, &Commands::pass, BEFORE_USER, HLP_PASS);
+  cmd_info["QUIT"] = command_info(-1, &Commands::quit, BEFORE_USER, HLP_QUIT);
+  cmd_info["NOOP"] = command_info(0, &Commands::sim1, COM_NOOP, HLP_NOOP);
+  cmd_info["MDTM"] = command_info(-2, &Commands::sim2, COM_MDTM, HLP_MDTM);
+  cmd_info["FEAT"] =
+    command_info(0, &Commands::sim1, COM_FEAT | BEFORE_USER, HLP_FEAT);
+  cmd_info["RNFR"] = command_info(-2, &Commands::rname, COM_FROM, HLP_RNFR);
+  cmd_info["RNTO"] = command_info(-2, &Commands::rname, COM_TO, HLP_RNTO);
+  cmd_info["MLST"] = command_info(-2, &Commands::sim2, COM_MLST, HLP_MLST);
+  cmd_info["MLSD"] = command_info(-1, &Commands::list, COM_MLSD, HLP_MLSD);
+  cmd_info["APPE"] = command_info(-2, &Commands::stor, COM_APPE, HLP_APPE);
+  cmd_info["OPTS"] = command_info(-1, &Commands::opts, 0, HLP_OPTS);
+  cmd_info["REST"] = command_info(1, &Commands::rest, 0, HLP_REST);
+  // the X*** commands are required by rfc1123.
+  cmd_info["XCWD"] = command_info(-2, &Commands::cwd, COM_CWD, HLP_CWD);
+  cmd_info["XPWD"] = command_info(0, &Commands::sim1, COM_PWD, HLP_PWD);
+  cmd_info["XCUP"] = command_info(0, &Commands::sim1, COM_CDUP, HLP_CDUP);
+  cmd_info["XMKD"] = command_info(-2, &Commands::mkd, 0, HLP_MKD);
+  cmd_info["XRMD"] = command_info(-2, &Commands::rmd, 0, HLP_RMD);
 #ifdef USE_TLS
-  { "AUTH",  1, &Commands::auth, BEFORE_USER, HLP_AUTH },
-  { "PBSZ",  1, &Commands::sim1, COM_PBSZ | BEFORE_USER, HLP_PBSZ },
-  { "PROT",  1, &Commands::sim1, COM_PROT | BEFORE_USER, HLP_PROT }, //40
-  { "CCC",   0, &Commands::sim1, COM_CCC,   HLP_CCC  },
+  cmd_info["AUTH"] = command_info(1, &Commands::auth, BEFORE_USER, HLP_AUTH);
+  cmd_info["PBSZ"] =
+    command_info(1, &Commands::sim1, COM_PBSZ | BEFORE_USER, HLP_PBSZ);
+  cmd_info["PROT"] =
+    command_info(1, &Commands::sim1, COM_PROT | BEFORE_USER, HLP_PROT);
+  cmd_info["CCC"] = command_info(0, &Commands::sim1, COM_CCC, HLP_CCC);
   // these commands are required to be understood by rfc2228, even if
   // we don't use them.
-  { "CONF", -1, &Commands::sim1, COM_CONF | BEFORE_USER, "" },
-  { "MIC",  -1, &Commands::sim1, COM_MIC | BEFORE_USER, "" },
-  { "ENC",  -1, &Commands::sim1, COM_ENC | BEFORE_USER, "" },
+  cmd_info["CONF"] =
+    command_info(-1, &Commands::sim1, COM_CONF | BEFORE_USER, "");
+  cmd_info["MIC"] =
+    command_info(-1, &Commands::sim1, COM_MIC | BEFORE_USER, "");
+  cmd_info["ENC"] =
+    command_info(-1, &Commands::sim1, COM_ENC | BEFORE_USER, "");
 #endif // USE_TLS
-  // An empty hlp field means no help will be displayed about the 
-  // object, what is noteworthy is that those entries must come last 
-  // as to not screwup the format when we display the help list.
-  { "ABOR",  0, &Commands::abor, 0,         ""       },
-  { "\n",    0, NULL           , LAST_COM,  ""       } // this goes last.
-};
+  // An empty hlp field means no help will be displayed about the command
+  cmd_info["ABOR"] = command_info(0, &Commands::abor, 0, HLP_ABOR);
+}
+
+Handler::~Handler()
+{
+  cmd_info.clear();
+}
 
 class User *Handler::initializer(int socket_id)
 {
@@ -76,7 +91,7 @@ class User *Handler::initializer(int soc
   socklen_t len = sizeof(sockaddr_in);
   
   getpeername(socket_id, reinterpret_cast<struct sockaddr*>(&addr), &len);
-  class User *user = new User(socket_id, addr); // initiating the user object, with initial stuff
+  class User *user = new User(socket_id, addr);
 
   string log = "new connection from: ";
   log += inet_ntoa(addr.sin_addr);
@@ -90,17 +105,16 @@ class User *Handler::initializer(int soc
   if(static_cast<int>(user_list.size()) > config->general.connection_limit)
   {
     // reached maximum number of connections...
-    message = "421 Maximum number of connections reached, please try again later.";
-    sem_wait(&user->user_lock);
-    user->logged_in = LOGGED_OUT;
-    sem_post(&user->user_lock);
+    message =
+      "421 Maximum number of connections reached, please try again later.";
+    user->settings(FAILURE, B_SET);
   }
   else
   {
     // dns lookup should go here.. find a nice way of doing it.
     message = "220 " + config->general.welcome_msg;
   }
-  comm.ftp_send(user, message);
+  *user << message << "\r\n";
 
   return(user);
 }
@@ -110,10 +124,12 @@ bool Handler::init(User *user)
   while(true)
   {
     string command;
-    if(user->logged_in == LOGGED_OUT)
+    if(user->settings(FAILURE))
       break;
-    if(comm.ftp_receive(user, command))
-      Handler::interpreter(user, command);
+    *user >> command;
+    util.clean_string(command);
+    if(!command.empty())
+      this->interpreter(user, command);
   }
   return(true); // we're returning to wrapper for exit.
 }
@@ -121,59 +137,58 @@ bool Handler::init(User *user)
 bool Handler::interpreter(User *user, string command)
 {
   string argument, message;
-  int cmd = 0;
   class StringTokenizer *st = NULL;
 
-  int words = util.clean_string(command);
-
-  if(words > 0)
-  {
-    st = new StringTokenizer(command, " ", true);
+  st = new StringTokenizer(command, " ", false);
+  int words = st->countTokens() - 1;
     command = st->nextToken();
-  }
 
-  transform(command.begin(), command.end(), command.begin(), (int(*)(int))toupper);
-  if(st)
-  {
+  transform(command.begin(), command.end(), command.begin(),
+      (int(*)(int))toupper);
+
     if(command == "SITE")
     {
       st->nextToken();
       command += " " + st->nextToken();
-      transform(command.begin() + 4, command.end(), command.begin() + 4, (int(*)(int))toupper);
+    transform(command.begin() + 4, command.end(), command.begin() + 4,
+        (int(*)(int))toupper);
+    words--;
     }
     argument = st->restTokens();
     delete(st);
-  }
 
   if(command != "PASS")
     logging->log_this(5, TYPE_DEBUG, command + "-" + argument);
   else
     logging->log_this(5, TYPE_DEBUG, "PASS-**********");
 
-  for(; cmd_info[cmd].extra_flag != LAST_COM; cmd++)
-    if(cmd_info[cmd].command == command)
-      break;
+  hash_map<const char*, struct command_info, hash<const char*>,
+    Utilities::eqstr>::iterator cmd_it = cmd_info.find(command.c_str());
     
-  if(cmd_info[cmd].extra_flag == LAST_COM)
+  if(cmd_it == cmd_info.end())
     message = "500 '" + command + "' not understood.";
   else
   {
-    if(user->logged_in < LOGGED_IN && !util.is_set(cmd_info[cmd].extra_flag, BEFORE_USER))
+    if(user->logged_in() < LOGGED_IN &&
+        !util.is_set((*(cmd_it)).second.extra_flag,BEFORE_USER))
       message = "530 Login with 'USER' and 'PASS' first.";
     else
     {
-      if((user->temp_buf.size() > 0) && command != "RNTO")
-        user->temp_buf.erase(); // we don't want this hanging around forever.
+      if((!user->temp_buf().empty()) && command != "RNTO")
+        user->temp_buf(""); // we don't want this hanging around forever.
 
-      if(words == 0 && words < cmd_info[cmd].ant_arg)
+      if(words == 0 && (((*(cmd_it)).second.ant_arg == -2) ||
+            (words < (*(cmd_it)).second.ant_arg)))
         message = "501 '" + command + "': requires an argument.";
-      else if(cmd_info[cmd].ant_arg != -1 && words > cmd_info[cmd].ant_arg)
+      else if((*(cmd_it)).second.ant_arg >= 0 &&
+          words > (*(cmd_it)).second.ant_arg)
         message = "501 '" + command + "': to many arguments to command.";
       else
-        ((comm.*cmd_info[cmd].pf) (user, argument, cmd_info[cmd].extra_flag));
+        ((comm.*(*(cmd_it)).second.pf)
+         (user, argument, (*(cmd_it)).second.extra_flag));
     }
   }
-  if(message.size() > 0)
-    comm.ftp_send(user, message);
+  if(!message.empty())
+    *user << message << "\r\n";
   return(true);
 }
Index: Handler.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Handler.h,v
retrieving revision 1.47
diff -u -p -b -r1.47 Handler.h
--- Handler.h	8 Jan 2005 16:22:02 -0000	1.47
+++ Handler.h	10 Apr 2005 22:43:19 -0000
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////
-// $Id: Handler.h,v 1.47 2005/01/08 16:22:02 klas Exp $
+// $Id: Handler.h,v 1.51 2005/03/27 23:50:18 jockelundin Exp $
 //
 // Usage: the main loop for connected clients and interpreter for
 //        ftp commands.
@@ -8,25 +8,37 @@
 //    LICENSE for complete information.
 /////////////////////////////////////////////////////////////////////
 
-#ifndef __HANDLER_H__
-#define __HANDLER_H__
+#ifndef HANDLER_H
+#define HANDLER_H
 
-#ifdef __HANDLER_CPP__
+#ifdef HANDLER_CPP
 #include "generic.h"
+#include "Utilities.h"
+#include "Configuration.h"
+#include "Log.h"
+#include "Connection.h"
 #include "Commands.h"
 #include "User.h"
-#include "Configuration.h"
-#include "Utilities.h"
 #include "StringTokenizer.h"
-#include "Log.h"
 #endif
 
 struct command_info {
-  const string command; //the command name
-  const int ant_arg; //Number of arguments. -1 means variable size.
-  bool (Commands::*pf)(User*, string, unsigned int); //the function we will call
-  const unsigned int extra_flag; //extra flags that is needed by that function
-  const string hlp_info; //the help field, if this is empty no help will be displayed
+  int ant_arg; //Number of arguments. -1 means variable size.
+  bool (Commands::*pf)(User*, string, unsigned int);
+  unsigned int extra_flag; //extra flags that is needed by that function
+  string hlp_info; // if this is empty, no help.
+  // constructor for assigning the hash
+  command_info(int a,
+      bool (Commands::*p)(User*, string, unsigned int),
+      unsigned int e, string h)
+  {
+    ant_arg = a;
+    pf = p;
+    extra_flag = e;
+    hlp_info = h;
+  }
+  // and one to make the hash functions happy that it still looks like a struct
+  command_info() {};
 };
 
 class Handler
@@ -34,15 +46,16 @@ class Handler
   private:
     bool interpreter(User*, string);
   public:
+    Handler();
+    ~Handler();
     bool init(User*);
     class User *initializer(int);
-    static struct command_info cmd_info[];
+    hash_map<const char*, struct command_info, hash<const char*>,
+      Utilities::eqstr> cmd_info;
 };
 
-extern class Commands comm;
-extern class Configuration *config;
-extern bool term;
-extern bool hup;
+extern class Handler *handler;
+
 extern vector<User*> user_list;
 extern sem_t user_list_lock;
 
Index: Log.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Log.cpp,v
retrieving revision 1.21
diff -u -p -b -r1.21 Log.cpp
--- Log.cpp	23 Jan 2005 17:56:47 -0000	1.21
+++ Log.cpp	10 Apr 2005 22:43:19 -0000
@@ -1,5 +1,5 @@
 ///////////////////////////////////////////////////////////////////
-//  $Id: Log.cpp,v 1.21 2005/01/23 17:56:47 klas Exp $
+//  $Id: Log.cpp,v 1.29 2005/03/27 23:50:18 jockelundin Exp $
 //
 //  Usage: Logging functions etc.
 //
@@ -7,13 +7,15 @@
 //    LICENSE for complete information.
 /////////////////////////////////////////////////////////////////////
 
-#define __LOG_CPP__
+#define LOG_CPP
 #include "Log.h"
 
+class Log *logging;
+
 Log::Log(void)
 {
-  sem_init(&this->to_log_lock, 0, 1); // 1 means it is not locked
-  sem_init(&this->data_ready, 0, 0); // and 0 means it is locked, flagged to 1 when there is data
+  sem_init(&this->to_log_lock, 0, 1);
+  sem_init(&this->data_ready, 0, 0);
 
   openlog("BabyFTPd", LOG_PID, LOG_FTP);
 }
@@ -40,20 +42,33 @@ void Log::log_this(int log_level, int lo
   this->to_log.push_back(temp_data);
   sem_post(&this->to_log_lock);
 
-  sem_post(&this->data_ready); // bump data_ready to tell main thread there is data
+  // bump data_ready to tell main thread there is data
+  sem_post(&this->data_ready);
 }
 
 
 void Log::log_main(void)
 {
   setuid(config->general.user);
+  setgid(config->general.group);
+
+  size_t time_string_len;
+
+  {
+    string temp;
+    time_string_len = util.time_string(temp, config->logger.time_format, 500);
+    time_string_len++;
+  }
+
   while(true)
   {
-    sem_wait(&this->data_ready); // if data_ready is zero we wait for it to increase
+    // if data_ready is zero we wait for it to increase
+    sem_wait(&this->data_ready);
 
     if(this->to_log.size() == 0)
     {
-      this->log_this(3, TYPE_DEBUG, "data->ready triggered without there being any data to log.");
+      this->log_this(3, TYPE_DEBUG,
+          "data->ready triggered without there being any data to log.");
       continue;
     }
 
@@ -90,6 +105,15 @@ void Log::log_main(void)
         break;
     }
 
+    if((temp_data.type == TYPE_AUTH && !config->logger.authlog.empty()) ||
+        (temp_data.type == TYPE_INFO && !config->logger.infolog.empty()) ||
+        (temp_data.type == TYPE_DEBUG && !config->logger.debuglog.empty()))
+    {
+      string timestr;
+      util.time_string(timestr, config->logger.time_format, time_string_len);
+      temp_data.message.insert(0, timestr);
+    }
+
     switch(temp_data.type)
     {
       case TYPE_XFER:
@@ -146,8 +170,8 @@ void Log::authlog(struct log_data data_t
   }
 }
 
-  void Log::infolog(struct log_data data_to_log)
-  {
+void Log::infolog(struct log_data data_to_log)
+{
   if(config->logger.infolog.empty())
     syslog(LOG_FTP | data_to_log.level, data_to_log.message.c_str());
   else
Index: Log.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Log.h,v
retrieving revision 1.8
diff -u -p -b -r1.8 Log.h
--- Log.h	17 Jan 2005 22:21:44 -0000	1.8
+++ Log.h	10 Apr 2005 22:43:19 -0000
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////
-//  $Id: Log.h,v 1.8 2005/01/17 22:21:44 klas Exp $
+//  $Id: Log.h,v 1.11 2005/03/28 16:57:13 klas Exp $
 //
 //  Usage: Logging defines, functions etc.
 //
@@ -7,14 +7,14 @@
 //    LICENSE for complete information.
 /////////////////////////////////////////////////////////////////////
 
-#ifndef __LOG_H__
-#define __LOG_H__
+#ifndef LOG_H
+#define LOG_H
 
-#ifdef __LOG_CPP__
+#ifdef LOG_CPP
 #include "generic.h"
 #include "Configuration.h"
 #include "Utilities.h"
-#endif // __LOG_CPP__
+#endif // LOG_CPP
 
 class Log
 {
@@ -34,6 +34,7 @@ class Log
     ~Log(void);
 };
 
+extern class Log *logging;
 
 struct log_data
 {
@@ -42,12 +43,9 @@ struct log_data
   string message;
 };
 
-extern class Log *logging;
-extern class Configuration *config;
-
-#define TYPE_XFER 1
-#define TYPE_AUTH 2
-#define TYPE_INFO 3
-#define TYPE_DEBUG 4
+const int TYPE_XFER = 1;
+const int TYPE_AUTH = 2;
+const int TYPE_INFO = 3;
+const int TYPE_DEBUG = 4;
 
-#endif // __LOG_H__
+#endif // LOG_H
Index: Makefile.in
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Makefile.in,v
retrieving revision 1.5
diff -u -p -b -r1.5 Makefile.in
--- Makefile.in	21 Nov 2004 23:00:27 -0000	1.5
+++ Makefile.in	10 Apr 2005 22:43:19 -0000
@@ -1,5 +1,5 @@
 # babyftpd Makefile.in - Makefile template used by 'configure'
-# $Id: Makefile.in,v 1.5 2004/11/21 23:00:27 klas Exp $
+# $Id: Makefile.in,v 1.6 2005/03/27 23:50:18 jockelundin Exp $
 #
 
 # C compiler to use
@@ -9,7 +9,7 @@ CXXFLAGS = @CXXFLAGS@ -D_REENTRANT
 
 LIBS = @LIBS@
 
-OBJFILES = User.o Utilities.o Configuration.o Commands.o Commands2.o Handler.o babyftpd.o StringTokenizer.o Log.o
+OBJFILES = Connection.o Data.o User.o Utilities.o Configuration.o Commands.o Commands2.o Handler.o babyftpd.o StringTokenizer.o Log.o
 
 default: all
 
Index: StringTokenizer.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/StringTokenizer.cpp,v
retrieving revision 1.9
diff -u -p -b -r1.9 StringTokenizer.cpp
--- StringTokenizer.cpp	16 Jan 2005 01:19:12 -0000	1.9
+++ StringTokenizer.cpp	10 Apr 2005 22:43:20 -0000
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////
-// $Id: StringTokenizer.cpp,v 1.9 2005/01/16 01:19:12 klas Exp $
+// $Id: StringTokenizer.cpp,v 1.16 2005/04/02 09:41:46 klas Exp $
 //
 // Usage: Used to chop up a string in "tokens"
 //
@@ -7,106 +7,92 @@
 //    LICENSE for complete information.
 /////////////////////////////////////////////////////////////////////
 
-#define __STRINGTOKENIZER_CPP__
+#define STRINGTOKENIZER_CPP
 #include "StringTokenizer.h"
 
 /*
-This class is made to handel strings. The StringTokenizer chops a string up into tokens using delimeters defined in a string passed ass an argument to the constructor.
+This class is made to handel strings. The StringTokenizer chops a string 
+up into tokens using delimeters defined in a string passed ass an argument 
+to the constructor.
 
-The idea of writing this class comes from a similar class in java. but this is more functional and works in a better way.
+The idea of writing this class comes from a similar class in java. but this 
+is more functional and works in a better way.
 
 The construktor takes three arguments, two strings and one bool. 
 The first string is the string you want to make tokens of.
-The secound string is a string containing all the characters that are going to cut the string.
-The bool determins if the token chars should come with the tokens or not. If the bool is set to false, the chars used to chop up the string is taken away.
+The secound string is a string containing all the characters that are 
+going to cut the string.
+The bool determins if the token chars should come with the tokens or not.
+If the bool is set to false, the chars used to chop up the string is taken 
+away.
 */
 
 //Original constructor
-StringTokenizer::StringTokenizer(string str, string delim = " \t\n\r\f", bool returnDelims = false)
+StringTokenizer::StringTokenizer(string str, string delim = " \t\n\r\f",
+    bool returnDelims = false)
 {
-  currentPosition = 0;
-  newPosition = -1;
-  delimsChanged = false;
   text = str;
-  maxPosition = str.length();
+  currentPosition = text.begin();
   delimiters = delim;
   retDelims = returnDelims;
 }
 
 //Private function used to get the position of the token.
-int StringTokenizer::scanToken(int startPos) 
+string::iterator StringTokenizer::scanToken(string::iterator startPos) const
 {
-  int position = startPos;
-  while (position < maxPosition) {
-    char c = text[position];
-    if (isDelimiter(c))
+  while (startPos != text.end())
+  {
+    if (isDelimiter(*startPos))
+    {
+      if(retDelims)
+        startPos++;
       break;
-    position++; 
   }
-  if (retDelims && (startPos == position)) {
-    char c = text[position];
-    if (isDelimiter(c))
-      position++;
+    startPos++;
   }
-  return position;  
-}
-
-//Private function that checks if a chatacter is a belimiter.
-bool StringTokenizer::isDelimiter(char character)
-{
-  for(size_t i = 0; i < delimiters.length(); i++){
-    if ((character == delimiters[i]) && (delimiters.find_first_of(character) >= 0))
-      return true;    
-  }
-  return false;
+  return startPos;
 }
 
 //Private function that returns a position so that the delimiters is skipped.
-int StringTokenizer::skipDelimiters(int startPos) 
+string::iterator StringTokenizer::skipDelimiters(string::iterator startPos)
+  const
 {
-  if (delimiters.compare("") == 0)
-    logging->log_this(2, TYPE_INFO, "Error: NullPointerException.");
-
-  int position = startPos;
-  while (!retDelims && position < maxPosition) {
-    char c = text[position];
-    if (!isDelimiter(c) || (delimiters.find_first_of(c) < 0)){
-      return position;
-    }
-    position++;
+  while (!retDelims && startPos != text.end())
+  {
+    if (!isDelimiter(*startPos) || (delimiters.find(*startPos) == string::npos))
+      break;
+    startPos++;
   }
-  return position;
+  return startPos;
 }
 
-//Public function that checks if the string got ore tokens.
-bool StringTokenizer::hasMoreTokens()
+//Public function that checks if the string got more tokens.
+bool StringTokenizer::hasMoreTokens() const
 {
-  newPosition = skipDelimiters(currentPosition);
-  return (newPosition < maxPosition);
+  return((skipDelimiters(currentPosition) == text.end()) ? false : true);
 }
 
 //Public function that returns the next token.
 string StringTokenizer::nextToken()
 {
-  currentPosition = (newPosition >= 0 && !delimsChanged) ? newPosition : skipDelimiters(currentPosition);
-  delimsChanged = false;
-  newPosition = -1;
+  currentPosition = skipDelimiters(currentPosition);
 
-  if (currentPosition >= maxPosition)
+  if (currentPosition == text.end())
     logging->log_this(2, TYPE_INFO, "NoSuchElementException.");
 
-  int start = currentPosition;
+  string::iterator start = currentPosition;
   currentPosition = scanToken(currentPosition);
-  return text.substr(start, currentPosition-start);
+  return string(start, currentPosition);
 }
 
 //Public functoin that retutns the number of tokens the string is containing.
-int StringTokenizer::countTokens() {
+int StringTokenizer::countTokens() const
+{
   int count = 0;
-  int currpos = currentPosition;
-  while (currpos < maxPosition) {
+  string::iterator currpos = currentPosition;
+  while (currpos != text.end()) {
     currpos = skipDelimiters(currpos);
-    if (currpos >= maxPosition)
+    if (currpos == text.end())
       break;
     currpos = scanToken(currpos);
     count++; 
@@ -114,25 +100,9 @@ int StringTokenizer::countTokens() {
   return count;  
 }
 
-//Public function that returns the next token.
-string StringTokenizer::nextToken(string delim) 
-{
-  delimiters = delim;
-  delimsChanged = true;
-  return nextToken();  
-}
-
 //Public function that returns all the tokens after the last used.
 string StringTokenizer::restTokens(void)
 {
-  string str = "";
-  while(hasMoreTokens())
-    str += nextToken();
-
-  for(size_t i = 0; i < delimiters.length(); i++){
-    while(str.find_first_of(delimiters[i]) == 0){
-       str.erase(0, 1);
-    }
-  }  
-  return str;    
+  return(string(isDelimiter(*currentPosition) ?
+        currentPosition + 1 : currentPosition, text.end()));
 }
Index: StringTokenizer.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/StringTokenizer.h,v
retrieving revision 1.8
diff -u -p -b -r1.8 StringTokenizer.h
--- StringTokenizer.h	16 Jan 2005 01:19:12 -0000	1.8
+++ StringTokenizer.h	10 Apr 2005 22:43:20 -0000
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////
-// $Id: StringTokenizer.h,v 1.8 2005/01/16 01:19:12 klas Exp $
+// $Id: StringTokenizer.h,v 1.12 2005/03/29 19:02:29 klas Exp $
 //
 // Usage: Used to chop up a string in "tokens"
 //
@@ -7,10 +7,10 @@
 //    LICENSE for complete information.
 /////////////////////////////////////////////////////////////////////
 
-#ifndef __STRINGTOKENIZER_H__
-#define __STRINGTOKENIZER_H__
+#ifndef STRINGTOKENIZER_H
+#define STRINGTOKENIZER_H
 
-#ifdef __STRINGTOKENIZER_CPP__
+#ifdef STRINGTOKENIZER_CPP
 #include "generic.h"
 #include "Log.h"
 #endif
@@ -19,24 +19,21 @@ class StringTokenizer
 {
   public:
     StringTokenizer(string, string, bool);
-    bool hasMoreTokens(void);
+    bool hasMoreTokens(void) const;
     string nextToken();
-    int countTokens(void);
-    string nextToken(string);
+    int countTokens(void) const;
     string restTokens(void);
 
   private:
-    int skipDelimiters(int);
-    int scanToken(int);
-    bool isDelimiter(char);
-
-    int currentPosition;
-    int newPosition;
-    int maxPosition;
+    string::iterator skipDelimiters(string::iterator) const;
+    string::iterator scanToken(string::iterator) const;
+    bool isDelimiter(char c) const
+    { return(delimiters.find(c) == string::npos ? false : true ); }
+
+    string::iterator currentPosition;
     string text;
     string delimiters;
     bool retDelims;
-    bool delimsChanged;    
 };
 
 #endif
Index: User.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/User.cpp,v
retrieving revision 1.70
diff -u -p -b -r1.70 User.cpp
--- User.cpp	23 Jan 2005 01:34:53 -0000	1.70
+++ User.cpp	10 Apr 2005 22:43:20 -0000
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////
-// $Id: User.cpp,v 1.70 2005/01/23 01:34:53 klas Exp $
+// $Id: User.cpp,v 1.92 2005/04/10 21:57:55 klas Exp $
 //
 // Usage: Holds information about connected users.
 //
@@ -7,173 +7,58 @@
 //    LICENSE for complete information.
 /////////////////////////////////////////////////////////////////////
 
-#define __USER_CPP__
-#define __NETINCLS__
+#define USER_CPP
+#define NETINCLS
 #include "User.h"
 
-Data::Data(User *user, int file_id, string file_name, int trans_type){
-  sem_wait(&user->user_lock);
-  sem_init(&data_lock, 0, 1);
-
-  Data::file_id = file_id;
-  Data::file_name = file_name;
-  Data::data_addr = user->data_addr;
-  Data::settings = user->settings;
-  Data::user = user;
-  if(trans_type == 1)
-    this->settings.set(RETR_TRANS);
-
-  this->settings.set(DATA_OPEN);
-  Data::rest_pos = 0;
-#ifdef USE_TLS
-  this->settings.reset(TLS_INITED);
-  this->settings.reset(TLS_ENABLED);
-#endif // USE_TLS
-
-  if(user->settings.test(PASSIVE))
-  {
-    socklen_t len = sizeof(this->data_addr);
-    if((this->data_id = accept(user->data_id, reinterpret_cast<struct sockaddr*>(&this->data_addr), &len)) < 0)
-      this->settings.reset(DATA_OPEN);
-    close(user->data_id);
-  }
-  else // ACTIVE
-  {
-    if(this->data_id)
-      close(Data::data_id);
-
-    this->data_id = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-    int on = 1;
-    setsockopt(this->data_id, SOL_SOCKET, SO_REUSEADDR,
-        (static_cast<void*>(&on)), sizeof(on));
-    struct sockaddr_in my_addr;
-    my_addr.sin_port = htons(20);
-    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
-    // active connections should originate at port 20, for that we need root.
-    uid_t old_uid = geteuid();
-    seteuid(0);
-    int ret = bind(this->data_id, reinterpret_cast<struct sockaddr*>
-        (&my_addr), sizeof(my_addr));
-    seteuid(old_uid);
-    if(ret != 0)
-      logging->log_this(3, LOG_INFO,
-          "Failed to bind to data port 20, falling back to arbitrary port.");
-
-    if(connect(this->data_id, reinterpret_cast<struct sockaddr*>
-          (&this->data_addr), sizeof(this->data_addr)) < 0)
-      this->settings.reset(DATA_OPEN);
-  }
-  // if the data connection comes from another host than the
-  // ctrl connection we drop it.. unless the user is tls protected.
-  if(this->data_addr.sin_addr.s_addr != user->ctrl_addr.sin_addr.s_addr)
-  {
-#ifdef USE_TLS
-    if(!user->settings.test(TLS_ENABLED))
-    {
-#endif // USE_TLS
-      close(this->data_id);
-      this->settings.reset(DATA_OPEN);
-    }
-#ifdef USE_TLS
-  }
-  if(this->settings.test(TLS_PROT_D)) // setting tls stuff up, handshake later
-  { 
-    comm.init_tls(this->data_session);
-    this->settings.set(TLS_INITED);
-    gnutls_transport_set_ptr(this->data_session, reinterpret_cast<gnutls_transport_ptr>(this->data_id));
-  }
-#endif //USE_TLS
-  sem_post(&user->user_lock);
-}
-
-bool Data::isOpen() const
+User::User(int socket_id, struct sockaddr_in &addr) : Connection()
 {
-  return(this->settings.test(DATA_OPEN));
-}
-
-Data::~Data(){
-  if(data_id)
-    close(data_id);
-  if(file_id)
-    close(file_id);
-
-  sem_destroy(&data_lock);
-
-#ifdef USE_TLS
-  // TLS cleanup here.
-  if(this->settings.test(TLS_INITED))
-  {
-    gnutls_db_remove_session(this->data_session);
-    gnutls_deinit(this->data_session);
-  }
-#endif
-          
-  if(!pthread_equal(pthread_self(), user->thread_id))
-  {
-    sem_wait(&user->user_lock);
-    user->data_vec.erase(user->get_data_it(pthread_self()));
-    sem_post(&user->user_lock);
-    logging->log_this(4, TYPE_INFO, "data thread " + util.itos(thread_id) + " exiting. Remaining data connections for " + user->user_name + " is: " + util.itos(user->data_vec.size()));
-  }
-}
-
-User::User(int socket_id, struct sockaddr_in &addr){
-  sem_init(&user_lock, 0, 1);
-
-  User::ctrl_addr = addr;
-  User::socket_id = socket_id;
-  User::logged_in = NOT_LOGGED_IN;
-  User::thread_id = pthread_self();
-  User::data_id = 0;
-  User::rest_pos = 0;
-  User::host_name = inet_ntoa(User::ctrl_addr.sin_addr);
-  User::root_dir = "/";
-  User::failed_logins = 0;
-  User::total_transfered = 0;
-  this->settings.reset();
+  this->_root_dir = "/";
+  this->_failed_logins = 0;
+  this->_total_transfered = 0;
+  this->_logged_in = NOT_LOGGED_IN;
+  this->_data_id = 0;
+  this->_rest_pos = 0;
+  this->addr(addr);
+  this->socket_id(socket_id);
+  this->thread_id(pthread_self());
+  this->host_name(inet_ntoa(this->addr().sin_addr));
+  struct timeval tmp = {config->general_user.idle, 0};
+  this->timeout(tmp);
 
   // setting the bitflags on mlst_facts... not to clean..
   for(int i = 0; i <= LAST_FACT; i++)
-    this->settings.set(i);
+    this->settings(i, B_SET);
 }
 
-User::~User(){
-  if(socket_id)
-    close(socket_id);
-  if(data_id)
-    close(data_id);
+User::~User()
+{
+  if(this->_data_id)
+    close(this->_data_id);
 
-  if(settings.test(PAM_DONE))
+  if(this->settings(PAM_DONE))
   {
     sem_wait(&comm.pam_lock);
-    pam_close_session(pamh, PAM_SILENT);
-    pam_end(pamh, PAM_SUCCESS);
+    pam_close_session(this->_pamh, PAM_SILENT);
+    pam_end(this->_pamh, PAM_SUCCESS);
     sem_post(&comm.pam_lock);
   }
 
-  sem_destroy(&user_lock);
-
   // killing off any ongoing data threads
-  for(vector<Data*>::iterator data_it = data_vec.begin(); data_it != data_vec.end(); data_it++)
+  for(vector<Data*>::iterator data_it = data_vec.begin();
+      data_it != data_vec.end(); data_it++)
     if(*data_it)
-      pthread_cancel((*data_it)->thread_id);
+      pthread_cancel((*data_it)->thread_id());
 
   while(data_vec.size())
   {  }
 
-#ifdef USE_TLS
-  // TLS cleanup here.
-  if(this->settings.test(TLS_INITED))
-  {
-    gnutls_db_remove_session(this->ctrl_session);
-    gnutls_deinit(this->ctrl_session);
-  }
-#endif
-
   sem_wait(&user_list_lock);
-  user_list.erase(get_user_it(pthread_self()));
+  user_list.erase(get_user_it(this->thread_id()));
   sem_post(&user_list_lock);
-  logging->log_this(3, TYPE_INFO, "user thread " + util.itos(thread_id) + " exiting, connected users:" + util.itos(user_list.size()));
+  logging->log_this(4, TYPE_INFO, "user thread " +
+      util.itos(this->thread_id()) + " exiting, connected users:" +
+      util.itos(user_list.size()));
 }
 
 vector<User*>::iterator get_user_it(pthread_t thread_id)
@@ -181,7 +66,7 @@ vector<User*>::iterator get_user_it(pthr
   vector<User*>::iterator user_it;
   
   for(user_it = user_list.begin(); user_it != user_list.end(); user_it++)
-    if(pthread_equal((*user_it)->thread_id, thread_id))
+    if(pthread_equal((*user_it)->thread_id(), thread_id))
       break;
   
   return(user_it);
@@ -192,8 +77,9 @@ vector<Data*>::iterator User::get_data_i
 {
   vector<Data*>::iterator data_it;
 
-  for(data_it = this->data_vec.begin(); data_it != this->data_vec.end(); data_it++)
-    if(pthread_equal((*data_it)->thread_id, thread_id))
+  for(data_it = this->data_vec.begin(); data_it != this->data_vec.end();
+      data_it++)
+    if(pthread_equal((*data_it)->thread_id(), thread_id))
       break;
   
   return(data_it);
@@ -201,17 +87,62 @@ vector<Data*>::iterator User::get_data_i
 
 bool User::data_vec_full() const
 {
-  if(data_vec.size() >= static_cast<size_t>(config->get_user(user_name).sim_data))
+  if(data_vec.size() >=
+      static_cast<size_t>(config->get_user(this->user_name()).sim_data))
     return(true);
   return(false);
 }
 
-void kill_user(void *user)
+bool User::login(void)
 {
-  delete static_cast<User*>(user);
+  char pwdata[PWBUF_SIZE];
+  struct passwd pwd, *pwd_ptr;
+  this->logged_in(LOGGED_IN);
+  getpwnam_r(this->user_name().c_str(), &pwd, pwdata, sizeof(pwdata),
+      &pwd_ptr);
+  uid_t uid = pwd.pw_uid;
+  gid_t gid = pwd.pw_gid;
+  if(config->get_user(this->user_name()).only_home)
+    this->root_dir(pwd.pw_dir);
+  this->current_dir(pwd.pw_dir);
+  seteuid(0);
+  setgid(gid);
+  seteuid(uid); // dropping permissions...
+  struct timeval tmp = {config->get_user(this->user_name()).idle, 0};
+  this->timeout(tmp);
+  return true;
 }
   
-void kill_data(void *data)
+User &User::operator>>(string &message)
 {
-  delete static_cast<Data*>(data);
+  char tmp_buf[210] = { 0 };
+  fd_set rfd;
+  FD_ZERO(&rfd);
+  FD_SET(this->socket_id(), &rfd);
+  timeval tv = this->timeout();
+
+  if(!select(this->socket_id() + 1, &rfd, NULL, NULL, &tv))
+  {
+    *this << "421 Connection timed out("
+      << util.itos(config->get_user(this->user_name()).idle)
+      << " seconds). Disconnected.";
+    if(this->logged_in())
+      logging->log_this(3, TYPE_AUTH, "User " + this->user_name() +
+          ", disconnected due to timeout.");
+
+    this->settings(FAILURE, B_SET);
+  }
+  else
+  {
+    if(this->receive(tmp_buf, 200) > 0)
+      message = tmp_buf;
+    else
+      this->settings(FAILURE, B_SET);
+  }
+  return *this;
+}
+
+void kill_user(void *user)
+{
+  delete static_cast<User*>(user);
 }
Index: User.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/User.h,v
retrieving revision 1.74
diff -u -p -b -r1.74 User.h
--- User.h	16 Jan 2005 00:55:24 -0000	1.74
+++ User.h	10 Apr 2005 22:43:20 -0000
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////
-// $Id: User.h,v 1.74 2005/01/16 00:55:24 klas Exp $
+// $Id: User.h,v 1.91 2005/04/10 21:57:36 jockelundin Exp $
 //
 // Usage: Holds information about connected users.
 //
@@ -7,122 +7,62 @@
 //    LICENSE for complete information.
 /////////////////////////////////////////////////////////////////////
 
-#ifndef __USER_H__
-#define __USER_H__
+#ifndef USER_H
+#define USER_H
 
-#ifdef __USER_CPP__
+#ifdef USER_CPP
 #include "generic.h"
-#include "Configuration.h"
-#include "Handler.h"
-#include "Commands.h"
 #include "Utilities.h"
+#include "Configuration.h"
 #include "Log.h"
+#include "Connection.h"
+#include "Data.h"
+#include "Commands.h"
 #endif
 
-#define NOT_LOGGED_IN 0
-#define USER_SUPPLIED 1
-#define LOGGED_IN 2
-#define LOGGED_OUT 3
-
-#define FACT_TYPE 0 // for mlsx
-#define FACT_SIZE 1
-#define FACT_MODIFY 2
-#define FACT_UNIQUE 3
-#define FACT_U_MODE 4
-#define FACT_U_UID 5
-#define FACT_U_GID 6
-#define FACT_U_LINK 7
-#define FACT_PERM 8
-#define LAST_FACT 8
-
-#define DATA_ACCT 9
-#define BINARY 10
-#define COMPRESSED 11
-#define PASSIVE 12
-#define ANONYMOUS 13
-#define DATA_OPEN 14
-#define EPSV_ONLY 15
-#define RETR_TRANS 16
-#define PAM_DONE 17
-#ifdef USE_TLS
-#define TLS_ENABLED 18
-#define TLS_INITED 19
-#define TLS_PBSZ 20
-#define TLS_PROT_D 21
-
-// keep an eye on these.. different depending on TLS or not...
-#define TOTAL_U_BIT 22
-#else
-#define TOTAL_U_BIT 18
-#endif // USE_TLS
-
+const int NOT_LOGGED_IN = 0;
+const int USER_SUPPLIED = 1;
+const int LOGGED_IN = 2;
+
+// macro that builds get and set functions for us.
+#define MEMBER(func, type, var)  private: type var; \
+  public: type func(void)const{return var;} \
+  public: void func(type var){ \
+          sem_wait(&this->lock); \
+          this->var = var; \
+          sem_post(&this->lock); \
+        }
 
-class Data
-{
-  private:
-    bool open;
-  public:
-    Data(User *user, int file_id = -1, string file_name = "", int trans_type = 0);
-    ~Data();
-    bool isOpen() const;
-    int data_id;
-    struct sockaddr_in data_addr;
-    int trans_type;
-    int file_id;
-    bitset<TOTAL_U_BIT> settings;
-    off_t rest_pos;
-    sem_t data_lock;
-    pthread_t thread_id;
-    string file_name;
-    string host_name; // NEVER modify this after dns_lookup thread is spawned. never.
-    User *user;
-#ifdef USE_TLS
-    gnutls_session data_session;
-#endif // USE_TLS
-};
-
-class User
+class User : public Connection
 {
+  friend class Data;
   public:
     User(int socket_id, struct sockaddr_in &addr);
     vector<Data*>::iterator get_data_it(pthread_t);
+    User &operator>>(string &message);
     bool data_vec_full() const;
+    bool login();
     ~User();
     vector<Data*> data_vec;
-    string user_name;
-    int logged_in;
-    int socket_id;
-    int data_id;
-    struct sockaddr_in ctrl_addr;
-    struct sockaddr_in data_addr;
-    string host_name; // NEVER modify this after dns_lookup thread is spawned. never.
-    string current_dir;
-    string root_dir;
-    string temp_buf; // is this for anything but rename? shouldn't be the way I just changed it around -kl
-    bitset<TOTAL_U_BIT> settings;
-    int failed_logins;
-    off_t rest_pos;
-    unsigned long long total_transfered; // this variable will hold the total number of transfered bytes.
-    sem_t user_lock;
-    pthread_t thread_id;
-#ifdef USE_TLS
-    gnutls_session ctrl_session;
-#endif // USE_TLS
-    pam_handle_t *pamh;
+    MEMBER(logged_in, int, _logged_in);
+    MEMBER(data_id, int, _data_id);
+    MEMBER(data_addr, struct sockaddr_in, _data_addr);
+    MEMBER(current_dir, string, _current_dir);
+    MEMBER(root_dir, string, _root_dir);
+    MEMBER(temp_buf, string, _temp_buf); // is this for anything but rename?
+    MEMBER(failed_logins, int, _failed_logins);
+    MEMBER(total_transfered, unsigned long long, _total_transfered);
+    MEMBER(pamh, pam_handle_t*, _pamh);
+    MEMBER(rest_pos, off_t, _rest_pos);
 };
 
+#undef MEMBER
+
 vector<User*>::iterator get_user_it(pthread_t);
 void kill_user(void*);
-void kill_data(void*);
 
 extern pthread_key_t my_user;
-extern pthread_key_t my_data;
 extern sem_t user_list_lock;
-extern pthread_t main_thread;
 extern vector<User*> user_list;
-extern class Configuration *config;
-extern class Handler handler;
-extern class Commands comm;
-extern class Utilities util;
 
-#endif
+#endif // USER_H
Index: Utilities.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Utilities.cpp,v
retrieving revision 1.40
diff -u -p -b -r1.40 Utilities.cpp
--- Utilities.cpp	8 Jan 2005 22:42:18 -0000	1.40
+++ Utilities.cpp	10 Apr 2005 22:43:20 -0000
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////
-// $Id: Utilities.cpp,v 1.40 2005/01/08 22:42:18 klas Exp $
+// $Id: Utilities.cpp,v 1.49 2005/04/06 18:06:28 klas Exp $
 //
 // Usage: Various functions that doesn't belong anywhere else.
 //
@@ -7,9 +7,10 @@
 //    LICENSE for complete information.
 /////////////////////////////////////////////////////////////////////
 
-#define __UTILITIES_CPP__
+#define UTILITIES_CPP
 #include "Utilities.h"
 
+class Utilities util;
 
 // function to remove line break at the end of a string
 void Utilities::strip_line_breaks(string &victim)
@@ -36,13 +37,14 @@ int Utilities::clean_string(string &vict
   while(isspace(victim[victim.size() - 1]))
     victim.erase(victim.size() - 1, 1);
   
-  for(string::iterator victim_it = victim.begin(); victim_it != victim.end(); ++victim_it)
+  for(string::iterator victim_it = victim.begin(); victim_it != victim.end();
+      ++victim_it)
   {
-    if(isspace(*victim_it) || iscntrl(*victim_it))
+    if(isspace(*victim_it) || !isascii(*victim_it))
     {
-      if(*victim_it == ' ') // isspace() triggers on more than space...
+      if(*victim_it == ' ')
         spaces++;
-      if((isspace(*(victim_it - 1))) || iscntrl(*victim_it))
+      if((isspace(*(victim_it - 1))) || !isascii(*victim_it))
       {
         if(*victim_it == ' ')
           spaces--;
@@ -55,12 +57,12 @@ int Utilities::clean_string(string &vict
 }
 
 // function to convert an int to a string
-string Utilities::itos(int num)
+string Utilities::itos(long long int num)
 {
   string message;
-  char buf[12];
+  char buf[12] = {0};
   
-  snprintf(buf, 12, "%d", num);
+  snprintf(buf, 12, "%lld", num);
   message = buf;
   
   return(message);
@@ -86,26 +88,26 @@ string Utilities::parse_perms_ls(mode_t 
   else if(S_ISSOCK(perm))
     attr[0] = 's';
 
-  if(test(perm, S_IRUSR)) attr[1] = 'r';
-  if(test(perm, S_IWUSR)) attr[2] = 'w';
-  if(test(perm, S_IXUSR)) {
-    if(test(perm, S_ISUID)) attr[3] = 's';
+  if(is_set(perm, S_IRUSR)) attr[1] = 'r';
+  if(is_set(perm, S_IWUSR)) attr[2] = 'w';
+  if(is_set(perm, S_IXUSR)) {
+    if(is_set(perm, S_ISUID)) attr[3] = 's';
     else attr[3] = 'x';
-  } else if(test(perm, S_ISUID)) attr[3] = 'S';
+  } else if(is_set(perm, S_ISUID)) attr[3] = 'S';
   
-  if(test(perm, S_IRGRP)) attr[4] = 'r';
-  if(test(perm, S_IWGRP)) attr[5] = 'w';
-  if(test(perm, S_IXGRP)) {
-    if(test(perm, S_ISGID)) attr[6] = 's';
+  if(is_set(perm, S_IRGRP)) attr[4] = 'r';
+  if(is_set(perm, S_IWGRP)) attr[5] = 'w';
+  if(is_set(perm, S_IXGRP)) {
+    if(is_set(perm, S_ISGID)) attr[6] = 's';
     else attr[6] = 'x';
-  } else if(test(perm, S_ISGID)) attr[6] = 'S';
+  } else if(is_set(perm, S_ISGID)) attr[6] = 'S';
   
-  if(test(perm, S_IROTH)) attr[7] = 'r';
-  if(test(perm, S_IWOTH)) attr[8] = 'w';
-  if(test(perm, S_IXOTH)) {
-    if(test(perm, S_ISVTX)) attr[9] = 't';
+  if(is_set(perm, S_IROTH)) attr[7] = 'r';
+  if(is_set(perm, S_IWOTH)) attr[8] = 'w';
+  if(is_set(perm, S_IXOTH)) {
+    if(is_set(perm, S_ISVTX)) attr[9] = 't';
     else attr[9] = 'x';
-  } else if(test(perm, S_ISVTX)) attr[9] = 'T';
+  } else if(is_set(perm, S_ISVTX)) attr[9] = 'T';
 
   return(attr);
 }
@@ -116,32 +118,50 @@ string Utilities::parse_perms_num(mode_t
 
   attr = "0000";
   
-  if(test(perm, S_ISUID))
+  if(is_set(perm, S_ISUID))
     attr[0] += 4;
-  if(test(perm, S_ISGID))
+  if(is_set(perm, S_ISGID))
     attr[0] += 2;
-  if(test(perm, S_ISVTX))
+  if(is_set(perm, S_ISVTX))
     attr[0]++;
-  if(test(perm, S_IRUSR))
+  if(is_set(perm, S_IRUSR))
     attr[1] += 4;
-  if(test(perm, S_IWUSR))
+  if(is_set(perm, S_IWUSR))
     attr[1] += 2;
-  if(test(perm, S_IXUSR))
+  if(is_set(perm, S_IXUSR))
     attr[1]++;
 
-  if(test(perm, S_IRGRP))
+  if(is_set(perm, S_IRGRP))
     attr[2] += 4;    
-  if(test(perm, S_IWGRP)) 
+  if(is_set(perm, S_IWGRP))
     attr[2] += 2;    
-  if(test(perm, S_IXGRP))
+  if(is_set(perm, S_IXGRP))
     attr[2]++;
 
-  if(test(perm, S_IROTH))
+  if(is_set(perm, S_IROTH))
     attr[3] += 4;
-  if(test(perm, S_IWOTH))
+  if(is_set(perm, S_IWOTH))
     attr[3] += 2;
-  if(test(perm, S_IXOTH))
+  if(is_set(perm, S_IXOTH))
     attr[3]++;
 
   return(attr);
 }
+
+
+int Utilities::time_string(string &victim, string format,
+    size_t len, time_t timep)
+{
+  char *timestr = new char[len + 1];
+  struct tm now;
+  memset(timestr, 0, len + 1);
+
+  if(timep == 0)
+    timep = time(NULL);
+  localtime_r(&timep, &now);
+  int ret = strftime(timestr, len, format.c_str(), &now);
+  victim = timestr;
+
+  delete[] timestr;
+  return(ret);
+}
Index: Utilities.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Utilities.h,v
retrieving revision 1.26
diff -u -p -b -r1.26 Utilities.h
--- Utilities.h	2 Dec 2004 22:28:42 -0000	1.26
+++ Utilities.h	10 Apr 2005 22:43:20 -0000
@@ -1,15 +1,16 @@
 /////////////////////////////////////////////////////////////////////
-//  File: Utilities.h
+// $Id: Utilities.h,v 1.33 2005/03/29 18:02:58 klas Exp $
+//
 // Usage: Various functions that doesn't belong anywhere else.
 //
 //  A part of babyftpd, licensed under the GNU GPL, see the file
 //    LICENSE for complete information.
 /////////////////////////////////////////////////////////////////////
 
-#ifndef __UTILITIES_H__
-#define __UTILITIES_H__
+#ifndef UTILITIES_H
+#define UTILITIES_H
 
-#ifdef __UTILITIES_CPP__
+#ifdef UTILITIES_CPP
 #include "generic.h"
 #endif
 
@@ -20,13 +21,12 @@ class Utilities
   public:
     void strip_line_breaks(string&);
     int clean_string(string&);
-    string itos(int);
+    string itos(long long int);
     string parse_perms_ls(mode_t);
     string parse_perms_num(mode_t);
+    int time_string(string&, string, size_t len = 50, time_t timep = 0);
 
     // small inline functions, previously macros
-    bool test(int a, int b)
-    { return((a & b) == b); }
     bool is_set(unsigned int flag, unsigned int bit)
     { return(flag & bit); }
     void set_bit(unsigned int &flag, unsigned int bit)
@@ -35,6 +35,13 @@ class Utilities
     { flag &= ~bit; }
     void toggle_bit(unsigned int &flag, unsigned int bit)
     { flag ^= bit;  }
+    struct eqstr
+    {
+      bool operator()(const char *s1, const char *s2)
+      { return strcmp(s1, s2) == 0; }
+    };
 };
 
+extern class Utilities util;
+
 #endif
Index: babyftpd.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/babyftpd.cpp,v
retrieving revision 1.109
diff -u -p -b -r1.109 babyftpd.cpp
--- babyftpd.cpp	22 Jan 2005 23:06:05 -0000	1.109
+++ babyftpd.cpp	10 Apr 2005 22:43:20 -0000
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////
-//  $Id: babyftpd.cpp,v 1.109 2005/01/22 23:06:05 klas Exp $
+//  $Id: babyftpd.cpp,v 1.129 2005/04/09 12:11:16 klas Exp $
 //
 // Usage: main function, commandline parsing and function that
 //        on the main socket.
@@ -8,8 +8,8 @@
 //    LICENSE for complete information.
 /////////////////////////////////////////////////////////////////////
 
-#define __BABYFTPD_CPP__
-#define __NETINCLS__
+#define BABYFTPD_CPP
+#define NETINCLS
 #include "babyftpd.h"
 
 int main(int argc, char **argv)
@@ -17,15 +17,16 @@ int main(int argc, char **argv)
   pid_t pid;
 
   logging = new class Log;
-  // we want the config object created before we parse commandline as that can set stuff.
   config = new class Configuration;
-  parse_commandline(argc, argv);
+  config->parse_command_line(argc, argv);
   // we send the start message for logging here so that starts our log.
   logging->log_this(3, TYPE_INFO, config->general.welcome_msg + " started.");
   // the config file variable exists in the Configuration class :p
   if(config->parse_config_file() == false)
-    cout << "Failed to open the config file (" << config->conf_file << "), check if it exists + permissions." << endl;
-  
+    cout << "Failed to open the config file (" << config->conf_file <<
+      "), check if it exists + permissions." << endl;
+  // if the conffile changes variables from commandline we reset them.
+  config->parse_command_line(argc, argv);
   
   if(config->general.no_daemon == false)
     pid = fork();
@@ -75,9 +76,11 @@ int main(int argc, char **argv)
       pthread_key_create(&my_data, kill_data);
 
       sem_init(&user_list_lock, 0, 1);
+      sem_init(&cleanup_list_lock, 0, 1);
       sem_init(&comm.pam_lock, 0, 1);
 
       signal(SIGTERM, sig_handler);
+      signal(SIGINT, sig_handler);
       signal(SIGHUP, sig_handler);
       signal(SIGPIPE, sig_handler);
 
@@ -93,15 +96,19 @@ int main(int argc, char **argv)
       gnutls_global_init();
 
       gnutls_certificate_allocate_credentials(&x509_cred);
-      ret = gnutls_certificate_set_x509_trust_file(x509_cred, config->general.tls_ca.c_str(), GNUTLS_X509_FMT_PEM);
+      ret = gnutls_certificate_set_x509_trust_file(x509_cred,
+          config->general.tls_ca.c_str(), GNUTLS_X509_FMT_PEM);
       if(ret < 0)
         logging->log_this(2, TYPE_INFO, gnutls_strerror(ret));
 
-      ret = gnutls_certificate_set_x509_crl_file(x509_cred, config->general.tls_crl.c_str(), GNUTLS_X509_FMT_PEM);
+      ret = gnutls_certificate_set_x509_crl_file(x509_cred,
+          config->general.tls_crl.c_str(), GNUTLS_X509_FMT_PEM);
       if(ret < 0)
         logging->log_this(2, TYPE_INFO, gnutls_strerror(ret));
 
-      ret = gnutls_certificate_set_x509_key_file(x509_cred, config->general.tls_cert.c_str(), config->general.tls_key.c_str(), GNUTLS_X509_FMT_PEM);
+      ret = gnutls_certificate_set_x509_key_file(x509_cred,
+          config->general.tls_cert.c_str(), config->general.tls_key.c_str(),
+          GNUTLS_X509_FMT_PEM);
       if(ret < 0)
         logging->log_this(2, TYPE_INFO, gnutls_strerror(ret));
 
@@ -113,7 +120,7 @@ int main(int argc, char **argv)
       dh_time = time(NULL);
 
 #endif // USE_TLS
-      
+      handler = new Handler;
       listener();
       break;
       
@@ -133,42 +140,48 @@ bool listener(void)
   address.sin_family = AF_INET;
   address.sin_addr.s_addr = INADDR_ANY;
   address.sin_port = htons(config->general.port);
-  if(bind(first_socket, reinterpret_cast<struct sockaddr*>(&address), sizeof(address)) < 0)
+  if(bind(first_socket, reinterpret_cast<struct sockaddr*>(&address),
+        sizeof(address)) < 0)
   {
-    logging->log_this(2, TYPE_INFO, "Port " + util.itos(config->general.port) + " already in use.");
+    logging->log_this(2, TYPE_INFO, "Port " +
+        util.itos(config->general.port) + " already in use.");
     exit_baby(1, EXIT_FULL);
   }
   seteuid(config->general.user);
+  setgid(config->general.group);
   listen(first_socket, LISTEN_LIMIT);
   while(true)
   {
     pthread_t thread_tmp;
     socklen_t socketlen = sizeof(struct sockaddr_in);
     fd_set sock_set;
-    time_t before = time(NULL);
+    time_t before = time(NULL) - (config->general.lifebeat_tick - tv.tv_sec);
 
     FD_ZERO(&sock_set);
     FD_SET(first_socket, &sock_set);
     if(!select(first_socket + 1, &sock_set, NULL, NULL, &tv))
     {
       lifebeat();
-      tv.tv_sec = config->general.lifebeat_tick; // important to reset lifebeat.
+      tv.tv_sec = config->general.lifebeat_tick; // important to reset lifebeat
       continue;
     }
 
-    int final_socket[0];
-    final_socket[0] = accept(first_socket,
+    int *final_socket = new int;
+    *final_socket = accept(first_socket,
         reinterpret_cast<struct sockaddr*>(&address), &socketlen);
-    if(final_socket[0] != -1)
-    {
-      pthread_create(&thread_tmp, NULL, user_wrapper,
-          static_cast<void*>(final_socket));
-      pthread_detach(thread_tmp);
-    }
+    if(*final_socket != -1)
+      pthread_create(&thread_tmp, NULL, user_wrapper, (final_socket));
     else
+    {
       logging->log_this(3, TYPE_INFO, "Failed to accept new caller.");
+      // if the socket is accepted and the thread created the pointer will
+      // get deleted there after it is stored.
+      delete final_socket;
+    }
 
     tv.tv_sec = config->general.lifebeat_tick - (time(NULL) - before);
+    if(tv.tv_sec < 0 || tv.tv_sec > config->general.lifebeat_tick)
+      tv.tv_sec = 0;
   }
   close(first_socket);
 
@@ -179,13 +192,24 @@ void lifebeat(void)
 {
   logging->log_this(4, TYPE_DEBUG, "lifebeat reached.");
 
+  sem_wait(&cleanup_list_lock);
+  while(!cleanup_list.empty())
+  {
+    logging->log_this(4, TYPE_DEBUG, "joining thread #" +
+        util.itos(cleanup_list.back()));
+    pthread_join(cleanup_list.back(), NULL);
+    cleanup_list.pop_back();
+  }
+  sem_post(&cleanup_list_lock);
+
 #ifdef USE_TLS
   if((time(NULL) - dh_time) > config->general.dh_regenerate)
   {
     gnutls_dh_params temp_dh_params;
     gnutls_dh_params_init(&temp_dh_params);
     gnutls_dh_params_generate2(temp_dh_params, 1024);
-    dh_params = temp_dh_params;
+    gnutls_dh_params_cpy(dh_params, temp_dh_params);
+    gnutls_dh_params_deinit(temp_dh_params);
     gnutls_certificate_set_dh_params(x509_cred, dh_params);
     dh_time = time(NULL);
   }
@@ -196,12 +220,14 @@ static void *user_wrapper(void *socket_i
 {
   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
 
-  class User *user = handler.initializer(static_cast<int>((static_cast<int*>(socket_id)[0])));
+  class User *user = handler->initializer((*(static_cast<int*>(socket_id))));
+  delete (static_cast<int*>(socket_id));
   pthread_setspecific(my_user, static_cast<void*>(user));
 
-  handler.init(user);
+  handler->init(user);
 
-  logging->log_this(3, TYPE_DEBUG, "closed connection from: " + user->host_name);
+  logging->log_this(3, TYPE_DEBUG, "closed connection from: " +
+      user->host_name());
   pthread_exit(NULL); // the thread exits..., and takes the user with it..
 }
 
@@ -213,150 +239,21 @@ static void *log_wrapper(void *not_used)
   pthread_exit(NULL);
 }
 
-void parse_commandline(int argc, char **argv)
-{
-  int opt = 0;
-  const char *cmdopts = "hvc:nVl:qp:";
-  bool help, verbose, conf_file, no_daemon, version, quiet, port;
-  help = verbose = conf_file = no_daemon = version = quiet = port = false;
-   
-  // struct for the long options... neat, huh?
-  struct option longopts[] =
-  {
-    { "help",      0, 0, 'h' },
-    { "verbose",   0, 0, 'v' },
-    { "conf-file", 1, 0, 'c' },
-    { "no-daemon", 0, 0, 'd' },
-    { "version",   0, 0, 'V' },
-    { "log-level", 1, 0, 'l' },
-    { "quiet",     0, 0, 'q' },
-    { "port",      1, 0, 'p' },
-    { 0,           0, 0, 0   }
-  };
-  
-  while((opt = getopt_long(argc, argv, cmdopts, longopts, NULL)) != -1)
-  {
-    switch(opt)
-    {
-      case 'h':
-        // help code
-        help = true;
-        break;
-
-      case 'v':
-        // verbosity code
-        // FIXME implement this throughout the code
-        verbose = true;
-        config->general.verbose++;
-        break;
-
-      case 'c':
-        // alternate conf file code
-        conf_file = true;
-        config->conf_file = optarg;
-        break;
-
-      case 'n':
-        // no-daemon code
-        no_daemon = true;
-        config->general.no_daemon = true;
-        break;
-
-      case 'V':
-        // Version code
-        version = true;
-        break;
-
-      case 'l':
-        // Log level code...
-        int log_level;
-        if(atoi(optarg) > 10)
-          log_level = 10;
-        else if(atoi(optarg) < 0)
-          log_level = 0;
-        else
-          log_level = atoi(optarg);
-        config->logger.log_level = log_level;
-        break;
-
-      case 'q':
-        // shh... quiet...
-        // FIXME implement this throughout the code
-        quiet = true;
-        config->general.verbose--;
-        break;
-
-      case 'p':
-        // alternate port...
-        // FIXME this should disable port argument from conffile...
-        port = true;
-        config->general.port = atoi(optarg);
-        break;
-        
-      case '?':
-        // unknown option...
-        break;
-        
-      case ':':
-        // missing parameter...
-        break;
-        
-      default:
-        // shouldn't get here...
-        cout << "Hey... you got waaay off track somewhere above :p" << endl;
-        break;
-    }
-
-    if(help == true)
-    { 
-      cout << "Usage: " << argv[0] << " [OPTION]..." << endl;
-      cout << endl;
-      cout << "  -h, --help            display this help and exit" << endl;
-      cout << "  -v, --verbose         increase verbosity(*)" << endl;
-      cout << "  -c, --conf-file=FILE  supply another conf file instead of the default" << endl;
-      cout << "  -n, --no-daemon       do not fork into the background" << endl;
-      cout << "  -V, --version         display version information and exit" << endl;
-      cout << "  -l, --log-level=LEVEL set the log level, 1-10, default is 5" << endl;
-      cout << "  -q, --quiet           decrease verbosity(*)" << endl;
-      cout << "  -p, --port=PORT       specify an alternate port" << endl;
-      cout << endl;
-      cout << "    (*) only hooks for these options in yet, doesn't really work." << endl;
-      cout << endl;
-      cout << "Please, report bugs, either directly to the developers" << endl;
-      cout << "or via sourceforge (http://www.sf.net/projects/babyftpd)." << endl;
-
-      exit_baby(0, EXIT_EARLY);
-    }
-    
-    if(version == true)
-    {
-      string version_info;
-      // we're building the version string here... again, smallest possible scope...
-      version_info = "BabyFTPd version ";
-      version_info += VERSION;
-      version_info += "\nBuilt on ";
-      version_info += __DATE__;
-      version_info += " ";
-      version_info += __TIME__;
-      version_info += "\n";
-      
-      cout << version_info;
-      exit_baby(0, EXIT_EARLY);
-    }
-  }
-}
 
 void sig_handler(int signal)
 {
   switch(signal)
   {
     case SIGTERM:
+    case SIGINT:
       logging->log_this(3, TYPE_INFO, "SIGTERM received.");
 
-      logging->log_this(4, TYPE_DEBUG, "main thread. users: " + util.itos(user_list.size()));
+      logging->log_this(4, TYPE_DEBUG, "main thread. users: " +
+          util.itos(user_list.size()));
 
-      for(vector<User*>::iterator user_it = user_list.begin(); user_it != user_list.end(); user_it++)
-        pthread_cancel((*user_it)->thread_id);
+      for(vector<User*>::iterator user_it = user_list.begin();
+          user_it != user_list.end(); user_it++)
+        pthread_cancel((*user_it)->thread_id());
 
       struct timeval times;
       times.tv_sec = 1;
@@ -385,15 +282,26 @@ void sig_handler(int signal)
 
 void exit_baby(int num, int how)
 {
+  sem_wait(&cleanup_list_lock);
+  while(!cleanup_list.empty())
+  {
+    logging->log_this(4, TYPE_DEBUG, "joining thread #" +
+        util.itos(cleanup_list.back()));
+    pthread_join(cleanup_list.back(), NULL);
+    cleanup_list.pop_back();
+  }
+  sem_post(&cleanup_list_lock);
 
   if(how == EXIT_FULL)
   {
+    delete handler;
     pthread_key_delete(my_user);
     pthread_key_delete(my_data);
 
 #ifdef USE_TLS
     // emptying and freeing session cache.
-    for(vector<cache*>::iterator cache_it = cache_db.begin(); cache_it != cache_db.end(); cache_it++)
+    for(vector<cache*>::iterator cache_it = cache_db.begin();
+        cache_it != cache_db.end(); cache_it++)
     {
       gnutls_free((*cache_it)->key.data);
       gnutls_free((*cache_it)->data.data);
@@ -407,11 +315,12 @@ void exit_baby(int num, int how)
     sem_destroy(&cache_lock);
 #endif // USE_TLS 
     sem_destroy(&comm.pam_lock);
+    sem_destroy(&cleanup_list_lock);
     sem_destroy(&user_list_lock);
     pthread_cancel(logging_thread);
     pthread_join(logging_thread, NULL);
   }
-  delete(logging);
+  delete logging;
 
   delete config;
   exit(num);
@@ -427,7 +336,8 @@ int tls_db_store(void *dbf, gnutls_datum
   temp_cache->key.size = key.size;
   memcpy(temp_cache->key.data, key.data, key.size);
 
-  temp_cache->data.data = static_cast<unsigned char*>(gnutls_malloc(data.size));
+  temp_cache->data.data =
+    static_cast<unsigned char*>(gnutls_malloc(data.size));
   temp_cache->data.size = data.size;
   memcpy(temp_cache->data.data, data.data, data.size);
 
@@ -441,7 +351,8 @@ gnutls_datum tls_db_fetch(void *dbf, gnu
 {
   gnutls_datum data = { NULL, 0 };
 
-  for(vector<cache*>::iterator cache_it = cache_db.begin(); cache_it != cache_db.end(); cache_it++)
+  for(vector<cache*>::iterator cache_it = cache_db.begin();
+      cache_it != cache_db.end(); cache_it++)
     if(memcmp(key.data, (*cache_it)->key.data, (*cache_it)->key.size) == 0)
     {
       data.size = (*cache_it)->data.size;
@@ -455,7 +366,8 @@ gnutls_datum tls_db_fetch(void *dbf, gnu
 
 int tls_db_delete(void *dbf, gnutls_datum key)
 {
-  for(vector<cache*>::iterator cache_it = cache_db.begin(); cache_it != cache_db.end(); cache_it++)
+  for(vector<cache*>::iterator cache_it = cache_db.begin();
+      cache_it != cache_db.end(); cache_it++)
     if(memcmp(key.data, (*cache_it)->key.data, (*cache_it)->key.size) == 0)
     {
       sem_wait(&cache_lock);
Index: babyftpd.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/babyftpd.h,v
retrieving revision 1.47
diff -u -p -b -r1.47 babyftpd.h
--- babyftpd.h	18 Jan 2005 22:44:36 -0000	1.47
+++ babyftpd.h	10 Apr 2005 22:43:20 -0000
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////
-// $Id: babyftpd.h,v 1.47 2005/01/18 22:44:36 klas Exp $
+// $Id: babyftpd.h,v 1.54 2005/03/28 17:08:12 klas Exp $
 //
 // Usage: main function, commandline parsing and function that
 //        on the main socket.
@@ -8,21 +8,23 @@
 //    LICENSE for complete information.
 /////////////////////////////////////////////////////////////////////
 
-#ifndef __BABYFTPD_H__
-#define __BABYFTPD_H__
+#ifndef BABYFTPD_H
+#define BABYFTPD_H
 
-#ifdef __BABYFTPD_CPP__
+#ifdef BABYFTPD_CPP
 #include "generic.h"
+#include "Utilities.h"
 #include "Configuration.h"
+#include "Log.h"
+#include "Connection.h"
 #include "User.h"
+#include "Data.h"
 #include "Handler.h"
 #include "Commands.h"
-#include "Utilities.h"
-#include "Log.h"
 #ifdef USE_TLS
 #include "tls.cpp"
 #endif // USE_TLS
-#endif // __BABYFTPD_CPP__
+#endif // BABYFTPD_CPP
 
 static void *user_wrapper(void*) __attribute__ ((noreturn));
 static void *log_wrapper(void*) __attribute__ ((noreturn));
@@ -32,26 +34,18 @@ void sig_handler(int);
 void exit_baby(int, int);
 void lifebeat(void);
 
-class Configuration *config;
-class Handler handler;
-class Commands comm;
-class Utilities util;
-class Log *logging;
-
 // thread specific storage..
 pthread_key_t my_user = 0;
 pthread_key_t my_data = 0;
 
 sem_t user_list_lock;
+sem_t cleanup_list_lock;
 pthread_t main_thread; // for managing on shutdown and signal handling.
 pthread_t logging_thread; // so we can join it.
 vector<User*> user_list;
+vector<pthread_t> cleanup_list;
 
-#define LISTEN_LIMIT 5
-
-// for exit_baby()
-#define EXIT_EARLY 1
-#define EXIT_FULL  2
+const int LISTEN_LIMIT = 5;
 
 #ifdef USE_TLS
 gnutls_certificate_credentials x509_cred;
Index: commands.hlp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/commands.hlp,v
retrieving revision 1.4
diff -u -p -b -r1.4 commands.hlp
--- commands.hlp	10 Jul 2004 17:22:27 -0000	1.4
+++ commands.hlp	10 Apr 2005 22:43:20 -0000
@@ -1,40 +1,40 @@
-#define HLP_USER " <username> (send username)"
-#define HLP_PASS " <password> (send password)" 
-#define HLP_RETR " <file> (sends a file over a data connection)"
-#define HLP_STOR " <file> (receives a file over a data connection)"
-#define HLP_PORT " <a1,a2,a3,a4,p1,p2> (specify client data port)"
-#define HLP_EPRT " <|protocol|adress|port|> (specify client data adress and port)"
-#define HLP_QUIT " (logout)"
-#define HLP_LIST " <path> (lists a directory or file(s))"
-#define HLP_TYPE " <type> (sets the transfer type for future transfers)"
-#define HLP_HELP " [<command> (returns help for specified command)] (shows available commands)"
-#define HLP_SYST " (returns the system type)"
-#define HLP_NOOP " (no operation)"
-#define HLP_DELE " <file> (delete file)"
-#define HLP_NLST " <path> (lists only names of a directory or file(s))"
-#define HLP_PASV " (returns a non standard data port for transfers)"
-#define HLP_EPSV " (returns a non standard data port (extended format) for transfers)"
-#define HLP_REST " <integer> (Sets the file pointer for the next RETR/STOR command to <value> (bytes))"
-#define HLP_APPE " <file> (appends to file over data connection)"
-#define HLP_RNFR " <file> (given before RNTO, filename to rename from, must be followed by RNTO)"
-#define HLP_RNTO " <file> (given after RNFR, filename to rename to, can only be used after RNFR)"
-#define HLP_SIZE " <file> (returns the exact number of octets <file> occupies)"
-#define HLP_MDTM " <file> (returns modification time of <file>)"
-#define HLP_FEAT " (returns available extensions)"
-#define HLP_MLST " <file> (returns file info for machine parsing)"
-#define HLP_MLSD " <dir> (returns file listing for machine parsing)"
-#define HLP_OPTS " [<command> <options> (modifies options for specified command)] shows what commands options are available for)"
-#define HLP_MODE " <mode> (Set the transfer mode, only accepts S and Z)"
-#define HLP_STRU " <file structure> (OBSOLETE, only accepts F (file))"
-#define HLP_PWD  " (returns current working directory)"
-#define HLP_MKD  " <dir> (creates directory)"
-#define HLP_CDUP " (changes current working directory to parent directory)"
-#define HLP_RMD  " <dir> (removes directory)"
-#define HLP_CWD  " <path> (changes current working directory)"
-#define HLP_FEAT " (returns available extensions)"
+const char *HLP_USER = " <username> (send username)";
+const char *HLP_PASS = " <password> (send password)" ;
+const char *HLP_RETR = " <file> (sends a file over a data connection)";
+const char *HLP_STOR = " <file> (receives a file over a data connection)";
+const char *HLP_PORT = " <a1,a2,a3,a4,p1,p2> (specify client data port)";
+const char *HLP_EPRT = " <|protocol|adress|port|> (specify client data adress and port)";
+const char *HLP_QUIT = " (logout)";
+const char *HLP_LIST = " <path> (lists a directory or file(s))";
+const char *HLP_TYPE = " <type> (sets the transfer type for future transfers)";
+const char *HLP_HELP = " [<command> (returns help for specified command)] (shows available commands)";
+const char *HLP_SYST = " (returns the system type)";
+const char *HLP_NOOP = " (no operation)";
+const char *HLP_DELE = " <file> (delete file)";
+const char *HLP_NLST = " <path> (lists only names of a directory or file(s))";
+const char *HLP_PASV = " (returns a non standard data port for transfers)";
+const char *HLP_EPSV = " (returns a non standard data port (extended format) for transfers)";
+const char *HLP_REST = " <integer> (Sets the file pointer for the next RETR/STOR command to <value> (bytes))";
+const char *HLP_APPE = " <file> (appends to file over data connection)";
+const char *HLP_RNFR = " <file> (given before RNTO, filename to rename from, must be followed by RNTO)";
+const char *HLP_RNTO = " <file> (given after RNFR, filename to rename to, can only be used after RNFR)";
+const char *HLP_SIZE = " <file> (returns the exact number of octets <file> occupies)";
+const char *HLP_MDTM = " <file> (returns modification time of <file>)";
+const char *HLP_FEAT = " (returns available extensions)";
+const char *HLP_MLST = " <file> (returns file info for machine parsing)";
+const char *HLP_MLSD = " <dir> (returns file listing for machine parsing)";
+const char *HLP_OPTS = " [<command> <options> (modifies options for specified command)] shows what commands options are available for)";
+const char *HLP_MODE = " <mode> (Set the transfer mode, only accepts S and Z)";
+const char *HLP_STRU = " <file structure> (OBSOLETE, only accepts F (file))";
+const char *HLP_PWD  = " (returns current working directory)";
+const char *HLP_MKD  = " <dir> (creates directory)";
+const char *HLP_CDUP = " (changes current working directory to parent directory)";
+const char *HLP_RMD  = " <dir> (removes directory)";
+const char *HLP_CWD  = " <path> (changes current working directory)";
+const char *HLP_ABOR = " (cancels any ongoing data transfers)";
 #ifdef USE_TLS
-#define HLP_AUTH " <mechanism> (initiates security mechanism)"
-#define HLP_PBSZ " <integer> (negotiates protection buffer size, 0 for stream)"
-#define HLP_PROT " <prot level> (sets protection level for data channel (P|S|E|C))"
-#define HLP_CCC " (sets protection level of command channel to CLEAR)"
+const char *HLP_AUTH = " <mechanism> (initiates security mechanism)";
+const char *HLP_PBSZ = " <integer> (negotiates protection buffer size, 0 for stream)";
+const char *HLP_PROT = " <prot level> (sets protection level for data channel (P|S|E|C))";
+const char *HLP_CCC = " (sets protection level of command channel to CLEAR)";
 #endif // USE_TLS
Index: config.h.in
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/config.h.in,v
retrieving revision 1.11
diff -u -p -b -r1.11 config.h.in
--- config.h.in	18 Jan 2005 22:57:04 -0000	1.11
+++ config.h.in	10 Apr 2005 22:43:20 -0000
@@ -1,5 +1,10 @@
 /* config.h.in.  Generated from configure.in by autoheader.  */
-/* $Id: config.h.in,v 1.11 2005/01/18 22:57:04 klas Exp $ */
+
+/* Define to 1 if the `closedir' function returns void instead of `int'. */
+#undef CLOSEDIR_VOID
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#undef HAVE_ARPA_INET_H
 
 /* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
    */
@@ -8,6 +13,15 @@
 /* Define to 1 if you have the <fcntl.h> header file. */
 #undef HAVE_FCNTL_H
 
+/* Define to 1 if you have the `fork' function. */
+#undef HAVE_FORK
+
+/* Define to 1 if you have the `gethostname' function. */
+#undef HAVE_GETHOSTNAME
+
+/* Define to 1 if you have the `inet_ntoa' function. */
+#undef HAVE_INET_NTOA
+
 /* Define to 1 if you have the <inttypes.h> header file. */
 #undef HAVE_INTTYPES_H
 
@@ -17,24 +31,69 @@
 /* Define to 1 if you have the `pthread' library (-lpthread). */
 #undef HAVE_LIBPTHREAD
 
+/* Define to 1 if you have the `localtime_r' function. */
+#undef HAVE_LOCALTIME_R
+
+/* Define to 1 if `lstat' has the bug that it succeeds when given the
+   zero-length file name argument. */
+#undef HAVE_LSTAT_EMPTY_STRING_BUG
+
+/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
+   to 0 otherwise. */
+#undef HAVE_MALLOC
+
 /* Define to 1 if you have the <memory.h> header file. */
 #undef HAVE_MEMORY_H
 
+/* Define to 1 if you have the `memset' function. */
+#undef HAVE_MEMSET
+
+/* Define to 1 if you have the `mkdir' function. */
+#undef HAVE_MKDIR
+
 /* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
 #undef HAVE_NDIR_H
 
+/* Define to 1 if you have the `rmdir' function. */
+#undef HAVE_RMDIR
+
+/* Define to 1 if you have the <security/pam_appl.h> header file. */
+#undef HAVE_SECURITY_PAM_APPL_H
+
+/* Define to 1 if you have the `select' function. */
+#undef HAVE_SELECT
+
+/* Define to 1 if you have the `socket' function. */
+#undef HAVE_SOCKET
+
+/* Define to 1 if `stat' has the bug that it succeeds when given the
+   zero-length file name argument. */
+#undef HAVE_STAT_EMPTY_STRING_BUG
+
+/* Define to 1 if stdbool.h conforms to C99. */
+#undef HAVE_STDBOOL_H
+
 /* Define to 1 if you have the <stdint.h> header file. */
 #undef HAVE_STDINT_H
 
 /* Define to 1 if you have the <stdlib.h> header file. */
 #undef HAVE_STDLIB_H
 
+/* Define to 1 if you have the `strftime' function. */
+#undef HAVE_STRFTIME
+
 /* Define to 1 if you have the <strings.h> header file. */
 #undef HAVE_STRINGS_H
 
 /* Define to 1 if you have the <string.h> header file. */
 #undef HAVE_STRING_H
 
+/* Define to 1 if you have the `strndup' function. */
+#undef HAVE_STRNDUP
+
+/* Define to 1 if you have the `strtoul' function. */
+#undef HAVE_STRTOUL
+
 /* Define to 1 if you have the <syslog.h> header file. */
 #undef HAVE_SYSLOG_H
 
@@ -46,6 +105,12 @@
    */
 #undef HAVE_SYS_NDIR_H
 
+/* Define to 1 if you have the <sys/select.h> header file. */
+#undef HAVE_SYS_SELECT_H
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_H
+
 /* Define to 1 if you have the <sys/stat.h> header file. */
 #undef HAVE_SYS_STAT_H
 
@@ -58,6 +123,40 @@
 /* Define to 1 if you have the <unistd.h> header file. */
 #undef HAVE_UNISTD_H
 
+/* Define to 1 if you have the `vfork' function. */
+#undef HAVE_VFORK
+
+/* Define to 1 if you have the <vfork.h> header file. */
+#undef HAVE_VFORK_H
+
+/* Define to 1 if `fork' works. */
+#undef HAVE_WORKING_FORK
+
+/* Define to 1 if `vfork' works. */
+#undef HAVE_WORKING_VFORK
+
+/* Define to 1 if the system has the type `_Bool'. */
+#undef HAVE__BOOL
+
+/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
+   slash. */
+#undef LSTAT_FOLLOWS_SLASHED_SYMLINK
+
+/* Define to mark libgcrypt being 1.1.92 or older */
+#undef OLD_GCRYPT
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#undef RETSIGTYPE
+
+/* Define to the type of arg 1 for `select'. */
+#undef SELECT_TYPE_ARG1
+
+/* Define to the type of args 2, 3 and 4 for `select'. */
+#undef SELECT_TYPE_ARG234
+
+/* Define to the type of arg 5 for `select'. */
+#undef SELECT_TYPE_ARG5
+
 /* Define to 1 if the `S_IS*' macros in <sys/stat.h> do not work properly. */
 #undef STAT_MACROS_BROKEN
 
@@ -67,20 +166,44 @@
 /* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
 #undef TIME_WITH_SYS_TIME
 
+/* Define to 1 if your <sys/time.h> declares `struct tm'. */
+#undef TM_IN_SYS_TIME
+
+/* newls enable */
+#undef USE_NEWLS
+
+/* tls enable */
+#undef USE_TLS
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
 /* Define to `int' if <sys/types.h> doesn't define. */
 #undef gid_t
 
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+   calls it, or to nothing if 'inline' is not supported under any name.  */
+#ifndef __cplusplus
+#undef inline
+#endif
+
+/* Define to rpl_malloc if the replacement function should be used. */
+#undef malloc
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef mode_t
+
+/* Define to `long' if <sys/types.h> does not define. */
+#undef off_t
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef pid_t
+
 /* Define to `unsigned' if <sys/types.h> does not define. */
 #undef size_t
 
 /* Define to `int' if <sys/types.h> doesn't define. */
 #undef uid_t
 
-/* Define to use TLS */
-#undef USE_TLS
-
-/* Define to mark libgcrypt being 1.1.92 or older */
-#undef OLD_GCRYPT
-
-/* Define to use new format for list */
-#undef USE_NEWLS
+/* Define as `fork' if `vfork' does not work. */
+#undef vfork
Index: configure.in
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/configure.in,v
retrieving revision 1.24
diff -u -p -b -r1.24 configure.in
--- configure.in	16 Jan 2005 11:16:24 -0000	1.24
+++ configure.in	10 Apr 2005 22:43:20 -0000
@@ -1,9 +1,9 @@
 dnl Process this file with autoconf to produce a configure script.
-dnl $Id: configure.in,v 1.24 2005/01/16 11:16:24 klas Exp $
-AC_PREREQ(2.50)
-AC_INIT(babyftpd)
+dnl $Id: configure.in,v 1.28 2005/03/30 21:10:56 klas Exp $
+AC_PREREQ(2.59)
+AC_INIT
+AC_CONFIG_SRCDIR([babyftpd.cpp])
 AC_LANG(C++)
-AC_CONFIG_SRCDIR(babyftpd.cpp)
 AC_CONFIG_HEADER(config.h)
   
 dnl Checks for programs.
@@ -22,13 +22,43 @@ dnl Checks for header files.
 AC_HEADER_DIRENT
 AC_HEADER_STAT
 AC_HEADER_TIME
-AC_CHECK_HEADERS(fcntl.h sys/time.h syslog.h unistd.h)
+AC_CHECK_HEADERS(fcntl.h sys/time.h syslog.h unistd.h arpa/inet.h)
+
+AC_C_INLINE
+AC_C_CONST
 
 dnl Checks for typedefs, structures, and compiler characteristics.
 AC_TYPE_SIZE_T
 AC_TYPE_UID_T
+AC_TYPE_MODE_T
+AC_TYPE_OFF_T
+AC_TYPE_PID_T
+AC_TYPE_SIGNAL
+
+AC_STRUCT_TM
 
 dnl Checks for library functions.
+AC_FUNC_FORK
+AC_FUNC_CLOSEDIR_VOID
+AC_FUNC_STAT
+AC_FUNC_LSTAT
+AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK
+AC_FUNC_MALLOC
+AC_FUNC_MEMCMP
+AC_FUNC_SELECT_ARGTYPES
+AC_FUNC_STRFTIME
+AC_HEADER_STDBOOL
+
+AC_CHECK_FUNCS([gethostname])
+AC_CHECK_FUNCS([inet_ntoa])
+AC_CHECK_FUNCS([localtime_r])
+AC_CHECK_FUNCS([memset])
+AC_CHECK_FUNCS([mkdir])
+AC_CHECK_FUNCS([rmdir])
+AC_CHECK_FUNCS([select])
+AC_CHECK_FUNCS([socket])
+AC_CHECK_FUNCS([strndup])
+AC_CHECK_FUNCS([strtoul])
 
 dnl babystuff
 AC_MSG_CHECKING(whether there is a cradle present)
@@ -45,23 +75,23 @@ AC_MSG_RESULT(no (wheew!))
 
 dnl Commandline enabling/disabling
 AC_ARG_ENABLE(tls,
-    [AC_HELP_STRING([--disable-tls],[Disable the use of tls for encryption.])],
+    [AS_HELP_STRING(--disable-tls,Disable the use of tls for encryption.)],
     [ if test "x$enableval" = "xyes"; then
       AC_CHECK_LIB(gnutls, gnutls_dh_params_generate2, 
-      AC_DEFINE(USE_TLS 1,,[tls enable])
+      AC_DEFINE([USE_TLS],[1],[tls enable])
       LIBS="$LIBS -lgnutls"
       )
     fi],
     AC_CHECK_LIB(gnutls, gnutls_dh_params_generate2,
-    AC_DEFINE(USE_TLS 1,,[tls enable])
+    AC_DEFINE([USE_TLS],[1],[tls enable])
     LIBS="$LIBS -lgnutls"
     )
 )
 
 AC_ARG_ENABLE(newls,
-    [AC_HELP_STRING([--enable-newls],[Enable use of new format for list (yyyy-mm-dd hh:mm).])],
+    [AS_HELP_STRING(--enable-newls,Enable use of new format for list (yyyy-mm-dd hh:mm).)],
     [ if test "x$enableval" = "xyes"; then
-      AC_DEFINE(USE_NEWLS 1,,[newls enable])
+      AC_DEFINE([USE_NEWLS],[1],[newls enable])
       fi]
 )
 
@@ -84,7 +114,7 @@ int main(void)
 }
       ]])],[
 AC_MSG_RESULT(no)
-AC_DEFINE(OLD_GCRYPT 1,,[ Define to mark libgcrypt being 1.1.92 or older])
+AC_DEFINE([OLD_GCRYPT],[1],[ Define to mark libgcrypt being 1.1.92 or older])
 ],[AC_MSG_RESULT(yes)
 ],[])
 
@@ -96,4 +126,5 @@ else
   AC_CHECK_LIB(pam, pam_start)
 fi
 
-AC_OUTPUT(Makefile)
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
Index: generic.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/generic.h,v
retrieving revision 1.76
diff -u -p -b -r1.76 generic.h
--- generic.h	22 Jan 2005 23:06:05 -0000	1.76
+++ generic.h	10 Apr 2005 22:43:20 -0000
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////
-//  $Id: generic.h,v 1.76 2005/01/22 23:06:05 klas Exp $
+//  $Id: generic.h,v 1.92 2005/04/02 11:46:25 klas Exp $
 //
 // Usage: some defines that is important for correct inclusions of
 //        system headers, and all system header includes.
@@ -8,40 +8,42 @@
 //    LICENSE for complete information.
 /////////////////////////////////////////////////////////////////////
 
-#ifndef __GENERIC_H__
-#define __GENERIC_H__
+#ifndef GENERIC_H
+#define GENERIC_H
 
 #define __USE_GNU 1
 
 #include "config.h"
-#include <unistd.h>
 #include <string>
 #include <vector>
 #include <semaphore.h>
+#include <assert.h>
 
-#ifdef __NETINCLS__
+#ifdef NETINCLS
 #include <sys/socket.h>
 #include <arpa/inet.h>
-#endif // __NETINCLS__
+#endif // NETINCLS
 
-#if (!defined(__STRINGTOKENIZER_H__) && !defined(__CONFIGURATION_H__) && !defined(__UTILITIES_H__))
+#if (!defined(STRINGTOKENIZER_H) && !defined(CONFIGURATION_H) && \
+    !defined(UTILITIES_H) && !defined(LOG_H))
 #include <pthread.h>
-#include <cerrno>
 #include <bitset>
 #include <security/pam_appl.h>
+#include <ext/hash_map>
 #ifdef USE_TLS
 #include <gnutls/gnutls.h>
 #endif // USE_TLS
 using std::bitset;
+using __gnu_cxx::hash_map;
+using __gnu_cxx::hash;
 #endif // stuff...
 
-#ifdef __UTILITIES_H__
+#ifdef UTILITIES_H
 #include <sys/stat.h>
-#endif // __UTILITIES_H__
+#endif // UTILITIES_H
 
-#ifdef __BABYFTPD_H__
+#ifdef BABYFTPD_H
 #include <csignal>
-#include <getopt.h>
 #include <sys/stat.h>
 #include <iostream>
 using std::cout;
@@ -51,47 +53,56 @@ using std::endl;
 #include <gcrypt.h>
 #endif // OLD_GCRYPT
 #endif // USE_TLS
-#endif // __BABYFTPD_H__
+#endif // BABYFTPD_H
 
-#ifdef __HANDLER_H__
-#include <csignal>
-#include <netdb.h>
+#ifdef HANDLER_H
 #include <algorithm>
-#endif // __HANDLER_H__
+#endif // HANDLER_H
 
-#ifdef __CONFIGURATION_H__
-#include <sys/utsname.h>
+#ifdef CONFIGURATION_H
 #include <fstream>
-#include <climits>
 #include <pwd.h>
+#include <grp.h>
+#include <getopt.h>
+#include <iostream>
+using std::cout;
+using std::endl;
 using std::ifstream;
-#endif // __CONFIGURATION_H__
+#endif // CONFIGURATION_H
 
-#ifdef __COMMANDS_H__
+#ifdef COMMANDS_H
 #include <pwd.h>
 #include <sys/stat.h>
-#endif // __COMMANDS_H__
+#endif // COMMANDS_H
 
-#ifdef __COMMANDS2_CPP__
+#ifdef COMMANDS2_CPP
 #include <grp.h>
-#include <shadow.h>
-#include <crypt.h>
 #include <dirent.h>
 #include <fcntl.h>
 #include <algorithm>
-#endif // __COMMANDS2_CPP__
+#endif // COMMANDS2_CPP
 
-#ifdef __LOG_H__
+#ifdef LOG_H
 #include <syslog.h>
 #include <fstream>
 using std::ofstream;
 using std::endl;
-#endif // __LOG_H__
+#endif // LOG_H
 
-#ifdef __USER_H__
-#include <security/pam_appl.h>
-#include <security/pam_misc.h>
-#endif // __USER_H__
+#ifdef CONNECTION_H
+#ifdef USE_TLS
+#include <gnutls/x509.h>
+#endif // USE_TLS
+#endif // CONNECTION_H
+
+#ifdef USER_H
+#include <pwd.h>
+#endif // USER_H
+
+#ifdef DATA_H
+#include <fcntl.h>
+#include <unistd.h>
+#endif // DATA_H
 
 using std::string;
 using std::vector;
@@ -103,8 +114,14 @@ class Handler;
 class Commands;
 class Utilities;
 class StringTokenizer;
+class Log;
+class Connection;
+
+const int PWBUF_SIZE = 100;
 
-#define VERSION "CVS: "__DATE__ // XXX remember to change this when releasing...
+// for exit_baby()
+const int EXIT_EARLY = 1;
+const int EXIT_FULL = 2;
 
 #ifndef FTP_LOG
 #define FTP_LOG DAEMON_LOG
Index: tls.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/tls.cpp,v
retrieving revision 1.1
diff -u -p -b -r1.1 tls.cpp
--- tls.cpp	18 Jan 2005 22:44:37 -0000	1.1
+++ tls.cpp	10 Apr 2005 22:43:20 -0000
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////
-// $Id: tls.cpp,v 1.1 2005/01/18 22:44:37 klas Exp $
+// $Id: tls.cpp,v 1.2 2005/02/14 21:48:27 klas Exp $
 //
 // Shamelessly ripped code from gcrypt.h, modified for c++.
 //   Included when we have new gcrypt and need to tell it how to do
@@ -10,6 +10,7 @@
 //
 ////////////////////////////////////////////////////////////////////
 
+#define TLS_CPP
 #ifndef OLD_GCRYPT
 
 static int gcry_pthread_mutex_init (void **priv)
Index: examples/babyftpd.conf
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/examples/babyftpd.conf,v
retrieving revision 1.4
diff -u -p -b -r1.4 babyftpd.conf
--- examples/babyftpd.conf	22 Jan 2005 23:06:05 -0000	1.4
+++ examples/babyftpd.conf	10 Apr 2005 22:43:20 -0000
@@ -1,47 +1,75 @@
-# $Id: babyftpd.conf,v 1.4 2005/01/22 23:06:05 klas Exp $
+# $Id: babyftpd.conf,v 1.15 2005/04/02 10:54:03 klas Exp $
 # General section, for... general stuff
 general
 {
   # port 21 - standard ftp port.
   port 21
 
+  # port 20, standard port for outgoing data
+  data_port 20
+
   # admin email
   admin_email you@your.host
 
   # maximum number of control connections.
   connection_limit 100
 
-  # run the lifebeat() every this seconds
-  lifebeat_tick 300
-
-  #TLS stuff
-  tls_key /root/tls_key.pem
-  tls_cert /root/tls_cert.pem
-  tls_ca /root/tls_ca.pem
-  tls_crl /root/tls_crl.pem
-
-  # how often to regenerate diffie-hellman parameters (in seconds)
-  dh_regenerate 3600
-
-  # what user to run the process under?
-  user ftp
+  # run the lifebeat() every this timeunit (supported are second,
+  #  minute, hour and day)
+  # Note: with large number of connections you want a low value here.
+  lifebeat_tick 5 minutes
+
+  # what user and group to run under? (user:group)
+  user ftp:daemon
+
+  # private key for the server
+  tls_key /etc/babyftpd/tls_key.pem
+
+  # the servers cert
+  tls_cert /etc/babyftpd/tls_cert.pem
+
+  # CA file. This should contain your certificate chain. Also any ca
+  #  certificates that may authenticate clients.
+  tls_ca /etc/babyftpd/tls_ca.pem
+
+  # valid crl list, or don't use at all if your site doesn't make use of
+  #  crls.
+  tls_crl /etc/babyftpd/tls_crl.pem
+
+  # how often to regenerate diffie-hellman parameters (in timeunits)
+  #  Note: this is run inside lifebeat().
+  dh_regenerate 1 hour
+
+  # this is the path to an external program that is used to check if a 
+  #  clients cert is allowed to login as that user. is given 4 arguments:
+  #  username, host/ip ca subject, client subject. return 0 if the login 
+  #  is allowed.
+  # Note that the ca certificate in question must be present in the
+  #  tls_ca file.
+  verify_client /etc/babyftpd/verify_client.pl
 }
 
 # user section, for... user stuff
 user
 {
   # and a user general section, for... user general stuff
-  # all options for single users can be used in this section as well, they will then
-  #   affect all users. options on specific users override general options.
+  #  all options for single users can be used in this section as well, they
+  #  will then affect all users. options on specific users override general 
+  #  options.
   general
   {
     # number of control connections/user
     user_limit 5
 
-    # number of simultaneously open data connections/control connection (list & nlst excluded)
+    # number of simultaneously open data connections/control connection
+    #  (list & nlst excluded)
     sim_data 3
 
-    # encryption:
+    # idle time. after no action on the control connection for this time
+    #  the user is booted. (provided there are no active data transfers either)
+    idle 15 minutes
+
+    # encryption (allow/deny/force/force_cert):
     encryption allow
   }
 
@@ -49,7 +77,7 @@ user
   root
   {
     # this useer is not allowed to login
-    noftp 1
+    noftp true
   }
 
   berra
@@ -58,12 +86,15 @@ user
     only_home 1
 
     # this user can only use passive data transfers
-    only_passive 1
+    only_passive true
 
     user_limit 2
 
     # encryption can have several values..
     encryption deny
+
+    # how long can this user be idle? (in timeunits)
+    idle 5 minutes
   }
 }
 
@@ -73,7 +104,7 @@ log
   log_level 3
 
   # by default we want xferlogging
-  xferlog 1
+  xferlog true
 
   # xferlogging go to /var/log/xferlog
   xferlog_file /var/log/xferlog
@@ -89,4 +120,8 @@ log
 
   # thread id on all logging..
   thread_id 1
+
+  # when logging to a file of our own, what time format should be used?
+  # this example is same as syslog, see strftime(3) for variables.
+  time_format %b %e %H:%M:%S
 }
Index: examples/verify_client.pl
===================================================================
RCS file: examples/verify_client.pl
diff -N examples/verify_client.pl
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ examples/verify_client.pl	10 Apr 2005 22:43:20 -0000
@@ -0,0 +1,45 @@
+#!/usr/bin/perl -w
+# $Id: verify_client.pl,v 1.5 2005/04/09 16:03:40 klas Exp $
+
+# small example script included with babyftpd to show how you can match
+# certificates to usernames. The certificate is already validated against your
+# loaded ca's, this is just to check the mapping to user.
+
+# the daemon will give us 4 commandline parameters:
+#   username of the user attempting to login
+#   hostname or ip of the client
+#            (yes, it can be either, at the time of writing it is always ip
+#            but I mean to fix that.)
+#   dn of the ca that has issued the certificate that is used
+#   dn of the certificate
+
+# Note1: this example doesn't make use of hostname or ca dn. (args 2 and 3).
+
+# Note2: this way of matching dn -> user only allows for each dn to be used
+#        once. If you want to be able to allow a certificate to login as
+#        several users you need to come up with something more clever.
+
+use strict;
+
+(scalar(@ARGV) == 4) or die;
+
+my $user;
+my $hostname;
+my $ca_dn;
+my $user_dn;
+
+($user, $hostname, $ca_dn, $user_dn) = @ARGV;
+
+my %users =
+(
+  "C=SE,O=gablorg,CN=berra Bertil Bertilsson,UID=1001" => "berra",
+  "C=SE,O=gablorg,CN=bosse Bo Bergman,UID=1002" => "bosse",
+);
+my $exit = 1;
+
+if(defined($users{$user_dn}) && $users{$user_dn} eq $user)
+{
+  $exit = 0;
+}
+
+exit $exit;
