/* * Web discussion groups. * The group is identified by an argument. * Each group is kept as a flat directory. * There we keep a file per article. * Each file is made of a series of sections. * The first section is considered the article * The following ones are the replies. * The Id determines to which post they are replying. * Number | Id.Number * title * Author * body lines... * \a (ascii 07 / bell) */ #include <u.h>#include <libc.h>#include <bio.h>#include <ctype.h>#include "httpd.h"#include "httpsrv.h"#include "article.h" enum { Nfront = 10, // max # in front page}; typedef struct Req Req;struct Req{ char* group; char* file; int op; HSPairs*args;}; typedef struct Opf Opf;struct Opf { char op; void (*f)(Req*);}; static Hio* hout;static Hio houtb;static int vermaj;static HConnect*hcon; static int debug = 0; static char Dfltreq[]= "o=i&g=dso";static char Top[] = "/groups";static char Mark[] = "DYNAMIC"; /* * Strings seen in output, to help with localization. */static char Msearchresults[] = "Resultados de la búsqueda";static char Mreturn[] = "Volver.";static char Msendnew[]= "Enviar nuevo articulo";static char Mreply[] = "Responder";static char Mreplyto[]= "Responder a...";static char Manswers[] = "resp.";static char Mgoonreading[] = "Seguir leyendo otros";static char Mgoback[] = "Volver atras";static char Mreadmore[] = "Leer mas";static char Mreturntoindex[] = "Volver a la portada";static char Mwrote[] = "dice...";static char Mby[] = "Por";static char Mrequiredfieldmissing[] = "No has escrito en un campo requerido.\n" "Usa back en tu navegador e intenta de nuevo\n";static char Mclickheretoreturn[] = "<p>Hecho. Pulsa " "<a href=\"/magic/group?o=i&g=%s\">aquí</a> " "para volver.\n";static char Mbodywithlonglines[]= "Tienes lineas muy largas. Usa back en tu navegador\n" "y usa return para que tu mensaje tenga lineas\n" "que se puedan leer bien (max 80 caracteres).\n"; static voidbadreq(void){ hfail(hcon, HBadReq); exits(nil);} static voidnotfound(char* o){ hfail(hcon, HNotFound, o); exits(nil);} static voidfailure(void){ hfail(hcon, HTempFail); exits(nil);} static voidheaders(char* title){ if(vermaj){ hokheaders(hcon); hprint(hout, "Content-type: text/html; charset=UTF-8\r\n"); hflush(hout); } if (title != nil) hprint(hout, "<head><title>%s</title></head><body>\n", title);} static voidnotice(char* fmt, ...){ va_list arg; char buf[1024], *out; va_start(arg, fmt); out = vseprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); *out = 0; headers("notice"); hprint(hout, "%s", buf); hprint(hout, "</body>\n");} static char*getarg(Req* r, char arg, int must){ HSPairs*p; for (p = r->args; p != nil; p = p->next){ if (p->s[0] == arg) return p->t; } if (must) badreq(); return nil;} static voidparsereq(char* s, Req* r){ char* v; memset(r, 0, sizeof(Req)); r->args = hparsequery(hcon, s); v = getarg(r, 'o', 1); r->op = *v; v = getarg(r, 'g', 1); r->group = strdup(v);} static voidoutfilehdr(Biobuf* bin){ char* ln; while(ln = Brdstr(bin, '\n', 1)){ if (strcmp(ln, Mark) == 0){ free(ln); return; } hprint(hout, "%s\n", ln); free(ln); }} static voidoutfiletail(Biobuf* bin){ char* ln; while(ln = Brdstr(bin, '\n', 1)){ hprint(hout, "%s\n", ln); free(ln); }} static voidgetpostintro(Post* p, char* intro, int max, int* more){ char* e; char* w; int l; e = strecpy(intro, intro+max-4, p->body); l = e - intro; *more = (p->body[l] != 0); if (*more){ w = strrchr(intro, ' '); if (w) e = w; strcpy(e, "..."); }} static char* Mon[] = { "Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"}; static char*artdate(ulong mt){ Tm* tm; static char str[40]; if (mt == 0) return ""; tm = localtime(mt); seprint(str, str+40, "(%d-%s)", tm->mday, Mon[tm->mon]); return str;} static Biobuf*bopenfile(char* group, char* art, ulong* mt){ char* file; Biobuf* ib; Dir* d; file = smprint("%s/%s/%s", Top, group, art); if (strstr(file, "..")){ free(file); badreq(); } if (mt != nil){ *mt = 0; d = dirstat(file); if (d != nil){ *mt = d->mtime; free(d); } } ib = Bopen(file, OREAD); free(file); return ib;} static intoutpostintro(char* g, char* file, int verbose){ Biobuf* bin; Post* p; int n; char intro[120]; int more; char* date; ulong mt; bin = bopenfile(g, file, &mt); if (bin == nil) return 0; if (debug) fprint(2, "%s/%s\n", g, file); p = readarticle(bin, &n); if (p == nil){ Bterm(bin); return 0; } date = artdate(mt); if(debug) fprint(2, "article %s\n", p->title); switch(verbose){ case 0: hprint(hout,"<b><a href=\"/magic/group?o=a&g=%s&a=%s\">", g, file); hprint(hout, "%s</a> (%s; %d posts)</b><br>\n", p->title, p->author, n); break; case 1: hprint(hout,"<p> <a href=\"/magic/group?o=a&g=%s&a=%s\">", g, file); hprint(hout, "<font size=+1><b>%s</b> %s</a> ", p->title, date); hprint(hout, "<font size=-1><b>(%s %s)</b><br>\n", Mby, p->author); break; default: hprint(hout, "<p><font size=+1><hr><b>%s</b> %s", p->title, date); hprint(hout, "<br><font size=-1><b>%s %s</b> <br>\n", Mby, p->author); getpostintro(p, intro, sizeof(intro), &more); hprint(hout, "<font size=+0>%s<br>\n", intro); if (n > 1) hprint(hout, "<a href=\"/magic/group?o=a&g=%s&a=%s\">Leer " "mas</a> (%d %s.)<p>\n", g, file, n-1, Manswers); else hprint(hout, "<a href=\"/magic/group?o=a&g=%s&a=%s\">%s" "</a><p>\n",g, file, Mreadmore); } closepost(p); Bterm(bin); return 1;} static char*readfile(char* fname){ Dir* d; vlong len; int fd; char* buf; fd = open(fname, OREAD); if (fd < 0) return nil; d = dirfstat(fd); assert (d != nil); len = d->length; free(d); buf = malloc(len +1); buf[0] = 0; readn(fd, buf, len); close(fd); buf[len] = 0; return buf;} static intidxsort(void* a, void* b){ Dir* da = a; Dir* db = b; if (da->name[0] != 'A') return 1; return (int)(da->mtime - db->mtime);} static char*readindex(Req* req, char*** entsp, int* nentsp){ Dir* d; char** ents; int fd; char* buf; char* dir; int nd; long l; int i, j; char* s; /* Experiment, read all article files, and * sort by mtime. */ if (strstr(req->group, "..")) notfound(".."); dir = smprint("%s/%s", Top, req->group); fd = open(dir, OREAD); if (fd < 0) failure(); nd = dirreadall(fd, &d); close(fd); if (nd <= 0){ *entsp = nil; *nentsp = 0; return nil; } qsort(d, nd, sizeof(d[0]), idxsort); l = 0; for (i = 0; i < nd; i++){ if (d[i].name[0] != 'A') continue; l += strlen(d[i].name) + 2; } s = buf = malloc(l); ents=malloc(sizeof(char*)*(nd+1)); if (buf == nil){ free(d); failure(); } for (i = j = 0; i < nd; i++){ //hprint(hout, "debug: art = %s\n<br>", d[i].name); if (d[i].name[0] == 'A'){ ents[j++] = s; s = strecpy(s, buf+l, d[i].name); *s++ = 0; } } *entsp = ents; *nentsp= j; free(d); return buf;} static voidgetidxreq(Req* req){ static char prevfmt[] = "<a href=\"/magic/group?o=i&g=%s&S=%d\">%s</a><p>\n"; static char nextfmt[] = "<a href=\"/magic/group?o=i&g=%s&S=%d\">%s</a><p>\n"; char* idx; char** ents; int nents; char* buf; Biobuf* ib; int i; int start; int n; char* s; idx = smprint("%s/%s/index.html", Top, req->group); start = 0; s = getarg(req, 'S', 0); if (s != nil) start=atoi(s); ib = Bopen(idx, OREAD); if (ib == nil) notfound("index"); headers(nil); outfilehdr(ib); buf = readindex(req, &ents, &nents); n = 0; for (i = nents-1; i >= 0; i--){ if (start != 0 && n == start){ hprint(hout, prevfmt , req->group, ((start - Nfront) < 0 ? 0 : (start - Nfront)), Mgoback); } if (n >= start && n < start+Nfront) outpostintro(req->group, ents[i], 2); if (n == start+Nfront){ hprint(hout, nextfmt, req->group, start + Nfront, Mgoonreading); break; } n++; } outfiletail(ib); Bterm(ib); free(idx); free(ents); free(buf);} static voidoutpost(int tab, char* g, char* f, Post* p){ char tabs[Nids+1]; char *np; char *sp; char* s; char* titlefmt= "%s<font size=+2><b>%s</b><font size=-2>\n<p>"; char* authfmt= "%s<font size=+1><b>%s %s</b><font size=-1><br>\n"; char* otherfmt = "%s<font size=+1><b>%s %s</b><font size=-1><br>\n"; char* replfmt = "%s<a href=\"/magic/group?o=r&g=%s&a=%s&" "i=%s\">%s</a>\n"; memset(tabs, '\t', sizeof(tabs)); tabs[tab] = 0; if (tab <= 0){ if (p->title[0] != 0) hprint(hout, titlefmt, tabs, p->title); hprint(hout, authfmt, tabs, Mby, p->author); } else hprint(hout, otherfmt, tabs, p->author, Mwrote); sp = p->body; if (p->body[0] != 0){ while(sp != nil){ np = strchr(sp, '\n'); hprint(hout, "%s", tabs); if (np == nil) hprint(hout, "%s\n", sp); else{ hwrite(hout, sp, np-sp); hprint(hout, "\n"); np++; } sp = np; } } if (g != nil && f != nil){ s = id2str(p->ids, p->nids); hprint(hout, replfmt, tabs, g, f, s, Mreply); } hprint(hout, "<hr>\n");} static voidoutarticle(int tab, char* g, char* f, Post* p){ int i; outpost(tab, g, f, p); for (i = 0; i < p->nsons; i++) outarticle(tab+1, g, f, p->sons[i]);} static Post*openarticle(char* group, char* art){ Post* p; Biobuf* ib; ulong mt; ib = bopenfile(group, art, &mt);Lines 1–500 of 855. Load more
/* * Web discussion groups. * The group is identified by an argument. * Each group is kept as a flat directory. * There we keep a file per article. * Each file is made of a series of sections. * The first section is considered the article * The following ones are the replies. * The Id determines to which post they are replying. * Number | Id.Number * title * Author * body lines... * \a (ascii 07 / bell) */ #include <u.h> #include <libc.h> #include <bio.h> #include <ctype.h> #include "httpd.h" #include "httpsrv.h" #include "article.h" enum { Nfront = 10, // max # in front page }; typedef struct Req Req; struct Req{ char* group; char* file; int op; HSPairs*args; }; typedef struct Opf Opf; struct Opf { char op; void (*f)(Req*); }; static Hio* hout; static Hio houtb; static int vermaj; static HConnect*hcon; static int debug = 0; static char Dfltreq[]= "o=i&g=dso"; static char Top[] = "/groups"; static char Mark[] = "DYNAMIC"; /* * Strings seen in output, to help with localization. */ static char Msearchresults[] = "Resultados de la búsqueda"; static char Mreturn[] = "Volver."; static char Msendnew[]= "Enviar nuevo articulo"; static char Mreply[] = "Responder"; static char Mreplyto[]= "Responder a..."; static char Manswers[] = "resp."; static char Mgoonreading[] = "Seguir leyendo otros"; static char Mgoback[] = "Volver atras"; static char Mreadmore[] = "Leer mas"; static char Mreturntoindex[] = "Volver a la portada"; static char Mwrote[] = "dice..."; static char Mby[] = "Por"; static char Mrequiredfieldmissing[] = "No has escrito en un campo requerido.\n" "Usa back en tu navegador e intenta de nuevo\n"; static char Mclickheretoreturn[] = "<p>Hecho. Pulsa " "<a href=\"/magic/group?o=i&g=%s\">aquí</a> " "para volver.\n"; static char Mbodywithlonglines[]= "Tienes lineas muy largas. Usa back en tu navegador\n" "y usa return para que tu mensaje tenga lineas\n" "que se puedan leer bien (max 80 caracteres).\n"; static void badreq(void) { hfail(hcon, HBadReq); exits(nil); } static void notfound(char* o) { hfail(hcon, HNotFound, o); exits(nil); } static void failure(void) { hfail(hcon, HTempFail); exits(nil); } static void headers(char* title) { if(vermaj){ hokheaders(hcon); hprint(hout, "Content-type: text/html; charset=UTF-8\r\n"); hflush(hout); } if (title != nil) hprint(hout, "<head><title>%s</title></head><body>\n", title); } static void notice(char* fmt, ...) { va_list arg; char buf[1024], *out; va_start(arg, fmt); out = vseprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); *out = 0; headers("notice"); hprint(hout, "%s", buf); hprint(hout, "</body>\n"); } static char* getarg(Req* r, char arg, int must) { HSPairs*p; for (p = r->args; p != nil; p = p->next){ if (p->s[0] == arg) return p->t; } if (must) badreq(); return nil; } static void parsereq(char* s, Req* r) { char* v; memset(r, 0, sizeof(Req)); r->args = hparsequery(hcon, s); v = getarg(r, 'o', 1); r->op = *v; v = getarg(r, 'g', 1); r->group = strdup(v); } static void outfilehdr(Biobuf* bin) { char* ln; while(ln = Brdstr(bin, '\n', 1)){ if (strcmp(ln, Mark) == 0){ free(ln); return; } hprint(hout, "%s\n", ln); free(ln); } } static void outfiletail(Biobuf* bin) { char* ln; while(ln = Brdstr(bin, '\n', 1)){ hprint(hout, "%s\n", ln); free(ln); } } static void getpostintro(Post* p, char* intro, int max, int* more) { char* e; char* w; int l; e = strecpy(intro, intro+max-4, p->body); l = e - intro; *more = (p->body[l] != 0); if (*more){ w = strrchr(intro, ' '); if (w) e = w; strcpy(e, "..."); } } static char* Mon[] = { "Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic" }; static char* artdate(ulong mt) { Tm* tm; static char str[40]; if (mt == 0) return ""; tm = localtime(mt); seprint(str, str+40, "(%d-%s)", tm->mday, Mon[tm->mon]); return str; } static Biobuf* bopenfile(char* group, char* art, ulong* mt) { char* file; Biobuf* ib; Dir* d; file = smprint("%s/%s/%s", Top, group, art); if (strstr(file, "..")){ free(file); badreq(); } if (mt != nil){ *mt = 0; d = dirstat(file); if (d != nil){ *mt = d->mtime; free(d); } } ib = Bopen(file, OREAD); free(file); return ib; } static int outpostintro(char* g, char* file, int verbose) { Biobuf* bin; Post* p; int n; char intro[120]; int more; char* date; ulong mt; bin = bopenfile(g, file, &mt); if (bin == nil) return 0; if (debug) fprint(2, "%s/%s\n", g, file); p = readarticle(bin, &n); if (p == nil){ Bterm(bin); return 0; } date = artdate(mt); if(debug) fprint(2, "article %s\n", p->title); switch(verbose){ case 0: hprint(hout,"<b><a href=\"/magic/group?o=a&g=%s&a=%s\">", g, file); hprint(hout, "%s</a> (%s; %d posts)</b><br>\n", p->title, p->author, n); break; case 1: hprint(hout,"<p> <a href=\"/magic/group?o=a&g=%s&a=%s\">", g, file); hprint(hout, "<font size=+1><b>%s</b> %s</a> ", p->title, date); hprint(hout, "<font size=-1><b>(%s %s)</b><br>\n", Mby, p->author); break; default: hprint(hout, "<p><font size=+1><hr><b>%s</b> %s", p->title, date); hprint(hout, "<br><font size=-1><b>%s %s</b> <br>\n", Mby, p->author); getpostintro(p, intro, sizeof(intro), &more); hprint(hout, "<font size=+0>%s<br>\n", intro); if (n > 1) hprint(hout, "<a href=\"/magic/group?o=a&g=%s&a=%s\">Leer " "mas</a> (%d %s.)<p>\n", g, file, n-1, Manswers); else hprint(hout, "<a href=\"/magic/group?o=a&g=%s&a=%s\">%s" "</a><p>\n",g, file, Mreadmore); } closepost(p); Bterm(bin); return 1; } static char* readfile(char* fname) { Dir* d; vlong len; int fd; char* buf; fd = open(fname, OREAD); if (fd < 0) return nil; d = dirfstat(fd); assert (d != nil); len = d->length; free(d); buf = malloc(len +1); buf[0] = 0; readn(fd, buf, len); close(fd); buf[len] = 0; return buf; } static int idxsort(void* a, void* b) { Dir* da = a; Dir* db = b; if (da->name[0] != 'A') return 1; return (int)(da->mtime - db->mtime); } static char* readindex(Req* req, char*** entsp, int* nentsp) { Dir* d; char** ents; int fd; char* buf; char* dir; int nd; long l; int i, j; char* s; /* Experiment, read all article files, and * sort by mtime. */ if (strstr(req->group, "..")) notfound(".."); dir = smprint("%s/%s", Top, req->group); fd = open(dir, OREAD); if (fd < 0) failure(); nd = dirreadall(fd, &d); close(fd); if (nd <= 0){ *entsp = nil; *nentsp = 0; return nil; } qsort(d, nd, sizeof(d[0]), idxsort); l = 0; for (i = 0; i < nd; i++){ if (d[i].name[0] != 'A') continue; l += strlen(d[i].name) + 2; } s = buf = malloc(l); ents=malloc(sizeof(char*)*(nd+1)); if (buf == nil){ free(d); failure(); } for (i = j = 0; i < nd; i++){ //hprint(hout, "debug: art = %s\n<br>", d[i].name); if (d[i].name[0] == 'A'){ ents[j++] = s; s = strecpy(s, buf+l, d[i].name); *s++ = 0; } } *entsp = ents; *nentsp= j; free(d); return buf; } static void getidxreq(Req* req) { static char prevfmt[] = "<a href=\"/magic/group?o=i&g=%s&S=%d\">%s</a><p>\n"; static char nextfmt[] = "<a href=\"/magic/group?o=i&g=%s&S=%d\">%s</a><p>\n"; char* idx; char** ents; int nents; char* buf; Biobuf* ib; int i; int start; int n; char* s; idx = smprint("%s/%s/index.html", Top, req->group); start = 0; s = getarg(req, 'S', 0); if (s != nil) start=atoi(s); ib = Bopen(idx, OREAD); if (ib == nil) notfound("index"); headers(nil); outfilehdr(ib); buf = readindex(req, &ents, &nents); n = 0; for (i = nents-1; i >= 0; i--){ if (start != 0 && n == start){ hprint(hout, prevfmt , req->group, ((start - Nfront) < 0 ? 0 : (start - Nfront)), Mgoback); } if (n >= start && n < start+Nfront) outpostintro(req->group, ents[i], 2); if (n == start+Nfront){ hprint(hout, nextfmt, req->group, start + Nfront, Mgoonreading); break; } n++; } outfiletail(ib); Bterm(ib); free(idx); free(ents); free(buf); } static void outpost(int tab, char* g, char* f, Post* p) { char tabs[Nids+1]; char *np; char *sp; char* s; char* titlefmt= "%s<font size=+2><b>%s</b><font size=-2>\n<p>"; char* authfmt= "%s<font size=+1><b>%s %s</b><font size=-1><br>\n"; char* otherfmt = "%s<font size=+1><b>%s %s</b><font size=-1><br>\n"; char* replfmt = "%s<a href=\"/magic/group?o=r&g=%s&a=%s&" "i=%s\">%s</a>\n"; memset(tabs, '\t', sizeof(tabs)); tabs[tab] = 0; if (tab <= 0){ if (p->title[0] != 0) hprint(hout, titlefmt, tabs, p->title); hprint(hout, authfmt, tabs, Mby, p->author); } else hprint(hout, otherfmt, tabs, p->author, Mwrote); sp = p->body; if (p->body[0] != 0){ while(sp != nil){ np = strchr(sp, '\n'); hprint(hout, "%s", tabs); if (np == nil) hprint(hout, "%s\n", sp); else{ hwrite(hout, sp, np-sp); hprint(hout, "\n"); np++; } sp = np; } } if (g != nil && f != nil){ s = id2str(p->ids, p->nids); hprint(hout, replfmt, tabs, g, f, s, Mreply); } hprint(hout, "<hr>\n"); } static void outarticle(int tab, char* g, char* f, Post* p) { int i; outpost(tab, g, f, p); for (i = 0; i < p->nsons; i++) outarticle(tab+1, g, f, p->sons[i]); } static Post* openarticle(char* group, char* art) { Post* p; Biobuf* ib; ulong mt; ib = bopenfile(group, art, &mt);