#include <qtextstream.h>
#include <qregexp.h>

#include "ftpaccess.h"
#include "support.h"
#include <fstream.h>
#include <stdlib.h>
#include <string.h>
#ifdef __DECCXX
#define _RWSTD_NO_NAMESPACE
#endif
#include <strstream.h>
#include <ctype.h>
#include <stdio.h>

#include <qfile.h>

#include <kdebug.h>

ftpaccess::ftpaccess(const QString & filename)
{
	classes=0;	// The pointers have to be set to 0 before calling
	noretrieves=0;	// cleanup() - most systems don't like free(0);
	messages=0;	
	readmes=0;	
	dlFree=0;
	dlFreeDir=0;
	mailRecipients=0;
	uploads=0;
	vhosts=0;

	cleanup();	// Set everything to default values...
	
	if(filename!=0)
		read(filename);
}
ftpaccess::~ftpaccess()
{
	cleanup();
}
void ftpaccess::cleanup()
{
	if(classes!=0) {
		for(unsigned int i=0;i<numclasses;i++)
			delete classes[i];
		free(classes);
		classes=0;
	}
	anonRoot=QString::null;
	guestRoot=QString::null;
	passwd=QString::null;
	shadow=QString::null;
	mailFrom=QString::null;
	mailServer=QString::null;
	numclasses=0;
	if(noretrieves!=0)
		delete noretrieves;
	if(dlFree!=0)
		delete dlFree;
	if(dlFreeDir!=0)
		delete dlFreeDir;
	if(mailRecipients!=0)
		delete mailRecipients;
	noretrieves=new QStringList;
	dlFree=new QStringList;
	dlFreeDir=new QStringList;
	mailRecipients=new QStringList;
	loginfails=5;
	priv=false;
	chmd=ANONYMOUS|GUEST|REAL;
	delet=ANONYMOUS|GUEST|REAL;
	overwrite=ANONYMOUS|GUEST|REAL;
	rename=ANONYMOUS|GUEST|REAL;
	umsk=ANONYMOUS|GUEST|REAL;
	passwd_check=NONE;
	banner=QString::null;
	hostname=QString::null;
	adminmail=QString::null;
	if(readmes!=0) {
		free(readmes);
		readmes=0;
	}
	if(messages!=0) {
		free(messages);
		messages=0;
	}
	if(uploads!=0) {
		free(uploads);
		uploads=0;
	}
	if(vhosts!=0) {
		free(vhosts);
		vhosts=0;
	}
	numvirtual=0;
	numMessages=0;
	numReadmes=0;
	msgShowEverytime=false;
	rdmShowEverytime=false;
	logCommands=0;
	logTransfers=0;
	logSecurity=0;
	logSyslog=0;
	ulDlRatio=0;
	ulDlRatioWho=0;
	limitTime=0;
	limitTimeWho=0;
	limitUpload=0;
	limitUploadWho=0;
	limitDownload=0;
	limitDownloadWho=0;
	mailAdmin=0;
}
bool ftpaccess::read(const QString & filename)
{
	ifstream f(QFile::encodeName(filename));
	if(f.bad())
		return false;
	cleanup();	// Set some defaults in case ftpaccess doesn't
			// contain everything

	char *tmp=new char[4096];
	char *line, *cmd;
	while(f.good()) {	// read the classes first - other commands refer to them...
		memset(tmp,0,4096);
		f.getline(tmp,4096,'\n');
		line=strdup(tmp);
		cmd=strtok(line," \t");
		if(cmd!=0 && *cmd!='#') {
			if(!strcasecmp(cmd,"class"))
				addClass(strtok(NULL,"\n"));
		}
		free(line);
	}
//	f.seekg(0,ios::beg);
	f.close();		// for some reason, f.seekg(0,ios::beg) doesn't
	f.open(QFile::encodeName(filename));	// work; f.close(); f.open() does. :/

	while(f.good()) {
		memset(tmp,0,4096);
		f.getline(tmp,4096,'\n');
		line=strdup(tmp);
		cmd=strtok(line," \t");
		if(cmd!=0 && *cmd!='#') {
			if(!strcasecmp(cmd,"autogroup")) {
				QString gn=QString(strtok(0," \t"));
				ftpclass *f=seekClass(strtok(0,"\n"));
				if(f!=0)
					f->setAutogroup(gn);
			} else if(!strcasecmp(cmd,"limit")) {
				ftpclass *f=seekClass(strtok(0," \t"));
				if(f!=0)
					f->addLimit(strtok(0,"\n"));
			} else if(!strcasecmp(cmd,"noretrieve")) {
				char *f;
				while((f=strtok(0," \t")))
					noretrieves->append(f);
			} else if(!strcasecmp(cmd,"loginfails")) {
				loginfails=atoi(strtok(0,"\n"));
			} else if(!strcasecmp(cmd,"private")) {
				if(!strcasecmp(strtok(0,"\n"),"yes"))
					priv=true;
				else
					priv=false;
			} else if(!strcasecmp(cmd,"banner")) {
				banner=strdup(strtok(0,"\n"));
			} else if(!strcasecmp(cmd,"hostname")) {
				hostname=strdup(strtok(0,"\n"));
			} else if(!strcasecmp(cmd,"email")) {
				adminmail=strdup(strtok(0,"\n"));
			} else if(!strcasecmp(cmd,"show-everytime")) {
				if(!strcasecmp(strtok(0," \t"),"message")) {
					if(!strcasecmp(strtok(0," \t"),"yes"))
						msgShowEverytime=true;
					else
						msgShowEverytime=false;
				} else {
					if(!strcasecmp(strtok(0," \t"),"yes"))
						rdmShowEverytime=true;
					else
						rdmShowEverytime=false;
				}
			} else if(!strcasecmp(cmd,"readme") || !strcasecmp(cmd,"message")) {
				char *path=strtok(0," \t");
				char *when=strtok(0," \t");
				char *dir=0;
				char *cls=strtok(0,"\n");
				if(strchr(when,'='))
					dir=strchr(when,'=')+1;
				ftpmessage *m=new ftpmessage(strcasecmp(cmd,"readme")==0,strcasecmp(when,"LOGIN")==0,path,dir,cls);
				if(!strcasecmp(cmd,"readme"))
					addReadme(m);
				else
					addMessage(m);
			} else if(!strcasecmp(cmd,"chmod")) {
				bool y=!strcasecmp(strtok(0," \t"),"yes");
				char *types=strtok(0,"\n");
				if(y) {
					if(strcasestr(types,"anonymous"))
						chmd|=ANONYMOUS;
					if(strcasestr(types,"guest"))
						chmd|=GUEST;
					if(strcasestr(types,"real"))
						chmd|=REAL;
				} else {
					if(strcasestr(types,"anonymous"))
						chmd&=~ANONYMOUS;
					if(strcasestr(types,"guest"))
						chmd&=~GUEST;
					if(strcasestr(types,"real"))
						chmd&=~REAL;
				}
			} else if(!strcasecmp(cmd,"delete")) {
				bool y=!strcasecmp(strtok(0," \t"),"yes");
				char *types=strtok(0,"\n");
				if(y) {
					if(strcasestr(types,"anonymous"))
						delet|=ANONYMOUS;
					if(strcasestr(types,"guest"))
						delet|=GUEST;
					if(strcasestr(types,"real"))
						delet|=REAL;
				} else {
					if(strcasestr(types,"anonymous"))
						delet&=~ANONYMOUS;
					if(strcasestr(types,"guest"))
						delet&=~GUEST;
					if(strcasestr(types,"real"))
						delet&=~REAL;
				}
			} else if(!strcasecmp(cmd,"overwrite")) {
				bool y=!strcasecmp(strtok(0," \t"),"yes");
				char *types=strtok(0,"\n");
				if(y) {
					if(strcasestr(types,"anonymous"))
						overwrite|=ANONYMOUS;
					if(strcasestr(types,"guest"))
						overwrite|=GUEST;
					if(strcasestr(types,"real"))
						overwrite|=REAL;
				} else {
					if(strcasestr(types,"anonymous"))
						overwrite&=~ANONYMOUS;
					if(strcasestr(types,"guest"))
						overwrite&=~GUEST;
					if(strcasestr(types,"real"))
						overwrite&=~REAL;
				}
			} else if(!strcasecmp(cmd,"rename")) {
				bool y=!strcasecmp(strtok(0," \t"),"yes");
				char *types=strtok(0,"\n");
				if(y) {
					if(strcasestr(types,"anonymous"))
						rename|=ANONYMOUS;
					if(strcasestr(types,"guest"))
						rename|=GUEST;
					if(strcasestr(types,"real"))
						rename|=REAL;
				} else {
					if(strcasestr(types,"anonymous"))
						rename&=~ANONYMOUS;
					if(strcasestr(types,"guest"))
						rename&=~GUEST;
					if(strcasestr(types,"real"))
						rename&=~REAL;
				}
			} else if(!strcasecmp(cmd,"umask")) {
				bool y=!strcasecmp(strtok(0," \t"),"yes");
				char *types=strtok(0,"\n");
				if(y) {
					if(strcasestr(types,"anonymous"))
						umsk|=ANONYMOUS;
					if(strcasestr(types,"guest"))
						umsk|=GUEST;
					if(strcasestr(types,"real"))
						umsk|=REAL;
				} else {
					if(strcasestr(types,"anonymous"))
						umsk&=~ANONYMOUS;
					if(strcasestr(types,"guest"))
						umsk&=~GUEST;
					if(strcasestr(types,"real"))
						umsk&=~REAL;
				}
			} else if(!strcasecmp(cmd,"passwd-check")) {
				passwd_check=0;
				char *type=strtok(0," \t");
				if(!strcasecmp(type,"none"))
					passwd_check|=NONE;
				else if(!strcasecmp(type,"trivial"))
					passwd_check|=TRIVIAL;
				else if(!strcasecmp(type,"rfc822"))
					passwd_check|=RFC822;
				char *enforce=strtok(0," \t\r");
				if(enforce && !strcasecmp(enforce,"enforce"))
					passwd_check|=ENFORCE;
				else if(enforce && !strcasecmp(enforce,"warn")) {
					passwd_check|=WARN;
				}
			} else if(!strcasecmp(cmd,"log")) {
				char *what=strtok(0," \t\n");
				if(!strcasecmp(what,"syslog"))
					logSyslog=true;
				else if(!strcasecmp(what,"commands")) {
					char *classes=strtok(0,"\n");
					if(strcasestr(classes,"anonymous"))
						logCommands|=ANONYMOUS;
					if(strcasestr(classes,"guest"))
						logCommands|=GUEST;
					if(strcasestr(classes,"real"))
						logCommands|=REAL;
				} else if(!strcasecmp(what,"security")) {
					char *classes=strtok(0,"\n");
					if(strcasestr(classes,"anonymous"))
						logSecurity|=ANONYMOUS;
					if(strcasestr(classes,"guest"))
						logSecurity|=GUEST;
					if(strcasestr(classes,"real"))
						logSecurity|=REAL;
				} else if(!strcasecmp(what,"transfers")) {
					char *classes=strtok(0," \t");
					char *dirs=strtok(0,"\n");
					if(strcasestr(dirs,"inbound")) {
						if(strcasestr(classes,"anonymous"))
							logTransfers|=ANONYMOUS;
						if(strcasestr(classes,"guest"))
							logTransfers|=GUEST;
						if(strcasestr(classes,"real"))
							logTransfers|=REAL;
					}
					if(strcasestr(dirs,"outbound")) {
						if(strcasestr(classes,"anonymous"))
							logTransfers|=ANONYMOUS_OUT;
						if(strcasestr(classes,"guest"))
							logTransfers|=GUEST_OUT;
						if(strcasestr(classes,"real"))
							logTransfers|=REAL_OUT;
					}
				}
			} else if(!strcasecmp(cmd,"anonymous-root")) {
				anonRoot=strdup(strtok(0," \t\n"));
			} else if(!strcasecmp(cmd,"guest-root")) {
				guestRoot=strdup(strtok(0," \t\n"));
			} else if(!strcasecmp(cmd,"passwd")) {
				passwd=strdup(strtok(0,"\n"));
			} else if(!strcasecmp(cmd,"shadow")) {
				shadow=strdup(strtok(0,"\n"));
			} else if(!strcasecmp(cmd,"ul-dl-rate")) {
				char *users=strtok(0," \t");
				if(!strcasecmp(users,"anonymous"))
					ulDlRatioWho=ANONYMOUS;
				else if(!strcasecmp(users,"guest"))
					ulDlRatioWho=GUEST;
				else
					ulDlRatioWho=ANONYMOUS|GUEST;
				ulDlRatio=atoi(strtok(0,"\n"));
			} else if(!strcasecmp(cmd,"limit-time")) {
				char *users=strtok(0," \t");
				if(!strcasecmp(users,"anonymous"))
					limitTimeWho=ANONYMOUS;
				else if(!strcasecmp(users,"guest"))
					limitTimeWho=GUEST;
				else
					limitTimeWho=ANONYMOUS|GUEST;
				limitTime=atoi(strtok(0,"\n"));
			} else if(!strcasecmp(cmd,"limit-upload")) {
				char *users=strtok(0," \t");
				if(!strcasecmp(users,"anonymous"))
					limitUploadWho=ANONYMOUS;
				else if(!strcasecmp(users,"guest"))
					limitUploadWho=GUEST;
				else
					limitUploadWho=ANONYMOUS|GUEST;
				limitUpload=atoi(strtok(0,"\n"));
			} else if(!strcasecmp(cmd,"limit-download")) {
				char *users=strtok(0," \t");
				if(!strcasecmp(users,"anonymous"))
					limitDownloadWho=ANONYMOUS;
				else if(!strcasecmp(users,"guest"))
					limitDownloadWho=GUEST;
				else
					limitDownloadWho=ANONYMOUS|GUEST;
				limitDownload=atoi(strtok(0,"\n"));
			} else if(!strcasecmp(cmd,"dl-free")) {
				char *who=strtok(0," \t");
				char *f;
				while((f=strtok(0," \t")))
					dlFree->append(f);
			} else if(!strcasecmp(cmd,"dl-free-dir")) {
				char *who=strtok(0," \t");
				char *f;
				while((f=strtok(0," \t")))
					dlFreeDir->append(f);
			} else if(!strcasecmp(cmd,"incmail")) {
				mailRecipients->append(strtok(0,"\n"));
				mailAdmin=true;
			} else if(!strcasecmp(cmd,"mailfrom")) {
				setMailFrom(strtok(0,"\n"));
				mailAdmin=true;
			} else if(!strcasecmp(cmd,"mailserver")) {
				setMailServer(strtok(0,"\n"));
				mailAdmin=true;
			} else if(!strcasecmp(cmd,"virtual")) {
				char *t=strtok(0, " \t\n");
				if(t!=0) {
					QString IP=t;
					t=strtok(0, " \t\n");
					QString what=t;
					if(what!="root" && what!="banner" && what!="logfile" && what!="hostname" && what!="email" && what!="allow" && what!="deny" && what!="private" && what!="passwd" && what!="shadow") // unknown command
						continue;
					t=strtok(0, "\n");
					ftpvirtual *v=0;
					bool isNew=true;
					for(int i=0; i<numvirtual && isNew; i++)
						if(vhosts[i]->getIP()==IP) {
							isNew=false;
							v=vhosts[i];
						}
					if(isNew) { // Create new vhost...
						v=new ftpvirtual;
						v->setIP(IP);
						addVirtual(v);
					}
					if(what=="root")
						v->setRoot(t);
					else if(what=="banner")
						v->setBanner(t);
					else if(what=="logfile")
						v->setLogfile(t);
					else if(what=="hostname")
						v->setHostname(t);
					else if(what=="email")
						v->setEmail(t);
					else if(what=="allow" || what=="deny") {
						while(isspace(*t)) t++;
						while(isspace(t[strlen(t)-1])) t[strlen(t)-1]=0;
						if(strlen(t)==0 || !strcmp(t,"*")) {
							if(what=="allow")
								v->setReal(ftpvirtual::all);
							else
								v->setReal(ftpvirtual::none);
						} else {
							if(what=="allow")
								v->setReal(ftpvirtual::allowSpecified);
							else
								v->setReal(ftpvirtual::denySpecified);
							v->clearUsers();
							char *s=strtok(t, " \t\n");
							while(s) {
								v->addUser(s);
								s=strtok(0, " \t\n");
							}
						}
					} else if(what=="private")
						v->setAnon(false);
					else if(what=="passwd")
						v->setPasswd(t);
					else if(what=="shadow")
						v->setShadow(t);
					else
						kdWarning() << "unknown directive virtual " << what << endl;
				}

			} else if(!strcasecmp(cmd,"upload")) {
				char *rd, *dg, *allow, *own, *grp, *mode, *dir, *dmod;
				ftpupload *u=new ftpupload;
				rd=strtok(0," \t\n");
				if(rd!=0) {
					u->setRootDir(rd);
					dg=strtok(0," \t\n");
					if(dg!=0) {
						u->setDirGlob(dg);
						allow=strtok(0," \t\n");
						if(allow!=0) {
							u->setAllow(strcasecmp(allow,"yes")==0);
							own=strtok(0," \t\n");
							if(own!=0) {
								u->setOwner(own);
								grp=strtok(0," \t\n");
								if(grp!=0) {
									u->setGroup(grp);
									mode=strtok(0," \t\n");
									if(mode!=0) {
										u->setMode(strtoul(mode,0,8));
										dir=strtok(0," \t\n");
										if(dir!=0) {
											u->setPermitDirs(strcasecmp(dir,"dirs")==0);
											dmod=strtok(0," \t\n");
											if(dmod!=0)
												u->setDMode(strtoul(dmod,0,8));
										}
									}
								}
							}
						}
					}
				}
				addUpload(u);
			}
		}
		free(line);
	}
	f.close();
	return true;
}
bool ftpaccess::write(const QString & filename)
{
	ofstream f(QFile::encodeName(filename));
	if(f.bad())
		return false;
	f << *this;
	f.close();
	return true;
}
void ftpaccess::addClass(const QString & cmd)
{
	ftpclass *f=new ftpclass(cmd);
	addClass(f);
}
void ftpaccess::addClass(ftpclass *c)
{
	if(classes==0)
		classes=(ftpclass **) malloc(sizeof(ftpclass *)*50);
	classes[numclasses++]=c;
	if((numclasses%50)==0)
		classes=(ftpclass **) malloc(sizeof(ftpclass *)*(numclasses+50));
}
void ftpaccess::removeClass(const QString & name)
{
	for(unsigned int i=0;i<numclasses;i++) {
		if(classes[i]->getName() == name) {
			delete classes[i];
			for(unsigned int j=i;j<numclasses-1;j++)
				classes[j]=classes[j+1];
			classes[numclasses--]=0;
		}
	}
}
ftpclass *ftpaccess::getNextClass(ftpclass *previous)
{
	if(classes==0)
		return 0;
	if(previous==0)
		return classes[0];
	for(unsigned int i=0; i<numclasses-1; i++)		
		if(classes[i]==previous)
			return classes[i+1];
	return 0;
}
ftpclass *ftpaccess::seekClass(const QString & name)
{
	ftpclass *f=0;
	ftpclass *thisclass=0;
	while((f=getNextClass(f))!=0 && thisclass==0)
		if (name == f->getName())
			thisclass=f;
	return thisclass;
}
ftpmessage *ftpaccess::getFirstMessage() const
{
	if(numMessages==0)
		return 0;
	return messages[0];
}
ftpmessage *ftpaccess::getNextMessage(ftpmessage *m) const
{
	if(numMessages==0)
		return 0;
	for(unsigned int i=0; i<numMessages-1; i++)
		if(messages[i]==m)
			return messages[i+1];
	return 0;
}
ftpmessage *ftpaccess::getMessage(unsigned int const num) const
{
	if(num<numMessages)
		return messages[num];
	return 0;
}
void ftpaccess::addMessage(ftpmessage *m)
{
	if(messages==0)
		messages=(ftpmessage **) malloc(sizeof(ftpmessage *)*50);
	messages[numMessages++]=m;
	if((numMessages%50)==0)
		messages=(ftpmessage **) malloc(sizeof(ftpmessage *)*(numMessages+50));
}
void ftpaccess::deleteMessage(ftpmessage *m)
{
	for(unsigned int i=0;i<numMessages;i++) {
		if(messages[i]==m) {
			delete messages[i];
			for(unsigned int j=i;j<numMessages-1;j++)
				messages[j]=messages[j+1];
			messages[numMessages--]=0;
		}
	}
}
void ftpaccess::deleteMessage(int num)
{
	for(unsigned int i=num;i<numMessages-1;i++)
		messages[i]=messages[i+1];
	messages[numMessages--]=0;
}
ftpmessage *ftpaccess::getFirstReadme() const
{
	if(numReadmes==0)
		return 0;
	return readmes[0];
}
ftpmessage *ftpaccess::getNextReadme(ftpmessage *r) const
{
	if(numReadmes==0)
		return 0;
	for(unsigned int i=0; i<numReadmes-1; i++)
		if(readmes[i]==r)
			return readmes[i+1];
	return 0;
}
ftpmessage *ftpaccess::getReadme(unsigned int const num) const
{
	if(num<numReadmes)
		return readmes[num];
	return 0;
}
void ftpaccess::addReadme(ftpmessage *r)
{
	if(readmes==0)
		readmes=(ftpmessage **) malloc(sizeof(ftpmessage *)*50);
	readmes[numReadmes++]=r;
	if((numReadmes%50)==0)
		readmes=(ftpmessage **) malloc(sizeof(ftpmessage *)*(numReadmes+50));
}
void ftpaccess::deleteReadme(ftpmessage *r)
{
	for(unsigned int i=0;i<numReadmes;i++) {
		if(readmes[i]==r) {
			delete readmes[i];
			for(unsigned int j=i;j<numReadmes-1;j++)
				readmes[j]=readmes[j+1];
			readmes[numReadmes--]=0;
		}
	}
}
void ftpaccess::deleteReadme(int num)
{
	for(unsigned int i=num;i<numReadmes-1;i++)
		readmes[i]=readmes[i+1];
	readmes[numReadmes--]=0;
}
void ftpaccess::setAnonRoot(QString const a)
{
	anonRoot=a;
}
void ftpaccess::setGuestRoot(QString const g)
{
	guestRoot=g;
}
void ftpaccess::setPasswd(QString const p)
{
	passwd=p;
}
void ftpaccess::setShadow(QString const s)
{
	shadow=s;
}
void ftpaccess::addVirtual(ftpvirtual *v)
{
	if(vhosts==0)
		vhosts=(ftpvirtual **) malloc(sizeof(ftpvirtual*));
	else
		vhosts=(ftpvirtual **) realloc(vhosts, sizeof(ftpvirtual*)*(numvirtual+1));
	vhosts[numvirtual++]=v;
}
ftpupload *ftpaccess::getFirstUpload() const
{
	if(uploads!=0)
		return uploads[0];
	return 0;
}
ftpupload *ftpaccess::getNextUpload(ftpupload *last) const
{
	for(int i=0; i<numUploads-1; i++)
		if(uploads[i]==last)
			return uploads[i+1];
	return 0;
}
ftpupload *ftpaccess::getUploadNo(int num) const
{
	if(numUploads>=num)
		return uploads[num];
	return 0;
}
int ftpaccess::getNumUploads() const
{
	return numUploads;
}
void ftpaccess::deleteUpload(int num)
{
	if(num>=0 && num<numUploads) {
		delete uploads[num];
		for(int i=num; i<numUploads-1; i++)
			uploads[i]=uploads[i+1];
	}
	numUploads--;
}
void ftpaccess::deleteUpload(ftpupload *u)
{
	for(int i=0; i<numUploads; i++)
		if(uploads[i]==u) {
			delete uploads[i];
			for(int j=i; j<numUploads-1; j++)
				uploads[j]=uploads[j+1];
		}
	numUploads--;
}
void ftpaccess::addUpload(ftpupload *u)
{
	if(uploads==0) {
		uploads=(ftpupload **) malloc(1024*sizeof(ftpupload *));
		numUploads=0;
	}
	uploads[numUploads++]=u;
}

ftpclass::ftpclass(const QString & cmd)
{
	first=0;
	users=0;
	auto_group=QString::null;
	if(cmd==0) {
		name=QString::null;
		addrs=QString::null;
	} else {
		char *Cmd=strdup(cmd.ascii());
		name=strdup(strtok(Cmd," \t"));
		char *tmp=strtok(NULL," \t");
		if(strcasestr(tmp,"anonymous"))
			users|=ANONYMOUS;
		if(strcasestr(tmp,"guest"))
			users|=GUEST;
		if(strcasestr(tmp,"real"))
			users|=REAL;
		addrs=strdup(strtok(NULL,"\n"));
		free(Cmd);
	}
}
ftpclass::~ftpclass()
{
}
void ftpclass::setAnon(bool anon)
{
	if(anon)
		users|=ANONYMOUS;
	else
		users&=~ANONYMOUS;
}
void ftpclass::setGuest(bool guest)
{
	if(guest)
		users|=GUEST;
	else
		users&=~GUEST;
}
void ftpclass::setReal(bool real)
{
	if(real)
		users|=REAL;
	else
		users&=~REAL;
}
void ftpclass::setName(QString const &newname)
{
	name=newname.copy();
}
void ftpclass::setAddrs(QString const &newaddrs)
{
	addrs=newaddrs.copy();
}
void ftpclass::setAutogroup(const QString &group)
{
	if(group.isEmpty())
		auto_group=QString::null;
	else
		auto_group=group.copy();
}
void ftpclass::addLimit(ftplimit *l)
{
	if(first==0)
		first=l;
	else
		first->addLimit(l);
}
void ftpclass::addLimit(QString const &l)
{
	ftplimit *f=new ftplimit(l);
	addLimit(f);
}
void ftpclass::deleteLimit(ftplimit const * const l)
{
	ftplimit *fl=0, *previous=0;
	bool done=false;
	while((fl=getNextLimit(fl)) && !done) {
		if(fl==l) {
			if(previous!=0)
				previous->setNextLimit(getNextLimit(fl));
			else
				first=getNextLimit(fl);
			delete fl;
			done=true;
		}
		previous=fl;
	}
}

ftplimit *ftpclass::getNextLimit(ftplimit const * const l) const
{
	if(l==0)
		return first;
	else {
		ftplimit *f=first, *n=0;
		while(f!=0 && n==0) {
			if(f==l) n=f->getNext();
			f=f->getNext();
		}
		return n;
	}
}
bool const ftpclass::isAnon() const
{
	return (users&ANONYMOUS)!=0;
}
bool const ftpclass::isGuest() const
{
	return (users&GUEST)!=0;
}
bool const ftpclass::isReal() const
{
	return (users&REAL)!=0;
}
QString const &ftpclass::getName() const
{
	return name;
}
QString const &ftpclass::getAddrs() const
{
	return addrs;
}
QString const &ftpclass::getAutogroup() const
{
	return auto_group;
}

ftplimit::ftplimit(QString const &limit)
{
	weekdays=0;
	hours=QString::null;
	numUsers=0;
	msgfile=QString::null;
	next=0;
	if(limit!=0) {
		char *s=strdup(limit.latin1());
		char *num=strtok(s," \t");
		numUsers=atoi(num);
		char *ti=strtok(0," \t");
		msgfile=strdup(strtok(0,"\n"));
		if(strchr(ti,'|')) {
			char *tmp=strtok(ti,"|");
			char *tmp2;
			while((tmp2=strtok(0,"|"))!=0) {
				ftplimit *nl=new ftplimit(numUsers,tmp2,msgfile);
				addLimit(nl);
			}
			ti=tmp;
		}
		if(strcasestr(ti,"any"))
			setAny(true);
		if(strcasestr(ti,"Mo"))
			setMo(true);
		if(strcasestr(ti,"Tu"))
			setTu(true);
		if(strcasestr(ti,"We"))
			setWe(true);
		if(strcasestr(ti,"Th"))
			setTh(true);
		if(strcasestr(ti,"Fr"))
			setFr(true);
		if(strcasestr(ti,"Sa"))
			setSa(true);
		if(strcasestr(ti,"Su"))
			setSu(true);
		char *h=strdup(ti);
		while(isalpha(*h))
			strcpy(h,h+1);
		if(strlen(h)>0)
			hours=QString(h);
		else
			hours=QString::null;
		free(h);
		free(s);
	}
}
ftplimit::ftplimit(int const num, QString const &t, QString const &msg)
{
	weekdays=0;
	numUsers=num;
	msgfile=msg;
	next=0;
	if(strcasestr(t.latin1(),"any"))
		setAny(true);
	if(strcasestr(t.latin1(),"Mo"))
		setMo(true);
	if(strcasestr(t.latin1(),"Tu"))
		setTu(true);
	if(strcasestr(t.latin1(),"We"))
		setWe(true);
	if(strcasestr(t.latin1(),"Th"))
		setTh(true);
	if(strcasestr(t.latin1(),"Fr"))
		setFr(true);
	if(strcasestr(t.latin1(),"Sa"))
		setSa(true);
	if(strcasestr(t.latin1(),"Su"))
		setSu(true);

	QString h = t;
	while (h.at(0).isLetter())
		h.remove(0, 1);

	if (!h.isEmpty())
		hours=QString(h);
	else
		hours=QString::null;
}
ftplimit::~ftplimit()
{
}
void ftplimit::setAny(bool any)
{
	if(any)
		weekdays|=ANY;
	else
		weekdays&=~ANY;

}
void ftplimit::setMo(bool mo)
{
	if(mo)
		weekdays|=MO;
	else
		weekdays&=~MO;
}
void ftplimit::setTu(bool tu)
{
	if(tu)
		weekdays|=TU;
	else
		weekdays&=~TU;
}
void ftplimit::setWe(bool we)
{
	if(we)
		weekdays|=WE;
	else
		weekdays&=~WE;
}
void ftplimit::setTh(bool th)
{
	if(th)
		weekdays|=TH;
	else
		weekdays&=~TH;
}
void ftplimit::setFr(bool fr)
{
	if(fr)
		weekdays|=FR;
	else
		weekdays&=~FR;
}
void ftplimit::setSa(bool sa)
{
	if(sa)
		weekdays|=SA;
	else
		weekdays&=~SA;
}
void ftplimit::setSu(bool su)
{
	if(su)
		weekdays|=SU;
	else
		weekdays&=~SU;
}
void ftplimit::setHours(QString const &h)
{
	hours=h.copy();
}
void ftplimit::setMsgfile(QString const &f)
{
	msgfile=f.copy();
}
void ftplimit::addLimit(ftplimit * n)
{
	if(next==0)
		next=n;
	else
		next->addLimit(n);
}
QString const ftplimit::getId() const	// Return a recognizable ID for the limit
{
	char *h=(char *) malloc(128);
	memset(h,0,128);
	strstream id(h,128);
	id.seekp(0);
	if(isAny())
		id << "Any";
	if(isMo())
		id << "Mo";
	if(isTu())
		id << "Tu";
	if(isWe())
		id << "We";
	if(isTh())
		id << "Th";
	if(isFr())
		id << "Fr";
	if(isSa())
		id << "Sa";
	if(isSu())
		id << "Su";
	if(getHours()!=0)
		id << getHours().ascii();
	if(getNum()>=0)
		id << ":" << getNum();
	else
		id << ": no limit";
	return QString(h);
}
void ftplimit::setNextLimit(ftplimit *fl)
{
	next=fl;
}

ftpmessage::ftpmessage(bool Readme, bool Login, QString const &p, 
		       QString const &d, QString const &c)
{
	isReadme=Readme;
	atLogin=Login;
	path=p;
	dir=d;
	cls=c;
	if(!dir.isEmpty() && dir.at(dir.length()-1)=='/')
		dir.truncate(dir.length() - 1);

        cls.replace(QRegExp("\t"), " "); // Replace tabs with spaces - more readable!
}

ftpmessage::~ftpmessage()
{
}

QString const ftpmessage::getId() const // return a readable ID for QListBox
{
        QString ID;
//	char *ID=(char *) malloc(1024);
//	strstream i(ID,1024);
	QTextStream i(&ID, IO_WriteOnly);
	i << path << " (";
	if(atLogin)
		i << "login";
	else
		i << "cwd:" << dir;
	i << ")";
	if(!cls.isEmpty())
		i << " [" << cls << "]";
	return ID;
}

ftpupload::ftpupload()
{
	rootDir=QString::null;
	dirGlob=QString::null;
	allow=false;
	owner=QString::null;
	group=QString::null;
	mode=0600; // set reasonable defaults...
	permitDirs=false;
	dMode=0700;
}
ftpupload::~ftpupload()
{
}
QString const ftpupload::getId() const
{
	QString result;
	result=rootDir + " " + dirGlob + ": ";
	if(allow) {
		result += "yes";
		if(owner!=0) {
			result += " " + owner;
			if(group!=0)
				result += "." + group;
		}
	} else
		result += "no";
	return result;
}

ftpvirtual::ftpvirtual()
{
	IP=QString::null;
	root=QString::null;
	banner=QString::null;
	logfile=QString::null;
	hostname=QString::null;
	email=QString::null;
	passwd=QString::null;
	shadow=QString::null;
	allowAnon=true;
	allowReal=none;
	users.clear();
}

ostream &operator <<(ostream &os, ftpaccess &f)
{
	ftpclass *c=f.getNextClass(0);
	os << "# This file was generated by the KDE wu-ftpd configurator." << endl
	   << "# (c) 2000 by Bernhard Rosenkränzer (bero@redhat.com)" << endl;
	while(c!=0) {
		os << *c;
		c=f.getNextClass(c);
	}
	ftpupload *u=f.getFirstUpload();
	while(u) {
		os << *u;
		u=f.getNextUpload(u);
	}

	if(f.noretrieves!=0) {
	os << "noretrieve";
		for ( QStringList::Iterator it = f.noretrieves->begin();
		      it != f.noretrieves->end();
		      ++it )
			os << " " << (*it).latin1();
		os << endl;
	}

	os << "loginfails " << f.loginfails << endl;
	if(f.priv)
		os << "private yes" << endl;
	else
		os << "private no" << endl;
	if(!f.banner.isEmpty())
		os << "banner " << f.banner.latin1() << endl;
	if(!f.hostname.isEmpty())
		os << "hostname " << f.hostname.latin1() << endl;
	if(!f.adminmail.isEmpty())
		os << "email " << f.adminmail.latin1() << endl;
	if(f.msgShowEverytime)
		os << "show-everytime message yes" << endl;
	if(f.rdmShowEverytime)
		os << "show-everytime readme yes" << endl;
	for(int i=0;i<f.getNumMessages();i++)
		os << *(f.getMessage(i));
	for(int i=0;i<f.getNumReadmes();i++)
		os << *(f.getReadme(i));
	if(f.chmd!=(ANONYMOUS|GUEST|REAL)) {
		bool first=true;
		os << "chmod\tno\t";
		if((f.chmd&ANONYMOUS)==0) {
			os << "anonymous";
			first=false;
		}
		if((f.chmd&GUEST)==0) {
			if(!first)
				os << ",";
			else
				first=false;
			os << "guest";
		}
		if((f.chmd&REAL)==0) {
			if(!first)
				os << ",";
			os << "real";
		}
		os << endl;
	}
	if(f.delet!=(ANONYMOUS|GUEST|REAL)) {
		bool first=true;
		os << "delete\tno\t";
		if((f.delet&ANONYMOUS)==0) {
			os << "anonymous";
			first=false;
		}
		if((f.delet&GUEST)==0) {
			if(!first)
				os << ",";
			else
				first=false;
			os << "guest";
		}
		if((f.delet&REAL)==0) {
			if(!first)
				os << ",";
			os << "real";
		}
		os << endl;
	}
	if(f.overwrite!=(ANONYMOUS|GUEST|REAL)) {
		bool first=true;
		os << "overwrite\tno\t";
		if((f.overwrite&ANONYMOUS)==0) {
			os << "anonymous";
			first=false;
		}
		if((f.overwrite&GUEST)==0) {
			if(!first)
				os << ",";
			else
				first=false;
			os << "guest";
		}
		if((f.overwrite&REAL)==0) {
			if(!first)
				os << ",";
			os << "real";
		}
		os << endl;
	}
	if(f.rename!=(ANONYMOUS|GUEST|REAL)) {
		bool first=true;
		os << "rename\tno\t";
		if((f.rename&ANONYMOUS)==0) {
			os << "anonymous";
			first=false;
		}
		if((f.rename&GUEST)==0) {
			if(!first)
				os << ",";
			else
				first=false;
			os << "guest";
		}
		if((f.rename&REAL)==0) {
			if(!first)
				os << ",";
			os << "real";
		}
		os << endl;
	}
	if(f.umsk!=(ANONYMOUS|GUEST|REAL)) {
		bool first=true;
		os << "umask\tno\t";
		if((f.umsk&ANONYMOUS)==0) {
			os << "anonymous";
			first=false;
		}
		if((f.umsk&GUEST)==0) {
			if(!first)
				os << ",";
			else
				first=false;
			os << "guest";
		}
		if((f.umsk&REAL)==0) {
			if(!first)
				os << ",";
			os << "real";
		}
		os << endl;
	}
	os << "passwd-check\t";
	if(f.passwd_check&TRIVIAL)
		os << "trivial\t";
	else if(f.passwd_check&RFC822)
		os << "rfc822\t";
	else
		os << "none\t";
	if(f.passwd_check&ENFORCE)
		os << "enforce" << endl;
	else
		os << "warn" << endl;
	if(f.logCommands!=0) {
		bool first=true;
		os << "log commands ";
		if(f.logCommands&ANONYMOUS) {
			os << "anonymous";
			first=false;
		}
		if(f.logCommands&GUEST) {
			if(first)
				first=false;
			else
				os << ",";
			os << "guest";
		}
		if(f.logCommands&REAL) {
			if(!first)
				os << ",";
			os << "real";
		}
		os << endl;
	}
	if(f.logSecurity!=0) {
		bool first=true;
		os << "log security ";
		if(f.logSecurity&ANONYMOUS) {
			os << "anonymous";
			first=false;
		}
		if(f.logSecurity&GUEST) {
			if(first)
				first=false;
			else
				os << ",";
			os << "guest";
		}
		if(f.logSecurity&REAL) {
			if(!first)
				os << ",";
			os << "real";
		}
		os << endl;
	}
	if(f.logTransfers&(ANONYMOUS|GUEST|REAL)) {
		bool first=true;
		os << "log transfers ";
		if(f.logTransfers&ANONYMOUS) {
			os << "anonymous";
			first=false;
		}
		if(f.logTransfers&GUEST) {
			if(first)
				first=false;
			else
				os << ",";
			os << "guest";
		}
		if(f.logTransfers&REAL) {
			if(!first)
				os << ",";
			os << "real";
		}
		os << " inbound" << endl;
	}
	if(f.logTransfers&(ANONYMOUS_OUT|GUEST_OUT|REAL_OUT)) {
		bool first=true;
		os << "log transfers ";
		if(f.logTransfers&ANONYMOUS_OUT) {
			os << "anonymous";
			first=false;
		}
		if(f.logTransfers&GUEST_OUT) {
			if(first)
				first=false;
			else
				os << ",";
			os << "guest";
		}
		if(f.logTransfers&REAL_OUT) {
			if(!first)
				os << ",";
			os << "real";
		}
		os << " outbound" << endl;
	}
	if(f.logSyslog)
		os << "log syslog" << endl;
	if(!f.anonRoot.isEmpty())
		os << "anonymous-root " << f.anonRoot.latin1() << endl;
	if(!f.guestRoot.isEmpty())
		os << "guest-root " << f.guestRoot.latin1() << endl;
	if(!f.passwd.isEmpty())
		os << "passwd " << f.passwd.latin1() << endl;
	if(!f.shadow.isEmpty())
		os << "shadow " << f.shadow.latin1() << endl;
	if(f.ulDlRatioWho) {
		os << "ul-dl-rate ";
		if((f.ulDlRatioWho&(ANONYMOUS|GUEST))==(ANONYMOUS|GUEST))
			os << "* ";
		else if(f.ulDlRatioWho&ANONYMOUS)
			os << "anonymous ";
		else if(f.ulDlRatioWho&GUEST)
			os << "guest ";
		os << f.ulDlRatio << endl;
	}
	if(f.limitTimeWho) {
		os << "limit-time ";
		if((f.limitTimeWho&(ANONYMOUS|GUEST))==(ANONYMOUS|GUEST))
			os << "* ";
		else if(f.limitTimeWho&ANONYMOUS)
			os << "anonymous ";
		else if(f.limitTimeWho&GUEST)
			os << "guest ";
		os << f.limitTime << endl;
	}
	if(f.limitUploadWho) {
		os << "limit-upload ";
		if((f.limitUploadWho&(ANONYMOUS|GUEST))==(ANONYMOUS|GUEST))
			os << "* ";
		else if(f.limitUploadWho&ANONYMOUS)
			os << "anonymous ";
		else if(f.limitUploadWho&GUEST)
			os << "guest ";
		os << f.limitUpload << endl;
	}
	if(f.limitDownloadWho) {
		os << "limit-download ";
		if((f.limitDownloadWho&(ANONYMOUS|GUEST))==(ANONYMOUS|GUEST))
			os << "* ";
		else if(f.limitDownloadWho&ANONYMOUS)
			os << "anonymous ";
		else if(f.limitDownloadWho&GUEST)
			os << "guest ";
		os << f.limitDownload << endl;
	}
	for ( QStringList::Iterator it = f.dlFree->begin();
	      it != f.dlFree->end();
	      ++it )
		os << "dl-free * " << (*it).latin1() << endl;

	for ( QStringList::Iterator it = f.dlFreeDir->begin();
	      it != f.dlFreeDir->end();
	      ++it )
		os << "dl-free-dir * " << (*it).latin1() << endl;
	if(f.mailAdmin) {
		if(!f.mailFrom.isEmpty())
			os << "mailfrom " << f.mailFrom.latin1() << endl;
		if(!f.mailServer.isEmpty())
			os << "mailserver " << f.mailServer.latin1() << endl;
		for ( QStringList::Iterator it = f.mailRecipients->begin();
		      it != f.mailRecipients->end();
		      ++it )
			os << "incmail " << (*it).latin1() << endl;
	}
	os << endl;
	// Add virtual hosts...
	for(int i=0; i<f.numvirtual; i++)
		os << *(f.vhosts[i]);

	return os;
}
ostream &operator<<(ostream &os, ftpclass &f)
{
	bool first=true;
	os << "class " << f.getName().latin1() << " ";
	if(f.isAnon()) {
		os << "anonymous";
		first=false;
	}
	if(f.isGuest()) {
		if(!first)
			os << ",";
		os << "guest";
		first=false;
	}
	if(f.isReal()) {
		if(!first)
			os << ",";
		os << "real";
	}
	os << " " << f.getAddrs().latin1() << endl;
	if(!f.getAutogroup().isEmpty()) {
		os << "autogroup " << f.getAutogroup().latin1() << " " << f.getName().latin1() << endl;
	}
	ftplimit *fl=0;
	while((fl=f.getNextLimit(fl)))
		os << "limit " << f.getName().latin1() << "\t" << *fl;
	return os;
}
ostream &operator <<(ostream &os, ftplimit &f)
{
	os << f.getNum() << "\t";
	if(f.isAny())
		os << "Any";
	else {
		if(f.isMo())
			os << "Mo";
		if(f.isTu())
			os << "Tu";
		if(f.isWe())
			os << "We";
		if(f.isTh())
			os << "Th";
		if(f.isFr())
			os << "Fr";
		if(f.isSa())
			os << "Sa";
		if(f.isSu())
			os << "Su";
	}
	if(f.getHours()!=0)
		os << f.getHours().latin1();
	if(!f.getMsgfile().isEmpty())
		os << "\t" << f.getMsgfile().latin1() << endl;
	else
		os << "\t" << "/etc/msg.toomany" << endl;
	return os;
}
ostream &operator << (ostream &os, ftpmessage &f)
{
	if(f.isReadme)
		os << "readme\t";
	else
		os << "message\t";
	os << f.path.latin1() << "\t";
	if(f.atLogin)
		os << "LOGIN";
	else
		os << "CWD=" << f.dir.latin1();
	if(f.cls!=0)
		os << "\t" << f.cls.latin1();
	os << endl;
	return os;
}
ostream &operator << (ostream &os, ftpupload &f)
{
	os << "upload\t" << f.rootDir.latin1() << "\t" << f.dirGlob.latin1() << "\t";
	if(f.allow)
		os << "yes";
	else
		os << "no";
	if(!f.owner.isEmpty()) {
		os << "\t" << f.owner.latin1();
		if(!f.group.isEmpty()) {
			os << "\t" << f.group.latin1();
			if(f.mode) {
				os << "\t0" << std::oct << f.mode << "\t";
				if(f.permitDirs) {
					os << "dirs";
					if(f.dMode)
						os << "\t0" << std::oct << f.dMode;
				} else
					os << "nodirs";
			}
		}
	}
	os << endl;
	return os;
}
ostream &operator << (ostream &os, ftpvirtual &f)
{
	if(f.IP.isEmpty())
		return os;
	if(!f.root.isEmpty())
		os << "virtual " << f.IP.latin1() << " root " << f.root.latin1() << endl;
	if(!f.banner.isEmpty())
		os << "virtual " << f.IP.latin1() << " banner " << f.banner.latin1() << endl;
	if(!f.logfile.isEmpty())
		os << "virtual " << f.IP.latin1() << " logfile " << f.logfile.latin1() << endl;
	if(!f.hostname.isEmpty())
		os << "virtual " << f.IP.latin1() << " hostname " << f.hostname.latin1() << endl;
	if(!f.email.isEmpty())
		os << "virtual " << f.IP.latin1() << " email " << f.email.latin1() << endl;
	if(!f.passwd.isEmpty())
		os << "virtual " << f.IP.latin1() << " passwd " << f.passwd.latin1() << endl;
	if(!f.shadow.isEmpty())
		os << "virtual " << f.IP.latin1() << " shadow " << f.shadow.latin1() << endl;
	if(!f.allowAnon)
		os << "virtual " << f.IP.latin1() << " private" << endl;
	if(f.allowReal==ftpvirtual::none)
		os << "virtual " << f.IP.latin1() << " deny *" << endl;
	else if(f.allowReal==ftpvirtual::all)
		os << "virtual " << f.IP.latin1() << " allow *" << endl;
	else if(f.allowReal==ftpvirtual::allowSpecified || f.allowReal==ftpvirtual::denySpecified) {
		if(f.allowReal==ftpvirtual::allowSpecified)
			os << "virtual " << f.IP.latin1() << " allow ";
		else
			os << "virtual " << f.IP.latin1() << " deny ";
		for(QStringList::Iterator it=f.users.begin(); it!=f.users.end(); ++it) {
			os << (*it).latin1() << " ";
		}
		os << endl;
	}
	os << endl;
	return os;
}

