/*
 *  Copyright (c) 1998 Denis Perchine <dyp@perchine.com>
 *  Copyright (c) 2004 Szombathelyi György <gyurco@freemail.hu>
 *  Former maintainer: Adriaan de Groot <groot@kde.org>
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public
 *  License version 2 as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 **/

#include "globals.h"

#include <errno.h>
#include <sys/types.h>
#include <sys/file.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include <fcntl.h>
#include <tqstring.h>
#include <tqdir.h>

#include "kglobal_.h"
#include "kuser.h"
#include "misc.h"
#include <kstandarddirs.h>
#include <tdemessagebox.h>
#include <kprocess.h>
#include <kdebug.h>
#include <tdeio/netaccess.h>
#include <kurl.h>

// class KUser

KU::KUser::KUser()
{
  p_change = 0;
  p_expire = -1;
  p_uid     = 0;
  p_gid     = 100;

  s_min     = 0;
  s_max     = 99999;
  s_warn    = 7;
  s_inact   = -1;
//  s_flag    = 0;
  caps = 0;
  isCreateHome = false;
  isCreateMailBox = false;
  isCopySkel = false;
  isDeleteHome = false;
  isDeleteMailBox = false;

  isDisabled = true;
}

KU::KUser::KUser(const KU::KUser *user)
{
  copy(user);
}

void KU::KUser::copy(const KU::KUser *user)
{
  if ( user != this ) {
    caps = user->caps;
    p_name = user->p_name;
    p_surname = user->p_surname;
    p_email = user->p_email;
    p_pwd = user->p_pwd;
    p_dir = user->p_dir;
    p_shell = user->p_shell;
    p_fname = user->p_fname;
    p_office = user->p_office;
    p_ophone = user->p_ophone;
    p_hphone = user->p_hphone;
    p_class = user->p_class;
    p_change = user->p_change;
    p_expire = user->p_expire;
    p_office1 = user->p_office1;
    p_office2 = user->p_office2;
    p_address = user->p_address;

    p_uid     = user->p_uid;
    p_gid     = user->p_gid;

    s_pwd = user->s_pwd;
    s_min     = user->s_min;
    s_max     = user->s_max;
    s_warn    = user->s_warn;
    s_inact   = user->s_inact;
    s_flag    = user->s_flag;

    sam_lmpwd = user->sam_lmpwd;
    sam_ntpwd = user->sam_ntpwd;
    sam_loginscript = user->sam_loginscript;
    sam_profile = user->sam_profile;
    sam_homedrive = user->sam_homedrive;
    sam_homepath = user->sam_homepath;
    sam_workstations = user->sam_workstations;
    sam_domain = user->sam_domain;
    sid = user->sid;
    pgroup_sid = user->pgroup_sid;

    isCreateHome = user->isCreateHome;
    isCreateMailBox = user->isCreateMailBox;
    isDeleteHome = user->isDeleteHome;
    isDeleteMailBox = user->isDeleteMailBox;
    isCopySkel = user->isCopySkel;
    isDisabled = user->isDisabled;
  }
}

KU::KUser::~KUser()
{
}

void KU::KUser::setCaps( int data )
{
  caps = data;
}

int KU::KUser::getCaps()
{
  return caps;
}

bool KU::KUser::getDeleteHome()
{
  return isDeleteHome;
}

bool KU::KUser::getDeleteMailBox()
{
  return isDeleteMailBox;
}

bool KU::KUser::getCreateHome()
{
  return isCreateHome;
}

bool KU::KUser::getCreateMailBox()
{
  return isCreateMailBox;
}

bool KU::KUser::getCopySkel()
{
  return isCopySkel;
}

const TQString &KU::KUser::getName() const
{
  return p_name;
}

const TQString &KU::KUser::getSurname() const
{
  return p_surname;
}

const TQString &KU::KUser::getEmail() const
{
  return p_email;
}

const TQString &KU::KUser::getPwd() const
{
  return p_pwd;
}

const TQString &KU::KUser::getHomeDir() const
{
  return p_dir;
}

const TQString &KU::KUser::getShell() const
{
  return p_shell;
}

const TQString &KU::KUser::getFullName() const
{
  return p_fname;
}

bool KU::KUser::getDisabled() const
{
  return isDisabled;
}

// FreeBSD apparently uses the GECOS fields differently than other Unices.
// Create some better named functions to make the FreeBSD code clear
const TQString &KU::KUser::getOffice() const
{
  return p_office;
}

const TQString &KU::KUser::getWorkPhone() const
{
  return p_ophone;
}

const TQString &KU::KUser::getHomePhone() const
{
  return p_hphone;
}

// New fields needed for the FreeBSD /etc/master.passwd file
const TQString &KU::KUser::getClass() const
{
  return p_class;
}

const TQString &KU::KUser::getOffice1() const
{
  return p_office1;
}

const TQString &KU::KUser::getOffice2() const
{
  return p_office2;
}

const TQString &KU::KUser::getAddress() const
{
  return p_address;
}

uid_t KU::KUser::getUID() const
{
  return p_uid;
}

gid_t KU::KUser::getGID() const
{
  return p_gid;
}

const TQString &KU::KUser::getSPwd() const
{
  return s_pwd;
}

time_t KU::KUser::getLastChange() const
{
  return p_change;
}

int KU::KUser::getMin() const
{
  return s_min;
}

int KU::KUser::getMax() const
{
  return s_max;
}

int KU::KUser::getWarn() const
{
  return s_warn;
}

int KU::KUser::getInactive() const
{
  return s_inact;
}

int KU::KUser::getFlag() const
{
  return s_flag;
}

time_t KU::KUser::getExpire() const
{
  return p_expire;
}

const TQString &KU::KUser::getLMPwd() const //  sam_lmpwd,
{
  return sam_lmpwd;
}

const TQString &KU::KUser::getNTPwd() const //sam_ntpwd,
{
  return sam_ntpwd;
}

const TQString &KU::KUser::getLoginScript() const //sam_loginscript,
{
  return sam_loginscript;
}

const TQString &KU::KUser::getProfilePath() const //  sam_profile,
{
  return sam_profile;
}

const TQString &KU::KUser::getHomeDrive() const //sam_homedrive,
{
  return sam_homedrive;
}

const TQString &KU::KUser::getHomePath() const //sam_homepath;
{
  return sam_homepath;
}

const TQString &KU::KUser::getWorkstations() const //sam_workstation;
{
  return sam_workstations;
}

const TQString &KU::KUser::getDomain() const //sam_domain;
{
  return sam_domain;
}

const SID &KU::KUser::getSID() const //sid,
{
  return sid;
}

const SID &KU::KUser::getPGSID() const //pgroup_sid;
{
  return pgroup_sid;
}

void KU::KUser::setName(const TQString &data)
{
  p_name = data;
}

void KU::KUser::setSurname(const TQString &data)
{
  p_surname = data;
}

void KU::KUser::setEmail(const TQString &data)
{
  p_email = data;
}

void KU::KUser::setPwd(const TQString &data)
{
  p_pwd = data;
}

void KU::KUser::setHomeDir(const TQString &data)
{
  p_dir = data;
}

void KU::KUser::setShell(const TQString &data)
{
  p_shell = data;
}

void KU::KUser::setFullName(const TQString &data)
{
  p_fname = data;
}

void KU::KUser::setDisabled(bool data)
{
  isDisabled = data;
}

// FreeBSD apparently uses the GECOS fields differently than other Unices.
// Create some better named functions to make the FreeBSD code clear
void KU::KUser::setOffice(const TQString &data)
{
  p_office = data;
}

void KU::KUser::setWorkPhone(const TQString &data)
{
  p_ophone = data;
}

void KU::KUser::setHomePhone(const TQString &data)
{
  p_hphone = data;
}

// New fields needed for the FreeBSD /etc/master.passwd file
void KU::KUser::setClass(const TQString &data)
{
  p_class = data;
}

void KU::KUser::setLastChange(time_t data)
{
  p_change = data;
}

void KU::KUser::setExpire(time_t data)
{
  p_expire = data;
}

void KU::KUser::setOffice1(const TQString &data)
{
  p_office1 = data;
}

void KU::KUser::setOffice2(const TQString &data)
{
  p_office2 = data;
}

void KU::KUser::setAddress(const TQString &data)
{
  p_address = data;
}

void KU::KUser::setUID(uid_t data)
{
  p_uid = data;
}

void KU::KUser::setGID(gid_t data)
{
  p_gid = data;
}

void KU::KUser::setSPwd(const TQString &data)
{
  s_pwd = data;
}

void KU::KUser::setMin(int data)
{
  s_min = data;
}

void KU::KUser::setMax(int data)
{
  s_max = data;
}

void KU::KUser::setWarn(int data)
{
  s_warn = data;
}

void KU::KUser::setInactive(int data)
{
  s_inact = data;
}

void KU::KUser::setLMPwd( const TQString &data ) //  sam_lmpwd,
{
  sam_lmpwd = data;
}

void KU::KUser::setNTPwd( const TQString &data ) //sam_ntpwd,
{
  sam_ntpwd = data;
}

void KU::KUser::setLoginScript( const TQString &data ) //sam_loginscript,
{
  sam_loginscript = data;
}

void KU::KUser::setProfilePath( const TQString &data) //  sam_profile,
{
  sam_profile = data;
}

void KU::KUser::setHomeDrive( const TQString &data ) //sam_homedrive,
{
  sam_homedrive = data;
}

void KU::KUser::setHomePath( const TQString &data ) //sam_homepath;
{
  sam_homepath = data;
}

void KU::KUser::setWorkstations( const TQString &data ) //sam_workstation;
{
  sam_workstations = data;
}

void KU::KUser::setDomain( const TQString &data ) //sam_domain
{
  sam_domain = data;
}

void KU::KUser::setSID( const SID &data ) //sid,
{
  sid = data;
}

void KU::KUser::setPGSID( const SID &data ) //pgroup_sid;
{
  pgroup_sid = data;
}

void KU::KUser::setFlag(int data)
{
  s_flag = data;
}

void KU::KUser::setCreateHome(bool data)
{
  isCreateHome = data;
}

void KU::KUser::setCreateMailBox(bool data)
{
  isCreateMailBox = data;
}

void KU::KUser::setCopySkel(bool data)
{
  isCopySkel = data;
}

void KU::KUser::setDeleteHome(bool data)
{
  isDeleteHome = data;
}

void KU::KUser::setDeleteMailBox(bool data)
{
  isDeleteMailBox = data;
}

int KU::KUser::createHome()
{

  if(p_dir.isNull() || p_dir.isEmpty()) {
    KMessageBox::sorry( 0, i18n("Cannot create home folder for %1: it is null or empty.").arg(p_name) );
    return(0);
  }
  if (mkdir(TQFile::encodeName(p_dir), 0700) != 0) {
    if (errno != EEXIST)
    {
      KMessageBox::error( 0, i18n("Cannot create home folder %1.\nError: %2").arg(p_dir).arg(TQString::fromLocal8Bit(strerror(errno))) );
      return(0);
    }
  }

  if (chown(TQFile::encodeName(p_dir), p_uid, p_gid) != 0) {
    KMessageBox::error( 0, i18n("Cannot change owner of home folder %1.\nError: %2").arg(p_dir).arg(TQString::fromLocal8Bit(strerror(errno))) );
    return(1);
  }

  if (chmod(TQFile::encodeName(p_dir), KU_HOMEDIR_PERM) != 0) {
    KMessageBox::error( 0, i18n("Cannot change permissions on home folder %1.\nError: %2").arg(p_dir).arg(TQString::fromLocal8Bit(strerror(errno))) );
    return(1);
  }
  return(1);
}

int KU::KUser::tryCreate(const TQString &dir)
{
  struct stat sb;
  int rc = 0;

  rc = stat(TQFile::encodeName(dir), &sb);
  if (rc == 0) {
    if (S_ISDIR(sb.st_mode)) {
      if (KMessageBox::warningContinueCancel( 0,
        i18n("Folder %1 already exists!\nWill make %2 owner and change permissions.\nDo you want to continue?").arg(dir).arg(p_name),
        TQString(), KStdGuiItem::cont() ) == KMessageBox::Continue) {

        if (chown(TQFile::encodeName(dir), p_uid, p_gid) != 0) {
          KMessageBox::error( 0, i18n("Cannot change owner of %1 folder.\nError: %2") .arg(dir).arg(TQString::fromLocal8Bit(strerror(errno))) );
        }
        return(0);
      } else {
        KMessageBox::information( 0, i18n("Folder %1 left 'as is'.\nVerify ownership and permissions for user %2 who may not be able to log in!").arg(dir).arg(p_name) );
        return(-1);
      }
    } else {
      KMessageBox::information( 0, i18n("%1 exists and is not a folder. User %2 will not be able to log in!").arg(dir).arg(p_name) );
      return(-1);
    }
  } else {
    if (errno == ENOENT) {
      if (mkdir(TQFile::encodeName(dir), 0700) != 0) {
        KMessageBox::error( 0, i18n("Cannot create %1 folder.\nError: %2").arg(dir).arg(TQString::fromLocal8Bit(strerror(errno))));
        return(-1);
      }
      if (chown(TQFile::encodeName(dir), p_uid, p_gid) != 0) {
        KMessageBox::error( 0, i18n("Cannot change owner of %1 folder.\nError: %2").arg(dir).arg(TQString::fromLocal8Bit(strerror(errno))) );
      }
      return(0);
    } else {
      KMessageBox::error( 0, i18n("stat call on %1 failed.\nError: %2").arg(dir).arg(TQString::fromLocal8Bit(strerror(errno))) );
      return(-1);
    }
  }
}

int KU::KUser::createMailBox()
{
  TQString mailboxpath;
  int fd;
  mailboxpath = TQFile::decodeName(MAIL_SPOOL_DIR) + "/" + p_name;
  if((fd = open(TQFile::encodeName(mailboxpath), O_CREAT|O_EXCL|O_WRONLY,
                S_IRUSR|S_IWUSR)) < 0) {
    if (errno != EEXIST)
    {
      KMessageBox::error( 0, i18n("Cannot create %1: %2")
                .arg(mailboxpath)
                .arg(TQString::fromLocal8Bit(strerror(errno))) );
      return -1;
    }
  }

  close(fd);

  if (chown(TQFile::encodeName(mailboxpath), p_uid, KU_MAILBOX_GID) != 0) {
    KMessageBox::error( 0, i18n("Cannot change owner on mailbox: %1\nError: %2")
                .arg(mailboxpath).arg(TQString::fromLocal8Bit(strerror(errno))) );
    return -1;
  }

  if (chmod(TQFile::encodeName(mailboxpath), KU_MAILBOX_PERM) != 0) {
    KMessageBox::error( 0, i18n("Cannot change permissions on mailbox: %1\nError: %2")
                .arg(mailboxpath).arg(TQString::fromLocal8Bit(strerror(errno))) );
    return -1;
  }

  return 0;
}

void KU::KUser::copyDir(const TQString &srcPath, const TQString &dstPath)
{
  mode_t mode;
  TQDir s(srcPath);
  TQDir d(dstPath);

  TQString dot = TQString::fromLatin1(".");
  TQString dotdot = TQString::fromLatin1("..");
  
  s.setFilter( TQDir::All | TQDir::Hidden | TQDir::System );

  for (uint i=0; i<s.count(); i++) {
    TQString name(s[i]);

    if (name == dot)
      continue;
    if (name == dotdot)
      continue;

    TQString filename(s.filePath(name));

    TQFileInfo info(filename);
    mode = 0;
    if ( info.permission(TQFileInfo::ReadOwner) ) mode |=  S_IRUSR;
    if ( info.permission(TQFileInfo::WriteOwner) ) mode |=  S_IWUSR;
    if ( info.permission(TQFileInfo::ExeOwner) ) mode |=  S_IXUSR;
    if ( info.permission(TQFileInfo::ReadGroup) ) mode |=  S_IRGRP;
    if ( info.permission(TQFileInfo::WriteGroup) ) mode |=  S_IWGRP;
    if ( info.permission(TQFileInfo::ExeGroup) ) mode |=  S_IXGRP;
    if ( info.permission(TQFileInfo::ReadOther) ) mode |=  S_IROTH;
    if ( info.permission(TQFileInfo::WriteOther) ) mode |=  S_IWOTH;
    if ( info.permission(TQFileInfo::ExeOther) ) mode |=  S_IXOTH;

    if ( info.isSymLink() ) {
      TQString link = info.readLink();

      if (symlink(TQFile::encodeName(link),TQFile::encodeName(d.filePath(name))) != 0) {
        KMessageBox::error( 0, i18n("Error creating symlink %1.\nError: %2")
                  .arg(d.filePath(s[i])).arg(TQString::fromLocal8Bit(strerror(errno))) );
      }
    } else if ( info.isDir() ) {
      TQDir dir(filename);

      d.mkdir(name, false);
      copyDir(s.filePath(name), d.filePath(name));

      if (chown(TQFile::encodeName(d.filePath(name)), p_uid, p_gid) != 0) {
        KMessageBox::error( 0, i18n("Cannot change owner of folder %1.\nError: %2")
                  .arg(d.filePath(s[i])).arg(TQString::fromLocal8Bit(strerror(errno))) );
      }

      if (chmod(TQFile::encodeName(d.filePath(name)), mode) != 0) {
        KMessageBox::error( 0, i18n("Cannot change permissions on folder %1.\nError: %2")
                  .arg(d.filePath(s[i])).arg(TQString::fromLocal8Bit(strerror(errno))) );
      }

    } else {
      if (copyFile(filename, d.filePath(name)) == -1) {
        continue;
      }

      if (chown(TQFile::encodeName(d.filePath(name)), p_uid, p_gid) != 0) {
        KMessageBox::error( 0, i18n("Cannot change owner of file %1.\nError: %2")
                  .arg(d.filePath(s[i])).arg(TQString::fromLocal8Bit(strerror(errno))) );
      }

      if (chmod(TQFile::encodeName(d.filePath(name)), mode) != 0) {
        KMessageBox::error( 0, i18n("Cannot change permissions on file %1.\nError: %2")
                  .arg(d.filePath(s[i])).arg(TQString::fromLocal8Bit(strerror(errno))) );
      }
    }
  }
}

int KU::KUser::copySkel()
{
  TQDir s(TQFile::decodeName(SKELDIR));
  TQDir d(p_dir);
  mode_t mode;
  
  if (!s.exists()) {
    KMessageBox::error( 0, i18n("Folder %1 does not exist, cannot copy skeleton for %2.").arg(s.absPath()).arg(p_name) );
    return (-1);
  }

  if (!d.exists()) {
    KMessageBox::error( 0, i18n("Folder %1 does not exist, cannot copy skeleton.").arg(d.absPath()) );
    return (-1);
  }
  
  mode = umask(0007);
  copyDir(s.absPath(), d.absPath());
  umask( mode );
  
  return 0;
}

int KU::KUser::removeHome()
{
  struct stat sb;

  if (!stat(TQFile::encodeName(p_dir), &sb))
    if (S_ISDIR(sb.st_mode) && sb.st_uid == p_uid) {
      if (!TDEIO::NetAccess::del(KURL::fromPathOrURL(p_dir), NULL)) {
             KMessageBox::error( 0, i18n("Cannot remove home folder %1.\nError: %2")
                       .arg(p_dir).arg(TDEIO::NetAccess::lastErrorString()) );
      }
    } else {
      KMessageBox::error( 0, i18n("Removal of home folder %1 failed (uid = %2, gid = %3).").arg(p_dir).arg(sb.st_uid).arg(sb.st_gid) );
    }
  else {
    KMessageBox::error( 0, i18n("stat call on file %1 failed.\nError: %2")
                 .arg(p_dir).arg(TQString::fromLocal8Bit(strerror(errno))) );
  }

  return 0;
}

//TODO: remove at jobs too.

int KU::KUser::removeCrontabs()
{
  TQString file;
  TQString command;

  file = TQFile::decodeName(CRONTAB_DIR) + "/" + p_name;
  if ( access(TQFile::encodeName(file), F_OK) == 0 ) {
    command = TQString::fromLatin1("crontab -u %1 -r").arg(TDEProcess::quote(p_name));
    if ( system(TQFile::encodeName(command)) != 0 ) {
      KMessageBox::error( 0, i18n("Cannot remove crontab %1.\nError: %2")
                  .arg(command).arg(TQString::fromLocal8Bit(strerror(errno))) );
     }
  }

  return 0;
}

int KU::KUser::removeMailBox()
{
  TQString file;

  file = TQFile::decodeName(MAIL_SPOOL_DIR) + "/" + p_name;
  if (remove(TQFile::encodeName(file)) != 0) {
    KMessageBox::error( 0, i18n("Cannot remove mailbox %1.\nError: %2")
                .arg(file).arg(TQString::fromLocal8Bit(strerror(errno))) );
  }

  return 0;
}

int KU::KUser::removeProcesses()
{
  // be paranoid -- kill all processes owned by that user, if not root.

  if (p_uid != 0)
    switch (fork()) {
      case 0:
        setuid(p_uid);
        kill(-1, 9);
        _exit(0);
        break;
      case -1:
        KMessageBox::error( 0,
          i18n("Cannot fork while trying to kill processes for uid %1.").arg(p_uid) );
        break;
    }

  return 0;
}

KU::KUsers::KUsers(KUserPrefsBase *cfg)
{
  mUsers.setAutoDelete(true);
  mCfg = cfg;
}

KU::KUsers::~KUsers()
{
  mUsers.clear();
}

const TQString &KU::KUsers::getDOMSID() const
{
  return domsid;
}

void KU::KUsers::parseGecos( const char *gecos, TQString &name, 
  TQString &field1, TQString &field2, TQString &field3 )
{
  int no = 0;
  const char *s = gecos;
  const char *pos = NULL;
  // At least one part of the string exists
  for(;;) {
    pos = strchr(s, ',');
    TQString val;
    if(pos == NULL)
      val = TQString::fromLocal8Bit(s);
    else
      val = TQString::fromLocal8Bit(s, (int)(pos-s));

    switch(no) {
      case 0: name = val; break;
      case 1: field1 = val; break;
      case 2: field2 = val; break;
      case 3: field3 = val; break;
    }
    if(pos == NULL) break;
    s = pos+1;
    no++;
  }
}  

void KU::KUsers::fillGecos(KU::KUser *user, const char *gecos)
{
  TQString name,field1,field2,field3;
  parseGecos( gecos, name, field1, field2, field3 );
  user->setFullName( name );
  caps & Cap_BSD ? user->setOffice( field1 ) : user->setOffice1( field1 );
  caps & Cap_BSD ? user->setWorkPhone( field2 ) : user->setOffice2( field2 );
  caps & Cap_BSD ? user->setHomePhone( field3 ) : user->setAddress( field3 );
}

bool KU::KUsers::doCreate(KU::KUser *user)
{
  TQString h_dir;

  if(user->getCreateMailBox()) {
    user->createMailBox();
    user->setCreateMailBox(false);
  }

  if(user->getCreateHome()) {
    if(user->createHome()) {
      user->setCreateHome(false);
     } else {
       return false; // if createHome fails, copySkel is irrelevant!
     }

     if(user->getCopySkel()) {
       if((user->copySkel()) == 0) {
         user->setCopySkel(false);
       }
     }

  }
  return true;
}

bool KU::KUsers::doDelete( KU::KUser *user )
{
  kdDebug() << "delete user: " << user->getName() << " uid: " << user->getUID() << endl;
  if ( user->isDeleteHome ) {
    user->removeHome();
    user->removeCrontabs();
  }
  if ( user->isDeleteMailBox )
    user->removeMailBox();
/*
  user->removeProcesses();
*/
  return true;
}

KU::KUser *KU::KUsers::lookup(const TQString & name)
{
  KU::KUser *user;
  TQPtrListIterator<KU::KUser> it( mUsers );

  while ( (user = it.current()) != 0 && user->getName() != name ) ++it;
  return user;
}

KU::KUser *KU::KUsers::lookup(uid_t uid)
{
  KU::KUser *user;
  TQPtrListIterator<KU::KUser> it( mUsers );

  while ( (user = it.current()) != 0 && user->getUID() != uid ) ++it;
  return user;
}

KU::KUser *KU::KUsers::lookup_sam( const SID &sid )
{
  KU::KUser *user;
  TQPtrListIterator<KU::KUser> it( mUsers );

  while ( (user = it.current()) != 0 && user->getSID() != sid ) ++it;
  return user;
}

KU::KUser *KU::KUsers::lookup_sam( const TQString &sid )
{
  KU::KUser *user;
  TQPtrListIterator<KU::KUser> it( mUsers );

  while ( (user = it.current()) != 0 && user->getSID().getSID() != sid ) ++it;
  return user;
}

KU::KUser *KU::KUsers::lookup_sam( uint rid )
{
  KU::KUser *user;
  TQPtrListIterator<KU::KUser> it( mUsers );

  while ( (user = it.current()) != 0 && user->getSID().getRID() != rid ) ++it;
  return user;
}

uid_t KU::KUsers::first_free()
{
  uid_t t;

  for (t = mCfg->firstUID() ; t<65534; t++)
    if (lookup(t) == NULL)
      return t;

  return NO_FREE;
}

uint KU::KUsers::first_free_sam()
{
  uint t;

  for (t = 1000; t<65534; t++)
    if (lookup_sam(t) == NULL)
      return t;

  return 0;
}

uint KU::KUsers::count() const
{
  return mUsers.count();
}

KU::KUser *KU::KUsers::operator[](uint num)
{
  return mUsers.at(num);
}

KU::KUser *KU::KUsers::first()
{
  return mUsers.first();
}

KU::KUser *KU::KUsers::next()
{
  return mUsers.next();
}

void KU::KUsers::add(KU::KUser *user)
{
  mAdd.append( user );
}

void KU::KUsers::del(KU::KUser *user)
{
  mDel.append( user );
}

void KU::KUsers::mod(KU::KUser *uold, const KU::KUser &unew)
{
  mMod.insert( uold, unew );
}

void KU::KUsers::commit()
{
  kdDebug() << "KU::KUsers::commit()" << endl;
  KU::KUser *user;
  DelIt dit( mDelSucc );
  AddIt ait( mAddSucc );
  ModIt mit = mModSucc.begin();

//commit modifications
  while ( mit != mModSucc.end() ) {
    *(mit.key()) = mit.data();
    mit++;
  }
//commit deletes
  while ( (user = dit.current()) != 0 ) {
    ++dit;
    doDelete( user );
    mUsers.remove( user );
  }
//commit additions
  while ( (user = ait.current()) != 0 ) {
    ++ait;
    doCreate( user );
    mUsers.append( user );
  }

//clear the unsuccessful modifications
  cancelMods();
}

void KU::KUsers::cancelMods()
{
  KU::KUser *user;
  while ( (user = mAdd.first()) ) {
    delete user;
    mAdd.remove();
  }
  mDel.clear();
  mMod.clear();
}
