* Updated message reading to use the new MSG4 command, so we can do output
[citadel.git] / webcit / messages.c
1 /*
2  * $Id$
3  *
4  * Functions which deal with the fetching and displaying of messages.
5  *
6  */
7
8 #include <ctype.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <fcntl.h>
13 #include <signal.h>
14 #include <sys/types.h>
15 #include <sys/wait.h>
16 #include <sys/socket.h>
17 #include <limits.h>
18 #include <netinet/in.h>
19 #include <netdb.h>
20 #include <string.h>
21 #include <pwd.h>
22 #include <errno.h>
23 #include <stdarg.h>
24 #include <pthread.h>
25 #include <signal.h>
26 #include "webcit.h"
27 #include "vcard.h"
28 #include "webserver.h"
29
30
31 /*
32  * Look for URL's embedded in a buffer and make them linkable.  We use a
33  * target window in order to keep the BBS session in its own window.
34  */
35 void url(buf)
36 char buf[];
37 {
38
39         int pos;
40         int start, end;
41         char ench;
42         char urlbuf[SIZ];
43         char outbuf[1024];
44
45         start = (-1);
46         end = strlen(buf);
47         ench = 0;
48
49         for (pos = 0; pos < strlen(buf); ++pos) {
50                 if (!strncasecmp(&buf[pos], "http://", 7))
51                         start = pos;
52                 if (!strncasecmp(&buf[pos], "ftp://", 6))
53                         start = pos;
54         }
55
56         if (start < 0)
57                 return;
58
59         if ((start > 0) && (buf[start - 1] == '<'))
60                 ench = '>';
61         if ((start > 0) && (buf[start - 1] == '['))
62                 ench = ']';
63         if ((start > 0) && (buf[start - 1] == '('))
64                 ench = ')';
65         if ((start > 0) && (buf[start - 1] == '{'))
66                 ench = '}';
67
68         for (pos = strlen(buf); pos > start; --pos) {
69                 if ((buf[pos] == ' ') || (buf[pos] == ench))
70                         end = pos;
71         }
72
73         strncpy(urlbuf, &buf[start], end - start);
74         urlbuf[end - start] = 0;
75
76         strncpy(outbuf, buf, start);
77         sprintf(&outbuf[start], "%cA HREF=%c%s%c TARGET=%c%s%c%c%s%c/A%c",
78                 LB, QU, urlbuf, QU, QU, TARGET, QU, RB, urlbuf, LB, RB);
79         strcat(outbuf, &buf[end]);
80         if ( strlen(outbuf) < 250 )
81                 strcpy(buf, outbuf);
82 }
83
84
85 /* display_vcard() calls this after parsing the textual vCard into
86  * our 'struct vCard' data object.
87  *
88  * Set 'full' to nonzero to display the full card, otherwise it will only
89  * show a summary line.
90  */
91 void display_parsed_vcard(struct vCard *v, int full) {
92         int i, j;
93         char buf[SIZ];
94         char *name;
95
96         if (!full) {
97                 wprintf("<TD>");
98                 name = vcard_get_prop(v, "n", 1, 0, 0);
99                 if (name != NULL) {
100                         strcpy(buf, name);
101                         escputs(buf);
102                 }
103                 else {
104                         wprintf("&nbsp;");
105                 }
106                 wprintf("</TD>");
107                 return;
108         }
109
110         wprintf("<TABLE bgcolor=#888888>");
111         if (v->numprops) for (i=0; i<(v->numprops); ++i) {
112                 if (!strcasecmp(v->prop[i].name, "n")) {
113                         wprintf("<TR BGCOLOR=#AAAAAA>"
114                         "<TD BGCOLOR=#FFFFFF>"
115                         "<IMG VALIGN=CENTER SRC=\"/static/vcard.gif\"></TD>"
116                         "<TD><FONT SIZE=+1><B>");
117                         escputs(v->prop[i].value);
118                         wprintf("</B></FONT></TD></TR>\n");
119                 }
120                 else if (!strcasecmp(v->prop[i].name, "email;internet")) {
121                         wprintf("<TR><TD>Internet e-mail:</TD>"
122                                 "<TD><A HREF=\"mailto:");
123                         urlescputs(v->prop[i].value);
124                         wprintf("\">");
125                         escputs(v->prop[i].value);
126                         wprintf("</A></TD></TR>\n");
127                 }
128                 else if (!strcasecmp(v->prop[i].name, "adr")) {
129                         wprintf("<TR><TD>Address:</TD><TD>");
130                         for (j=0; j<num_tokens(v->prop[i].value, ';'); ++j) {
131                                 extract_token(buf, v->prop[i].value, j, ';');
132                                 if (strlen(buf) > 0) {
133                                         escputs(buf);
134                                         wprintf("<BR>");
135                                 }
136                         }
137                         wprintf("</TD></TR>\n");
138                 }
139                 else if (!strncasecmp(v->prop[i].name, "tel;", 4)) {
140                         wprintf("<TR><TD>%s telephone:</TD><TD>",
141                                 &v->prop[i].name[4]);
142                         for (j=0; j<num_tokens(v->prop[i].value, ';'); ++j) {
143                                 extract_token(buf, v->prop[i].value, j, ';');
144                                 if (strlen(buf) > 0) {
145                                         escputs(buf);
146                                         wprintf("<BR>");
147                                 }
148                         }
149                         wprintf("</TD></TR>\n");
150                 }
151                 else {
152                         wprintf("<TR><TD>");
153                         escputs(v->prop[i].name);
154                         wprintf("</TD><TD>");
155                         escputs(v->prop[i].value);
156                         wprintf("</TD></TR>\n");
157                 }
158         }
159         wprintf("</TABLE>\n");
160 }
161
162
163
164 /*
165  * Display a textual vCard
166  * (Converts to a vCard object and then calls the actual display function)
167  * Set 'full' to nonzero to display the whole card instead of a one-liner
168  */
169 void display_vcard(char *vcard_source, char alpha, int full) {
170         struct vCard *v;
171         char *name;
172         char buf[SIZ];
173         char this_alpha = 0;
174
175         v = vcard_load(vcard_source);
176         if (v == NULL) return;
177
178         name = vcard_get_prop(v, "n", 1, 0, 0);
179         if (name != NULL) {
180                 strcpy(buf, name);
181                 this_alpha = buf[0];
182         }
183
184         if ( (alpha == 0)
185            || ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha)) )
186            || ((!isalpha(alpha)) && (!isalpha(this_alpha))) ) {
187
188                 display_parsed_vcard(v, full);
189
190         }
191
192         vcard_free(v);
193 }
194
195
196
197
198 /*
199  * I wanna SEE that message!
200  */
201 void read_message(long msgnum) {
202         char buf[SIZ];
203         char mime_partnum[SIZ];
204         char mime_filename[SIZ];
205         char mime_content_type[SIZ];
206         char mime_disposition[SIZ];
207         int mime_length;
208         char *mime_http = NULL;
209         char m_subject[SIZ];
210         char from[SIZ];
211         char node[SIZ];
212         char rfca[SIZ];
213         char reply_to[512];
214         char now[SIZ];
215         int format_type = 0;
216         int nhdr = 0;
217         int bq = 0;
218         char vcard_partnum[SIZ];
219         char *vcard_source = NULL;
220
221         strcpy(from, "");
222         strcpy(node, "");
223         strcpy(rfca, "");
224         strcpy(reply_to, "");
225         strcpy(vcard_partnum, "");
226
227         serv_printf("MSG4 %ld", msgnum);
228         serv_gets(buf);
229         if (buf[0] != '1') {
230                 wprintf("<STRONG>ERROR:</STRONG> %s<BR>\n", &buf[4]);
231                 return;
232         }
233
234         wprintf("<TABLE WIDTH=100%% BORDER=0 CELLSPACING=0 "
235                 "CELLPADDING=1 BGCOLOR=CCCCCC><TR><TD>\n");
236
237         wprintf("<FONT ");
238         wprintf("SIZE=+1 ");
239         wprintf("COLOR=\"000000\"> ");
240         strcpy(m_subject, "");
241
242         while (serv_gets(buf), strcasecmp(buf, "text")) {
243                 if (!strncasecmp(buf, "nhdr=yes", 8))
244                         nhdr = 1;
245                 if (nhdr == 1)
246                         buf[0] = '_';
247                 if (!strncasecmp(buf, "type=", 5))
248                         format_type = atoi(&buf[5]);
249                 if (!strncasecmp(buf, "from=", 5)) {
250                         strcpy(from, &buf[5]);
251                         wprintf("from <A HREF=\"/showuser&who=");
252                         urlescputs(from);
253                         wprintf("\">");
254                         escputs(from);
255                         wprintf("</A> ");
256                 }
257                 if (!strncasecmp(buf, "subj=", 5))
258                         strcpy(m_subject, &buf[5]);
259                 if ((!strncasecmp(buf, "hnod=", 5))
260                     && (strcasecmp(&buf[5], serv_info.serv_humannode)))
261                         wprintf("(%s) ", &buf[5]);
262                 if ((!strncasecmp(buf, "room=", 5))
263                     && (strcasecmp(&buf[5], WC->wc_roomname)))
264                         wprintf("in %s> ", &buf[5]);
265                 if (!strncasecmp(buf, "rfca=", 5)) {
266                         strcpy(rfca, &buf[5]);
267                         wprintf("&lt;");
268                         escputs(rfca);
269                         wprintf("&gt; ");
270                 }
271
272                 if (!strncasecmp(buf, "node=", 5)) {
273                         if ( ((WC->room_flags & QR_NETWORK)
274                         || ((strcasecmp(&buf[5], serv_info.serv_nodename)
275                         && (strcasecmp(&buf[5], serv_info.serv_fqdn)))))
276                         && (strlen(rfca)==0)
277                         ) {
278                                 wprintf("@%s ", &buf[5]);
279                         }
280                 }
281                 if (!strncasecmp(buf, "rcpt=", 5))
282                         wprintf("to %s ", &buf[5]);
283                 if (!strncasecmp(buf, "time=", 5)) {
284                         fmt_date(now, atol(&buf[5]));
285                         wprintf("%s ", now);
286                 }
287
288                 if (!strncasecmp(buf, "part=", 5)) {
289                         extract(mime_filename, &buf[5], 1);
290                         extract(mime_partnum, &buf[5], 2);
291                         extract(mime_disposition, &buf[5], 3);
292                         extract(mime_content_type, &buf[5], 4);
293                         mime_length = extract_int(&buf[5], 5);
294
295                         if (!strcasecmp(mime_disposition, "attachment")) {
296                                 if (mime_http == NULL) {
297                                         mime_http = malloc(512);
298                                         strcpy(mime_http, "");
299                                 }
300                                 else {
301                                         mime_http = realloc(mime_http,
302                                                 strlen(mime_http) + 512);
303                                 }
304                                 sprintf(&mime_http[strlen(mime_http)],
305                                         "<A HREF=\"/output_mimepart?"
306                                         "msgnum=%ld&partnum=%s\" "
307                                         "TARGET=\"wc.%ld.%s\">"
308                                         "<IMG SRC=\"/static/attachment.gif\" "
309                                         "BORDER=0 ALIGN=MIDDLE>\n"
310                                         "Part %s: %s (%s, %d bytes)</A><BR>\n",
311                                         msgnum, mime_partnum,
312                                         msgnum, mime_partnum,
313                                         mime_partnum, mime_filename,
314                                         mime_content_type, mime_length);
315                         }
316
317                         if ((!strcasecmp(mime_disposition, "inline"))
318                            && (!strncasecmp(mime_content_type, "image/", 6)) ){
319                                 if (mime_http == NULL) {
320                                         mime_http = malloc(512);
321                                         strcpy(mime_http, "");
322                                 }
323                                 else {
324                                         mime_http = realloc(mime_http,
325                                                 strlen(mime_http) + 512);
326                                 }
327                                 sprintf(&mime_http[strlen(mime_http)],
328                                         "<IMG SRC=\"/output_mimepart?"
329                                         "msgnum=%ld&partnum=%s\">",
330                                         msgnum, mime_partnum);
331                         }
332
333                         if (!strcasecmp(mime_content_type, "text/x-vcard")) {
334                                 strcpy(vcard_partnum, mime_partnum);
335                         }
336
337                 }
338
339         }
340
341
342         /* Generate a reply-to address */
343         if (strlen(rfca) > 0) {
344                 strcpy(reply_to, rfca);
345         }
346         else {
347                 if (strlen(node) > 0) {
348                         snprintf(reply_to, sizeof(reply_to), "%s @ %s",
349                                 from, node);
350                 }
351                 else {
352                         snprintf(reply_to, sizeof(reply_to), "%s", from);
353                 }
354         }
355
356         if (nhdr == 1) {
357                 wprintf("****");
358         }
359
360         wprintf("</FONT></TD>");
361
362         wprintf("<TD ALIGN=RIGHT>\n"
363                 "<TABLE BORDER=0><TR>\n");
364
365         wprintf("<TD BGCOLOR=\"AAAADD\">"
366                 "<A HREF=\"/readfwd?startmsg=%ld", msgnum);
367         wprintf("&maxmsgs=1&summary=0\">Read</A>"
368                 "</TD>\n", msgnum);
369
370         wprintf("<TD BGCOLOR=\"AAAADD\">"
371                 "<A HREF=\"/display_enter?recp=");
372         urlescputs(reply_to);
373         wprintf("\"><FONT SIZE=-1>Reply</FONT></A>"
374                 "</TD>\n", msgnum);
375
376         if (WC->is_room_aide) {
377                 wprintf("<TD BGCOLOR=\"AAAADD\">"
378                         "<A HREF=\"/confirm_move_msg"
379                         "&msgid=%ld"
380                         "\"><FONT SIZE=-1>Move</FONT></A>"
381                         "</TD>\n", msgnum);
382
383                 wprintf("<TD BGCOLOR=\"AAAADD\">"
384                         "<A HREF=\"/delete_msg"
385                         "&msgid=%ld\""
386                         "onClick=\"return confirm('Delete this message?');\""
387                         "><FONT SIZE=-1>Del</FONT></A>"
388                         "</TD>\n", msgnum);
389         }
390
391         wprintf("</TR></TABLE>\n"
392                 "</TD>\n");
393
394         if (strlen(m_subject) > 0) {
395                 wprintf("<TR><TD><FONT COLOR=\"0000FF\">"
396                         "Subject: %s</FONT>"
397                         "</TD><TD>&nbsp;</TD></TR>\n", m_subject);
398         }
399
400         wprintf("</TR></TABLE>\n");
401
402         /* 
403          * Learn the content type
404          */
405         strcpy(mime_content_type, "text/plain");
406         while (serv_gets(buf), (strlen(buf) > 0)) {
407                 if (!strncasecmp(buf, "Content-type: ", 14)) {
408                         safestrncpy(mime_content_type, &buf[14],
409                                 sizeof(mime_content_type));
410                 }
411         }
412
413         /* Messages in legacy Citadel variformat get handled thusly... */
414         if (!strcasecmp(mime_content_type, "text/x-citadel-variformat")) {
415                 fmout(NULL);
416         }
417
418         /* Boring old 80-column fixed format text gets handled this way... */
419         else if (!strcasecmp(mime_content_type, "text/plain")) {
420                 while (serv_gets(buf), strcmp(buf, "000")) {
421                         while ((strlen(buf) > 0) && (isspace(buf[strlen(buf) - 1])))
422                                 buf[strlen(buf) - 1] = 0;
423                         if ((bq == 0) &&
424                         ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) || (!strncmp(buf, " :-)", 4)))) {
425                                 wprintf("<FONT COLOR=\"000044\"><I>");
426                                 bq = 1;
427                         } else if ((bq == 1) &&
428                                 (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) && (strncmp(buf, " :-)", 4))) {
429                                 wprintf("</FONT></I>");
430                                 bq = 0;
431                         }
432                         wprintf("<TT>");
433                         url(buf);
434                         escputs(buf);
435                         wprintf("</TT><BR>\n");
436                 }
437                 wprintf("</I><BR>");
438         }
439
440         else /* HTML is fun, but we've got to strip it first */
441         if (!strcasecmp(mime_content_type, "text/html")) {
442                 output_html();
443         }
444
445         /* Unknown weirdness */
446         else {
447                 wprintf("I don't know how to display %s<BR>\n",
448                         mime_content_type);
449                 while (serv_gets(buf), strcmp(buf, "000")) { }
450         }
451
452
453         /* Afterwards, offer links to download attachments 'n' such */
454         if (mime_http != NULL) {
455                 wprintf("%s", mime_http);
456                 free(mime_http);
457         }
458
459         if (strlen(vcard_partnum) > 0) {
460                 vcard_source = load_mimepart(msgnum, vcard_partnum);
461                 if (vcard_source != NULL) {
462
463                         /* If it's my vCard I can edit it */
464                         if ( (!strcasecmp(WC->wc_roomname, USERCONFIGROOM))
465                            || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM))) {
466                                 wprintf("<A HREF=\"/edit_vcard?"
467                                         "msgnum=%ld&partnum=%s\">",
468                                         msgnum, vcard_partnum);
469                                 wprintf("(edit)</A>");
470                         }
471
472                         /* In all cases, display the full card */
473                         display_vcard(vcard_source, 0, 1);
474                         free(vcard_source);
475                 }
476         }
477
478 }
479
480
481 void summarize_message(long msgnum) {
482         char buf[SIZ];
483
484         struct {
485                 char date[SIZ];
486                 char from[SIZ];
487                 char to[SIZ];
488                 char subj[SIZ];
489                 int hasattachments;
490         } summ;
491
492         memset(&summ, 0, sizeof(summ));
493         strcpy(summ.subj, "(no subject)");
494
495         sprintf(buf, "MSG0 %ld|1", msgnum);     /* ask for headers only */
496         serv_puts(buf);
497         serv_gets(buf);
498         if (buf[0] != '1') return;
499
500         while (serv_gets(buf), strcmp(buf, "000")) {
501                 if (!strncasecmp(buf, "from=", 5)) {
502                         strcpy(summ.from, &buf[5]);
503                 }
504                 if (!strncasecmp(buf, "subj=", 5)) {
505                         strcpy(summ.subj, &buf[5]);
506                 }
507                 if (!strncasecmp(buf, "rfca=", 5)) {
508                         strcat(summ.from, " <");
509                         strcat(summ.from, &buf[5]);
510                         strcat(summ.from, ">");
511                 }
512
513                 if (!strncasecmp(buf, "node=", 5)) {
514                         if ( ((WC->room_flags & QR_NETWORK)
515                         || ((strcasecmp(&buf[5], serv_info.serv_nodename)
516                         && (strcasecmp(&buf[5], serv_info.serv_fqdn)))))
517                         ) {
518                                 strcat(summ.from, " @ ");
519                                 strcat(summ.from, &buf[5]);
520                         }
521                 }
522
523                 if (!strncasecmp(buf, "rcpt=", 5)) {
524                         strcpy(summ.to, &buf[5]);
525                 }
526
527                 if (!strncasecmp(buf, "time=", 5)) {
528                         fmt_date(summ.date, atol(&buf[5]));
529                 }
530         }
531
532         wprintf("<TD><A HREF=\"/readfwd?startmsg=%ld"
533                 "&maxmsgs=1&summary=0\">", 
534                 msgnum);
535         escputs(summ.subj);
536         wprintf("</A></TD><TD>");
537         escputs(summ.from);
538         wprintf(" </TD><TD>");
539         escputs(summ.date);
540         wprintf(" </TD>");
541         wprintf("<TD>"
542                 "<INPUT TYPE=\"checkbox\" NAME=\"msg_%ld\" VALUE=\"yes\">"
543                 "</TD>\n");
544
545         return;
546 }
547
548
549
550 void display_addressbook(long msgnum, char alpha) {
551         char buf[SIZ];
552         char mime_partnum[SIZ];
553         char mime_filename[SIZ];
554         char mime_content_type[SIZ];
555         char mime_disposition[SIZ];
556         int mime_length;
557         char vcard_partnum[SIZ];
558         char *vcard_source = NULL;
559
560         struct {
561                 char date[SIZ];
562                 char from[SIZ];
563                 char to[SIZ];
564                 char subj[SIZ];
565                 int hasattachments;
566         } summ;
567
568         memset(&summ, 0, sizeof(summ));
569         strcpy(summ.subj, "(no subject)");
570
571         sprintf(buf, "MSG0 %ld|1", msgnum);     /* ask for headers only */
572         serv_puts(buf);
573         serv_gets(buf);
574         if (buf[0] != '1') return;
575
576         while (serv_gets(buf), strcmp(buf, "000")) {
577                 if (!strncasecmp(buf, "part=", 5)) {
578                         extract(mime_filename, &buf[5], 1);
579                         extract(mime_partnum, &buf[5], 2);
580                         extract(mime_disposition, &buf[5], 3);
581                         extract(mime_content_type, &buf[5], 4);
582                         mime_length = extract_int(&buf[5], 5);
583
584                         if (!strcasecmp(mime_content_type, "text/x-vcard")) {
585                                 strcpy(vcard_partnum, mime_partnum);
586                         }
587
588                 }
589         }
590
591         if (strlen(vcard_partnum) > 0) {
592                 vcard_source = load_mimepart(msgnum, vcard_partnum);
593                 if (vcard_source != NULL) {
594
595                         /* Display the summary line */
596                         display_vcard(vcard_source, alpha, 0);
597
598                         /* If it's my vCard I can edit it */
599                         if ( (!strcasecmp(WC->wc_roomname, USERCONFIGROOM))
600                            || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM))) {
601                                 wprintf("<A HREF=\"/edit_vcard?"
602                                         "msgnum=%ld&partnum=%s\">",
603                                         msgnum, vcard_partnum);
604                                 wprintf("(edit)</A>");
605                         }
606
607                         free(vcard_source);
608                 }
609         }
610
611 }
612
613
614
615 /* 
616  * load message pointers from the server
617  */
618 int load_msg_ptrs(servcmd)
619 char *servcmd;
620 {
621         char buf[SIZ];
622         int nummsgs;
623
624         nummsgs = 0;
625         serv_puts(servcmd);
626         serv_gets(buf);
627         if (buf[0] != '1') {
628                 wprintf("<EM>%s</EM><BR>\n", &buf[4]);
629                 return (nummsgs);
630         }
631         while (serv_gets(buf), strcmp(buf, "000")) {
632                 WC->msgarr[nummsgs] = atol(buf);
633                 ++nummsgs;
634         }
635         return (nummsgs);
636 }
637
638
639 /*
640  * command loop for reading messages
641  */
642 void readloop(char *oper)
643 {
644         char cmd[SIZ];
645         char buf[SIZ];
646         int a, b, i;
647         int nummsgs;
648         long startmsg;
649         int maxmsgs;
650         int num_displayed = 0;
651         int is_summary = 0;
652         int is_addressbook = 0;
653         int remaining_messages;
654         int lo, hi;
655         int lowest_displayed = (-1);
656         int highest_displayed = 0;
657         long pn_previous = 0L;
658         long pn_current = 0L;
659         long pn_next = 0L;
660         int bg = 0;
661         char alpha = 0;
662
663         startmsg = atol(bstr("startmsg"));
664         maxmsgs = atoi(bstr("maxmsgs"));
665         is_summary = atoi(bstr("summary"));
666         if (maxmsgs == 0) maxmsgs = 20;
667
668         output_headers(1);
669
670         if (!strcmp(oper, "readnew")) {
671                 strcpy(cmd, "MSGS NEW");
672         } else if (!strcmp(oper, "readold")) {
673                 strcpy(cmd, "MSGS OLD");
674         } else {
675                 strcpy(cmd, "MSGS ALL");
676         }
677
678         /* FIXME put in the correct constant #defs */
679         if ((WC->wc_view == 1) && (maxmsgs > 1)) {
680                 is_summary = 1;
681                 strcpy(cmd, "MSGS ALL");
682                 maxmsgs = 32767;
683         }
684         if ((WC->wc_view == 2) && (maxmsgs > 1)) {
685                 is_addressbook = 1;
686                 strcpy(cmd, "MSGS ALL");
687                 maxmsgs = 32767;
688                 if (bstr("alpha") == NULL) {
689                         alpha = 'A';
690                 }
691                 else {
692                         strcpy(buf, bstr("alpha"));
693                         alpha = buf[0];
694                 }
695
696                 for (i='A'; i<='Z'; ++i) {
697                         if (i == alpha) wprintf("<FONT SIZE=+2>"
698                                                 "%c</FONT>\n", i);
699                         else {
700                                 wprintf("<A HREF=\"/readfwd?alpha=%c\">"
701                                         "%c</A>\n", i, i);
702                         }
703                         wprintf("&nbsp;");
704                 }
705                 if (!isalpha(alpha)) wprintf("<FONT SIZE=+2>(other)</FONT>\n");
706                 else wprintf("<A HREF=\"/readfwd?alpha=1\">(other)</A>\n");
707                 wprintf("<HR width=100%%>\n");
708         }
709
710         nummsgs = load_msg_ptrs(cmd);
711         if (nummsgs == 0) {
712                 if (!strcmp(oper, "readnew")) {
713                         wprintf("<EM>No new messages in this room.</EM>\n");
714                 } else if (!strcmp(oper, "readold")) {
715                         wprintf("<EM>No old messages in this room.</EM>\n");
716                 } else {
717                         wprintf("<EM>This room is empty.</EM>\n");
718                 }
719                 goto DONE;
720         }
721
722         if (startmsg == 0L) startmsg = WC->msgarr[0];
723         remaining_messages = 0;
724
725         for (a = 0; a < nummsgs; ++a) {
726                 if (WC->msgarr[a] >= startmsg) {
727                         ++remaining_messages;
728                 }
729         }
730
731         if (is_summary) {
732                 wprintf("<FORM METHOD=\"POST\" ACTION=\"/do_stuff_to_msgs\">\n"
733                         "<TABLE border=0 cellspacing=0 "
734                         "cellpadding=0 width=100%%>\n"
735                         "<TR>"
736                         "<TD><I>Subject</I></TD>"
737                         "<TD><I>Sender</I></TD>"
738                         "<TD><I>Date</I></TD>"
739                         "<TD></TD>"
740                         "</TR>\n"
741                 );
742         }
743
744         if (is_addressbook) {
745                 wprintf("<TABLE border=0 cellspacing=0 "
746                         "cellpadding=0 width=100%%>\n"
747                 );
748         }
749
750         for (a = 0; a < nummsgs; ++a) {
751                 if ((WC->msgarr[a] >= startmsg) && (num_displayed < maxmsgs)) {
752
753                         /* Learn which msgs "Prev" & "Next" buttons go to */
754                         pn_current = WC->msgarr[a];
755                         if (a > 0) pn_previous = WC->msgarr[a-1];
756                         if (a < (nummsgs-1)) pn_next = WC->msgarr[a+1];
757
758                         /* If a tabular view, set up the line */
759                         if ( (is_summary) || (is_addressbook) ) {
760                                 bg = 1 - bg;
761                                 wprintf("<TR BGCOLOR=%s>",
762                                         (bg ? "DDDDDD" : "FFFFFF")
763                                 );
764                         }
765
766                         /* Display the message */
767                         if (is_summary) {
768                                 summarize_message(WC->msgarr[a]);
769                         }
770                         else if (is_addressbook) {
771                                 display_addressbook(WC->msgarr[a], alpha);
772                         }
773                         else {
774                                 read_message(WC->msgarr[a]);
775                         }
776
777                         /* If a tabular view, finish the line */
778                         if ( (is_summary) || (is_addressbook) ) {
779                                 wprintf("</TR>\n");
780                         }
781
782                         if (lowest_displayed < 0) lowest_displayed = a;
783                         highest_displayed = a;
784
785                         ++num_displayed;
786                         --remaining_messages;
787                 }
788         }
789
790         if (is_summary) {
791                 wprintf("</TABLE>\n");
792         }
793
794         if (is_addressbook) {
795                 wprintf("</TABLE>\n");
796         }
797
798         /* Bump these because although we're thinking in zero base, the user
799          * is a drooling idiot and is thinking in one base.
800          */
801         ++lowest_displayed;
802         ++highest_displayed;
803
804         /* If we're only looking at one message, do a prev/next thing */
805         if (num_displayed == 1) {
806
807                 wprintf("<CENTER>"
808                         "<TABLE BORDER=0 WIDTH=100%% BGCOLOR=DDDDDD><TR><TD>"
809                         "Reading #%d of %d messages.</TD>\n"
810                         "<TD ALIGN=RIGHT><FONT SIZE=+1>",
811                         lowest_displayed, nummsgs);
812
813                 if (is_summary) {
814                         wprintf("<INPUT TYPE=\"submit\" NAME=\"sc\" "
815                                 "VALUE=\"Delete selected\">\n");
816                 }
817
818                 if (pn_previous > 0L) {
819                         wprintf("<A HREF=\"/%s"
820                                 "?startmsg=%ld"
821                                 "&maxmsgs=1"
822                                 "&summary=0\">"
823                                 "Previous</A> \n",
824                                         oper,
825                                         pn_previous );
826                 }
827
828                 if (pn_next > 0L) {
829                         wprintf("<A HREF=\"/%s"
830                                 "?startmsg=%ld"
831                                 "&maxmsgs=1"
832                                 "&summary=0\">"
833                                 "Next</A> \n",
834                                         oper,
835                                         pn_next );
836                 }
837
838                 wprintf("<A HREF=\"/%s?startmsg=%ld"
839                         "&maxmsgs=999999&summary=1\">"
840                         "Summary"
841                         "</A>",
842                         oper,
843                         WC->msgarr[0]);
844
845                 wprintf("</TD></TR></TABLE></CENTER>\n");
846         }
847
848
849         /*
850          * If we're not currently looking at ALL requested
851          * messages, then display the selector bar
852          */
853         if (num_displayed > 1) {
854                 wprintf("<CENTER>"
855                         "<TABLE BORDER=0 WIDTH=100%% BGCOLOR=DDDDDD><TR><TD>"
856                         "Reading #%d-%d of %d messages.</TD>\n"
857                         "<TD ALIGN=RIGHT><FONT SIZE=+1>",
858                         lowest_displayed, highest_displayed, nummsgs);
859
860                 if (is_summary) {
861                         wprintf("<INPUT TYPE=\"submit\" NAME=\"sc\" "
862                                 "VALUE=\"Delete selected\">\n");
863                 }
864
865
866                 for (b=0; b<nummsgs; b = b + maxmsgs) {
867                 lo = b+1;
868                 hi = b+maxmsgs+1;
869                 if (hi > nummsgs) hi = nummsgs;
870                         if (WC->msgarr[b] != startmsg) {
871                                 wprintf("<A HREF=\"/%s"
872                                         "?startmsg=%ld"
873                                         "&maxmsgs=%d"
874                                         "&summary=%d\">"
875                                         "%d-%d</A> \n",
876                                                 oper,
877                                                 WC->msgarr[b],
878                                                 maxmsgs,
879                                                 is_summary,
880                                                 lo, hi);
881                         }
882                         else {
883                                 wprintf("%d-%d \n", lo, hi);
884                         }
885
886                 }
887                 wprintf("<A HREF=\"/%s?startmsg=%ld"
888                         "&maxmsgs=999999&summary=%d\">"
889                         "ALL"
890                         "</A> ",
891                         oper,
892                         WC->msgarr[0], is_summary);
893
894                 wprintf("<A HREF=\"/%s?startmsg=%ld"
895                         "&maxmsgs=999999&summary=1\">"
896                         "Summary"
897                         "</A>",
898                         oper,
899                         WC->msgarr[0]);
900
901                 wprintf("</TD></TR></TABLE></CENTER>\n");
902         }
903         if (is_summary) wprintf("</FORM>\n");
904
905 DONE:   wDumpContent(1);
906 }
907
908
909
910
911 /*
912  * Post message (or don't post message)
913  *
914  * Note regarding the "dont_post" variable:
915  * A random value (actually, it's just a timestamp) is inserted as a hidden
916  * field called "postseq" when the display_enter page is generated.  This
917  * value is checked when posting, using the static variable dont_post.  If a
918  * user attempts to post twice using the same dont_post value, the message is
919  * discarded.  This prevents the accidental double-saving of the same message
920  * if the user happens to click the browser "back" button.
921  */
922 void post_message(void)
923 {
924         char buf[SIZ];
925         static long dont_post = (-1L);
926
927         output_headers(1);
928
929         wprintf("<FONT FACE=\"Arial,Helvetica,sans-serif\">");
930         strcpy(buf, bstr("sc"));
931         if (strcasecmp(buf, "Save message")) {
932                 wprintf("Cancelled.  Message was not posted.<BR>\n");
933         } else if (atol(bstr("postseq")) == dont_post) {
934                 wprintf("Automatically cancelled because you have already "
935                         "saved this message.<BR>\n");
936         } else {
937                 sprintf(buf, "ENT0 1|%s|0|4|%s",
938                         bstr("recp"),
939                         bstr("subject") );
940                 serv_puts(buf);
941                 serv_gets(buf);
942                 if (buf[0] == '4') {
943                         serv_puts("Content-type: text/html");
944                         serv_puts("");
945                         text_to_server(bstr("msgtext"), 1);
946                         serv_puts("000");
947                         wprintf("Message has been posted.<BR>\n");
948                         dont_post = atol(bstr("postseq"));
949                 } else {
950                         wprintf("<EM>%s</EM><BR>\n", &buf[4]);
951                 }
952         }
953
954         wDumpContent(1);
955 }
956
957
958
959
960 /*
961  * display the message entry screen
962  */
963 void display_enter(void)
964 {
965         char buf[SIZ];
966         long now;
967         struct tm *tm;
968
969         output_headers(1);
970
971         wprintf("<FONT FACE=\"Arial,Helvetica,sans-serif\">");
972
973         sprintf(buf, "ENT0 0|%s|0|0", bstr("recp"));
974         serv_puts(buf);
975         serv_gets(buf);
976
977         if (!strncmp(buf, "570", 3)) {
978                 if (strlen(bstr("recp")) > 0) {
979                         wprintf("<EM>%s</EM><BR>\n", &buf[4]);
980                 }
981                 do_template("prompt_for_recipient");
982                 goto DONE;
983         }
984         if (buf[0] != '2') {
985                 wprintf("<EM>%s</EM><BR>\n", &buf[4]);
986                 goto DONE;
987         }
988
989         now = time(NULL);
990         tm = (struct tm *) localtime(&now);
991         strcpy(buf, (char *) asctime(tm));
992         buf[strlen(buf) - 1] = 0;
993         strcpy(&buf[16], &buf[19]);
994         wprintf("</CENTER><FONT COLOR=\"440000\">\n"
995                 "<IMG SRC=\"static/enter.gif\" ALIGN=MIDDLE ALT=\" \" "
996                 "onLoad=\"document.enterform.msgtext.focus();\" >");
997         wprintf("<B> %s ", &buf[4]);
998         wprintf("from %s ", WC->wc_username);
999         if (strlen(bstr("recp")) > 0)
1000                 wprintf("to %s ", bstr("recp"));
1001         wprintf("in %s&gt; ", WC->wc_roomname);
1002         wprintf("</B></FONT><BR><CENTER>\n");
1003
1004         wprintf("<FORM METHOD=\"POST\" ACTION=\"/post\" "
1005                 "NAME=\"enterform\">\n");
1006         wprintf("<INPUT TYPE=\"hidden\" NAME=\"recp\" VALUE=\"%s\">\n",
1007                 bstr("recp"));
1008         wprintf("<INPUT TYPE=\"hidden\" NAME=\"postseq\" VALUE=\"%ld\">\n",
1009                 now);
1010         wprintf("<FONT SIZE=-1>Subject (optional):</FONT>"
1011                 "<INPUT TYPE=\"text\" NAME=\"subject\" MAXLENGTH=70>"
1012                 "&nbsp;&nbsp;&nbsp;"
1013                 "<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Save message\">"
1014                 "<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Cancel\"><BR>\n");
1015
1016         wprintf("<TEXTAREA NAME=\"msgtext\" wrap=soft ROWS=30 COLS=80 "
1017                 "WIDTH=80></TEXTAREA><P>\n");
1018
1019         wprintf("</FORM></CENTER>\n");
1020 DONE:   wDumpContent(1);
1021         wprintf("</FONT>");
1022 }
1023
1024
1025
1026
1027
1028
1029
1030
1031 void delete_msg(void)
1032 {
1033         long msgid;
1034         char buf[SIZ];
1035
1036         msgid = atol(bstr("msgid"));
1037
1038         output_headers(1);
1039
1040         sprintf(buf, "DELE %ld", msgid);
1041         serv_puts(buf);
1042         serv_gets(buf);
1043         wprintf("<EM>%s</EM><BR>\n", &buf[4]);
1044
1045         wDumpContent(1);
1046 }
1047
1048
1049
1050
1051 /*
1052  * Confirm move of a message
1053  */
1054 void confirm_move_msg(void)
1055 {
1056         long msgid;
1057         char buf[SIZ];
1058         char targ[SIZ];
1059
1060         msgid = atol(bstr("msgid"));
1061
1062         output_headers(1);
1063
1064         wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=770000><TR><TD>");
1065         wprintf("<FONT SIZE=+1 COLOR=\"FFFFFF\"");
1066         wprintf("<B>Confirm move of message</B>\n");
1067         wprintf("</FONT></TD></TR></TABLE>\n");
1068
1069         wprintf("<CENTER>");
1070
1071         wprintf("Please select the room to which you would like this message moved:<BR>\n");
1072
1073         wprintf("<FORM METHOD=\"POST\" ACTION=\"/move_msg\">\n");
1074         wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgid\" VALUE=\"%s\">\n",
1075                 bstr("msgid"));
1076
1077
1078         wprintf("<SELECT NAME=\"target_room\" SIZE=5>\n");
1079         serv_puts("LKRA");
1080         serv_gets(buf);
1081         if (buf[0] == '1') {
1082                 while (serv_gets(buf), strcmp(buf, "000")) {
1083                         extract(targ, buf, 0);
1084                         wprintf("<OPTION>");
1085                         escputs(targ);
1086                         wprintf("\n");
1087                 }
1088         }
1089         wprintf("</SELECT>\n");
1090         wprintf("<BR>\n");
1091
1092         wprintf("<INPUT TYPE=\"submit\" NAME=\"yesno\" VALUE=\"Move\">");
1093         wprintf("<INPUT TYPE=\"submit\" NAME=\"yesno\" VALUE=\"Cancel\">");
1094         wprintf("</FORM></CENTER>\n");
1095
1096         wprintf("</CENTER>\n");
1097         wDumpContent(1);
1098 }
1099
1100
1101
1102 void move_msg(void)
1103 {
1104         long msgid;
1105         char buf[SIZ];
1106
1107         msgid = atol(bstr("msgid"));
1108
1109         output_headers(1);
1110
1111         if (!strcasecmp(bstr("yesno"), "Move")) {
1112                 sprintf(buf, "MOVE %ld|%s", msgid, bstr("target_room"));
1113                 serv_puts(buf);
1114                 serv_gets(buf);
1115                 wprintf("<EM>%s</EM><BR>\n", &buf[4]);
1116         } else {
1117                 wprintf("<EM>Message not moved.</EM><BR>\n");
1118         }
1119
1120         wDumpContent(1);
1121 }
1122
1123
1124
1125 void do_stuff_to_msgs(void) {
1126         char buf[SIZ];
1127         char sc[SIZ];
1128
1129         struct stuff_t {
1130                 struct stuff_t *next;
1131                 long msgnum;
1132         };
1133
1134         struct stuff_t *stuff = NULL;
1135         struct stuff_t *ptr;
1136
1137
1138         serv_puts("MSGS ALL");
1139         serv_gets(buf);
1140
1141         if (buf[0] == '1') while (serv_gets(buf), strcmp(buf, "000")) {
1142                 ptr = malloc(sizeof(struct stuff_t));
1143                 ptr->msgnum = atol(buf);
1144                 ptr->next = stuff;
1145                 stuff = ptr;
1146         }
1147
1148         strcpy(sc, bstr("sc"));
1149
1150         while (stuff != NULL) {
1151
1152                 sprintf(buf, "msg_%ld", stuff->msgnum);
1153                 if (!strcasecmp(bstr(buf), "yes")) {
1154
1155                         if (!strcasecmp(sc, "Delete selected")) {
1156                                 serv_printf("DELE %ld", stuff->msgnum);
1157                                 serv_gets(buf);
1158                         }
1159
1160                 }
1161
1162                 ptr = stuff->next;
1163                 free(stuff);
1164                 stuff = ptr;
1165         }
1166
1167         readloop("readfwd");
1168 }