Browsing pmikkelsen/neinchat at commit HEAD: /main.c

view raw
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>

enum {
	Qroot,
		Qnickname,
		Qspeak,
		Qcurrentchan,
		Qchannels,
	Qmax,
};

typedef struct NeinFile NeinFile;
typedef struct NeinAux NeinAux;

struct NeinFile {
	char	*name;
	Qid	qid;
	ulong	mode;
};

struct NeinAux {
	char	*nickname;
	int	currentChan;
};

void	fsattach(Req*);
void	fsstat(Req*);
void	fsread(Req*);
void	fswrite(Req*);
char	*fsclone(Fid*, Fid*);
char	*fswalk1(Fid*, char*, Qid*);
int	rootgen(int, Dir*, void*);
int	channelsgen(int, Dir*, void*);
NeinFile	*findfile(uvlong);
int	findchannel(char*);
void	fillstat(Dir*, NeinFile);
void	readchanlog(Req*, uvlong);

NeinFile qroot[] = {
	"nickname", {Qnickname, 0, QTFILE}, 0666,
	"speak", {Qspeak, 0, QTFILE}, 0222,
	"currentchan", {Qcurrentchan, 0, QTFILE}, 0666,
	"channels", {Qchannels, 0, QTDIR}, 0555 | DMDIR,
};

int nchannels;
NeinFile *channels;

NeinFile root = {"/", {Qroot, 0, QTDIR}, 555 | DMDIR};

void
main(void)
{
	Srv fs = {
		.attach = fsattach,
		.walk1 = fswalk1,
		.clone = fsclone,
		.stat = fsstat,
		.read = fsread,
		.write = fswrite,
	};
	char *postname = "neinchat";
	char *addr = "tcp!*!12345";
	nchannels = 0;
	channels = nil;
	print("Starting neinchat server on %s and posting to /srv/%s\n", addr, postname);
	listensrv(&fs, addr);
	postmountsrv(&fs, "neinchat", nil, MREPL|MCREATE);
	exits(nil);
}

void
fsattach(Req *r)
{
	static int nicknamecount;
	NeinAux *aux;

	r->fid->qid = root.qid;
	r->ofcall.qid = r->fid->qid;

	aux = emalloc9p(sizeof(NeinAux));
	aux->nickname = smprint("RandomUser%d", nicknamecount++);
	aux->currentChan = -1;
	r->fid->aux = aux;
	respond(r, nil);
}

void
fsstat(Req *r)
{
	NeinFile *f = findfile(r->fid->qid.path);
	if(f == nil){
		respond(r, "not found");
		return;
	}
	fillstat(&r->d, *f);
	respond(r, nil);
}

void
fsread(Req *r)
{
	char *str;
	NeinAux *aux = r->fid->aux;
	uvlong path = r->fid->qid.path;
	
	if(path >= Qmax && path < (Qmax + nchannels)){
		readchanlog(r, path);
		return;
	}

	switch(path){
	case Qroot:
		dirread9p(r, rootgen, nil);
		respond(r, nil);
		break;
	case Qchannels:
		dirread9p(r, channelsgen, nil);
		respond(r, nil);
		break;
	case Qnickname:
		str = smprint("%s\n", aux->nickname);
		readstr(r, str);
		free(str);
		respond(r, nil);
		break;
	case Qcurrentchan:
		if(aux->currentChan == -1){
			respond(r, "no channel selected");
			return;
		}else{
			str = smprint("%s\n", channels[aux->currentChan].name);
			readstr(r, str);
			free(str);
			respond(r, nil);
		}
		break;
	default:
		respond(r, "wut no");
	}
}

void
fswrite(Req *r)
{
	NeinAux *aux = r->fid->aux;
	char *buf;

	if(r->ifcall.offset != 0){
		respond(r, "Can't write at offset");
		return;
	}

	switch(r->fid->qid.path){
	case Qnickname:
		if(r->ifcall.count > 64){
			respond(r, "nickname too long");
			return;
		}
		r->ofcall.count = r->ifcall.count;
		buf = emalloc9p(r->ifcall.count + 1);
		memcpy(buf, r->ifcall.data, r->ifcall.count);
		buf[r->ifcall.count] = 0;
		free(aux->nickname);
		aux->nickname = buf;
		respond(r, nil);
		break;
	case Qcurrentchan:
		if(r->ifcall.count > 64){
			respond(r, "Channel name too long");
			return;
		}
		r->ofcall.count = r->ifcall.count;
		buf = emalloc9p(r->ifcall.count + 1);
		memcpy(buf, r->ifcall.data, r->ifcall.count);
		buf[r->ifcall.count] = 0;
		aux->currentChan = findchannel(buf);
		respond(r, nil);
		break;
	case Qspeak:
		respond(r, "Speaking not implemented yet ☺");
		break;
	default:
		respond(r, "write prohibited");
	}
}

char *
fsclone(Fid *old, Fid *new)
{
	new->aux = old->aux;
	return nil;
}

char *
fswalk1(Fid *fid, char *name, Qid *qid)
{
	int i;

	if(strcmp(name, "..") == 0){
		*qid = root.qid;
		fid->qid = *qid;
		return nil;
	}

	switch(fid->qid.path){
	case Qroot:
		for(i = 0; i < nelem(qroot); i++){
			if(strcmp(qroot[i].name, name) == 0){
				*qid = qroot[i].qid;
				fid->qid = *qid;
				return nil;
			}
		}
		break;
	case Qchannels:
		for(i = 0; i < nchannels; i++){
			if(strcmp(channels[i].name, name) == 0){
				*qid = channels[i].qid;
				fid->qid = *qid;
				return nil;
			}
		}
		break;
	}
	return "not found";
}

int
rootgen(int n, Dir *d, void *)
{
	if(n >= nelem(qroot))
		return -1;
	NeinFile f = qroot[n];
	fillstat(d, f);
	return 0;
}

int
channelsgen(int n, Dir *d, void *)
{
	if(n >= nchannels)
		return -1;

	NeinFile f = channels[n];
	fillstat(d, f);
	return 0;
}

NeinFile *
findfile(uvlong path)
{
	int i;
	if(path == Qroot)
		return &root;
	
	for(i = 0; i < nelem(qroot); i++){
		if(qroot[i].qid.path == path)
			return &qroot[i];
	}
	return nil;
}

int
findchannel(char *name)
{
	int i;
	int path;
	for(i = 0; i < nchannels; i++){
		if(strcmp(channels[i].name, name) == 0)
			return i;
	}

	nchannels++;
	path = Qmax + i;
	channels = erealloc9p(channels, sizeof(NeinFile) * nchannels);
	channels[i] = (NeinFile){name, (Qid){path, 0, QTFILE}, 0444};
	return i;
}

void
fillstat(Dir *d, NeinFile f)
{
	char *username = getuser();
	d->qid = f.qid;
	d->mode = f.mode;
	d->length = 0;
	d->name = estrdup9p(f.name);
	d->uid = estrdup9p(username);
	d->gid = estrdup9p(username);
	d->muid = estrdup9p(username);
	d->atime = time(0);
	d->mtime = time(0);
}

void
readchanlog(Req *r, uvlong path)
{
	USED(path);
	readstr(r, "Nothing to see here yet\n");
	respond(r, nil);
}