Index: .cvsignore
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/.cvsignore,v
retrieving revision 1.2
retrieving revision 1.4
diff -u -p -b -r1.2 -r1.4
--- .cvsignore	16 Jun 2004 14:25:15 -0000	1.2
+++ .cvsignore	15 Jul 2004 12:10:43 -0000	1.4
@@ -2,6 +2,7 @@ depend
 ChangeLog*
 Makefile
 configure
+config.cache
 config.status
 config.log
 config.h
@@ -9,3 +10,4 @@ autom4te.cache
 babyftpd
 *.o
 *.swp
+*.pem
Index: Commands.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Commands.cpp,v
retrieving revision 1.189
retrieving revision 1.227
diff -u -p -b -r1.189 -r1.227
--- Commands.cpp	10 Jul 2004 14:14:45 -0000	1.189
+++ Commands.cpp	16 Jan 2005 00:58:16 -0000	1.227
@@ -1,5 +1,6 @@
 /////////////////////////////////////////////////////////////////////
-//  File: Commands.cpp
+// $Id: Commands.cpp,v 1.227 2005/01/16 00:58:16 klas Exp $
+//
 // Usage: Ftp commands and data transfer functions
 //
 //  A part of babyftpd, licensed under the GNU GPL, see the file
@@ -14,25 +15,40 @@
 bool Commands::ftp_send(User *user, string message)
 {
   int ret;
-  message += "\r\n";
-  pthread_mutex_lock(&user->lock);
+  fd_set wfds;
+  struct timeval tv;
+
+  if(user->socket_id < 0)
+  {
+    user->logged_in = LOGGED_OUT;
+    return(false);
+  }
+  FD_ZERO(&wfds);
+  FD_SET(user->socket_id, &wfds);
+  tv.tv_sec = 1;
+  tv.tv_usec = 0;
   
+  sem_wait(&user->user_lock);
+  if(!select(user->socket_id + 1, NULL, &wfds, NULL, &tv))
+  {
+    user->logged_in = LOGGED_OUT;
+    sem_post(&user->user_lock);
+    return(false);
+  }
+  message += "\r\n";
 #ifdef USE_TLS
-  if(is_set(user->settings, TLS_ENABLED))
-    ret = gnutls_record_send(user->ctrl_session, (void*)message.c_str(), message.size());
+  if(user->settings.test(TLS_ENABLED))
+    ret = gnutls_record_send(user->ctrl_session, static_cast<const void*>(message.c_str()), message.size());
   else
 #endif // USE_TLS
-    ret = write(user->socket_id, (void*)message.c_str(), message.size());
+    ret = write(user->socket_id, static_cast<const void*>(message.c_str()), message.size());
 
+  sem_post(&user->user_lock);
   if(ret < 0)
   {
-    pthread_mutex_unlock(&user->lock);
-    pthread_mutex_lock(&sys_log_lock);
-    syslog(LOG_ERR, "write to socket %d failed: %m", user->socket_id);
-    pthread_mutex_unlock(&sys_log_lock);
+    logging->log_this(3, TYPE_INFO, "write to socket " + util.itos(user->socket_id) + " failed: %m"); // I think %m should work here as well.. maybe?
     return(false);
   }
-  pthread_mutex_unlock(&user->lock);
   return(true);
 }
 
@@ -47,50 +63,41 @@ bool Commands::ftp_receive(User *user, s
   // We _really_ want to pass rfd as a reference...
   FD_ZERO(&rfd);
   FD_SET(user->socket_id, &rfd);
-  int max_fd = user->socket_id + 1;
   
-  if(!(select(max_fd, &rfd, NULL, NULL, &time)))
+  if(!(select(user->socket_id + 1, &rfd, NULL, NULL, &time)))
   {
     if(user->data_vec.size())
       return(false);
     Commands::ftp_send(user, "421 Connection timed out(" + util.itos(config->get_user(user->user_name).idle) + " seconds). Disconnected.");
     if(user->logged_in == LOGGED_IN)
-    {
-      string log;
-      log = "User " + user->user_name + ", disconnected due to timeout.";
-      pthread_mutex_lock(&sys_log_lock);
-      syslog(LOG_INFO, log.c_str());
-      pthread_mutex_unlock(&sys_log_lock);
-    }
-    pthread_mutex_lock(&user->lock);
+      logging->log_this(3, TYPE_AUTH, "User " + user->user_name + ", disconnected due to timeout.");
+
+    sem_wait(&user->user_lock);
     user->logged_in = LOGGED_OUT;
-    pthread_mutex_unlock(&user->lock);
+    sem_post(&user->user_lock);
     close(user->socket_id);
     return(false);
   }
   
-  char *tmp_buf = new char[210];
-  memset((void*)tmp_buf, '\0', 210); // making sure a variable is empty is damn important.
+  char tmp_buf[210] = { 0 };
   
 #ifdef USE_TLS
-  if(is_set(user->settings, TLS_ENABLED))
-    gnutls_record_recv(user->ctrl_session, (void*)tmp_buf, 200);
+  if(user->settings.test(TLS_ENABLED))
+    gnutls_record_recv(user->ctrl_session, static_cast<void*>(tmp_buf), 200);
   else
 #endif // USE_TLS
-    read(user->socket_id, (void*)tmp_buf, 200);
+    read(user->socket_id, static_cast<void*>(tmp_buf), 200);
   message.assign(tmp_buf, (strlen(tmp_buf)));
   
-  delete[] tmp_buf;
-  
   if(message.size() == 2) // no need to try parsing just "\r\n"
     return(false);
   if(message.size() == 0)
   {
     // this code makes sure that we don't get stuck in an endless loop if the connection just dissapears...
     close(user->socket_id);
-    pthread_mutex_lock(&user->lock);
+    sem_wait(&user->user_lock);
     user->logged_in = LOGGED_OUT;
-    pthread_mutex_unlock(&user->lock);
+    sem_post(&user->user_lock);
     return(false);
   }
   else // everything is normal and we've got a string to play with
@@ -99,58 +106,52 @@ bool Commands::ftp_receive(User *user, s
 
 void *data_wrapper(void *void_data)
 {
-  class Data *data = (class Data*)void_data;
-  class dns_wrapper *dns_wrap = new dns_wrapper;
+  class Data *data = static_cast<class Data*>(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;
+  string message;
+  size_t read_size = READ_SIZE; // for lack of finding a good define that fits here.
   fd_set set; // no, it isn't a bad name, but we'll use it for both RETR and STOR
-  struct timeval time;
+  struct timeval timeout;
+  time_t start_time = time(NULL);
+  size_t trans_size = 0; // will hold the total number of sent bytes
 
-  time.tv_sec = config->get_user(data->user->user_name).idle; // same timeout for data as control?
-  time.tv_usec = 0;
+  timeout.tv_sec = 5; // same timeout for data as control? no, 5 seconds is more than enough.
+  timeout.tv_usec = 0;
 
-  pthread_setspecific(my_object, (void*)data);
+
+  pthread_setspecific(my_data, void_data);
+  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
 
   data->host_name = inet_ntoa(data->data_addr.sin_addr);
 
-  setpwent(); // I don't like it, but we need to drop permissions again...
-  pwd = getpwnam(data->user->user_name.c_str());
-  uid = pwd->pw_uid;
-  gid = pwd->pw_gid;
-  endpwent();
+  // I don't like it, but we need to drop permissions again...
+  struct passwd pwd, *pwd_ptr;
+  char pwdata[PWBUF_SIZE];
+  getpwnam_r(data->user->user_name.c_str(), &pwd, pwdata, sizeof(pwdata), &pwd_ptr);
+  uid_t uid = pwd.pw_uid;
+  gid_t gid = pwd.pw_gid;
   setuid(uid);
   setgid(gid);
 
   FD_ZERO(&set);
   FD_SET(data->data_id, &set);
   
-  pthread_mutex_lock(&data->data_lock);
+  sem_wait(&data->data_lock);
   data->thread_id = pthread_self();
-  dns_wrap->addr = data->data_addr;
-  dns_wrap->host_name = &data->host_name;
-  dns_lookup((void*)dns_wrap);
-  //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);
+  // do a dns lookup here.
+  sem_post(&data->data_lock);
 
-  if(!(select(data->data_id + 1, &set, &set, NULL, &time)))
+  if(!(select(data->data_id + 1, &set, &set, NULL, &timeout)))
   {
     message = "451 Data connection timed out(" + util.itos(config->get_user(data->user->user_name).idle) + " seconds).";
-    log_message = "User " + data->user->user_name + ", lost a data connection due to timeout.";
-    log_type = LOG_INFO;
+    logging->log_this(3, TYPE_INFO, "User " + data->user->user_name + ", lost a data connection due to timeout.");
   }
 #ifdef USE_TLS // handshakes etc
-  else if(is_set(data->settings, TLS_INITED))
+  else if(data->settings.test(TLS_INITED))
   {
-    int ret;
-    pthread_mutex_lock(&data->data_lock);
-    ret = gnutls_handshake(data->data_session);
-    pthread_mutex_unlock(&data->data_lock);
+    sem_wait(&data->data_lock);
+    int ret = gnutls_handshake(data->data_session);
+    sem_post(&data->data_lock);
     if(ret < 0) // failed handshake
     {
       message = "451 Failed to negotiate TLS as was asked. Closing data connection.";
@@ -158,26 +159,25 @@ void *data_wrapper(void *void_data)
     }
     else // handshake is aok
     {
+      // tls is picky about how much in one send/recv
       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);
+      sem_wait(&data->data_lock);
+      data->settings.set(TLS_ENABLED);
+      sem_post(&data->data_lock);
     }
   }
-  else
 #endif // USE_TLS
-    read_size = 16384;
   while(data->data_id && message.size() == 0)
   {
-    char *read_buf = new char[read_size];
+    char read_buf[read_size];
     size_t size, size2;
-    if(is_set(data->settings, RETR_TRANS))
+    if(data->settings.test(RETR_TRANS))
       size2 = read(data->file_id, read_buf, read_size);
 
 #ifdef USE_TLS
-    if(is_set(data->settings, TLS_ENABLED))
+    if(data->settings.test(TLS_ENABLED))
     {
-      if(is_set(data->settings, RETR_TRANS))
+      if(data->settings.test(RETR_TRANS))
         size = gnutls_record_send(data->data_session, read_buf, size2);
       else // STOR_TRANS
         size = gnutls_record_recv(data->data_session, read_buf, read_size);
@@ -185,19 +185,19 @@ void *data_wrapper(void *void_data)
     else
     {
 #endif
-      if(is_set(data->settings, RETR_TRANS))
+      if(data->settings.test(RETR_TRANS))
         size = write(data->data_id, read_buf, size2);
       else // STOR_STRANS
         size = recv(data->data_id, read_buf, read_size, MSG_WAITALL);
 #ifdef USE_TLS
     } // the else just before last #endif
 #endif
-    if(!is_set(data->settings, RETR_TRANS))
+    trans_size += size;
+    if(!data->settings.test(RETR_TRANS))
     {
       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;
+        logging->log_this(3, TYPE_DEBUG, "Write failed to file: " + data->file_name + " from host: " + data->host_name + " with error %m");
         message = "451 File transfer failed.";
       }
       else if(size == 0)
@@ -215,82 +215,98 @@ void *data_wrapper(void *void_data)
       else if(size2 != read_size)
         message = "226 Transfer complete.";
     }
-    delete[] read_buf;
   }
   
-  if(log_message.size() > 0)
-  {
-    pthread_mutex_lock(&sys_log_lock);
-    syslog(log_type, log_message.c_str());  
-    pthread_mutex_unlock(&sys_log_lock);
-  }
   comm.ftp_send(data->user, message);
-  delete data;
-  //pthread_exit(NULL);
-  return(NULL);
+  if(config->logger.xferlog)
+  {
+    const time_t timep = time(NULL);
+    struct tm now;
+    char time_string[30] = {0};
+
+    gmtime_r(&timep, &now);
+    strftime(time_string, 28, "%a %b %d %H:%M:%S %Y ", &now);
+
+    // let us build a string!
+    string xferlog = time_string;
+    xferlog += util.itos(time(NULL) - start_time);
+    xferlog += " " + data->host_name;
+    xferlog += " " + util.itos(trans_size);
+    xferlog += " " + data->file_name + " ";
+    xferlog += (data->settings.test(BINARY)?"b":"a");
+    xferlog += " _ "; // we don't support any special-action to flag here.. or is encryption special? have to make up a flag I guess ;)
+    xferlog += (data->settings.test(RETR_TRANS)?"o":"i");
+    xferlog += " r"; // same thing again, we only support real users so far.. 
+    xferlog += " " + data->user->user_name;
+    xferlog += " FTP"; // maybe ftps for encrypted?
+    xferlog += " 0"; // there should be more auth methods available here?
+    xferlog += " *"; // or should this be uid?
+
+    logging->log_this(1, TYPE_XFER, xferlog);
+  }
+  sem_wait(&data->user->user_lock);
+  data->user->total_transfered += trans_size;
+  sem_post(&data->user->user_lock);
+
+  pthread_exit(NULL);
 }
 
-// XXX look at using realpath() in this function, should work ALOT better.
+// parsing strings so we get something good to work with,
+// beware of this function, it is one of the most expensive
+// functions in the game...
 string Commands::parsedir(User *user, string orig_string)
 {
-  string *dir_array;
   string new_string;
-  int token_count;
 
   if(orig_string[0] != '/')
     orig_string = user->current_dir + '/' + orig_string;
       
   class StringTokenizer *token = new StringTokenizer(orig_string, "/", false);
+  vector<string> dir_array;
+  dir_array.reserve(token->countTokens());
 
-  dir_array = new string[token->countTokens()];
+  while(token->hasMoreTokens())
+  {
+    dir_array.push_back(token->nextToken());
 
-  for(token_count = 0; token->hasMoreTokens(); token_count++)
+    if(dir_array.back().size() < 3)
+    { // to save us from expensive string comparisons
+      if(dir_array.back() == "..")
   {
-    if(token_count < 0)
-      token_count = 0;
-    dir_array[token_count] = token->nextToken();
-    if(dir_array[token_count] == "..")
-      token_count -= 2;
-    else if(dir_array[token_count] == ".")
-      token_count -= 1;
+        dir_array.pop_back();
+        if(!dir_array.empty())
+          dir_array.pop_back();
   }
+      else if(dir_array.back() == ".")
+        dir_array.pop_back();
+    }
+  }
+  delete token;
 
-  if(token_count < 1)
+  if(dir_array.empty())
     new_string = '/';
   else
-  {
-    for(int i = 0; i != token_count; i++)
-      new_string += '/' + dir_array[i];
-  }
-
-  delete[] dir_array;
-  delete token;
+    for(vector<string>::iterator dir_it = dir_array.begin(); dir_it != dir_array.end(); dir_it++)
+      new_string += '/' + *dir_it;
 
-  if(config->get_user(user->user_name).only_home)
+  if(user->root_dir.size() > 1)
     if(new_string.find(user->root_dir) != 0)
-    //if(new_string.compare(0, user->root_dir.size(), user->root_dir) != 0)
       new_string = user->root_dir + new_string;
 
+
   return(new_string);
 }
 
 #ifdef USE_TLS
 
-gnutls_session Commands::init_tls(void)
+void Commands::init_tls(gnutls_session &session)
 {
-  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);
@@ -299,8 +315,49 @@ gnutls_session Commands::init_tls(void)
   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
+
+int pam_conversate(int num_msg, const struct pam_message **msg,
+    struct pam_response **resp, void *appdata_ptr)
+{
+  struct pam_response *reply = new struct pam_response[0];
+  int count = 0;
+  struct pam_data *auth_data = (static_cast<struct pam_data*>(appdata_ptr));
+
+  for(int i = 0; i < num_msg; i++)
+  {
+    switch(msg[i]->msg_style)
+    {
+      case PAM_PROMPT_ECHO_ON:
+      case PAM_PROMPT_ECHO_OFF:
+        if(count != 0)
+        {
+          struct pam_response *reply_tmp = new struct pam_response[count + 1];
+          memcpy(reply_tmp, reply, count);
+          delete[] reply;
+          reply = reply_tmp;
+        }
+        // is this really how it should be done?
+        if(msg[i]->msg_style == PAM_PROMPT_ECHO_ON)
+          reply[i].resp = strndup(auth_data->user_name.c_str(),
+              auth_data->user_name.size());
+        else
+          reply[i].resp = strndup(auth_data->password.c_str(),
+              auth_data->password.size());
+
+        reply[count++].resp_retcode = PAM_SUCCESS;
+        break;
+
+      case PAM_TEXT_INFO:
+        break;
+
+      case PAM_ERROR_MSG:
+      default:
+        delete[] reply;
+        return PAM_CONV_ERR;
+    }
+  }
+  *resp = reply;
+  return PAM_SUCCESS;
+}
Index: Commands.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Commands.h,v
retrieving revision 1.65
retrieving revision 1.80
diff -u -p -b -r1.65 -r1.80
--- Commands.h	10 Jul 2004 14:14:45 -0000	1.65
+++ Commands.h	22 Jan 2005 17:18:50 -0000	1.80
@@ -16,91 +16,102 @@
 #include "Configuration.h"
 #include "Handler.h"
 #include "StringTokenizer.h"
+#include "Log.h"
 #endif
 
 class Commands
 {
   private:
     string parsedir(User*, string);
-    string mod_time(string, struct stat);
+    string mod_time(struct stat);
     string mlsx(User*, string);
-    string list_file(User*, string);
+    string list_file(string);
     string mlst_string(User*);
   public:
-    bool user(User*, string);
-    bool pass(User*, string);
-    bool retr(User*, string);
-    bool stor(User*, string);
-    bool port(User*, string);
-    bool quit(User*, string);
-    bool list(User*, string);
-    bool help(User*, string);
-    bool mkd(User*, string);
-    bool rmd(User*, string);
-    bool cwd(User*, string);
-    bool cdup(User*, string);
-    bool dele(User*, string);
-    bool pasv(User*, string);
+    bool user(User*, string, unsigned int);
+    bool pass(User*, string, unsigned int);
+    bool retr(User*, string, unsigned int);
+    bool stor(User*, string, unsigned int);
+    bool port(User*, string, unsigned int);
+    bool quit(User*, string, unsigned int);
+    bool list(User*, string, unsigned int);
+    bool help(User*, string, unsigned int);
+    bool mkd(User*, string, unsigned int);
+    bool rmd(User*, string, unsigned int);
+    bool cwd(User*, string, unsigned int);
+    bool dele(User*, string, unsigned int);
+    bool pasv(User*, string, unsigned int);
     bool ftp_send(User*, string);
     bool ftp_receive(User*, string&);
-    bool rname(User*, string);
-    bool opts(User*, string);
-    bool rest(User*, string);
-    bool abor(User*, string);
+    bool rname(User*, string, unsigned int);
+    bool opts(User*, string, unsigned int);
+    bool rest(User*, string, unsigned int);
+    bool abor(User*, string, unsigned int);
     // functions for simple commands that don't justify an own function
-    bool sim1(User*, string);
-    bool sim2(User*, string);
+    bool sim1(User*, string, unsigned int);
+    bool sim2(User*, string, unsigned int);
+    sem_t pam_lock; // pam is not threadsafe.
 #ifdef USE_TLS
     // collection of encryption commands
-    bool auth(User*, string);
-    gnutls_session init_tls(void);
+    bool auth(User*, string, unsigned int);
+    void init_tls(gnutls_session&);
 #endif // USE_TLS
 };
 
 // the extra flags we can play with...
-#define COM_LONG 1  // for list()
-#define COM_SHORT 2 
-#define COM_MLSD 3
-
-#define COM_FROM 4 // for rname()
-#define COM_TO 5
-
-#define COM_STOR 6 // for stor()
-#define COM_APPE 7
-
-#define COM_NOOP 10 // for sim1()
-#define COM_PWD  11 
-#define COM_SYST 12 
-#define COM_CDUP 13
-#define COM_FEAT 14
-#define COM_TYPE 15
-#define COM_MODE 16
-#define COM_STRU 17
+#define COM_LONG (1 << 0)  // for list()
+#define COM_SHORT (1 << 1) 
+#define COM_MLSD (1 << 2)
+
+#define COM_FROM (1 << 0) // for rname()
+#define COM_TO (1 << 1)
+
+#define COM_STOR (1 << 0) // for stor()
+#define COM_APPE (1 << 1)
+
+#define COM_CDUP (1 << 0)
+#define COM_CWD  (1 << 1)
+
+#define COM_NOOP (1 << 0) // for sim1()
+#define COM_PWD  (1 << 1)
+#define COM_SYST (1 << 2)
+#define COM_FEAT (1 << 3)
+#define COM_TYPE (1 << 4)
+#define COM_MODE (1 << 5)
+#define COM_STRU (1 << 6)
 #ifdef USE_TLS
-#define COM_PBSZ 50
-#define COM_PROT 51
-#define COM_CCC  52
+#define COM_PBSZ (1 << 7)
+#define COM_PROT (1 << 8)
+#define COM_CCC  (1 << 9)
+#define COM_CONF (1 << 10)
+#define COM_MIC (1 << 11)
+#define COM_ENC (1 << 12)
 #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 COM_SIZE (1 << 0) // for sim2()
+#define COM_MDTM (1 << 1)
+#define COM_MLST (1 << 2)
+
+#define COM_PORT (1 << 0) // port and pasv
+#define COM_EPRT (1 << 1)
+#define COM_PASV (1 << 2)
+#define COM_EPSV (1 << 3)
 
+#define BEFORE_USER (1 << 31) // remember that these are used in conunctions with those above..
+#define LAST_COM (1 << 30)
 
 #define PROT_IPV4 1 // EPRT and EPSV
 #define PROT_IPV6 2
 
-void *data_wrapper(void*);
+#define READ_SIZE 16384
+
+#define PWBUF_SIZE 100
+
+void *data_wrapper(void*) __attribute__ ((noreturn));
 
 extern class Configuration *config;
 extern class Utilities util;
 extern class Handler handler;
-extern pthread_mutex_t sys_log_lock;
 extern vector<User*> user_list;
 
 #ifdef USE_TLS
@@ -110,4 +121,12 @@ extern gnutls_datum tls_db_fetch(void*, 
 extern int tls_db_delete(void*, gnutls_datum);
 #endif // USE_TLS
 
+int pam_conversate(int, const struct pam_message**,
+    struct pam_response**, void*);
+
+struct pam_data {
+  string user_name;
+  string password;
+};
+
 #endif
Index: Commands2.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Commands2.cpp,v
retrieving revision 1.42
retrieving revision 1.119
diff -u -p -b -r1.42 -r1.119
--- Commands2.cpp	10 Jul 2004 14:14:45 -0000	1.42
+++ Commands2.cpp	23 Jan 2005 01:34:53 -0000	1.119
@@ -1,5 +1,6 @@
 /////////////////////////////////////////////////////////////////////
-//  File: Commands2.cpp
+// $Id: Commands2.cpp,v 1.119 2005/01/23 01:34:53 klas Exp $
+//
 // Usage: Ftp commands and data transfer functions
 //
 //  A part of babyftpd, licensed under the GNU GPL, see the file
@@ -11,25 +12,18 @@
 #define __NETINCLS__
 #include "Commands.h"
 
-bool Commands::quit(User *user, string argument)
+bool Commands::quit(User *user, string argument, unsigned int flag)
 {
-  Commands::ftp_send(user, "221 Goodbye.");
-  if(user->logged_in == LOGGED_IN)
-  {
-    string log;
-    log = "User " + user->user_name + " logged out.";
-    pthread_mutex_lock(&sys_log_lock);
-    syslog(LOG_INFO, log.c_str());
-    pthread_mutex_unlock(&sys_log_lock);
-  }
-  pthread_mutex_lock(&user->lock);
+  Commands::ftp_send(user, "221 Goodbye. You transfered a total of " + util.itos(user->total_transfered) + " bytes.");
+
+  sem_wait(&user->user_lock);
   user->logged_in = LOGGED_OUT;
-  pthread_mutex_unlock(&user->lock);
+  sem_post(&user->user_lock);
   close(user->socket_id);
   return(true);
 }
 
-bool Commands::user(User *user, string user_name)
+bool Commands::user(User *user, string user_name, unsigned int flag)
 {
   if(user->logged_in == LOGGED_IN)
   {
@@ -37,9 +31,27 @@ bool Commands::user(User *user, string u
     return(false);
   }
 
+#ifdef USE_TLS
+  string failure;
+  if(config->get_user(user_name).encryption == FORCE &&
+      !user->settings.test(TLS_ENABLED))
+    failure = "421 You muse enable encryption before logging in, disconnecting.";
+  else if(config->get_user(user_name).encryption == DENY &&
+      user->settings.test(TLS_ENABLED))
+    failure =  "421 Sorry, you are not allowed to connect with encryption enabled, disconnecting.";
+
+  if(!failure.empty())
+  {
+    Commands::ftp_send(user, failure);
+    sem_wait(&user->user_lock);
+    user->logged_in = LOGGED_OUT;
+    sem_post(&user->user_lock);
+    return(false);
+  }
+#endif // USE_TLS
   int current_connections = 0;
-  vector<User*>::iterator user_it;
-  for(user_it = user_list.begin(); user_it != user_list.end(); user_it++){
+  for(vector<User*>::iterator user_it = user_list.begin();
+      user_it != user_list.end(); user_it++){
     if((*user_it)->user_name == user_name)
       current_connections++;
   }
@@ -48,28 +60,25 @@ bool Commands::user(User *user, string u
      return(false);
   }
 
-  pthread_mutex_lock(&user->lock);
+  sem_wait(&user->user_lock);
   user->user_name = user_name;
-  pthread_mutex_unlock(&user->lock);
-  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);
+  sem_post(&user->user_lock);
+  Commands::ftp_send(user, "331 Password required for " + user_name + ".");
 
   return(true);
 }
 
-bool Commands::abor(User *user, string argument) // FIXME please?
+bool Commands::abor(User *user, string argument, unsigned int flag) // FIXME please?
 {
   ftp_send(user, "502 Command not implemented.");
   return(true);
 }
 
-bool Commands::pass(User *user, string passwd)
+bool Commands::pass(User *user, string passwd, unsigned int flag)
 {
-  struct spwd *shadow; // the shadow struct
-  char *salt;
-  string pass;
+  char pwdata[PWBUF_SIZE];
+  struct passwd pwd, *pwd_ptr;
   
   // before everything else, lets check if it is really time for pass...
   if(user->logged_in != USER_SUPPLIED)
@@ -84,101 +93,79 @@ bool Commands::pass(User *user, string p
     return(false);
   }
 
-  setspent(); // open the shadow database...
-  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.
+  seteuid(0); // we need to become root to check the user
+  // pam stuff
+  struct pam_data *auth_data = new struct pam_data;
+  struct pam_conv pamconv = { &pam_conversate, auth_data };
+  int pam_error = 0;
+
+  auth_data->user_name = user->user_name;
+  auth_data->password = passwd;
+
+  sem_wait(&comm.pam_lock);
+  pam_error = pam_start("babyftpd", user->user_name.c_str(),
+      &pamconv, &user->pamh);
+
+  if(pam_error == 0)
   {
-    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)
+    pam_error = pam_authenticate(user->pamh, 0);
+    if(pam_error == 0)
     {
-      user->logged_in = LOGGED_OUT;
-      message = "421 To many failed logins. Disconnecting.";
+      pam_error = pam_acct_mgmt(user->pamh, 0);
+      if(pam_error == 0)
+        pam_error = pam_setcred(user->pamh, PAM_ESTABLISH_CRED);
     }
-    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...
-  passwd = crypt(passwd.c_str(), salt); // encrypt the user supplied password
-  pass = shadow->sp_pwdp; // get the encrypted password from the shadow database
-  endspent(); // close the shadow database
-  if(passwd != pass) // no match...
-  {
-    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)
+  if(pam_error != 0)
     {
-      user->logged_in = LOGGED_OUT;
-      message = "421 To many failed logins. Disconnecting.";
+    pam_end(user->pamh, pam_error);
+    sem_post(&pam_lock);
+    seteuid(config->general.user); // and back to some other user..
     }
-    else
-      message = "530 Login denied.";
-    pthread_mutex_unlock(&user->lock);
 
-    ftp_send(user, message);
+  delete auth_data;
 
-    log = "Failed login attempt from ";
-    if(user->host_name.size() > 0)
-      log.append(user->host_name);
-    else
-      log.append(inet_ntoa(user->ctrl_addr.sin_addr));
-    log += " with user name " + user->user_name + ".";
-    
-    pthread_mutex_lock(&sys_log_lock);
-    syslog(LOG_NOTICE, log.c_str());
-    pthread_mutex_unlock(&sys_log_lock);
-    return(false);
-  }
-  else // match =)
+  if(pam_error == 0)
+  {
+    pam_open_session(user->pamh, PAM_SILENT);
+    user->settings.set(PAM_DONE);
+
+    const char *pam_username;
+    pam_get_item(user->pamh, PAM_USER,
+        (reinterpret_cast<const void**>(&pam_username)));
+    sem_post(&pam_lock);
+    if(user->user_name != pam_username)
   {
-    struct passwd *pwd;
-    string log;
-    uid_t uid;
-    gid_t gid;
+      logging->log_this(4, LOG_INFO, "PAM changed username of " +
+          user->user_name + " to " + pam_username);
+      user->user_name = pam_username;
+    }
+
     Commands::ftp_send(user, "230 Password accepted, proceeding.");
-    pthread_mutex_lock(&user->lock);
+    sem_wait(&user->user_lock);
     user->logged_in = LOGGED_IN;
-    pthread_mutex_unlock(&user->lock);
-    setpwent();
-    pwd = getpwnam(user->user_name.c_str());
-    uid = pwd->pw_uid;
-    gid = pwd->pw_gid;
-    pthread_mutex_lock(&user->lock);
+    sem_post(&user->user_lock);
+    getpwnam_r(user->user_name.c_str(), &pwd, pwdata, sizeof(pwdata), &pwd_ptr);
+    uid_t uid = pwd.pw_uid;
+    gid_t gid = pwd.pw_gid;
+    sem_wait(&user->user_lock);
     if(config->get_user(user->user_name).only_home)
-      user->root_dir = pwd->pw_dir;
-    user->current_dir = pwd->pw_dir;
-    pthread_mutex_unlock(&user->lock);
-    log = "User " + user->user_name + " logged in.";
-    endpwent();
-    pthread_mutex_lock(&sys_log_lock);
-    syslog(LOG_INFO, log.c_str());
-    pthread_mutex_unlock(&sys_log_lock);
+      user->root_dir = pwd.pw_dir;
+    user->current_dir = pwd.pw_dir;
+    sem_post(&user->user_lock);
     setgid(gid);
-    setuid(uid); // dropping permissions...
+    seteuid(uid); // dropping permissions...
     return(true);
   }
+  sem_post(&user->user_lock);
+  ftp_send(user, "530 Login denied.");
+  return(false);
 }
 
-bool Commands::help(User *user, string argument)
+bool Commands::help(User *user, string argument, unsigned int flag)
 {
   string message;
-  if(argument == "")
+  if(!argument.size())
   {
     int cmd = 0;
     message = "214-The following commands are understood:\r\n ";
@@ -186,7 +173,7 @@ bool Commands::help(User *user, string a
     {
       if(cmd != 0 && cmd % 8 == 0)
         message += "\r\n ";
-      if(handler.cmd_info[cmd].hlp_info != NULL)
+      if(handler.cmd_info[cmd].hlp_info.size() != 0)
         message += handler.cmd_info[cmd].command + "\t";
       cmd++;
     }
@@ -195,11 +182,11 @@ bool Commands::help(User *user, string a
   else
   {
     // other help stuff here...
-    int cmd;
     message  = "214 Syntax: ";
-    util.upper(argument);
-    for(cmd = 0; handler.cmd_info[cmd].command != "\n"; cmd++)
-      if(handler.cmd_info[cmd].command == argument && handler.cmd_info[cmd].hlp_info != NULL)
+    transform(argument.begin(), argument.end(), argument.begin(), (int(*)(int))toupper);
+    int cmd = 0;
+    for(; handler.cmd_info[cmd].command != "\n"; cmd++)
+      if(handler.cmd_info[cmd].command == argument && handler.cmd_info[cmd].hlp_info.size() != 0)
       {
         message += handler.cmd_info[cmd].command + handler.cmd_info[cmd].hlp_info;
         break;
@@ -212,26 +199,35 @@ bool Commands::help(User *user, string a
   return(true);
 }
 
-bool Commands::cwd(User *user, string dir)
+bool Commands::cwd(User *user, string dir, unsigned int flag)
 { 
   struct stat stt;
-  string message, new_dir = Commands::parsedir(user, dir);
+  string message, new_dir;
+  DIR *tmp_dir;
+
+  if(util.is_set(flag, COM_CDUP))
+    dir = "..";
+
+  if((user->root_dir.size() > 1 && (user->current_dir.size() == user->root_dir.size())) && (dir == ".." || dir == "."))
+    new_dir = user->root_dir;
+  else
+    new_dir = Commands::parsedir(user, dir);
+
   
   lstat(new_dir.c_str(), &stt);
 
   if(S_ISLNK(stt.st_mode))
   {
-    char *real_name = new char[65];
+    char real_name[65] = { 0 };
     int len;
 
     len = readlink(new_dir.c_str(), real_name, 60);
     real_name[len] = '\0';
     
     new_dir = Commands::parsedir(user, real_name);
-    delete[] real_name;
   }
   
-  if(!opendir(new_dir.c_str()))
+  if(!(tmp_dir = opendir(new_dir.c_str())))
   {
     if(errno == EACCES)
       message = "550 '" + new_dir + "' permission denied.";
@@ -240,23 +236,25 @@ bool Commands::cwd(User *user, string di
   }
   else
   {
+    if(util.is_set(flag, COM_CDUP))
+      message = "200 'CDUP' command successful.";
+    else // COM_CWD
     message = "250 'CWD' command successful.";
-    pthread_mutex_lock(&user->lock);
+
+    sem_wait(&user->user_lock);
     user->current_dir = new_dir;
-    pthread_mutex_unlock(&user->lock);
+    sem_post(&user->user_lock);
   }
+  closedir(tmp_dir);
   ftp_send(user, message);
   return(true);
 }
 
-bool Commands::retr(User *user, string file)
+bool Commands::retr(User *user, string file, unsigned int flag)
 {
-  string file_name;
-  int file_id;
   struct stat stt;
-  class Data *data;
   
-  file_name = parsedir(user, file);
+  string file_name = parsedir(user, file);
   stat(file_name.c_str(), &stt);
   if(!S_ISREG(stt.st_mode))
   {
@@ -264,59 +262,48 @@ bool Commands::retr(User *user, string f
     return(false);
   }
   
-  file_id = open(file_name.c_str(), O_RDONLY);
+  int file_id = open(file_name.c_str(), O_RDONLY);
   if(user->rest_pos > 0)
   {
     lseek(file_id, user->rest_pos, SEEK_SET);
-    pthread_mutex_lock(&user->lock);
+    sem_wait(&user->user_lock);
     user->rest_pos = 0;
-    pthread_mutex_unlock(&user->lock);
+    sem_post(&user->user_lock);
   }
   
-  ftp_send(user, "150 Opening " + (string)(is_set(user->settings, BINARY)?"BINARY":"ASCII") + " mode data connection for \"" + file + "\" (" + util.itos(stt.st_size) + " bytes).");
+  ftp_send(user, "150 Opening " + static_cast<string>(user->settings.test(BINARY)?"BINARY":"ASCII") + " mode data connection for \"" + file + "\" (" + util.itos(stt.st_size) + " bytes).");
   
-  data = new Data(user, file_id, file_name, 1);
-  if(!data->isOpen())
-  {
-    ftp_send(user, "425 Can't open data connection.");
-    delete data;
-    return(false);
-  }
-  else if(user->data_vec_full())
+  class Data *data = new Data(user, file_id, file_name, 1);
+  if(!data->isOpen() || user->data_vec_full())
   {
     ftp_send(user, "425 Can't open data connection.");
     delete data;
     return(false);
   }
   
-  pthread_mutex_lock(&user->lock);
+  sem_wait(&user->user_lock);
   if(user->rest_pos > 0)
   {
-    pthread_mutex_lock(&data->data_lock);
+    sem_wait(&data->data_lock);
     data->rest_pos = user->rest_pos;
-    pthread_mutex_unlock(&data->data_lock);
+    sem_post(&data->data_lock);
     user->rest_pos = 0;
   }
 
   user->data_vec.push_back(data);
-  pthread_mutex_unlock(&user->lock);
-  pthread_create(&data->thread_id, NULL, data_wrapper, (void*)data);
+  sem_post(&user->user_lock);
+  pthread_create(&data->thread_id, NULL, data_wrapper, static_cast<void*>(data));
   pthread_detach(data->thread_id);
   return(true);
 }
 
-bool Commands::stor(User *user, string file)
+bool Commands::stor(User *user, string file, unsigned int flag)
 {
-  string file_name;
-  class Data *data;
-  int file_id, com_type;
-
-  com_type = file[0];
-  file.erase(0, 1);
+  int file_id;
 
-  file_name = parsedir(user, file);
+  string file_name = parsedir(user, file);
   
-  if(com_type == COM_STOR)
+  if(util.is_set(flag, COM_STOR))
     file_id = open(file_name.c_str(), O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
   else // com_type == COM_APPE
     file_id = open(file_name.c_str(), O_WRONLY|O_APPEND|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
@@ -336,59 +323,52 @@ bool Commands::stor(User *user, string f
     return false;
   }
   
-  if(user->rest_pos > 0 && com_type == COM_STOR) // shouldn't try to restart an appending :p
+  if(user->rest_pos > 0 && util.is_set(flag, COM_STOR)) // shouldn't try to restart an appending :p
   {
     lseek(file_id, user->rest_pos, SEEK_SET);
-    pthread_mutex_lock(&user->lock);
+    sem_wait(&user->user_lock);
     user->rest_pos = 0;
-    pthread_mutex_unlock(&user->lock);
+    sem_post(&user->user_lock);
   }
   
-  ftp_send(user, "150 Opening " + (string)(is_set(user->settings, BINARY)?"BINARY":"ASCII") + " mode data connection for \"" + file + "\".");
+  ftp_send(user, "150 Opening " + static_cast<string>(user->settings.test(BINARY)?"BINARY":"ASCII") + " mode data connection for \"" + file + "\".");
 
-  data = new Data(user, file_id, file_name, 0);
-  if(!data->isOpen())
-  {
-    ftp_send(user, "425 Can't open data connection.");
-    delete data;
-    return(false);
-  }
-  else if(user->data_vec_full())
+  class Data *data = new Data(user, file_id, file_name, 0);
+  if(!data->isOpen() || user->data_vec_full())
   {
     ftp_send(user, "425 Can't open data connection.");
     delete data;
     return(false);
   }
 
-  pthread_mutex_lock(&user->lock);
-  if(user->rest_pos > 0 && com_type == COM_STOR) // shouldn't try to restart an appending :p
+  sem_wait(&user->user_lock);
+  if(user->rest_pos > 0 && util.is_set(flag, COM_STOR)) // shouldn't try to restart an appending :p
   {
     lseek(file_id, user->rest_pos, SEEK_SET);
-    pthread_mutex_lock(&data->data_lock);
+    sem_wait(&data->data_lock);
     data->rest_pos = user->rest_pos;
-    pthread_mutex_unlock(&data->data_lock);
+    sem_post(&data->data_lock);
     user->rest_pos = 0;
   }
   
   user->data_vec.push_back(data);
-  pthread_mutex_unlock(&user->lock);
-  pthread_create(&data->thread_id, NULL, data_wrapper, (void*)data);
+  sem_post(&user->user_lock);
+  pthread_create(&data->thread_id, NULL, data_wrapper, static_cast<void*>(data));
   pthread_detach(data->thread_id);
   return(true);
 }
 
-bool Commands::rest(User *user, string argument)
+bool Commands::rest(User *user, string argument, unsigned int flag)
 {
   char *endptr;
-  long int fpos;
   string message;
   
-  fpos = strtoull(argument.c_str(), &endptr, 0);
+  unsigned long fpos = strtoul(argument.c_str(), &endptr, 0);
   if(!*endptr)
   {
-    pthread_mutex_lock(&user->lock);
+    sem_wait(&user->user_lock);
     user->rest_pos = fpos;
-    pthread_mutex_unlock(&user->lock);
+    sem_post(&user->user_lock);
     message = "350 Restarting at " + argument + ". Send STOR or RETR.";
   }
   else
@@ -398,183 +378,209 @@ bool Commands::rest(User *user, string a
   return(true);
 }
 
-bool Commands::list(User *user, string argument)
+bool Commands::list(User *user, string argument, unsigned int flag)
 {
   struct stat stt;
-  string file, message;
-  int type;
-  class Data *data;
-  bool ret;
+  string failure;
+
+  if(argument.size() == 0)
+    argument = user->current_dir;
   
-  type = argument[0];
-  argument.erase(0, 1);
+  string file = parsedir(user, argument);
+  int stat_err = lstat(file.c_str(), &stt);
 
-  if(type == COM_LONG && argument[0] == '-')
+  if(stat_err == -1 && ((util.is_set(flag, COM_LONG) || util.is_set(flag, COM_MLSD)) && argument[0] == '-'))
   {
     class StringTokenizer *token = new StringTokenizer(argument, " ", false);
     token->nextToken();
     argument = token->restTokens();
     delete token;
-  }
-  if(argument == "")
-    argument = user->current_dir;
-
   file = parsedir(user, argument);
-  lstat(file.c_str(), &stt);
+    stat_err = lstat(file.c_str(), &stt);
+  }
 
-  if(!S_ISDIR(stt.st_mode) && type == COM_MLSD)
+  if(!S_ISDIR(stt.st_mode) && util.is_set(flag, COM_MLSD))
   {  
     ftp_send(user, "501 Argument to MLSD has to be a directory.");
     return false;
   }
 
-  data = new Data(user);
+  class Data *data = new Data(user);
 
   if(!data->isOpen())
-  {
-    ftp_send(user, "425 Can't open data connection.");
-    ret = false;
-  }
+    failure = "425 Can't open data connection.";
   else
   {
+    string message;
+    size_t write_size = READ_SIZE;
     if(S_ISDIR(stt.st_mode))
     {
-      struct dirent **dirstr;
-      int num_files, files_past;
+      struct dirent dirstr, *dirptr;
+      vector<string> dirlist;
+
+      DIR *dirp = opendir(file.c_str());
+      while((readdir_r(dirp, &dirstr, &dirptr) == 0) && dirptr != NULL)
+      {
+#ifdef _DIRENT_HAVE_D_TYPE
+        if((dirstr.d_type == DT_DIR) && util.is_set(flag, COM_SHORT))
+          continue;
+#endif // _DIRENT_HAVE_D_TYPE
+        dirlist.push_back(dirstr.d_name);
+      }
+
+      closedir(dirp);
       
-      files_past = 0;
-      num_files = scandir(file.c_str(), &dirstr, 0, alphasort);
-      while(files_past < num_files)
+      sort(dirlist.begin(), dirlist.end()); // sorting the directory so we output first things first
+      for(vector<string>::iterator dir_it = dirlist.begin(); dir_it != dirlist.end(); dir_it++)
       {
-        if(type == COM_LONG)
-          message.append(list_file(user, dirstr[files_past]->d_name));
-        else if(type == COM_MLSD)
-          message.append(mlsx(user, parsedir(user, dirstr[files_past]->d_name)));
+        if(util.is_set(flag, COM_LONG))
+          message += list_file(file + "/" + *dir_it);
+        else if(util.is_set(flag, COM_MLSD))
+          message += mlsx(user, file + "/" + *dir_it);
         else
-          message.append(dirstr[files_past]->d_name); // short list.
-        message.append("\r\n"); // and newline.
-        free(dirstr[files_past++]); // free the memory allocated by scandir()
+          message += *dir_it; // short list.
+
+        message += "\r\n"; // and newline.
       }
-      free(dirstr); // and the last memory from scandir()
     }
     else
     {
-      if(type == COM_LONG)
-        message.append(list_file(user, argument)); 
+      if(util.is_set(flag, COM_LONG))
+        message += list_file(file);
       else
-        message.append(argument);
-      message.append("\r\n");
+        message += argument;
+      message += "\r\n";
     }   
-    pthread_mutex_lock(&data->data_lock);
+    sem_wait(&data->data_lock);
 #ifdef USE_TLS
-    if(is_set(user->settings, TLS_PROT_D))
+    if(user->settings.test(TLS_PROT_D))
     {
       ftp_send(user, "150 Opening PROTECTED ASCII mode data connection for file list.");
       if(gnutls_handshake(data->data_session) < 0) // handshake failed
-        ftp_send(user, "451 Failed to negotiate TLS as was asked. Closing data connection.");
+        failure =  "451 Failed to negotiate TLS as was asked. Closing data connection.";
       else
-        gnutls_record_send(data->data_session, (void*)message.c_str(), message.size());
+        write_size = gnutls_record_get_max_size(data->data_session);
     }
     else
-    {
-#endif // USE_TLS
+#endif
     ftp_send(user, "150 Opening ASCII mode data connection for file list.");
-    write(data->data_id, (void*)message.c_str(), message.size());
+
+
+    size_t written = 0;
+    struct timeval tv = { 1,0 };
+    fd_set wfds;
+    FD_ZERO(&wfds);
+    FD_SET(data->data_id, &wfds);
+
+    if(!select(data->data_id + 1, NULL, &wfds, NULL, &tv))
+      failure = "425 Data connection lost.";
+
+    while(written < message.size() && !failure.size())
+    {
+      string writing;
+
+      writing.assign(message, written, write_size);
 #ifdef USE_TLS
-    } // else to using TLS for data at all..
+      if(user->settings.test(TLS_PROT_D))
+      {
+        if(failure.size() == 0)
+          written += gnutls_record_send(data->data_session, static_cast<const void*>(writing.c_str()), writing.size());
+      } else
 #endif // USE_TLS
+        written += write(data->data_id, static_cast<const void*>(writing.c_str()), writing.size());
+    }
     close(data->data_id);
-    pthread_mutex_unlock(&data->data_lock);
-    ftp_send(user, "226 Transfer complete.");
-    ret = true;
+    sem_post(&data->data_lock);
+    sem_wait(&user->user_lock);
+    user->total_transfered += written;
+    sem_post(&user->user_lock);
   }
+  if(failure.size() > 0)
+    ftp_send(user, failure);
+  else
+    ftp_send(user, "226 Transfer complete.");
   delete data;
-  return(ret);
+  return(true);
 }
 
-string Commands::list_file(User *user, string file)
+string Commands::list_file(string file_name)
 {
-  char *time_string, *size, *nlink;
-  string user_name, group_name, message, file_name;
+  char size[20], nlink[6], time_string[25], pwdbuf[PWBUF_SIZE], grpbuf[PWBUF_SIZE];
+  string user_name, group_name;
   struct stat file_stat;
-  struct passwd *pwd;
-  struct group *grp;
-  setpwent();
-  setgrent();
-                                        
-  file_name = parsedir(user, file);
-
-  size = new char[20];
-  nlink = new char[20];
-  time_string = new char[25];
-  memset(size, '\0', 20);
-  memset(nlink, '\0', 20);
-  memset(time_string, '\0', 25);
+  struct passwd pwd, *pwd_ptr;
+  struct group grp, *grp_ptr;
+  struct tm file_time;
+
   lstat(file_name.c_str(), &file_stat);
 
-  sprintf(nlink, "%3d ", file_stat.st_nlink); // number of hard links
+  snprintf(nlink, 5, "%3d ", static_cast<int>(file_stat.st_nlink)); // number of hard links
 
-  if((pwd = getpwuid(file_stat.st_uid))) // username
-    user_name = pwd->pw_name;
+  if((getpwuid_r(file_stat.st_uid, &pwd, pwdbuf, sizeof(pwdbuf), &pwd_ptr)) == 0)
+    user_name = pwd.pw_name;
   else
     user_name = util.itos(file_stat.st_uid);
   
-  if((grp = getgrgid(file_stat.st_gid))) // groupname
-    group_name = grp->gr_name;
+  if((getgrgid_r(file_stat.st_gid, &grp, grpbuf, sizeof(grpbuf), &grp_ptr)) == 0)
+    group_name = grp.gr_name;
   else
     group_name = util.itos(file_stat.st_gid);
 
-  sprintf(size, "%9d ", (int)file_stat.st_size); // filesize
+  snprintf(size, 19, "%9d ", static_cast<int>(file_stat.st_size)); // filesize
 
-  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.
+  gmtime_r(&file_stat.st_mtime, &file_time);
+  // modification time, either in new ls format, or old..
+#ifdef USE_NEWLS
+  strftime(time_string, 24, "%Y-%m-%d %H:%M ", &file_time);
+#else
+  if((time(NULL) - file_stat.st_mtime) > (60 * 60 * 24 * 365)) // is the file over a year old?
+    strftime(time_string, 24, "%b %e  %Y ", &file_time);
+  else // if it's not, we display the time it was created as well.
+    strftime(time_string, 24, "%b %e %H:%M ", &file_time);
+#endif // USE_NEWLS
+
+  string message = (util.parse_perms_ls(file_stat.st_mode) + ' ')
+    + nlink + user_name + '\t' + group_name + '\t' + size + time_string
+    + basename(file_name.c_str()); // parsing everything on one line is neater.
  
   // if it's a link...
   if(S_ISLNK(file_stat.st_mode))
   {
-    char *real_name = new char[55];
-    int len;
-    len = readlink(file_name.c_str(), real_name, 50);  
+    char real_name[55];
+    int len = readlink(file_name.c_str(), real_name, 50);
     real_name[len] = '\0';
     message += " -> ";
     message += real_name;
-    delete[] real_name;
   }
-  
-  endpwent();
-  endgrent();
-  delete[] time_string;
-  delete[] nlink;
-  delete[] size;
   return(message);
 }
 
 string Commands::mlsx(User *user, string file_name)
 {
+  char pwdbuf[PWBUF_SIZE], grpbuf[PWBUF_SIZE];
+  struct passwd pwd, *pwd_ptr;
+  struct group grp, *grp_ptr;
+
   struct stat stt;
-  string message, type, user_name, group_name, file;
+  string message;
 
-  file = parsedir(user, file_name);
-  lstat(file.c_str(), &stt);
+  lstat(file_name.c_str(), &stt);
 
   // type.
-  if(is_set(user->mlst_facts, FACT_TYPE))
+  if(user->settings.test(FACT_TYPE))
   {
+    string type;
     if(S_ISREG(stt.st_mode))
       type = "file";
     else if(S_ISDIR(stt.st_mode))
     {
-      if(file == user->current_dir)
+      if(file_name == user->current_dir)
       {
         type = "cdir";
-        file += "/.";
+        file_name += "/.";
       }
-      else if(file == parsedir(user, user->current_dir + "/.."))
-      {
+      else if(file_name == parsedir(user, user->current_dir + "/.."))
         type = "pdir";
-        //file += "/.."; // this messes the string up as it is already parsed once...
-      }
       else
         type = "dir";
     }
@@ -592,37 +598,30 @@ string Commands::mlsx(User *user, string
     message =  "Type=" + type + ";";
   }
 
-  if(is_set(user->mlst_facts, FACT_SIZE))
+  if(user->settings.test(FACT_SIZE))
     message += "Size=" + util.itos(stt.st_size) + ";";
-  if(is_set(user->mlst_facts, FACT_MODIFY))
-    message += "Modify=" + mod_time(file_name, stt) + ";"; // mod time...
-  if(is_set(user->mlst_facts, FACT_UNIQUE))
+  if(user->settings.test(FACT_MODIFY))
+    message += "Modify=" + mod_time(stt) + ";"; // mod time...
+  if(user->settings.test(FACT_UNIQUE))
     message += "Unique=" + util.itos(stt.st_dev) + "g" + util.itos(stt.st_ino) + ";";
-  if(is_set(user->mlst_facts, FACT_PERM))
+  if(user->settings.test(FACT_PERM))
     {
       bool owner = false, group = false;
-      struct passwd *pwd;
-      struct group *grp;
       
-      setpwent();
-      if((pwd = getpwnam(user->user_name.c_str())))
+    if(getpwnam_r(user->user_name.c_str(), &pwd, pwdbuf, sizeof(pwdbuf), &pwd_ptr))
       {
-        if(pwd->pw_uid == stt.st_uid)
+      if(pwd.pw_uid == stt.st_uid)
           owner = true;
-        else if(pwd->pw_gid == stt.st_gid)
+      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])
+      if(getgrgid_r(stt.st_gid, &grp, grpbuf, sizeof(grpbuf), &grp_ptr))
+        for(int i = 0; grp.gr_mem[i] != NULL; i++)
+          if(user->user_name == grp.gr_mem[i])
               group = true;
-        
-        endgrent();
       }
       
       message += "Perm=";
@@ -630,54 +629,54 @@ string Commands::mlsx(User *user, string
       {
         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(util.test(stt.st_mode, S_IRUSR)) message += "l";
+        if(util.test(stt.st_mode, S_IWUSR)) message += "cmp";
+        if(util.test(stt.st_mode, S_IXUSR)) message += "e";
         }
-        if(test(stt.st_mode, S_IRUSR)) message += "r";
-        if(test(stt.st_mode, S_IWUSR)) message += "adfw";
+      if(util.test(stt.st_mode, S_IRUSR)) message += "r";
+      if(util.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(util.test(stt.st_mode, S_IRGRP)) message += "l";
+        if(util.test(stt.st_mode, S_IWGRP)) message += "cmp";
+        if(util.test(stt.st_mode, S_IXGRP)) message += "e";
         }
-        if(test(stt.st_mode, S_IRGRP)) message += "r";
-        if(test(stt.st_mode, S_IWGRP)) message += "adfw";
+      if(util.test(stt.st_mode, S_IRGRP)) message += "r";
+      if(util.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(util.test(stt.st_mode, S_IROTH)) message += "l";
+        if(util.test(stt.st_mode, S_IWOTH)) message += "cmp";
+        if(util.test(stt.st_mode, S_IXOTH)) message += "e";
         }
-        if(test(stt.st_mode, S_IROTH)) message += "r";
-        if(test(stt.st_mode, S_IWOTH)) message += "adfw";
+      if(util.test(stt.st_mode, S_IROTH)) message += "r";
+      if(util.test(stt.st_mode, S_IWOTH)) message += "adfw";
       }
       message += ";";
     }
-  if(is_set(user->mlst_facts, FACT_U_MODE))
+  if(user->settings.test(FACT_U_MODE))
     message += "Unix.mode=" + util.parse_perms_num(stt.st_mode) + ";";
-  if(is_set(user->mlst_facts, FACT_U_UID))
+  if(user->settings.test(FACT_U_UID))
     message += "Unix.uid=" + util.itos(stt.st_uid) + ";";
-  if(is_set(user->mlst_facts, FACT_U_GID))
+  if(user->settings.test(FACT_U_GID))
     message += "Unix.gid=" + util.itos(stt.st_gid) + ";";
-  if(is_set(user->mlst_facts, FACT_U_LINK))
+  if(user->settings.test(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());
-  message += " " + file;
+  if(user->root_dir.size() > 1)
+    file_name.erase(0, user->root_dir.size());
+  message += " " + file_name;
 
   return(message);
 }
 
 
-bool Commands::mkd(User *user, string dir)
+bool Commands::mkd(User *user, string dir, unsigned int flag)
 {
   string message, new_dir = parsedir(user, dir);
 
@@ -700,7 +699,7 @@ bool Commands::mkd(User *user, string di
   return(true);
 }
 
-bool Commands::rmd(User *user, string dir)
+bool Commands::rmd(User *user, string dir, unsigned int flag)
 {
   string message, new_dir = parsedir(user, dir);
   
@@ -721,22 +720,21 @@ bool Commands::rmd(User *user, string di
   return(true);
 }
 
-bool Commands::dele(User *user, string file)
+bool Commands::dele(User *user, string file, unsigned int flag)
 {
   string message, new_file = parsedir(user, file);
 
   if(unlink(new_file.c_str()))
   {
     message = "550 ";
-    if(errno == (EPERM || EACCES))
-      message = "File access denied, not deleted.";
+    if(errno == EPERM || errno == EACCES)
+      message += "File access denied, not deleted.";
     else if(errno == EPERM)
-      message = "File \"" + new_file + "\" is a directory.";
-    else if(errno == (ENOENT || EISDIR))
-      message = "File not found.";
+      message += "File \"" + new_file + "\" is a directory.";
+    else if(errno == ENOENT || errno == EISDIR)
+      message += "File not found.";
     else
       message = "450 File not available.";
-    return(false);
   }
   else
     message = "250 'DELE' command successful.";
@@ -745,21 +743,20 @@ bool Commands::dele(User *user, string f
   return(true);
 }
 
-bool Commands::port(User *user, string argument)
+bool Commands::port(User *user, string argument, unsigned int flag)
 {
   string tmp_adress, message;
-  int port, command = argument[0];
-  argument.erase(0, 1);
+  int port;
 
   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'";
+  if(user->settings.test(EPSV_ONLY))
+    message = "501 Only 'EPSV' may be used after the issuing of 'EPSV ALL'";
 
   else if((config->get_user(user->user_name).only_passive))
     message = "501 Sorry, active data transfers are disabled for your account.";
       
-  else if(command == COM_PORT)
+  else if(util.is_set(flag, COM_PORT))
   {
     class StringTokenizer *token = new StringTokenizer(argument, ",", false);
   
@@ -777,10 +774,10 @@ bool Commands::port(User *user, string a
     }
     delete token;
   }
-  else if(command == COM_EPRT)
+  else if(util.is_set(flag, COM_EPRT))
   {
     char separator[1];
-    separator[0] = (char)argument[0];
+    separator[0] = static_cast<char>(argument[0]);
     class StringTokenizer *token = new StringTokenizer(argument, separator, false);
 
     if(token->countTokens() != 3 || argument[argument.size() - 1] != argument[0])
@@ -801,100 +798,92 @@ bool Commands::port(User *user, string a
 
   if(message.size() == 0)
   {
-    pthread_mutex_lock(&user->lock);
+    sem_wait(&user->user_lock);
     if((inet_aton(tmp_adress.c_str(), &user->data_addr.sin_addr)) == 0)
     {
       message = "501 Illegal '";
-      message += (command == COM_PORT ? "PORT" : "EPRT");
+      message += (util.is_set(flag, COM_PORT) ? "PORT" : "EPRT");
       message += "' command.";
     }
     else
     {
       user->data_addr.sin_port = port;
       user->data_addr.sin_family = AF_INET;
-      remove_bit(user->settings, PASSIVE);
+      user->settings.reset(PASSIVE);
       message += "200 '";
-      message += (command == COM_PORT ? "PORT" : "EPRT");
+      message += (util.is_set(flag, COM_PORT) ? "PORT" : "EPRT");
       message += "' command successful.";
     }
-    pthread_mutex_unlock(&user->lock);
+    sem_post(&user->user_lock);
   }
   
   ftp_send(user, message);
   return(true);
 }
 
-bool Commands::pasv(User *user, string argument)
+bool Commands::pasv(User *user, string argument, unsigned int flag)
 {
   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);
+
+  if(util.is_set(flag, COM_EPSV) && argument.size() > 0)
+  {
+    transform(argument.begin(), argument.end(), argument.begin(), (int(*)(int))toupper);
+    if(argument == "ALL" && util.is_set(flag, COM_EPSV))
+    {
+      sem_wait(&user->user_lock);
+      user->settings.set(EPSV_ONLY);
+      sem_post(&user->user_lock);
     message = "200 Now only accepting 'EPSV'";
   }
   else
+      message = "501 '" + argument + "' is not a valid argument to 'EPSV'.";
+  }
+  else
   {
-    len = sizeof(tmp_addr);
-    len2 = sizeof(tmp_addr2);
+    struct sockaddr_in tmp_addr, tmp_addr2;
+    socklen_t len = sizeof(tmp_addr);
 
-    memset(&user->data_addr, 0, sizeof(user->data_addr));
     memset(&tmp_addr, 0, len);
-    memset(&tmp_addr2, 0, len2);
+    memset(&tmp_addr2, 0, len);
 
-    pthread_mutex_lock(&user->lock);
+    sem_wait(&user->user_lock);
+    memset(&user->data_addr, 0, sizeof(user->data_addr));
     if(user->data_id)
       close(user->data_id);
 
     user->data_id = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
     user->data_addr.sin_family = AF_INET;
     user->data_addr.sin_addr.s_addr = INADDR_ANY;
-    bind(user->data_id, (struct sockaddr*)&user->data_addr, sizeof(user->data_addr));
+    bind(user->data_id, reinterpret_cast<struct sockaddr*>(&user->data_addr), sizeof(user->data_addr));
     listen(user->data_id, 1);
-    getsockname(user->data_id, (struct sockaddr*)&tmp_addr, &len);
-    getsockname(user->socket_id, (struct sockaddr*)&tmp_addr2, &len2);
-    set_bit(user->settings, PASSIVE);
-    pthread_mutex_unlock(&user->lock);
+    getsockname(user->data_id, reinterpret_cast<struct sockaddr*>(&tmp_addr), &len);
+    getsockname(user->socket_id, reinterpret_cast<struct sockaddr*>(&tmp_addr2), &len);
+    user->settings.set(PASSIVE);
+    sem_post(&user->user_lock);
 
-    p = ntohs(tmp_addr.sin_port);
+    unsigned int p = ntohs(tmp_addr.sin_port);
 
-    if(command == COM_PASV)
+    if(util.is_set(flag, COM_PASV))
     {
-      if(is_set(user->settings, EPSV_ONLY))
-        message = "501 Only 'EPSV' may be user after the issuing of 'EPSV ALL'";
+      if(user->settings.test(EPSV_ONLY))
+        message = "501 Only 'EPSV' may be used 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();
-        ip_addr[2] = token->nextToken();
-        ip_addr[3] = token->nextToken();
-        ip_addr[4] = token->nextToken();
-        delete token;
+        class StringTokenizer *token = new StringTokenizer(inet_ntoa(tmp_addr2.sin_addr), ".", false);
 
-        message = "227 Entering Passive Mode ("
-          + ip_addr[1] + ","
-          + ip_addr[2] + ","
-          + ip_addr[3] + ","
-          + ip_addr[4] + ","
-          + util.itos((p >> 8) & 255) + ","
+        message = "227 Entering Passive Mode (";
+        message += token->nextToken() + ",";
+        message += token->nextToken() + ",";
+        message += token->nextToken() + ",";
+        message += token->nextToken() + ",";
+        message += util.itos((p >> 8) & 255) + ","
           + util.itos(p & 255) + ").";
+
+        delete token;
       }
     }
-    else if(command == COM_EPSV)
+    else if(util.is_set(flag, COM_EPSV))
     {
       if(argument.size() > 0)
         if(argument != util.itos(PROT_IPV4))
@@ -909,24 +898,20 @@ bool Commands::pasv(User *user, string a
 }
 
 // rnfr and rnto
-bool Commands::rname(User *user, string argument)
+bool Commands::rname(User *user, string argument, unsigned int flag)
 {
-  int command;
   string file_name, message;
 
-  command = argument[0];
-  argument.erase(0, 1);
-
   file_name = parsedir(user, argument);
 
-  if(command == COM_FROM)
+  if(util.is_set(flag, COM_FROM))
   {
-    pthread_mutex_lock(&user->lock);
+    sem_wait(&user->user_lock);
     user->temp_buf = file_name;
-    pthread_mutex_unlock(&user->lock);
+    sem_post(&user->user_lock);
     message = "350 Command OK, waiting for RNTO.";
   }
-  else if(command == COM_TO)
+  else if(util.is_set(flag, COM_TO))
   {
     if(user->temp_buf.size() == 0)
       message = "503 RNTO must come after RNFR.";
@@ -938,13 +923,14 @@ bool Commands::rname(User *user, string 
         message = "550 Access denied.";
       else
         message = "553 Rename failed.";
+      user->temp_buf.erase();
     }
   }
   ftp_send(user, message);
   return(true);
 }
  
-bool Commands::opts(User *user, string argument)
+bool Commands::opts(User *user, string argument, unsigned int flag)
 {
   string command, message;
   class StringTokenizer *st;
@@ -957,7 +943,7 @@ bool Commands::opts(User *user, string a
   }
   else
   {
-    util.upper(argument);
+    transform(argument.begin(), argument.end(), argument.begin(), (int(*)(int))toupper);
     st = new StringTokenizer(argument, " ", true);
     command = st->nextToken();
     argument = st->restTokens();
@@ -968,24 +954,24 @@ bool Commands::opts(User *user, string a
       string fact;
       st = new StringTokenizer(argument, ";", false);
 
-      pthread_mutex_lock(&user->lock);
+      sem_wait(&user->user_lock);
       while(st->hasMoreTokens())
       {
         fact = st->nextToken();
-        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);
+        if(fact == "TYPE") user->settings.flip(FACT_TYPE);
+        else if(fact == "SIZE") user->settings.flip(FACT_SIZE);
+        else if(fact == "MODIFY") user->settings.flip(FACT_MODIFY);
+        else if(fact == "UNIQUE") user->settings.flip(FACT_UNIQUE);
+        else if(fact == "PERM") user->settings.flip(FACT_PERM);
+        else if(fact == "UNIX.MODE") user->settings.flip(FACT_U_MODE);
+        else if(fact == "UNIX.UID") user->settings.flip(FACT_U_UID);
+        else if(fact == "UNIX.GID") user->settings.flip(FACT_U_GID);
+        else if(fact == "UNIX.LINK") user->settings.flip(FACT_U_LINK);
       };
 
-      pthread_mutex_unlock(&user->lock);
+      sem_post(&user->user_lock);
 
-      message = "200 MLS§T string now showing: " + mlst_string(user);
+      message = "200 MLST string now showing: " + mlst_string(user);
       delete st;
     }
     else
@@ -1001,53 +987,42 @@ string Commands::mlst_string(User *user)
 {
   string message;
 
-  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) ? "*;" : ";");
+  message += ("Type"); message += (user->settings.test(FACT_TYPE) ? "*;" : ";");
+  message += ("Size"); message += (user->settings.test(FACT_SIZE) ? "*;" : ";");
+  message += ("Modify"); message += (user->settings.test(FACT_MODIFY) ? "*;" : ";");
+  message += ("Unique"); message += (user->settings.test(FACT_UNIQUE) ? "*;" : ";");
+  message += ("Perm"); message += (user->settings.test(FACT_PERM) ? "*;" : ";");
+  message += ("Unix.mode"); message += (user->settings.test(FACT_U_MODE) ? "*;" : ";");
+  message += ("Unix.uid"); message += (user->settings.test(FACT_U_UID) ? "*;" : ";");
+  message += ("Unix.gid"); message += (user->settings.test(FACT_U_GID) ? "*;" : ";");
+  message += ("Unix.link"); message += (user->settings.test(FACT_U_LINK) ? "*;" : ";");
   return(message);
 }
 
 // functions for small left over commands (noop, pwd, etc...)
-bool Commands::sim1(User *user, string argument)
+bool Commands::sim1(User *user, string argument, unsigned int flag)
 {
-  int command;
   string message;
   
-  command = argument[0];
-  argument.erase(0, 1);
-  util.upper(argument);
+  transform(argument.begin(), argument.end(), argument.begin(), (int(*)(int))toupper);
   
-  if(command == COM_NOOP)
+  if(util.is_set(flag, COM_NOOP))
     message = "200 'NOOP': Command successful.";
 
-  else if(command == COM_PWD)
+  else if(util.is_set(flag, COM_PWD))
   {
     string dir = user->current_dir;
-    if(config->get_user(user->user_name).only_home)
+    if(user->root_dir.size() > 1)
       dir.erase(0, user->root_dir.size());
     if(dir == "")
       dir = "/";
     message = "257 \"" + dir + "\" is current directory.";
   }
     
-  else if(command == COM_SYST)
+  else if(util.is_set(flag, COM_SYST))
     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.";
-  }
-
-  else if(command == COM_FEAT)
+  else if(util.is_set(flag, COM_FEAT))
   {
     message =  "211-Extensions supported:\r\n";
     message += " SIZE\r\n";
@@ -1065,27 +1040,27 @@ bool Commands::sim1(User *user, string a
     message += "211 End.";
   }
 
-  else if(command == COM_TYPE)
+  else if(util.is_set(flag, 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);
+      sem_wait(&user->user_lock);
+      user->settings.reset(BINARY);
+      sem_post(&user->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);
+      sem_wait(&user->user_lock);
+      user->settings.reset(BINARY);
+      sem_post(&user->user_lock);
     }
     else
       message = "504 'TYPE " + argument + "' not understood.";
   }
 
-  else if(command == COM_MODE)
+  else if(util.is_set(flag, COM_MODE))
   {
     if(argument == "S")
     {
@@ -1095,7 +1070,7 @@ bool Commands::sim1(User *user, string a
       message = "504 'MODE " + argument + "' not understood.";
   }
 
-  else if(command == COM_STRU)
+  else if(util.is_set(flag, COM_STRU))
   {
     if(argument == "F")
       message = "200 Structure set to F.";
@@ -1103,14 +1078,14 @@ bool Commands::sim1(User *user, string a
       message = "504 'STRU " + argument + "' not understood.";
   }
 #ifdef USE_TLS
-  else if(command == COM_PBSZ)
+  else if(util.is_set(flag, COM_PBSZ))
   {
-    if(is_set(user->settings, TLS_ENABLED))
+    if(user->settings.test(TLS_ENABLED))
     {
       if(argument == "0")
       {
         message = "200 PBSZ set for stream transfer.";
-        set_bit(user->settings, TLS_PBSZ);
+        user->settings.set(TLS_PBSZ);
       }
       else
         message = "501 PBSZ has to be set to 0 for use with TLS.";
@@ -1119,20 +1094,20 @@ bool Commands::sim1(User *user, string a
       message = "503 AUTH must be issued and negotiated before PBSZ can be used.";
   }
 
-  else if(command == COM_PROT)
+  else if(util.is_set(flag, COM_PROT))
   {
-    if(!is_set(user->settings, TLS_ENABLED) || !is_set(user->settings, TLS_PBSZ))
+    if(!user->settings.test(TLS_ENABLED) || !user->settings.test(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);
+          user->settings.reset(TLS_PROT_D);
           break;
         case 'P':
           message = "200 Data connection set to PRIVATE level.";
-          set_bit(user->settings, TLS_PROT_D);
+          user->settings.set(TLS_PROT_D);
           break;
         case 'S':
         case 'E':
@@ -1142,16 +1117,35 @@ bool Commands::sim1(User *user, string a
           message = "504 Protection level '" + argument + "' not understood.";
       }
   }
-  else if(command == COM_CCC) // Clear Command Channel
+  else if(util.is_set(flag, COM_CCC)) // Clear Command Channel
   {
-    if(!is_set(user->settings, TLS_ENABLED))
+    if(!user->settings.test(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);
+      user->settings.reset(TLS_ENABLED);
+    }
     }
+  else if(util.is_set(flag, COM_CONF | COM_MIC | COM_ENC))
+  {
+    // note, if we implement som other security messure than TLS we might
+    // need these commands, for now they reside here and only return
+    // a 503 or 537.
+    string command;
+    if(util.is_set(flag, COM_CONF))
+      command = "CONF";
+    else if(util.is_set(flag, COM_MIC))
+      command += "MIC";
+    else
+      command += "ENC";
+
+    if(!user->settings.test(TLS_ENABLED))
+      message = "503 Command " + command
+        + " can not be issued before a valid security exchange.";
+    else
+      message = "537 Command " + command + " not used in TLS mode.";
   }
 #endif // USE_TLS
 
@@ -1160,25 +1154,21 @@ bool Commands::sim1(User *user, string a
   return(true);
 }
 
-bool Commands::sim2(User *user, string argument)
+bool Commands::sim2(User *user, string argument, unsigned int flag)
 {
-  int command;
   string file_name, message;
   struct stat stt;
 
-  command = argument[0];
-  argument.erase(0, 1);
-  
   file_name = parsedir(user, argument);
   if(!(stat(file_name.c_str(), &stt)))
   {
-    if(command == COM_SIZE)
+    if(util.is_set(flag, COM_SIZE))
       message = "213 " + util.itos(stt.st_size);
 
-    else if(command == COM_MDTM)
-      message = "213 " + mod_time(file_name, stt);
+    else if(util.is_set(flag, COM_MDTM))
+      message = "213 " + mod_time(stt);
 
-    else if(command == COM_MLST)
+    else if(util.is_set(flag, COM_MLST))
     {
       message  = "250-Listing " + argument + "\r\n";
       message += mlsx(user, file_name) + "\r\n";
@@ -1192,51 +1182,53 @@ bool Commands::sim2(User *user, string a
   return(true);
 }
 
-string Commands::mod_time(string file_name, struct stat stt)
+string Commands::mod_time(struct stat stt)
 {
-  char *date = new char[100];
-  struct tm *m;
-  string message;
+  char date[16] = {0};
+  struct tm m;
   
-  m = gmtime(&(stt.st_mtime));
-  snprintf(date, 95, "%u%02u%02u%02u%02u%02u", m->tm_year + 1900,m->tm_mon + 1, m->tm_mday, m->tm_hour, m->tm_min, m->tm_sec);
-  message = date;
-  delete[] date;
+  gmtime_r(&(stt.st_mtime), &m);
+  strftime(date, 15, "%Y%m%d%H%M%S", &m);
+  string message = date;
   return(message);
 }
 
 #ifdef USE_TLS
 
-bool Commands::auth(User *user, string argument)
+bool Commands::auth(User *user, string argument, unsigned int flag)
 {
-  int ret;
-  string message;
-  
-  util.upper(argument);
+  transform(argument.begin(), argument.end(), argument.begin(), (int(*)(int))toupper);
   
-  if(argument == "TLS" || argument == "TLS-C")
+  if(argument == "TLS" || argument == "TLS-C" || argument == "SSL")
   {
-    pthread_mutex_lock(&user->lock);
-    user->ctrl_session = Commands::init_tls();
-    set_bit(user->settings, TLS_INITED);
+    if(config->get_user(user->user_name).encryption == DENY)
+    {
+      ftp_send(user, "421 Sorry, you are not allowed to use encryption, disconnecting.");
+      sem_wait(&user->user_lock);
+      user->logged_in = LOGGED_OUT;
+      sem_post(&user->user_lock);
+    }
+    sem_wait(&user->user_lock);
+    Commands::init_tls(user->ctrl_session);
+    user->settings.set(TLS_INITED);
 
-    gnutls_transport_set_ptr(user->ctrl_session, (gnutls_transport_ptr)user->socket_id);
-    pthread_mutex_unlock(&user->lock);
+    gnutls_transport_set_ptr(user->ctrl_session, reinterpret_cast<gnutls_transport_ptr>(user->socket_id));
+    sem_post(&user->user_lock);
     ftp_send(user, "234 Now starting encryption using TLS.");
-    pthread_mutex_lock(&user->lock);
-    ret = gnutls_handshake(user->ctrl_session);
+    sem_wait(&user->user_lock);
 
-    if(ret < 0) // we failed =(
-    {
+    if(gnutls_handshake(user->ctrl_session) < 0)
+    { // failure =(
       user->logged_in = LOGGED_OUT;
-      pthread_mutex_unlock(&user->lock);
-      cout << gnutls_strerror(ret);
+      sem_post(&user->user_lock);
       ftp_send(user, "421 Handshake failed. Disconnecting.");
     }
-    else // wee!
-    {
-      set_bit(user->settings, TLS_ENABLED);
-      pthread_mutex_unlock(&user->lock);
+    else
+    { // wee!
+      user->settings.set(TLS_ENABLED);
+      if(argument == "SSL")
+        user->settings.set(TLS_PROT_D);
+      sem_post(&user->user_lock);
     }
   }
   else
Index: Configuration.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Configuration.cpp,v
retrieving revision 1.38
retrieving revision 1.57
diff -u -p -b -r1.38 -r1.57
--- Configuration.cpp	10 Jul 2004 14:14:45 -0000	1.38
+++ Configuration.cpp	23 Jan 2005 00:23:45 -0000	1.57
@@ -1,3 +1,13 @@
+/////////////////////////////////////////////////////////////////////
+//  $Id: Configuration.cpp,v 1.57 2005/01/23 00:23:45 klas Exp $
+//
+//  Usage: Functions and stuff for Configure class, holding and
+//           reading config stuff.
+//
+//  A part of babyftpd, licensed under the GNU GPL, see the file 
+//    LICENSE for complete information.                                         
+/////////////////////////////////////////////////////////////////////
+
 #define __CONFIGURATION_CPP__
 
 #include "Configuration.h"
@@ -12,26 +22,31 @@ Configuration::Configuration()
   general.port = 21; // default port
   general.default_type = 0;
   general.verbose = 1;
-  general.log_level = 5;
-  general.xferlog = 1; // log all transfers or not?
-  general.xferlog_file = "/var/log/xferlog"; // where transfer logs go
   general.no_daemon = false; // daemon mode or not?
   general.os = "UNIX"; // what OS is this?
   general.admin_email = ""; // admin email?
-  char *tmp = new char[20];
+  general.lifebeat_tick = 300; // run the lifebeat() function every this
+  char tmp[20];
   gethostname(tmp, 20);
   general.welcome_msg = "BabyFTPd ";
   general.welcome_msg += VERSION;
   general.welcome_msg += " Server [";
   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";
+  general.dh_regenerate = 3600; // seconds between diffie-hellman regenerations
+  general.user = 0; // by default when run all as root
+
+  general_user.encryption = ALLOW;
 #endif	
+  logger.log_level = 3; // 3 is probably a sensible default?
+  logger.xferlog = true; // by default all transfers are logged
+  logger.xferlog_file = "/var/log/xferlog"; // here
+  logger.thread_id = false; // we don't want thread ids all over the logs
   
   // 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";
@@ -45,51 +60,43 @@ Configuration::Configuration()
 
 Configuration::~Configuration()
 {
-  if(parsed)
-    delete[] users; 
 }
 
 bool Configuration::parse_config_file()
 {
-  int rows = file_rows(conf_file);
-  string Sgeneral, Suser, user_names;
+  vector<string> file_buff;
+  fill_buff(file_buff, conf_file);
   
-  string *file_buff = new string[rows];
-  fill_buff(file_buff, conf_file, rows);
+  string Sgeneral = get_variables(file_buff, "general", 1);
+  set_general(Sgeneral);
   
-  get_variables(file_buff, rows, Sgeneral, "general");
-  set_general(general, Sgeneral);
+  string Suser = get_variables(file_buff, "general", 2);
+  general_user = set_users(Suser);
   
-  get_variables(file_buff, rows, Suser, "general", 2);
-  set_users(general_user, Suser);
+  string Slog = get_variables(file_buff, "log", 1);
+  set_logger(Slog);
 
-  num_users = count_users(file_buff, rows, user_names) - 1; // first user is 0.
+  string user_names;
+  num_users = count_users(file_buff, user_names) - 1; // first user is 0.
   
   class StringTokenizer *names = new StringTokenizer(user_names, ",", false);
   names->nextToken(); // not nice, but nicer.
   
-  users = new struct users_stru[num_users]; 
-  parsed = true;
+  users.reserve(num_users + 1);
   for(int i = 0; i < num_users; i++)
   {
-    string Susers;
-    get_variables(file_buff, rows, Susers, names->nextToken().c_str(), 2);
-    set_users(users[i], Susers);
+    string Susers = get_variables(file_buff, names->nextToken().c_str(), 2);
+    users.push_back(set_users(Susers));
   }
 
+  parsed = true;
   delete names;
-  delete[] file_buff;
   return true;
 }
 
-void Configuration::set_users(struct users_stru &usr, string &variables)
+struct users_stru Configuration::set_users(string variables)
 {
-  usr.sim_data = general_user.sim_data;
-  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;
+  struct users_stru usr = general_user;
   
   class StringTokenizer *var = new StringTokenizer(variables, "\n", false);
   usr.id = var->nextToken();
@@ -100,56 +107,86 @@ void Configuration::set_users(struct use
     class StringTokenizer *st = new StringTokenizer(str, " ", false);
     string op = st->nextToken();
     string arg = st->nextToken();
-    if(op.compare(users_op[0]) == 0)
+    if(op == users_op[0])
       usr.sim_data = atoi(arg.c_str());
-    else if(op.compare(users_op[1]) == 0)
+    else if(op == users_op[1])
       usr.idle = atoi(arg.c_str());
-    else if(op.compare(users_op[2]) == 0)
+    else if(op == users_op[2])
       usr.noftp = atoi(arg.c_str());
-    else if(op.compare(users_op[3]) == 0)
+    else if(op == users_op[3])
       usr.only_home = atoi(arg.c_str());
-    else if(op.compare(users_op[4]) == 0)
+    else if(op == users_op[4])
       usr.only_passive = atoi(arg.c_str());
-    else if(op.compare(users_op[5]) == 0)
+    else if(op == users_op[5])
       usr.user_limit = atoi(arg.c_str());
+#ifdef USE_TLS
+    else if(op == users_op[6])
+    {
+      if(arg == "allow")
+        usr.encryption = ALLOW;
+      else if(arg == "deny")
+        usr.encryption = DENY;
+      else if(arg == "force")
+        usr.encryption = FORCE;
+    }
+#endif // USE_TLS
     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 << " only_passive=" << usr.only_passive << " user_limit=" << usr.user_limit << endl;
+  string for_logging =
+     "user conf for: " + usr.id
+     + " sim_data=" + util.itos(usr.sim_data)
+     + " idle=" + util.itos(usr.idle)
+     + " noftp=" + util.itos(usr.noftp)
+     + " only_home=" + util.itos(usr.only_home)
+     + " only_passive=" + util.itos(usr.only_passive)
+     + " user_limit=" + util.itos(usr.user_limit)
+#ifdef USE_TLS
+     + " encryption=";
+  if(usr.encryption == ALLOW)
+    for_logging += "allow";
+  else if(usr.encryption == DENY)
+    for_logging += "deny";
+  else if(usr.encryption == FORCE)
+    for_logging += "force"
+#endif // USE_TLS
+     ;
+  logging->log_this(3, TYPE_INFO, for_logging);
+
+  return(usr);
 }
 
 struct users_stru &Configuration::get_user(string name)
 {
-  for(int i = 0; i < num_users; i++)
-    if(users[i].id == name && name != "")
-      return users[i];
+  for(vector<users_stru>::iterator use_it = users.begin();
+      use_it != users.end(); use_it++)
+    if((*use_it).id == name && name != "")
+      return(*use_it);
   return general_user;
 }
 
-int Configuration::count_users(string *&buff, int rows, string &users)
+int Configuration::count_users(vector<string> buff, string &users)
 {
   int ant = 0;
   int deep = 1, curr_deep = 0;
   string name = "user", section = "";
-  for(int i = 0; i < rows; i++)
+  for(vector<string>::iterator buff_it = buff.begin(); buff_it != buff.end(); buff_it++)
   {
-    if(buff[i][0] == '#' || buff[i][0] == '\0')
-      continue;
-    if(strcmp(buff[i].c_str(), "{") == 0)
+    if(*buff_it == "{")
     {
-      if(curr_deep == deep && section.compare(name) == 0)
+      if(curr_deep == deep && section == name)
       {
         ant++;
-        users += buff[i-1] + ",";
+        users += *(buff_it - 1) + ",";
       }
       curr_deep++;
       if(curr_deep == deep) 
-        section = buff[i-1];
+        section = *(buff_it - 1);
     }
-    else if(strcmp(buff[i].c_str(), "}") == 0)
+    else if(*buff_it == "}")
     {
-      if(curr_deep == deep && section.compare(name) == 0)
+      if(curr_deep == deep && section == name)
         return ant;
       curr_deep--;
     }
@@ -157,10 +194,10 @@ int Configuration::count_users(string *&
   return ant;
 }
 
-void Configuration::set_general(struct general_stru &gen, string &variables)
+bool Configuration::set_general(string variables)
 {
   class StringTokenizer *var = new StringTokenizer(variables, "\n", false);
-  gen.id = var->nextToken();
+  general.id = var->nextToken();
   
   while(var->hasMoreTokens())
   {
@@ -168,110 +205,182 @@ void Configuration::set_general(struct g
     class StringTokenizer *st = new StringTokenizer(str, " ", false);
     string op = st->nextToken();
     string arg = st->restTokens();
-    if(op.compare(general_op[0]) == 0)
-      gen.connection_limit = atoi(arg.c_str());
-    else if(op.compare(general_op[1]) == 0)
-      gen.port = atoi(arg.c_str());
-    else if(op.compare(general_op[2]) == 0)
-      gen.default_type = atoi(arg.c_str());
-    else if(op.compare(general_op[3]) == 0)
-      gen.no_daemon = atoi(arg.c_str());
-    else if(op.compare(general_op[4]) == 0)
-      gen.verbose = atoi(arg.c_str());
-    else if(op.compare(general_op[5]) == 0)
-      gen.log_level = atoi(arg.c_str());
-    else if(op.compare(general_op[6]) == 0)
-      gen.xferlog = atoi(arg.c_str());
-    else if(op.compare(general_op[7]) == 0)
-      gen.admin_email = arg.c_str();
-    else if(op.compare(general_op[8]) == 0)
-      gen.welcome_msg = arg.c_str();
-    else if(op.compare(general_op[9]) == 0)
-      gen.xferlog_file = arg.c_str();
+    if(op == general_op[0])
+      general.connection_limit = atoi(arg.c_str());
+    else if(op == general_op[1])
+      general.port = atoi(arg.c_str());
+    else if(op == general_op[2])
+      general.default_type = atoi(arg.c_str());
+    else if(op == general_op[3])
+      general.no_daemon = atoi(arg.c_str());
+    else if(op == general_op[4])
+      general.verbose = atoi(arg.c_str());
+    else if(op == general_op[5])
+      general.admin_email = arg;
+    else if(op == general_op[6])
+      general.welcome_msg = arg;
+    else if(op == general_op[7])
+      general.lifebeat_tick = atoi(arg.c_str());
+    else if(op == general_op[8])
+    {
+      struct passwd *pwd = getpwnam(arg.c_str());
+      if(pwd == NULL)
+        general.user = 0;
+      else
+        general.user = pwd->pw_uid;
+    }
 #ifdef USE_TLS
-    else if(op.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();
+    else if(op == general_op[9])
+      general.tls_key = arg;
+    else if(op == general_op[10])
+      general.tls_cert = arg;
+    else if(op == general_op[11])
+      general.tls_ca = arg;
+    else if(op == general_op[12])
+      general.tls_crl = arg;
+    else if(op == general_op[13])
+      general.dh_regenerate = atoi(arg.c_str());
 #endif
     delete st;
   }
   delete var;
+
+  string for_logging =
+    "general configuration: \
+    connection_limit=" + util.itos(general.connection_limit)
+    + " port=" + util.itos(general.port)
+    + " default_type=" + util.itos(general.default_type)
+    + " verbose=" + util.itos(general.verbose)
+    + " no_daemon=" + util.itos(general.no_daemon)
+    + " os=" + general.os
+    + " admin_email=" + general.admin_email
+    + " welcome_msg=" + general.welcome_msg
+    + " lifebeat_tick=" + util.itos(general.lifebeat_tick)
+    + " user=" + util.itos(general.user)
+#ifdef USE_TLS
+    + " tls_key=" + general.tls_key
+    + " tls_cert=" + general.tls_cert
+    + " tls_ca=" + general.tls_ca
+    + " tls_crl=" + general.tls_crl
+    + " dh_regenerate=" + util.itos(general.dh_regenerate)
+#endif // USE_TLS
+    ;
+  logging->log_this(3, TYPE_INFO, for_logging);
+
+  return(true);
 }
 
-void Configuration::get_variables(string *&buff, int rows, string &str, const char *key, int deep)
+bool Configuration::set_logger(string variables)
 {
-  int curr_deep = 0;
-  string section = "";
-  for(int i = 0; i < rows; i++)
+  class StringTokenizer *var = new StringTokenizer(variables, "\n", false);
+
+  while(var->hasMoreTokens())
   {
-    if(buff[i][0] == '#' || buff[i][0] == '\0')
-      continue;
-    if(strcmp(buff[i].c_str(), "{") == 0)
+    string str = var->nextToken();
+    class StringTokenizer *st = new StringTokenizer(str, " ", false);
+    string op = st->nextToken();
+    string arg = st->restTokens();
+    if(op == log_op[0])
+      logger.log_level = atoi(arg.c_str());
+    else if(op == log_op[1])
     {
-      section = buff[i-1];
-      curr_deep++;
-      if(curr_deep == deep)
-        str = section + "\n";
+      if(!(atoi(arg.c_str())))
+        logger.xferlog = false;
     }
-    else if(strcmp(buff[i].c_str(), "}") == 0)
+
+    else if(op == log_op[2])
+      logger.xferlog_file = arg;
+
+    else if(op == log_op[3])
     {
-      if(curr_deep == deep && section == key)
-        return;
-      curr_deep--;
+      if(arg != "syslog")
+        logger.authlog = arg;
     }
-    else if(curr_deep == deep && section == key && buff[i+1][0] != '{')
+
+    else if(op == log_op[4])
     {
-      str += buff[i] + "\n";
+      if(arg != "syslog")
+        logger.infolog = arg;
     }
-  }
-}
 
-void Configuration::fill_buff(string *&buff, string &filename, int rows)
-{
-  ifstream fin;
-  fin.open(filename.c_str(), ios::in);
+    else if(op == log_op[5])
+    {
+      if(arg != "syslog")
+        logger.debuglog = arg;
+    }
   
-  for(int i = 0; !fin.eof() && i < rows; i++)
+    else if(op == log_op[6])
   {
-    char *buffer = new char[251];
-    memset(buffer, '\0', 251);
-    fin.getline(buffer, 250);
-    trim(buffer);
-    buff[i] = string(buffer);
-    delete[] buffer;
+      if((atoi(arg.c_str())))
+        logger.thread_id = true;
   }
 
-  fin.close();
+    delete st;
+  }
+  delete var;
+
+  string for_logging =
+    "logging configuration: \
+    log_level=" + util.itos(logger.log_level)
+    + " xferlog_enable=" + util.itos(logger.xferlog)
+    + " xferlog_file=" + logger.xferlog_file + " "
+    + log_op[3] + (logger.authlog.empty() ?
+        "=syslog " : "=" + logger.authlog + " ")
+    + log_op[4] + (logger.infolog.empty() ?
+        "=syslog " : "=" + logger.infolog + " ")
+    + log_op[5] + (logger.debuglog.empty() ?
+        "=syslog " : "=" + logger.debuglog)
+    + " thread_id=" + util.itos(logger.thread_id)
+    ;
+  logging->log_this(3, TYPE_INFO, for_logging);
+
+  return(true);
 }
 
-int Configuration::file_rows(string &filename)
+string Configuration::get_variables(vector<string> &buff, const char *key, int deep)
 {
-  ifstream fin;
-  fin.open(filename.c_str(), ios::in);
-  int rows = 0;
-  while(!fin.eof())
+  int curr_deep = 0;
+  string section, str;
+  for(vector<string>::iterator buff_it = buff.begin(); buff_it != buff.end(); buff_it++)
+  {
+    if((*buff_it).empty())
+      continue;
+    size_t comment = (*buff_it).find_first_of('#');
+    if(comment != string::npos)
+      (*buff_it).erase(comment);
+    if(*buff_it == "{")
   {
-    if(fin.get() == '\n')
-      rows++;
+      section = *(buff_it - 1);
+      curr_deep++;
+      if(curr_deep == deep)
+        str = section + "\n";
   }
-  fin.close();
-  return rows;
+    else if(*buff_it == "}")
+    {
+      if(curr_deep == deep && section == key)
+        break;
+      curr_deep--;
+    }
+    else if(curr_deep == deep && section == key && *(buff_it + 1) != "{")
+      str += *buff_it + "\n";
+  }
+  return(str);
 }
 
-void Configuration::trim(char *str)
+void Configuration::fill_buff(vector<string> &buff, string filename)
 {
-    while(str[0] == ' ')
-    {
-      for(unsigned int i = 0; i < strlen(str); i++)
-        str[i] = str[i+1];
-    }
-    while(str[strlen(str)-1] == ' ')
+  ifstream fin;
+  fin.open(filename.c_str(), ifstream::in);
+
+  while(fin.good())
     {
-      str[strlen(str)-1] = '\0';
+    string buffer;
+    getline(fin, buffer);
+    util.clean_string(buffer);
+    if(!fin.good() || buffer.empty())
+      continue;
+    buff.push_back(buffer);
     }
+
+  fin.close();
 }
Index: Configuration.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Configuration.h,v
retrieving revision 1.21
retrieving revision 1.33
diff -u -p -b -r1.21 -r1.33
--- Configuration.h	10 Jul 2004 14:14:45 -0000	1.21
+++ Configuration.h	22 Jan 2005 23:06:05 -0000	1.33
@@ -1,11 +1,35 @@
+/////////////////////////////////////////////////////////////////////
+//  $Id: Configuration.h,v 1.33 2005/01/22 23:06:05 klas Exp $
+//
+//  Usage: Configure class, reading the config file and holding
+//           configurish variables.
+//
+//  A part of babyftpd, licensed under the GNU GPL, see the file                
+//    LICENSE for complete information.                                         
+/////////////////////////////////////////////////////////////////////
+
+
 #ifndef __CONFIGURATION_H__
 #define __CONFIGURATION_H__
 
 #ifdef __CONFIGURATION_CPP__
 #include "generic.h"
 #include "StringTokenizer.h"
+#include "Utilities.h"
+#include "Log.h"
 #endif
 
+struct log_stru
+{
+  int log_level;
+  bool xferlog;
+  string xferlog_file;
+  string authlog;
+  string infolog;
+  string debuglog;
+  bool thread_id;
+};
+
 struct users_stru
 {
   string id;    
@@ -15,6 +39,9 @@ struct users_stru
   int only_home;
   int only_passive;
   int user_limit;
+#ifdef USE_TLS
+  int encryption;
+#endif // USE_TLS
 };
 
 struct general_stru
@@ -24,48 +51,48 @@ struct general_stru
   int port; 
   int default_type;
   int verbose;
-  int log_level;
-  int xferlog;
   bool no_daemon;
   string os;
   string admin_email;
   string welcome_msg;
-  string xferlog_file;
+  int lifebeat_tick;
+  uid_t user;
 #ifdef USE_TLS  
   string tls_key;
   string tls_cert;
   string tls_ca;
   string tls_crl;
+  int dh_regenerate;
 #endif
 };
 
 class Configuration
 {
   private:
-    struct users_stru *users;
+    vector<users_stru> users;
     int num_users;
     bool parsed;
-    int count_users(string *&buff, int rows, string &users);
-    int file_rows(string &filename);
-    void fill_buff(string *&buff, string & filename, int rows);
-    void get_variables(string *&buff, int rows, string &str, const char *key, int deep = 1);
-    void set_general(struct general_stru &gen, string &variables);
-    void set_users(struct users_stru &usr, string &variables);
-    void trim(char *str);
+    int count_users(vector<string>, string&);
+    void fill_buff(vector<string>&, string);
+    string get_variables(vector<string>&, const char*, int);
+    bool set_general(string variables);
+    struct users_stru set_users(string);
+    bool set_logger(string variables);
   public:
     bool parse_config_file();
     string conf_file;
+    struct log_stru logger;
     struct general_stru general;
     struct users_stru general_user;
     Configuration(void);
-    struct users_stru &get_user(string name);
+    struct users_stru &get_user(string);
     ~Configuration(void);
 };
 
 #ifdef USE_TLS
 const string general_op[14] = 
 #else
-const string general_op[10] =
+const string general_op[9] =
 #endif // USE_TLS
 {
   "connection_limit",
@@ -73,20 +100,35 @@ const string general_op[10] =
   "default:type", 
   "no_daemon", 
   "verbose", 
-  "log_level", 
-  "xferlog", 
   "admin_email", 
   "welcome_msg",
-  "xferlog_file"
+  "lifebeat_tick",
+  "user"
 #ifdef USE_TLS
   ,"tls_key",
   "tls_cert",
   "tls_ca",
-  "tls_crl"
+  "tls_crl",
+  "dh_regenerate"
 #endif
 };
 
+const string log_op[7] =
+{
+  "log_level",
+  "xferlog",
+  "xferlog_file",
+  "authlog",
+  "infolog",
+  "debuglog",
+  "thread_id"
+};
+
+#ifdef USE_TLS
+const string users_op[7] =
+#else
 const string users_op[6] =
+#endif // USE_TLS
 {
   "sim_data", 
   "idle",
@@ -94,6 +136,16 @@ const string users_op[6] =
   "only_home",
   "only_passive",
   "user_limit"
+#ifdef USE_TLS
+  ,"encryption"
+#endif // USE_TLS
 };
 
+extern class Utilities util;
+
+#ifdef USE_TLS
+#define ALLOW 1
+#define FORCE 2
+#define DENY 3
+#endif // USE_TLS
 #endif
Index: FIX
===================================================================
RCS file: FIX
diff -N FIX
--- FIX	28 Jun 2004 21:56:40 -0000	1.24
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,29 +0,0 @@
-* stripping or handling telnet commands -kl 2003-01-11
-
-* Aborting data connections/threads with ABOR and SIGTERM -kl 2003-01-11
-
-* creating an own interface for syslog, so we can put that where we want. -kl 2003-01-11
-
-* Implement SMNT command -jl 2003-01-12
-
-* Implement ALLO command -jl 2003-01-12
-
-* Implement STOU command -jl 2003-01-12
-
-* Implement ACCT command (not interesting at this point) -jl 2003-01-12
-
-* Implement ABOR command -jl 2003-01-12
-
-* Implement SITE command -jl 2003-01-12
-
-* Implement STAT command -jl 2003-01-12
-
-* 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
-
-* implement correct xferlog behaviour (http://aolserver.am.net/docs/2.3/ftp-ch4.htm) -kl 2004-05-06
-
-* Implement correct behaviour for ASCII mode. (convert CR LF to CR). -kl 2004-06-13
-
-* 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.118
retrieving revision 1.143
diff -u -p -b -r1.118 -r1.143
--- Handler.cpp	10 Jul 2004 14:14:45 -0000	1.118
+++ Handler.cpp	22 Jan 2005 17:18:51 -0000	1.143
@@ -1,5 +1,6 @@
 /////////////////////////////////////////////////////////////////////
-//  File: Handler.cpp
+// $Id: Handler.cpp,v 1.143 2005/01/22 17:18:51 klas Exp $
+//
 // Usage: the main loop for connected clients and interpreter for
 //        ftp commands.
 //
@@ -14,7 +15,7 @@
 
 struct command_info Handler::cmd_info[] = {
   { "SYST",  0, &Commands::sim1, COM_SYST,  HLP_SYST }, //0
-  { "HELP", -1, &Commands::help, 0,         HLP_HELP },
+  { "HELP", -1, &Commands::help, BEFORE_USER, HLP_HELP },
   { "RETR", -1, &Commands::retr, 0,         HLP_RETR },
   { "STOR", -1, &Commands::stor, COM_STOR,  HLP_STOR },
   { "PORT",  1, &Commands::port, COM_PORT,  HLP_PORT },
@@ -22,8 +23,8 @@ struct command_info Handler::cmd_info[] 
   { "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
+  { "CWD",  -1, &Commands::cwd,  COM_CWD,   HLP_CWD  },
+  { "CDUP",  0, &Commands::cwd,  COM_CDUP,  HLP_CDUP }, //10
   { "MKD",  -1, &Commands::mkd,  0,         HLP_MKD  },
   { "RMD",  -1, &Commands::rmd,  0,         HLP_RMD  },
   { "DELE", -1, &Commands::dele, 0,         HLP_DELE },
@@ -33,87 +34,77 @@ struct command_info Handler::cmd_info[] 
   { "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 },
+  { "USER",  1, &Commands::user, BEFORE_USER, HLP_USER }, //20
+  { "PASS",  1, &Commands::pass, BEFORE_USER, HLP_PASS },
+  { "QUIT", -1, &Commands::quit, BEFORE_USER, HLP_QUIT },
   { "NOOP",  0, &Commands::sim1, COM_NOOP,  HLP_NOOP },
   { "MDTM", -1, &Commands::sim2, COM_MDTM,  HLP_MDTM },
-  { "FEAT",  0, &Commands::sim1, COM_FEAT,  HLP_FEAT }, //25
+  { "FEAT",  0, &Commands::sim1, COM_FEAT | BEFORE_USER, HLP_FEAT }, //25
   { "RNFR", -1, &Commands::rname, COM_FROM, HLP_RNFR },
   { "RNTO", -1, &Commands::rname, COM_TO,   HLP_RNTO },
   { "MLST", -1, &Commands::sim2, COM_MLST,  HLP_MLST },
   { "MLSD", -1, &Commands::list, COM_MLSD,  HLP_MLSD },
-  { "APPE", -1, &Commands::stor, COM_APPE,  HLP_APPE },
-  { "OPTS", -1, &Commands::opts, 0,         HLP_OPTS }, //30
+  { "APPE", -1, &Commands::stor, COM_APPE,  HLP_APPE }, //30
+  { "OPTS", -1, &Commands::opts, 0,         HLP_OPTS },
   { "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 },
+  { "XCWD", -1, &Commands::cwd,  COM_CWD,   HLP_CWD  }, // as of rfc 1123
+  { "XPWD",  0, &Commands::sim1, COM_PWD,   HLP_PWD  },
+  { "XCUP",  0, &Commands::sim1, COM_CDUP,  HLP_CDUP }, //35
+  { "XMKD", -1, &Commands::mkd,  0,         HLP_MKD  },
+  { "XRMD", -1, &Commands::rmd,  0,         HLP_RMD  },
 #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
+  { "AUTH",  1, &Commands::auth, BEFORE_USER, HLP_AUTH },
+  { "PBSZ",  1, &Commands::sim1, COM_PBSZ | BEFORE_USER, HLP_PBSZ },
+  { "PROT",  1, &Commands::sim1, COM_PROT | BEFORE_USER, HLP_PROT }, //40
   { "CCC",  0,  &Commands::sim1, COM_CCC,   HLP_CCC },
+  // these commands are required to be understood by rfc2228, even if
+  // we don't use them.
+  { "CONF", -1, &Commands::sim1, COM_CONF | BEFORE_USER, "" },
+  { "MIC",  -1, &Commands::sim1, COM_MIC | BEFORE_USER, "" },
+  { "ENC",  -1, &Commands::sim1, COM_ENC | BEFORE_USER, "" },
 #endif // USE_TLS                
-  { "\n",    0, NULL           , 0,         NULL     } // this goes last.
+  // An empty hlp field means no help will be displayed about the 
+  // object, what is noteworthy is that those entries must come last 
+  // as to not screwup the format when we display the help list.
+  { "ABOR",  0, &Commands::abor, 0,         ""       },
+  { "\n",    0, NULL           , LAST_COM,  ""       } // this goes last.
 };
 
 class User *Handler::initializer(int socket_id)
 {
   struct sockaddr_in addr;
-  string log;
   socklen_t len = sizeof(sockaddr_in);
-  class dns_wrapper *dns_wrap = new dns_wrapper;
   
-  getpeername(socket_id, (struct sockaddr*)&addr, &len);
+  getpeername(socket_id, reinterpret_cast<struct sockaddr*>(&addr), &len);
   class User *user = new User(socket_id, addr); // initiating the user object, with initial stuff
 
-  log = "new connection from: ";
-  log.append(inet_ntoa(addr.sin_addr));
-  pthread_mutex_lock(&sys_log_lock);
-  syslog(LOG_DEBUG, log.c_str());
-  pthread_mutex_unlock(&sys_log_lock);
+  string log = "new connection from: ";
+  log += inet_ntoa(addr.sin_addr);
+  logging->log_this(3, TYPE_INFO, log);
 
-  pthread_mutex_lock(&user_list_mutex);
+  sem_wait(&user_list_lock);
   user_list.push_back(user);
-  pthread_mutex_unlock(&user_list_mutex);
+  sem_post(&user_list_lock);
   
-  if((int)user_list.size() > config->general.connection_limit)
+  string message;
+  if(static_cast<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.");
-    pthread_mutex_lock(&user->lock);
-    close(user->socket_id);
+    message = "421 Maximum number of connections reached, please try again later.";
+    sem_wait(&user->user_lock);
     user->logged_in = LOGGED_OUT;
-    pthread_mutex_unlock(&user->lock);
+    sem_post(&user->user_lock);
   }
+  else
+  {
+    // dns lookup should go here.. find a nice way of doing it.
+    message = "220 " + config->general.welcome_msg;
+  }
+  comm.ftp_send(user, message);
   
-  dns_wrap->addr = user->ctrl_addr;
-  dns_wrap->host_name = &user->host_name;
-  pthread_mutex_lock(&user->lock);
-  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);
   return(user);
 }
 
-void *dns_lookup(void *dns_wrap)
-{
-  struct hostent *host_struct;
-  int ip = inet_addr(inet_ntoa(((class dns_wrapper*)dns_wrap)->addr.sin_addr));
-  host_struct = gethostbyaddr((char*)&ip, sizeof(ip), AF_INET);
-  if(host_struct)
-    *((class dns_wrapper*)dns_wrap)->host_name = host_struct->h_name;
-
-  cout << *((class dns_wrapper*)dns_wrap)->host_name << endl;
-  delete (class dns_wrapper*)dns_wrap;
-  return(NULL);
-}
-
 bool Handler::init(User *user)
 {
   while(true)
@@ -129,49 +120,60 @@ bool Handler::init(User *user)
 
 bool Handler::interpreter(User *user, string command)
 {
-  string argument;
-  int words, cmd;
+  string argument, message;
+  int cmd = 0;
+  class StringTokenizer *st = NULL;
+
+  int words = util.clean_string(command);
 
-  if((words = util.clean_string(command)) > 0)
+  if(words > 0)
   {
-    class StringTokenizer *st = new StringTokenizer(command, " ", true);
+    st = new StringTokenizer(command, " ", true);
     command = st->nextToken();
+  }
+
+  transform(command.begin(), command.end(), command.begin(), (int(*)(int))toupper);
+  if(st)
+  {
+    if(command == "SITE")
+    {
+      st->nextToken();
+      command += " " + st->nextToken();
+      transform(command.begin() + 4, command.end(), command.begin() + 4, (int(*)(int))toupper);
+    }
     argument = st->restTokens();
-    delete st;
+    delete(st);
   }
-  util.upper(command);
+
   if(command != "PASS")
-    cout << command << "-" << argument << endl;
+    logging->log_this(5, TYPE_DEBUG, command + "-" + argument);
   else
-    cout << "PASS-********" << endl;
+    logging->log_this(5, TYPE_DEBUG, "PASS-**********");
   
-  for(cmd = 0; cmd_info[cmd].command != "\n"; cmd++)
+  for(; cmd_info[cmd].extra_flag != LAST_COM; cmd++)
     if(cmd_info[cmd].command == command)
       break;
     
-  if(cmd_info[cmd].command == "\n")
-    comm.ftp_send(user, "500 '" + command + "' not understood.");
+  if(cmd_info[cmd].extra_flag == LAST_COM)
+    message = "500 '" + command + "' not understood.";
+  else
+  {
+    if(user->logged_in < LOGGED_IN && !util.is_set(cmd_info[cmd].extra_flag, BEFORE_USER))
+      message = "530 Login with 'USER' and 'PASS' first.";
   else
   {
     if((user->temp_buf.size() > 0) && command != "RNTO")
       user->temp_buf.erase(); // we don't want this hanging around forever.
 
-    if(user->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.");
+        message = "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.");
+        message = "501 '" + command + "': to many arguments to command.";
       else
-      {
-        if(cmd_info[cmd].extra_flag > 0)
-          argument.insert(argument.begin(), cmd_info[cmd].extra_flag);
-        bool (Commands::*pf)(User*, string) = cmd_info[cmd].pf;
-        (comm.*pf)(user, argument);
-      }
+        ((comm.*cmd_info[cmd].pf) (user, argument, cmd_info[cmd].extra_flag));
     }
   }
+  if(message.size() > 0)
+    comm.ftp_send(user, message);
   return(true);
 }
Index: Handler.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Handler.h,v
retrieving revision 1.40
retrieving revision 1.47
diff -u -p -b -r1.40 -r1.47
--- Handler.h	18 Jun 2004 19:42:43 -0000	1.40
+++ Handler.h	8 Jan 2005 16:22:02 -0000	1.47
@@ -1,5 +1,6 @@
 /////////////////////////////////////////////////////////////////////
-//  File: Handler.h
+// $Id: Handler.h,v 1.47 2005/01/08 16:22:02 klas Exp $
+//
 // Usage: the main loop for connected clients and interpreter for
 //        ftp commands.
 //
@@ -17,14 +18,15 @@
 #include "Configuration.h"
 #include "Utilities.h"
 #include "StringTokenizer.h"
+#include "Log.h"
 #endif
 
 struct command_info {
-  const string command;
+  const string command; //the command name
   const int ant_arg; //Number of arguments. -1 means variable size.
-  bool (Commands::*pf)(User*, string);
-  const int extra_flag;
-  const char *hlp_info;
+  bool (Commands::*pf)(User*, string, unsigned int); //the function we will call
+  const unsigned int extra_flag; //extra flags that is needed by that function
+  const string hlp_info; //the help field, if this is empty no help will be displayed
 };
 
 class Handler
@@ -37,20 +39,11 @@ class Handler
     static struct command_info cmd_info[];
 };
 
-class dns_wrapper
-{
-  public:
-    struct sockaddr_in addr;
-    string *host_name;
-};
-
 extern class Commands comm;
 extern class Configuration *config;
 extern bool term;
 extern bool hup;
 extern vector<User*> user_list;
-extern pthread_mutex_t user_list_mutex;
-extern pthread_mutex_t sys_log_lock;
-void *dns_lookup(void*);
+extern sem_t user_list_lock;
 
 #endif
Index: Log.cpp
===================================================================
RCS file: Log.cpp
diff -N Log.cpp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Log.cpp	23 Jan 2005 17:56:47 -0000	1.21
@@ -0,0 +1,187 @@
+///////////////////////////////////////////////////////////////////
+//  $Id: Log.cpp,v 1.21 2005/01/23 17:56:47 klas Exp $
+//
+//  Usage: Logging functions etc.
+//
+//  A part of babyftpd, licensed under the GNU GPL, see the file
+//    LICENSE for complete information.
+/////////////////////////////////////////////////////////////////////
+
+#define __LOG_CPP__
+#include "Log.h"
+
+Log::Log(void)
+{
+  sem_init(&this->to_log_lock, 0, 1); // 1 means it is not locked
+  sem_init(&this->data_ready, 0, 0); // and 0 means it is locked, flagged to 1 when there is data
+
+  openlog("BabyFTPd", LOG_PID, LOG_FTP);
+}
+
+Log::~Log(void)
+{
+  closelog();
+
+  sem_destroy(&this->to_log_lock);
+  sem_destroy(&this->data_ready);
+}
+
+void Log::log_this(int log_level, int log_type, string log_message)
+{
+  if(log_level > config->logger.log_level && log_type != TYPE_XFER)
+    return; // no point in pushing data that wont be logged..
+
+  struct log_data temp_data = {log_level, log_type, log_message};
+
+  if(temp_data.type != TYPE_XFER && config->logger.thread_id)
+    temp_data.message += " (" + util.itos(pthread_self()) + ")";
+
+  sem_wait(&this->to_log_lock);
+  this->to_log.push_back(temp_data);
+  sem_post(&this->to_log_lock);
+
+  sem_post(&this->data_ready); // bump data_ready to tell main thread there is data
+}
+
+
+void Log::log_main(void)
+{
+  setuid(config->general.user);
+  while(true)
+  {
+    sem_wait(&this->data_ready); // if data_ready is zero we wait for it to increase
+
+    if(this->to_log.size() == 0)
+    {
+      this->log_this(3, TYPE_DEBUG, "data->ready triggered without there being any data to log.");
+      continue;
+    }
+
+    sem_wait(&this->to_log_lock);
+    struct log_data temp_data = this->to_log.front();
+    this->to_log.erase(this->to_log.begin());
+    sem_post(&this->to_log_lock);
+
+    switch(temp_data.level)
+    {
+      case 0:
+        temp_data.level = LOG_CRIT;
+        break;
+
+      case 1:
+        temp_data.level = LOG_ERR;
+        break;
+
+      case 2:
+        temp_data.level = LOG_WARNING;
+        break;
+
+      case 3:
+        temp_data.level = LOG_NOTICE;
+        break;
+
+      case 4:
+        temp_data.level = LOG_INFO;
+        break;
+
+      case 5:
+      default: // so we get everything else as well ;)
+        temp_data.level = LOG_DEBUG;
+        break;
+    }
+
+    switch(temp_data.type)
+    {
+      case TYPE_XFER:
+        this->xferlog(temp_data);
+        break;
+
+      case TYPE_AUTH:
+        this->authlog(temp_data);
+        break;
+
+      case TYPE_INFO:
+        this->infolog(temp_data);
+        break;
+
+      case TYPE_DEBUG:
+        this->debuglog(temp_data);
+        break;
+    }
+  }
+}
+
+void Log::xferlog(struct log_data data_to_log)
+{
+  ofstream xstream;
+  xstream.open(config->logger.xferlog_file.c_str(),
+      ofstream::out | ofstream::app);
+  if(!xstream)
+    log_this(2, TYPE_INFO, "Failed to open xferlog file \""
+        + config->logger.xferlog_file  + "\" for writing, check permissions.");
+  else
+  {
+    xstream << data_to_log.message << endl;
+    xstream.close();
+  }
+}
+
+void Log::authlog(struct log_data data_to_log)
+{
+  if(config->logger.authlog.empty())
+    syslog(LOG_AUTHPRIV | data_to_log.level, data_to_log.message.c_str());
+  else
+  {
+    ofstream astream;
+    astream.open(config->logger.authlog.c_str(),
+        ofstream::out | ofstream::app);
+    if(!astream)
+      log_this(2, TYPE_INFO, "Failed to open authlog file \""
+          + config->logger.authlog  + "\" for writing, check permissions.");
+    else
+    {
+      astream << data_to_log.message << endl;
+      astream.close();
+    }
+  }
+}
+
+  void Log::infolog(struct log_data data_to_log)
+  {
+  if(config->logger.infolog.empty())
+    syslog(LOG_FTP | data_to_log.level, data_to_log.message.c_str());
+  else
+  {
+    ofstream istream;
+    istream.open(config->logger.infolog.c_str(),
+        ofstream::out | ofstream::app);
+    if(!istream)
+      log_this(2, TYPE_INFO, "Failed to open infolog file \""
+          + config->logger.infolog  + "\" for writing, check permissions.");
+    else
+    {
+      istream << data_to_log.message << endl;
+      istream.close();
+    }
+  }
+}
+
+void Log::debuglog(struct log_data data_to_log)
+{
+  if(config->logger.infolog.empty())
+    syslog(LOG_FTP | data_to_log.level, data_to_log.message.c_str());
+  else
+  {
+    ofstream dstream;
+    dstream.open(config->logger.debuglog.c_str(),
+        ofstream::out | ofstream::app);
+    if(!dstream)
+      log_this(2, TYPE_INFO, "Failed to open debuglog file \""
+          + config->logger.debuglog  + "\" for writing, check permissions.");
+    else
+    {
+      dstream << data_to_log.message << endl;
+      dstream.close();
+    }
+  }
+}
Index: Log.h
===================================================================
RCS file: Log.h
diff -N Log.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Log.h	17 Jan 2005 22:21:44 -0000	1.8
@@ -0,0 +1,53 @@
+/////////////////////////////////////////////////////////////////////
+//  $Id: Log.h,v 1.8 2005/01/17 22:21:44 klas Exp $
+//
+//  Usage: Logging defines, functions etc.
+//
+//  A part of babyftpd, licensed under the GNU GPL, see the file
+//    LICENSE for complete information.
+/////////////////////////////////////////////////////////////////////
+
+#ifndef __LOG_H__
+#define __LOG_H__
+
+#ifdef __LOG_CPP__
+#include "generic.h"
+#include "Configuration.h"
+#include "Utilities.h"
+#endif // __LOG_CPP__
+
+class Log
+{
+  private:
+    vector<struct log_data> to_log;
+    sem_t to_log_lock; // locking the vector when we push/pop it
+    sem_t data_ready; // flagging this when there is data available for logging
+    void xferlog(struct log_data);
+    void authlog(struct log_data);
+    void infolog(struct log_data);
+    void debuglog(struct log_data);
+
+  public:
+    void log_main(void);
+    void log_this(int, int, string);
+    Log(void);
+    ~Log(void);
+};
+
+
+struct log_data
+{
+  int level;
+  int type;
+  string message;
+};
+
+extern class Log *logging;
+extern class Configuration *config;
+
+#define TYPE_XFER 1
+#define TYPE_AUTH 2
+#define TYPE_INFO 3
+#define TYPE_DEBUG 4
+
+#endif // __LOG_H__
Index: Makefile.in
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Makefile.in,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -p -b -r1.4 -r1.5
--- Makefile.in	20 May 2004 21:13:10 -0000	1.4
+++ Makefile.in	21 Nov 2004 23:00:27 -0000	1.5
@@ -1,4 +1,5 @@
 # babyftpd Makefile.in - Makefile template used by 'configure'
+# $Id: Makefile.in,v 1.5 2004/11/21 23:00:27 klas Exp $
 #
 
 # C compiler to use
@@ -8,7 +9,7 @@ CXXFLAGS = @CXXFLAGS@ -D_REENTRANT
 
 LIBS = @LIBS@
 
-OBJFILES = User.o Utilities.o Configuration.o Commands.o Commands2.o Handler.o babyftpd.o StringTokenizer.o
+OBJFILES = User.o Utilities.o Configuration.o Commands.o Commands2.o Handler.o babyftpd.o StringTokenizer.o Log.o
 
 default: all
 
Index: README
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/README,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -p -b -r1.12 -r1.13
--- README	10 Jul 2004 14:20:19 -0000	1.12
+++ README	7 Oct 2004 20:55:20 -0000	1.13
@@ -14,7 +14,7 @@ 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
+http://www.ford-hutchinson.com/~fh-1-pfh/draft-murray-auth-ftp-ssl-15.txt
 
 more standards and the like will follow here..
 
Index: StringTokenizer.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/StringTokenizer.cpp,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -p -b -r1.8 -r1.9
--- StringTokenizer.cpp	20 May 2004 21:33:43 -0000	1.8
+++ StringTokenizer.cpp	16 Jan 2005 01:19:12 -0000	1.9
@@ -1,5 +1,6 @@
 /////////////////////////////////////////////////////////////////////
-//  File: StringTokenizer.cpp
+// $Id: StringTokenizer.cpp,v 1.9 2005/01/16 01:19:12 klas Exp $
+//
 // Usage: Used to chop up a string in "tokens"
 //
 //  A part of babyftpd, licensed under the GNU GPL, see the file
@@ -64,7 +65,7 @@ bool StringTokenizer::isDelimiter(char c
 int StringTokenizer::skipDelimiters(int startPos) 
 {
   if (delimiters.compare("") == 0)  
-    cout << "Error: NullPointerException." << endl;
+    logging->log_this(2, TYPE_INFO, "Error: NullPointerException.");
     
   int position = startPos;
   while (!retDelims && position < maxPosition) {
@@ -92,7 +93,7 @@ string StringTokenizer::nextToken()
   newPosition = -1;
 
   if (currentPosition >= maxPosition)
-    cout << "NoSuchElementException." << endl;
+    logging->log_this(2, TYPE_INFO, "NoSuchElementException.");
   
   int start = currentPosition;
   currentPosition = scanToken(currentPosition);
Index: StringTokenizer.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/StringTokenizer.h,v
retrieving revision 1.6
retrieving revision 1.8
diff -u -p -b -r1.6 -r1.8
--- StringTokenizer.h	20 May 2004 21:33:43 -0000	1.6
+++ StringTokenizer.h	16 Jan 2005 01:19:12 -0000	1.8
@@ -1,16 +1,18 @@
 /////////////////////////////////////////////////////////////////////
-//  File: StringTokenizer.h
+// $Id: StringTokenizer.h,v 1.8 2005/01/16 01:19:12 klas Exp $
+//
 // Usage: Used to chop up a string in "tokens"
 //
 //  A part of babyftpd, licensed under the GNU GPL, see the file
 //    LICENSE for complete information.
 /////////////////////////////////////////////////////////////////////
 
-#ifndef __STRINGTOKENIZER_H_
-#define __STRINGTOKENIZER_H_
+#ifndef __STRINGTOKENIZER_H__
+#define __STRINGTOKENIZER_H__
 
 #ifdef __STRINGTOKENIZER_CPP__
 #include "generic.h"
+#include "Log.h"
 #endif
 
 class StringTokenizer
Index: User.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/User.cpp,v
retrieving revision 1.51
retrieving revision 1.70
diff -u -p -b -r1.51 -r1.70
--- User.cpp	10 Jul 2004 14:14:45 -0000	1.51
+++ User.cpp	23 Jan 2005 01:34:53 -0000	1.70
@@ -1,5 +1,6 @@
 /////////////////////////////////////////////////////////////////////
-//  File: User.cpp
+// $Id: User.cpp,v 1.70 2005/01/23 01:34:53 klas Exp $
+//
 // Usage: Holds information about connected users.
 //
 //  A part of babyftpd, licensed under the GNU GPL, see the file
@@ -11,60 +12,96 @@
 #include "User.h"
 
 Data::Data(User *user, int file_id, string file_name, int trans_type){
-  pthread_mutex_init(&data_lock, NULL);
+  sem_wait(&user->user_lock);
+  sem_init(&data_lock, 0, 1);
+
   Data::file_id = file_id;
   Data::file_name = file_name;
-  Data::data_id = user->data_id;
   Data::data_addr = user->data_addr;
   Data::settings = user->settings;
   Data::user = user;
   if(trans_type == 1)
-    set_bit(this->settings, RETR_TRANS);
-  set_bit(Data::settings, OPEN);
+    this->settings.set(RETR_TRANS);
+
+  this->settings.set(DATA_OPEN);
   Data::rest_pos = 0;
 #ifdef USE_TLS
-  remove_bit(Data::settings, TLS_INITED);
-  remove_bit(Data::settings, TLS_ENABLED);
+  this->settings.reset(TLS_INITED);
+  this->settings.reset(TLS_ENABLED);
 #endif // USE_TLS
   
-  if(!is_set(user->settings, PASSIVE))
+  if(user->settings.test(PASSIVE))
+  {
+    socklen_t len = sizeof(this->data_addr);
+    if((this->data_id = accept(user->data_id, reinterpret_cast<struct sockaddr*>(&this->data_addr), &len)) < 0)
+      this->settings.reset(DATA_OPEN);
+    close(user->data_id);
+  }
+  else // ACTIVE
   { 
     if(this->data_id)
       close(Data::data_id);
-    this->data_id = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-    if(connect(this->data_id, (struct sockaddr*)&this->data_addr, sizeof(this->data_addr)) < 0)
-      remove_bit(Data::settings, OPEN);
     
-  }
-  else
+    this->data_id = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    int on = 1;
+    setsockopt(this->data_id, SOL_SOCKET, SO_REUSEADDR,
+        (static_cast<void*>(&on)), sizeof(on));
+    struct sockaddr_in my_addr;
+    my_addr.sin_port = htons(20);
+    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+    // active connections should originate at port 20, for that we need root.
+    uid_t old_uid = geteuid();
+    seteuid(0);
+    int ret = bind(this->data_id, reinterpret_cast<struct sockaddr*>
+        (&my_addr), sizeof(my_addr));
+    seteuid(old_uid);
+    if(ret != 0)
+      logging->log_this(3, LOG_INFO,
+          "Failed to bind to data port 20, falling back to arbitrary port.");
+
+    if(connect(this->data_id, reinterpret_cast<struct sockaddr*>
+          (&this->data_addr), sizeof(this->data_addr)) < 0)
+      this->settings.reset(DATA_OPEN);
+  }
+  // if the data connection comes from another host than the
+  // ctrl connection we drop it.. unless the user is tls protected.
+  if(this->data_addr.sin_addr.s_addr != user->ctrl_addr.sin_addr.s_addr)
   { 
-    socklen_t len = sizeof(this->data_addr);
-    if((this->data_id = accept(this->data_id, (struct sockaddr*)&this->data_addr, &len)) < 0)
-      remove_bit(Data::settings, OPEN);
+#ifdef USE_TLS
+    if(!user->settings.test(TLS_ENABLED))
+    {
+#endif // USE_TLS
+      close(this->data_id);
+      this->settings.reset(DATA_OPEN);
   }
 #ifdef USE_TLS
-  if(is_set(this->settings, TLS_PROT_D)) // settingup tls stuff, handshake later
+  }
+  if(this->settings.test(TLS_PROT_D)) // setting tls stuff up, 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);
+    comm.init_tls(this->data_session);
+    this->settings.set(TLS_INITED);
+    gnutls_transport_set_ptr(this->data_session, reinterpret_cast<gnutls_transport_ptr>(this->data_id));
   }
 #endif //USE_TLS
+  sem_post(&user->user_lock);
 }
 
-bool Data::isOpen()
+bool Data::isOpen() const
 {
-  return(is_set(Data::settings, OPEN));
+  return(this->settings.test(DATA_OPEN));
 }
 
 Data::~Data(){
-  pthread_mutex_unlock(&data_lock);
+  if(data_id)
   close(data_id);
+  if(file_id)
   close(file_id);
-  pthread_mutex_destroy(&data_lock);
+
+  sem_destroy(&data_lock);
+
 #ifdef USE_TLS
   // TLS cleanup here.
-  if(is_set(this->settings, TLS_INITED))
+  if(this->settings.test(TLS_INITED))
   {
     gnutls_db_remove_session(this->data_session);
     gnutls_deinit(this->data_session);
@@ -73,16 +110,16 @@ Data::~Data(){
           
   if(!pthread_equal(pthread_self(), user->thread_id))
   {
-    pthread_mutex_lock(&user->lock);
+    sem_wait(&user->user_lock);
     user->data_vec.erase(user->get_data_it(pthread_self()));
-    pthread_mutex_unlock(&user->lock);
-    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.
+    sem_post(&user->user_lock);
+    logging->log_this(4, TYPE_INFO, "data thread " + util.itos(thread_id) + " exiting. Remaining data connections for " + user->user_name + " is: " + util.itos(user->data_vec.size()));
   }
 }
 
 User::User(int socket_id, struct sockaddr_in &addr){
-  pthread_mutex_init(&lock, NULL);
+  sem_init(&user_lock, 0, 1);
+
   User::ctrl_addr = addr;
   User::socket_id = socket_id;
   User::logged_in = NOT_LOGGED_IN;
@@ -92,39 +129,51 @@ User::User(int socket_id, struct sockadd
   User::host_name = inet_ntoa(User::ctrl_addr.sin_addr);
   User::root_dir = "/";
   User::failed_logins = 0;
-  User::settings = 0;
+  User::total_transfered = 0;
+  this->settings.reset();
 
   // 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;
+  for(int i = 0; i <= LAST_FACT; i++)
+    this->settings.set(i);
 }
 
 User::~User(){
-  pthread_mutex_unlock(&lock);
+  if(socket_id)
   close(socket_id);
+  if(data_id)
+    close(data_id);
+
+  if(settings.test(PAM_DONE))
+  {
+    sem_wait(&comm.pam_lock);
+    pam_close_session(pamh, PAM_SILENT);
+    pam_end(pamh, PAM_SUCCESS);
+    sem_post(&comm.pam_lock);
+  }
+
+  sem_destroy(&user_lock);
   
   // killing off any ongoing data threads
   for(vector<Data*>::iterator data_it = data_vec.begin(); data_it != data_vec.end(); data_it++)
-    pthread_kill((*data_it)->thread_id, 15);
+    if(*data_it)
+      pthread_cancel((*data_it)->thread_id);
 
   while(data_vec.size())
   {  }
 
 #ifdef USE_TLS
   // TLS cleanup here.
-  if(is_set(this->settings, TLS_INITED))
+  if(this->settings.test(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);
+  sem_wait(&user_list_lock);
   user_list.erase(get_user_it(pthread_self()));
-  pthread_mutex_unlock(&user_list_mutex);
-  pthread_kill(dns_thread, 9);
-  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.
+  sem_post(&user_list_lock);
+  logging->log_this(3, TYPE_INFO, "user thread " + util.itos(thread_id) + " exiting, connected users:" + util.itos(user_list.size()));
 }
 
 vector<User*>::iterator get_user_it(pthread_t thread_id)
@@ -150,9 +199,19 @@ vector<Data*>::iterator User::get_data_i
   return(data_it);
 }
 
-bool User::data_vec_full(){
-  if(data_vec.size() >= (size_t)config->get_user(user_name).sim_data)
+bool User::data_vec_full() const
+{
+  if(data_vec.size() >= static_cast<size_t>(config->get_user(user_name).sim_data))
     return(true);
   return(false);
 }
 
+void kill_user(void *user)
+{
+  delete static_cast<User*>(user);
+}
+  
+void kill_data(void *data)
+{
+  delete static_cast<Data*>(data);
+}
Index: User.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/User.h,v
retrieving revision 1.62
retrieving revision 1.74
diff -u -p -b -r1.62 -r1.74
--- User.h	10 Jul 2004 14:14:45 -0000	1.62
+++ User.h	16 Jan 2005 00:55:24 -0000	1.74
@@ -1,5 +1,6 @@
 /////////////////////////////////////////////////////////////////////
-//  File: User.h
+// $Id: User.h,v 1.74 2005/01/16 00:55:24 klas Exp $
+//
 // Usage: Holds information about connected users.
 //
 //  A part of babyftpd, licensed under the GNU GPL, see the file
@@ -14,6 +15,8 @@
 #include "Configuration.h"
 #include "Handler.h"
 #include "Commands.h"
+#include "Utilities.h"
+#include "Log.h"
 #endif
 
 #define NOT_LOGGED_IN 0
@@ -21,31 +24,39 @@
 #define LOGGED_IN 2
 #define LOGGED_OUT 3
 
-#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)
+#define FACT_TYPE 0 // for mlsx
+#define FACT_SIZE 1
+#define FACT_MODIFY 2
+#define FACT_UNIQUE 3
+#define FACT_U_MODE 4
+#define FACT_U_UID 5
+#define FACT_U_GID 6
+#define FACT_U_LINK 7
+#define FACT_PERM 8
+#define LAST_FACT 8
+
+#define DATA_ACCT 9
+#define BINARY 10
+#define COMPRESSED 11
+#define PASSIVE 12
+#define ANONYMOUS 13
+#define DATA_OPEN 14
+#define EPSV_ONLY 15
+#define RETR_TRANS 16
+#define PAM_DONE 17
 #ifdef USE_TLS
-#define TLS_ENABLED (1 << 20)
-#define TLS_INITED (1 << 21)
-#define TLS_PBSZ (1 << 22)
-#define TLS_PROT_D (1 << 23)
+#define TLS_ENABLED 18
+#define TLS_INITED 19
+#define TLS_PBSZ 20
+#define TLS_PROT_D 21
+
+// keep an eye on these.. different depending on TLS or not...
+#define TOTAL_U_BIT 22
+#else
+#define TOTAL_U_BIT 18
 #endif // USE_TLS
 
+
 class Data
 {
   private:
@@ -53,16 +64,15 @@ class Data
   public:
     Data(User *user, int file_id = -1, string file_name = "", int trans_type = 0);
     ~Data();
-    bool isOpen();
+    bool isOpen() const;
     int data_id;
     struct sockaddr_in data_addr;
     int trans_type;
     int file_id;
-    unsigned int settings;
+    bitset<TOTAL_U_BIT> settings;
     off_t rest_pos;
-    pthread_mutex_t data_lock;
+    sem_t data_lock;
     pthread_t thread_id;
-    pthread_t dns_thread;
     string file_name;
     string host_name; // NEVER modify this after dns_lookup thread is spawned. never.
     User *user;
@@ -76,7 +86,7 @@ class User
   public:
     User(int socket_id, struct sockaddr_in &addr);
     vector<Data*>::iterator get_data_it(pthread_t);
-    bool data_vec_full();
+    bool data_vec_full() const;
     ~User();
     vector<Data*> data_vec;
     string user_name;
@@ -89,26 +99,30 @@ class User
     string current_dir;
     string root_dir;
     string temp_buf; // is this for anything but rename? shouldn't be the way I just changed it around -kl
-    unsigned int mlst_facts; // bit vector for mlsx
-    unsigned int settings; // bit vector for other settings
+    bitset<TOTAL_U_BIT> settings;
     int failed_logins;
     off_t rest_pos;
-    pthread_mutex_t lock;
+    unsigned long long total_transfered; // this variable will hold the total number of transfered bytes.
+    sem_t user_lock;
     pthread_t thread_id;
-    pthread_t dns_thread;
 #ifdef USE_TLS
     gnutls_session ctrl_session;
 #endif // USE_TLS
+    pam_handle_t *pamh;
 };
 
 vector<User*>::iterator get_user_it(pthread_t);
+void kill_user(void*);
+void kill_data(void*);
 
-extern pthread_key_t my_object;
-extern pthread_mutex_t user_list_mutex;
+extern pthread_key_t my_user;
+extern pthread_key_t my_data;
+extern sem_t user_list_lock;
 extern pthread_t main_thread;
 extern vector<User*> user_list;
 extern class Configuration *config;
 extern class Handler handler;
 extern class Commands comm;
+extern class Utilities util;
 
 #endif
Index: Utilities.cpp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Utilities.cpp,v
retrieving revision 1.36
retrieving revision 1.40
diff -u -p -b -r1.36 -r1.40
--- Utilities.cpp	24 Dec 2002 00:42:11 -0000	1.36
+++ Utilities.cpp	8 Jan 2005 22:42:18 -0000	1.40
@@ -1,5 +1,6 @@
 /////////////////////////////////////////////////////////////////////
-//  File: Utilities.cpp
+// $Id: Utilities.cpp,v 1.40 2005/01/08 22:42:18 klas Exp $
+//
 // Usage: Various functions that doesn't belong anywhere else.
 //
 //  A part of babyftpd, licensed under the GNU GPL, see the file
@@ -18,13 +19,6 @@ void Utilities::strip_line_breaks(string
     victim = victim.erase(last);
 }
 
-// function to convert a string into upper case
-void Utilities::upper(string &orig_string)
-{
-  for(string::iterator upper_it = orig_string.begin(); upper_it != orig_string.end(); upper_it++)
-    *upper_it = toupper(*upper_it);
-}
-
 // function to clean a string from:
 //      leading spaces
 //      trailing spaces
@@ -44,7 +38,7 @@ int Utilities::clean_string(string &vict
   
   for(string::iterator victim_it = victim.begin(); victim_it != victim.end(); ++victim_it)
   {
-    if(isspace(*victim_it)) // yes klas, that isspace() is needed /kl 010826
+    if(isspace(*victim_it) || iscntrl(*victim_it))
     {
       if(*victim_it == ' ') // isspace() triggers on more than space...
         spaces++;
@@ -64,13 +58,11 @@ int Utilities::clean_string(string &vict
 string Utilities::itos(int num)
 {
   string message;
-  char *buf = new char[10];
-  memset((void*)buf, '\0', 10);
+  char buf[12];
   
-  snprintf(buf, 10, "%d", num);
+  snprintf(buf, 12, "%d", num);
   message = buf;
   
-  delete[] buf;
   return(message);
 }
 
Index: Utilities.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/Utilities.h,v
retrieving revision 1.23
retrieving revision 1.26
diff -u -p -b -r1.23 -r1.26
--- Utilities.h	20 May 2004 21:33:43 -0000	1.23
+++ Utilities.h	2 Dec 2004 22:28:42 -0000	1.26
@@ -19,11 +19,22 @@ class Utilities
 
   public:
     void strip_line_breaks(string&);
-    void upper(string&);
     int clean_string(string&);
     string itos(int);
     string parse_perms_ls(mode_t);
     string parse_perms_num(mode_t);
+
+    // small inline functions, previously macros
+    bool test(int a, int b)
+    { return((a & b) == b); }
+    bool is_set(unsigned int flag, unsigned int bit)
+    { return(flag & bit); }
+    void set_bit(unsigned int &flag, unsigned int bit)
+    { flag |= bit; }
+    void remove_bit(unsigned int &flag, unsigned int bit)
+    { flag &= ~bit; }
+    void toggle_bit(unsigned int &flag, unsigned int bit)
+    { flag ^= bit;  }
 };
 
 #endif
Index: babyftpd.conf
===================================================================
RCS file: babyftpd.conf
diff -N babyftpd.conf
--- babyftpd.conf	10 Jul 2004 14:14:45 -0000	1.12
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,55 +0,0 @@
-# General section, for... general stuff
-general          
-{
-  # well... this is only for testing so far =)
-  port 2122
-
-  # transfer log - on.
-  xferlog 1
-
-  # admin email
-  admin_email you@your.host
-
-  # 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
-user
-{
-  # and a user general section, for... user general stuff
-  # all options for single users can be used in this section as well, they will then
-  #   affect all users. options on specific users override general options.
-  general
-  {
-    # number of control connections/user
-    user_limit 5
-    
-    # number of simultaneously open data connections/control connection (list & nlst excluded)
-    sim_data 3
-  }
-
-  # a per user section for... per user stuff.
-  root
-  {
-    # this useer is not allowed to login
-    noftp 1
-  }
-
-  berra
-  {
-    # 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.71
retrieving revision 1.109
diff -u -p -b -r1.71 -r1.109
--- babyftpd.cpp	10 Jul 2004 14:16:49 -0000	1.71
+++ babyftpd.cpp	22 Jan 2005 23:06:05 -0000	1.109
@@ -1,5 +1,6 @@
 /////////////////////////////////////////////////////////////////////
-//  File: babyftpd.cpp
+//  $Id: babyftpd.cpp,v 1.109 2005/01/22 23:06:05 klas Exp $
+//
 // Usage: main function, commandline parsing and function that
 //        on the main socket.
 //
@@ -15,15 +16,16 @@ int main(int argc, char **argv)
 {
   pid_t pid;
 
+  logging = new class Log;
   // we want the config object created before we parse commandline as that can set stuff.
   config = new class Configuration;
   parse_commandline(argc, argv);
-  openlog("BabyFTPd", LOG_CONS | LOG_PID, LOG_FTP); 
+  // we send the start message for logging here so that starts our log.
+  logging->log_this(3, TYPE_INFO, config->general.welcome_msg + " started.");
   // the config file variable exists in the Configuration class :p
   if(config->parse_config_file() == false)
     cout << "Failed to open the config file (" << config->conf_file << "), check if it exists + permissions." << endl;
   
-  syslog(LOG_INFO, "%s started", config->general.welcome_msg.c_str());
   
   if(config->general.no_daemon == false)
     pid = fork();
@@ -61,47 +63,54 @@ int main(int argc, char **argv)
 
       chdir("/");
       umask(0);
-      /*
+
       close(STDIN_FILENO);
       close(STDOUT_FILENO);
       close(STDERR_FILENO);
-      */
 
-      pthread_mutex_init(&user_list_mutex, NULL);
-      pthread_mutex_init(&sys_log_lock, NULL);
+      // we can't spawn the logging thread until after we are daemonized
+      pthread_create(&logging_thread, NULL, log_wrapper, NULL);
+
+      pthread_key_create(&my_user, kill_user);
+      pthread_key_create(&my_data, kill_data);
 
-      pthread_key_create(&my_object, NULL);
+      sem_init(&user_list_lock, 0, 1);
+      sem_init(&comm.pam_lock, 0, 1);
 
       signal(SIGTERM, sig_handler);
       signal(SIGHUP, sig_handler);
-      signal(SIGABRT, sig_handler);
+      signal(SIGPIPE, sig_handler);
 
       main_thread = pthread_self();
 
 #ifdef USE_TLS
 #warning "TLS ENABLED."
+#ifndef OLD_GCRYPT
+      gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+#endif // OLD_GCRYPT
       int ret;
+      sem_init(&cache_lock, 0, 1);
       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);
+        logging->log_this(2, TYPE_INFO, gnutls_strerror(ret));
 
       ret = gnutls_certificate_set_x509_crl_file(x509_cred, config->general.tls_crl.c_str(), GNUTLS_X509_FMT_PEM);
       if(ret < 0)
-        gnutls_perror(ret);
+        logging->log_this(2, TYPE_INFO, gnutls_strerror(ret));
       
       ret = gnutls_certificate_set_x509_key_file(x509_cred, config->general.tls_cert.c_str(), config->general.tls_key.c_str(), GNUTLS_X509_FMT_PEM);
       if(ret < 0)
-        gnutls_perror(ret);
-      // this should be setup so it is rerun every couple of hours
+        logging->log_this(2, TYPE_INFO, gnutls_strerror(ret));
+
       gnutls_dh_params_init(&dh_params);
+
       gnutls_dh_params_generate2(dh_params, 1024);
 
       gnutls_certificate_set_dh_params(x509_cred, dh_params);
+      dh_time = time(NULL);
 
 #endif // USE_TLS
       
@@ -113,62 +122,95 @@ int main(int argc, char **argv)
       _exit(0);
       break;
   }
-  closelog();
 }
 
 bool listener(void)
 {
   struct sockaddr_in address;
-  int first_socket;
-  pthread_t thread_tmp;
-  int final_socket;
-  socklen_t socketlen;
+  struct timeval tv = {config->general.lifebeat_tick,0};
   
-  first_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+  int first_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
   address.sin_family = AF_INET;
   address.sin_addr.s_addr = INADDR_ANY;
   address.sin_port = htons(config->general.port);
-  if(bind(first_socket, (struct sockaddr*)&address, sizeof(address)) < 0)
+  if(bind(first_socket, reinterpret_cast<struct sockaddr*>(&address), sizeof(address)) < 0)
   {
-    cout << "Port " << config->general.port << " already in use." << endl;
-    exit_baby(1);
+    logging->log_this(2, TYPE_INFO, "Port " + util.itos(config->general.port) + " already in use.");
+    exit_baby(1, EXIT_FULL);
   }
+  seteuid(config->general.user);
+  listen(first_socket, LISTEN_LIMIT);
   while(true)
   { 
-    listen(first_socket, LISTEN_LIMIT);
-    socketlen = sizeof(struct sockaddr_in);
-    final_socket = accept(first_socket, (struct sockaddr*)&address, &socketlen);
-    // check for received signals here... at least for signals that should affect anything at this stage :p
-    
-    if(sigs.hup == true)
-      config->parse_config_file(); // careful about this, maybe create a new func, config->reread_config() ?
+    pthread_t thread_tmp;
+    socklen_t socketlen = sizeof(struct sockaddr_in);
+    fd_set sock_set;
+    time_t before = time(NULL);
+
+    FD_ZERO(&sock_set);
+    FD_SET(first_socket, &sock_set);
+    if(!select(first_socket + 1, &sock_set, NULL, NULL, &tv))
+    {
+      lifebeat();
+      tv.tv_sec = config->general.lifebeat_tick; // important to reset lifebeat.
+      continue;
+    }
+
+    int final_socket[0];
+    final_socket[0] = accept(first_socket,
+        reinterpret_cast<struct sockaddr*>(&address), &socketlen);
+    if(final_socket[0] != -1)
+    {
+      pthread_create(&thread_tmp, NULL, user_wrapper,
+          static_cast<void*>(final_socket));
+      pthread_detach(thread_tmp);
+    }
+    else
+      logging->log_this(3, TYPE_INFO, "Failed to accept new caller.");
     
-    pthread_create(&thread_tmp, NULL, wrapper, (void*)final_socket);
-    pthread_detach(thread_tmp); // detaching isn't good, to much mem, we loose the thread idea, is this still true? I think not.
+    tv.tv_sec = config->general.lifebeat_tick - (time(NULL) - before);
   }
   close(first_socket);
   
   return(true);
 }
 
-static void *wrapper(void *socket_id)
+void lifebeat(void)
+{
+  logging->log_this(4, TYPE_DEBUG, "lifebeat reached.");
+
+#ifdef USE_TLS
+  if((time(NULL) - dh_time) > config->general.dh_regenerate)
+  {
+    gnutls_dh_params temp_dh_params;
+    gnutls_dh_params_init(&temp_dh_params);
+    gnutls_dh_params_generate2(temp_dh_params, 1024);
+    dh_params = temp_dh_params;
+    gnutls_certificate_set_dh_params(x509_cred, dh_params);
+    dh_time = time(NULL);
+  }
+#endif // USE_TLS
+}
+
+static void *user_wrapper(void *socket_id)
 {
-  class User *user;
-  pthread_setspecific(my_object, (void*)user);
+  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
     
-  user = handler.initializer((int)socket_id);
+  class User *user = handler.initializer(static_cast<int>((static_cast<int*>(socket_id)[0])));
+  pthread_setspecific(my_user, static_cast<void*>(user));
   
-  if(user->logged_in != LOGGED_OUT)
     handler.init(user);
   
-  string log = "closed connection from: ";
-  log.append(user->host_name);
-  pthread_mutex_lock(&sys_log_lock);
-  syslog(LOG_DEBUG, log.c_str());
-  pthread_mutex_unlock(&sys_log_lock);
+  logging->log_this(3, TYPE_DEBUG, "closed connection from: " + user->host_name);
+  pthread_exit(NULL); // the thread exits..., and takes the user with it..
+}
+
+static void *log_wrapper(void *not_used)
+{
+  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+  logging->log_main();
   
-  delete user; // user object destroyed.
-  pthread_exit(NULL); // the thread exits...
+  pthread_exit(NULL);
 }
 
 void parse_commandline(int argc, char **argv)
@@ -234,7 +276,7 @@ void parse_commandline(int argc, char **
           log_level = 0;
         else
           log_level = atoi(optarg);
-        config->general.log_level = log_level;
+        config->logger.log_level = log_level;
         break;
 
       case 'q':
@@ -280,9 +322,10 @@ void parse_commandline(int argc, char **
       cout << endl;
       cout << "    (*) only hooks for these options in yet, doesn't really work." << endl;
       cout << endl;
-      cout << "Please, report bugs, either directly to the developers or via sourceforge (http://www.sf.net/projects/babyftpd)." << endl;
+      cout << "Please, report bugs, either directly to the developers" << endl;
+      cout << "or via sourceforge (http://www.sf.net/projects/babyftpd)." << endl;
 
-      exit_baby(0);
+      exit_baby(0, EXIT_EARLY);
     }
     
     if(version == true)
@@ -298,7 +341,7 @@ void parse_commandline(int argc, char **
       version_info += "\n";
       
       cout << version_info;
-      exit_baby(0);
+      exit_baby(0, EXIT_EARLY);
     }
   }
 }
@@ -308,84 +351,45 @@ void sig_handler(int signal)
   switch(signal)
   {
     case SIGTERM:
-      pthread_t curr_thread;
-      curr_thread = pthread_self();
+      logging->log_this(3, TYPE_INFO, "SIGTERM received.");
 
-      cout << "thread " << curr_thread << " received SIGTERM" << endl;
-      sigs.term = true;
+      logging->log_this(4, TYPE_DEBUG, "main thread. users: " + util.itos(user_list.size()));
+
+      for(vector<User*>::iterator user_it = user_list.begin(); user_it != user_list.end(); user_it++)
+        pthread_cancel((*user_it)->thread_id);
 
-      if(pthread_equal(curr_thread, main_thread)) // the real stuff...
-      {
         struct timeval times;
         times.tv_sec = 1;
         times.tv_usec = 0;
+      select(0, NULL, NULL, NULL, &times);
         
-        pthread_mutex_lock(&sys_log_lock);
-        syslog(LOG_NOTICE, "SIGTERM received.");
-        pthread_mutex_unlock(&sys_log_lock);
-                    
-        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.
-
-        cout << "main thread exiting." << endl; 
-        exit_baby(1);
-      }
-      else
-      {
-        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.");
-          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);
-
-      }
+      logging->log_this(3, TYPE_INFO, "main thread exiting.");
+      exit_baby(1, EXIT_FULL);
       break;
 
     case SIGHUP:
-      pthread_mutex_lock(&sys_log_lock);
-      syslog(LOG_NOTICE, "SIGHUP received.");
-      pthread_mutex_unlock(&sys_log_lock);
-      cout << "hup received..." << endl;
-      sigs.hup = true;
+      logging->log_this(3, TYPE_INFO,
+          "SIGHUP received, reloading configuration");
+      config->parse_config_file();
       break;
 
-    case SIGABRT:
-      pthread_mutex_lock(&sys_log_lock);
-      syslog(LOG_NOTICE, "SIGABRT received.");
-      pthread_mutex_unlock(&sys_log_lock);
-      cout << "abort received..." << endl;
-      sigs.abort = true;
+    case SIGPIPE:
+      logging->log_this(3, TYPE_INFO,
+          "SIGPIPE received in thread: " + util.itos(pthread_self()));
+      if(!pthread_equal(pthread_self(), main_thread) ||
+          pthread_equal(pthread_self(), logging_thread))
+        pthread_exit(NULL); // if this is a user or data thread, bail.
       break;
   }
 }
 
-
-Signals::Signals()
+void exit_baby(int num, int how)
 {
-  Signals::hup = false;
-  Signals::term = false;
-  Signals::abort = false;
-}
 
-void exit_baby(int num)
-{
-  pthread_mutex_destroy(&user_list_mutex);
-  pthread_mutex_destroy(&sys_log_lock);
-  pthread_key_delete(my_object);
-  delete config;
+  if(how == EXIT_FULL)
+  {
+    pthread_key_delete(my_user);
+    pthread_key_delete(my_data);
 
 #ifdef USE_TLS
   // emptying and freeing session cache.
@@ -397,11 +401,19 @@ void exit_baby(int num)
     cache_db.erase(cache_it);
   }
   
-  pthread_mutex_destroy(&cache_lock);
+    gnutls_dh_params_deinit(dh_params);
   gnutls_certificate_free_credentials(x509_cred);
   gnutls_global_deinit();
+    sem_destroy(&cache_lock);
 #endif // USE_TLS
+    sem_destroy(&comm.pam_lock);
+    sem_destroy(&user_list_lock);
+    pthread_cancel(logging_thread);
+    pthread_join(logging_thread, NULL);
+  }
+  delete(logging);
 
+  delete config;
   exit(num);
 }
 
@@ -411,17 +423,17 @@ int tls_db_store(void *dbf, gnutls_datum
 {
   struct cache *temp_cache = new cache;
   
-  (void*)temp_cache->key.data = gnutls_malloc(key.size);
+  temp_cache->key.data = static_cast<unsigned char*>(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.data = static_cast<unsigned char*>(gnutls_malloc(data.size));
   temp_cache->data.size = data.size;
   memcpy(temp_cache->data.data, data.data, data.size);
   
-  pthread_mutex_lock(&cache_lock);
+  sem_wait(&cache_lock);
   cache_db.push_back(temp_cache);
-  pthread_mutex_unlock(&cache_lock);
+  sem_post(&cache_lock);
   return(0);
 }
 
@@ -433,7 +445,7 @@ gnutls_datum tls_db_fetch(void *dbf, gnu
     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);
+      data.data = static_cast<unsigned char*>(gnutls_malloc(data.size));
       memcpy(data.data, (*cache_it)->data.data, data.size);
       break;
     }
@@ -446,12 +458,12 @@ int tls_db_delete(void *dbf, gnutls_datu
   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);
+      sem_wait(&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);
+      sem_post(&cache_lock);
       return(0);
     }
         
Index: babyftpd.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/babyftpd.h,v
retrieving revision 1.31
retrieving revision 1.47
diff -u -p -b -r1.31 -r1.47
--- babyftpd.h	10 Jul 2004 14:14:45 -0000	1.31
+++ babyftpd.h	18 Jan 2005 22:44:36 -0000	1.47
@@ -1,5 +1,6 @@
 /////////////////////////////////////////////////////////////////////
-//  File: babyftpd.h
+// $Id: babyftpd.h,v 1.47 2005/01/18 22:44:36 klas Exp $
+//
 // Usage: main function, commandline parsing and function that
 //        on the main socket.
 //
@@ -17,41 +18,45 @@
 #include "Handler.h"
 #include "Commands.h"
 #include "Utilities.h"
-#endif
+#include "Log.h"
+#ifdef USE_TLS
+#include "tls.cpp"
+#endif // USE_TLS
+#endif // __BABYFTPD_CPP__
 
-static void *wrapper(void*);
+static void *user_wrapper(void*) __attribute__ ((noreturn));
+static void *log_wrapper(void*) __attribute__ ((noreturn));
 bool listener(void);
 void parse_commandline(int, char**);
 void sig_handler(int);
-void exit_baby(int);
-
-class Signals
-{
-  public:
-    Signals();
-    bool term;
-    bool hup;
-    bool abort;
-} sigs;
+void exit_baby(int, int);
+void lifebeat(void);
 
 class Configuration *config;
 class Handler handler;
 class Commands comm;
 class Utilities util;
+class Log *logging;
 
 // thread specific storage..
-pthread_key_t my_object = 0;
+pthread_key_t my_user = 0;
+pthread_key_t my_data = 0;
 
-pthread_mutex_t sys_log_lock;
-pthread_mutex_t user_list_mutex;
+sem_t user_list_lock;
 pthread_t main_thread; // for managing on shutdown and signal handling.
+pthread_t logging_thread; // so we can join it.
 vector<User*> user_list;
 
 #define LISTEN_LIMIT 5
 
+// for exit_baby()
+#define EXIT_EARLY 1
+#define EXIT_FULL  2
+
 #ifdef USE_TLS
 gnutls_certificate_credentials x509_cred;
 static gnutls_dh_params dh_params;
+time_t dh_time;
 int tls_db_store(void*, gnutls_datum, gnutls_datum);
 gnutls_datum tls_db_fetch(void*, gnutls_datum);
 int tls_db_delete(void*, gnutls_datum);
@@ -63,7 +68,7 @@ struct cache
 };
 
 vector<cache*> cache_db;
-pthread_mutex_t cache_lock;
+sem_t cache_lock;
 #endif // USE_TLS
 
 #endif
Index: commands.hlp
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/commands.hlp,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -p -b -r1.3 -r1.4
--- commands.hlp	10 Jul 2004 14:14:45 -0000	1.3
+++ commands.hlp	10 Jul 2004 17:22:27 -0000	1.4
@@ -3,7 +3,7 @@
 #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_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)"
Index: config.h.in
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/config.h.in,v
retrieving revision 1.6
retrieving revision 1.11
diff -u -p -b -r1.6 -r1.11
--- config.h.in	10 Jul 2004 14:14:45 -0000	1.6
+++ config.h.in	18 Jan 2005 22:57:04 -0000	1.11
@@ -1,4 +1,5 @@
 /* config.h.in.  Generated from configure.in by autoheader.  */
+/* $Id: config.h.in,v 1.11 2005/01/18 22:57:04 klas Exp $ */
 
 /* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
    */
@@ -10,8 +11,8 @@
 /* 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 `pam' library (-lpam). */
+#undef HAVE_LIBPAM
 
 /* Define to 1 if you have the `pthread' library (-lpthread). */
 #undef HAVE_LIBPTHREAD
@@ -77,3 +78,9 @@
 
 /* Define to use TLS */
 #undef USE_TLS
+
+/* Define to mark libgcrypt being 1.1.92 or older */
+#undef OLD_GCRYPT
+
+/* Define to use new format for list */
+#undef USE_NEWLS
Index: configure.in
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/configure.in,v
retrieving revision 1.13
retrieving revision 1.24
diff -u -p -b -r1.13 -r1.24
--- configure.in	10 Jul 2004 14:14:45 -0000	1.13
+++ configure.in	16 Jan 2005 11:16:24 -0000	1.24
@@ -1,6 +1,8 @@
 dnl Process this file with autoconf to produce a configure script.
-AC_PREREQ(2.57)
+dnl $Id: configure.in,v 1.24 2005/01/16 11:16:24 klas Exp $
+AC_PREREQ(2.50)
 AC_INIT(babyftpd)
+AC_LANG(C++)
 AC_CONFIG_SRCDIR(babyftpd.cpp)
 AC_CONFIG_HEADER(config.h)
   
@@ -11,8 +13,10 @@ AC_PROG_CXXCPP
 AC_PROG_MAKE_SET
 
 dnl Checks for libraries.
-AC_CHECK_LIB(crypt, crypt)
 AC_CHECK_LIB(pthread, pthread_create)
+if test "x$ac_cv_lib_pthread_pthread_create" != "xyes" ; then
+  AC_MSG_ERROR(babyftpd needs a working pthreads package.)
+fi
 
 dnl Checks for header files.
 AC_HEADER_DIRENT
@@ -26,19 +30,70 @@ AC_TYPE_UID_T
 
 dnl Checks for library functions.
 
+dnl babystuff
+AC_MSG_CHECKING(whether there is a cradle present)
+AC_MSG_RESULT(yes)
+
+AC_MSG_CHECKING(if we have to use a pacifier)
+AC_MSG_RESULT(yes)
+
+AC_MSG_CHECKING(if the gruel is still hot enough)
+AC_MSG_RESULT(yes)
+
+AC_MSG_CHECKING(for poop in -ldiaper)
+AC_MSG_RESULT(no (wheew!))
+
 dnl Commandline enabling/disabling
 AC_ARG_ENABLE(tls,
     [AC_HELP_STRING([--disable-tls],[Disable the use of tls for encryption.])],
     [ if test "x$enableval" = "xyes"; then
-      AC_CHECK_LIB(gnutls, gnutls_init, 
-      AC_DEFINE(USE_TLS,,[tls enable])
+      AC_CHECK_LIB(gnutls, gnutls_dh_params_generate2, 
+      AC_DEFINE(USE_TLS 1,,[tls enable])
       LIBS="$LIBS -lgnutls"
       )
     fi],
-    AC_CHECK_LIB(gnutls, gnutls_init,
-    AC_DEFINE(USE_TLS,,[tls enable])
+    AC_CHECK_LIB(gnutls, gnutls_dh_params_generate2,
+    AC_DEFINE(USE_TLS 1,,[tls enable])
     LIBS="$LIBS -lgnutls"
     )
 )
 
+AC_ARG_ENABLE(newls,
+    [AC_HELP_STRING([--enable-newls],[Enable use of new format for list (yyyy-mm-dd hh:mm).])],
+    [ if test "x$enableval" = "xyes"; then
+      AC_DEFINE(USE_NEWLS 1,,[newls enable])
+      fi]
+)
+
+AC_MSG_CHECKING(if we have to tell gcrypt what threads package we are running)
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#ifdef USE_TLS
+#include <gcrypt.h>
+#endif
+
+int main(void)
+{
+  int ret;
+#ifdef USE_TLS
+  if(gcry_check_version("1.1.93"))
+    ret = 1;
+  else
+#endif
+    ret = 0;
+  return(ret);
+}
+      ]])],[
+AC_MSG_RESULT(no)
+AC_DEFINE(OLD_GCRYPT 1,,[ Define to mark libgcrypt being 1.1.92 or older])
+],[AC_MSG_RESULT(yes)
+],[])
+
+dnl PAM stuff..
+AC_CHECK_HEADERS(security/pam_appl.h)
+if test "x$ac_cv_header_security_pam_appl_h" != "xyes" ; then
+  AC_MSG_ERROR(PAM headers not found.)
+else
+  AC_CHECK_LIB(pam, pam_start)
+fi
+
 AC_OUTPUT(Makefile)
Index: generic.h
===================================================================
RCS file: /cvsroot/babyftpd/babyftpd/generic.h,v
retrieving revision 1.59
retrieving revision 1.76
diff -u -p -b -r1.59 -r1.76
--- generic.h	10 Jul 2004 14:14:45 -0000	1.59
+++ generic.h	22 Jan 2005 23:06:05 -0000	1.76
@@ -1,5 +1,6 @@
 /////////////////////////////////////////////////////////////////////
-//  File: generic.h
+//  $Id: generic.h,v 1.76 2005/01/22 23:06:05 klas Exp $
+//
 // Usage: some defines that is important for correct inclusions of
 //        system headers, and all system header includes.
 //
@@ -12,9 +13,11 @@
 
 #define __USE_GNU 1
 
-#include <iostream>
-#include <syslog.h>
 #include "config.h"
+#include <unistd.h>
+#include <string>
+#include <vector>
+#include <semaphore.h>
 
 #ifdef __NETINCLS__
 #include <sys/socket.h>
@@ -24,38 +27,44 @@
 #if (!defined(__STRINGTOKENIZER_H__) && !defined(__CONFIGURATION_H__) && !defined(__UTILITIES_H__))
 #include <pthread.h>
 #include <cerrno>
-#include <vector>
+#include <bitset>
+#include <security/pam_appl.h>
 #ifdef USE_TLS
 #include <gnutls/gnutls.h>
 #endif // USE_TLS
+using std::bitset;
 #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__
 #include <csignal>
 #include <getopt.h>
 #include <sys/stat.h>
+#include <iostream>
+using std::cout;
+using std::endl;
+#ifdef USE_TLS
+#ifndef OLD_GCRYPT
+#include <gcrypt.h>
+#endif // OLD_GCRYPT
+#endif // USE_TLS
 #endif // __BABYFTPD_H__
 
 #ifdef __HANDLER_H__
 #include <csignal>
 #include <netdb.h>
+#include <algorithm>
 #endif // __HANDLER_H__
 
 #ifdef __CONFIGURATION_H__
 #include <sys/utsname.h>
 #include <fstream>
 #include <climits>
-#include <string>
+#include <pwd.h>
+using std::ifstream;
 #endif // __CONFIGURATION_H__
 
 #ifdef __COMMANDS_H__
@@ -69,9 +78,23 @@
 #include <crypt.h>
 #include <dirent.h>
 #include <fcntl.h>
+#include <algorithm>
 #endif // __COMMANDS2_CPP__
 
-using namespace std;
+#ifdef __LOG_H__
+#include <syslog.h>
+#include <fstream>
+using std::ofstream;
+using std::endl;
+#endif // __LOG_H__
+
+#ifdef __USER_H__
+#include <security/pam_appl.h>
+#include <security/pam_misc.h>
+#endif // __USER_H__
+
+using std::string;
+using std::vector;
 
 class Configuration;
 class User;
@@ -87,11 +110,4 @@ class StringTokenizer;
 #define FTP_LOG DAEMON_LOG
 #endif // FTP_LOG
 
-#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
Index: tls.cpp
===================================================================
RCS file: tls.cpp
diff -N tls.cpp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tls.cpp	18 Jan 2005 22:44:37 -0000	1.1
@@ -0,0 +1,59 @@
+/////////////////////////////////////////////////////////////////////
+// $Id: tls.cpp,v 1.1 2005/01/18 22:44:37 klas Exp $
+//
+// Shamelessly ripped code from gcrypt.h, modified for c++.
+//   Included when we have new gcrypt and need to tell it how to do
+//   thread locking for us.
+//
+// A part of babyftpd, licensed under the GNU GPL, see the file
+//   LICENSE for complete information.
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef OLD_GCRYPT
+
+static int gcry_pthread_mutex_init (void **priv)
+{
+  int err = 0;
+  pthread_mutex_t *lock = new pthread_mutex_t;
+  if (!lock)
+    err = ENOMEM;
+  if (!err)
+  {
+    err = pthread_mutex_init (lock, NULL);
+    if (err)
+      delete lock;
+    else
+      *priv = lock;
+  }
+  return err;
+}
+
+static int gcry_pthread_mutex_destroy (void **lock)
+{
+  pthread_mutex_t *temp_lock = (static_cast<pthread_mutex_t*>(*lock));
+  int err = pthread_mutex_destroy(temp_lock);
+  delete temp_lock;
+  return err;
+}
+
+static int gcry_pthread_mutex_lock (void **lock)
+{
+  pthread_mutex_t *temp_lock = (static_cast<pthread_mutex_t*>(*lock));
+  return pthread_mutex_lock(temp_lock);
+}
+
+static int gcry_pthread_mutex_unlock (void **lock)
+{
+  pthread_mutex_t *temp_lock = (static_cast<pthread_mutex_t*>(*lock));
+  return pthread_mutex_unlock(temp_lock);
+}
+
+static struct gcry_thread_cbs gcry_threads_pthread =
+{
+  GCRY_THREAD_OPTION_PTHREAD, NULL,
+  gcry_pthread_mutex_init, gcry_pthread_mutex_destroy,
+  gcry_pthread_mutex_lock, gcry_pthread_mutex_unlock
+};
+
+#endif // OLD_GCRYPT
Index: examples/babyftpd.conf
===================================================================
RCS file: examples/babyftpd.conf
diff -N examples/babyftpd.conf
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ examples/babyftpd.conf	22 Jan 2005 23:06:05 -0000	1.4
@@ -0,0 +1,92 @@
+# $Id: babyftpd.conf,v 1.4 2005/01/22 23:06:05 klas Exp $
+# General section, for... general stuff
+general
+{
+  # port 21 - standard ftp port.
+  port 21
+
+  # admin email
+  admin_email you@your.host
+
+  # maximum number of control connections.
+  connection_limit 100
+
+  # run the lifebeat() every this seconds
+  lifebeat_tick 300
+
+  #TLS stuff
+  tls_key /root/tls_key.pem
+  tls_cert /root/tls_cert.pem
+  tls_ca /root/tls_ca.pem
+  tls_crl /root/tls_crl.pem
+
+  # how often to regenerate diffie-hellman parameters (in seconds)
+  dh_regenerate 3600
+
+  # what user to run the process under?
+  user ftp
+}
+
+# user section, for... user stuff
+user
+{
+  # and a user general section, for... user general stuff
+  # all options for single users can be used in this section as well, they will then
+  #   affect all users. options on specific users override general options.
+  general
+  {
+    # number of control connections/user
+    user_limit 5
+
+    # number of simultaneously open data connections/control connection (list & nlst excluded)
+    sim_data 3
+
+    # encryption:
+    encryption allow
+  }
+
+  # a per user section for... per user stuff.
+  root
+  {
+    # this useer is not allowed to login
+    noftp 1
+  }
+
+  berra
+  {
+    # 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
+
+    # encryption can have several values..
+    encryption deny
+  }
+}
+
+log
+{
+  # a default loglevel of 3 is neat
+  log_level 3
+
+  # by default we want xferlogging
+  xferlog 1
+
+  # xferlogging go to /var/log/xferlog
+  xferlog_file /var/log/xferlog
+
+  # authlog by way of syslog
+  authlog syslog
+
+  # info also by way of syslog
+  infolog syslog
+
+  # and debug as well
+  debuglog syslog
+
+  # thread id on all logging..
+  thread_id 1
+}
Index: examples/babyftpd.pam
===================================================================
RCS file: examples/babyftpd.pam
diff -N examples/babyftpd.pam
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ examples/babyftpd.pam	23 Jan 2005 00:39:50 -0000	1.2
@@ -0,0 +1,10 @@
+# $Id: babyftpd.pam,v 1.2 2005/01/23 00:39:50 klas Exp $
+# put this file or one like it in /etc/pam.d/ with the name babyftpd.
+# 
+# babyftpd should work with most kinds of pam modules. Extensive testing
+#  hasn't taken place yet, if you have trouble getting pam modules that
+#  should work with babyftpd to work, contact us.
+
+auth    required        pam_unix.so nullok_secure
+account required        pam_unix.so
+session required        pam_unix.so
