ref: effe53dedb08022fc45652e44193ba43fb5fbef3
dir: /vcardfs.c/
#include <u.h> #include <libc.h> #include <fcall.h> #include <thread.h> #include <9p.h> #include "libvcard/vcard.h" /* rough structure /john-doe/ export (export single contact as vcard file) /fn/ /data (line value) /group (line group) /params/ p1 (param value) ... /tel/ /data (line value) /group (line group) /params/ ... /ctl - "write" (save file) /import - write new vcf file to import / export - read all vcards as vcf */ void usage(void) { fprint(2, "usage: %s [-s srv] [-m mtpt] [file]\n", argv0); exits("usage"); } static void* emalloc(long n) { void *p; p = mallocz(n, 1); if (!p) sysfatal("emalloc: %r"); return p; } static char* estrdup(char *s) { void *t; t = strdup(s); if (!t) sysfatal("estrdup: %r"); return t; } enum { Qroot, Qctl, Qimport, Qgexport, Qcard, Qexport, Qline, Qdata, Qgroup, Qparams, Qparamdata, }; typedef struct Vfile Vfile; struct Vfile { int level; Vcard *card; Vline *line; Vparam *param; Vfile *cardfile; File *file; char *serialized; }; char *user = nil; char *mtpt = "/mnt/vcard"; char *service = nil; char *file = nil; Vcard *cards = nil; static char* getcardname(Vcard*); static Vfile* emkvfile(int level, Vcard *c, Vline *l, Vparam *p, Vfile *cfile) { Vfile *f; f = mallocz(sizeof(Vfile), 1); if (!f) sysfatal("%r"); f->level = level; f->card = c; f->line = l; f->param = p; f->cardfile = cfile; return f; } static void readserialized(Req *r, Vfile *f) { int n; char *s; Vcard *tmp; if (f->level == Qgexport) { if (f->cardfile->serialized) free(f->cardfile->serialized); f->cardfile->serialized = vcmserialize(cards); if (!f->cardfile->serialized) { responderror(r); return; } goto Readin; } if (f->cardfile->serialized) goto Readin; tmp = f->card->next; f->card->next = nil; f->cardfile->serialized = vcmserialize(f->card); f->card->next = tmp; if (!f->cardfile->serialized) { responderror(r); return; } Readin: s = f->cardfile->serialized + r->ifcall.offset; n = strlen(s); n = n < r->ifcall.count ? n : r->ifcall.count; readbuf(r, s, n); respond(r, nil); } static void fsread(Req *r) { Vfile *f; f = r->fid->file->aux; switch (f->level) { case Qctl: respond(r, nil); return; case Qimport: respond(r, nil); return; case Qgexport: readserialized(r, f); return; case Qexport: readserialized(r, f); return; case Qdata: readstr(r, f->line->value); respond(r, nil); return; case Qgroup: if (!f->line->group) { respond(r, "file not found"); return; } readstr(r, f->line->group); respond(r, nil); return; case Qparamdata: readstr(r, f->param->value); respond(r, nil); return; } respond(r, "not implemented"); } static void condrenamecard(Vfile *f) { char *s; if (strcmp(f->line->name, "n") != 0 && strcmp(f->line->name, "fn") != 0) return; if (!f->cardfile->file) return; s = getcardname(f->card); if (!s) return; if (f->cardfile->file->name) free(f->cardfile->file->name); f->cardfile->file->name = s; } static char* getsanitized(char *s) { return s; } static void writedata(Req *r, Vfile *f) { char *s; switch (f->level) { case Qparamdata: if (!f->param) goto Err; s = mallocz(r->ifcall.count + 1, 1); if (!s) { responderror(r); return; } memcpy(s, r->ifcall.data, r->ifcall.count); /* chop off last \n, if exists */ if (s[r->ifcall.count - 1] == '\n') s[r->ifcall.count - 1] = 0; if (f->param->value) free(f->param->value); f->param->value = s; break; case Qdata: if (!f->line) goto Err; s = mallocz(r->ifcall.count + 1, 1); if (!s) { responderror(r); return; } memcpy(s, r->ifcall.data, r->ifcall.count); if (s[r->ifcall.count - 1] == '\n') s[r->ifcall.count - 1] = 0; if (f->line->value) free(f->line->value); f->line->value = getsanitized(s); condrenamecard(f); break; case Qgroup: if (!f->line) goto Err; s = mallocz(r->ifcall.count + 1, 1); if (!s) { responderror(r); return; } memcpy(s, r->ifcall.data, r->ifcall.count); if (s[r->ifcall.count - 1] == '\n') s[r->ifcall.count - 1] = 0; if (f->line->group) free(f->line->group); f->line->group = s; break; default: respond(r, "file not found"); return; } if (f->cardfile->serialized) free(f->cardfile->serialized); f->cardfile->serialized = nil; respond(r, nil); return; Err: respond(r, "error"); } static void fswrite(Req *r) { Vfile *f; f = r->fid->file->aux; switch (f->level) { case Qctl: break; case Qexport: respond(r, "not a function"); return; case Qparamdata: case Qdata: case Qgroup: writedata(r, f); return; } respond(r, "not implemented"); } static Vparam* emkvparam(Vline *line, char *name) { Vparam *vp, *p; vp = mallocz(sizeof(Vparam), 1); if (!vp) sysfatal("%r"); vp->name = estrdup(name); vp->value = estrdup(""); if (!line->params) { line->params = vp; return vp; } for (p = line->params; p->next; p = p->next) continue; p->next = vp; return vp; } static void fscreate(Req *r) { File *f, *nf; Vfile *vf; Vparam *vp; nf = nil; USED(nf); f = r->fid->file; vf = f->aux; if (r->ifcall.perm&DMDIR) goto Nil; switch (vf->level) { case Qline: if (strcmp(r->ifcall.name, "group") != 0) goto Nil; nf = createfile(f, r->ifcall.name, user, 0666, nil); if (!nf) { responderror(r); return; } nf->aux = emkvfile(Qgroup, vf->card, vf->line, nil, vf->cardfile); vf->line->group = estrdup(""); break; case Qparams: nf = createfile(f, r->ifcall.name, user, 0666, nil); if (!nf) { responderror(r); return; } vp = emkvparam(vf->line, r->ifcall.name); nf->aux = emkvfile(Qparamdata, vf->card, vf->line, vp, vf->cardfile); break; default: goto Nil; } if (nf) { r->fid->file = nf; r->ofcall.qid = nf->qid; } if (vf->cardfile->serialized) free(vf->cardfile->serialized); vf->cardfile->serialized = nil; respond(r, nil); return; Nil: respond(r, "create prohibited"); } Srv fs = { .read = fsread, .write = fswrite, .create = fscreate, }; /* TODO: LOOKAT: /sys/src/cmd/webcookies.c:/createfile /sys/src/cmd/aux/gps/gpsfs.c:/createfile */ static char* safename(char *n) { char *s; s = estrdup(n); n = s; while (*n) { switch (*n) { case ' ': *n = '_'; break; case '\t': *n = '_'; break; } n++; } return s; } static char* getcardname(Vcard *c) { Vline *l; Vline *fn = nil, *n = nil; for (l = c->content; l; l = l->next) { if (cistrcmp(l->name, "fn") == 0) fn = l; else if (cistrcmp(l->name, "n") == 0) n = l; if (fn && n) break; } if (fn) return safename(fn->value); if (n) return safename(n->value); return nil; } static void initcardfiles(Vcard *chain) { File *fc, *fl, *f; Vcard *c; Vline *l; Vparam *p; Vfile *vf, *cf; char *s; for (c = chain; c; c = c->next) { s = getcardname(c); if (!s) continue; cf = emkvfile(Qcard, c, nil, nil, nil); fc = createfile(fs.tree->root, s, user, DMDIR|0777, cf); cf->file = fc; free(s); if (!fc) sysfatal("%r"); vf = emkvfile(Qexport, c, nil, nil, cf); createfile(fc, "export", user, 0444, vf); for (l = c->content; l; l = l->next) { vf = emkvfile(Qline, c, l, nil, cf); fl = createfile(fc, l->name, user, DMDIR|0777, vf); vf = emkvfile(Qdata, c, l, nil, cf); createfile(fl, "data", user, 0666, vf); if (l->group) { vf = emkvfile(Qgroup, c, l, nil, cf); createfile(fl, "group", user, 0666, vf); } vf = emkvfile(Qparams, c, l, nil, cf); f = createfile(fl, "params", user, DMDIR|0777, vf); for (p = l->params; p; p = p->next) { vf = emkvfile(Qparamdata, c, l, p, cf); createfile(f, p->name, user, 0666, vf); } } } } void main(int argc, char **argv) { Vfile *f; ARGBEGIN{ case 'D': chatty9p++; break; case 'm': mtpt = EARGF(usage()); break; case 's': service = EARGF(usage()); break; default: usage(); break; }ARGEND; rfork(RFNOTEG); user = getuser(); if (argc == 1) file = argv[0]; else { file = smprint("/usr/%s/lib/vcarddb.vcf", user); if (!file) sysfatal("%r"); } cards = vcparsefile(file); if (!cards) sysfatal("%r"); fs.tree = alloctree("vcf", "vcf", DMDIR|0555, nil); createfile(fs.tree->root, "ctl", user, 0666, emkvfile(Qctl, nil, nil, nil, nil)); createfile(fs.tree->root, "import", user, 0666, emkvfile(Qimport, nil, nil, nil, nil)); f = emkvfile(Qgexport, nil, nil, nil, nil); f->cardfile = f; createfile(fs.tree->root, "export", user, 0444, f); initcardfiles(cards); postmountsrv(&fs, service, mtpt, MREPL); exits(nil); }