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