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