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