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