Example #1
0
File: vacfs.c Project: npe9/harvey
char*
rversion(Fid *unused)
{
	Fid *f;

	USED(unused);

	for(f = fids; f; f = f->next)
		if(f->busy)
			rclunk(f);

	if(rhdr.msize < 256)
		return vtstrdup("version: message size too small");
	messagesize = rhdr.msize;
	if(messagesize > sizeof mdata)
		messagesize = sizeof mdata;
	thdr.msize = messagesize;
	if(strncmp(rhdr.version, "9P2000", 6) != 0)
		return vtstrdup("unrecognized 9P version");
	thdr.version = "9P2000";
	if(strncmp(rhdr.version, "9P2000.u", 8) == 0){
		dotu = 1;
		thdr.version = "9P2000.u";
	}
	return nil;
}
Example #2
0
File: vacfs.c Project: npe9/harvey
char*
rread(Fid *f)
{
	char *buf;
	int64_t off;
	int cnt;
	VacFile *vf;
	char err[80];
	int n;

	if(!f->busy)
		return vtstrdup(Enotexist);
	vf = f->file;
	thdr.count = 0;
	off = rhdr.offset;
	buf = thdr.data;
	cnt = rhdr.count;
	if(f->qid.type & QTDIR)
		n = vacdirread(f, buf, off, cnt);
	else if(vacfilegetmode(f->file)&ModeDevice)
		return vtstrdup("device");
	else if(vacfilegetmode(f->file)&ModeLink)
		return vtstrdup("symbolic link");
	else if(vacfilegetmode(f->file)&ModeNamedPipe)
		return vtstrdup("named pipe");
	else
		n = vacfileread(vf, buf, cnt, off);
	if(n < 0) {
		rerrstr(err, sizeof err);
		return vtstrdup(err);
	}
	thdr.count = n;
	return 0;
}
Example #3
0
File: vacfs.c Project: npe9/harvey
char *
rremove(Fid *f)
{
	VacFile *vf, *vfp;
	char errbuf[80];
	char *err = nil;

	if(!f->busy)
		return vtstrdup(Enotexist);
	vf = f->file;
	vfp = vacfilegetparent(vf);

	if(!permf(vfp, f->user, Pwrite)) {
		err = Eperm;
		goto Exit;
	}

	if(!vacfileremove(vf)) {
		rerrstr(errbuf, sizeof errbuf);
		err = errbuf;
	}

Exit:
	vacfiledecref(vfp);
	rclunk(f);
	return vtstrdup(err);
}
Example #4
0
File: vacfs.c Project: npe9/harvey
char *
rwstat(Fid *f)
{
	if(!f->busy)
		return vtstrdup(Enotexist);
	return vtstrdup(Erdonly);
}
Example #5
0
File: vacfs.c Project: npe9/harvey
char*
rattach(Fid *f)
{
	/* no authentication for the momment */
	VacFile *file;
	char err[80];

	file = vacfsgetroot(fs);
	if(file == nil) {
		rerrstr(err, sizeof err);
		return vtstrdup(err);
	}

	f->busy = 1;
	f->file = file;
	f->qid.path = vacfilegetid(f->file);
	f->qid.vers = 0;
	f->qid.type = QTDIR;
	thdr.qid = f->qid;
	if(rhdr.uname[0])
		f->user = vtstrdup(rhdr.uname);
	else
		f->user = "******";
	return 0;
}
Example #6
0
File: vacfs.c Project: npe9/harvey
char*
rcreate(Fid* fid)
{
	VacFile *vf;
	uint32_t mode;

	if(fid->open)
		return vtstrdup(Eisopen);
	if(!fid->busy)
		return vtstrdup(Enotexist);
	if(fs->mode & ModeSnapshot)
		return vtstrdup(Erdonly);
	vf = fid->file;
	if(!vacfileisdir(vf))
		return vtstrdup(Enotdir);
	if(!permf(vf, fid->user, Pwrite))
		return vtstrdup(Eperm);

	mode = rhdr.perm & 0777;

	if(rhdr.perm & DMDIR){
		if((rhdr.mode & OTRUNC) || (rhdr.perm & DMAPPEND))
			return vtstrdup(Emode);
		switch(rhdr.mode & OPERM){
		default:
			return vtstrdup(Emode);
		case OEXEC:
		case OREAD:
			break;
		case OWRITE:
		case ORDWR:
			return vtstrdup(Eperm);
		}
		mode |= ModeDir;
	}
	vf = vacfilecreate(vf, rhdr.name, mode);
	if(vf == nil) {
		char err[80];
		rerrstr(err, sizeof err);

		return vtstrdup(err);
	}

	vacfiledecref(fid->file);

	fid->file = vf;
	fid->qid.type = QTFILE;
	if(vacfileisdir(vf))
		fid->qid.type = QTDIR;
	fid->qid.vers = vacfilegetmcount(vf);
	fid->qid.path = vacfilegetid(vf);

	thdr.qid = fid->qid;
	thdr.iounit = messagesize - IOHDRSZ;

	return 0;
}
Example #7
0
int
authRead(Fid* afid, void* data, int count)
{
	AuthInfo *ai;
	AuthRpc *rpc;

	if((rpc = afid->rpc) == nil){
		werrstr("not an auth fid");
		return -1;
	}

	switch(auth_rpc(rpc, "read", nil, 0)){
	default:
		werrstr("fossil authRead: auth protocol not finished");
		return -1;
	case ARdone:
		if((ai = auth_getinfo(rpc)) == nil){
			werrstr("%r");
			break;
		}
		if(ai->cuid == nil || *ai->cuid == '\0'){
			werrstr("auth with no cuid");
			auth_freeAI(ai);
			break;
		}
		assert(afid->cuname == nil);
		afid->cuname = vtstrdup(ai->cuid);
		auth_freeAI(ai);
		if(Dflag)
			fprint(2, "authRead cuname %s\n", afid->cuname);
		assert(afid->uid == nil);
		if((afid->uid = uidByUname(afid->cuname)) == nil){
			werrstr("unknown user %#q", afid->cuname);
			break;
		}
		return 0;
	case ARok:
		if(count < rpc->narg){
			werrstr("not enough data in auth read");
			break;
		}
		memmove(data, rpc->arg, rpc->narg);
		return rpc->narg;
	case ARphase:
		werrstr("%r");
		break;
	}
	return -1;
}
Example #8
0
/*
 * convert File* to full path name in malloced string.
 * this hasn't been as useful as we hoped it would be.
 */
char *
fileName(File *f)
{
	char *name, *pname;
	File *p;
	static char root[] = "/";

	if (f == nil)
		return vtstrdup("/**GOK**");

	p = fileGetParent(f);
	if (p == f)
		name = vtstrdup(root);
	else {
		pname = fileName(p);
		if (strcmp(pname, root) == 0)
			name = smprint("/%s", f->dir.elem);
		else
			name = smprint("%s/%s", pname, f->dir.elem);
		free(pname);
	}
	fileDecRef(p);
	return name;
}
Example #9
0
File: vacfs.c Project: npe9/harvey
char *
rstat(Fid *f)
{
	VacDir dir;
	static uint8_t statbuf[1024];
	VacFile *parent;
	
	if(!f->busy)
		return vtstrdup(Enotexist);
	parent = vacfilegetparent(f->file);
	vacfilegetdir(f->file, &dir);
	thdr.stat = statbuf;
	thdr.nstat = vacstat(parent, &dir, thdr.stat, sizeof statbuf);
	vdcleanup(&dir);
	vacfiledecref(parent);
	return 0;
}
Example #10
0
File: vacfs.c Project: npe9/harvey
char *
ropen(Fid *f)
{
	int mode, trunc;

	if(f->open)
		return vtstrdup(Eisopen);
	if(!f->busy)
		return vtstrdup(Enotexist);

	mode = rhdr.mode;
	thdr.iounit = messagesize - IOHDRSZ;
	if(f->qid.type & QTDIR){
		if(mode != OREAD)
			return vtstrdup(Eperm);
		if(!perm(f, Pread))
			return vtstrdup(Eperm);
		thdr.qid = f->qid;
		f->vde = nil;
		f->open = 1;
		return 0;
	}
	if(mode & ORCLOSE)
		return vtstrdup(Erdonly);
	trunc = mode & OTRUNC;
	mode &= OPERM;
	if(mode==OWRITE || mode==ORDWR || trunc)
		if(!perm(f, Pwrite))
			return vtstrdup(Eperm);
	if(mode==OREAD || mode==ORDWR)
		if(!perm(f, Pread))
			return vtstrdup(Eperm);
	if(mode==OEXEC)
		if(!perm(f, Pexec))
			return vtstrdup(Eperm);
	thdr.qid = f->qid;
	thdr.iounit = messagesize - IOHDRSZ;
	f->open = 1;
	return 0;
}
Example #11
0
/*
 * f->source and f->msource must NOT be locked.
 * see fileMetaLock.
 */
static void
fileWAccess(File* f, char *mid)
{
	if(f->mode == OReadOnly)
		return;

	fileMetaLock(f);
	f->dir.atime = f->dir.mtime = time(0L);
	if(strcmp(f->dir.mid, mid) != 0){
		vtfree(f->dir.mid);
		f->dir.mid = vtstrdup(mid);
	}
	f->dir.mcount++;
	f->dirty = 1;
	fileMetaUnlock(f);

/*RSC: let's try this */
/*presotto - lets not
	if(f->up)
		fileWAccess(f->up, mid);
*/
}
Example #12
0
File: vacfs.c Project: npe9/harvey
char*
rauth(Fid *f)
{
	USED(f);
	return vtstrdup("vacfs: authentication not required");
}
Example #13
0
File *
fileCreate(File *f, char *elem, ulong mode, char *uid)
{
	File *ff;
	DirEntry *dir;
	Source *pr, *r, *mr;
	int isdir;

	if(!fileLock(f))
		return nil;

	r = nil;
	mr = nil;
	for(ff = f->down; ff; ff=ff->next){
		if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
			ff = nil;
			werrstr(EExists);
			goto Err1;
		}
	}

	ff = dirLookup(f, elem);
	if(ff != nil){
		werrstr(EExists);
		goto Err1;
	}

	pr = f->source;
	if(pr->mode != OReadWrite){
		werrstr(EReadOnly);
		goto Err1;
	}

	if(!sourceLock2(f->source, f->msource, -1))
		goto Err1;

	ff = fileAlloc(f->fs);
	isdir = mode & ModeDir;

	r = sourceCreate(pr, pr->dsize, isdir, 0);
	if(r == nil)
		goto Err;
	if(isdir){
		mr = sourceCreate(pr, pr->dsize, 0, r->offset);
		if(mr == nil)
			goto Err;
	}

	dir = &ff->dir;
	dir->elem = vtstrdup(elem);
	dir->entry = r->offset;
	dir->gen = r->gen;
	if(isdir){
		dir->mentry = mr->offset;
		dir->mgen = mr->gen;
	}
	dir->size = 0;
	if(!fsNextQid(f->fs, &dir->qid))
		goto Err;
	dir->uid = vtstrdup(uid);
	dir->gid = vtstrdup(f->dir.gid);
	dir->mid = vtstrdup(uid);
	dir->mtime = time(0L);
	dir->mcount = 0;
	dir->ctime = dir->mtime;
	dir->atime = dir->mtime;
	dir->mode = mode;

	ff->boff = fileMetaAlloc(f, dir, 0);
	if(ff->boff == NilBlock)
		goto Err;

	sourceUnlock(f->source);
	sourceUnlock(f->msource);

	ff->source = r;
	r->file = ff;			/* point back */
	ff->msource = mr;

	if(mode&ModeTemporary){
		if(!sourceLock2(r, mr, -1))
			goto Err1;
		fileSetTmp(ff, 1);
		sourceUnlock(r);
		if(mr)
			sourceUnlock(mr);
	}

	/* committed */

	/* link in and up parent ref count */
	ff->next = f->down;
	f->down = ff;
	ff->up = f;
	fileIncRef(f);

	fileWAccess(f, uid);

	fileUnlock(f);
	return ff;

Err:
	sourceUnlock(f->source);
	sourceUnlock(f->msource);
Err1:
	if(r){
		sourceLock(r, -1);
		sourceRemove(r);
	}
	if(mr){
		sourceLock(mr, -1);
		sourceRemove(mr);
	}
	if(ff)
		fileDecRef(ff);
	fileUnlock(f);
	return 0;
}
Example #14
0
File: vacfs.c Project: npe9/harvey
char*
rwrite(Fid *f)
{
	USED(f);
	return vtstrdup(Erdonly);
}
Example #15
0
int
fileSetDir(File *f, DirEntry *dir, char *uid)
{
	File *ff;
	char *oelem;
	u32int mask;
	u64int size;

	/* can not set permissions for the root */
	if(fileIsRoot(f)){
		werrstr(ERoot);
		return 0;
	}

	if(!fileLock(f))
		return 0;

	if(f->source->mode != OReadWrite){
		werrstr(EReadOnly);
		fileUnlock(f);
		return 0;
	}

	fileMetaLock(f);

	/* check new name does not already exist */
	if(strcmp(f->dir.elem, dir->elem) != 0){
		for(ff = f->up->down; ff; ff=ff->next){
			if(strcmp(dir->elem, ff->dir.elem) == 0 && !ff->removed){
				werrstr(EExists);
				goto Err;
			}
		}

		ff = dirLookup(f->up, dir->elem);
		if(ff != nil){
			fileDecRef(ff);
			werrstr(EExists);
			goto Err;
		}
	}

	if(!sourceLock2(f->source, f->msource, -1))
		goto Err;
	if(!fileIsDir(f)){
		size = sourceGetSize(f->source);
		if(size != dir->size){
			if(!sourceSetSize(f->source, dir->size)){
				sourceUnlock(f->source);
				if(f->msource)
					sourceUnlock(f->msource);
				goto Err;
			}
			/* commited to changing it now */
		}
	}
	/* commited to changing it now */
	if((f->dir.mode&ModeTemporary) != (dir->mode&ModeTemporary))
		fileSetTmp(f, dir->mode&ModeTemporary);
	sourceUnlock(f->source);
	if(f->msource)
		sourceUnlock(f->msource);

	oelem = nil;
	if(strcmp(f->dir.elem, dir->elem) != 0){
		oelem = f->dir.elem;
		f->dir.elem = vtstrdup(dir->elem);
	}

	if(strcmp(f->dir.uid, dir->uid) != 0){
		vtfree(f->dir.uid);
		f->dir.uid = vtstrdup(dir->uid);
	}

	if(strcmp(f->dir.gid, dir->gid) != 0){
		vtfree(f->dir.gid);
		f->dir.gid = vtstrdup(dir->gid);
	}

	f->dir.mtime = dir->mtime;
	f->dir.atime = dir->atime;

//fprint(2, "mode %x %x ", f->dir.mode, dir->mode);
	mask = ~(ModeDir|ModeSnapshot);
	f->dir.mode &= ~mask;
	f->dir.mode |= mask & dir->mode;
	f->dirty = 1;
//fprint(2, "->%x\n", f->dir.mode);

	fileMetaFlush2(f, oelem);
	vtfree(oelem);

	fileMetaUnlock(f);
	fileUnlock(f);

	fileWAccess(f->up, uid);

	return 1;
Err:
	fileMetaUnlock(f);
	fileUnlock(f);
	return 0;
}
Example #16
0
int
authCheck(Fcall* t, Fid* fid, Fsys* fsys)
{
	Con *con;
	Fid *afid;
	uchar buf[1];

	/*
	 * Can't lookup with FidWlock here as there may be
	 * protocol to do. Use a separate lock to protect altering
	 * the auth information inside afid.
	 */
	con = fid->con;
	if(t->afid == NOFID){
		/*
		 * If no authentication is asked for, allow
		 * "none" provided the connection has already
		 * been authenticatated.
		 *
		 * The console is allowed to attach without
		 * authentication.
		 */
		rlock(&con->alock);
		if(con->isconsole){
			/* anything goes */
		}else if((con->flags&ConNoneAllow) || con->aok){
			static int noneprint;

			if(noneprint++ < 10)
				consPrint("attach %s as %s: allowing as none\n",
					fsysGetName(fsys), fid->uname);
			vtfree(fid->uname);
			fid->uname = vtstrdup(unamenone);
		}else{
			runlock(&con->alock);
			consPrint("attach %s as %s: connection not authenticated, not console\n",
				fsysGetName(fsys), fid->uname);
			werrstr("cannot attach as none before authentication");
			return 0;
		}
		runlock(&con->alock);

		if((fid->uid = uidByUname(fid->uname)) == nil){
			consPrint("attach %s as %s: unknown uname\n",
				fsysGetName(fsys), fid->uname);
			werrstr("unknown user");
			return 0;
		}
		return 1;
	}

	if((afid = fidGet(con, t->afid, 0)) == nil){
		consPrint("attach %s as %s: bad afid\n",
			fsysGetName(fsys), fid->uname);
		werrstr("bad authentication fid");
		return 0;
	}

	/*
	 * Check valid afid;
	 * check uname and aname match.
	 */
	if(!(afid->qid.type & QTAUTH)){
		consPrint("attach %s as %s: afid not an auth file\n",
			fsysGetName(fsys), fid->uname);
		fidPut(afid);
		werrstr("bad authentication fid");
		return 0;
	}
	if(strcmp(afid->uname, fid->uname) != 0 || afid->fsys != fsys){
		consPrint("attach %s as %s: afid is for %s as %s\n",
			fsysGetName(fsys), fid->uname,
			fsysGetName(afid->fsys), afid->uname);
		fidPut(afid);
		werrstr("attach/auth mismatch");
		return 0;
	}

	qlock(&afid->alock);
	if(afid->cuname == nil){
		if(authRead(afid, buf, 0) != 0 || afid->cuname == nil){
			qunlock(&afid->alock);
			consPrint("attach %s as %s: %r\n",
				fsysGetName(fsys), fid->uname);
			fidPut(afid);
			werrstr("fossil authCheck: auth protocol not finished");
			return 0;
		}
	}
	qunlock(&afid->alock);

	assert(fid->uid == nil);
	if((fid->uid = uidByUname(afid->cuname)) == nil){
		consPrint("attach %s as %s: unknown cuname %s\n",
			fsysGetName(fsys), fid->uname, afid->cuname);
		fidPut(afid);
		werrstr("unknown user");
		return 0;
	}

	vtfree(fid->uname);
	fid->uname = vtstrdup(afid->cuname);
	fidPut(afid);

	/*
	 * Allow "none" once the connection has been authenticated.
	 */
	wlock(&con->alock);
	con->aok = 1;
	wunlock(&con->alock);

	return 1;
}
Example #17
0
File: vacfs.c Project: npe9/harvey
char*
rwalk(Fid *f)
{
	VacFile *file, *nfile;
	Fid *nf;
	int nqid, nwname;
	Qid qid;
	char *err = nil;

	if(f->busy == 0)
		return Enotexist;
	nf = nil;
	if(rhdr.fid != rhdr.newfid){
		if(f->open)
			return vtstrdup(Eisopen);
		if(f->busy == 0)
			return vtstrdup(Enotexist);
		nf = newfid(rhdr.newfid);
		if(nf->busy)
			return vtstrdup(Eisopen);
		nf->busy = 1;
		nf->open = 0;
		nf->qid = f->qid;
		nf->file = vacfileincref(f->file);
		nf->user = vtstrdup(f->user);
		f = nf;
	}

	nwname = rhdr.nwname;

	/* easy case */
	if(nwname == 0) {
		thdr.nwqid = 0;
		return 0;
	}

	file = f->file;
	vacfileincref(file);
	qid = f->qid;

	for(nqid = 0; nqid < nwname; nqid++){
		if((qid.type & QTDIR) == 0){
			err = Enotdir;
			break;
		}
		if(!permf(file, f->user, Pexec)) {
			err = Eperm;
			break;
		}
		nfile = vacfilewalk(file, rhdr.wname[nqid]);
		if(nfile == nil)
			break;
		vacfiledecref(file);
		file = nfile;
		qid.type = QTFILE;
		if(vacfileisdir(file))
			qid.type = QTDIR;
		qid.vers = vacfilegetmcount(file);
		qid.path = vacfilegetid(file);
		thdr.wqid[nqid] = qid;
	}

	thdr.nwqid = nqid;

	if(nqid == nwname){
		/* success */
		f->qid = thdr.wqid[nqid-1];
		vacfiledecref(f->file);
		f->file = file;
		return 0;
	}

	vacfiledecref(file);
	if(nf != nil)
		rclunk(nf);

	/* only error on the first element */
	if(nqid == 0)
		return vtstrdup(err);

	return 0;
}
Example #18
0
int
vtversion(VtConn *z)
{
	char buf[VtMaxStringSize], *p, *ep, *prefix, *pp;
	int i;

	qlock(&z->lk);
	if(z->state != VtStateAlloc){
		werrstr("bad session state");
		qunlock(&z->lk);
		return -1;
	}

	qlock(&z->inlk);
	qlock(&z->outlk);

	p = buf;
	ep = buf + sizeof buf;
	prefix = "venti-";
	p = seprint(p, ep, "%s", prefix);
	p += strlen(p);
	for(i=0; okvers[i]; i++)
		p = seprint(p, ep, "%s%s", i ? ":" : "", okvers[i]);
	p = seprint(p, ep, "-libventi\n");
	assert(p-buf < sizeof buf);

	if(write(z->outfd, buf, p-buf) != p-buf)
		goto Err;
	vtdebug(z, "version string out: %s", buf);

	if(vtreadversion(z, prefix, buf, sizeof buf) < 0)
		goto Err;
	vtdebug(z, "version string in: %s", buf);

	p = buf+strlen(prefix);
	for(; *p; p=pp){
		if(*p == ':' || *p == '-')
			p++;
		pp = strpbrk(p, ":-");
		if(pp == nil)
			pp = p+strlen(p);
		for(i=0; okvers[i]; i++)
			if(strlen(okvers[i]) == pp-p && memcmp(okvers[i], p, pp-p) == 0){
				*pp = 0;
				z->version = vtstrdup(p);
				goto Okay;
			}
	}
	werrstr("unable to negotiate version");
	goto Err;

Okay:
	z->state = VtStateConnected;
	qunlock(&z->inlk);
	qunlock(&z->outlk);
	qunlock(&z->lk);
	return 0;

Err:
	werrstr("vtversion: %r");
	if(z->infd >= 0)
		close(z->infd);
	if(z->outfd >= 0 && z->outfd != z->infd)
		close(z->outfd);
	z->infd = -1;
	z->outfd = -1;
	z->state = VtStateClosed;
	qunlock(&z->inlk);
	qunlock(&z->outlk);
	qunlock(&z->lk);
	return -1;
}
Example #19
0
int
rdconf(char *file, Conf *conf)
{
	char *s, *line, *flds[10];
	int i, ok;
	IFile f;
	
	if(readifile(&f, file) < 0)
		return -1;
	memset(conf, 0, sizeof *conf);
	ok = -1;
	line = nil;
	for(;;){
		s = ifileline(&f);
		if(s == nil){
			ok = 0;
			break;
		}
		line = estrdup(s);
		i = getfields(s, flds, nelem(flds), 1, " \t\r");
		if(i <= 0 || strcmp(flds[0], "mgr") != 0) {
			/* do nothing */
		}else if(i == 4 && strcmp(flds[1], "mirror") == 0) {
			if(conf->nmirror%64 == 0)
				conf->mirror = vtrealloc(conf->mirror, (conf->nmirror+64)*sizeof(conf->mirror[0]));
			conf->mirror[conf->nmirror].src = vtstrdup(flds[2]);
			conf->mirror[conf->nmirror].dst = vtstrdup(flds[3]);
			conf->nmirror++;
		}else if(i == 3 && strcmp(flds[1], "mirrorfreq") == 0) {
			conf->mirrorfreq = atoi(flds[2]);
		}else if(i == 3 && strcmp(flds[1], "verify") == 0) {
			if(conf->nverify%64 == 0)
				conf->verify = vtrealloc(conf->verify, (conf->nverify+64)*sizeof(conf->verify[0]));
			conf->verify[conf->nverify++] = vtstrdup(flds[2]);	
		}else if(i == 3 && strcmp(flds[1], "verifyfreq") == 0) {
			conf->verifyfreq = atoi(flds[2]);
		}else if(i == 3 && strcmp(flds[1], "httpaddr") == 0){
			if(conf->httpaddr){
				seterr(EAdmin, "duplicate httpaddr lines in configuration file %s", file);
				break;
			}
			conf->httpaddr = estrdup(flds[2]);
		}else if(i == 3 && strcmp(flds[1], "webroot") == 0){
			if(conf->webroot){
				seterr(EAdmin, "duplicate webroot lines in configuration file %s", file);
				break;
			}
			conf->webroot = estrdup(flds[2]);
		}else if(i == 3 && strcmp(flds[1], "smtp") == 0) {
			if(conf->smtp){
				seterr(EAdmin, "duplicate smtp lines in configuration file %s", file);
				break;
			}
			conf->smtp = estrdup(flds[2]);
		}else if(i == 3 && strcmp(flds[1], "mailfrom") == 0) {
			if(conf->mailfrom){
				seterr(EAdmin, "duplicate mailfrom lines in configuration file %s", file);
				break;
			}
			conf->mailfrom = estrdup(flds[2]);
		}else if(i == 3 && strcmp(flds[1], "mailto") == 0) {
			if(conf->mailto){
				seterr(EAdmin, "duplicate mailto lines in configuration file %s", file);
				break;
			}
			conf->mailto = estrdup(flds[2]);
		}else{
			seterr(EAdmin, "illegal line '%s' in configuration file %s", line, file);
			break;
		}
		free(line);
		line = nil;
	}
	free(line);
	freeifile(&f);
	return ok;
}