5988a9599d87ceb348b89311e8e9dc6f7073a3dc
[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 ALIGN=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[SIZ];
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 cal_partnum[SIZ];
220         char *part_source = NULL;
221
222         strcpy(from, "");
223         strcpy(node, "");
224         strcpy(rfca, "");
225         strcpy(reply_to, "");
226         strcpy(vcard_partnum, "");
227         strcpy(cal_partnum, "");
228         strcpy(mime_http, "");
229
230         serv_printf("MSG4 %ld", msgnum);
231         serv_gets(buf);
232         if (buf[0] != '1') {
233                 wprintf("<STRONG>ERROR:</STRONG> %s<BR>\n", &buf[4]);
234                 return;
235         }
236
237         wprintf("<TABLE WIDTH=100%% BORDER=0 CELLSPACING=0 "
238                 "CELLPADDING=1 BGCOLOR=\"#CCCCCC\"><TR><TD>\n");
239
240         wprintf("<SPAN CLASS=\"message_header\">");
241         strcpy(m_subject, "");
242
243         while (serv_gets(buf), strcasecmp(buf, "text")) {
244                 if (!strcmp(buf, "000")) {
245                         wprintf("<I>unexpected end of message</I><BR><BR>\n");
246                         wprintf("</SPAN>\n");
247                         return;
248                 }
249                 if (!strncasecmp(buf, "nhdr=yes", 8))
250                         nhdr = 1;
251                 if (nhdr == 1)
252                         buf[0] = '_';
253                 if (!strncasecmp(buf, "type=", 5))
254                         format_type = atoi(&buf[5]);
255                 if (!strncasecmp(buf, "from=", 5)) {
256                         strcpy(from, &buf[5]);
257                         wprintf("from <A HREF=\"/showuser&who=");
258                         urlescputs(from);
259                         wprintf("\">");
260                         escputs(from);
261                         wprintf("</A> ");
262                 }
263                 if (!strncasecmp(buf, "subj=", 5))
264                         strcpy(m_subject, &buf[5]);
265                 if ((!strncasecmp(buf, "hnod=", 5))
266                     && (strcasecmp(&buf[5], serv_info.serv_humannode)))
267                         wprintf("(%s) ", &buf[5]);
268                 if ((!strncasecmp(buf, "room=", 5))
269                     && (strcasecmp(&buf[5], WC->wc_roomname))
270                     && (strlen(&buf[5])>0) )
271                         wprintf("in %s> ", &buf[5]);
272                 if (!strncasecmp(buf, "rfca=", 5)) {
273                         strcpy(rfca, &buf[5]);
274                         wprintf("&lt;");
275                         escputs(rfca);
276                         wprintf("&gt; ");
277                 }
278
279                 if (!strncasecmp(buf, "node=", 5)) {
280                         strcpy(node, &buf[5]);
281                         if ( ((WC->room_flags & QR_NETWORK)
282                         || ((strcasecmp(&buf[5], serv_info.serv_nodename)
283                         && (strcasecmp(&buf[5], serv_info.serv_fqdn)))))
284                         && (strlen(rfca)==0)
285                         ) {
286                                 wprintf("@%s ", &buf[5]);
287                         }
288                 }
289                 if (!strncasecmp(buf, "rcpt=", 5))
290                         wprintf("to %s ", &buf[5]);
291                 if (!strncasecmp(buf, "time=", 5)) {
292                         fmt_date(now, atol(&buf[5]));
293                         wprintf("%s ", now);
294                 }
295
296                 if (!strncasecmp(buf, "part=", 5)) {
297                         extract(mime_filename, &buf[5], 1);
298                         extract(mime_partnum, &buf[5], 2);
299                         extract(mime_disposition, &buf[5], 3);
300                         extract(mime_content_type, &buf[5], 4);
301                         mime_length = extract_int(&buf[5], 5);
302
303                         if (!strcasecmp(mime_disposition, "attachment")) {
304                                 snprintf(&mime_http[strlen(mime_http)],
305                                         (sizeof(mime_http) - strlen(mime_http) - 1),
306                                         "<A HREF=\"/output_mimepart?"
307                                         "msgnum=%ld&partnum=%s\" "
308                                         "TARGET=\"wc.%ld.%s\">"
309                                         "<IMG SRC=\"/static/attachment.gif\" "
310                                         "BORDER=0 ALIGN=MIDDLE>\n"
311                                         "Part %s: %s (%s, %d bytes)</A><BR>\n",
312                                         msgnum, mime_partnum,
313                                         msgnum, mime_partnum,
314                                         mime_partnum, mime_filename,
315                                         mime_content_type, mime_length);
316                         }
317
318                         if ((!strcasecmp(mime_disposition, "inline"))
319                            && (!strncasecmp(mime_content_type, "image/", 6)) ){
320                                 snprintf(&mime_http[strlen(mime_http)],
321                                         (sizeof(mime_http) - strlen(mime_http) - 1),
322                                         "<IMG SRC=\"/output_mimepart?"
323                                         "msgnum=%ld&partnum=%s\">",
324                                         msgnum, mime_partnum);
325                         }
326
327                         /*** begin handler prep ***/
328                         if (!strcasecmp(mime_content_type, "text/x-vcard")) {
329                                 strcpy(vcard_partnum, mime_partnum);
330                         }
331
332                         if (!strcasecmp(mime_content_type, "text/calendar")) {
333                                 strcpy(cal_partnum, mime_partnum);
334                         }
335
336                         /*** end handler prep ***/
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                    && (strcasecmp(node, serv_info.serv_nodename))
349                    && (strcasecmp(node, serv_info.serv_humannode)) ) {
350                         snprintf(reply_to, sizeof(reply_to), "%s @ %s",
351                                 from, node);
352                 }
353                 else {
354                         snprintf(reply_to, sizeof(reply_to), "%s", from);
355                 }
356         }
357
358         if (nhdr == 1) {
359                 wprintf("****");
360         }
361
362         wprintf("</SPAN></TD>");
363
364         wprintf("<TD ALIGN=RIGHT>\n"
365                 "<TABLE BORDER=0><TR>\n");
366
367         /***  "Read" button is now superfluous
368          ***
369          ***wprintf("<TD BGCOLOR=\"#AAAADD\">"
370          ***    "<A HREF=\"/readfwd?startmsg=%ld", msgnum);
371          ***wprintf("&maxmsgs=1&summary=0\">Read</A>"
372          ***    "</TD>\n", msgnum);
373          ***/
374
375         wprintf("<TD BGCOLOR=\"#AAAADD\">"
376                 "<A HREF=\"/display_enter?recp=");
377         urlescputs(reply_to);
378         wprintf("\"><FONT SIZE=-1>Reply</FONT></A>"
379                 "</TD>\n", msgnum);
380
381         if (WC->is_room_aide) {
382                 wprintf("<TD BGCOLOR=\"#AAAADD\">"
383                         "<A HREF=\"/confirm_move_msg"
384                         "&msgid=%ld"
385                         "\"><FONT SIZE=-1>Move</FONT></A>"
386                         "</TD>\n", msgnum);
387
388                 wprintf("<TD BGCOLOR=\"#AAAADD\">"
389                         "<A HREF=\"/delete_msg"
390                         "&msgid=%ld\""
391                         "onClick=\"return confirm('Delete this message?');\""
392                         "><FONT SIZE=-1>Del</FONT></A>"
393                         "</TD>\n", msgnum);
394         }
395
396         wprintf("</TR></TABLE>\n"
397                 "</TD>\n");
398
399         if (strlen(m_subject) > 0) {
400                 wprintf("<TR><TD>"
401                         "<SPAN CLASS=\"message_subject\">"
402                         "Subject: %s"
403                         "</SPAN>"
404                         "</TD><TD>&nbsp;</TD></TR>\n", m_subject);
405         }
406
407         wprintf("</TR></TABLE>\n");
408
409         /* Begin body */
410         wprintf("<TABLE BORDER=0 WIDTH=100%% BGCOLOR=#FFFFFF "
411                 "CELLPADDING=0 CELLSPACING=0><TR><TD>");
412
413         /* 
414          * Learn the content type
415          */
416         strcpy(mime_content_type, "text/plain");
417         while (serv_gets(buf), (strlen(buf) > 0)) {
418                 if (!strcmp(buf, "000")) {
419                         wprintf("<I>unexpected end of message</I><BR><BR>\n");
420                         goto ENDBODY;
421                 }
422                 if (!strncasecmp(buf, "Content-type: ", 14)) {
423                         safestrncpy(mime_content_type, &buf[14],
424                                 sizeof(mime_content_type));
425                 }
426         }
427
428         /* Messages in legacy Citadel variformat get handled thusly... */
429         if (!strcasecmp(mime_content_type, "text/x-citadel-variformat")) {
430                 fmout(NULL, "JUSTIFY");
431         }
432
433         /* Boring old 80-column fixed format text gets handled this way... */
434         else if (!strcasecmp(mime_content_type, "text/plain")) {
435                 while (serv_gets(buf), strcmp(buf, "000")) {
436                         if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = 0;
437                         if (buf[strlen(buf)-1] == '\r') buf[strlen(buf)-1] = 0;
438                         while ((strlen(buf) > 0) && (isspace(buf[strlen(buf) - 1])))
439                                 buf[strlen(buf) - 1] = 0;
440                         if ((bq == 0) &&
441                         ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) || (!strncmp(buf, " :-)", 4)))) {
442                                 wprintf("<SPAN CLASS=\"pull_quote\">");
443                                 bq = 1;
444                         } else if ((bq == 1) &&
445                                 (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) && (strncmp(buf, " :-)", 4))) {
446                                 wprintf("</SPAN>");
447                                 bq = 0;
448                         }
449                         wprintf("<TT>");
450                         url(buf);
451                         escputs(buf);
452                         wprintf("</TT><BR>\n");
453                 }
454                 wprintf("</I><BR>");
455         }
456
457         else /* HTML is fun, but we've got to strip it first */
458         if (!strcasecmp(mime_content_type, "text/html")) {
459                 output_html();
460         }
461
462         /* Unknown weirdness */
463         else {
464                 wprintf("I don't know how to display %s<BR>\n",
465                         mime_content_type);
466                 while (serv_gets(buf), strcmp(buf, "000")) { }
467         }
468
469
470         /* Afterwards, offer links to download attachments 'n' such */
471         if (strlen(mime_http) > 0) {
472                 wprintf("%s", mime_http);
473         }
474
475         /* Handler for vCard parts */
476         if (strlen(vcard_partnum) > 0) {
477                 part_source = load_mimepart(msgnum, vcard_partnum);
478                 if (part_source != NULL) {
479
480                         /* If it's my vCard I can edit it */
481                         if ( (!strcasecmp(WC->wc_roomname, USERCONFIGROOM))
482                            || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM))) {
483                                 wprintf("<A HREF=\"/edit_vcard?"
484                                         "msgnum=%ld&partnum=%s\">",
485                                         msgnum, vcard_partnum);
486                                 wprintf("(edit)</A>");
487                         }
488
489                         /* In all cases, display the full card */
490                         display_vcard(part_source, 0, 1);
491                 }
492         }
493
494         /* Handler for calendar parts */
495         if (strlen(cal_partnum) > 0) {
496                 part_source = load_mimepart(msgnum, cal_partnum);
497                 if (part_source != NULL) {
498                         cal_process_attachment(part_source,
499                                                 msgnum, cal_partnum);
500                 }
501         }
502
503         if (part_source) {
504                 free(part_source);
505                 part_source = NULL;
506         }
507
508 ENDBODY:
509         wprintf("</TD></TR></TABLE>\n");
510 }
511
512
513 void summarize_message(long msgnum) {
514         char buf[SIZ];
515
516         struct {
517                 char date[SIZ];
518                 char from[SIZ];
519                 char to[SIZ];
520                 char subj[SIZ];
521                 int hasattachments;
522         } summ;
523
524         memset(&summ, 0, sizeof(summ));
525         strcpy(summ.subj, "(no subject)");
526
527         sprintf(buf, "MSG0 %ld|1", msgnum);     /* ask for headers only */
528         serv_puts(buf);
529         serv_gets(buf);
530         if (buf[0] != '1') return;
531
532         while (serv_gets(buf), strcmp(buf, "000")) {
533                 if (!strncasecmp(buf, "from=", 5)) {
534                         strcpy(summ.from, &buf[5]);
535                 }
536                 if (!strncasecmp(buf, "subj=", 5)) {
537                         strcpy(summ.subj, &buf[5]);
538                 }
539                 if (!strncasecmp(buf, "rfca=", 5)) {
540                         strcat(summ.from, " <");
541                         strcat(summ.from, &buf[5]);
542                         strcat(summ.from, ">");
543                 }
544
545                 if (!strncasecmp(buf, "node=", 5)) {
546                         if ( ((WC->room_flags & QR_NETWORK)
547                         || ((strcasecmp(&buf[5], serv_info.serv_nodename)
548                         && (strcasecmp(&buf[5], serv_info.serv_fqdn)))))
549                         ) {
550                                 strcat(summ.from, " @ ");
551                                 strcat(summ.from, &buf[5]);
552                         }
553                 }
554
555                 if (!strncasecmp(buf, "rcpt=", 5)) {
556                         strcpy(summ.to, &buf[5]);
557                 }
558
559                 if (!strncasecmp(buf, "time=", 5)) {
560                         fmt_date(summ.date, atol(&buf[5]));
561                 }
562         }
563
564         wprintf("<TD><A HREF=\"/readfwd?startmsg=%ld"
565                 "&maxmsgs=1&summary=0\">", 
566                 msgnum);
567         escputs(summ.subj);
568         wprintf("</A></TD><TD>");
569         escputs(summ.from);
570         wprintf(" </TD><TD>");
571         escputs(summ.date);
572         wprintf(" </TD>");
573         wprintf("<TD>"
574                 "<INPUT TYPE=\"checkbox\" NAME=\"msg_%ld\" VALUE=\"yes\">"
575                 "</TD>\n");
576
577         return;
578 }
579
580
581
582 void display_addressbook(long msgnum, char alpha) {
583         char buf[SIZ];
584         char mime_partnum[SIZ];
585         char mime_filename[SIZ];
586         char mime_content_type[SIZ];
587         char mime_disposition[SIZ];
588         int mime_length;
589         char vcard_partnum[SIZ];
590         char *vcard_source = NULL;
591
592         struct {
593                 char date[SIZ];
594                 char from[SIZ];
595                 char to[SIZ];
596                 char subj[SIZ];
597                 int hasattachments;
598         } summ;
599
600         memset(&summ, 0, sizeof(summ));
601         strcpy(summ.subj, "(no subject)");
602
603         sprintf(buf, "MSG0 %ld|1", msgnum);     /* ask for headers only */
604         serv_puts(buf);
605         serv_gets(buf);
606         if (buf[0] != '1') return;
607
608         while (serv_gets(buf), strcmp(buf, "000")) {
609                 if (!strncasecmp(buf, "part=", 5)) {
610                         extract(mime_filename, &buf[5], 1);
611                         extract(mime_partnum, &buf[5], 2);
612                         extract(mime_disposition, &buf[5], 3);
613                         extract(mime_content_type, &buf[5], 4);
614                         mime_length = extract_int(&buf[5], 5);
615
616                         if (!strcasecmp(mime_content_type, "text/x-vcard")) {
617                                 strcpy(vcard_partnum, mime_partnum);
618                         }
619
620                 }
621         }
622
623         if (strlen(vcard_partnum) > 0) {
624                 vcard_source = load_mimepart(msgnum, vcard_partnum);
625                 if (vcard_source != NULL) {
626
627                         /* Display the summary line */
628                         display_vcard(vcard_source, alpha, 0);
629
630                         /* If it's my vCard I can edit it */
631                         if ( (!strcasecmp(WC->wc_roomname, USERCONFIGROOM))
632                            || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM))) {
633                                 wprintf("<A HREF=\"/edit_vcard?"
634                                         "msgnum=%ld&partnum=%s\">",
635                                         msgnum, vcard_partnum);
636                                 wprintf("(edit)</A>");
637                         }
638
639                         free(vcard_source);
640                 }
641         }
642
643 }
644
645
646
647 /* 
648  * load message pointers from the server
649  */
650 int load_msg_ptrs(char *servcmd)
651 {
652         char buf[SIZ];
653         int nummsgs;
654
655         nummsgs = 0;
656         serv_puts(servcmd);
657         serv_gets(buf);
658         if (buf[0] != '1') {
659                 wprintf("<EM>%s</EM><BR>\n", &buf[4]);
660                 return (nummsgs);
661         }
662         while (serv_gets(buf), strcmp(buf, "000")) {
663                 WC->msgarr[nummsgs] = atol(buf);
664                 /* FIXME check for overflow */
665                 ++nummsgs;
666         }
667         return (nummsgs);
668 }
669
670
671 /*
672  * command loop for reading messages
673  */
674 void readloop(char *oper)
675 {
676         char cmd[SIZ];
677         char buf[SIZ];
678         int a, b, i;
679         int nummsgs;
680         long startmsg;
681         int maxmsgs;
682         int num_displayed = 0;
683         int is_summary = 0;
684         int is_addressbook = 0;
685         int is_calendar = 0;
686         int is_tasks = 0;
687         int remaining_messages;
688         int lo, hi;
689         int lowest_displayed = (-1);
690         int highest_displayed = 0;
691         long pn_previous = 0L;
692         long pn_current = 0L;
693         long pn_next = 0L;
694         int bg = 0;
695         char alpha = 0;
696
697         startmsg = atol(bstr("startmsg"));
698         maxmsgs = atoi(bstr("maxmsgs"));
699         is_summary = atoi(bstr("summary"));
700         if (maxmsgs == 0) maxmsgs = 20;
701
702         output_headers(1);
703
704         if (!strcmp(oper, "readnew")) {
705                 strcpy(cmd, "MSGS NEW");
706         } else if (!strcmp(oper, "readold")) {
707                 strcpy(cmd, "MSGS OLD");
708         } else {
709                 strcpy(cmd, "MSGS ALL");
710         }
711
712         /* FIXME put in the correct constant #defs */
713         if ((WC->wc_view == 1) && (maxmsgs > 1)) {
714                 is_summary = 1;
715                 strcpy(cmd, "MSGS ALL");
716                 maxmsgs = 32767;
717         }
718         if ((WC->wc_view == 2) && (maxmsgs > 1)) {
719                 is_addressbook = 1;
720                 strcpy(cmd, "MSGS ALL");
721                 maxmsgs = 32767;
722                 if (bstr("alpha") == NULL) {
723                         alpha = 'A';
724                 }
725                 else {
726                         strcpy(buf, bstr("alpha"));
727                         alpha = buf[0];
728                 }
729
730                 for (i='A'; i<='Z'; ++i) {
731                         if (i == alpha) wprintf("<FONT SIZE=+2>"
732                                                 "%c</FONT>\n", i);
733                         else {
734                                 wprintf("<A HREF=\"/readfwd?alpha=%c\">"
735                                         "%c</A>\n", i, i);
736                         }
737                         wprintf("&nbsp;");
738                 }
739                 if (!isalpha(alpha)) wprintf("<FONT SIZE=+2>(other)</FONT>\n");
740                 else wprintf("<A HREF=\"/readfwd?alpha=1\">(other)</A>\n");
741                 wprintf("<HR width=100%%>\n");
742         }
743         if (WC->wc_view == 3) {         /* calendar */
744                 is_calendar = 1;
745                 strcpy(cmd, "MSGS ALL");
746                 maxmsgs = 32767;
747         }
748         if (WC->wc_view == 4) {         /* tasks */
749                 is_tasks = 1;
750                 strcpy(cmd, "MSGS ALL");
751                 maxmsgs = 32767;
752                 wprintf("<UL>");
753         }
754
755         nummsgs = load_msg_ptrs(cmd);
756         if (nummsgs == 0) {
757
758                 if ((!is_tasks) && (!is_calendar)) {
759                         if (!strcmp(oper, "readnew")) {
760                                 wprintf("<EM>No new messages in this room.</EM>\n");
761                         } else if (!strcmp(oper, "readold")) {
762                                 wprintf("<EM>No old messages in this room.</EM>\n");
763                         } else {
764                                 wprintf("<EM>This room is empty.</EM>\n");
765                         }
766                 }
767
768                 goto DONE;
769         }
770
771         if (startmsg == 0L) startmsg = WC->msgarr[0];
772         remaining_messages = 0;
773
774         for (a = 0; a < nummsgs; ++a) {
775                 if (WC->msgarr[a] >= startmsg) {
776                         ++remaining_messages;
777                 }
778         }
779
780         if (is_summary) {
781                 wprintf("<FORM METHOD=\"POST\" ACTION=\"/do_stuff_to_msgs\">\n"
782                         "<TABLE border=0 cellspacing=0 "
783                         "cellpadding=0 width=100%%>\n"
784                         "<TR>"
785                         "<TD><I>Subject</I></TD>"
786                         "<TD><I>Sender</I></TD>"
787                         "<TD><I>Date</I></TD>"
788                         "<TD></TD>"
789                         "</TR>\n"
790                 );
791         }
792
793         if (is_addressbook) {
794                 wprintf("<TABLE border=0 cellspacing=0 "
795                         "cellpadding=0 width=100%%>\n"
796                 );
797         }
798
799         for (a = 0; a < nummsgs; ++a) {
800                 if ((WC->msgarr[a] >= startmsg) && (num_displayed < maxmsgs)) {
801
802                         /* Learn which msgs "Prev" & "Next" buttons go to */
803                         pn_current = WC->msgarr[a];
804                         if (a > 0) pn_previous = WC->msgarr[a-1];
805                         if (a < (nummsgs-1)) pn_next = WC->msgarr[a+1];
806
807                         /* If a tabular view, set up the line */
808                         if ( (is_summary) || (is_addressbook) ) {
809                                 bg = 1 - bg;
810                                 wprintf("<TR BGCOLOR=\"#%s\">",
811                                         (bg ? "DDDDDD" : "FFFFFF")
812                                 );
813                         }
814
815                         /* Display the message */
816                         if (is_summary) {
817                                 summarize_message(WC->msgarr[a]);
818                         }
819                         else if (is_addressbook) {
820                                 display_addressbook(WC->msgarr[a], alpha);
821                         }
822                         else if (is_calendar) {
823                                 display_calendar(WC->msgarr[a]);
824                         }
825                         else if (is_tasks) {
826                                 display_task(WC->msgarr[a]);
827                         }
828                         else {
829                                 read_message(WC->msgarr[a]);
830                         }
831
832                         /* If a tabular view, finish the line */
833                         if ( (is_summary) || (is_addressbook) ) {
834                                 wprintf("</TR>\n");
835                         }
836
837                         if (lowest_displayed < 0) lowest_displayed = a;
838                         highest_displayed = a;
839
840                         ++num_displayed;
841                         --remaining_messages;
842                 }
843         }
844
845         if (is_summary) {
846                 wprintf("</TABLE>\n");
847         }
848
849         if (is_addressbook) {
850                 wprintf("</TABLE>\n");
851         }
852
853         if (is_tasks) {
854                 wprintf("</UL>\n");
855         }
856
857         /* Bump these because although we're thinking in zero base, the user
858          * is a drooling idiot and is thinking in one base.
859          */
860         ++lowest_displayed;
861         ++highest_displayed;
862
863         /* If we're only looking at one message, do a prev/next thing */
864         if (num_displayed == 1) {
865            if ((!is_tasks) && (!is_calendar)) {
866
867                 wprintf("<CENTER>"
868                         "<TABLE BORDER=0 WIDTH=100%% BGCOLOR=\"#DDDDDD\"><TR><TD>"
869                         "Reading #%d of %d messages.</TD>\n"
870                         "<TD ALIGN=RIGHT><FONT SIZE=+1>",
871                         lowest_displayed, nummsgs);
872
873                 if (is_summary) {
874                         wprintf("<INPUT TYPE=\"submit\" NAME=\"sc\" "
875                                 "VALUE=\"Delete selected\">\n");
876                 }
877
878                 if (pn_previous > 0L) {
879                         wprintf("<A HREF=\"/%s"
880                                 "?startmsg=%ld"
881                                 "&maxmsgs=1"
882                                 "&summary=0\">"
883                                 "Previous</A> \n",
884                                         oper,
885                                         pn_previous );
886                 }
887
888                 if (pn_next > 0L) {
889                         wprintf("<A HREF=\"/%s"
890                                 "?startmsg=%ld"
891                                 "&maxmsgs=1"
892                                 "&summary=0\">"
893                                 "Next</A> \n",
894                                         oper,
895                                         pn_next );
896                 }
897
898                 wprintf("<A HREF=\"/%s?startmsg=%ld"
899                         "&maxmsgs=999999&summary=1\">"
900                         "Summary"
901                         "</A>",
902                         oper,
903                         WC->msgarr[0]);
904
905                 wprintf("</TD></TR></TABLE></CENTER>\n");
906             }
907         }
908
909
910         /*
911          * If we're not currently looking at ALL requested
912          * messages, then display the selector bar
913          */
914         if (num_displayed > 1) {
915            if ((!is_tasks) && (!is_calendar)) {
916                 wprintf("<CENTER>"
917                         "<TABLE BORDER=0 WIDTH=100%% BGCOLOR=\"#DDDDDD\"><TR><TD>"
918                         "Reading #%d-%d of %d messages.</TD>\n"
919                         "<TD ALIGN=RIGHT><FONT SIZE=+1>",
920                         lowest_displayed, highest_displayed, nummsgs);
921
922                 if (is_summary) {
923                         wprintf("<INPUT TYPE=\"submit\" NAME=\"sc\" "
924                                 "VALUE=\"Delete selected\">\n");
925                 }
926
927
928                 for (b=0; b<nummsgs; b = b + maxmsgs) {
929                 lo = b+1;
930                 hi = b+maxmsgs+1;
931                 if (hi > nummsgs) hi = nummsgs;
932                         if (WC->msgarr[b] != startmsg) {
933                                 wprintf("<A HREF=\"/%s"
934                                         "?startmsg=%ld"
935                                         "&maxmsgs=%d"
936                                         "&summary=%d\">"
937                                         "%d-%d</A> \n",
938                                                 oper,
939                                                 WC->msgarr[b],
940                                                 maxmsgs,
941                                                 is_summary,
942                                                 lo, hi);
943                         }
944                         else {
945                                 wprintf("%d-%d \n", lo, hi);
946                         }
947
948                 }
949                 wprintf("<A HREF=\"/%s?startmsg=%ld"
950                         "&maxmsgs=999999&summary=%d\">"
951                         "ALL"
952                         "</A> ",
953                         oper,
954                         WC->msgarr[0], is_summary);
955
956                 wprintf("<A HREF=\"/%s?startmsg=%ld"
957                         "&maxmsgs=999999&summary=1\">"
958                         "Summary"
959                         "</A>",
960                         oper,
961                         WC->msgarr[0]);
962
963                 wprintf("</TD></TR></TABLE></CENTER>\n");
964             }
965         }
966         if (is_summary) wprintf("</FORM>\n");
967
968 DONE:
969         if (is_tasks) {
970                 wprintf("<A HREF=\"/display_edit_task?msgnum=0\">"
971                         "Add new task</A>\n"
972                 );
973         }
974
975         if (is_calendar) {
976                 do_calendar_view();     /* Render the calendar */
977         }
978
979         wDumpContent(1);
980 }
981
982
983 /*
984  * Back end for post_message() ... this is where the actual message
985  * gets transmitted to the server.
986  */
987 void post_mime_to_server(void) {
988         char boundary[SIZ];
989         int is_multipart = 0;
990         static int seq = 0;
991         struct wc_attachment *att;
992         char *encoded;
993         size_t encoded_length;
994         
995         /* If there are attachments, we have to do multipart/mixed */
996         if (WC->first_attachment != NULL) {
997                 is_multipart = 1;
998         }
999
1000         if (is_multipart) {
1001                 sprintf(boundary, "---Citadel-Multipart-%s-%04x%04x---",
1002                         serv_info.serv_fqdn,
1003                         getpid(),
1004                         ++seq
1005                 );
1006
1007                 /* Remember, serv_printf() appends an extra newline */
1008                 serv_printf("Content-type: multipart/mixed; "
1009                         "boundary=\"%s\"\n", boundary);
1010                 serv_printf("This is a multipart message in MIME format.\n");
1011                 serv_printf("--%s", boundary);
1012         }
1013
1014         serv_puts("Content-type: text/html");
1015         serv_puts("");
1016         text_to_server(bstr("msgtext"), 1);
1017
1018         if (is_multipart) {
1019
1020                 /* Add in the attachments */
1021                 for (att = WC->first_attachment; att!=NULL; att=att->next) {
1022
1023                         encoded_length = ((att->length * 150) / 100);
1024                         encoded = malloc(encoded_length);
1025                         if (encoded == NULL) break;
1026                         CtdlEncodeBase64(encoded, att->data, att->length);
1027
1028                         serv_printf("--%s", boundary);
1029                         serv_printf("Content-type: %s", att->content_type);
1030                         serv_printf("Content-disposition: attachment; "
1031                                 "filename=\"%s\"", att->filename);
1032                         serv_puts("Content-transfer-encoding: base64");
1033                         serv_puts("");
1034                         serv_write(encoded, strlen(encoded));
1035                         serv_puts("");
1036                         serv_puts("");
1037                         free(encoded);
1038                 }
1039                 serv_printf("--%s--", boundary);
1040         }
1041
1042         serv_puts("000");
1043 }
1044
1045
1046 /*
1047  * Post message (or don't post message)
1048  *
1049  * Note regarding the "dont_post" variable:
1050  * A random value (actually, it's just a timestamp) is inserted as a hidden
1051  * field called "postseq" when the display_enter page is generated.  This
1052  * value is checked when posting, using the static variable dont_post.  If a
1053  * user attempts to post twice using the same dont_post value, the message is
1054  * discarded.  This prevents the accidental double-saving of the same message
1055  * if the user happens to click the browser "back" button.
1056  */
1057 void post_message(void)
1058 {
1059         char buf[SIZ];
1060         static long dont_post = (-1L);
1061         struct wc_attachment *att;
1062
1063         if (WC->upload_length > 0) {
1064
1065                 att = malloc(sizeof(struct wc_attachment));
1066                 memset(att, 0, sizeof(struct wc_attachment));
1067                 att->next = WC->first_attachment;
1068                 WC->first_attachment = att;
1069                 att->length = WC->upload_length;
1070                 strcpy(att->content_type, WC->upload_content_type);
1071                 strcpy(att->filename, WC->upload_filename);
1072
1073                 /* Netscape sends a simple filename, which is what we want,
1074                  * but Satan's browser sends an entire pathname.  Reduce
1075                  * the path to just a filename if we need to.
1076                  */
1077                 while (num_tokens(att->filename, '/') > 1) {
1078                         remove_token(att->filename, 0, '/');
1079                 }
1080                 while (num_tokens(att->filename, '\\') > 1) {
1081                         remove_token(att->filename, 0, '\\');
1082                 }
1083
1084                 /* Transfer control of this memory from the upload struct
1085                  * to the attachment struct.
1086                  */
1087                 att->data = WC->upload;
1088                 WC->upload_length = 0;
1089                 WC->upload = NULL;
1090                 display_enter();
1091                 return;
1092         }
1093
1094         if (strcasecmp(bstr("sc"), "Save message")) {
1095                 sprintf(WC->ImportantMessage, 
1096                         "Cancelled.  Message was not posted.");
1097         } else if (atol(bstr("postseq")) == dont_post) {
1098                 sprintf(WC->ImportantMessage, 
1099                         "Automatically cancelled because you have already "
1100                         "saved this message.");
1101         } else {
1102                 sprintf(buf, "ENT0 1|%s|0|4|%s",
1103                         bstr("recp"),
1104                         bstr("subject") );
1105                 serv_puts(buf);
1106                 serv_gets(buf);
1107                 if (buf[0] == '4') {
1108                         post_mime_to_server();
1109                         sprintf(WC->ImportantMessage, 
1110                                 "Message has been posted.\n");
1111                         dont_post = atol(bstr("postseq"));
1112                 } else {
1113                         sprintf(WC->ImportantMessage, 
1114                                 "%s", &buf[4]);
1115                 }
1116         }
1117
1118         free_attachments(WC);
1119         readloop("readnew");
1120 }
1121
1122
1123
1124
1125 /*
1126  * display the message entry screen
1127  */
1128 void display_enter(void)
1129 {
1130         char buf[SIZ];
1131         long now;
1132         struct wc_attachment *att;
1133
1134         output_headers(1);
1135
1136         sprintf(buf, "ENT0 0|%s|0|0", bstr("recp"));
1137         serv_puts(buf);
1138         serv_gets(buf);
1139
1140         if (!strncmp(buf, "570", 3)) {
1141                 if (strlen(bstr("recp")) > 0) {
1142                         svprintf("RECPERROR", WCS_STRING,
1143                                 "<SPAN CLASS=\"errormsg\">%s</SPAN><BR>\n",
1144                                 &buf[4]
1145                         );
1146                 }
1147                 do_template("prompt_for_recipient");
1148                 goto DONE;
1149         }
1150         if (buf[0] != '2') {
1151                 wprintf("<EM>%s</EM><BR>\n", &buf[4]);
1152                 goto DONE;
1153         }
1154
1155         now = time(NULL);
1156         fmt_date(buf, now);
1157         strcat(&buf[strlen(buf)], " <I>from</I> ");
1158         stresc(&buf[strlen(buf)], WC->wc_username, 1);
1159         if (strlen(bstr("recp")) > 0) {
1160                 strcat(&buf[strlen(buf)], " <I>to</I> ");
1161                 stresc(&buf[strlen(buf)], bstr("recp"), 1);
1162         }
1163         strcat(&buf[strlen(buf)], " <I>in</I> ");
1164         stresc(&buf[strlen(buf)], WC->wc_roomname, 1);
1165         svprintf("BOXTITLE", WCS_STRING, buf);
1166         do_template("beginbox");
1167
1168         wprintf("<CENTER>\n");
1169
1170         wprintf("<FORM ENCTYPE=\"multipart/form-data\" "
1171                 "METHOD=\"POST\" ACTION=\"/post\" "
1172                 "NAME=\"enterform\">\n");
1173         wprintf("<INPUT TYPE=\"hidden\" NAME=\"recp\" VALUE=\"%s\">\n",
1174                 bstr("recp"));
1175         wprintf("<INPUT TYPE=\"hidden\" NAME=\"postseq\" VALUE=\"%ld\">\n",
1176                 now);
1177         wprintf("<IMG SRC=\"static/enter.gif\" ALIGN=MIDDLE ALT=\" \" "
1178                 "onLoad=\"document.enterform.msgtext.focus();\" >");
1179         wprintf("<FONT SIZE=-1>Subject (optional):</FONT>"
1180                 "<INPUT TYPE=\"text\" NAME=\"subject\" VALUE=\"");
1181         escputs(bstr("subject"));
1182         wprintf("\" MAXLENGTH=70>"
1183                 "&nbsp;&nbsp;&nbsp;"
1184                 "<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Save message\">"
1185                 "<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Cancel\"><BR>\n");
1186
1187         wprintf("<TEXTAREA NAME=\"msgtext\" wrap=soft ROWS=25 COLS=80 "
1188                 "WIDTH=80>");
1189         escputs(bstr("msgtext"));
1190         wprintf("</TEXTAREA><BR>\n");
1191
1192         /* Enumerate any attachments which are already in place... */
1193         for (att = WC->first_attachment; att != NULL; att = att->next) {
1194                 wprintf("<IMG SRC=\"/static/attachment.gif\" "
1195                         "BORDER=0 ALIGN=MIDDLE> Attachment: ");
1196                 escputs(att->filename);
1197                 wprintf(" (%s, %d bytes)<BR>\n",
1198                         att->content_type, att->length);
1199         }
1200
1201         /* Now offer the ability to attach additional files... */
1202         wprintf("Attach file: <input NAME=\"attachfile\" "
1203                 "SIZE=48 TYPE=\"file\">\n&nbsp;&nbsp;"
1204                 "<input type=\"submit\" name=\"attach\" value=\"Add\">\n");
1205
1206         wprintf("</FORM></CENTER>\n");
1207         do_template("endbox");
1208 DONE:   wDumpContent(1);
1209 }
1210
1211
1212
1213
1214
1215
1216
1217
1218 void delete_msg(void)
1219 {
1220         long msgid;
1221         char buf[SIZ];
1222
1223         msgid = atol(bstr("msgid"));
1224
1225         output_headers(1);
1226
1227         sprintf(buf, "DELE %ld", msgid);
1228         serv_puts(buf);
1229         serv_gets(buf);
1230         wprintf("<EM>%s</EM><BR>\n", &buf[4]);
1231
1232         wDumpContent(1);
1233 }
1234
1235
1236
1237
1238 /*
1239  * Confirm move of a message
1240  */
1241 void confirm_move_msg(void)
1242 {
1243         long msgid;
1244         char buf[SIZ];
1245         char targ[SIZ];
1246
1247         msgid = atol(bstr("msgid"));
1248
1249         output_headers(1);
1250
1251         wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#770000\"><TR><TD>");
1252         wprintf("<FONT SIZE=+1 COLOR=\"#FFFFFF\"");
1253         wprintf("<B>Confirm move of message</B>\n");
1254         wprintf("</FONT></TD></TR></TABLE>\n");
1255
1256         wprintf("<CENTER>");
1257
1258         wprintf("Please select the room to which you would like this message moved:<BR>\n");
1259
1260         wprintf("<FORM METHOD=\"POST\" ACTION=\"/move_msg\">\n");
1261         wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgid\" VALUE=\"%s\">\n",
1262                 bstr("msgid"));
1263
1264
1265         wprintf("<SELECT NAME=\"target_room\" SIZE=5>\n");
1266         serv_puts("LKRA");
1267         serv_gets(buf);
1268         if (buf[0] == '1') {
1269                 while (serv_gets(buf), strcmp(buf, "000")) {
1270                         extract(targ, buf, 0);
1271                         wprintf("<OPTION>");
1272                         escputs(targ);
1273                         wprintf("\n");
1274                 }
1275         }
1276         wprintf("</SELECT>\n");
1277         wprintf("<BR>\n");
1278
1279         wprintf("<INPUT TYPE=\"submit\" NAME=\"yesno\" VALUE=\"Move\">");
1280         wprintf("<INPUT TYPE=\"submit\" NAME=\"yesno\" VALUE=\"Cancel\">");
1281         wprintf("</FORM></CENTER>\n");
1282
1283         wprintf("</CENTER>\n");
1284         wDumpContent(1);
1285 }
1286
1287
1288
1289 void move_msg(void)
1290 {
1291         long msgid;
1292         char buf[SIZ];
1293
1294         msgid = atol(bstr("msgid"));
1295
1296         output_headers(1);
1297
1298         if (!strcasecmp(bstr("yesno"), "Move")) {
1299                 sprintf(buf, "MOVE %ld|%s", msgid, bstr("target_room"));
1300                 serv_puts(buf);
1301                 serv_gets(buf);
1302                 wprintf("<EM>%s</EM><BR>\n", &buf[4]);
1303         } else {
1304                 wprintf("<EM>Message not moved.</EM><BR>\n");
1305         }
1306
1307         wDumpContent(1);
1308 }
1309
1310
1311
1312 void do_stuff_to_msgs(void) {
1313         char buf[SIZ];
1314         char sc[SIZ];
1315
1316         struct stuff_t {
1317                 struct stuff_t *next;
1318                 long msgnum;
1319         };
1320
1321         struct stuff_t *stuff = NULL;
1322         struct stuff_t *ptr;
1323
1324
1325         serv_puts("MSGS ALL");
1326         serv_gets(buf);
1327
1328         if (buf[0] == '1') while (serv_gets(buf), strcmp(buf, "000")) {
1329                 ptr = malloc(sizeof(struct stuff_t));
1330                 ptr->msgnum = atol(buf);
1331                 ptr->next = stuff;
1332                 stuff = ptr;
1333         }
1334
1335         strcpy(sc, bstr("sc"));
1336
1337         while (stuff != NULL) {
1338
1339                 sprintf(buf, "msg_%ld", stuff->msgnum);
1340                 if (!strcasecmp(bstr(buf), "yes")) {
1341
1342                         if (!strcasecmp(sc, "Delete selected")) {
1343                                 serv_printf("DELE %ld", stuff->msgnum);
1344                                 serv_gets(buf);
1345                         }
1346
1347                 }
1348
1349                 ptr = stuff->next;
1350                 free(stuff);
1351                 stuff = ptr;
1352         }
1353
1354         readloop("readfwd");
1355 }