ezmlmx 0.68
ezmlmx
Loading...
Searching...
No Matches
ezmlm-cgi.c
Go to the documentation of this file.
1#include <sys/types.h>
2#include <unistd.h>
3#include "alloc.h"
4#include "direntry.h"
5#include "datetime.h"
6#include "now.h"
7#include "stralloc.h"
8#include "logmsg.h"
9#include "error.h"
10#include "env.h"
11#include "sig.h"
12#include "open.h"
13#include "getln.h"
14#include "case.h"
15#include "scan.h"
16#include "str.h"
17#include "fmt.h"
18#include "readwrite.h"
19#include "wait.h"
20#include "exit.h"
21#include "buffer.h"
22#include "getconf.h"
23#include "alloc.h"
24#include "constmap.h"
25#include "byte.h"
26#include "subscribe.h"
27#include "errtxt.h"
28#include "makehash.h"
29#include "mime.h"
30#include "idx.h"
31#include "date2yyyymm.h"
32#include "cgi.h"
33#include "constmap.h"
34
35#define WHO "ezmlm-cgi"
36
44
45#define SUBSCRIBE "-subscribe"
46#define FAQ "-faq"
47#define TXT_CGI_SUBSCRIBE "\">[Subscribe To List]</a>\n"
48#define TXT_CGI_FAQ "\">[List FAQ]</a>\n"
49
50int flagshowhtml = 1; /* show text/html parts. This leads to duplication */
51 /* when both text/plain and text/html are in a */
52 /* multipart/alternative message, but it is assumed*/
53 /* that text/html is not frivolous, but only used */
54 /* when the formatting is important. */
55int flagobscure = 0; /* Don't remove Sender's E-mail address in message path. */
56 /* Overridden by config file (- before list name). */
57
58/**************** Header processing ***********************/
59char headers_used[] = "Subject\\From\\Date\\content-type\\"
60 "content-transfer-encoding\\mime-version";
61/* index of headers displayed (shown in order listed above) */
62int headers_shown[] = {1,1,1,0,0,0};
63/* index of specific headers */
64#define NO_HDRS 6
65#define HDR_SUBJECT 1
66#define HDR_FROM 2
67#define HDR_CT 4
68#define HDR_CTENC 5
69#define HDR_VERSION 6
70
71/* Need to add inits if you increase NO_HDRS */
72stralloc hdr[NO_HDRS] = { {0},{0},{0},{0},{0},{0} };
73/**************** Header processing ***********************/
74
75
76/* index of subject in above, first = 1 */
77
78/* TODO: Sort headers before display. Find a way to display the body with the */
79/* correct charset, ideally letting the browser do the work (should really */
80/* be able to specify charset for DIV !) */
81
82/* ulong at least 32 bits. (Creating a Year 0xffffff problem ;-) */
83#define MAXULONG 0xffffffff
84
85/* navigation: navstr[0] = {m,s,a,d,i} ; item (message, subject, author, date, index) */
86/* navstr[1] = {m,s,a} ; view (no date, no index) */
87/* navstr[2] = {p,s,n,p,n} ; direction (previous, same, next, previous, next) */
88/* SHOULD: navstr[2] = {F,p,s,n,L} ; direction (First, previous, same, next, Last) */
89
90char navstr[5] = "xxx:";
91#define VIEW "-msadiz"
92#define VIEW_MESSAGE 1
93#define VIEW_TOPIC 2 // build up by ezmlm-archive ./archive/subjects/[ab]
94#define VIEW_AUTHOR 3 // build up by ezmlm-archive; ./archive/authors/[ab]
95#define VIEW_DATE 4 // no such peristent thing; p -> s -> n message
96#define VIEW_INDEX 5 // generic ./archive; indexed by ezmlm-idx => ./archive/[n]/index
97
98#define DIRECT "psnpnz" // toggle: p->s->n->p->n
99#define DIRECT_SAME 0
100#define DIRECT_NEXT 1
101#define DIRECT_PREV -1 /* use only as the argument for some functions. Terrible hack for date links (fixed) */
102#define DIRECT_FIRST 3
103#define DIRECT_LAST 2
104
105char *dir = 0;
106char *local = 0;
107char *host = 0;
108char *home = 0;
109char *banner = 0;
110const char *charset = 0;
111char *stylesheet = 0;
112char *cntl = 0; // three character control code => navstr
113char strnum[FMT_ULONG];
114/* these are the only headers we really care about for message display */
115/* one can always retrieve the complete message by E-mail */
116stralloc charg = {0};
117stralloc url = {0};
118stralloc author = {0};
119stralloc subject = {0};
120stralloc base = {0};
121stralloc line = {0};
122stralloc decline = {0}; /* for rfc2047-decoded headers and QP/base64 */
123stralloc cfline = {0}; /* from config file */
124stralloc fname = {0};
125stralloc dtline = {0};
126stralloc headers = {0};
127stralloc encoding = {0};
128stralloc content = {0};
129stralloc charsetbase = {0};
130stralloc curcharset = {0};
131stralloc sainit = {0};
132stralloc listname = {0};
133struct constmap headermap;
134unsigned long uid,euid;
136int so = 0;
137int ss23 = 0;
138int state = 0;
140int match; /* used everywhere and no overlap */
141int fd; /* same; never >1 open */
142int cache; /* 0 = don't; 1 = don't know; 2 = do */
145unsigned int flagmime;
146unsigned int cs, csbase, pos;
150char cn1 = 0;
151char cn2 = 0;
152char lastjp[] = "B"; /* to get back to the correct JP after line break */
153char *bannerargs[4]; // external banner does not work --eh
154
155
156struct msginfo { /* clean info on the target message */
157 char item; /* What we want (= message) */
158 char view; /* View of desired items */
159 int direction; /* Relation to current view [may be calculated] */
160 unsigned long source; /* reference message number */
161 unsigned long target;
162 unsigned long date;
163 unsigned long *authnav; /* msgnav structure */
164 unsigned long *subjnav; /* msgnav structure */
165 char *author;
166 char *subject;
167 char *cgiarg; /* sub/auth as expected from view */
169
172
175
176char inbuf[4096];
177buffer bi;
178
179static void die_nomem() { logmsg(WHO,111,FATAL,ERR_NOMEM); }
180static void die_syntax(const char *s) { logmsg(WHO,100,ERROR,B(ERR_SYNTAX,"ezcgirc: ",s)); }
181
182char outbuf[4096];
183buffer bo = BUFFER_INIT(write,1,outbuf,sizeof(outbuf));
184
185void oput(const char *s,unsigned int l)
186{
187 if (buffer_put(&bo,s,l) == -1)
188 logmsg(WHO,111,FATAL,B(ERR_WRITE,"stdout"));
189}
190
191void oputs(const char *s)
192{
193 oput(s,str_len(s));
194}
195
196/* this error is for things that happen only if program logic is screwed up */
197
198void die_prog(const char *s)
199{
200 logmsg(WHO,100,FATAL,B("program error (please send bug report to ezmlmx@fehcom.de): ",s," Command: ",cntl));
201 }
202
208
209void cgierr(const char *s,const char *s1,const char *s2)
210{
211 logmsg(WHO,0,WARN,B("cgierr: ",s,s1,s2));
212 oputs("Content-type: text/html\n");
213 oputs("Status: 404 \n\n");
214 oputs("<html>\n <head> </head> <body> \n");
215 oputs("<h3><strong>ezmlm-cgi</strong>: <tt> ");
216 if (s) oputs(s);
217 if (s1) oputs(s1);
218 if (s2) oputs(s2);
219 oputs("</tt></h3>\n</body>\n</html>\n");
220 buffer_flush(&bo);
221 _exit(0);
222}
223
224unsigned long msgnav[5]; /* 0 prev prev 1 prev 2 this 3 next 4 next-next */
225
227{
228 flagpre = flag;
229 precharcount = 0;
230 cn1 = 0; cn2 = 0; /* just in case */
231}
232
244
245unsigned int decode_charset(const char *s,unsigned int l)
246{
247 if (case_startb(s,l,"iso-8859") || case_startb(s,l,"us-ascii") ||
248 case_startb(s,l,"utf")) /* at the moment, we can do utf-8 right */
249 return CS_NONE; /* what is utf-7 (used by OE)? */
250 if (case_startb(s,l,"x-cp") ||
251 case_startb(s,l,"cp") ||
252 case_startb(s,l,"x-mac") ||
253 case_startb(s,l,"koi8")) return CS_NONE;
254 if (!l || *s == 'x' || *s == 'X') return CS_BAD;
255 if (case_startb(s,l,"iso-2022")) {
256 if (case_startb(s+8,l-8,"-cn"))
257 return CS_2022_CN;
258 if (case_startb(s+8,l-8,"-jp"))
259 return CS_2022_JP;
260 return CS_2022_KR;
261 }
262 if (case_startb(s,l,"cn-") ||
263 case_startb(s,l,"hz-gb") ||
264 case_startb(s,l,"gb") ||
265 case_startb(s,l,"big5"))
266 return CS_CN; /* Only consideration for linebreak */
267 if (case_startb(s,l,"iso_8859") ||
268 case_startb(s,l,"latin") ||
269 case_startb(s,l,"windows")) return CS_NONE;
270 return CS_BAD;
271}
272
293
294void html_put (const char *s,unsigned int l)
295{
296 if (!cs) { /* us-ascii & iso-8859- & unrecognized */
297 for (;l--;s++) {
298 precharcount++;
299 switch (*s) {
300 case '>': oputs("&gt;"); break;
301 case '<': oputs("&lt;"); break;
302 case '"': oputs("&quot;"); break;
303 case '&': oputs("&amp;"); break;
304 case '\n': precharcount = 0; oput(s,1); break;
305 case ' ': if (precharcount >= 84 && flagpre) {
306 oput("\n",1); /* in place of ' ' */
307 precharcount = 0;
308 } else
309 oput(s,1); /* otherwise out with it. */
310 break;
311 default: oput(s,1); break;
312 }
313 }
314 } else if (cs == CS_CN) { /* cn-, gb*, big5 */
315 for (;l--;s++) {
316 precharcount++;
317 if (cn1) { cn2 = cn1; cn1 = 0; } /* this is byte 2 */
318 else { cn2 = 0; cn1 = *s & 0x80; } /* this is byte 1/2 or ascii */
319 if (!cn1 && !cn2) { /* ascii */
320 switch (*s) {
321 case '>': oputs("&gt;"); break;
322 case '<': oputs("&lt;"); break;
323 case '"': oputs("&quot;"); break;
324 case '&': oputs("&amp;"); break;
325 case '\n': precharcount = 0; oput(s,1); break;
326 case ' ': if (precharcount >= 84 && flagpre) {
327 oput("\n",1); /* break in ascii sequence */
328 precharcount = 0;
329 } else
330 oput(s,1);
331 break;
332 default: oput(s,1); break;
333 }
334 } else if (precharcount >= 84 && flagpre && cn2) {
335 oput("\n",1); /* break after 2-byte code */
336 precharcount = 0;
337 }
338 }
339 } else { /* iso-2022 => PAIN! */
340 for (; l--; s++) {
341 precharcount++;
342 if (ss23) { /* ss2/ss3 character */
343 ss23--;
344 oput(s,1);
345 continue;
346 }
347 if (so) { /* = 0 ascii, = 1 SO charset */
348 if (!(*s & 0xe0)) { /* ctrl-char */
349 switch (*s) {
350 case ESC: state = 1; break;
351 case SI: so = 0; break;
352 case '\n': precharcount = 0; break;
353 default: break;
354 }
355 }
356 oput(s,1);
357 } else { /* check only ascii */
358 switch (*s) {
359 case '>': oputs("&gt;"); break;
360 case '<': oputs("&lt;"); break;
361 case '"': oputs("&quot;"); break;
362 case '&': oputs("&amp;"); break;
363 case ' ': if (precharcount >= 84 && flagpre) {
364 oput("\n",1); /* break in ascii sequence */
365 precharcount = 0;
366 } else
367 oput(s,1);
368 break;
369 default: oput(s,1);
370 if (!(*s & 0xe0)) {
371 switch (*s) {
372 case SO: so = 1; break;
373 case ESC: state = 1; break;
374 case SI: so = 0; break; /* shouldn't happen */
375 case '\n': precharcount = 0; break;
376 default: break;
377 }
378 }
379 }
380 }
381
382 /* by now all output is done, now ESC interpretation */
383
384 if (state) {
385 /* ESC code - don't count */
387 state++;
388 switch (state) {
389 case 2: break; /* this was the ESC */
390 case 3: switch (*s) {
391 case 'N': ss23 = (cs & 1) + 1; state = 0; break;
392 case 'O': ss23 = 2; state = 0; break;
393 case '(': state = 20; so = 0; break; /* JP ascii */
394 case '$': break; /* var S2/SS2/SS3 des*/
395 case '.': state = 10; /* g3 settings, one more char */
396 default: state = 0; break; /* or JP */
397 }
398 break;
399 case 4: switch (*s) { /* s2/ss2/ss3 or JP 2 byte shift */
400 case 'B':
401 case '@': lastjp[0] = *s;
402 so = 1; state = 0; break; /* JP */
403 default: break; /* other SS2/3 des */
404 }
405 break;
406 case 5: state = 0; break; /* 4th char of ESC $ *|+|) X */
407 case 11: state = 0; break; /* 3nd char of ESC . */
408 case 21: state = 0; break; /* ESC ( X for JP */
409 default: die_prog("bad state in html_put"); break;
410 }
411 } else if (so && flagpre && precharcount >= 84) {
412 /* 84 is nicer than 78/80 since most use GUI browser */
413 /* iso-2022-* line splitter here. SO only, SI done above */
414 /* For JP need even precharcount, add ESC ( B \n ESC $B */
415 if (so && !(precharcount & 1)) { /* even */
416 precharcount = 0; /* reset */
417 if (cs == CS_2022_JP) { /* JP uses ESC like SI/SO */
418 oputs(TOASCII);
419 oput("\n",1);
420 oputs(TOJP);
421 oput(lastjp,1);
422 } else {
423 if (so) {
424 /* For iso-2022-CN: nothing if SI, otherwise SI \n SO */
425 /* For iso-2022-KR same */
427 } else
428 oput("\n",1);
429 }
430 }
431 }
432 } // end for loop
433 } // end else
434}
435
436char hexchar[] = "0123456789ABCDEF";
437char enc_url[] = "%00";
438
439void urlencode_put(const char *s,unsigned int l)
440{
441 for (; l--; s++) {
442 unsigned char ch;
443 ch = (unsigned char) *s;
444 if (ch <= 32 || ch > 127 || byte_chr("?<>=/:%+#\"",10,ch) != 10) {
445 enc_url[2] = hexchar[ch & 0xf];
446 enc_url[1] = hexchar[(ch >> 4) & 0xf];
447 oput(enc_url,3);
448 } else
449 oput(s,1);
450 }
451}
452
453void urlencode_puts(const char *s)
454{
455 urlencode_put(s,str_len(s));
456}
457
458void anchor_put(unsigned char *s,unsigned int l)
459{
460 unsigned char *cpl, *cpafter, *cpstart, *cpend;
461 unsigned int pos, i;
462
463 pos = byte_chr(s,l,':'); //+
464 if (pos + 3 >= l || !pos) { /* no ':' no URL (most lines) */
465 html_put(s,l);
466 return;
467 }
468
469 cpl = s;
470 cpafter = s + l;
471 for (;;) {
472 cpstart = (char *) 0;
473 if (s[pos + 1] == '/' && s[pos + 2] == '/') {
474 cpend = s + pos + 2;
475
476 for (i = pos - 1; (i < l && i + 6 >= pos); i--) { /* pos always >=1 */
477 if ((s[i] < 'a' || s[i] > 'z') && (s[i] < 'A' || s[i] > 'Z')) {
478 cpstart = s + i + 1; /* "[:alpha:]{1,5}://" accepted */
479 break;
480 }
481 if (!i && i + 6 < pos) {
482 cpstart = s;
483 break;
484 }
485 }
486 }
487 if (cpstart) { /* found URL */
488 while (cpend < cpafter && str_chr(" \t\n",*cpend) == 3)
489 cpend++;
490 cpend--; /* locate end */
491 while (cpend > cpstart && str_chr(".,;])>\"\'",*cpend) != 8)
492 cpend--;
493 html_put(cpl,cpstart - cpl); /* std txt */
494 oputs("<a href=\""); /* link start */
495 oput(cpstart,cpend - cpstart + 1); /* link */
496 oputs("\">");
497 html_put(cpstart,cpend - cpstart + 1); /* visible */
498 oputs("</a>"); /* end */
499 cpl = cpend + 1;
500 pos = cpend - s;
501 if (pos >= l) return;
502 } else
503 pos++;
504
505 pos += byte_chr(s + pos,l - pos,':'); //*
506 if (pos + 3 >= l) {
507 html_put(cpl,cpafter - cpl); /* std txt */
508 return;
509 }
510 }
511}
512
513int checkhash(const char *s)
514{
515 int l = HASHLEN;
516 while (l--) {
517 if (*s < 'a' || *s > 'p') return 0; /* illegal */
518 s++;
519 }
520 if (*s) return 0; /* extraneous junk */
521 return 1;
522}
523
530
531int uri2fn(stralloc *sa,char item,unsigned long n,const char *hash)
532{
533 if (!stralloc_copys(sa,"archive/")) die_nomem();
534 if (item == VIEW_MESSAGE) {
535 if (!stralloc_catb(sa,strnum,fmt_ulong(strnum,n/100))) die_nomem();
536 if (!stralloc_cats(sa,"/")) die_nomem();
537 if (!stralloc_catb(sa,strnum,fmt_uint0(strnum,(unsigned int) (n%100),2))) die_nomem();
538 } else if (item == VIEW_DATE) {
539 if (!stralloc_cats(sa,"threads/")) die_nomem();
540 if (!stralloc_catb(sa,strnum,fmt_ulong(strnum,n)))
541 die_nomem();
542 } else if (item == VIEW_INDEX) {
543 if (!stralloc_catb(sa,strnum,fmt_ulong(strnum,n/100))) die_nomem();
544 if (!stralloc_cats(sa,"/index")) die_nomem();
545 } else {
546 if (item == VIEW_AUTHOR) {
547 if (!stralloc_cats(sa,"authors/")) die_nomem();
548 } else {
549 if (!stralloc_cats(sa,"subjects/")) die_nomem();
550 }
551 if (!hash) return 0;
552 if (!stralloc_catb(sa,hash,2)) die_nomem();
553 if (!stralloc_cats(sa,"/")) die_nomem();
554 if (!stralloc_catb(sa,hash + 2,HASHLEN - 2)) die_nomem();
555 }
556 if (!stralloc_0(sa)) die_nomem();
557
558 return 1;
559}
560
570
571void alink(struct msginfo *infop,unsigned char item,unsigned char view,
572 unsigned long msgnum,const char *data,unsigned int l)
573{
574 const char *cp;
575
576 cp = (const char *) 0;
577 if (view == VIEW_TOPIC && infop->target == msgnum)
578 oputs("<a name=\"b\"></a>");
579
580 oput(url.s,url.len);
581 navstr[0] = VIEW[item];
582 navstr[1] = VIEW[view];
583 navstr[2] = DIRECT[DIRECT_SAME + 1]; // DIRECT_NEXT
584
585 if (item == VIEW_MESSAGE && view == VIEW_AUTHOR) {
586 if (infop->subject) {
587 navstr[1] = VIEW[VIEW_TOPIC];
588 cp = infop->subject; /* always HASLEN in length due to decode_cmd */
589 }
590 }
591 oputs(navstr); /* e.g. map: */
592 oput(strnum,fmt_ulong(strnum,msgnum));
593 if (!cp && l >= HASHLEN)
594 cp = data;
595 if (infop->date) {
596 oput(":",1); //+
597 oput(strnum,fmt_ulong(strnum,infop->date));
598 }
599 if (cp) {
600 oput(":",1); //
601 oput(cp,HASHLEN);
602 }
603 switch (item) {
604 case VIEW_MESSAGE: oputs("\" class=\"mlk\">"); break;
605 case VIEW_AUTHOR: oputs("#b\" class=\"alk\">"); break;
606 case VIEW_TOPIC: oputs("#b\" class=\"slk\">"); break;
607 default: oputs("#b\">"); break;
608 }
609 if (HASHLEN + 1 < l)
610 html_put(data + HASHLEN + 1,l - HASHLEN - 1);
611 else
612 oputs("(none)");
613 oputs("</a>");
614}
615
620
621void index_messages(struct msginfo *infop,unsigned char item)
622{
623 oput(url.s,url.len);
624 navstr[0] = VIEW[item];
625 navstr[1] = VIEW[item];
626 navstr[2] = DIRECT[DIRECT_SAME + 1];
627 unsigned char c = navstr[2];
628 if (c != 's' && c != 'p' && c != 'n') navstr[2] = 'p';
629 navstr[3] = ':';
630 oputs(navstr); /* e.g. map: */
631 oput(strnum,fmt_ulong(strnum,infop->target));
632
633 if (infop->date) {
634 oput(":",1); // +
635 oput(strnum,fmt_ulong(strnum,infop->date));
636 }
637
638 switch (item) {
639 case VIEW_AUTHOR: if (infop->author) {
640 oput(":",1); //
641 oputs(infop->author);
642 } break;
643 case VIEW_TOPIC: if (infop->subject) {
644 oput(":",1); // +
645 oputs(infop->subject);
646 } break;
647 default: break;
648 }
649 oputs("#b\"");
650}
651
657
658void nav_links(struct msginfo *infop,unsigned char view,unsigned char direction)
659{
660 unsigned long msgnum;
661 char *acc;
662
663 oput(url.s,url.len);
665 navstr[1] = VIEW[view];
666 navstr[2] = DIRECT[direction + 1]; // DIRECT_PREV = -1 ;-)
667 unsigned char c = navstr[2];
668 if (c != 's' && c != 'p' && c != 'n') navstr[2] = 'p'; // UB fix
669 navstr[3] = ':';
670 msgnum = infop->target;
671 acc = 0;
672
673 switch (view) {
674 case VIEW_TOPIC: if (infop->subject) acc = infop->subject;
675 if (infop->subjnav) { /* translate to message navigation */
676 if (infop->subjnav[direction]) {
677 msgnum = infop->subjnav[direction];
678 navstr[2] = DIRECT[DIRECT_SAME + 1];
679 }
680 }
681 acc = infop->subject; break;
682 case VIEW_AUTHOR: if (infop->author) acc = infop->author;
683 if (infop->authnav) { /* translate to message navigation */
684 if (infop->authnav[direction]) {
685 msgnum = infop->authnav[direction];
686 navstr[2] = DIRECT[DIRECT_SAME + 1];
687 }
688 }
689 acc = infop->author; break;
690 default: break;
691 }
692
693 oputs(navstr);
694 oput(strnum,fmt_ulong(strnum,msgnum));
695
696 if (acc) {
697 oputs(":"); //+
698 oputs(acc);
699 }
700 oputs("\">");
701}
702
704{
705 oputs("?subject=");
706 urlencode_puts("Just Click \"SEND\"!");
707}
708
710{
711 const char *cp, *cp1, *cp2;
712
713 if (home && *home) {
714 cp = home;
715 for(;;) {
716 cp1 = cp;
717 while (*cp1 && *cp1 != '=')
718 cp1++;
719 if (!*cp1) break;
720 cp2 = cp1;
721 while (*cp2 && *cp2 != ',') cp2++;
722 oputs("<a href=\"");
723 oput(cp1 + 1,cp2 - cp1 - 1);
724 oputs("\">");
725 oput(cp,cp1 - cp);
726 oputs("</a>\n");
727 if (!*cp2) break;
728 cp = cp2 + 1;
729 }
730 }
731}
732
734{
735 oputs("<a href=\"mailto:");
736 oputs(local);
738 oputs("@");
739 oputs(host);
740 justpress();
742 oputs("<a href=\"mailto:");
743 oputs(local);
744 oputs(FAQ);
745 oputs("@");
746 oputs(host);
747 justpress();
749}
750
755
756void message_links(struct msginfo *infop)
757{
758 oputs("<div class=msglinks><strong>Messages by: \n");
759
760 nav_links(infop,VIEW_TOPIC,DIRECT_PREV); // <- topic ->
761 oputs("[&larr;</a> ");
763 oputs(">topic</a> ");
765 oputs("&rarr;]</a> \n");
766
767 nav_links(infop,VIEW_MESSAGE,DIRECT_PREV); // <- date ->
768 oputs("[&larr;</a> ");
770 oputs(">time</a> ");
772 oputs("&rarr;]</a> \n");
773
774 nav_links(infop,VIEW_AUTHOR,DIRECT_PREV); // <- author ->
775 oputs("[&larr;</a> ");
777 oputs(">author</a> ");
779 oputs("&rarr;]</a> |\n");
781
782 oputs(">[Topics]</a>\n"); // [Threads]
783 home_link();
784
785 subfaq_link();
786 oputs("</strong></div>\n");
787}
788
789#define SPC_BASE 1
790#define SPC_BANNER 2
791
797
798void html_header(const char *t,const char *s,unsigned int l,const char *class,int flagspecial)
799{
800 oputs("Content-Type: text/html; charset=");
802
803 oputs("\nCache-Control: ");
804 switch (cache) {
805 case 0: oputs("no-cache"); break; /* known upper border */
806 case 1: oputs("max-age=300"); break; /* 5 min - most lists aren't that fast*/
807 case 2: oputs("max-age=1209600"); break; /* 14 days is a long time */
808 }
809 oputs("\n\n");
810 oputs("<!DOCTYPE html>\n<html>\n<head>\n<title>");
811
812 if (local) {
813 oputs(local);
814 oputs("@");
815 oputs(host);
816 oputs(": ");
817 }
818 if (t) oputs(t);
819 if (s) html_put(s,l);
820 oputs("</title>\n");
821 if (class && *class && stylesheet && *stylesheet) {
822 oputs("<link href=\"");
824 oputs("\" rel=\"stylesheet\" type=\"text/css\" />\n");
825 }
826 if (!flagrobot) /* robot access allowed to follow */
827 oputs("<meta name=\"robots\" content=\"noindex\" />\n");
828 if (flagrobot < 2)
829 oputs("<meta name=\"robots\" content=\"nofollow\" />\n");
830 if (flagspecial & SPC_BASE)
831 oput(base.s,base.len);
832 oputs("</head>\n");
833 if (class && *class) {
834 oputs("<body class=");
835 oputs(class);
836 oputs(">\n");
837 } else
838 oputs("<body>\n");
839
840}
841
842void html_footer(int flagspecial)
843{
844 if ((flagspecial & SPC_BANNER) && banner && *banner) {
845 oputs("<div class=\"banner\">\n");
846 if (*banner == '<') {
847 oputs(banner);
848 oputs("</div>\n");
849 } else {
850 // system(banner); // still missing; cat iframe?
851 logmsg(WHO,100,ERROR,"Sorry - banner programs not supported");
852 }
853 }
854 oputs("</body>\n</html>\n");
855 buffer_flush(&bo);
856}
857
858/* DATE functions */
859
864
865void date_links(struct msginfo *infop,unsigned long d,char direction)
866{
867 oput(url.s,url.len);
868 navstr[0] = VIEW[VIEW_DATE];
869 navstr[1] = VIEW[VIEW_DATE];
870 navstr[2] = DIRECT[direction + 1];
871 oputs(navstr);
872 if (direction == DIRECT_LAST)
873 oput("0",1); /* suppress msgnum to avoid going there */
874 else
875 oput(strnum,fmt_ulong(strnum,infop->target));
876 oputs(":"); //+
877 oput(strnum,fmt_ulong(strnum,d));
878 oputs("#b\">");
879
880 switch (direction) {
881 case DIRECT_SAME: if (dateline(&dtline,d) < 0) die_nomem();
882 oput(dtline.s,dtline.len); break;
883 case DIRECT_PREV: oputs("[&larr;]"); break;
884 case DIRECT_NEXT: oputs("[&rarr;]"); break;
885 case DIRECT_FIRST: oputs("[&lArr;]"); break;
886 case DIRECT_LAST: oputs("[&rArr;]"); break;
887 }
888 oputs("</a>");
889}
890
895
896void finddate(struct msginfo *infop)
897{
898 DIR *archivedir;
899 direntry *d;
900 unsigned long ddate, startdate;
901 unsigned long below, above;
902
903 below = 0L;
904 above = MAXULONG; /* creating a Y 0xffffff problem */
905 startdate = infop->date;
906 archivedir = opendir("archive/threads/");
907 if (!archivedir)
908 if (errno != ENOENT)
909 logmsg(WHO,111,ERROR,B(ERR_OPEN,"/archive/threads: ",dir));
910 else
911 logmsg(WHO,100,ERROR,B(ERR_OPEN,"/archive/threads: ",dir));
912
913 while ((d = readdir(archivedir))) { /* dxxx/ */
914 if (str_equal(d->d_name,".")) continue;
915 if (str_equal(d->d_name,"..")) continue;
916 scan_ulong(d->d_name,&ddate);
917 if (!ddate) continue; /* just in case some smart guy ... */
918 if (startdate) {
919 if (ddate > startdate && ddate < above) above = ddate;
920 if (ddate < startdate && ddate > below) below = ddate;
921 } else {
922 if (ddate < above) above = ddate;
923 if (ddate > below) below = ddate;
924 }
925 }
926 closedir(archivedir);
927
928 if (infop->direction == DIRECT_NEXT && (above != MAXULONG || !below))
929 /* we always give a valid date as long as there is at least one */
930 infop->date = above;
931 else
932 infop->date = below;
933 return;
934}
935
936void latestdate(struct msginfo *infop,int flagfail)
937{
938 if (!flagfail) {
939 datetime_tai(&dt,now());
940 infop->date = ((unsigned long) dt.year + 2000) * 100 + dt.mon + 1;
941 } else {
942 infop->date = 0;
943 infop->direction = DIRECT_PREV; // DIRECT_PREV = -1;!!
944 finddate(infop);
945 }
946}
947
948void firstdate(struct msginfo *infop)
949{
950 infop->date = 0;
951 infop->direction = DIRECT_NEXT;
952 finddate(infop);
953}
954
960
961void gtdate(struct msginfo *infop,int flagfail)
962{
963 if (!flagfail) { /* guess */
964 if (infop->direction == DIRECT_NEXT) {
965 infop->date++;
966 if (infop->date%100 > 12) infop->date += (100 - 12);
967 } else if (infop->direction == DIRECT_PREV) {
968 infop->date--;
969 if (!infop->date%100) infop->date -= (100 - 12);
970 }
971 } else
972 finddate(infop);
973 return;
974}
975
976void index_links(struct msginfo *infop)
977{
978 unsigned long tmpmsg;
979
980 tmpmsg = infop->target;
981 infop->target = 1;
982 oputs("<div class=idxlinks><strong>\n");
984 oputs(">[&lArr;]</a>\n");
985 if (tmpmsg >= 100) infop->target = tmpmsg - 100;
987 oputs(">[&larr;]</a>\n");
988 infop->target = tmpmsg + 100;
990 oputs(">[&rarr;]</a>\n");
991 infop->target = MAXULONG;
993 oputs(">[&rArr;]</a> |\n");
994 infop->target = tmpmsg;
996 oputs(">[Topics by date]</a>\n");
997 subfaq_link();
998 home_link();
999 oputs("</strong></div>\n");
1000}
1001
1002int show_index(struct msginfo *infop)
1003{
1004 unsigned long thismsg;
1005 unsigned int pos,l;
1006 char ch;
1007
1009
1010 if ((fd = open_read(fname.s)) == -1) {
1011 if (errno == ENOENT)
1012 return 0;
1013 else
1014 logmsg(WHO,111,ERROR,B(ERR_OPEN,fname.s));
1015 }
1016
1017 buffer_init(&bi,buffer_unixread,fd,inbuf,sizeof(inbuf));
1018 if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,(unsigned long) (infop->target/100))))
1019 die_nomem();
1020 if (!stralloc_cats(&line,"xx")) die_nomem();
1021 html_header("Messages ",line.s,line.len,"idxbody",SPC_BANNER | SPC_BASE);
1022 index_links(infop);
1023 oputs("<div><hr /></div><h1 id=\"idxhdr\">");
1024 oputs("Messages ");
1025 oput(line.s,line.len);
1026 oputs("</h1>\n");
1027 oputs("<div class=\"idx\"><hr />\n");
1028
1029 for (;;) {
1030 if (getln(&bi,&line,&match,'\n') == -1)
1031 logmsg(WHO,111,ERROR,B(ERR_READ,fname.s));
1032 if (!match) break;
1033 pos = scan_ulong(line.s,&thismsg);
1034 l = pos;
1035 ch = line.s[pos++];
1036 pos++;
1037 if (line.len < pos + 1 + HASHLEN)
1038 logmsg(WHO,100,ERROR,"index line with truncated subject entry");
1039 if (!stralloc_copyb(&subject,line.s + pos,HASHLEN)) die_nomem();
1040 if (!stralloc_0(&subject)) die_nomem();
1041 infop->view = VIEW_TOPIC;
1042 infop->subject = subject.s;
1043 oput(strnum,fmt_uint0(strnum,(unsigned int) thismsg % 100,2));
1044 oputs(": "); // +
1045 alink(infop,VIEW_MESSAGE,VIEW_TOPIC,thismsg,line.s + pos,line.len - pos - 1);
1046 oputs("\n");
1047 if (ch == ':') { //+
1048 if (getln(&bi,&line,&match,'\n') == -1)
1049 logmsg(WHO,111,ERROR,B(ERR_READ,fname.s));
1050 if (!match) break;
1051 pos = byte_chr(line.s,line.len,';');
1052 if (pos != line.len) {
1053 infop->date = date2yyyymm(line.s);
1054 oputs("(");
1055 alink(infop,VIEW_AUTHOR,VIEW_AUTHOR,thismsg,line.s + pos + 1,line.len - pos - 2);
1056 oputs(")<br>\n");
1057 }
1058 }
1059 }
1060
1061 close(fd);
1062 oputs("\n<hr /></div>\n");
1063 index_links(infop);
1065 return 1;
1066}
1067
1068void object_links(struct msginfo *infop,char item)
1069{
1070
1071 oputs("<div class=objlinks><strong>\n");
1072 if (item == VIEW_DATE) {
1073 date_links(infop,0,DIRECT_FIRST);
1074 date_links(infop,infop->date,DIRECT_PREV);
1075 date_links(infop,infop->date,DIRECT_NEXT);
1076 date_links(infop,0,DIRECT_LAST);
1077 oputs("\n");
1078 } else {
1079 if (!infop->target) infop->view = VIEW_DATE;
1081 oputs(">[Topics by date]</a>\n");
1082 }
1083 if (item != VIEW_INDEX) {
1085 oputs(">[Messages by date]</a>\n");
1086 }
1087 home_link();
1088 subfaq_link();
1089 oputs("</strong></div>\n");
1090}
1091
1097
1098int show_object(struct msginfo *infop,char item)
1099{
1100 unsigned long lastdate, thisdate, thismsg;
1101 char linkitem;
1102 char targetitem;
1103 unsigned int pos;
1104
1105
1106 lastdate = 0L;
1107 targetitem = VIEW_MESSAGE; /* default message is target */
1108
1109 switch (item) {
1110 case VIEW_TOPIC: if (!uri2fn(&fname,VIEW_TOPIC,0L,infop->subject)) return 0; break;
1111 case VIEW_AUTHOR: if (!uri2fn(&fname,VIEW_AUTHOR,0L,infop->author)) return 0; break;
1112 case VIEW_DATE: if (!uri2fn(&fname,VIEW_DATE,infop->date,"")) return 0; break;
1113 default: die_prog("Bad object type in show_object");
1114 }
1115
1116 if ((fd = open_read(fname.s)) == -1) {
1117 if (errno == ENOENT)
1118 return 0;
1119 else
1120 logmsg(WHO,111,ERROR,B(ERR_OPEN,fname.s));
1121 }
1122
1123 buffer_init(&bi,buffer_unixread,fd,inbuf,sizeof(inbuf));
1124 if (item != VIEW_DATE) {
1125 if (getln(&bi,&line,&match,'\n') == -1) /* read subject */
1126 logmsg(WHO,111,ERROR,B(ERR_READ,fname.s));
1127 if (!match || line.len < HASHLEN + 2)
1128 logmsg(WHO,111,ERROR,B(ERR_READ,fname.s," nothing there"));
1129 }
1130 switch (item) {
1131 case VIEW_TOPIC: html_header("Topics on: ",line.s + HASHLEN + 1,
1132 line.len - HASHLEN - 2,"subjbody",SPC_BANNER | SPC_BASE);
1133 object_links(infop,item);
1134 oputs("<div><hr /></div><h1>On: ");
1135 oput(line.s + HASHLEN + 1,line.len - HASHLEN - 2);
1136 oputs("</h1>\n");
1137 break;
1138 case VIEW_AUTHOR: html_header("Posts by: ",line.s + HASHLEN + 1,
1139 line.len - HASHLEN - 2,"authbody",SPC_BANNER | SPC_BASE);
1140 object_links(infop,item);
1141 oputs("<hr><h1>By: ");
1142 oput(line.s + HASHLEN + 1,line.len - HASHLEN - 2);
1143 oputs("</h1>\n");
1144 break;
1145 /* targetitem = VIEW_TOPIC;*/ /* thread index is target */
1146 case VIEW_DATE: thisdate = infop->date;
1147 if (dateline(&dtline,infop->date) < 0) die_nomem();
1148 html_header("Topics for ",
1149 dtline.s,dtline.len,"threadsbody",SPC_BANNER | SPC_BASE);
1150 object_links(infop,item);
1151 oputs("<hr>\n<h1>Topics for ");
1152
1153 oput(dtline.s,dtline.len);
1154 oputs("</h1>\n");
1155 break;
1156 default: die_prog("unrecognized object type in show_object");
1157 }
1158
1159 oputs("<div class=obj>\n");
1160
1161
1162 for (;;) {
1163 if (getln(&bi,&line,&match,'\n') == -1) /* read subject */
1164 logmsg(WHO,111,ERROR,B(ERR_READ,fname.s));
1165 if (!match) break;
1166 pos = scan_ulong(line.s,&thismsg);
1167 if (line.s[pos++] != ':')
1168 logmsg(WHO,100,ERROR,B("entry in ",fname.s," lacks message number"));
1169 if (item != VIEW_DATE) { /* no date for threads by date */
1170 pos += scan_ulong(line.s + pos,&thisdate);
1171 infop->date = thisdate;
1172 if (line.s[pos++] != ':')
1173 logmsg(WHO,100,ERROR,B("entry in ",fname.s," lacks date"));
1174 }
1175 if (line.len < pos + HASHLEN + 2)
1176 logmsg(WHO,100,ERROR,B("entry in ",fname.s," lacks hash"));
1177
1178 if (thisdate != lastdate) {
1179 oputs("</ul><hr>\n"); // first </ul> ingnored; why bother? (a must)
1180 // oput(dtline.s,dtline.len);
1181 lastdate = thisdate;
1182 oputs("<ul>\n");
1183 }
1184 oputs("<li>");
1185 if (item == VIEW_TOPIC)
1186 linkitem = VIEW_AUTHOR;
1187 else
1188 linkitem = VIEW_TOPIC;
1189 alink(infop,targetitem,linkitem,thismsg,line.s + pos,line.len - pos - 1);
1190 oputs("</li>\n"); /* li terminated at each link */
1191 }
1192 close(fd);
1193
1194 oputs("</ul>\n");
1195 if (!infop->target) oputs("<a name=b></a>");
1196 oputs("<hr>\n");
1197 object_links(infop,item);
1199
1200 return 1;
1201}
1202
1204{
1205 mime_current->charset.len = 0; /* exist but need emptying */
1206 mime_current->boundary.len = 0;
1207 mime_current->ctype.len = 0;
1208 mime_current->mimetype = MIME_NONE;
1209 mime_current->ctenc = CTENC_NONE;
1210 mime_current->cs = CS_NONE;
1211}
1212
1214{
1216 if (mime_current)
1217 mime_current = mime_current->next;
1218 if (!mime_current) {
1219 if (!(mime_current = (mime_info *) alloc(sizeof (mime_info))))
1220 die_nomem();
1221 mime_current->charset = sainit; /* init */
1222 mime_current->boundary = sainit;
1223 mime_current->ctype = sainit;
1224 mime_current->next = (mime_info *) 0;
1225 mime_current->previous = mime_tmp;
1226 }
1227 clear_mime();
1228 if (mime_tmp)
1229 mime_current->level = mime_tmp->level + 1;
1230 else
1231 mime_current->level = 1;
1232}
1233
1239
1240void mime_getarg(stralloc *sa,char **s, unsigned int *l)
1241{
1242 char *cp, *cpafter, *cpnext;
1243
1244 if (!*l || !**s) return;
1245 if (**s == '"') {
1246 (*s)++; (*l)--;
1247 cp = *s; cpnext = cp + *l; cpafter = cpnext;
1248 while (cp < cpafter) {
1249 if (*cp == '"') break;
1250 cp++;
1251 }
1252 cpnext = cp;
1253 } else {
1254 cp = *s; cpnext = cp + *l; cpafter = cpnext;
1255 while (cp < cpafter) {
1256 if (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == ';') break;
1257 cp++;
1258 }
1259 cpnext = cp;
1260 }
1261
1262 if (!stralloc_copyb(sa,*s,cp - *s)) die_nomem();
1263 *l = cpafter - cpnext; /* always >= 0 */
1264 *s = cpnext;
1265
1266 return;
1267}
1268
1269void decode_mime_type(char *s,unsigned int l,unsigned int flagmime)
1270{
1271 char *st;
1272 unsigned int r, lt;
1273
1274 if (!flagmime || !l) { /* treat non-MIME as plain text */
1275 mime_current->mimetype = MIME_TEXT_PLAIN;
1276 if (!stralloc_copys(&curcharset,charset)) die_nomem();
1277 /* should be us-ascii, but this is very likely better */
1278 return;
1279 }
1281 while (l && (*s == ' ' || *s == '\t')) { s++; l--; } /* skip LWSP */
1282 mime_getarg(&(mime_current->ctype),&s,&l);
1283 st = mime_current->ctype.s;
1284 lt = mime_current->ctype.len;
1285 if (case_startb(st,lt,"text")) { /* text types */
1286 r = MIME_TEXT; st += 4; lt -= 4;
1287 if (case_startb(st,lt,"/plain")) {
1288 r = MIME_TEXT_PLAIN; st += 6; lt -= 6;
1289 } else if (case_startb(st,lt,"/html")) {
1290 r = MIME_TEXT_HTML; st += 5; lt -= 5;
1291 } else if (case_startb(st,lt,"/enriched")) {
1292 r = MIME_TEXT_ENRICHED; st += 9; lt -= 9;
1293 } else if (case_startb(st,lt,"/x-vcard")) {
1294 r = MIME_TEXT_ENRICHED; st += 8; lt -= 8;
1295 }
1296 } else if (case_startb(st,lt,"multipart")) { /* multipart types */
1297 r = MIME_MULTI; st += 9; lt -= 9;
1298 if (case_startb(st,lt,"/alternative")) {
1299 r = MIME_MULTI_ALTERNATIVE; st += 12; lt -= 12;
1300 } else if (case_startb(st,lt,"/mixed")) {
1301 r = MIME_MULTI_MIXED; st += 6; lt -= 6;
1302 } else if (case_startb(st,lt,"/digest")) {
1303 r = MIME_MULTI_DIGEST; st += 7; lt -= 7;
1304 } else if (case_startb(st,lt,"/signed")) {
1305 r = MIME_MULTI_SIGNED; st += 7; lt -= 7;
1306 }
1307 } else if (case_startb(st,lt,"message")) { /* message types */
1308 r = MIME_MESSAGE; st += 7; lt -= 7;
1309 if (case_startb(st,lt,"/rfc822")) {
1310 r = MIME_MESSAGE_RFC822; st += 7; lt -= 7;
1311 }
1312 }
1313 mime_current->mimetype = r;
1314
1315 while (l) {
1316 while (l && (*s == ' ' || *s == '\t' || *s == ';' || *s == '\n')) {
1317 s++; l--; } /* skip ;LWSP */
1318 if (case_startb(s,l,"boundary=")) {
1319 s += 9; l -= 9;
1320 mime_getarg(&(mime_current->boundary),&s,&l);
1321 } else if (case_startb(s,l,"charset=")) {
1322 s += 8; l -= 8;
1323 mime_getarg(&(mime_current->charset),&s,&l);
1324 cs = decode_charset(mime_current->charset.s,
1325 mime_current->charset.len);
1326 if (cs == CS_BAD) cs = csbase; /* keep base cs */
1327 else
1328 if (!stralloc_copy(&curcharset,&mime_current->charset)) die_nomem();
1329 } else { /* skip non LWSP */
1330 for (;;) {
1331 if (!l) break;
1332 if (*s == '"') {
1333 s++, l--;
1334 while (l && *s != '"') { s++, l--; }
1335 if (l) { s++, l--; }
1336 break;
1337 } else {
1338 if (!l || *s == ' ' || *s == '\t' || *s == '\n') break;
1339 s++; l--;
1340 }
1341 }
1342 }
1343 }
1344
1345 return;
1346}
1347
1348void decode_transfer_encoding(char *s,unsigned int l)
1349{
1350 unsigned int r;
1351 mime_current->ctenc = CTENC_NONE;
1352 if (!l || (mime_current->mimetype & MIME_MULTI)) return;
1353 /* base64/QP ignored for multipart */
1354 r = CTENC_NONE;
1355 while (l && (*s == ' ' || *s == '\t')) { s++; l--; } /* skip LWSP */
1356 s[l-1] = 0;
1357 if (case_startb(s,l,"quoted-printable")) {
1358 r = CTENC_QP;
1359 } else if (case_startb(s,l,"base64")) {
1360 r = CTENC_BASE64;
1361 }
1362 mime_current->ctenc = r;
1363 return;
1364}
1365
1370
1372{
1373 mime_info *tmp;
1374
1375 if (*line.s != '-' || line.s[1] != '-') return 0;
1376 tmp = mime_current;
1377 while (tmp) {
1378 if (tmp->boundary.len) {
1379 if (line.len > tmp->boundary.len + 2 &&
1380 !case_diffb(line.s+2,tmp->boundary.len,tmp->boundary.s)) {
1381 if (line.s[tmp->boundary.len + 2] == '-' &&
1382 line.s[tmp->boundary.len + 3] == '-') { /* end */
1383 mime_current = tmp;
1384 clear_mime();
1385 return 2;
1386 } else { /* start */
1387 mime_current = tmp;
1388 new_mime();
1389 return 1;
1390 }
1391 }
1392 }
1393 tmp = tmp->previous;
1394 }
1395 if (!stralloc_copys(&curcharset,charset)) die_nomem();
1396 /* suprtfluous since header done by now */
1397 cs = csbase;
1398
1399 return 0;
1400}
1401
1419
1420void start_message_page(struct msginfo *infop)
1421{
1422 if (!stralloc_copyb(&decline,strnum,fmt_ulong(strnum,infop->target)))
1423 die_nomem();
1424 if (!stralloc_cats(&decline,":")) die_nomem();
1425 if (!stralloc_0(&decline)) die_nomem();
1426 decode_hdr(hdr[HDR_SUBJECT - 1].s,hdr[HDR_SUBJECT - 1].len,&line);
1427 if (!mime_current)
1428 new_mime(); /* allocate */
1429 else
1430 clear_mime();
1432 html_header(decline.s,line.s,line.len - 1,"msgbody",SPC_BASE);
1433 decline.len = 0; /* reset */
1434 message_links(infop); // top page
1435 oputs("<div class=\"message\">\n");
1436}
1437
1445
1446void show_part(struct msginfo *infop,int flagshowheaders,int flagstartseen)
1447{
1448 char *cp;
1449 int flaginheader;
1450 int whatheader;
1451 int flaggoodfield;
1452 int flaghtml;
1453 int btype, i;
1454 unsigned int colpos;
1455 char linetype;
1456
1457 flaginheader = 1;
1458 for (i = 0; i < NO_HDRS; i++) hdr[i].len = 0;
1459 flaggoodfield = 1;
1460 match = 1;
1461 recursion_level++; /* one up */
1462
1463 for (flaghtml = whatheader = 0;;) {
1464 if (!match) return;
1465 if (getln(&bi,&line,&match,'\n') == -1)
1466 logmsg(WHO,111,ERROR,B(ERR_READ,fname.s));
1467 if (!match) return;
1468 if ((btype = check_boundary())) {
1469 if (decline.len) { /* flush last line that doesn't */
1470 if (flaghtml) /* end in \n for QP/base64 */
1471 oput(decline.s,decline.len);
1472 else
1473 anchor_put(decline.s,decline.len);
1474 decline.len = 0;
1475 }
1476 if (flagpre) { /* ending part was <pre> */
1477 oputs("</pre>");
1478 toggle_flagpre(0);
1479 }
1480 if (mime_current->level < recursion_level) return;
1481 if (btype == 1) {
1482 flagstartseen = 1;
1483 flaggoodfield = 1;
1484 flaginheader = 1;
1485 } else
1486 flagstartseen = 0;
1487 continue;
1488 }
1489 if (!flagstartseen) continue; /* skip to start */
1490 if (flaginheader) {
1491 if (line.len == 1) {
1492 if (flagshowheaders) { /* rfc822hdr only */
1493 if (flagtoplevel)
1494 start_message_page(infop); /* so we can put subj in TITLE */
1495 oputs("<div class=\"rfc822hdr\"><hr />\n");
1496
1497 for (i = 0; i < NO_HDRS; i++) {
1498 if (!hdr[i].len || !headers_shown[i]) continue;
1499 if (i == HDR_SUBJECT - 1 && flagtoplevel)
1500 oputs("<span class=\"subject\">");
1501 oputs("<em>");
1502 oputs(constmap_get(&headermap,i + 1));
1503 oputs(":</em>");
1504 decode_hdr(hdr[i].s,hdr[i].len,&line);
1505 if (i == HDR_SUBJECT - 1 && flagtoplevel) {
1506 oputs("<a class=\"relk\" href=\"mailto:");
1507 oputs(local);
1508 oput("@",1);
1509 oputs(host);
1510 oputs("?subject=");
1511 urlencode_put(line.s + 1,line.len - 2);
1512 oputs("\">");
1513 }
1514 if (flagobscure && i == HDR_FROM - 1) {
1515 int k;
1516 oputs(" ");
1517 k = author_name(&cp,line.s,line.len);
1518 decode_hdr(cp,k,&decline);
1519 html_put(decline.s,decline.len);
1520 } else {
1521 decode_hdr(hdr[i].s,hdr[i].len,&decline);
1522 html_put(decline.s,decline.len - 1);
1523 }
1524 if (i == HDR_SUBJECT - 1 && flagtoplevel)
1525 oputs("</a></span>");
1526 oputs("\n<br />"); // FIXME
1527 }
1528
1529 oputs("</div>\n");
1530 }
1531 flaginheader = 0;
1532 flagtoplevel = 0;
1533 flaggoodfield = 1;
1534 flaghtml = 0;
1535 if (!flagmime)
1536 flagmime = hdr[HDR_VERSION - 1].len; /* MIME-Version header */
1539 content.len = 0; encoding.len = 0;
1540
1541 switch (mime_current->mimetype) {
1542 case MIME_MULTI_SIGNED:
1543 case MIME_MULTI_MIXED:
1545 case MIME_MULTI_DIGEST: show_part(infop,0,0);
1547 flagstartseen = 0;
1548 flaginheader = 1;
1549 continue; // we are in a for loop
1550 case MIME_MESSAGE_RFC822: oputs("\n<pre>");
1551 toggle_flagpre(1);
1552 flagshowheaders = 1;
1553 flaginheader = 1;
1554 flagmime = 0;/* need new MIME-Version header */
1555 continue;
1556 case MIME_TEXT_HTML: if (flagshowhtml) {
1557 oputs("<hr />\n");
1558 flaghtml = 1;
1559 } else {
1560 oputs("<strong>[\"");
1561 oput(mime_current->ctype.s,mime_current->ctype.len);
1562 oputs("\" not shown]</strong>\n");
1563 flaggoodfield = 0; /* hide */
1564 }
1565 continue;
1566 case MIME_TEXT_PLAIN:
1567 case MIME_TEXT: /* in honor of Phil using "text" on the qmail list and rfc2045:5.2 */
1568 case MIME_NONE: oputs("<hr>\n<pre>\n");
1569 toggle_flagpre(1);
1570 continue;
1571 case MIME_TEXT_VCARD:
1572
1573 /* application/octetstream...*/
1574 default: oputs("<hr><strong>[\"");
1575 oput(mime_current->ctype.s,mime_current->ctype.len);
1576 oputs("\" not shown]</strong>\n");
1577 flaggoodfield = 0; /* hide */
1578 continue;
1579 }
1580 } else if (line.s[0] != ' ' && line.s[0] != '\t') {
1581 linetype = ' ';
1582 flaggoodfield = 0;
1583 colpos = byte_chr(line.s,line.len,':'); // FIXME
1584 if ((whatheader = constmap_index(&headermap,line.s,colpos))) {
1585 flaggoodfield = 1;
1586 if (!stralloc_copyb(&hdr[whatheader - 1],line.s + colpos + 1,
1587 line.len - colpos - 1)) die_nomem();
1588 }
1589 } else {
1590 if (whatheader)
1591 if (!stralloc_catb(&hdr[whatheader - 1],line.s,line.len))
1592 die_nomem();
1593 }
1594 } else {
1595 if (flaggoodfield) {
1596 if (mime_current->ctenc) {
1597 if (mime_current->ctenc == CTENC_QP)
1598 decode_qp(line.s,line.len,&decline);
1599 else
1600 decode_b64(line.s,line.len,&decline);
1601 if (decline.s[decline.len - 1] == '\n') { /* complete line */
1602 if (!stralloc_copy(&line,&decline)) die_nomem();
1603 decline.len = 0;
1604 } else /* incomplete - wait for next */
1605 line.len = 0; /* in case URL is split */
1606 }
1607 if (flaghtml)
1608 oput(line.s,line.len);
1609 else {
1610 anchor_put(line.s,line.len); /* body */
1611 }
1612 }
1613 }
1614 }
1615}
1616
1617int show_message(struct msginfo *infop)
1618{
1619 char *psz;
1620
1621 if(!stralloc_copys(&headers,(char *) headers_used)) die_nomem();
1622 if (!stralloc_0(&headers)) die_nomem();
1623 psz = headers.s;
1624
1625 while (*psz) {
1626 if (*psz == '\\') *psz = '\0';
1627 ++psz;
1628 }
1629 if (!constmap_init(&headermap,headers.s,headers.len,0))
1630 die_nomem();
1631
1633
1634 if ((fd = open_read(fname.s)) == -1) {
1635 if (errno == ENOENT)
1636 return 0;
1637 else
1638 logmsg(WHO,111,ERROR,B(ERR_OPEN,fname.s));
1639 }
1640
1641 buffer_init(&bi,buffer_unixread,fd,inbuf,sizeof(inbuf));
1642 toggle_flagpre(0);
1643 recursion_level = 0; /* recursion level for show_part */
1644 flagmime = 0; /* no active mime */
1645 flagtoplevel = 1; /* top message/rfc822 get special rx */
1646 new_mime(); /* initiate a MIME info storage slot */
1647
1648 show_part(infop,1,1); /* do real work, including html header etc */
1649 if (flagpre) oputs("</pre>\n"); // output done; close FD
1650 close(fd);
1651 oputs("<hr /></div>\n");
1652 message_links(infop); // bottom page
1653 html_footer(0);
1654
1655 return 1;
1656}
1657
1658char decode_item(char ch)
1659{
1660 switch (ch) {
1661 case 'm': return VIEW_MESSAGE;
1662 case 'a': return VIEW_AUTHOR;
1663 case 's': return VIEW_TOPIC;
1664 case 'd': return VIEW_DATE;
1665 case 'i': return VIEW_INDEX;
1666 default: cgierr("Navigation command contains ","illegal item code","");
1667 }
1668 return 0; /* never reached */
1669}
1670
1671char decode_direction(char ch)
1672{
1673 switch (ch) {
1674 case 's': return DIRECT_SAME;
1675 case 'n': return DIRECT_NEXT;
1676 case 'p': return DIRECT_PREV;
1677 default: cgierr("Navigation command contains ","illegal direction code","");
1678 }
1679 return 0; /* never reached */
1680}
1681
1694
1695int decode_cmd(char *cmd,struct msginfo *infop)
1696{
1697 char ch;
1698
1699 infop->source = 0L;
1700 infop->date = 0L;
1701 infop->author = (char *)0;
1702 infop->subject = (char *)0;
1703 infop->cgiarg = (char *)0;
1704
1705 if (!cmd || !*cmd) { /* main index */
1706 infop->item = VIEW_DATE;
1707 infop->view = VIEW_DATE;
1708 infop->direction = DIRECT_SAME;
1709 latestdate(&msginfo,0);
1710 infop->target = MAXULONG;
1711 return 1;
1712 }
1713
1714 ch = *(cmd++);
1715 if (ch >= '0' && ch <= '9') { /* numeric - simplified cntl: msgnum ... */
1716 cmd--;
1717 infop->item = VIEW_MESSAGE;
1718 infop->view = VIEW_MESSAGE;
1719 infop->direction = DIRECT_SAME;
1720 } else { /* item|view|direction:msgnum ... */
1721 infop->item = decode_item(ch);
1722 ch = *(cmd++);
1723 infop->view = decode_item(ch);
1724 ch = *(cmd++);
1725 infop->direction = decode_direction(ch);
1726 if (*(cmd++) != ':') return 0; //+
1727 }
1728 cmd += scan_ulong(cmd,&(infop->source));
1729 if (*(cmd++) != ':') return 0; //+
1730 if (*cmd >= '0' && *cmd <= '9') { /* numeric cmd hint [date] */
1731 cmd += scan_ulong(cmd,&(infop->date));
1732 if (!*cmd++) return 1; /* skip any char - should be ':' unless NUL */
1733 }
1734 if (checkhash(cmd)) { /* Ignore if illegal rather than complaining*/
1735 if (!stralloc_copyb(&charg,cmd,HASHLEN)) die_nomem();
1736 if (!stralloc_0(&charg)) die_nomem();
1737 infop->cgiarg = charg.s;
1738 }
1739 return 1;
1740}
1741
1742int msg2hash(struct msginfo *infop)
1743{
1744 unsigned int pos;
1745 unsigned long tmpmsg;
1746
1747 if (!infop->source) die_prog("source is 0 in msg2hash");
1748
1749 uri2fn(&fname,VIEW_INDEX,infop->source,"");
1750 if ((fd = open_read(fname.s)) == -1) {
1751 if (errno == ENOENT)
1752 return 0;
1753 else
1754 logmsg(WHO,111,ERROR,B(ERR_OPEN,fname.s));
1755 }
1756
1757 buffer_init(&bi,buffer_unixread,fd,inbuf,sizeof(inbuf));
1758 for (;;) {
1759 if (getln(&bi,&line,&match,'\n') == -1)
1760 logmsg(WHO,111,ERROR,B(ERR_READ,"index "));
1761 if (!match)
1762 return 0; /* didn't find message */
1763
1764 if (*line.s == '\t') continue; /* author line */
1765 pos = scan_ulong(line.s,&tmpmsg);
1766
1767 if (tmpmsg == infop->source) {
1768 if (line.s[pos++] != ':' || line.s[pos++] != ' ')
1769 logmsg(WHO,100,ERROR,B(ERR_SYNTAX,fname.s," missing subject separator"));
1770 if (line.len < HASHLEN + pos)
1771 logmsg(WHO,100,ERROR,B(ERR_SYNTAX,fname.s," missing subject hash"));
1772 if (!stralloc_copyb(&subject,line.s+pos,HASHLEN)) die_nomem();
1773 if (!stralloc_0(&subject)) die_nomem();
1774 infop->subject = subject.s;
1775 if (getln(&bi,&line,&match,'\n') == -1)
1776 logmsg(WHO,111,ERROR,B(ERR_READ,"index"));
1777 if (!match)
1778 logmsg(WHO,100,ERROR,B(ERR_SYNTAX,fname.s," author info missing. Truncated?"));
1779 pos = byte_chr(line.s,line.len,';');
1780 if (pos == line.len)
1781 logmsg(WHO,100,ERROR,B(ERR_SYNTAX,fname.s,"missing ';' after date"));
1782 if (pos > 1)
1783 infop->date = date2yyyymm(line.s+1); /* ';' marks end ok */
1784 pos++;
1785 if (line.len < HASHLEN + pos)
1786 logmsg(WHO,100,ERROR,B(ERR_SYNTAX,fname.s," missing author hash"));
1787 if (!stralloc_copyb(&author,line.s + pos,HASHLEN)) die_nomem();
1788 if (!stralloc_0(&author)) die_nomem();
1789 infop->author = author.s;
1790 close(fd);
1791
1792 return 1; /* success */
1793 }
1794 }
1795
1796 close(fd);
1797 return 0; /* failed to match */
1798}
1799
1810
1811void set_message(struct msginfo *infop)
1812{
1813 if (infop->direction == DIRECT_SAME) {
1814 infop->target = infop->source;
1815 return;
1816 }
1817 if ((fd = open_read(fname.s)) == -1) {
1818 if (errno == ENOENT)
1819 logmsg(WHO,100,ERROR,B(ERR_OPEN,fname.s," in set_message. Rerun ezmlm-archive!"));
1820 else
1821 logmsg(WHO,111,ERROR,B(ERR_OPEN,fname.s));
1822 }
1823
1824 buffer_init(&bi,buffer_unixread,fd,inbuf,sizeof(inbuf));
1825 if (infop->view != VIEW_DATE) {
1826 if (getln(&bi,&line,&match,'\n') == -1) /* first line */
1827 logmsg(WHO,111,ERROR,B(ERR_READ,fname.s));
1828 if (!match)
1829 logmsg(WHO,100,ERROR,B(ERR_SYNTAX,fname.s,": first line missing"));
1830 }
1831 msgnav[3] = 0L; /* next */
1832 msgnav[4] = 0L; /* after */
1833 infop->target = 0L;
1834
1835 for (;;) {
1836 if (getln(&bi,&line,&match,'\n') == -1)
1837 logmsg(WHO,111,ERROR,B(ERR_READ,fname.s));
1838 if (!match) break;
1839 msgnav[0] = msgnav[1];
1840 msgnav[1] = msgnav[2];
1841 pos = scan_ulong(line.s,&(msgnav[2]));
1842 if (infop->direction == DIRECT_FIRST && infop->view == VIEW_DATE) {
1843 if (pos + HASHLEN + 1 < line.len)
1844 if (!stralloc_copyb(&subject,line.s + pos + 1,HASHLEN)) die_nomem();
1845 if (!stralloc_0(&subject)) die_nomem();
1846 break;
1847 }
1848 if (msgnav[2] == infop->source) {
1849 if (getln(&bi,&line,&match,'\n') == -1)
1850 logmsg(WHO,111,ERROR,B(ERR_READ,fname.s));
1851 if (!match) break;
1852 scan_ulong(line.s,&(msgnav[3]));
1853 if (getln(&bi,&line,&match,'\n') == -1)
1854 logmsg(WHO,111,ERROR,B(ERR_READ,fname.s));
1855 if (!match) break;
1856 scan_ulong(line.s,&(msgnav[4]));
1857 break;
1858 }
1859 }
1860 close(fd);
1861
1862 switch (infop->view) {
1863 case VIEW_AUTHOR: infop->authnav = msgnav + 2 + infop->direction;
1864 infop->target = *(infop->authnav);
1865 infop->subject = (char *)0; /* what we know is not for this msg */
1866 infop->date = 0;
1867 break;
1868 case VIEW_TOPIC: if (infop->direction == DIRECT_FIRST)
1869 infop->target = msgnav[2];
1870 else {
1871 infop->subjnav = msgnav + 2 + infop->direction;
1872 infop->target = *(infop->subjnav);
1873 }
1874 infop->author = (char *)0; /* what we know is not for this msg */
1875 infop->date = 0;
1876 break;
1877 case VIEW_DATE: infop->target = msgnav[2];
1878 infop->subject = (char *)0; /* what we know is not for this msg */
1879 infop->author = (char *)0; /* what we know is not for this msg */
1880 break;
1881 default: die_prog("Bad item in set_message");
1882 }
1883 return;
1884}
1885
1886void auth2msg(struct msginfo *infop)
1887{
1888 if (!infop->author) die_prog("no such author in authmsg");
1889 if (!uri2fn(&fname,VIEW_AUTHOR,0L,infop->author)) die_prog("auth2msg");
1890 set_message(infop);
1891}
1892
1893void subj2msg(struct msginfo *infop)
1894{
1895 if (!infop->subject) die_prog("no such subject in subj2msg");
1896 if (!uri2fn(&fname,VIEW_TOPIC,0L,infop->subject)) die_prog("subj2msg");
1897 set_message(infop);
1898}
1899
1900void date2msg(struct msginfo *infop)
1901/* this is all a terrible hack */
1902{
1903 uri2fn(&fname,VIEW_DATE,infop->date,"");
1904 infop->direction = DIRECT_FIRST;
1905 infop->view = VIEW_DATE;
1906 set_message(infop); /* got first thread */
1907 infop->subject = subject.s;
1908 infop->view = VIEW_TOPIC;
1909 subj2msg(infop); /* get 1st message no in that thread */
1910}
1911
1912void findlastmsg(struct msginfo *infop)
1913{
1914 if (!getconf_line(&line,"num",0,dir))
1915 cgierr("Sorry, there are no more messages in the archive","","");
1916 if (!stralloc_0(&line)) die_nomem();
1917 scan_ulong(line.s,&(infop->target));
1918}
1919
1929
1930int navigate(struct msginfo *infop)
1931{
1932 infop->target = infop->source;
1933
1934 switch (infop->item) {
1935 /* we want to get a message back */
1936 case VIEW_MESSAGE: switch (infop->view) {
1937 case VIEW_MESSAGE: if (infop->direction == DIRECT_SAME)
1938 break;
1939 else if (infop->direction == DIRECT_NEXT)
1940 (infop->target)++;
1941 else { /* previous */
1942 cache = 2;
1943 if (infop->target >= 2)
1944 (infop->target)--;
1945 else
1946 infop->target = 1;
1947 }
1948 break;
1949 case VIEW_AUTHOR: infop->author = infop->cgiarg;
1950 if (!infop->author) /* we don't know author hash */
1951 if (!msg2hash(infop)) return 0;
1952 auth2msg(infop);
1953 break;
1954 case VIEW_TOPIC: infop->subject = infop->cgiarg;
1955 if (!infop->subject) /* we don't know Subject hash */
1956 if (!msg2hash(infop)) return 0;
1957 subj2msg(infop);
1958 break;
1959 }
1960 break;
1961 case VIEW_AUTHOR: switch (infop->view) {
1962 case VIEW_MESSAGE: if (!infop->author)
1963 if (!msg2hash(infop)) return 0;
1964 break;
1965 case VIEW_AUTHOR: infop->author = infop->cgiarg;
1966 if (!infop->author)
1967 if (!msg2hash(infop)) return 0;
1968 auth2msg(infop);
1969 break;
1970 case VIEW_TOPIC: infop->subject = infop->cgiarg;
1971 if (!infop->subject) /* we don't know Subject hash */
1972 if (!msg2hash(infop)) return 0;
1973 subj2msg(infop);
1974 break;
1975 }
1976 break;
1977 case VIEW_TOPIC: switch (infop->view) {
1978 case VIEW_MESSAGE: if (!msg2hash(infop)) return 0;
1979 break;
1980 case VIEW_AUTHOR: infop->author = infop->cgiarg;
1981 if (!infop->author)
1982 if (!msg2hash(infop)) return 0;
1983 auth2msg(infop);
1984 break;
1985 case VIEW_TOPIC: infop->subject = infop->cgiarg;
1986 if (!infop->subject) /* we don't know Subject hash */
1987 if (!msg2hash(infop)) return 0;
1988 subj2msg(infop);
1989 break;
1990 }
1991 break;
1992 case VIEW_DATE: switch (infop->view) { /* want a date reference */
1993 case VIEW_MESSAGE:
1994 case VIEW_AUTHOR:
1995 case VIEW_TOPIC:
1996 case VIEW_DATE: if (!infop->date && infop->source)
1997 if (!msg2hash(infop)) return 0;
1998 gtdate(infop,0);
1999 break;
2000 }
2001 break;
2002 case VIEW_INDEX: if (!infop->target) /* ignore direction etc - only for index */
2003 infop->target = infop->source;
2004 break;
2005 }
2006
2007 return 1;
2008}
2009
2017
2018void list_list(const char *listname)
2019{
2020 DIR *archivedir;
2021 direntry *d;
2022 unsigned long msgset;
2023
2024 flagrobot = 2;
2025 archivedir = opendir("archive/");
2026 if (!archivedir)
2027 if (errno != ENOENT)
2028 logmsg(WHO,111,ERROR,B(ERR_OPEN,"/archive: ",dir));
2029 else
2030 logmsg(WHO,100,ERROR,B(ERR_OPEN,"/archive: ",dir));
2031
2032 cache = 1;
2033 html_header("Robot index for message sets in list",0,0,0,0);
2034
2035 while ((d = readdir(archivedir))) {
2036 if (d->d_name[scan_ulong(d->d_name,&msgset)])
2037 continue; /* not numeric */
2038 oputs("<a href=\"../"); /* from /ezcgi/0/index to /ezcgi/listnum/index*/
2039 oputs(listname);
2040 oputs("/index/");
2041 oputs(d->d_name);
2042 oputs("\">[link]</a>\n");
2043 }
2044 closedir(archivedir);
2045 html_footer(0);
2046}
2047
2048void list_set(unsigned long msgset)
2049{
2050 unsigned int msgfirst,msgmax;
2051 unsigned long lastset;
2052
2053 flagrobot = 2;
2055 if (!stralloc_copys(&line,"<a href=\"../")) die_nomem();
2056 if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,msgset))) die_nomem();
2057 lastset = msginfo.target/100;
2058 cache = 2;
2059 msgfirst = 0;
2060 if (!msgset)
2061 msgfirst = 1;
2062 msgmax = 99;
2063 if (msgset > lastset) { /* assure empty list */
2064 msgmax = 0;
2065 msgfirst = 1;
2066 } else if (msgset == lastset) {
2067 cache = 0; /* still changing */
2068 msgmax = msginfo.target % 100;
2069 }
2070 html_header("Robot index for messages in set",0,0,0,0);
2071 while (msgfirst <= msgmax) {
2072 oput(line.s,line.len);
2073 oput(strnum,fmt_uint0(strnum,msgfirst,2));
2074 oputs("\">[link]</a>\n");
2075 msgfirst++;
2076 }
2077 html_footer(0);
2078}
2079
2080/**************** MAY BE SUID ROOT HERE ****************************/
2081void drop_priv(int flagchroot)
2082{
2083 if (!uid) logmsg(WHO,100,FATAL,ERR_SUID); /* not as root */
2084 if (!euid) {
2085 if (flagchroot)
2086 if (chroot(dir) == -1) /* chroot listdir */
2087 logmsg(WHO,111,FATAL,B("failed to chroot to: ",dir));
2088 if (setuid(uid) == -1) /* setuid */
2089 logmsg(WHO,111,FATAL,ERR_SETUID);
2090 }
2091 euid = (unsigned long) geteuid();
2092 if (!euid) logmsg(WHO,100,FATAL,ERR_SUID); /* setuid didn't do it*/
2093}
2094/*******************************************************************/
2095
2096int main(int argc,char **argv)
2097{
2098 char *cp = 0;
2099 char *cppath = 0;
2100 char *thislistname = 0;
2101 unsigned long tmpuid, msgset;
2102 unsigned long msgnum = 0;
2103 unsigned long port = 0L;
2104 unsigned long tmptarget;
2105 unsigned int pos,l;
2106 int flagindex = 0;
2107 int flagchroot = 1; /* chroot listdir if SUID root */
2108 int r = 0;
2109 char sep = '|';
2110 char *ezcgirc = 0;
2111
2112/******************** we may be SUID ROOT ******************************/
2113 uid = (unsigned long) getuid(); /* should be http */
2114 euid = (unsigned long) geteuid(); /* chroot only if 0 */
2115
2116 if ((ezcgirc = env_get("EZCGIRC"))) {
2117 if ((fd = open_read(ezcgirc)) == -1) /* read ezcgirc from envionment */
2118 logmsg(WHO,111,FATAL,B(ERR_OPEN,"ezcigrc from environment: ","'",ezcgirc,"'"));
2119 } else if (!euid) {
2120 if ((fd = open_read(EZ_CGIRC)) == -1) /* open system ezcgirc */
2121 logmsg(WHO,111,FATAL,B(ERR_OPEN,EZ_CGIRC));
2122 } else {
2123 if ((fd = open_read(EZ_CGIRC_LOC)) == -1) /* open local ezcgirc */
2124 logmsg(WHO,111,FATAL,B(ERR_OPEN,EZ_CGIRC_LOC));
2125 }
2126
2127 buffer_init(&bi,buffer_unixread,fd,inbuf,sizeof(inbuf)); /* set up buffer */
2128
2129 cntl = env_get("QUERY_STRING"); /* get command */
2130 cppath = env_get("PATH_INFO"); /* get path_info */
2131
2132 if (!cntl && !cppath) {
2133 cntl = argv[1]; // call from command line
2134 }
2135
2136 if (!cppath || !*cppath) {
2137 if (cntl && *cntl) { // QUERY_STRING: WHO/listname+nav:.....
2138 pos = str_chr(cntl,'+');
2139 if (cntl[pos] == '+') {
2140 cntl[pos] = '\0';
2141 thislistname = cntl;
2142 cntl += pos + 1;
2143 }
2144 }
2145 } else if (*cppath == '/') { // PATH_INFO: WHO/listname/msgno
2146 cppath++;
2147 thislistname = cppath;
2148 pos = str_chr(cppath,'/');
2149 if (thislistname[pos] == '/') {
2150 thislistname[pos] = '\0';
2151 cppath += pos;
2152 }
2153 }
2154
2155 if (!thislistname || !*thislistname) { // No list requested
2156 drop_priv(0);
2157 close(fd);
2158 cgierr("Sorry, no mailing list requested","","");
2159 _exit(0);
2160 }
2161
2162 for (;;) {
2163 if (getln(&bi,&cfline,&match,'\n') == -1) /* read line */
2164 logmsg(WHO,111,ERROR,B(ERR_READ,fname.s));
2165 if (!match) break;
2166 if (*cfline.s == '#' || cfline.len == 1) continue; /* skip comment/blank */
2167 cfline.s[cfline.len - 1] = '\0'; /* so all are sz */
2168 pos = str_chr(cfline.s,sep); /* list name for line */
2169 if (pos < cfline.len)
2170 stralloc_copyb(&listname,cfline.s,pos);
2171 if (str_diffn(thislistname,listname.s,listname.len)) continue;
2172 if (cfline.s[++pos] == '-') { /* no chroot if -uid*/
2173 flagchroot = 0;
2174 pos++;
2175 }
2176 pos += scan_ulong(cfline.s + pos,&tmpuid); /* listno for line */
2177 if (tmpuid) uid = tmpuid; /* override default */
2178 if (cfline.s[pos++] != sep)
2179 die_syntax("missing separator after user id");
2180 if (cfline.s[pos] != '/')
2181 die_syntax("dir does not start with slash");/* absolute path */
2182 l = byte_chr(cfline.s + pos,cfline.len - pos,sep);
2183 if (l == cfline.len - pos) /* listno:path:...*/
2184 die_syntax("missing separator after path");
2185 dir = cfline.s + pos;
2186 pos += l;
2187 cfline.s[pos++] = '\0'; /* .../dir\0 */
2188 break; /* do rest after dropping priv */
2189 }
2190 close(fd); /* don't accept uid 0 */
2191
2192 if (!dir) {
2193 drop_priv(0); /* don't trust cgierr. No dir, no chroot */
2194 cgierr("list ",thislistname,ERR_NOEXIST);
2195 }
2196 if (chdir(dir) == -1) /* chdir listdir */
2197 logmsg(WHO,111,FATAL,B(ERR_SWITCH,dir));
2198
2199 drop_priv(flagchroot);
2200
2201/******************************* RELAX **********************************/
2202
2203/********************* continue to process config line ******************/
2204
2205 flagrobot = 0;
2206 if (cfline.s[pos] == '-') {
2207 flagobscure = 1;
2208 pos++;
2209 }
2210 local = cfline.s + pos;
2211 l = byte_chr(cfline.s + pos, cfline.len - pos,sep); /* ... home */
2212 if (l < cfline.len - pos) { /* optional */
2213 pos += l;
2214 cfline.s[pos++] = '\0';
2215 home = cfline.s + pos;
2216 l = byte_chr(cfline.s + pos, cfline.len - pos,sep); /* ... charset */
2217 if (l < cfline.len - pos) { /* optional */
2218 pos += l;
2219 cfline.s[pos++] = '\0';
2220 charset = cfline.s + pos;
2221 l = byte_chr(cfline.s + pos,cfline.len - pos,sep); /* ... stylesheet */
2222 if (l < cfline.len - pos) { /* optional */
2223 pos += l;
2224 cfline.s[pos++] = '\0';
2225 stylesheet = cfline.s + pos;
2226 l = byte_chr(cfline.s + pos,cfline.len - pos,sep); /* ... bannerURL */
2227 if (l < cfline.len - pos) { /* optional */
2228 pos += l;
2229 cfline.s[pos++] = '\0';
2230 banner = cfline.s + pos;
2231 }
2232 }
2233 }
2234 }
2235 if (!charset || !*charset) /* UTF-8 default */
2237 if (!stralloc_copys(&curcharset,charset)) die_nomem();
2239 if (csbase == CS_BAD) csbase = CS_NONE;
2240 cs = csbase;
2241 pos = str_rchr(local,'@');
2242 if (local[pos] != '@')
2243 die_syntax("listaddress lacks '@'"); /* require host */
2244 local[pos++] = '\0';
2245 host = local + pos;
2246
2247/********************* Accomodate robots and PATH_INFO ****************/
2248
2249 if (flagindex) {
2250 if (*(cppath++) == '/') { /* /2/index/123 */
2251 cppath += scan_ulong(cppath,&msgset);
2252 list_set(msgset);
2253 } else /* /2/index */
2254 list_list(thislistname);
2255 _exit(0);
2256 }
2257
2258 if (cppath && *cppath) { /* /2/msgnum */
2259 flagrobot = 1; /* allow index, but "nofollow" */
2260 scan_ulong(cppath,&msgnum);
2261 } /* dealt with normally */
2262
2263/********************* Get info from server on BASE etc ****************/
2264
2265
2266 if (!stralloc_copys(&url,"<a href=\"")) die_nomem();
2267 if (!stralloc_copys(&base,"<base href=\"http://")) die_nomem();
2268
2269
2270 cp = env_get("SERVER_PORT");
2271 if (cp) { /* port */
2272 (void) scan_ulong(cp,&port);
2273 if ((unsigned int) port == 443) { /* https: */
2274 if (!stralloc_copys(&base,"<base href=\"https://")) die_nomem();
2275 }
2276 }
2277 if ((cp = env_get("HTTP_HOST")) || (cp = env_get("SERVER_NAME"))) {
2278 if (!stralloc_cats(&base,cp)) die_nomem();
2279 if (port && (unsigned int) port != 80 && (unsigned int) port != 443) {
2280 if (!stralloc_cats(&base,":")) die_nomem();
2281 if (!stralloc_catb(&base,strnum,fmt_ulong(strnum,port))) die_nomem();
2282 }
2283 }
2284 if ((cp = env_get("SCRIPT_NAME")) != 0) {
2285 if (!stralloc_cats(&base,cp)) die_nomem();
2286 pos = str_rchr(cp,'/');
2287 if (cp[pos] == '/')
2288 if (!stralloc_cats(&url,cp + pos + 1)) die_nomem();
2289 }
2290 if (!stralloc_cats(&base,"\" />\n")) die_nomem();
2291 if (!stralloc_cats(&url,"?")) die_nomem();
2292 if (thislistname) {
2293 if (!stralloc_cats(&url,thislistname)) die_nomem();
2294 if (!stralloc_cats(&url,"+")) die_nomem();
2295 }
2296 cache = 1; /* don't know if we want to cache */
2297
2298/****************************** Get command ****************************/
2299
2300 if (msgnum) { /* to support /listnum/msgno */
2303 cache = 2;
2304 } else {
2306 if (!navigate(&msginfo))
2307 cgierr("I'm sorry, Cpt. Nelson ... I can't navigate towards that, Cpt. Nelson ...","","");
2308 }
2309
2310 switch (msginfo.item) {
2311 case VIEW_MESSAGE: if (!(r = show_message(&msginfo))) { /* assume next exists ... */
2312 cache = 0; /* border cond. - no cache */
2313 msginfo.target = msginfo.source; /* show same */
2314 msginfo.subjnav = 0;
2315 msginfo.authnav = 0;
2316 r = show_message(&msginfo);
2317 }
2318 break;
2319 case VIEW_AUTHOR: if (!(r = show_object(&msginfo,VIEW_AUTHOR)))
2320 cgierr("I couldn't find the author for that message","","");
2321 break;
2322 case VIEW_TOPIC: if (!(r = show_object(&msginfo,VIEW_TOPIC)))
2323 cgierr("I couldn't find the subject for that message","","");
2324 break;
2325 case VIEW_DATE: if (!(r = show_object(&msginfo,VIEW_DATE))) {
2326 finddate(&msginfo);
2328 }
2329 break;
2330 case VIEW_INDEX: r = 1;
2331 if (show_index(&msginfo)) break; /* msgnumber valid */
2332 tmptarget = msginfo.target;
2334 cache = 0; /* latest one - no cache */
2335 if (msginfo.target > tmptarget) {
2336 cache = 2; /* first one won't change */
2337 msginfo.target = 1; /* try */
2338 if (show_index(&msginfo)) break;
2339 msginfo.date = 0; /* first indexes missing */
2340 firstdate(&msginfo); /* instead get first msg of first */
2341 date2msg(&msginfo); /* thread. */
2342 if (show_index(&msginfo)) break;
2343 } else
2344 r = show_index(&msginfo);
2345 break;
2346 default: logmsg(WHO,100,ERROR,"bad item in main");
2347 }
2348 if (!r) {
2349 findlastmsg(&msginfo); /* as last resort; last msgindex */
2350 cache = 0;
2351 r = show_message(&msginfo);
2352 }
2353
2354 _exit(0);
2355
2356 return 0;
2357}
datetime_sec now(void)
Definition now.c:5
#define EZ_CGIRC_LOC
Definition idx.h:272
#define EZ_CHARSET
Definition idx.h:275
#define EZ_CGIRC
Definition idx.h:269
#define SO
Definition mime.h:19
#define CTENC_QP
Definition mime.h:60
#define MIME_MESSAGE_RFC822
Definition mime.h:57
#define MIME_MULTI
Definition mime.h:44
#define MIME_MULTI_DIGEST
Definition mime.h:47
#define CS_2022_JP
Definition mime.h:33
#define ESC
Definition mime.h:17
#define CS_BAD
Definition mime.h:36
#define TOASCII
Definition mime.h:21
#define CTENC_BASE64
Definition mime.h:61
#define CS_NONE
Definition mime.h:35
#define TOJP
Definition mime.h:23
#define MIME_MULTI_SIGNED
Definition mime.h:48
#define CS_2022_CN
Definition mime.h:38
#define SI_LF_SO
Definition mime.h:28
#define MIME_TEXT_HTML
Definition mime.h:52
#define MIME_MULTI_MIXED
Definition mime.h:46
#define CS_2022_KR
Definition mime.h:34
#define SI
Definition mime.h:18
#define MIME_APPLICATION_OCTETSTREAM
Definition mime.h:43
#define MIME_MULTI_ALTERNATIVE
Definition mime.h:45
#define MIME_MESSAGE
Definition mime.h:56
#define MIME_TEXT_VCARD
Definition mime.h:54
#define CS_CN
Definition mime.h:40
#define MIME_TEXT_ENRICHED
Definition mime.h:53
#define MIME_TEXT
Definition mime.h:50
#define MIME_NONE
Definition mime.h:42
#define MIME_TEXT_PLAIN
Definition mime.h:51
#define CTENC_NONE
Definition mime.h:59
Error messages. If you translate these, I would urge you to keep the English version as well....
#define ERR_NOMEM
Definition errtxt.h:14
#define ERR_OPEN
Definition errtxt.h:30
#define ERR_NOEXIST
Definition errtxt.h:40
#define ERR_READ
Definition errtxt.h:18
#define ERR_SETUID
Definition errtxt.h:139
#define ERR_SYNTAX
Definition errtxt.h:121
#define ERR_SWITCH
Definition errtxt.h:42
#define ERR_WRITE
Definition errtxt.h:17
#define ERR_SUID
Definition errtxt.h:131
long datetime_sec
Definition datetime.h:15
void die_nomem()
Definition getconf.c:17
int getconf_line(stralloc *sa, const char *fn, int flagrequired, const char *dir)
Definition getconf.c:53
unsigned int author_name(char **sout, char *s, unsigned int l)
Definition author.c:19
#define WHO
Definition author.c:1
char inbuf[1024]
char outbuf[1024]
char * dir
buffer bi
buffer bo
int main()
Definition ezmlm-weed.c:69
void decode_b64(const char *cpfrom, unsigned int n, stralloc *outdata)
Definition decode_b64.c:33
#define data
Definition makehash.c:45
unsigned long t
Definition ezmlm-cron.c:56
void die_syntax()
Definition ezmlm-cron.c:78
const char * cp
Definition ezmlm-cron.c:76
unsigned int len
Definition ezmlm-cron.c:68
stralloc flag
Definition ezmlm-make.c:71
#define HDR_FROM
Definition ezmlm-cgi.c:66
void decode_transfer_encoding(char *s, unsigned int l)
Definition ezmlm-cgi.c:1348
void findlastmsg(struct msginfo *infop)
Definition ezmlm-cgi.c:1912
#define TXT_CGI_FAQ
Definition ezmlm-cgi.c:48
int show_index(struct msginfo *infop)
Definition ezmlm-cgi.c:1002
int flagpre
Definition ezmlm-cgi.c:148
void show_part(struct msginfo *infop, int flagshowheaders, int flagstartseen)
if flagshowheaders we display headers, otherwise not if flagstartseen we've already see the start bou...
Definition ezmlm-cgi.c:1446
void html_put(const char *s, unsigned int l)
At this time, us-ascii, iso-8859-? create no problems. We just encode some html chars....
Definition ezmlm-cgi.c:294
void html_header(const char *t, const char *s, unsigned int l, const char *class, int flagspecial)
flagspecial: 0x1 => robot index; no style sheet, no BASE flagspecial: 0x2 => banner,...
Definition ezmlm-cgi.c:798
void cgierr(const char *s, const char *s1, const char *s2)
Definition ezmlm-cgi.c:209
#define VIEW
Definition ezmlm-cgi.c:91
int flagobscure
Definition ezmlm-cgi.c:55
#define MAXULONG
Definition ezmlm-cgi.c:83
char cn1
Definition ezmlm-cgi.c:150
int flagshowhtml
Definition ezmlm-cgi.c:50
void index_messages(struct msginfo *infop, unsigned char item)
for links from message view back to author/subject/threads index
Definition ezmlm-cgi.c:621
void list_set(unsigned long msgset)
Definition ezmlm-cgi.c:2048
int navigate(struct msginfo *infop)
interprets msginfo to create msginfo. Upon return, msginfo can be trusted to have all info needed,...
Definition ezmlm-cgi.c:1930
#define VIEW_AUTHOR
Definition ezmlm-cgi.c:94
char * host
Definition ezmlm-cgi.c:107
stralloc base
Definition ezmlm-cgi.c:120
int flagrobot
Definition ezmlm-cgi.c:147
#define SPC_BASE
Definition ezmlm-cgi.c:789
char * stylesheet
Definition ezmlm-cgi.c:111
char * local
Definition ezmlm-cgi.c:106
int show_message(struct msginfo *infop)
Definition ezmlm-cgi.c:1617
void html_footer(int flagspecial)
Definition ezmlm-cgi.c:842
#define HDR_CTENC
Definition ezmlm-cgi.c:68
char decode_item(char ch)
Definition ezmlm-cgi.c:1658
int uri2fn(stralloc *sa, char item, unsigned long n, const char *hash)
from the inputs: item, num, and hash the normalized and relative file name URI of the target message ...
Definition ezmlm-cgi.c:531
struct constmap headermap
Definition ezmlm-cgi.c:133
void urlencode_puts(const char *s)
Definition ezmlm-cgi.c:453
#define VIEW_TOPIC
Definition ezmlm-cgi.c:93
void message_links(struct msginfo *infop)
Creates the html for all links from one message view.
Definition ezmlm-cgi.c:756
int check_boundary()
return 0 if no boundary, 1 if start, 2 if end
Definition ezmlm-cgi.c:1371
int headers_shown[]
Definition ezmlm-cgi.c:62
char * home
Definition ezmlm-cgi.c:108
#define VIEW_MESSAGE
Definition ezmlm-cgi.c:92
#define VIEW_INDEX
Definition ezmlm-cgi.c:96
stralloc charg
Definition ezmlm-cgi.c:116
char headers_used[]
Definition ezmlm-cgi.c:59
void toggle_flagpre(int flag)
Definition ezmlm-cgi.c:226
int child
Definition ezmlm-cgi.c:143
int show_object(struct msginfo *infop, char item)
shows thread, threads, author infop has the info needed to access the author/subject/thread file
Definition ezmlm-cgi.c:1098
void oput(const char *s, unsigned int l)
Definition ezmlm-cgi.c:185
char navstr[5]
Definition ezmlm-cgi.c:90
stralloc decline
Definition ezmlm-cgi.c:122
char decode_direction(char ch)
Definition ezmlm-cgi.c:1671
unsigned long uid
Definition ezmlm-cgi.c:134
mime_info * mime_current
Definition ezmlm-cgi.c:170
#define HDR_VERSION
Definition ezmlm-cgi.c:69
stralloc fname
Definition ezmlm-cgi.c:124
void clear_mime()
Definition ezmlm-cgi.c:1203
void new_mime()
Definition ezmlm-cgi.c:1213
void set_message(struct msginfo *infop)
Reads the file corresponding to infop->view and assumes fname.s is set correctly for this....
Definition ezmlm-cgi.c:1811
#define TXT_CGI_SUBSCRIBE
Definition ezmlm-cgi.c:47
void justpress()
Definition ezmlm-cgi.c:703
stralloc cfline
Definition ezmlm-cgi.c:123
unsigned int cs
Definition ezmlm-cgi.c:146
int msg2hash(struct msginfo *infop)
Definition ezmlm-cgi.c:1742
void date2msg(struct msginfo *infop)
Definition ezmlm-cgi.c:1900
void gtdate(struct msginfo *infop, int flagfail)
infop->date has to be 0 or valid on entry. Month outside of [1-12] on entry causes GIGO
Definition ezmlm-cgi.c:961
#define DIRECT_NEXT
Definition ezmlm-cgi.c:100
int checkhash(const char *s)
Definition ezmlm-cgi.c:513
int fd
Definition ezmlm-cgi.c:141
void subfaq_link()
Definition ezmlm-cgi.c:733
stralloc headers
Definition ezmlm-cgi.c:126
#define VIEW_DATE
Definition ezmlm-cgi.c:95
stralloc author
Definition ezmlm-cgi.c:118
stralloc listname
Definition ezmlm-cgi.c:132
stralloc encoding
Definition ezmlm-cgi.c:127
unsigned long msgnav[5]
Definition ezmlm-cgi.c:224
stralloc content
Definition ezmlm-cgi.c:128
#define DIRECT_FIRST
Definition ezmlm-cgi.c:102
#define HDR_CT
Definition ezmlm-cgi.c:67
int ss23
Definition ezmlm-cgi.c:137
#define DIRECT
Definition ezmlm-cgi.c:98
void list_list(const char *listname)
Make one link [for list_set()] per set of 100 archive messages. Assumption: Any directory DIR/archive...
Definition ezmlm-cgi.c:2018
int state
Definition ezmlm-cgi.c:138
void home_link()
Definition ezmlm-cgi.c:709
void die_prog(const char *s)
Definition ezmlm-cgi.c:198
datetime_sec when
Definition ezmlm-cgi.c:173
char lastjp[]
Definition ezmlm-cgi.c:152
#define HDR_SUBJECT
Definition ezmlm-cgi.c:65
stralloc charsetbase
Definition ezmlm-cgi.c:129
void firstdate(struct msginfo *infop)
Definition ezmlm-cgi.c:948
#define DIRECT_LAST
Definition ezmlm-cgi.c:103
void auth2msg(struct msginfo *infop)
Definition ezmlm-cgi.c:1886
stralloc hdr[NO_HDRS]
Definition ezmlm-cgi.c:72
char * bannerargs[4]
Definition ezmlm-cgi.c:153
char enc_url[]
Definition ezmlm-cgi.c:437
void anchor_put(unsigned char *s, unsigned int l)
Definition ezmlm-cgi.c:458
int so
Definition ezmlm-cgi.c:136
const char * charset
Definition ezmlm-cgi.c:110
void latestdate(struct msginfo *infop, int flagfail)
Definition ezmlm-cgi.c:936
int cache
Definition ezmlm-cgi.c:142
stralloc sainit
Definition ezmlm-cgi.c:131
int decode_cmd(char *cmd, struct msginfo *infop)
decodes cmd into infop. Assures that no security problems slip through by checking everything cmd: iv...
Definition ezmlm-cgi.c:1695
void index_links(struct msginfo *infop)
Definition ezmlm-cgi.c:976
stralloc curcharset
Definition ezmlm-cgi.c:130
int recursion_level
Definition ezmlm-cgi.c:135
stralloc dtline
Definition ezmlm-cgi.c:125
#define SUBSCRIBE
Definition ezmlm-cgi.c:45
void oputs(const char *s)
Definition ezmlm-cgi.c:191
#define NO_HDRS
Definition ezmlm-cgi.c:64
void urlencode_put(const char *s, unsigned int l)
Definition ezmlm-cgi.c:439
char cn2
Definition ezmlm-cgi.c:151
#define DIRECT_PREV
Definition ezmlm-cgi.c:101
struct datetime dt
Definition ezmlm-cgi.c:174
#define FAQ
Definition ezmlm-cgi.c:46
int flagtoplevel
Definition ezmlm-cgi.c:144
void date_links(struct msginfo *infop, unsigned long d, char direction)
output a date with link back to thread index
Definition ezmlm-cgi.c:865
void decode_mime_type(char *s, unsigned int l, unsigned int flagmime)
Definition ezmlm-cgi.c:1269
int match
Definition ezmlm-cgi.c:140
#define DIRECT_SAME
Definition ezmlm-cgi.c:99
void subj2msg(struct msginfo *infop)
Definition ezmlm-cgi.c:1893
int precharcount
Definition ezmlm-cgi.c:149
void nav_links(struct msginfo *infop, unsigned char view, unsigned char direction)
Creates using a maximum of available information only for links where the target is a message.
Definition ezmlm-cgi.c:658
char * cntl
Definition ezmlm-cgi.c:112
unsigned int flagmime
Definition ezmlm-cgi.c:145
void start_message_page(struct msginfo *infop)
header etc for message. Delayed to collect subject so that we can put that in TITLE....
Definition ezmlm-cgi.c:1420
unsigned int decode_charset(const char *s, unsigned int l)
return charset code. CS_BAD: Means that base charset should be used, i.e. that charset is empty or li...
Definition ezmlm-cgi.c:245
#define SPC_BANNER
Definition ezmlm-cgi.c:790
stralloc subject
Definition ezmlm-cgi.c:119
mime_info * mime_tmp
Definition ezmlm-cgi.c:171
int newlevel
Definition ezmlm-cgi.c:139
unsigned long euid
Definition ezmlm-cgi.c:134
void drop_priv(int flagchroot)
Definition ezmlm-cgi.c:2081
void object_links(struct msginfo *infop, char item)
Definition ezmlm-cgi.c:1068
void finddate(struct msginfo *infop)
DIRECT_SAME works as DIRECT_PREV, dvs returns previous date or last date.
Definition ezmlm-cgi.c:896
stralloc url
Definition ezmlm-cgi.c:117
char * banner
Definition ezmlm-cgi.c:109
void alink(struct msginfo *infop, unsigned char item, unsigned char view, unsigned long msgnum, const char *data, unsigned int l)
links with targets other msgnum -> msgnum. If the link is for author, we still supply subject,...
Definition ezmlm-cgi.c:571
int wstat
Definition ezmlm-cgi.c:143
void mime_getarg(stralloc *sa, char **s, unsigned int *l)
opies next token or "token" into sa and sets s & l appropriately for continuing the search
Definition ezmlm-cgi.c:1240
unsigned int csbase
Definition ezmlm-cgi.c:146
void decode_hdr(const char *indata, unsigned int n, stralloc *outdata)
Definition decode_hdr.c:24
#define HASHLEN
Definition idxthread.c:25
int dateline(stralloc *dt, unsigned long d)
Definition dateline.c:20
unsigned long msgnum
void datetime_tai(struct datetime *dt, datetime_sec t)
Definition datetime.c:9
unsigned int date2yyyymm(const char *s)
Definition date2yyyymm.c:18
void decode_qp(const char *cpfrom, unsigned int n, stralloc *outdata)
Definition decode_qp.c:35
char view
Definition ezmlm-cgi.c:158
unsigned long date
Definition ezmlm-cgi.c:162
char item
Definition ezmlm-cgi.c:157
char * author
Definition ezmlm-cgi.c:165
unsigned long target
Definition ezmlm-cgi.c:161
int direction
Definition ezmlm-cgi.c:159
unsigned long * authnav
Definition ezmlm-cgi.c:163
unsigned long source
Definition ezmlm-cgi.c:160
char * cgiarg
Definition ezmlm-cgi.c:167
unsigned long * subjnav
Definition ezmlm-cgi.c:164
char * subject
Definition ezmlm-cgi.c:166
void * previous
Definition mime.h:69
stralloc boundary
Definition mime.h:71
const char * logmsg(const char *dir, unsigned long num, unsigned long listno, unsigned long subs, int done)
Definition loginfo.c:32