Index: .cvsignore
===================================================================
RCS file: .cvsignore
diff -N .cvsignore
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ .cvsignore	16 Jun 2004 14:25:15 -0000	1.2
@@ -0,0 +1,11 @@
+depend
+ChangeLog*
+Makefile
+configure
+config.status
+config.log
+config.h
+autom4te.cache
+babyftpd
+*.o
+*.swp
Index: Commands.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Commands.cpp,v
retrieving revision 1.182
retrieving revision 1.189
diff -u -p -b -r1.182 -r1.189
--- Commands.cpp	21 May 2004 23:44:31 -0000	1.182
+++ Commands.cpp	10 Jul 2004 14:14:45 -0000	1.189
@@ -13,8 +13,18 @@
 
 bool Commands::ftp_send(User *user, string message)
 {
+  int ret;
+  message += "\r\n";
   pthread_mutex_lock(&user->lock);
-  if(write(user->socket_id, (void*)message.c_str(), message.size()) < 0)
+  
+#ifdef USE_TLS
+  if(is_set(user->settings, TLS_ENABLED))
+    ret = gnutls_record_send(user->ctrl_session, (void*)message.c_str(), message.size());
+  else
+#endif // USE_TLS
+    ret = write(user->socket_id, (void*)message.c_str(), message.size());
+
+  if(ret < 0)
   {
     pthread_mutex_unlock(&user->lock);
     pthread_mutex_lock(&sys_log_lock);
@@ -41,12 +51,9 @@ bool Commands::ftp_receive(User *user, s
   
   if(!(select(max_fd, &rfd, NULL, NULL, &time)))
   {
-    string message;
-    pthread_mutex_lock(&user->lock);
-    user->logged_out = true;
-    pthread_mutex_unlock(&user->lock);
-    message = "421 Connection timed out(" + util.itos(config->get_user(user->user_name).idle) + " seconds). Disconnected.\r\n";
-    Commands::ftp_send(user, message);
+    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)
     {
       string log;
@@ -55,6 +62,9 @@ bool Commands::ftp_receive(User *user, s
       syslog(LOG_INFO, log.c_str());
       pthread_mutex_unlock(&sys_log_lock);
     }
+    pthread_mutex_lock(&user->lock);
+    user->logged_in = LOGGED_OUT;
+    pthread_mutex_unlock(&user->lock);
     close(user->socket_id);
     return(false);
   }
@@ -62,6 +72,11 @@ bool Commands::ftp_receive(User *user, s
   char *tmp_buf = new char[210];
   memset((void*)tmp_buf, '\0', 210); // making sure a variable is empty is damn important.
   
+#ifdef USE_TLS
+  if(is_set(user->settings, TLS_ENABLED))
+    gnutls_record_recv(user->ctrl_session, (void*)tmp_buf, 200);
+  else
+#endif // USE_TLS
   read(user->socket_id, (void*)tmp_buf, 200);
   message.assign(tmp_buf, (strlen(tmp_buf)));
   
@@ -74,7 +89,7 @@ bool Commands::ftp_receive(User *user, s
     // this code makes sure that we don't get stuck in an endless loop if the connection just dissapears...
     close(user->socket_id);
     pthread_mutex_lock(&user->lock);
-    user->logged_out = true;
+    user->logged_in = LOGGED_OUT;
     pthread_mutex_unlock(&user->lock);
     return(false);
   }
@@ -89,6 +104,7 @@ void *data_wrapper(void *void_data)
   extern class Commands comm;
   string type, message, log_message;
   int log_type = 0;
+  size_t read_size; // for lack of finding a good define that fits here.
   struct passwd *pwd;
   uid_t uid;
   gid_t gid;
@@ -98,6 +114,8 @@ void *data_wrapper(void *void_data)
   time.tv_sec = config->get_user(data->user->user_name).idle; // same timeout for data as control?
   time.tv_usec = 0;
 
+  pthread_setspecific(my_object, (void*)data);
+
   data->host_name = inet_ntoa(data->data_addr.sin_addr);
 
   setpwent(); // I don't like it, but we need to drop permissions again...
@@ -119,70 +137,84 @@ void *data_wrapper(void *void_data)
   //pthread_create(&data->dns_thread, NULL, dns_lookup, (void*)dns_wrap); // new thread for dns lookup.
   //pthread_detach(data->dns_thread);
   pthread_mutex_unlock(&data->data_lock);
-  if(data->trans_type == RETR_TRANS)
-  {
-    struct stat stt;
-    fstat(data->file_id, &stt);
     
-    if(!(select(data->data_id + 1, NULL, &set, NULL, &time)))
+  if(!(select(data->data_id + 1, &set, &set, NULL, &time)))
     {
-      message = "451 Data connection timed out(" + util.itos(config->get_user(data->user->user_name).idle) + " seconds).\r\n";
+    message = "451 Data connection timed out(" + util.itos(config->get_user(data->user->user_name).idle) + " seconds).";
       log_message = "User " + data->user->user_name + ", lost a data connection due to timeout.";
       log_type = LOG_INFO; 
     }
-    
-    else if(!(sendfile(data->data_id, data->file_id, &data->rest_pos, stt.st_size - data->rest_pos)))
+#ifdef USE_TLS // handshakes etc
+  else if(is_set(data->settings, TLS_INITED))
     {
-      log_message = "Sendfile failed for file: " + data->file_name + " to host " + data->host_name + " with error: %m.";
-      log_type = LOG_DEBUG;
-      message = "451 File transfer failed.\r\n";
-    }
-    else
+    int ret;
+    pthread_mutex_lock(&data->data_lock);
+    ret = gnutls_handshake(data->data_session);
+    pthread_mutex_unlock(&data->data_lock);
+    if(ret < 0) // failed handshake
     {
-      if(config->general.xferlog)
+      message = "451 Failed to negotiate TLS as was asked. Closing data connection.";
+      gnutls_perror(ret);
+    }
+    else // handshake is aok
       {
-        log_message = "File " + data->file_name + " sent to " + data->user->user_name + " from " + data->host_name + ".";
-        log_type = LOG_INFO;
+      read_size = gnutls_record_get_max_size(data->data_session);
+      pthread_mutex_lock(&data->data_lock);
+      set_bit(data->settings, TLS_ENABLED);
+      pthread_mutex_unlock(&data->data_lock);
       }
-      message = "226 Transfer complete.\r\n";
     }
+  else
+#endif // USE_TLS
+    read_size = 16384;
+  while(data->data_id && message.size() == 0)
+  {
+    char *read_buf = new char[read_size];
+    size_t size, size2;
+    if(is_set(data->settings, RETR_TRANS))
+      size2 = read(data->file_id, read_buf, read_size);
+
+#ifdef USE_TLS
+    if(is_set(data->settings, TLS_ENABLED))
+    {
+      if(is_set(data->settings, 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);
   }
-
-  else if(data->trans_type == STOR_TRANS)
-  {
-    char *read_buf = new char[READ_SIZE];
-    if(!(select(data->data_id + 1, &set, NULL, NULL, &time)))
+    else
     {
-      message = "451 Data connection timed out(" + util.itos(config->get_user(data->user->user_name).idle) + " seconds).\r\n";
-      log_message = "User " + data->user->user_name + ", lost a data connection due to timeout.";
-      log_type = LOG_INFO;
-    }
-    while(data->data_id)
+#endif
+      if(is_set(data->settings, 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
+    if(!is_set(data->settings, RETR_TRANS))
     {
-      size_t size;
-
-      size = recv(data->data_id, read_buf, READ_SIZE, MSG_WAITALL);
       if(size != 0 && (write(data->file_id, read_buf, size) == -1))
       {
         log_message = "Write failed to file: " + data->file_name + " from host: " + data->host_name + " with error %m";
         log_type = LOG_DEBUG;
-        message = "451 File transfer failed.\r\n";
-        break;
+        message = "451 File transfer failed.";
       }
-      else if(size != READ_SIZE)
+      else if(size == 0)
       {
-        if(config->general.xferlog)
-        {
-          log_message = "File " + data->file_name + " received from " + data->user->user_name + " from " + data->host_name + ".";
-          log_type = LOG_INFO;
-        }
         if(close(data->file_id) != 0) // it's only important for STOR that the closing is proper.
-          message = "550 Failed saving file.\r\n";
+          message = "550 Failed saving file.";
         else
-          message = "226 Transfer complete.\r\n";
-        break;
+          message = "226 Transfer complete.";
       }
     }
+    else // RETR_TRANS
+    {
+      if(size != size2)
+        message = "550 Transfer failed.";
+      else if(size2 != read_size)
+        message = "226 Transfer complete.";
+    }
     delete[] read_buf;
   }
   
@@ -241,3 +273,34 @@ string Commands::parsedir(User *user, st
 
   return(new_string);
 }
+
+#ifdef USE_TLS
+
+gnutls_session Commands::init_tls(void)
+{
+  gnutls_session session;
+
+  gnutls_init(&session, GNUTLS_SERVER);
+
+  /* avoid calling all the priority functions, since the defaults
+   * *     * are adequate.
+   * *         */
+  gnutls_set_default_priority(session);   
+
+  gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+  /* request client certificate if any.
+   * *     */
+  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);
+  
+  return session;
+}
+
+#endif // USE_TLS
Index: Commands.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Commands.h,v
retrieving revision 1.59
retrieving revision 1.65
diff -u -p -b -r1.59 -r1.65
--- Commands.h	20 May 2004 21:33:43 -0000	1.59
+++ Commands.h	10 Jul 2004 14:14:45 -0000	1.65
@@ -34,7 +34,6 @@ class Commands
     bool port(User*, string);
     bool quit(User*, string);
     bool list(User*, string);
-    bool type(User*, string);
     bool help(User*, string);
     bool mkd(User*, string);
     bool rmd(User*, string);
@@ -51,6 +50,11 @@ class Commands
     // functions for simple commands that don't justify an own function
     bool sim1(User*, string);
     bool sim2(User*, string);
+#ifdef USE_TLS
+    // collection of encryption commands
+    bool auth(User*, string);
+    gnutls_session init_tls(void);
+#endif // USE_TLS
 };
 
 // the extra flags we can play with...
@@ -69,17 +73,41 @@ class Commands
 #define COM_SYST 12 
 #define COM_CDUP 13
 #define COM_FEAT 14
+#define COM_TYPE 15
+#define COM_MODE 16
+#define COM_STRU 17
+#ifdef USE_TLS
+#define COM_PBSZ 50
+#define COM_PROT 51
+#define COM_CCC  52
+#endif // USE_TLS
 
 #define COM_SIZE 20 // for sim2()
 #define COM_MDTM 21
 #define COM_MLST 22
 
+#define COM_PORT 25 // port and pasv
+#define COM_EPRT 26
+#define COM_PASV 27
+#define COM_EPSV 28
+
+
+#define PROT_IPV4 1 // EPRT and EPSV
+#define PROT_IPV6 2
+
 void *data_wrapper(void*);
 
 extern class Configuration *config;
 extern class Utilities util;
+extern class Handler handler;
 extern pthread_mutex_t sys_log_lock;
+extern vector<User*> user_list;
 
-#define READ_SIZE 16386
+#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
Index: Commands2.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Commands2.cpp,v
retrieving revision 1.7
retrieving revision 1.42
diff -u -p -b -r1.7 -r1.42
--- Commands2.cpp	29 May 2004 21:14:04 -0000	1.7
+++ Commands2.cpp	10 Jul 2004 14:14:45 -0000	1.42
@@ -13,11 +13,7 @@
 
 bool Commands::quit(User *user, string argument)
 {
-  Commands::ftp_send(user, "221 Goodbye.\r\n");
-  pthread_mutex_lock(&user->lock);
-  user->logged_out = true;
-  pthread_mutex_unlock(&user->lock);
-  close(user->socket_id);
+  Commands::ftp_send(user, "221 Goodbye.");
   if(user->logged_in == LOGGED_IN)
   {
     string log;
@@ -26,6 +22,10 @@ bool Commands::quit(User *user, string a
     syslog(LOG_INFO, log.c_str());
     pthread_mutex_unlock(&sys_log_lock);
   }
+  pthread_mutex_lock(&user->lock);
+  user->logged_in = LOGGED_OUT;
+  pthread_mutex_unlock(&user->lock);
+  close(user->socket_id);
   return(true);
 }
 
@@ -33,15 +33,25 @@ bool Commands::user(User *user, string u
 {
   if(user->logged_in == LOGGED_IN)
   {
-    Commands::ftp_send(user, "503 You are already logged in!\r\n");
+    Commands::ftp_send(user, "503 You are already logged in!");
+    return(false);
+  }
+
+  int current_connections = 0;
+  vector<User*>::iterator user_it;
+  for(user_it = user_list.begin(); user_it != user_list.end(); user_it++){
+    if((*user_it)->user_name == user_name)
+      current_connections++;
+  }
+  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);
   }
 
   pthread_mutex_lock(&user->lock);
   user->user_name = user_name;
   pthread_mutex_unlock(&user->lock);
-  string message = "331 Password required for " + user_name + ".\r\n";
-  Commands::ftp_send(user, message);
+  Commands::ftp_send(user, "331 Password required for " + user_name + ".");
   pthread_mutex_lock(&user->lock);
   user->logged_in = USER_SUPPLIED;
   pthread_mutex_unlock(&user->lock);
@@ -51,7 +61,7 @@ bool Commands::user(User *user, string u
 
 bool Commands::abor(User *user, string argument) // FIXME please?
 {
-  ftp_send(user, "502 Command not implemented.\r\n");
+  ftp_send(user, "502 Command not implemented.");
   return(true);
 }
 
@@ -64,10 +74,13 @@ bool Commands::pass(User *user, string p
   // before everything else, lets check if it is really time for pass...
   if(user->logged_in != USER_SUPPLIED)
   {
+    string message;
     if(user->logged_in == NOT_LOGGED_IN)
-      Commands::ftp_send(user, "503 Login with 'USER' first.\r\n");
+      message = "503 Login with 'USER' first.";
     else if(user->logged_in == LOGGED_IN)
-      Commands::ftp_send(user, "503 You are already logged in!\r\n");
+      message = "503 You are already logged in!";
+
+    ftp_send(user, message);
     return(false);
   }
 
@@ -75,11 +88,24 @@ bool Commands::pass(User *user, string p
   shadow = getspnam(user->user_name.c_str()); // find the user...
   if(!shadow || config->get_user(user->user_name).noftp) // if user doesn't exist or isnt' allowed to login.
   {
-    Commands::ftp_send(user, "530 Login denied.\r\n");
+    struct timeval times;
+    string message;
+    times.tv_sec = 5;
+    times.tv_usec = 0;
+    select(0, NULL, NULL, NULL, &times);
     pthread_mutex_lock(&user->lock);
     user->logged_in = NOT_LOGGED_IN;
+    user->failed_logins++;
+    if(user->failed_logins > 2)
+    {
+      user->logged_in = LOGGED_OUT;
+      message = "421 To many failed logins. Disconnecting.";
+    }
+    else
+      message = "530 Login denied.";
     pthread_mutex_unlock(&user->lock);
     endspent(); // close the shadow database...
+    ftp_send(user, message);
     return(false);
   }
   salt = shadow->sp_pwdp; // set the salt...
@@ -88,12 +114,25 @@ bool Commands::pass(User *user, string p
   endspent(); // close the shadow database
   if(passwd != pass) // no match...
   {
-    string log;
-    Commands::ftp_send(user, "530 Login denied.\r\n");
+    string log, message;
+    struct timeval times;
+    times.tv_sec = 5;
+    times.tv_usec = 0;
+    select(0, NULL, NULL, NULL, &times);
     pthread_mutex_lock(&user->lock);
     user->logged_in = NOT_LOGGED_IN;
+    user->failed_logins++;
+    if(user->failed_logins > 2)
+    {
+      user->logged_in = LOGGED_OUT;
+      message = "421 To many failed logins. Disconnecting.";
+    }
+    else
+      message = "530 Login denied.";
     pthread_mutex_unlock(&user->lock);
 
+    ftp_send(user, message);
+
     log = "Failed login attempt from ";
     if(user->host_name.size() > 0)
       log.append(user->host_name);
@@ -112,7 +151,7 @@ bool Commands::pass(User *user, string p
     string log;
     uid_t uid;
     gid_t gid;
-    Commands::ftp_send(user, "230 Password accepted, proceeding.\r\n");
+    Commands::ftp_send(user, "230 Password accepted, proceeding.");
     pthread_mutex_lock(&user->lock);
     user->logged_in = LOGGED_IN;
     pthread_mutex_unlock(&user->lock);
@@ -138,83 +177,45 @@ bool Commands::pass(User *user, string p
 
 bool Commands::help(User *user, string argument)
 {
+  string message;
   if(argument == "")
     {
-      string message;
-      message = "214-The following commands are understood (* == command unimplemented):\r\n";
-      message += "USER    PASS    RETR    STOR    PORT    QUIT    LIST    TYPE\r\n";
-      message += "ABOR*   HELP    CWD     PWD     MKD     SYST    CDUP    RMD\r\n";
-      message += "NOOP    DELE    NLST    PASV    REST    APPE    RNFR    RNTO\r\n";
-      message += "SIZE    MDTM    FEAT    MLST    MLSD    OPTS\r\n";
-      message += "214 if you have any trouble please contact the administrator at " + config->general.admin_email + ".\r\n";
-      Commands::ftp_send(user, message);
+    int cmd = 0;
+    message = "214-The following commands are understood:\r\n ";
+    while(handler.cmd_info[cmd].command != "\n")
+    {
+      if(cmd != 0 && cmd % 8 == 0)
+        message += "\r\n ";
+      if(handler.cmd_info[cmd].hlp_info != NULL)
+        message += handler.cmd_info[cmd].command + "\t";
+      cmd++;
+    }
+    message += "\r\n214 if you have any trouble please contact the administrator at " + config->general.admin_email + ".";
     }
   else
   {
     // other help stuff here...
+    int cmd;
+    message  = "214 Syntax: ";
     util.upper(argument);
-    if(argument == "USER")
-      ftp_send(user, "214 Syntax: USER <username> (send username)\r\n");
-    else if(argument == "PASS")
-      ftp_send(user, "214 Syntax: PASS <password> (send password)\r\n");
-    else if(argument == "QUIT")
-      ftp_send(user, "214 Syntax: QUIT (logout)\r\n");
-    else if(argument == "HELP")
-      ftp_send(user, "214 Syntax: HELP [<command> (returns help for specified command)] (shows available commands)\r\n");
-    else if(argument == "CWD")
-      ftp_send(user, "214 Syntax: CWD <path> (changes current working directory)\r\n");
-    else if(argument == "PWD")
-      ftp_send(user, "214 Syntax: PWD (returns current working directory)\r\n");
-    else if(argument == "MKD")
-      ftp_send(user, "214 Syntax: MKD <dir> (creates directory)\r\n");
-    else if(argument == "RMD")
-      ftp_send(user, "214 Syntax: RMD <dir> (removes directory)\r\n");
-    else if(argument == "CDUP")
-      ftp_send(user, "214 Syntax: CDUP (changes current working directory to parent directory)\r\n");
-    else if(argument == "NOOP")
-      ftp_send(user, "214 Syntax: NOOP (no operation)\r\n");
-    else if(argument == "DELE")
-      ftp_send(user, "214 Syntax: DELE <file> (delete file)\r\n");
-    else if(argument == "PORT")
-      ftp_send(user, "214 Syntax: PORT <a1,a2,a3,a4,p1,p2> (specify client data port)\r\n");
-    else if(argument == "LIST")
-      ftp_send(user, "214 Syntax: LIST <path> (lists a directory or file(s))\r\n");
-    else if(argument == "NLIST")
-      ftp_send(user, "214 Syntax: NLST <path> (lists only names of a directory or file(s))\r\n");
-    else if(argument == "TYPE")
-      ftp_send(user, "214 Syntax: TYPE <type> (sets the transfer type for future transfers)\r\n");
-    else if(argument == "RETR")
-      ftp_send(user, "214 Syntax: RETR <file> (sends a file over a data connection)\r\n");
-    else if(argument == "STOR")
-      ftp_send(user, "214 Syntax: STOR <file> (receives a file over a data connection)\r\n");
-    else if(argument == "PASV")
-      ftp_send(user, "214 Syntax: PASV (returns a non standard data port for transfers)\r\n");
-    else if(argument == "SIZE")
-      ftp_send(user, "214 Syntax: SIZE <file> (returns the exact number of octets <file> occupies).\r\n");
-    else if(argument == "MDTM")
-      ftp_send(user, "214 Syntax: MDTM <file> (returns modification time of <file>).\r\n");
-    else if(argument == "FEAT")
-      ftp_send(user, "214 Syntax: FEAT (returns available extensions).\r\n");
-    else if(argument == "MLST")
-      ftp_send(user, "214 Syntax: MLST <file> (returns file info for machine parsing).\r\n");
-    else if(argument == "MLSD")
-      ftp_send(user, "214 Syntax: MLSD <dir> (returns file listing for machine parsing).\r\n");
-    else if(argument == "REST")
-      ftp_send(user, "214 Syntax: REST <integer> (Sets the file pointer for the next RETR/STOR command to <value> (bytes)).\r\n");
-    else if(argument == "APPE")
-      ftp_send(user, "214 Syntax: APPE <file> (appends to file over data connection).\r\n");
-    else if(argument == "OPTS")
-      ftp_send(user, "214 Syntax: OPTS [<command> <options> modifies options for specified command] shows what commands options are available for.\r\n");
-    else
-      ftp_send(user, "502 Unknown command '" + argument + "'\r\n");
+    for(cmd = 0; handler.cmd_info[cmd].command != "\n"; cmd++)
+      if(handler.cmd_info[cmd].command == argument && handler.cmd_info[cmd].hlp_info != NULL)
+      {
+        message += handler.cmd_info[cmd].command + handler.cmd_info[cmd].hlp_info;
+        break;
   }
+    
+    if(handler.cmd_info[cmd].command == "\n")
+      message = "500 No help available for '" + argument + "'";
+  }
+  ftp_send(user, message);
   return(true);
 }
 
 bool Commands::cwd(User *user, string dir)
 { 
   struct stat stt;
-  string new_dir = Commands::parsedir(user, dir);
+  string message, new_dir = Commands::parsedir(user, dir);
   
   lstat(new_dir.c_str(), &stt);
 
@@ -233,21 +234,24 @@ bool Commands::cwd(User *user, string di
   if(!opendir(new_dir.c_str()))
   {
     if(errno == EACCES)
-      Commands::ftp_send(user, "550 '" + new_dir + "' permission denied.\r\n");
+      message = "550 '" + new_dir + "' permission denied.";
     else
-      Commands::ftp_send(user, "550 '" + new_dir + "' no such file or directory.\r\n");
-    return(false);
+      message = "550 '" + new_dir + "' no such file or directory.";
   }
-  Commands::ftp_send(user, "250 'CWD' command successful.\r\n");
+  else
+  {
+    message = "250 'CWD' command successful.";
   pthread_mutex_lock(&user->lock);
   user->current_dir = new_dir;
   pthread_mutex_unlock(&user->lock);
+  }
+  ftp_send(user, message);
   return(true);
 }
 
 bool Commands::retr(User *user, string file)
 {
-  string file_name, type;
+  string file_name;
   int file_id;
   struct stat stt;
   class Data *data;
@@ -256,7 +260,7 @@ bool Commands::retr(User *user, string f
   stat(file_name.c_str(), &stt);
   if(!S_ISREG(stt.st_mode))
   {
-    ftp_send(user, "550 \"" + file + "\" is not a regular file.\r\n");
+    ftp_send(user, "550 \"" + file + "\" is not a regular file.");
     return(false);
   }
   
@@ -269,22 +273,18 @@ bool Commands::retr(User *user, string f
     pthread_mutex_unlock(&user->lock);
   }
   
-  if(user->type == 'I')
-    type = "BINARY";
-  else
-    type = "ASCII";
-  ftp_send(user, "150 Opening " + type + " mode data connection for \"" + file + "\" (" + util.itos(stt.st_size) + " bytes).\r\n");
+  ftp_send(user, "150 Opening " + (string)(is_set(user->settings, BINARY)?"BINARY":"ASCII") + " mode data connection for \"" + file + "\" (" + util.itos(stt.st_size) + " bytes).");
   
-  data = new Data(user, file_id, file_name, RETR_TRANS);
+  data = new Data(user, file_id, file_name, 1);
   if(!data->isOpen())
   {
-    ftp_send(user, "425 Can't open data connection.\r\n");
+    ftp_send(user, "425 Can't open data connection.");
     delete data;
     return(false);
   }
   else if(user->data_vec_full())
   {
-    ftp_send(user, "425 Can't open data connection.\r\n");
+    ftp_send(user, "425 Can't open data connection.");
     delete data;
     return(false);
   }
@@ -307,7 +307,7 @@ bool Commands::retr(User *user, string f
 
 bool Commands::stor(User *user, string file)
 {
-  string file_name, type;
+  string file_name;
   class Data *data;
   int file_id, com_type;
 
@@ -325,13 +325,13 @@ bool Commands::stor(User *user, string f
   {
     string message;
     if(errno == EEXIST)
-      message = "550 File " + file_name + " already exists.\r\n";
+      message = "550 File " + file_name + " already exists.";
     else if(errno == EISDIR)
-      message = "550 " + file_name + " is a directory.\r\n";
+      message = "550 " + file_name + " is a directory.";
     else if(errno == EACCES)
-      message = "550 Access to " + file_name + " denied.\r\n";
+      message = "550 Access to " + file_name + " denied.";
     else
-      message = "550 File operation failed.\r\n";
+      message = "550 File operation failed.";
     ftp_send(user, message);
     return false;
   }
@@ -344,23 +344,18 @@ bool Commands::stor(User *user, string f
     pthread_mutex_unlock(&user->lock);
   }
   
-  if(user->type == 'I')
-    type = "BINARY";
-  else
-    type = "ASCII";
-
-  ftp_send(user, "150 Opening " + type + " mode data connection for \"" + file + "\".\r\n");
+  ftp_send(user, "150 Opening " + (string)(is_set(user->settings, BINARY)?"BINARY":"ASCII") + " mode data connection for \"" + file + "\".");
 
-  data = new Data(user, file_id, file_name, STOR_TRANS);
+  data = new Data(user, file_id, file_name, 0);
   if(!data->isOpen())
   {
-    ftp_send(user, "425 Can't open data connection.\r\n");
+    ftp_send(user, "425 Can't open data connection.");
     delete data;
     return(false);
   }
   else if(user->data_vec_full())
   {
-    ftp_send(user, "425 Can't open data connection.\r\n");
+    ftp_send(user, "425 Can't open data connection.");
     delete data;
     return(false);
   }
@@ -394,10 +389,10 @@ bool Commands::rest(User *user, string a
     pthread_mutex_lock(&user->lock);
     user->rest_pos = fpos;
     pthread_mutex_unlock(&user->lock);
-    message = "350 Restarting at " + argument + ". Send STOR or RETR.\r\n";
+    message = "350 Restarting at " + argument + ". Send STOR or RETR.";
   }
   else
-    message = "501 Restart point not in numeric format.\r\n";
+    message = "501 Restart point not in numeric format.";
     
   ftp_send(user, message);
   return(true);
@@ -414,6 +409,13 @@ bool Commands::list(User *user, string a
   type = argument[0];
   argument.erase(0, 1);
 
+  if(type == COM_LONG && argument[0] == '-')
+  {
+    class StringTokenizer *token = new StringTokenizer(argument, " ", false);
+    token->nextToken();
+    argument = token->restTokens();
+    delete token;
+  }
   if(argument == "")
     argument = user->current_dir;
 
@@ -422,7 +424,7 @@ bool Commands::list(User *user, string a
 
   if(!S_ISDIR(stt.st_mode) && type == COM_MLSD)
   {  
-    ftp_send(user, "501 Argument to MLSD has to be a directory.\r\n");
+    ftp_send(user, "501 Argument to MLSD has to be a directory.");
     return false;
   }
 
@@ -430,7 +432,7 @@ bool Commands::list(User *user, string a
 
   if(!data->isOpen())
   {
-    ftp_send(user, "425 Can't open data connection.\r\n");
+    ftp_send(user, "425 Can't open data connection.");
     ret = false;
   }
   else
@@ -463,12 +465,27 @@ bool Commands::list(User *user, string a
         message.append(argument);
       message.append("\r\n");
     }   
-    ftp_send(user, "150 Opening ASCII mode data connection for file list.\r\n");
     pthread_mutex_lock(&data->data_lock);
+#ifdef USE_TLS
+    if(is_set(user->settings, 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
+        ftp_send(user, "451 Failed to negotiate TLS as was asked. Closing data connection.");
+      else
+        gnutls_record_send(data->data_session, (void*)message.c_str(), message.size());
+    }
+    else
+    {
+#endif // USE_TLS
+    ftp_send(user, "150 Opening ASCII mode data connection for file list.");
     write(data->data_id, (void*)message.c_str(), message.size());
+#ifdef USE_TLS
+    } // else to using TLS for data at all..
+#endif // USE_TLS
     close(data->data_id);
     pthread_mutex_unlock(&data->data_lock);
-    ftp_send(user, "226 Transfer complete.\r\n");
+    ftp_send(user, "226 Transfer complete.");
     ret = true;
   }
   delete data;
@@ -495,28 +512,23 @@ string Commands::list_file(User *user, s
   memset(time_string, '\0', 25);
   lstat(file_name.c_str(), &file_stat);
 
-  message += (util.parse_perms_ls(file_stat.st_mode) + ' '); // first in the list comes the files permissions
-  sprintf(nlink, "%3d ", file_stat.st_nlink);
-  message += nlink; // then we have the number of hard links
+  sprintf(nlink, "%3d ", file_stat.st_nlink); // number of hard links
 
-  if((pwd = getpwuid(file_stat.st_uid)))
-    user_name = pwd->pw_name; // for some reason we can't put pwd->pw_name directly into message.
+  if((pwd = getpwuid(file_stat.st_uid))) // username
+    user_name = pwd->pw_name;
   else
     user_name = util.itos(file_stat.st_uid);
-  message += user_name + '\t'; // then owner
   
-  if((grp = getgrgid(file_stat.st_gid)))
+  if((grp = getgrgid(file_stat.st_gid))) // groupname
     group_name = grp->gr_name;
   else
     group_name = util.itos(file_stat.st_gid);
-  message += group_name + '\t'; // and group...
 
-  sprintf(size, "%9d ", (int)file_stat.st_size);
-  message += size; // and file size
+  sprintf(size, "%9d ", (int)file_stat.st_size); // filesize
 
-  strftime(time_string, 24, "%b %e %R ", localtime(&file_stat.st_mtime));
-  message += time_string; // modification time
-  message += file;
+  strftime(time_string, 24, "%b %e %R ", localtime(&file_stat.st_mtime)); // modification time
+
+  message += (util.parse_perms_ls(file_stat.st_mode) + ' ') + nlink + user_name + '\t' + group_name + '\t' + size + time_string + file; // parsing everything on one line is neater.
  
   // if it's a link...
   if(S_ISLNK(file_stat.st_mode))
@@ -547,7 +559,7 @@ string Commands::mlsx(User *user, string
   lstat(file.c_str(), &stt);
 
   // type.
-  if(user->mlst_facts[FACT_TYPE] == 1)
+  if(is_set(user->mlst_facts, FACT_TYPE))
   {
     if(S_ISREG(stt.st_mode))
       type = "file";
@@ -561,7 +573,7 @@ string Commands::mlsx(User *user, string
       else if(file == parsedir(user, user->current_dir + "/.."))
       {
         type = "pdir";
-        file += "/..";
+        //file += "/.."; // this messes the string up as it is already parsed once...
       }
       else
         type = "dir";
@@ -580,19 +592,82 @@ string Commands::mlsx(User *user, string
     message =  "Type=" + type + ";";
   }
 
-  if(user->mlst_facts[FACT_SIZE])
+  if(is_set(user->mlst_facts, FACT_SIZE))
     message += "Size=" + util.itos(stt.st_size) + ";";
-  if(user->mlst_facts[FACT_MODIFY])
+  if(is_set(user->mlst_facts, FACT_MODIFY))
     message += "Modify=" + mod_time(file_name, stt) + ";"; // mod time...
-  if(user->mlst_facts[FACT_UNIQUE])
+  if(is_set(user->mlst_facts, FACT_UNIQUE))
     message += "Unique=" + util.itos(stt.st_dev) + "g" + util.itos(stt.st_ino) + ";";
-  if(user->mlst_facts[FACT_U_MODE])
+  if(is_set(user->mlst_facts, FACT_PERM))
+    {
+      bool owner = false, group = false;
+      struct passwd *pwd;
+      struct group *grp;
+      
+      setpwent();
+      if((pwd = getpwnam(user->user_name.c_str())))
+      {
+        if(pwd->pw_uid == stt.st_uid)
+          owner = true;
+        else if(pwd->pw_gid == stt.st_gid)
+          group = true;
+      }
+      endpwent();
+
+      if(!group && !owner)
+      {
+        setgrent();
+        if((grp = getgrgid(stt.st_gid)))
+          for(int i = 0; grp->gr_mem[i] != NULL; i++)
+            if(user->user_name == grp->gr_mem[i])
+              group = true;
+        
+        endgrent();
+      }
+      
+      message += "Perm=";
+      if(owner)
+      {
+        if(S_ISDIR(stt.st_mode))
+        {
+          if(test(stt.st_mode, S_IRUSR)) message += "l";
+          if(test(stt.st_mode, S_IWUSR)) message += "cmp";
+          if(test(stt.st_mode, S_IXUSR)) message += "e";
+        }
+        if(test(stt.st_mode, S_IRUSR)) message += "r";
+        if(test(stt.st_mode, S_IWUSR)) message += "adfw";
+      }
+      else if(group)
+      {
+        if(S_ISDIR(stt.st_mode))
+        {
+          if(test(stt.st_mode, S_IRGRP)) message += "l";
+          if(test(stt.st_mode, S_IWGRP)) message += "cmp";
+          if(test(stt.st_mode, S_IXGRP)) message += "e";
+        }
+        if(test(stt.st_mode, S_IRGRP)) message += "r";
+        if(test(stt.st_mode, S_IWGRP)) message += "adfw";
+      }
+      else
+      {
+        if(S_ISDIR(stt.st_mode))
+        { 
+          if(test(stt.st_mode, S_IROTH)) message += "l";
+          if(test(stt.st_mode, S_IWOTH)) message += "cmp";
+          if(test(stt.st_mode, S_IXOTH)) message += "e";
+        }
+        if(test(stt.st_mode, S_IROTH)) message += "r";
+        if(test(stt.st_mode, S_IWOTH)) message += "adfw";
+      }
+      message += ";";
+    }
+  if(is_set(user->mlst_facts, FACT_U_MODE))
     message += "Unix.mode=" + util.parse_perms_num(stt.st_mode) + ";";
-  if(user->mlst_facts[FACT_U_UID])
+  if(is_set(user->mlst_facts, FACT_U_UID))
     message += "Unix.uid=" + util.itos(stt.st_uid) + ";";
-  if(user->mlst_facts[FACT_U_GID])
+  if(is_set(user->mlst_facts, FACT_U_GID))
     message += "Unix.gid=" + util.itos(stt.st_gid) + ";";
-  if(user->mlst_facts[FACT_U_LINK])
+  if(is_set(user->mlst_facts, FACT_U_LINK))
     message += "Unix.link=" + util.itos(stt.st_nlink) + ";";
   if(config->get_user(user->user_name).only_home)
     file.erase(0, user->root_dir.size());
@@ -601,128 +676,174 @@ string Commands::mlsx(User *user, string
   return(message);
 }
 
-bool Commands::type(User *user, string type)
-{
-  if(type == "I" || type == "i" || type == "A" || type == "a")
-  {
-    util.upper(type);
-    pthread_mutex_lock(&user->lock);
-    user->type = type[0];
-    pthread_mutex_unlock(&user->lock);
-    ftp_send(user, "200 Type set to " + type + ".\r\n");
-    return(true);
-  }
-
-  else
-  {
-    ftp_send(user, "500 'TYPE " + type + "' not understood.\r\n");
-    return(false);
-  }
-}
 
 bool Commands::mkd(User *user, string dir)
 {
-  string new_dir = parsedir(user, dir);
+  string message, new_dir = parsedir(user, dir);
 
   if(mkdir(new_dir.c_str(), 0755))
   {
+    message = "550 ";
     if(errno == EEXIST)
-      ftp_send(user, "550 File exists.\r\n");
+      message += "File exists.";
     else if(errno == EACCES)
-      ftp_send(user, "550 Write access denied.\r\n");
+      message += "Write access denied.";
     else if(errno == (ENOTDIR || ENOENT))
-      ftp_send(user, "550 Faulty pathname, directory not created.\r\n");
+      message += "Faulty pathname, directory not created.\r\n";
     else
-      ftp_send(user, "550 Directory not created.\r\n");
-    return(false);
+      message += "Directory not created.";
   }
-  ftp_send(user, "257 \"" + dir + "\" created.\r\n");
+  else
+    message = "257 \"" + dir + "\" created.\r\n";
+
+  ftp_send(user, message);
   return(true);
 }
 
 bool Commands::rmd(User *user, string dir)
 {
-  string new_dir = parsedir(user, dir);
+  string message, new_dir = parsedir(user, dir);
   
   if(rmdir(new_dir.c_str()))
   {
+    message = "550 ";
     if(errno == EACCES)
-      ftp_send(user, "550 Write access denied.\r\n");
+      message += "Write access denied.";
     else if(errno == (ENOTDIR || ENOENT))
-      ftp_send(user, "550 Faulty pathname, directory not removed.\r\n");
+      message += "Faulty pathname, directory not removed.";
     else
-      ftp_send(user, "550 Directory not removed.\r\n");
-    return(false);
+      message += "Directory not removed.";
   }
-  ftp_send(user, "250 \"" + dir + "\" removed.\r\n");
+  else
+    message = "250 \"" + dir + "\" removed.";
+
+  ftp_send(user, message);
   return(true);
 }
 
 bool Commands::dele(User *user, string file)
 {
-  string new_file = parsedir(user, file);
+  string message, new_file = parsedir(user, file);
 
   if(unlink(new_file.c_str()))
   {
+    message = "550 ";
     if(errno == (EPERM || EACCES))
-      ftp_send(user, "550 File access denied, not deleted.\r\n");
+      message = "File access denied, not deleted.";
     else if(errno == EPERM)
-      ftp_send(user, "550 File \"" + new_file + "\" is a directory.\r\n");
+      message = "File \"" + new_file + "\" is a directory.";
     else if(errno == (ENOENT || EISDIR))
-      ftp_send(user, "550 File not found.\r\n");
+      message = "File not found.";
     else
-      ftp_send(user, "450 File not available.\r\n");
+      message = "450 File not available.";
     return(false);
   }
-  ftp_send(user, "250 'DELE' command successful.\r\n");
+  else
+    message = "250 'DELE' command successful.";
+
+  ftp_send(user, message);
   return(true);
 }
 
 bool Commands::port(User *user, string argument)
 {
-  string tmp_add;
+  string tmp_adress, message;
+  int port, command = argument[0];
+  argument.erase(0, 1);
+
+  memset(&user->data_addr, 0, sizeof(user->data_addr));
+
+  if(is_set(user->settings, EPSV_ONLY))
+    message = "501 Only 'EPSV' may be user 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(command == COM_PORT)
+  {
   class StringTokenizer *token = new StringTokenizer(argument, ",", false);
   
   if(token->countTokens() != 6)
+      message = "501 Illegal 'PORT' command.";
+
+    else
   {
-    ftp_send(user, "501 Illegal 'PORT' command.\r\n");
+      tmp_adress = token->nextToken() + ".";  // if we do this on one line
+      tmp_adress += token->nextToken() + "."; // it builds the string from
+      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()));
+    }
     delete token;
-    return false;
   }
+  else if(command == COM_EPRT)
+  {
+    char separator[1];
+    separator[0] = (char)argument[0];
+    class StringTokenizer *token = new StringTokenizer(argument, separator, false);
   
-  memset(&user->data_addr, 0, sizeof(user->data_addr));
-
-  tmp_add = token->nextToken() + "." + token->nextToken() + "." + token->nextToken() + "." + token->nextToken();
+    if(token->countTokens() != 3 || argument[argument.size() - 1] != argument[0])
+      message = "501 Illegal 'EPRT' command.";
  
-  if((inet_aton(tmp_add.c_str(), &user->data_addr.sin_addr)) == 0)
+    else
   {
-    ftp_send(user, "501 Illegal 'PORT' command.\r\n");
+      if(token->nextToken() == util.itos(PROT_IPV4))
+      {
+        tmp_adress = token->nextToken();
+        port = htons(atoi(token->nextToken().c_str()));
+      }
+      else
+        message = "522 Network protocol not supported, use (1)";
+    }
     delete token;
-    return(false);
   }
    
+  if(message.size() == 0)
+  {
   pthread_mutex_lock(&user->lock);
-
-  user->data_addr.sin_port = (atoi((token->nextToken()).c_str()) << 8 | atoi((token->nextToken()).c_str()));
-
+    if((inet_aton(tmp_adress.c_str(), &user->data_addr.sin_addr)) == 0)
+    {
+      message = "501 Illegal '";
+      message += (command == COM_PORT ? "PORT" : "EPRT");
+      message += "' command.";
+    }
+    else
+    {
+      user->data_addr.sin_port = port;
   user->data_addr.sin_family = AF_INET;
-  user->passive = false;
+      remove_bit(user->settings, PASSIVE);
+      message += "200 '";
+      message += (command == COM_PORT ? "PORT" : "EPRT");
+      message += "' command successful.";
+    }
   pthread_mutex_unlock(&user->lock);
+  }
 
-  delete token;
-  ftp_send(user, "200 'PORT' command successful.\r\n");
-
+  ftp_send(user, message);
   return(true);
 }
 
-bool Commands::pasv(User *user, string empty)
+bool Commands::pasv(User *user, string argument)
 {
-  class StringTokenizer *token;
-  string ip_addr[5], message;
+  string message;
   struct sockaddr_in tmp_addr, tmp_addr2;
   socklen_t len, len2;
   unsigned int p;
+  int command = argument[0];
+
+  argument.erase(0, 1);
 
+  util.upper(argument);
+  if(argument == "ALL" && command == COM_EPSV)
+  {
+    pthread_mutex_lock(&user->lock);
+    set_bit(user->settings, EPSV_ONLY);
+    pthread_mutex_unlock(&user->lock);
+    message = "200 Now only accepting 'EPSV'";
+  }
+  else
+  {
   len = sizeof(tmp_addr);
   len2 = sizeof(tmp_addr2);
 
@@ -733,6 +854,7 @@ bool Commands::pasv(User *user, string e
   pthread_mutex_lock(&user->lock);
   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;
@@ -740,9 +862,21 @@ bool Commands::pasv(User *user, string e
   listen(user->data_id, 1);
   getsockname(user->data_id, (struct sockaddr*)&tmp_addr, &len);
   getsockname(user->socket_id, (struct sockaddr*)&tmp_addr2, &len2);
-  user->passive = true;
+    set_bit(user->settings, PASSIVE);
   pthread_mutex_unlock(&user->lock);
 
+    p = ntohs(tmp_addr.sin_port);
+
+    if(command == COM_PASV)
+    {
+      if(is_set(user->settings, EPSV_ONLY))
+        message = "501 Only 'EPSV' may be user after the issuing of 'EPSV ALL'";
+      
+      else
+      {
+        class StringTokenizer *token;
+        string ip_addr[5];
+
   ip_addr[0] = inet_ntoa(tmp_addr2.sin_addr);
   token = new StringTokenizer(ip_addr[0], ".", false);
   ip_addr[1] = token->nextToken();
@@ -751,18 +885,26 @@ bool Commands::pasv(User *user, string e
   ip_addr[4] = token->nextToken();
   delete token;
   
-  p = ntohs(tmp_addr.sin_port);
-  
   message = "227 Entering Passive Mode ("
     + ip_addr[1] + ","
     + ip_addr[2] + ","
     + ip_addr[3] + ","
     + ip_addr[4] + ","
     + util.itos((p >> 8) & 255) + ","
-    + util.itos(p & 255) + ")\r\n";
+          + util.itos(p & 255) + ").";
+      }
+    }
+    else if(command == COM_EPSV)
+    {
+      if(argument.size() > 0)
+        if(argument != util.itos(PROT_IPV4))
+          message = "522 Network protocol not supported, use (1)";
 
+      if(message.size() == 0)
+        message = "229 Entering Extended Passive Mode (|||" + util.itos(p) + "|)";
+    }
+  }
   ftp_send(user, message);
-
   return(true);
 }
 
@@ -780,26 +922,22 @@ bool Commands::rname(User *user, string 
   if(command == COM_FROM)
   {
     pthread_mutex_lock(&user->lock);
-    user->rename = 1;
     user->temp_buf = file_name;
     pthread_mutex_unlock(&user->lock);
-    message = "350 Command OK, waiting for RNTO.\r\n";
+    message = "350 Command OK, waiting for RNTO.";
   }
   else if(command == COM_TO)
   {
-    if(user->rename < 1)
-      message = "503 RNTO must come after RNFR.\r\n";
+    if(user->temp_buf.size() == 0)
+      message = "503 RNTO must come after RNFR.";
     else
     {
-      pthread_mutex_lock(&user->lock);
-      user->rename = 0;
-      pthread_mutex_unlock(&user->lock);
       if(!(rename(user->temp_buf.c_str(), file_name.c_str())))
-        message = "250 " + user->temp_buf + " renamed to " + file_name + ".\r\n";
+        message = "250 " + user->temp_buf + " renamed to " + file_name + ".";
       else if(errno == EACCES)
-        message = "550 Access denied.\r\n";
+        message = "550 Access denied.";
       else
-        message = "553 Rename failed.\r\n";
+        message = "553 Rename failed.";
     }
   }
   ftp_send(user, message);
@@ -815,15 +953,15 @@ bool Commands::opts(User *user, string a
   {
     message += "500-Recognized OPTS arguments: \r\n";
     message += " MLST\r\n";
-    message += "500 End.\r\n";
+    message += "500 End.";
   }
   else
   {
+    util.upper(argument);
     st = new StringTokenizer(argument, " ", true);
     command = st->nextToken();
     argument = st->restTokens();
     delete st;
-    util.upper(command);
   
     if(command == "MLST")
     {
@@ -831,27 +969,27 @@ bool Commands::opts(User *user, string a
       st = new StringTokenizer(argument, ";", false);
 
       pthread_mutex_lock(&user->lock);
-      do
+      while(st->hasMoreTokens())
       {
         fact = st->nextToken();
-        util.upper(fact);
-        if(fact == "TYPE") change(user->mlst_facts[FACT_TYPE]);
-        else if(fact == "SIZE") change(user->mlst_facts[FACT_SIZE]);
-        else if(fact == "MODIFY") change(user->mlst_facts[FACT_MODIFY]);
-        else if(fact == "UNIQUE") change(user->mlst_facts[FACT_UNIQUE]);
-        else if(fact == "UNIX.MODE") change(user->mlst_facts[FACT_U_MODE]);
-        else if(fact == "UNIX.UID") change(user->mlst_facts[FACT_U_UID]);
-        else if(fact == "UNIX.GID") change(user->mlst_facts[FACT_U_GID]);
-        else if(fact == "UNIX.LINK") change(user->mlst_facts[FACT_U_LINK]);
-      } while(st->hasMoreTokens());
+        if(fact == "TYPE") toggle(user->mlst_facts, FACT_TYPE);
+        else if(fact == "SIZE") toggle(user->mlst_facts, FACT_SIZE);
+        else if(fact == "MODIFY") toggle(user->mlst_facts, FACT_MODIFY);
+        else if(fact == "UNIQUE") toggle(user->mlst_facts, FACT_UNIQUE);
+        else if(fact == "PERM") toggle(user->mlst_facts, FACT_PERM);
+        else if(fact == "UNIX.MODE") toggle(user->mlst_facts, FACT_U_MODE);
+        else if(fact == "UNIX.UID") toggle(user->mlst_facts, FACT_U_UID);
+        else if(fact == "UNIX.GID") toggle(user->mlst_facts, FACT_U_GID);
+        else if(fact == "UNIX.LINK") toggle(user->mlst_facts, FACT_U_LINK);
+      };
 
       pthread_mutex_unlock(&user->lock);
 
-      message = "200 MLST string now showing: " + mlst_string(user) + "\r\n";
+      message = "200 MLS§T string now showing: " + mlst_string(user);
       delete st;
     }
     else
-      message = "501 " + command + " not known as argument to OPTS.\r\n";
+      message = "501 " + command + " not known as argument to OPTS.";
   }
 
   ftp_send(user, message);
@@ -863,14 +1001,15 @@ string Commands::mlst_string(User *user)
 {
   string message;
 
-  message += ("Type"); message += (user->mlst_facts[FACT_TYPE] == 1 ? "*;" : ";");
-  message += ("Size"); message += (user->mlst_facts[FACT_SIZE] == 1 ? "*;" : ";");
-  message += ("Modify"); message += (user->mlst_facts[FACT_MODIFY] == 1 ? "*;" : ";");
-  message += ("Unique"); message += (user->mlst_facts[FACT_UNIQUE] == 1 ? "*;" : ";");
-  message += ("Unix.mode"); message += (user->mlst_facts[FACT_U_MODE] == 1 ? "*;" : ";");
-  message += ("Unix.uid"); message += (user->mlst_facts[FACT_U_UID] == 1 ? "*;" : ";");
-  message += ("Unix.gid"); message += (user->mlst_facts[FACT_U_GID] == 1 ? "*;" : ";");
-  message += ("Unix.link"); message += (user->mlst_facts[FACT_U_LINK] == 1 ? "*;" : ";");
+  message += ("Type"); message += (is_set(user->mlst_facts, FACT_TYPE) ? "*;" : ";");
+  message += ("Size"); message += (is_set(user->mlst_facts, FACT_SIZE) ? "*;" : ";");
+  message += ("Modify"); message += (is_set(user->mlst_facts, FACT_MODIFY) ? "*;" : ";");
+  message += ("Unique"); message += (is_set(user->mlst_facts, FACT_UNIQUE) ? "*;" : ";");
+  message += ("Perm"); message += (is_set(user->mlst_facts, FACT_PERM) ? "*;" : ";");
+  message += ("Unix.mode"); message += (is_set(user->mlst_facts, FACT_U_MODE) ? "*;" : ";");
+  message += ("Unix.uid"); message += (is_set(user->mlst_facts, FACT_U_UID) ? "*;" : ";");
+  message += ("Unix.gid"); message += (is_set(user->mlst_facts, FACT_U_GID) ? "*;" : ";");
+  message += ("Unix.link"); message += (is_set(user->mlst_facts, FACT_U_LINK) ? "*;" : ";");
   return(message);
 }
 
@@ -882,9 +1021,10 @@ bool Commands::sim1(User *user, string a
   
   command = argument[0];
   argument.erase(0, 1);
+  util.upper(argument);
 
   if(command == COM_NOOP)
-    message = "200 'NOOP': Command successful.\r\n";
+    message = "200 'NOOP': Command successful.";
 
   else if(command == COM_PWD)
   {
@@ -893,18 +1033,18 @@ bool Commands::sim1(User *user, string a
       dir.erase(0, user->root_dir.size());
     if(dir == "")
       dir = "/";
-    message = "257 \"" + dir + "\" is current directory.\r\n";
+    message = "257 \"" + dir + "\" is current directory.";
   }
     
   else if(command == COM_SYST)
-    message = "215 " + config->general.os + " Type: L8\r\n";
+    message = "215 " + config->general.os + " Type: L8";
 
   else if(command == COM_CDUP)
   {
     pthread_mutex_lock(&user->lock);
     user->current_dir = parsedir(user, user->current_dir += "/..");
     pthread_mutex_unlock(&user->lock);
-    message = "200 'CDUP' command successful.\r\n";
+    message = "200 'CDUP' command successful.";
   }
 
   else if(command == COM_FEAT)
@@ -914,9 +1054,108 @@ bool Commands::sim1(User *user, string a
     message += " MDTM\r\n";
     message += " MLST " + mlst_string(user) + "\r\n";
     message += " REST STREAM\r\n";
-    message += "211 End.\r\n";
+    message += " EPRT\r\n";
+    message += " EPSV\r\n";
+#ifdef USE_TLS
+    message += " AUTH TLS\r\n";
+    message += " PBSZ\r\n";
+    message += " PROT\r\n";
+    message += " CCC\r\n";
+#endif // USE_TLS
+    message += "211 End.";
+  }
+
+  else if(command == COM_TYPE)
+  {
+    if(argument == "A" || argument == "A N")
+    {
+      message = "200 Type set to " + argument + ".";
+      pthread_mutex_lock(&user->lock);
+      remove_bit(user->settings, BINARY);
+      pthread_mutex_unlock(&user->lock);
+    }
+    else if(argument == "I" || argument == "L 8")
+    {
+      message = "200 Type set to " + argument + ".";
+      pthread_mutex_lock(&user->lock);
+      set_bit(user->settings, BINARY);
+      pthread_mutex_unlock(&user->lock);
+    }
+    else
+      message = "504 'TYPE " + argument + "' not understood.";
+  }
+
+  else if(command == COM_MODE)
+  {
+    if(argument == "S")
+    {
+      message = "200 Stream MODE enabled.";
+    }
+    else
+      message = "504 'MODE " + argument + "' not understood.";
+  }
+
+  else if(command == COM_STRU)
+  {
+    if(argument == "F")
+      message = "200 Structure set to F.";
+    else
+      message = "504 'STRU " + argument + "' not understood.";
+  }
+#ifdef USE_TLS
+  else if(command == COM_PBSZ)
+  {
+    if(is_set(user->settings, TLS_ENABLED))
+    {
+      if(argument == "0")
+      {
+        message = "200 PBSZ set for stream transfer.";
+        set_bit(user->settings, TLS_PBSZ);
+      }
+      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.";
+  }
+
+  else if(command == COM_PROT)
+  {
+    if(!is_set(user->settings, TLS_ENABLED) || !is_set(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.";
+          remove_bit(user->settings, TLS_PROT_D);
+          break;
+        case 'P':
+          message = "200 Data connection set to PRIVATE level.";
+          set_bit(user->settings, TLS_PROT_D);
+          break;
+        case 'S':
+        case 'E':
+          message = "536 Only CLEAR and PRIVATE level is available with TLS.";
+          break;
+        default:
+          message = "504 Protection level '" + argument + "' not understood.";
+      }
+  }
+  else if(command == COM_CCC) // Clear Command Channel
+  {
+    if(!is_set(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);
+      remove_bit(user->settings, TLS_ENABLED);
+    }
   }
+#endif // USE_TLS
 
+  if(message.size() > 0)
   ftp_send(user, message);
   return(true);
 }
@@ -934,20 +1173,20 @@ bool Commands::sim2(User *user, string a
   if(!(stat(file_name.c_str(), &stt)))
   {
     if(command == COM_SIZE)
-      message = "213 " + util.itos(stt.st_size) + "\r\n";
+      message = "213 " + util.itos(stt.st_size);
 
     else if(command == COM_MDTM)
-      message = "213 " + mod_time(file_name, stt) + "\r\n";
+      message = "213 " + mod_time(file_name, stt);
 
     else if(command == COM_MLST)
     {
       message  = "250-Listing " + argument + "\r\n";
       message += mlsx(user, file_name) + "\r\n";
-      message += "250 End.\r\n";
+      message += "250 End.";
     }
   }
   else
-    message = "550 \"" + argument + "\": No such file or directory.\r\n";
+    message = "550 \"" + argument + "\": No such file or directory.";
 
   ftp_send(user, message);
   return(true);
@@ -965,3 +1204,44 @@ string Commands::mod_time(string file_na
   delete[] date;
   return(message);
 }
+
+#ifdef USE_TLS
+
+bool Commands::auth(User *user, string argument)
+{
+  int ret;
+  string message;
+  
+  util.upper(argument);
+  
+  if(argument == "TLS" || argument == "TLS-C")
+  {
+    pthread_mutex_lock(&user->lock);
+    user->ctrl_session = Commands::init_tls();
+    set_bit(user->settings, TLS_INITED);
+
+    gnutls_transport_set_ptr(user->ctrl_session, (gnutls_transport_ptr)user->socket_id);
+    pthread_mutex_unlock(&user->lock);
+    ftp_send(user, "234 Now starting encryption using TLS.");
+    pthread_mutex_lock(&user->lock);
+    ret = gnutls_handshake(user->ctrl_session);
+
+    if(ret < 0) // we failed =(
+    {
+      user->logged_in = LOGGED_OUT;
+      pthread_mutex_unlock(&user->lock);
+      cout << gnutls_strerror(ret);
+      ftp_send(user, "421 Handshake failed. Disconnecting.");
+    }
+    else // wee!
+    {
+      set_bit(user->settings, TLS_ENABLED);
+      pthread_mutex_unlock(&user->lock);
+    }
+  }
+  else
+    ftp_send(user, "504 Sorry, I don't understand the mechanism '" + argument + "'.");
+
+  return true;
+}
+#endif // USE_TLS
Index: Configuration.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Configuration.cpp,v
retrieving revision 1.36
retrieving revision 1.38
diff -u -p -b -r1.36 -r1.38
--- Configuration.cpp	23 May 2004 02:14:31 -0000	1.36
+++ Configuration.cpp	10 Jul 2004 14:14:45 -0000	1.38
@@ -26,6 +26,12 @@ Configuration::Configuration()
   general.welcome_msg += tmp;
   general.welcome_msg += "]";
   delete[] tmp;
+#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";
+#endif	
   
   // 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";
@@ -33,6 +39,7 @@ Configuration::Configuration()
   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
 }
 
@@ -81,6 +88,7 @@ void Configuration::set_users(struct use
   usr.idle = general_user.idle;
   usr.noftp = general_user.noftp;
   usr.only_home = general_user.only_home;
+  usr.only_passive = general_user.only_passive;
   usr.user_limit = general_user.user_limit;
   
   class StringTokenizer *var = new StringTokenizer(variables, "\n", false);
@@ -101,12 +109,14 @@ void Configuration::set_users(struct use
     else if(op.compare(users_op[3]) == 0)
       usr.only_home = atoi(arg.c_str());
     else if(op.compare(users_op[4]) == 0)
+      usr.only_passive = atoi(arg.c_str());
+    else if(op.compare(users_op[5]) == 0)
       usr.user_limit = atoi(arg.c_str());
     delete st;
   }
   delete var;
   
-  cout << "configuration for: " << usr.id << " sim_data=" << usr.sim_data << " idle=" << usr.idle << " noftp=" << usr.noftp << " only_home=" << usr.only_home << " user_limit=" << usr.user_limit << endl;
+  cout << "configuration for: " << usr.id << " sim_data=" << usr.sim_data << " idle=" << usr.idle << " noftp=" << usr.noftp << " only_home=" << usr.only_home << " only_passive=" << usr.only_passive << " user_limit=" << usr.user_limit << endl;
 }
 
 struct users_stru &Configuration::get_user(string name)
@@ -178,6 +188,16 @@ void Configuration::set_general(struct g
       gen.welcome_msg = arg.c_str();
     else if(op.compare(general_op[9]) == 0)
       gen.xferlog_file = arg.c_str();
+#ifdef USE_TLS
+    else if(op.compare(general_op[10]) == 0)
+      gen.tls_key = arg.c_str();
+    else if(op.compare(general_op[11]) == 0)
+      gen.tls_cert = arg.c_str();
+    else if(op.compare(general_op[12]) == 0)
+      gen.tls_ca = arg.c_str();
+    else if(op.compare(general_op[13]) == 0)
+      gen.tls_crl = arg.c_str();
+#endif
     delete st;
   }
   delete var;
Index: Configuration.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Configuration.h,v
retrieving revision 1.19
retrieving revision 1.21
diff -u -p -b -r1.19 -r1.21
--- Configuration.h	23 May 2004 02:14:31 -0000	1.19
+++ Configuration.h	10 Jul 2004 14:14:45 -0000	1.21
@@ -13,6 +13,7 @@ struct users_stru
   int idle;
   int noftp;
   int only_home;
+  int only_passive;
   int user_limit;
 };
 
@@ -30,6 +31,12 @@ struct general_stru
   string admin_email;
   string welcome_msg;
   string xferlog_file;
+#ifdef USE_TLS  
+  string tls_key;
+  string tls_cert;
+  string tls_ca;
+  string tls_crl;
+#endif
 };
 
 class Configuration
@@ -55,7 +62,11 @@ class Configuration
     ~Configuration(void);
 };
 
+#ifdef USE_TLS
+const string general_op[14] = 
+#else
 const string general_op[10] = 
+#endif // USE_TLS
 {
   "connection_limit",
   "port", 
@@ -67,14 +78,21 @@ const string general_op[10] = 
   "admin_email", 
   "welcome_msg",
   "xferlog_file"
+#ifdef USE_TLS
+  ,"tls_key",
+  "tls_cert",
+  "tls_ca",
+  "tls_crl"
+#endif
 };
 
-const string users_op[5] =
+const string users_op[6] =
 {
   "sim_data", 
   "idle",
   "noftp",
   "only_home",
+  "only_passive",
   "user_limit"
 };
 
Index: FIX
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Attic/FIX,v
retrieving revision 1.16
retrieving revision 1.24
diff -u -p -b -r1.16 -r1.24
--- FIX	12 May 2004 19:59:00 -0000	1.16
+++ FIX	28 Jun 2004 21:56:40 -0000	1.24
@@ -6,10 +6,6 @@
 
 * Implement SMNT command -jl 2003-01-12
 
-* Implement MODE command -jl 2003-01-12
-
-* Implement STRU command -jl 2003-01-12
-
 * Implement ALLO command -jl 2003-01-12
 
 * Implement STOU command -jl 2003-01-12
@@ -24,8 +20,10 @@
 
 * load conffile before command line so command line overrides... OTOH, we want to be able to specify conffile on commandline... a bit like moment 22... -kl 2003-05-15
 
-* Add checks to timeout code so we don't destroy a idling control connection if there is data action.
+* implement correct xferlog behaviour (http://aolserver.am.net/docs/2.3/ftp-ch4.htm) -kl 2004-05-06
 
-* implement realpath() in parsedir(). should be more effective than my own parsing, and more crash safe.. -kl 2004-05-06 XXX BAD idea from man realpath(3) "Never  use this function. It is broken by design..." glob() ?
+* Implement correct behaviour for ASCII mode. (convert CR LF to CR). -kl 2004-06-13
 
-* implement correct xferlog behaviour (http://aolserver.am.net/docs/2.3/ftp-ch4.htm) -kl 2004-05-06
+* File transfers at 0 bytes fail, why? -kl 2004-06-19
+
+* Look over when and where we use parsedir(), for example a file listing uses parsedir _alot_. fix. -kl 2004-06-28
Index: Handler.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Handler.cpp,v
retrieving revision 1.105
retrieving revision 1.118
diff -u -p -b -r1.105 -r1.118
--- Handler.cpp	23 May 2004 02:14:31 -0000	1.105
+++ Handler.cpp	10 Jul 2004 14:14:45 -0000	1.118
@@ -10,20 +10,56 @@
 #define __HANDLER_CPP__
 #define __NETINCLS__
 #include "Handler.h"
+#include "commands.hlp"
 
-vector<User*>::iterator Handler::get_user_it(pthread_t thread_id)
-{
-  pthread_t curr_thread;
-  vector<User*>::iterator user_it;
-
-  curr_thread = pthread_self();
-
-  for(user_it = user_list.begin(); user_it != user_list.end(); user_it++)
-    if((pthread_equal((*user_it)->thread_id, curr_thread) != 0))
-      break;
-
-  return(user_it);
-}
+struct command_info Handler::cmd_info[] = {
+  { "SYST",  0, &Commands::sim1, COM_SYST,  HLP_SYST }, //0
+  { "HELP", -1, &Commands::help, 0,         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,  0,         HLP_CWD  },
+  { "CDUP",  0, &Commands::sim1, 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, 0,         HLP_USER }, //20
+  { "PASS",  1, &Commands::pass, 0,         HLP_PASS },
+  { "QUIT", -1, &Commands::quit, 0,         HLP_QUIT },
+  { "NOOP",  0, &Commands::sim1, COM_NOOP,  HLP_NOOP },
+  { "MDTM", -1, &Commands::sim2, COM_MDTM,  HLP_MDTM },
+  { "FEAT",  0, &Commands::sim1, COM_FEAT,  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 },
+  { "OPTS", -1, &Commands::opts, 0,         HLP_OPTS }, //30
+  { "REST",  1, &Commands::rest, 0,         HLP_REST },
+  { "ABOR",  0, &Commands::abor, 0,         NULL     }, //NULL means no help.
+  { "XCWD", -1, &Commands::cwd,  0,         cmd_info[9].hlp_info }, // as of rfc 1123
+  { "XPWD",  0, &Commands::sim1, COM_PWD,   cmd_info[8].hlp_info },
+  { "XCUP",  0, &Commands::sim1, COM_CDUP,  cmd_info[10].hlp_info }, //35
+  { "XMKD", -1, &Commands::mkd,  0,         cmd_info[11].hlp_info },
+  { "XRMD", -1, &Commands::rmd,  0,         cmd_info[12].hlp_info },
+#ifdef USE_TLS
+  { "AUTH", 1,  &Commands::auth, 0,         HLP_AUTH },
+  { "PBSZ", 1,  &Commands::sim1, COM_PBSZ,  HLP_PBSZ },
+  { "PROT", 1,  &Commands::sim1, COM_PROT,  HLP_PROT }, //40
+  { "CCC",  0,  &Commands::sim1, COM_CCC,   HLP_CCC },
+#endif // USE_TLS                
+  { "\n",    0, NULL           , 0,         NULL     } // this goes last.
+};
 
 class User *Handler::initializer(int socket_id)
 {
@@ -48,10 +84,10 @@ class User *Handler::initializer(int soc
   if((int)user_list.size() > config->general.connection_limit)
   {
     // reached maximum number of connections...
-    comm.ftp_send(user, "421 Maximum number of connections reached, please try again later.\r\n");
+    comm.ftp_send(user, "421 Maximum number of connections reached, please try again later.");
     pthread_mutex_lock(&user->lock);
     close(user->socket_id);
-    user->logged_out = true;
+    user->logged_in = LOGGED_OUT;
     pthread_mutex_unlock(&user->lock);
   }
   
@@ -61,7 +97,7 @@ class User *Handler::initializer(int soc
   pthread_create(&user->dns_thread, NULL, dns_lookup, (void*)dns_wrap);
   pthread_detach(user->dns_thread);
   pthread_mutex_unlock(&user->lock);
-  comm.ftp_send(user, "220 " + config->general.welcome_msg + "\r\n");
+  comm.ftp_send(user, "220 " + config->general.welcome_msg);
   return(user);
 }
 
@@ -83,7 +119,7 @@ bool Handler::init(User *user)
   while(true)
   {
     string command;
-    if(user->logged_out)
+    if(user->logged_in == LOGGED_OUT)
       break;
     if(comm.ftp_receive(user, command))
       Handler::interpreter(user, command);
@@ -109,56 +145,25 @@ bool Handler::interpreter(User *user, st
   else
     cout << "PASS-********" << endl;
   
-  struct Handler::command_info cmd_info[] = {
-    { "SYST", 0,     "",                       &Commands::sim1, COM_SYST },
-    { "HELP", words, words==0?"":argument,     &Commands::help, 0 },
-    { "RETR", words, words==0?"":argument,     &Commands::retr, 0 },
-    { "STOR", words, words==0?"":argument,     &Commands::stor, COM_STOR },
-    { "PORT", 1,     argument,                 &Commands::port, 0 },
-    { "PASV", 0,     "",                       &Commands::pasv, 0 },
-    { "PWD",  0,     "",                       &Commands::sim1, COM_PWD },
-    { "CWD",  words, words==0?"":argument,     &Commands::cwd,  0 },
-    { "CDUP", 0,     "",                       &Commands::sim1, COM_CDUP },
-    { "MKD",  words, words==0?"":argument,     &Commands::mkd,  0 },
-    { "RMD",  words, words==0?"":argument,     &Commands::rmd,  0 },
-    { "DELE", words, words==0?"":argument,     &Commands::dele, 0 },
-    { "LIST", words, words==0?"":argument,     &Commands::list, COM_LONG },
-    { "NLST", words, words==0?"":argument,     &Commands::list, COM_SHORT },
-    { "TYPE", 1,     argument,                 &Commands::type, 0 },
-    { "SIZE", words, words==0?"":argument,     &Commands::sim2, COM_SIZE },
-    { "USER", 1,     argument,                 &Commands::user, 0 },
-    { "PASS", 1,     argument,                 &Commands::pass, 0 },
-    { "QUIT", words, "",                       &Commands::quit, 0 },
-    { "NOOP", 0,     "",                       &Commands::sim1, COM_NOOP },
-    { "MDTM", words, words==0?"":argument,     &Commands::sim2, COM_MDTM },
-    { "FEAT", 0,     "",                       &Commands::sim1, COM_FEAT },
-    { "RNFR", words, words==0?"":argument,     &Commands::rname, COM_FROM },
-    { "RNTO", words, words==0?"":argument,     &Commands::rname, COM_TO },
-    { "MLST", words, words==0?"":argument,     &Commands::sim2, COM_MLST },
-    { "MLSD", words, words==0?"":argument,     &Commands::list, COM_MLSD },
-    { "APPE", words, words==0?"":argument,     &Commands::stor, COM_APPE },
-    { "OPTS", words, words==0?"":argument,     &Commands::opts, 0 },
-    { "REST", 1,     argument,                 &Commands::rest, 0 },
-    { "ABOR", 0,     "",                       &Commands::abor, 0 },
-    { "\n",   0,     "",                       NULL           , 0 } // this goes last.
-  };
- 
   for(cmd = 0; cmd_info[cmd].command != "\n"; cmd++)
     if(cmd_info[cmd].command == command)
       break;
     
   if(cmd_info[cmd].command == "\n")
-    comm.ftp_send(user, "500 '" + command + "' not understood.\r\n");
+    comm.ftp_send(user, "500 '" + command + "' not understood.");
   else
   {
-    if(user->logged_in < LOGGED_IN && command != "USER" && command != "HELP" && command != "PASS" && command != "QUIT" && command != "FEAT")
-      comm.ftp_send(user, "530 Login with 'USER' and 'PASS' first.\r\n");
+    if((user->temp_buf.size() > 0) && command != "RNTO")
+      user->temp_buf.erase(); // we don't want this hanging around forever.
+
+    if(user->logged_in < LOGGED_IN && command != "USER" && command != "HELP" && command != "PASS" && command != "QUIT" && command != "FEAT" && command != "AUTH" && command != "PROT" && command != "PBSZ")
+      comm.ftp_send(user, "530 Login with 'USER' and 'PASS' first.");
     else
     {
       if(words == 0 && words < cmd_info[cmd].ant_arg)
-        comm.ftp_send(user, "501 '" + command + "': requires an argument.\r\n");
-      else if(words > cmd_info[cmd].ant_arg)
-        comm.ftp_send(user, "501 '" + command + "': to many arguments to command.\r\n");
+        comm.ftp_send(user, "501 '" + command + "': requires an argument.");
+      else if(cmd_info[cmd].ant_arg != -1 && words > cmd_info[cmd].ant_arg)
+        comm.ftp_send(user, "501 '" + command + "': to many arguments to command.");
       else
       {
         if(cmd_info[cmd].extra_flag > 0)
Index: Handler.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Handler.h,v
retrieving revision 1.35
retrieving revision 1.40
diff -u -p -b -r1.35 -r1.40
--- Handler.h	20 May 2004 21:33:43 -0000	1.35
+++ Handler.h	18 Jun 2004 19:42:43 -0000	1.40
@@ -19,21 +19,22 @@
 #include "StringTokenizer.h"
 #endif
 
+struct command_info {
+  const string command;
+  const int ant_arg; //Number of arguments. -1 means variable size.
+  bool (Commands::*pf)(User*, string);
+  const int extra_flag;
+  const char *hlp_info;
+};
+
 class Handler
 {
   private:
     bool interpreter(User*, string);
-    struct command_info {
-      const string command;
-      const int ant_arg;
-      const string argument;
-      bool (Commands::*pf)(User*, string);
-      const int extra_flag;
-    };
   public:
     bool init(User*);
-    vector<User*>::iterator get_user_it(pthread_t);
     class User *initializer(int);
+    static struct command_info cmd_info[];
 };
 
 class dns_wrapper
@@ -43,9 +44,8 @@ class dns_wrapper
     string *host_name;
 };
 
-typedef int (Commands::*pf)(User*, string*);
-extern class Configuration *config;
 extern class Commands comm;
+extern class Configuration *config;
 extern bool term;
 extern bool hup;
 extern vector<User*> user_list;
Index: Log.cpp
===================================================================
RCS file: Log.cpp
diff -N Log.cpp
Index: Log.h
===================================================================
RCS file: Log.h
diff -N Log.h
Index: README
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/README,v
retrieving revision 1.5
retrieving revision 1.12
diff -u -p -b -r1.5 -r1.12
--- README	29 May 2004 21:34:48 -0000	1.5
+++ README	10 Jul 2004 14:20:19 -0000	1.12
@@ -1,15 +1,20 @@
-Babyftpd version 0.0b
+Babyftpd CVS version
 
-This is a ftp server written mostly in c++, it is threaded. One important thing is worth mentioning. It is _not_ a finished product, don't use it if you don't know what you're doing, it might break things. It has only been tested with linux2.4 and glibc2.2.
+This is a ftp server written mostly in c++, it is threaded. One important thing is worth mentioning. It is _not_ a finished product, don't use it if you don't know what you're doing, it might break things. It has only been tested with linux2.4, glibc 2.3 and gcc 3.3.
 
 As of now no install script is provided, the executable, babyftpd, can be copied into place, but really, it is not ready for production use. to get a list of switches to be used send the switch -h.
 
+Remember, if you are using the TLS support, you will probably want to change the locations where your certificate etc can be found in babyftpd.conf.
 
 links:
 http://rfc959.x42.com/
-http://www.ietf.org/internet-drafts/draft-ietf-ftpext-mlst-16.txt
 http://ftp.linuxarkivet.se/pub/rfc/authors/rfc3659.txt
 http://www.ietf.org/internet-drafts/draft-preston-ftpext-deflate-01.txt
+http://cr.yp.to/ftp.html
+http://rfc2428.x42.com/
+http://www.ietf.org/proceedings/02jul/I-D/draft-ietf-ftpext-data-connection-assurance-00.txt
+http://rfc2228.x42.com/
+http://www.ford-hutchinson.com/~fh-1-pfh/draft-murray-auth-ftp-ssl-14.txt
 
 more standards and the like will follow here..
 
Index: User.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/User.cpp,v
retrieving revision 1.38
retrieving revision 1.51
diff -u -p -b -r1.38 -r1.51
--- User.cpp	22 May 2004 12:52:47 -0000	1.38
+++ User.cpp	10 Jul 2004 14:14:45 -0000	1.51
@@ -14,32 +14,47 @@ Data::Data(User *user, int file_id, stri
   pthread_mutex_init(&data_lock, NULL);
   Data::file_id = file_id;
   Data::file_name = file_name;
-  Data::trans_type = trans_type;
   Data::data_id = user->data_id;
   Data::data_addr = user->data_addr;
+  Data::settings = user->settings;
   Data::user = user;
-  Data::open = true;
+  if(trans_type == 1)
+    set_bit(this->settings, RETR_TRANS);
+  set_bit(Data::settings, OPEN);
   Data::rest_pos = 0;
-  if(!user->passive)
+#ifdef USE_TLS
+  remove_bit(Data::settings, TLS_INITED);
+  remove_bit(Data::settings, TLS_ENABLED);
+#endif // USE_TLS
+  
+  if(!is_set(user->settings, PASSIVE))
   { 
     if(this->data_id)
       close(Data::data_id);
     this->data_id = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
     if(connect(this->data_id, (struct sockaddr*)&this->data_addr, sizeof(this->data_addr)) < 0)
-      open = false;
+      remove_bit(Data::settings, OPEN);
     
   }
   else
   { 
     socklen_t len = sizeof(this->data_addr);
     if((this->data_id = accept(this->data_id, (struct sockaddr*)&this->data_addr, &len)) < 0)
-      open = false;
+      remove_bit(Data::settings, OPEN);
+  }
+#ifdef USE_TLS
+  if(is_set(this->settings, TLS_PROT_D)) // settingup tls stuff, handshake later
+  { 
+    this->data_session = comm.init_tls();
+    set_bit(this->settings, TLS_INITED);
+    gnutls_transport_set_ptr(this->data_session, (gnutls_transport_ptr)this->data_id);
   }
+#endif //USE_TLS
 }
 
 bool Data::isOpen()
 {
-  return open;
+  return(is_set(Data::settings, OPEN));
 }
 
 Data::~Data(){
@@ -47,14 +62,21 @@ Data::~Data(){
   close(data_id);
   close(file_id);
   pthread_mutex_destroy(&data_lock);
-  if(pthread_self() != user->thread_id)
+#ifdef USE_TLS
+  // TLS cleanup here.
+  if(is_set(this->settings, TLS_INITED))
+  {
+    gnutls_db_remove_session(this->data_session);
+    gnutls_deinit(this->data_session);
+  }
+#endif
+          
+  if(!pthread_equal(pthread_self(), user->thread_id))
   {
     pthread_mutex_lock(&user->lock);
     user->data_vec.erase(user->get_data_it(pthread_self()));
     pthread_mutex_unlock(&user->lock);
-    //cout << "pthread_kill:" << pthread_kill(dns_thread, 9) << endl;
-    //cout << "pthread_join:" << pthread_join(dns_thread, NULL) << endl;
-    //cout << "thread " << thread_id << " exiting.\r\n";
+    cout << "data thread " << thread_id << " exiting. Remaining data connections for " << user->user_name << " is: " << user->data_vec.size() << endl;
     //pthread_exit(NULL); // this would cause a mem leak as the object isn't fully deleted, and the thread is calling delete data.
   }
 }
@@ -64,46 +86,65 @@ User::User(int socket_id, struct sockadd
   User::ctrl_addr = addr;
   User::socket_id = socket_id;
   User::logged_in = NOT_LOGGED_IN;
-  User::logged_out = false;
-  User::data_act = false;
-  User::type = 'A';
-  User::passive = false;
-  User::rename = 0;
   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::anonymous = 0;
+  User::failed_logins = 0;
+  User::settings = 0;
 
-  // for ability to change what we get...
-  for(int fact = 0; fact != NUM_FACTS; fact++)
-    User::mlst_facts[fact] = 1;
+  // setting the bitflags on mlst_facts... not to clean..
+  User::mlst_facts = FACT_TYPE | FACT_SIZE | FACT_MODIFY | FACT_UNIQUE | FACT_U_MODE | FACT_U_UID | FACT_U_GID | FACT_U_LINK | FACT_PERM;
 }
 
 User::~User(){
   pthread_mutex_unlock(&lock);
-  cout << "user object is:" << sizeof(this) << endl;
   close(socket_id);
-  data_vec.clear(); // this needs more refining, but I'm hunting mem now -kl.
+  
+  // killing off any ongoing data threads
+  for(vector<Data*>::iterator data_it = data_vec.begin(); data_it != data_vec.end(); data_it++)
+    pthread_kill((*data_it)->thread_id, 15);
+
+  while(data_vec.size())
+  {  }
+
+#ifdef USE_TLS
+  // TLS cleanup here.
+  if(is_set(this->settings, TLS_INITED))
+  {
+    gnutls_db_remove_session(this->ctrl_session);
+    gnutls_deinit(this->ctrl_session);
+  }
+#endif
+  
   pthread_mutex_destroy(&lock);
   pthread_mutex_lock(&user_list_mutex);
-  user_list.erase(handler.get_user_it(pthread_self()));
+  user_list.erase(get_user_it(pthread_self()));
   pthread_mutex_unlock(&user_list_mutex);
   pthread_kill(dns_thread, 9);
-  cout << "thread " << thread_id << " exiting, connected users:" << user_list.size() << ".\r\n";
+  cout << "user thread " << thread_id << " exiting, connected users:" << user_list.size() << ".\r\n";
   //pthread_exit(NULL); // We do NOT want to pthread_exit here, then the object isn't fully destroyed.
 }
 
+vector<User*>::iterator get_user_it(pthread_t thread_id)
+{ 
+  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))
+      break;
+  
+  return(user_it);
+}
+
+
 vector<Data*>::iterator User::get_data_it(pthread_t thread_id)
 {
-  pthread_t curr_thread;
   vector<Data*>::iterator data_it;
 
-  curr_thread = pthread_self();
-
-  for(data_it = data_vec.begin(); data_it != data_vec.end(); data_it++)
-    if(pthread_equal((*data_it)->thread_id, curr_thread) != 0)
+  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);
Index: User.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/User.h,v
retrieving revision 1.49
retrieving revision 1.62
diff -u -p -b -r1.49 -r1.62
--- User.h	22 May 2004 12:52:47 -0000	1.49
+++ User.h	10 Jul 2004 14:14:45 -0000	1.62
@@ -13,38 +13,52 @@
 #include "generic.h"
 #include "Configuration.h"
 #include "Handler.h"
+#include "Commands.h"
 #endif
 
 #define NOT_LOGGED_IN 0
 #define USER_SUPPLIED 1
 #define LOGGED_IN 2
+#define LOGGED_OUT 3
 
-#define RETR_TRANS 0
-#define STOR_TRANS 1
-#define LIST_TRANS 2
-
-#define NUM_FACTS 8 
-#define FACT_TYPE 0
-#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_TYPE (1 << 0) // for mlsx
+#define FACT_SIZE (1 << 1)
+#define FACT_MODIFY (1 << 2)
+#define FACT_UNIQUE (1 << 3)
+#define FACT_U_MODE (1 << 4)
+#define FACT_U_UID (1 << 5)
+#define FACT_U_GID (1 << 6)
+#define FACT_U_LINK (1 << 7)
+#define FACT_PERM (1 << 8)
+
+#define DATA_ACCT (1 << 0) // for settings
+#define BINARY (1 << 1)
+#define COMPRESSED (1 << 2)
+#define PASSIVE (1 << 3)
+#define ANONYMOUS (1 << 4)
+#define OPEN (1 << 5)
+#define EPSV_ONLY (1 << 6)
+#define RETR_TRANS (1 << 7)
+#ifdef USE_TLS
+#define TLS_ENABLED (1 << 20)
+#define TLS_INITED (1 << 21)
+#define TLS_PBSZ (1 << 22)
+#define TLS_PROT_D (1 << 23)
+#endif // USE_TLS
 
 class Data
 {
   private:
     bool open;
   public:
-    Data(User *user, int file_id = -1, string file_name = "", int trans_type = -1);
+    Data(User *user, int file_id = -1, string file_name = "", int trans_type = 0);
     ~Data();
     bool isOpen();
     int data_id;
     struct sockaddr_in data_addr;
     int trans_type;
     int file_id;
+    unsigned int settings;
     off_t rest_pos;
     pthread_mutex_t data_lock;
     pthread_t thread_id;
@@ -52,6 +66,9 @@ class Data
     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
@@ -68,27 +85,30 @@ class User
     int data_id;
     struct sockaddr_in ctrl_addr;
     struct sockaddr_in data_addr;
-    bool logged_out;
     string host_name; // NEVER modify this after dns_lookup thread is spawned. never.
     string current_dir;
     string root_dir;
-    bool data_act;
-    int rename;
-    char type;
-    bool passive;
-    string temp_buf; // is this for anything but rename?
-    int mlst_facts[NUM_FACTS];
-    int anonymous;
+    string temp_buf; // is this for anything but rename? shouldn't be the way I just changed it around -kl
+    unsigned int mlst_facts; // bit vector for mlsx
+    unsigned int settings; // bit vector for other settings
+    int failed_logins;
     off_t rest_pos;
     pthread_mutex_t lock;
     pthread_t thread_id;
     pthread_t dns_thread;
+#ifdef USE_TLS
+    gnutls_session ctrl_session;
+#endif // USE_TLS
 };
 
+vector<User*>::iterator get_user_it(pthread_t);
+
+extern pthread_key_t my_object;
 extern pthread_mutex_t user_list_mutex;
 extern pthread_t main_thread;
 extern vector<User*> user_list;
 extern class Configuration *config;
 extern class Handler handler;
+extern class Commands comm;
 
 #endif
Index: babyftpd.conf
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Attic/babyftpd.conf,v
retrieving revision 1.10
retrieving revision 1.12
diff -u -p -b -r1.10 -r1.12
--- babyftpd.conf	23 May 2004 02:14:31 -0000	1.10
+++ babyftpd.conf	10 Jul 2004 14:14:45 -0000	1.12
@@ -12,6 +12,12 @@ general          
 
   # maximum number of control connections.
   connection_limit 100
+
+  #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
 }
 
 # user section, for... user stuff
@@ -41,6 +47,9 @@ user
     # this user can only see/touch tis /her/it's home directory
     only_home 1
 
+    # this user can only use passive data transfers
+    only_passive 1
+
     user_limit 2
   }
 }
Index: babyftpd.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/babyftpd.cpp,v
retrieving revision 1.62
retrieving revision 1.71
diff -u -p -b -r1.62 -r1.71
--- babyftpd.cpp	19 May 2003 01:20:10 -0000	1.62
+++ babyftpd.cpp	10 Jul 2004 14:16:49 -0000	1.71
@@ -66,15 +66,45 @@ int main(int argc, char **argv)
       close(STDOUT_FILENO);
       close(STDERR_FILENO);
       */
+
       pthread_mutex_init(&user_list_mutex, NULL);
       pthread_mutex_init(&sys_log_lock, NULL);
 
+      pthread_key_create(&my_object, NULL);
+
       signal(SIGTERM, sig_handler);
       signal(SIGHUP, sig_handler);
       signal(SIGABRT, sig_handler);
 
       main_thread = pthread_self();
       
+#ifdef USE_TLS
+#warning "TLS ENABLED."
+      int ret;
+      gnutls_global_init();
+
+      pthread_mutex_init(&cache_lock, NULL);
+
+      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);
+      if(ret < 0)
+        gnutls_perror(ret);
+
+      ret = gnutls_certificate_set_x509_crl_file(x509_cred, config->general.tls_crl.c_str(), GNUTLS_X509_FMT_PEM);
+      if(ret < 0)
+        gnutls_perror(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);
+      if(ret < 0)
+        gnutls_perror(ret);
+      // this should be setup so it is rerun every couple of hours
+      gnutls_dh_params_init(&dh_params);
+      gnutls_dh_params_generate2(dh_params, 1024);
+
+      gnutls_certificate_set_dh_params(x509_cred, dh_params);
+
+#endif // USE_TLS
+      
       listener();
       break;
       
@@ -124,9 +154,11 @@ bool listener(void)
 static void *wrapper(void *socket_id)
 {
   class User *user;
+  pthread_setspecific(my_object, (void*)user);
+    
   user = handler.initializer((int)socket_id);
   
-  if(!user->logged_out)
+  if(user->logged_in != LOGGED_OUT)
     handler.init(user);
   
   string log = "closed connection from: ";
@@ -248,7 +280,7 @@ void parse_commandline(int argc, char **
       cout << endl;
       cout << "    (*) only hooks for these options in yet, doesn't really work." << endl;
       cout << endl;
-      cout << "Don't report bugs... fix them." << endl;
+      cout << "Please, report bugs, either directly to the developers or via sourceforge (http://www.sf.net/projects/babyftpd)." << endl;
 
       exit_baby(0);
     }
@@ -273,41 +305,53 @@ void parse_commandline(int argc, char **
 
 void sig_handler(int signal)
 {
-  vector<User*>::iterator user_it;
   switch(signal)
   {
     case SIGTERM:
       pthread_t curr_thread;
       curr_thread = pthread_self();
+
+      cout << "thread " << curr_thread << " received SIGTERM" << endl;
+      sigs.term = true;
+
+      if(pthread_equal(curr_thread, main_thread)) // the real stuff...
+      {
+        struct timeval times;
+        times.tv_sec = 1;
+        times.tv_usec = 0;
+        
       pthread_mutex_lock(&sys_log_lock);
       syslog(LOG_NOTICE, "SIGTERM received.");
       pthread_mutex_unlock(&sys_log_lock);
-      cout << "term received... in thread " << curr_thread << endl;
-      sigs.term = true;
 
-      if(curr_thread == main_thread) // the real stuff...
-      {
-        cout << "main thread" << endl;
-        while(user_list.size() != 0) // loop while there are child User threads.
-        {
-          pthread_kill(user_list[0]->thread_id, SIGTERM);
-          cout << "killed one, going to next." << endl;
-        }
+        cout << "main thread. users: " << user_list.size() << endl;
+        for(vector<User*>::iterator user_it = user_list.begin(); user_it != user_list.end(); user_it++)
+          pthread_kill((*user_it)->thread_id, SIGTERM);
+
+        select(0, NULL, NULL, NULL, &times); // wait one second before going on and exiting.
         
-        /*pthread_mutex_destroy(&user_list_mutex);
-        pthread_mutex_destroy(&sys_log_lock);
-        delete config;*/
         cout << "main thread exiting." << endl; 
         exit_baby(1);
       }
       else
       {
-        cout << "thread " << curr_thread << " received SIGTERM" << endl;
-        user_it = handler.get_user_it(curr_thread);
+        vector<User*>::iterator user_it = get_user_it(curr_thread);
+        if(*user_it != NULL) // this is a user, now let's kill it
+        {
         pthread_mutex_unlock(&(*user_it)->lock);
-        comm.ftp_send(*user_it, "421 Server shutting down, closing control connection.\r\n");
+          comm.ftp_send(*user_it, "421 Server shutting down, closing control connection.");
         delete *user_it;
       }
+        else // no? then let's assume it's a data object..
+        {
+          class Data *data;
+          data = (Data*)pthread_getspecific(my_object);
+          pthread_mutex_unlock(&data->data_lock);
+          delete data;
+        }
+        pthread_exit(NULL);
+
+      }
       break;
 
     case SIGHUP:
@@ -340,6 +384,78 @@ void exit_baby(int num)
 {
   pthread_mutex_destroy(&user_list_mutex);
   pthread_mutex_destroy(&sys_log_lock);
+  pthread_key_delete(my_object);
   delete config;
+
+#ifdef USE_TLS
+  // emptying and freeing session cache.
+  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);
+    delete(*cache_it);
+    cache_db.erase(cache_it);
+  }
+  
+  pthread_mutex_destroy(&cache_lock);
+  gnutls_certificate_free_credentials(x509_cred);
+  gnutls_global_deinit();
+#endif // USE_TLS
+
   exit(num);
 }
+
+#ifdef USE_TLS // code for session resuming
+
+int tls_db_store(void *dbf, gnutls_datum key, gnutls_datum data)
+{
+  struct cache *temp_cache = new cache;
+  
+  (void*)temp_cache->key.data = gnutls_malloc(key.size);
+  temp_cache->key.size = key.size;
+  memcpy(temp_cache->key.data, key.data, key.size);
+
+  (void*)temp_cache->data.data = gnutls_malloc(data.size);
+  temp_cache->data.size = data.size;
+  memcpy(temp_cache->data.data, data.data, data.size);
+  
+  pthread_mutex_lock(&cache_lock);
+  cache_db.push_back(temp_cache);
+  pthread_mutex_unlock(&cache_lock);
+  return(0);
+}
+
+gnutls_datum tls_db_fetch(void *dbf, gnutls_datum key)
+{
+  gnutls_datum data = { NULL, 0 };
+
+  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;
+      (void*)data.data = gnutls_malloc(data.size);
+      memcpy(data.data, (*cache_it)->data.data, data.size);
+      break;
+    }
+
+  return(data);
+}
+
+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++)
+    if(memcmp(key.data, (*cache_it)->key.data, (*cache_it)->key.size) == 0)
+    {
+      pthread_mutex_lock(&cache_lock);
+      gnutls_free((*cache_it)->key.data);
+      gnutls_free((*cache_it)->data.data);
+      delete(*cache_it);
+      cache_db.erase(cache_it);
+      pthread_mutex_unlock(&cache_lock);
+      return(0);
+    }
+        
+  return(-1);
+}
+
+#endif // USE_TLS
Index: babyftpd.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/babyftpd.h,v
retrieving revision 1.29
retrieving revision 1.31
diff -u -p -b -r1.29 -r1.31
--- babyftpd.h	20 May 2004 21:33:43 -0000	1.29
+++ babyftpd.h	10 Jul 2004 14:14:45 -0000	1.31
@@ -39,6 +39,9 @@ class Handler handler;
 class Commands comm;
 class Utilities util;
 
+// thread specific storage..
+pthread_key_t my_object = 0;
+
 pthread_mutex_t sys_log_lock;
 pthread_mutex_t user_list_mutex;
 pthread_t main_thread; // for managing on shutdown and signal handling.
@@ -46,4 +49,21 @@ vector<User*> user_list;
 
 #define LISTEN_LIMIT 5
 
+#ifdef USE_TLS
+gnutls_certificate_credentials x509_cred;
+static gnutls_dh_params dh_params;
+int tls_db_store(void*, gnutls_datum, gnutls_datum);
+gnutls_datum tls_db_fetch(void*, gnutls_datum);
+int tls_db_delete(void*, gnutls_datum);
+
+struct cache
+{
+  gnutls_datum key;
+  gnutls_datum data;
+};
+
+vector<cache*> cache_db;
+pthread_mutex_t cache_lock;
+#endif // USE_TLS
+
 #endif
Index: commands.hlp
===================================================================
RCS file: commands.hlp
diff -N commands.hlp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ commands.hlp	10 Jul 2004 14:14:45 -0000	1.3
@@ -0,0 +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)"
+#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)"
+#endif // USE_TLS
Index: config.h.in
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/config.h.in,v
retrieving revision 1.2
retrieving revision 1.6
diff -u -p -b -r1.2 -r1.6
--- config.h.in	26 Dec 2002 03:39:24 -0000	1.2
+++ config.h.in	10 Jul 2004 14:14:45 -0000	1.6
@@ -1,37 +1,79 @@
-/* config.h.in.  Generated automatically from configure.in by autoheader 2.13.  */
+/* config.h.in.  Generated from configure.in by autoheader.  */
 
-/* Define to `int' if <sys/types.h> doesn't define.  */
-#undef mode_t
-
-/* Define if you have the ANSI C header files.  */
-#undef STDC_HEADERS
-
-/* Define if you have the <dirent.h> header file.  */
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+   */
 #undef HAVE_DIRENT_H
 
-/* Define if you have the <fcntl.h> header file.  */
+/* Define to 1 if you have the <fcntl.h> header file. */
 #undef HAVE_FCNTL_H
 
-/* Define if you have the <ndir.h> header file.  */
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the `crypt' library (-lcrypt). */
+#undef HAVE_LIBCRYPT
+
+/* Define to 1 if you have the `pthread' library (-lpthread). */
+#undef HAVE_LIBPTHREAD
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
 #undef HAVE_NDIR_H
 
-/* Define if you have the <sys/dir.h> header file.  */
+/* 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 <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 <syslog.h> header file. */
+#undef HAVE_SYSLOG_H
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+   */
 #undef HAVE_SYS_DIR_H
 
-/* Define if you have the <sys/ndir.h> header file.  */
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+   */
 #undef HAVE_SYS_NDIR_H
 
-/* Define if you have the <sys/time.h> header file.  */
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/time.h> header file. */
 #undef HAVE_SYS_TIME_H
 
-/* Define if you have the <syslog.h> header file.  */
-#undef HAVE_SYSLOG_H
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
 
-/* Define if you have the <unistd.h> header file.  */
+/* Define to 1 if you have the <unistd.h> header file. */
 #undef HAVE_UNISTD_H
 
-/* Define if you have the crypt library (-lcrypt).  */
-#undef HAVE_LIBCRYPT
+/* Define to 1 if the `S_IS*' macros in <sys/stat.h> do not work properly. */
+#undef STAT_MACROS_BROKEN
 
-/* Define if you have the pthread library (-lpthread).  */
-#undef HAVE_LIBPTHREAD
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#undef TIME_WITH_SYS_TIME
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef gid_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
Index: configure.in
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/configure.in,v
retrieving revision 1.9
retrieving revision 1.13
diff -u -p -b -r1.9 -r1.13
--- configure.in	26 Dec 2002 00:41:06 -0000	1.9
+++ configure.in	10 Jul 2004 14:14:45 -0000	1.13
@@ -1,5 +1,7 @@
 dnl Process this file with autoconf to produce a configure script.
-AC_INIT(babyftpd.cpp)
+AC_PREREQ(2.57)
+AC_INIT(babyftpd)
+AC_CONFIG_SRCDIR(babyftpd.cpp)
 AC_CONFIG_HEADER(config.h)
   
 dnl Checks for programs.
@@ -24,4 +26,19 @@ AC_TYPE_UID_T
 
 dnl Checks for library functions.
 
+dnl Commandline enabling/disabling
+AC_ARG_ENABLE(tls,
+    [AC_HELP_STRING([--disable-tls],[Disable the use of tls for encryption.])],
+    [ if test "x$enableval" = "xyes"; then
+      AC_CHECK_LIB(gnutls, gnutls_init, 
+      AC_DEFINE(USE_TLS,,[tls enable])
+      LIBS="$LIBS -lgnutls"
+      )
+    fi],
+    AC_CHECK_LIB(gnutls, gnutls_init,
+    AC_DEFINE(USE_TLS,,[tls enable])
+    LIBS="$LIBS -lgnutls"
+    )
+)
+
 AC_OUTPUT(Makefile)
Index: generic.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/generic.h,v
retrieving revision 1.52
retrieving revision 1.59
diff -u -p -b -r1.52 -r1.59
--- generic.h	29 May 2004 21:50:02 -0000	1.52
+++ generic.h	10 Jul 2004 14:14:45 -0000	1.59
@@ -13,7 +13,6 @@
 #define __USE_GNU 1
 
 #include <iostream>
-#include <string>
 #include <syslog.h>
 #include "config.h"
 
@@ -26,11 +25,19 @@
 #include <pthread.h>
 #include <cerrno>
 #include <vector>
+#ifdef USE_TLS
+#include <gnutls/gnutls.h>
+#endif // USE_TLS
 #endif // stuff...
 
+#ifdef __STRINGTOKENIZER_H__
+#include <string>
+#endif // __STRINGTOKENIZER_H__
+
 #ifdef __UTILITIES_H__
 #include <cctype>
 #include <sys/stat.h>
+#include <string>
 #endif // __UTILITIES_H__
 
 #ifdef __BABYFTPD_H__
@@ -48,6 +55,7 @@
 #include <sys/utsname.h>
 #include <fstream>
 #include <climits>
+#include <string>
 #endif // __CONFIGURATION_H__
 
 #ifdef __COMMANDS_H__
@@ -55,10 +63,6 @@
 #include <sys/stat.h>
 #endif // __COMMANDS_H__
 
-#ifdef __COMMANDS_CPP__
-#include <sys/sendfile.h>
-#endif // __COMMANDS_CPP__
-
 #ifdef __COMMANDS2_CPP__
 #include <grp.h>
 #include <shadow.h>
@@ -85,5 +89,9 @@ class StringTokenizer;
 
 #define test(a, b)   (((a) & (b)) == (b))
 #define change(a)    ((a == 0) ? (a = 1) : (a = 0))
+#define is_set(flag, bit)     ((flag) & (bit))
+#define set_bit(flag, bit)    ((flag) |= (bit))
+#define remove_bit(flag, bit) ((flag) &= ~(bit))
+#define toggle(flag, bit)     ((flag) ^= (bit))
 
 #endif
