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