]> code.citadel.org Git - citadel.git/blob - citadel/messages.c
* Image viewer code for the text client. Hit 'I' to view an image
[citadel.git] / citadel / messages.c
1 /*
2  * $Id$
3  *
4  * Citadel/UX message support routines
5  * see copyright.txt for copyright information
6  */
7
8 #include "sysdep.h"
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <sys/types.h>
12 #include <fcntl.h>
13 #include <stdio.h>
14 #include <ctype.h>
15 #include <string.h>
16 #include <signal.h>
17 #include <errno.h>
18 #include <limits.h>
19 #include <sys/wait.h>
20 #include <sys/stat.h>
21
22 #if TIME_WITH_SYS_TIME
23 # include <sys/time.h>
24 # include <time.h>
25 #else
26 # if HAVE_SYS_TIME_H
27 #  include <sys/time.h>
28 # else
29 #  include <time.h>
30 # endif
31 #endif
32
33 #include <stdarg.h>
34 #include "citadel.h"
35 #include "citadel_ipc.h"
36 #include "citadel_decls.h"
37 #include "messages.h"
38 #include "commands.h"
39 #include "rooms.h"
40 #include "tools.h"
41 #include "html.h"
42 #ifndef HAVE_SNPRINTF
43 #include "snprintf.h"
44 #endif
45 #include "screen.h"
46
47 #define MAXWORDBUF SIZ
48 #define NO_REPLY_TO     "nobody ... xxxxxx"
49
50 char reply_to[SIZ];
51 char reply_subject[SIZ];
52
53 struct cittext {
54         struct cittext *next;
55         char text[MAXWORDBUF];
56 };
57
58 void sttybbs(int cmd);
59 int haschar(const char *st, int ch);
60 void getline(char *string, int lim);
61 int file_checksum(char *filename);
62 void progress(unsigned long curr, unsigned long cmax);
63
64 unsigned long *msg_arr = NULL;
65 int msg_arr_size = 0;
66 int num_msgs;
67 char rc_alt_semantics;
68 char rc_reply_extedit;
69 extern char room_name[];
70 extern unsigned room_flags;
71 extern long highest_msg_read;
72 extern struct CtdlServInfo serv_info;
73 extern char temp[];
74 extern char temp2[];
75 extern int screenwidth;
76 extern int screenheight;
77 extern long maxmsgnum;
78 extern char is_mail;
79 extern char is_aide;
80 extern char is_room_aide;
81 extern char fullname[];
82 extern char axlevel;
83 extern unsigned userflags;
84 extern char sigcaught;
85 extern char printcmd[];
86 extern int rc_allow_attachments;
87 extern int rc_display_message_numbers;
88 extern int rc_force_mail_prompts;
89 extern int editor_pid;
90 extern CtdlIPC *ipc_for_signal_handlers;        /* KLUDGE cover your eyes */
91 int num_urls = 0;
92 char urls[MAXURLS][SIZ];
93 char imagecmd[SIZ];
94 int has_images = 0;                             /* Current msg has images */
95 struct parts *last_message_parts = NULL;        /* Parts from last msg */
96
97 void ka_sigcatch(int signum)
98 {
99         alarm(S_KEEPALIVE);
100         signal(SIGALRM, ka_sigcatch);
101         CtdlIPCNoop(ipc_for_signal_handlers);
102 }
103
104
105 /*
106  * server keep-alive version of wait() (needed for external editor)
107  */
108 pid_t ka_wait(int *kstatus)
109 {
110         pid_t p;
111
112         alarm(S_KEEPALIVE);
113         signal(SIGALRM, ka_sigcatch);
114         do {
115                 errno = 0;
116                 p = wait(kstatus);
117         } while (errno == EINTR);
118         signal(SIGALRM, SIG_IGN);
119         alarm(0);
120         return (p);
121 }
122
123
124 /*
125  * version of system() that uses ka_wait()
126  */
127 int ka_system(char *shc)
128 {
129         pid_t childpid;
130         pid_t waitpid;
131         int retcode;
132
133         childpid = fork();
134         if (childpid < 0) {
135                 color(BRIGHT_RED);
136                 perror("Cannot fork");
137                 color(DIM_WHITE);
138                 return ((pid_t) childpid);
139         }
140
141         if (childpid == 0) {
142                 execlp("/bin/sh", "sh", "-c", shc, NULL);
143                 exit(127);
144         }
145
146         if (childpid > 0) {
147                 do {
148                         waitpid = ka_wait(&retcode);
149                 } while (waitpid != childpid);
150                 return (retcode);
151         }
152
153         return (-1);
154 }
155
156
157
158 /*
159  * add a newline to the buffer...
160  */
161 void add_newline(struct cittext *textlist)
162 {
163         struct cittext *ptr;
164
165         ptr = textlist;
166         while (ptr->next != NULL)
167                 ptr = ptr->next;
168
169         while (ptr->text[strlen(ptr->text) - 1] == 32)
170                 ptr->text[strlen(ptr->text) - 1] = 0;
171         /* strcat(ptr->text,"\n"); */
172
173         ptr->next = (struct cittext *)
174             malloc(sizeof(struct cittext));
175         ptr = ptr->next;
176         ptr->next = NULL;
177         strcpy(ptr->text, "");
178 }
179
180
181 /*
182  * add a word to the buffer...
183  */
184 void add_word(struct cittext *textlist, char *wordbuf)
185 {
186         struct cittext *ptr;
187
188         ptr = textlist;
189         while (ptr->next != NULL)
190                 ptr = ptr->next;
191
192         if (3 + strlen(ptr->text) + strlen(wordbuf) > screenwidth) {
193                 ptr->next = (struct cittext *)
194                     malloc(sizeof(struct cittext));
195                 ptr = ptr->next;
196                 ptr->next = NULL;
197                 strcpy(ptr->text, "");
198         }
199
200         strcat(ptr->text, wordbuf);
201         strcat(ptr->text, " ");
202 }
203
204
205 /*
206  * begin editing of an opened file pointed to by fp
207  */
208 void citedit(CtdlIPC *ipc, FILE * fp)
209 {
210         int a, prev, finished, b, last_space;
211         int appending = 0;
212         struct cittext *textlist = NULL;
213         struct cittext *ptr;
214         char wordbuf[MAXWORDBUF];
215
216         /* first, load the text into the buffer */
217         fseek(fp, 0L, 0);
218         textlist = (struct cittext *) malloc(sizeof(struct cittext));
219         textlist->next = NULL;
220         strcpy(textlist->text, "");
221
222         strcpy(wordbuf, "");
223         prev = (-1);
224         while (a = getc(fp), a >= 0) {
225                 appending = 1;
226                 if ((a == 32) || (a == 9) || (a == 13) || (a == 10)) {
227                         add_word(textlist, wordbuf);
228                         strcpy(wordbuf, "");
229                         if ((prev == 13) || (prev == 10)) {
230                                 add_word(textlist, "\n");
231                                 add_newline(textlist);
232                                 add_word(textlist, "");
233                         }
234                 } else {
235                         wordbuf[strlen(wordbuf) + 1] = 0;
236                         wordbuf[strlen(wordbuf)] = a;
237                 }
238                 if (strlen(wordbuf) + 3 > screenwidth) {
239                         add_word(textlist, wordbuf);
240                         strcpy(wordbuf, "");
241                 }
242                 prev = a;
243         }
244
245         /* get text */
246         finished = 0;
247         prev = (appending ? 13 : (-1));
248         strcpy(wordbuf, "");
249         async_ka_start();
250         do {
251                 a = inkey();
252                 if (a == 10)
253                         a = 13;
254                 if (a == 9)
255                         a = 32;
256                 if (a == 127)
257                         a = 8;
258
259         /******* new ***********/
260                 if ((a > 32) && (a < 127) && (prev == 13)) {
261                         add_word(textlist, "\n");
262                         scr_printf(" ");
263                 }
264         /***********************/
265
266                 if ((a == 32) && (prev == 13)) {
267                         add_word(textlist, "\n");
268                         add_newline(textlist);
269                 }
270
271                 if (a == 8) {
272                         if (strlen(wordbuf) > 0) {
273                                 wordbuf[strlen(wordbuf) - 1] = 0;
274                                 scr_putc(8);
275                                 scr_putc(32);
276                                 scr_putc(8);
277                         }
278                 } else if (a == 23) {
279                         do {
280                                 wordbuf[strlen(wordbuf) - 1] = 0;
281                                 scr_putc(8);
282                                 scr_putc(32);
283                                 scr_putc(8);
284                         } while (strlen(wordbuf) && wordbuf[strlen(wordbuf) - 1] != ' ');
285                 } else if (a == 13) {
286                         scr_printf("\n");
287                         if (strlen(wordbuf) == 0)
288                                 finished = 1;
289                         else {
290                                 for (b = 0; b < strlen(wordbuf); ++b)
291                                         if (wordbuf[b] == 32) {
292                                                 wordbuf[b] = 0;
293                                                 add_word(textlist,
294                                                          wordbuf);
295                                                 strcpy(wordbuf,
296                                                        &wordbuf[b + 1]);
297                                                 b = 0;
298                                         }
299                                 add_word(textlist, wordbuf);
300                                 strcpy(wordbuf, "");
301                         }
302                 } else {
303                         scr_putc(a);
304                         wordbuf[strlen(wordbuf) + 1] = 0;
305                         wordbuf[strlen(wordbuf)] = a;
306                 }
307                 if ((strlen(wordbuf) + 3) > screenwidth) {
308                         last_space = (-1);
309                         for (b = 0; b < strlen(wordbuf); ++b)
310                                 if (wordbuf[b] == 32)
311                                         last_space = b;
312                         if (last_space >= 0) {
313                                 for (b = 0; b < strlen(wordbuf); ++b)
314                                         if (wordbuf[b] == 32) {
315                                                 wordbuf[b] = 0;
316                                                 add_word(textlist,
317                                                          wordbuf);
318                                                 strcpy(wordbuf,
319                                                        &wordbuf[b + 1]);
320                                                 b = 0;
321                                         }
322                                 for (b = 0; b < strlen(wordbuf); ++b) {
323                                         scr_putc(8);
324                                         scr_putc(32);
325                                         scr_putc(8);
326                                 }
327                                 scr_printf("\n%s", wordbuf);
328                         } else {
329                                 add_word(textlist, wordbuf);
330                                 strcpy(wordbuf, "");
331                                 scr_printf("\n");
332                         }
333                 }
334                 prev = a;
335         } while (finished == 0);
336         async_ka_end();
337
338         /* write the buffer back to disk */
339         fseek(fp, 0L, 0);
340         for (ptr = textlist; ptr != NULL; ptr = ptr->next) {
341                 fprintf(fp, "%s", ptr->text);
342         }
343         putc(10, fp);
344         fflush(fp);
345         ftruncate(fileno(fp), ftell(fp));
346
347         /* and deallocate the memory we used */
348         while (textlist != NULL) {
349                 ptr = textlist->next;
350                 free(textlist);
351                 textlist = ptr;
352         }
353 }
354
355 /* Read a message from the server
356  */
357 int read_message(CtdlIPC *ipc,
358         long num,   /* message number */
359         int pagin, /* 0 = normal read, 1 = read with pagination, 2 = header */
360         FILE *dest) /* Destination file, NULL for screen */
361 {
362         char buf[SIZ];
363         char now[SIZ];
364         int format_type = 0;
365         int fr = 0;
366         int nhdr = 0;
367         struct ctdlipcmessage *message = NULL;
368         int r;                          /* IPC response code */
369         char *converted_text = NULL;
370         char *lineptr;
371         char *nextline;
372         char *searchptr;
373         int i;
374         char ch;
375         int linelen;
376         int final_line_is_blank = 0;
377
378         has_images = 0;
379
380         sigcaught = 0;
381         sttybbs(1);
382
383         strcpy(reply_to, NO_REPLY_TO);
384         strcpy(reply_subject, "");
385
386         r = CtdlIPCGetSingleMessage(ipc, num, (pagin == READ_HEADER ? 1 : 0),
387                                 (can_do_msg4 ? 4 : 0),
388                                 &message, buf);
389         if (r / 100 != 1) {
390                 err_printf("*** msg #%ld: %d %s\n", num, r, buf);
391                 ++lines_printed;
392                 lines_printed =
393                     checkpagin(lines_printed, pagin, screenheight);
394                 sttybbs(0);
395                 return (0);
396         }
397
398         if (dest) {
399                 fprintf(dest, "\n ");
400         } else {
401                 scr_printf("\n");
402                 ++lines_printed;
403                 lines_printed = checkpagin(lines_printed, pagin, screenheight);
404                 if (pagin != 2)
405                         scr_printf(" ");
406         }
407         if (pagin == 1 && !dest) {
408                 color(BRIGHT_CYAN);
409         }
410
411         /* View headers only */
412         if (pagin == 2) {
413                 pprintf("nhdr=%s\nfrom=%s\ntype=%d\nmsgn=%s\n",
414                                 message->nhdr ? "yes" : "no",
415                                 message->author, message->type,
416                                 message->msgid);
417                 if (strlen(message->subject)) {
418                         pprintf("subj=%s\n", message->subject);
419                 }
420                 if (strlen(message->email)) {
421                         pprintf("rfca=%s\n", message->email);
422                 }
423                 pprintf("hnod=%s\nroom=%s\nnode=%s\ntime=%s",
424                                 message->hnod, message->room,
425                                 message->node, 
426                                 asctime(localtime(&message->time)));
427                 if (strlen(message->recipient)) {
428                         pprintf("rcpt=%s\n", message->recipient);
429                 }
430                 if (message->attachments) {
431                         struct parts *ptr;
432
433                         for (ptr = message->attachments; ptr; ptr = ptr->next) {
434                                 pprintf("part=%s|%s|%s|%s|%s|%ld\n",
435                                         ptr->name, ptr->filename, ptr->number,
436                                         ptr->disposition, ptr->mimetype,
437                                         ptr->length);
438                         }
439                 }
440                 pprintf("\n");
441                 sttybbs(0);
442                 return (0);
443         }
444
445         if (rc_display_message_numbers) {
446                 if (dest) {
447                         fprintf(dest, "[#%s] ", message->msgid);
448                 } else {
449                         color(DIM_WHITE);
450                         scr_printf("[");
451                         color(BRIGHT_WHITE);
452                         scr_printf("#%s", message->msgid);
453                         color(DIM_WHITE);
454                         scr_printf("] ");
455                 }
456         }
457         if (nhdr == 1 && !is_room_aide) {
458                 if (dest) {
459                         fprintf(dest, " ****");
460                 } else {
461                         scr_printf(" ****");
462                 }
463         } else {
464                 fmt_date(now, sizeof now, message->time, 0);
465                 if (dest) {
466                         fprintf(dest, "%s from %s ", now, message->author);
467                         if (strlen(message->email)) {
468                                 fprintf(dest, "<%s> ", message->email);
469                         }
470                 } else {
471                         color(BRIGHT_CYAN);
472                         scr_printf("%s ", now);
473                         color(DIM_WHITE);
474                         scr_printf("from ");
475                         color(BRIGHT_CYAN);
476                         scr_printf("%s ", message->author);
477                         if (strlen(message->email)) {
478                                 color(DIM_WHITE);
479                                 scr_printf("<");
480                                 color(BRIGHT_BLUE);
481                                 scr_printf("%s", message->email);
482                                         color(DIM_WHITE);
483                                 scr_printf("> ");
484                         }
485                 }
486                 if (strlen(message->node)) {
487                         if ((room_flags & QR_NETWORK)
488                             || ((strcasecmp(message->node, serv_info.serv_nodename)
489                              && (strcasecmp(message->node, serv_info.serv_fqdn))))) {
490                                 if (strlen(message->email) == 0) {
491                                         if (dest) {
492                                                 fprintf(dest, "@%s ", message->node);
493                                         } else {
494                                                 color(DIM_WHITE);
495                                                 scr_printf("@");
496                                                 color(BRIGHT_YELLOW);
497                                                 scr_printf("%s ", message->node);
498                                         }
499                                 }
500                         }
501                 }
502                 if (strcasecmp(message->hnod, serv_info.serv_humannode)
503                     && (strlen(message->hnod)) && (!strlen(message->email))) {
504                         if (dest) {
505                                 fprintf(dest, "(%s) ", message->hnod);
506                         } else {
507                                 color(DIM_WHITE);
508                                 scr_printf("(");
509                                 color(BRIGHT_WHITE);
510                                 scr_printf("%s", message->hnod);
511                                 color(DIM_WHITE);
512                                 scr_printf(") ");
513                         }
514                 }
515                 if (strcasecmp(message->room, room_name) && (strlen(message->email) == 0)) {
516                         if (dest) {
517                                 fprintf(dest, "in %s> ", message->room);
518                         } else {
519                                 color(DIM_WHITE);
520                                 scr_printf("in ");
521                                 color(BRIGHT_MAGENTA);
522                                 scr_printf("%s> ", message->room);
523                         }
524                 }
525                 if (strlen(message->recipient)) {
526                         if (dest) {
527                                 fprintf(dest, "to %s ", message->recipient);
528                         } else {
529                                 color(DIM_WHITE);
530                                 scr_printf("to ");
531                                 color(BRIGHT_CYAN);
532                                 scr_printf("%s ", message->recipient);
533                         }
534                 }
535         }
536         
537         if (dest) {
538                 fprintf(dest, "\n");
539         } else {
540                 scr_printf("\n");
541         }
542
543         /* Set the reply-to address to an Internet e-mail address if possible
544          */
545         if (message->email != NULL) if (strlen(message->email) > 0) {
546                 safestrncpy(reply_to, message->email, sizeof reply_to);
547         }
548
549         /* But if we can't do that, set it to a Citadel address.
550          */
551         if (!strcmp(reply_to, NO_REPLY_TO)) {
552                 snprintf(reply_to, sizeof(reply_to), "%s @ %s",
553                          message->author, message->node);
554         }
555
556         if (!dest) {
557                 ++lines_printed;
558                 lines_printed = checkpagin(lines_printed, pagin, screenheight);
559         }
560
561         if (message->subject != NULL) {
562                 safestrncpy(reply_subject, message->subject,
563                                                 sizeof reply_subject);
564                 if (strlen(message->subject) > 0) {
565                         if (dest) {
566                                 fprintf(dest, "Subject: %s\n",
567                                                         message->subject);
568                         } else {
569                                 color(DIM_WHITE);
570                                 scr_printf("Subject: ");
571                                 color(BRIGHT_CYAN);
572                                 scr_printf("%s\n", message->subject);
573                                 ++lines_printed;
574                                 lines_printed = checkpagin(lines_printed,
575                                                 pagin, screenheight);
576                         }
577                 }
578         }
579
580         if (pagin == 1 && !dest) {
581                 color(BRIGHT_WHITE);
582         }
583
584         /******* end of header output, start of message text output *******/
585
586         /*
587          * Convert HTML to plain text, formatting for the actual width
588          * of the client screen.
589          */
590         if (!strcasecmp(message->content_type, "text/html")) {
591                 converted_text = html_to_ascii(message->text, screenwidth, 0);
592                 if (converted_text != NULL) {
593                         free(message->text);
594                         message->text = converted_text;
595                         format_type = 1;
596                 }
597         }
598
599         /* Text/plain is a different type */
600         if (!strcasecmp(message->content_type, "text/plain")) {
601                 format_type = 1;
602         }
603
604         /* Extract URL's */
605         num_urls = 0;   /* Start with a clean slate */
606         searchptr = message->text;
607         while ( (searchptr != NULL) && (num_urls < MAXURLS) ) {
608                 searchptr = strstr(searchptr, "http://");
609                 if (searchptr != NULL) {
610                         safestrncpy(urls[num_urls], searchptr, sizeof(urls[num_urls]));
611                         for (i = 0; i < strlen(urls[num_urls]); i++) {
612                                 ch = urls[num_urls][i];
613                                 if (ch == '>' || ch == '\"' || ch == ')' ||
614                                     ch == ' ' || ch == '\n') {
615                                         urls[num_urls][i] = 0;
616                                         break;
617                                 }
618                         }
619                         num_urls++;
620                         ++searchptr;
621                 }
622         }
623
624         /*
625          * Here we go
626          */
627         if (format_type == 0) {
628                 fr = fmout(screenwidth, NULL, message->text, dest,
629                            ((pagin == 1) ? 1 : 0), screenheight, (-1), 1);
630         } else {
631                 /* renderer for text/plain */
632
633                 lineptr = message->text;
634
635                 do {
636                         nextline = strchr(lineptr, '\n');
637                         if (nextline != NULL) {
638                                 *nextline = 0;
639                                 ++nextline;
640                                 if (*nextline == 0) nextline = NULL;
641                         }
642
643                         if (sigcaught == 0) {
644                                 linelen = strlen(lineptr);
645                                 if (lineptr[linelen-1] == '\r') {
646                                         lineptr[--linelen] = 0;
647                                 }
648                                 if (dest) {
649                                         fprintf(dest, "%s\n", lineptr);
650                                 } else {
651                                         scr_printf("%s\n", lineptr);
652                                         lines_printed = lines_printed + 1 +
653                                             (linelen / screenwidth);
654                                         lines_printed =
655                                             checkpagin(lines_printed, pagin,
656                                                        screenheight);
657                                 }
658                         }
659                         if (lineptr[0] == 0) final_line_is_blank = 1;
660                         else final_line_is_blank = 0;
661                         lineptr = nextline;
662                 } while (nextline);
663                 fr = sigcaught;
664         }
665         if (!final_line_is_blank) {
666                 if (dest) {
667                         fprintf(dest, "\n");
668                 }
669                 else {
670                         scr_printf("\n");
671                         ++lines_printed;
672                         lines_printed = checkpagin(lines_printed, pagin, screenheight);
673                 }
674         }
675
676         /* Enumerate any attachments */
677         if ( (pagin == 1) && (can_do_msg4) && (message->attachments) ) {
678                 struct parts *ptr;
679
680                 for (ptr = message->attachments; ptr; ptr = ptr->next) {
681                         if ( (!strcasecmp(ptr->disposition, "attachment"))
682                            || (!strcasecmp(ptr->disposition, "inline"))) {
683                                 color(DIM_WHITE);
684                                 scr_printf("Part ");
685                                 color(BRIGHT_MAGENTA);
686                                 scr_printf("%s", ptr->number);
687                                 color(DIM_WHITE);
688                                 scr_printf(": ");
689                                 color(BRIGHT_CYAN);
690                                 scr_printf("%s", ptr->filename);
691                                 color(DIM_WHITE);
692                                 scr_printf(" (%s, %ld bytes)\n", ptr->mimetype, ptr->length);
693                                 if (!strncmp(ptr->mimetype, "image/", 6))
694                                         has_images++;
695                         }
696                 }
697         }
698
699         /* Save the attachments info for later */
700         last_message_parts = message->attachments;
701
702         /* Now we're done */
703         free(message->text);
704         free(message);
705
706         if (pagin == 1 && !dest)
707                 color(DIM_WHITE);
708         sttybbs(0);
709         return (fr);
710 }
711
712 /*
713  * replace string function for the built-in editor
714  */
715 void replace_string(char *filename, long int startpos)
716 {
717         char buf[512];
718         char srch_str[128];
719         char rplc_str[128];
720         FILE *fp;
721         int a;
722         long rpos, wpos;
723         char *ptr;
724         int substitutions = 0;
725         long msglen = 0L;
726
727         scr_printf("Enter text to be replaced:\n: ");
728         getline(srch_str, (sizeof(srch_str)-1) );
729         if (strlen(srch_str) == 0)
730                 return;
731
732         scr_printf("Enter text to replace it with:\n: ");
733         getline(rplc_str, (sizeof(rplc_str)-1) );
734
735         fp = fopen(filename, "r+");
736         if (fp == NULL)
737                 return;
738
739         wpos = startpos;
740         fseek(fp, startpos, 0);
741         strcpy(buf, "");
742         while (a = getc(fp), a > 0) {
743                 ++msglen;
744                 buf[strlen(buf) + 1] = 0;
745                 buf[strlen(buf)] = a;
746                 if (strlen(buf) >= strlen(srch_str)) {
747                         ptr = (&buf[strlen(buf) - strlen(srch_str)]);
748                         if (!strncmp(ptr, srch_str, strlen(srch_str))) {
749                                 strcpy(ptr, rplc_str);
750                                 ++substitutions;
751                         }
752                 }
753                 if (strlen(buf) > 384) {
754                         rpos = ftell(fp);
755                         fseek(fp, wpos, 0);
756                         fwrite((char *) buf, 128, 1, fp);
757                         strcpy(buf, &buf[128]);
758                         wpos = ftell(fp);
759                         fseek(fp, rpos, 0);
760                 }
761         }
762         fseek(fp, wpos, 0);
763         if (strlen(buf) > 0)
764                 fwrite((char *) buf, strlen(buf), 1, fp);
765         wpos = ftell(fp);
766         fclose(fp);
767         truncate(filename, wpos);
768         scr_printf("<R>eplace made %d substitution(s).\n\n", substitutions);
769 }
770
771 /*
772  * Function to begin composing a new message
773  */
774 int client_make_message(CtdlIPC *ipc,
775                 char *filename,         /* temporary file name */
776                 char *recipient,        /* NULL if it's not mail */
777                 int is_anonymous,
778                 int format_type,
779                 int mode,
780                 char *subject)          /* buffer to store subject line */
781 {
782         FILE *fp;
783         int a, b, e_ex_code;
784         long beg;
785         char datestr[SIZ];
786         char header[SIZ];
787         char *editor_path = NULL;
788         int cksum = 0;
789
790         if (mode >= 2)
791         {
792                 if((mode-2) < MAX_EDITORS && strlen(editor_paths[mode-2]) > 0) {
793                         editor_path = editor_paths[mode-2];
794                 } else if (strlen(editor_paths[0]) > 0) {
795                         editor_path = editor_paths[0];
796                 } else {
797                         err_printf
798                             ("*** No editor available, using built-in editor\n");
799                         mode = 0;
800                 }
801         }
802
803         fmt_date(datestr, sizeof datestr, time(NULL), 0);
804         header[0] = 0;
805
806         if (room_flags & QR_ANONONLY && !recipient) {
807                 snprintf(header, sizeof header, " ****");
808         }
809         else {
810                 snprintf(header, sizeof header,
811                         " %s from %s",
812                         datestr,
813                         (is_anonymous ? "[anonymous]" : fullname)
814                         );
815                 if (strlen(recipient) > 0) {
816                         size_t tmp = strlen(header);
817                         snprintf(&header[tmp], sizeof header - tmp,
818                                 " to %s", recipient);
819                 }
820         }
821         scr_printf("%s\n", header);
822         if (subject != NULL) if (strlen(subject) > 0) {
823                 scr_printf("Subject: %s\n", subject);
824         }
825
826         beg = 0L;
827
828         if (mode == 1) {
829                 scr_printf("(Press ctrl-d when finished)\n");
830         }
831
832         if (mode == 0) {
833                 fp = fopen(filename, "r");
834                 if (fp != NULL) {
835                         fmout(screenwidth, fp, NULL, NULL, 0, screenheight, 0, 0);
836                         beg = ftell(fp);
837                         fclose(fp);
838                 } else {
839                         fp = fopen(filename, "w");
840                         if (fp == NULL) {
841                                 err_printf("*** Error opening temp file!\n"
842                                         "    %s: %s\n",
843                                         filename, strerror(errno));
844                         return(1);
845                         }
846                         fclose(fp);
847                 }
848         }
849
850 ME1:    switch (mode) {
851
852         case 0:
853                 fp = fopen(filename, "r+");
854                 if (fp == NULL) {
855                         err_printf("*** Error opening temp file!\n"
856                                 "    %s: %s\n",
857                                 filename, strerror(errno));
858                         return(1);
859                 }
860                 citedit(ipc, fp);
861                 fclose(fp);
862                 goto MECR;
863
864         case 1:
865                 fp = fopen(filename, "a");
866                 if (fp == NULL) {
867                         err_printf("*** Error opening temp file!\n"
868                                 "    %s: %s\n",
869                                 filename, strerror(errno));
870                         return(1);
871                 }
872                 do {
873                         a = inkey();
874                         if (a == 255)
875                                 a = 32;
876                         if (a == 13)
877                                 a = 10;
878                         if (a != 4) {
879                                 putc(a, fp);
880                                 scr_putc(a);
881                         }
882                         if (a == 10)
883                                 scr_putc(10);
884                 } while (a != 4);
885                 fclose(fp);
886                 break;
887
888         case 2:
889         default:        /* allow 2+ modes */
890                 e_ex_code = 1;  /* start with a failed exit code */
891                 editor_pid = fork();
892                 cksum = file_checksum(filename);
893                 screen_reset();
894                 sttybbs(SB_RESTORE);
895                 if (editor_pid == 0) {
896                         char tmp[SIZ];
897
898                         chmod(filename, 0600);
899                         snprintf(tmp, sizeof tmp, "WINDOW_TITLE=%s", header);
900                         putenv(tmp);
901                         execlp(editor_path, editor_path, filename, NULL);
902                         exit(1);
903                 }
904                 if (editor_pid > 0)
905                         do {
906                                 e_ex_code = 0;
907                                 b = ka_wait(&e_ex_code);
908                         } while ((b != editor_pid) && (b >= 0));
909                 editor_pid = (-1);
910                 sttybbs(0);
911                 screen_set();
912                 break;
913         }
914
915 MECR:   if (mode >= 2) {
916                 if (file_checksum(filename) == cksum) {
917                         err_printf("*** Aborted message.\n");
918                         e_ex_code = 1;
919                 }
920                 if (e_ex_code == 0)
921                         goto MEFIN;
922                 goto MEABT2;
923         }
924
925         b = keymenu("Entry command (? for options)",
926                     "<A>bort|<C>ontinue|<S>ave message|<P>rint formatted|"
927                     "add s<U>bject|"
928                     "<R>eplace string|<H>old message");
929
930         if (b == 'a')
931                 goto MEABT;
932         if (b == 'c')
933                 goto ME1;
934         if (b == 's')
935                 goto MEFIN;
936         if (b == 'p') {
937                 scr_printf(" %s from %s", datestr, fullname);
938                 if (strlen(recipient) > 0)
939                         scr_printf(" to %s", recipient);
940                 scr_printf("\n");
941                 if (subject != NULL) if (strlen(subject) > 0) {
942                         scr_printf("Subject: %s\n", subject);
943                 }
944                 fp = fopen(filename, "r");
945                 if (fp != NULL) {
946                         fmout(screenwidth, fp, NULL, NULL,
947                               ((userflags & US_PAGINATOR) ? 1 : 0),
948                               screenheight, 0, 0);
949                         beg = ftell(fp);
950                         fclose(fp);
951                 }
952                 goto MECR;
953         }
954         if (b == 'r') {
955                 replace_string(filename, 0L);
956                 goto MECR;
957         }
958         if (b == 'h') {
959                 return (2);
960         }
961         if (b == 'u') {
962                 if (subject != NULL) {
963                         newprompt("Subject: ", subject, 70);
964                 }
965                 goto MECR;
966         }
967
968 MEFIN:  return (0);
969
970 MEABT:  scr_printf("Are you sure? ");
971         if (yesno() == 0) {
972                 goto ME1;
973         }
974 MEABT2: unlink(filename);
975         return (2);
976 }
977
978
979 #if 0
980 /*
981  * Transmit message text to the server.
982  * 
983  * This loop also implements a "tick" counter that displays the progress, if
984  * we're sending something that will take a long time to transmit.
985  */
986 void transmit_message(CtdlIPC *ipc, FILE *fp)
987 {
988         char buf[SIZ];
989         int ch, a;
990         long msglen;
991         time_t lasttick;
992
993         fseek(fp, 0L, SEEK_END);
994         msglen = ftell(fp);
995         rewind(fp);
996         lasttick = time(NULL);
997         strcpy(buf, "");
998         while (ch = getc(fp), (ch >= 0)) {
999                 if (ch == 10) {
1000                         if (!strcmp(buf, "000"))
1001                                 strcpy(buf, ">000");
1002                         CtdlIPC_putline(ipc, buf);
1003                         strcpy(buf, "");
1004                 } else {
1005                         a = strlen(buf);
1006                         buf[a + 1] = 0;
1007                         buf[a] = ch;
1008                         if ((ch == 32) && (strlen(buf) > 200)) {
1009                                 buf[a] = 0;
1010                                 if (!strcmp(buf, "000"))
1011                                         strcpy(buf, ">000");
1012                                 CtdlIPC_putline(ipc, buf);
1013                                 strcpy(buf, "");
1014                         }
1015                         if (strlen(buf) > 250) {
1016                                 if (!strcmp(buf, "000"))
1017                                         strcpy(buf, ">000");
1018                                 CtdlIPC_putline(ipc, buf);
1019                                 strcpy(buf, "");
1020                         }
1021                 }
1022
1023                 if ((time(NULL) - lasttick) > 2L) {
1024                         scr_printf(" %3ld%% completed\r",
1025                                ((ftell(fp) * 100L) / msglen));
1026                         scr_flush();
1027                         lasttick = time(NULL);
1028                 }
1029
1030         }
1031         CtdlIPC_putline(ipc, buf);
1032         scr_printf("                \r");
1033         scr_flush();
1034 }
1035 #endif
1036
1037
1038 /*
1039  * Make sure there's room in msg_arr[] for at least one more.
1040  */
1041 void check_msg_arr_size(void) {
1042         if ((num_msgs + 1) > msg_arr_size) {
1043                 msg_arr_size += 512;
1044                 msg_arr = realloc(msg_arr,
1045                         ((sizeof(long)) * msg_arr_size) );
1046         }
1047 }
1048
1049
1050
1051 /*
1052  * entmsg()  -  edit and create a message
1053  *              returns 0 if message was saved
1054  */
1055 int entmsg(CtdlIPC *ipc,
1056                 int is_reply,   /* nonzero if this was a <R>eply command */
1057                 int c)          /* mode */
1058 {
1059         char buf[SIZ];
1060         int a, b;
1061         int need_recp = 0;
1062         int mode;
1063         long highmsg = 0L;
1064         FILE *fp;
1065         char subject[SIZ];
1066         struct ctdlipcmessage message;
1067         unsigned long *msgarr = NULL;
1068         int r;                  /* IPC response code */
1069
1070         if (c > 0)
1071                 mode = 1;
1072         else
1073                 mode = 0;
1074
1075         strcpy(subject, "");
1076
1077         /*
1078          * First, check to see if we have permission to enter a message in
1079          * this room.  The server will return an error code if we can't.
1080          */
1081         strcpy(message.recipient, "");
1082         strcpy(message.author, "");
1083         strcpy(message.subject, "");
1084         message.text = message.author;  /* point to "", changes later */
1085         message.anonymous = 0;
1086         message.type = mode;
1087         r = CtdlIPCPostMessage(ipc, 0, &message, buf);
1088
1089         if (r / 100 != 2 && r / 10 != 57) {
1090                 scr_printf("%s\n", buf);
1091                 return (1);
1092         }
1093
1094         /* Error code 570 is special.  It means that we CAN enter a message
1095          * in this room, but a recipient needs to be specified.
1096          */
1097         need_recp = 0;
1098         if (r / 10 == 57)
1099                 need_recp = 1;
1100
1101         /* If the user is a dumbass, tell them how to type. */
1102         if ((userflags & US_EXPERT) == 0) {
1103                 formout(ipc, "entermsg");
1104         }
1105
1106         /* Handle the selection of a recipient, if necessary. */
1107         strcpy(buf, "");
1108         if (need_recp == 1) {
1109                 if (axlevel >= 2) {
1110                         if (is_reply) {
1111                                 strcpy(buf, reply_to);
1112                         } else {
1113                                 scr_printf("Enter recipient: ");
1114                                 getline(buf, (SIZ-100) );
1115                                 if (strlen(buf) == 0)
1116                                         return (1);
1117                         }
1118                 } else
1119                         strcpy(buf, "sysop");
1120         }
1121         strcpy(message.recipient, buf);
1122
1123         if (is_reply) {
1124                 if (strlen(reply_subject) > 0) {
1125                         if (!strncasecmp(reply_subject,
1126                            "Re: ", 3)) {
1127                                 strcpy(message.subject, reply_subject);
1128                         }
1129                         else {
1130                                 snprintf(message.subject,
1131                                         sizeof message.subject,
1132                                         "Re: %s",
1133                                         reply_subject);
1134                         }
1135                 }
1136         }
1137
1138         if (room_flags & QR_ANONOPT) {
1139                 scr_printf("Anonymous (Y/N)? ");
1140                 if (yesno() == 1)
1141                         message.anonymous = 1;
1142         }
1143
1144         /* If it's mail, we've got to check the validity of the recipient... */
1145         if (strlen(message.recipient) > 0) {
1146                 r = CtdlIPCPostMessage(ipc, 0, &message, buf);
1147                 if (r / 100 != 2) {
1148                         scr_printf("%s\n", buf);
1149                         return (1);
1150                 }
1151         }
1152
1153         /* Learn the number of the newest message in in the room, so we can
1154          * tell upon saving whether someone else has posted too.
1155          */
1156         num_msgs = 0;
1157         r = CtdlIPCGetMessages(ipc, LastMessages, 1, NULL, &msgarr, buf);
1158         if (r / 100 != 1) {
1159                 scr_printf("%s\n", buf);
1160         } else {
1161                 for (num_msgs = 0; msgarr[num_msgs]; num_msgs++)
1162                         ;
1163         }
1164
1165         /* Now compose the message... */
1166         if (client_make_message(ipc, temp, message.recipient, message.anonymous, 0, c, message.subject) != 0) {
1167                 return (2);
1168         }
1169
1170         /* Reopen the temp file that was created, so we can send it */
1171         fp = fopen(temp, "r");
1172
1173         /* Yes, unlink it now, so it doesn't stick around if we crash */
1174         unlink(temp);
1175
1176         if (!fp || !(message.text = load_message_from_file(fp))) {
1177                 err_printf("*** Internal error while trying to save message!\n"
1178                         "%s: %s\n",
1179                         temp, strerror(errno));
1180                 return(errno);
1181         }
1182
1183         if (fp) fclose(fp);
1184
1185         /* Transmit message to the server */
1186         r = CtdlIPCPostMessage(ipc, 1, &message, buf);
1187         if (r / 100 != 4) {
1188                 scr_printf("%s\n", buf);
1189                 return (1);
1190         }
1191
1192         if (num_msgs >= 1) highmsg = msgarr[num_msgs - 1];
1193
1194         if (msgarr) free(msgarr);
1195         msgarr = NULL;
1196         r = CtdlIPCGetMessages(ipc, NewMessages, 0, NULL, &msgarr, buf);
1197         if (r / 100 != 1) {
1198                 scr_printf("%s\n", buf);
1199         } else {
1200                 for (num_msgs = 0; msgarr[num_msgs]; num_msgs++)
1201                         ;
1202         }
1203
1204         /* get new highest message number in room to set lrp for goto... */
1205         maxmsgnum = msgarr[num_msgs - 1];
1206
1207         /* now see if anyone else has posted in here */
1208         b = (-1);
1209         for (a = 0; a < num_msgs; ++a) {
1210                 if (msgarr[a] > highmsg) {
1211                         ++b;
1212                 }
1213         }
1214         if (msgarr) free(msgarr);
1215         msgarr = NULL;
1216
1217         /* In the Mail> room, this algorithm always counts one message
1218          * higher than in public rooms, so we decrement it by one.
1219          */
1220         if (need_recp) {
1221                 --b;
1222         }
1223
1224         if (b == 1) {
1225                 scr_printf("*** 1 additional message has been entered "
1226                         "in this room by another user.\n");
1227         }
1228         else if (b > 1) {
1229                 scr_printf("*** %d additional messages have been entered "
1230                         "in this room by other users.\n", b);
1231         }
1232
1233         return(0);
1234 }
1235
1236 /*
1237  * Do editing on a quoted file
1238  */
1239 void process_quote(void)
1240 {
1241         FILE *qfile, *tfile;
1242         char buf[128];
1243         int line, qstart, qend;
1244
1245         /* Unlink the second temp file as soon as it's opened, so it'll get
1246          * deleted even if the program dies
1247          */
1248         qfile = fopen(temp2, "r");
1249         unlink(temp2);
1250
1251         /* Display the quotable text with line numbers added */
1252         line = 0;
1253         fgets(buf, 128, qfile);
1254         while (fgets(buf, 128, qfile) != NULL) {
1255                 scr_printf("%2d %s", ++line, buf);
1256         }
1257         scr_printf("Begin quoting at [ 1] : ");
1258         getline(buf, 3);
1259         qstart = (buf[0] == 0) ? (1) : atoi(buf);
1260         scr_printf("  End quoting at [%d] : ", line);
1261         getline(buf, 3);
1262         qend = (buf[0] == 0) ? (line) : atoi(buf);
1263         rewind(qfile);
1264         line = 0;
1265         fgets(buf, 128, qfile);
1266         tfile = fopen(temp, "w");
1267         while (fgets(buf, 128, qfile) != NULL) {
1268                 if ((++line >= qstart) && (line <= qend))
1269                         fprintf(tfile, " >%s", buf);
1270         }
1271         fprintf(tfile, " \n");
1272         fclose(qfile);
1273         fclose(tfile);
1274         chmod(temp, 0666);
1275 }
1276
1277
1278
1279 /*
1280  * List the URL's which were embedded in the previous message
1281  */
1282 void list_urls(CtdlIPC *ipc)
1283 {
1284         int i;
1285         char cmd[SIZ];
1286
1287         if (num_urls == 0) {
1288                 scr_printf("There were no URL's in the previous message.\n\n");
1289                 return;
1290         }
1291
1292         for (i = 0; i < num_urls; ++i) {
1293                 scr_printf("%3d %s\n", i + 1, urls[i]);
1294         }
1295
1296         if ((i = num_urls) != 1)
1297                 i = intprompt("Display which one", 1, 1, num_urls);
1298
1299         snprintf(cmd, sizeof cmd, rc_url_cmd, urls[i - 1]);
1300         system(cmd);
1301         scr_printf("\n");
1302 }
1303
1304 /*
1305  * View an image attached to a message
1306  */
1307 void image_view(CtdlIPC *ipc, unsigned long msg)
1308 {
1309         char selected_part[SIZ];
1310         struct parts *ptr = last_message_parts;
1311         int found = 0;
1312
1313         scr_printf("\n");
1314         /* List available parts */
1315         for (ptr = last_message_parts; ptr; ptr = ptr->next) {
1316                 if ((!strcasecmp(ptr->disposition, "attachment")
1317                    || !strcasecmp(ptr->disposition, "inline"))
1318                    && !strncmp(ptr->mimetype, "image/", 6)) {
1319                         color(DIM_WHITE);
1320                         scr_printf("Part ");
1321                         color(BRIGHT_MAGENTA);
1322                         scr_printf("%s", ptr->number);
1323                         if (!found) {
1324                                 found = 1;
1325                                 strncpy(selected_part, ptr->number, SIZ-1);
1326                         }
1327                         color(DIM_WHITE);
1328                         scr_printf(": ");
1329                         color(BRIGHT_CYAN);
1330                         scr_printf("%s", ptr->filename);
1331                         color(DIM_WHITE);
1332                         scr_printf(" (%s, %ld bytes)\n", ptr->mimetype, ptr->length);
1333                 }
1334         }
1335
1336         while (found) {
1337                 found = 0;
1338                 strprompt("View which part (0 when done)", selected_part, SIZ-1);
1339                 for (ptr = last_message_parts; ptr; ptr = ptr->next) {
1340                         if ((!strcasecmp(ptr->disposition, "attachment")
1341                            || !strcasecmp(ptr->disposition, "inline"))
1342                            && !strncmp(ptr->mimetype, "image/", 6)
1343                            && !strcmp(ptr->number, selected_part)) {
1344                                 char tmp[PATH_MAX];
1345                                 char buf[SIZ];
1346                                 void *file = NULL; /* The downloaded file */
1347                                 int r;
1348
1349                                 // view image
1350                                 found = 1;
1351                                 r = CtdlIPCAttachmentDownload(ipc, msg, selected_part, &file, progress, buf);
1352                                 if (r / 100 != 2) {
1353                                         scr_printf("%s\n", buf);
1354                                 } else {
1355                                         size_t len;
1356
1357                                         len = (size_t)extract_long(buf, 0);
1358                                         progress(len, len);
1359                                         scr_flush();
1360                                         strcpy(tmp, tmpnam(NULL));
1361                                         save_buffer(file, len, tmp);
1362                                         free(file);
1363                                         snprintf(buf, sizeof buf, imagecmd, tmp);
1364                                         system(buf);
1365                                         unlink(tmp);
1366                                 }
1367                         }
1368                 }
1369         }
1370 }
1371  
1372
1373 /*
1374  * Read the messages in the current room
1375  */
1376 void readmsgs(CtdlIPC *ipc,
1377         enum MessageList c,             /* see listing in citadel_ipc.h */
1378         enum MessageDirection rdir,     /* 1=Forward (-1)=Reverse */
1379         int q           /* Number of msgs to read (if c==3) */
1380 ) {
1381         int a, b, e, f, g, start;
1382         int savedpos;
1383         int hold_sw = 0;
1384         char arcflag = 0;
1385         char quotflag = 0;
1386         int hold_color = 0;
1387         char prtfile[PATH_MAX];
1388         char pagin;
1389         char cmd[SIZ];
1390         char targ[ROOMNAMELEN];
1391         char filename[PATH_MAX];
1392         char save_to[PATH_MAX];
1393         void *attachment = NULL;        /* Downloaded attachment */
1394         FILE *dest = NULL;      /* Alternate destination other than screen */
1395         int r;                          /* IPC response code */
1396
1397         if (c < 0)
1398                 b = (num_msgs - 1);
1399         else
1400                 b = 0;
1401
1402         strcpy(prtfile, tmpnam(NULL));
1403
1404         if (msg_arr) {
1405                 free(msg_arr);
1406                 msg_arr = NULL;
1407         }
1408         r = CtdlIPCGetMessages(ipc, c, q, NULL, &msg_arr, cmd);
1409         if (r / 100 != 1) {
1410                 scr_printf("%s\n", cmd);
1411         } else {
1412                 for (num_msgs = 0; msg_arr[num_msgs]; num_msgs++)
1413                         ;
1414         }
1415
1416         if (num_msgs == 0) {    /* TODO look at this later */
1417                 if (c == LastMessages) return;
1418                 scr_printf("*** There are no ");
1419                 if (c == NewMessages) scr_printf("new ");
1420                 if (c == OldMessages) scr_printf("old ");
1421                 scr_printf("messages in this room.\n");
1422                 return;
1423         }
1424
1425         lines_printed = 0;
1426
1427         /* this loop cycles through each message... */
1428         start = ((rdir == 1) ? 0 : (num_msgs - 1));
1429         for (a = start; ((a < num_msgs) && (a >= 0)); a = a + rdir) {
1430                 while (msg_arr[a] == 0L) {
1431                         a = a + rdir;
1432                         if ((a == num_msgs) || (a == (-1)))
1433                                 return;
1434                 }
1435
1436 RAGAIN:         pagin = ((arcflag == 0)
1437                          && (quotflag == 0)
1438                          && (userflags & US_PAGINATOR)) ? 1 : 0;
1439
1440                 /* If we're doing a quote, set the screenwidth to 72 */
1441                 if (quotflag) {
1442                         hold_sw = screenwidth;
1443                         screenwidth = 72;
1444                 }
1445
1446                 /* If printing or archiving, set the screenwidth to 80 */
1447                 if (arcflag) {
1448                         hold_sw = screenwidth;
1449                         screenwidth = 80;
1450                 }
1451
1452                 /* clear parts list */
1453                 /* FIXME free() the old parts list */
1454                 last_message_parts = NULL;
1455
1456                 /* now read the message... */
1457                 e = read_message(ipc, msg_arr[a], pagin, dest);
1458
1459                 /* ...and set the screenwidth back if we have to */
1460                 if ((quotflag) || (arcflag)) {
1461                         screenwidth = hold_sw;
1462                 }
1463 RMSGREAD:       scr_flush();
1464                 highest_msg_read = msg_arr[a];
1465                 if (quotflag) {
1466                         fclose(dest);
1467                         dest = NULL;
1468                         quotflag = 0;
1469                         enable_color = hold_color;
1470                         process_quote();
1471                 }
1472                 if (arcflag) {
1473                         fclose(dest);
1474                         dest = NULL;
1475                         arcflag = 0;
1476                         enable_color = hold_color;
1477                         f = fork();
1478                         if (f == 0) {
1479                                 freopen(prtfile, "r", stdin);
1480                                 screen_reset();
1481                                 sttybbs(SB_RESTORE);
1482                                 ka_system(printcmd);
1483                                 sttybbs(SB_NO_INTR);
1484                                 screen_set();
1485                                 unlink(prtfile);
1486                                 exit(0);
1487                         }
1488                         if (f > 0)
1489                                 do {
1490                                         g = wait(NULL);
1491                                 } while ((g != f) && (g >= 0));
1492                         scr_printf("Message printed.\n");
1493                 }
1494                 if (rc_alt_semantics && c == 1) {
1495                         char buf[SIZ];
1496
1497                         r = CtdlIPCSetMessageSeen(ipc, msg_arr[a], 1, buf);
1498                 }
1499                 if (e == 3)
1500                         return;
1501                 if (((userflags & US_NOPROMPT) || (e == 2))
1502                     && (((room_flags & QR_MAILBOX) == 0)
1503                         || (rc_force_mail_prompts == 0))) {
1504                         e = 'n';
1505                 } else {
1506                         color(DIM_WHITE);
1507                         scr_printf("(");
1508                         color(BRIGHT_WHITE);
1509                         scr_printf("%d", num_msgs - a - 1);
1510                         color(DIM_WHITE);
1511                         scr_printf(") ");
1512
1513                         keyopt("<B>ack <A>gain <Q>uote <R>eply <N>ext <S>top m<Y> next ");
1514                         if (rc_url_cmd[0] && num_urls)
1515                                 keyopt("<U>RLview ");
1516                         if (has_images > 0 && strlen(imagecmd) > 0)
1517                                 keyopt("<I>mages ");
1518                         keyopt("<?>help -> ");
1519
1520                         do {
1521                                 lines_printed = 2;
1522                                 e = (inkey() & 127);
1523                                 e = tolower(e);
1524 /* return key same as <N> */ if (e == 10)
1525                                         e = 'n';
1526 /* space key same as <N> */ if (e == 32)
1527                                         e = 'n';
1528 /* del/move for aides only */
1529                                     if ((!is_room_aide)
1530                                         && ((room_flags & QR_MAILBOX) ==
1531                                             0)) {
1532                                         if ((e == 'd') || (e == 'm'))
1533                                                 e = 0;
1534                                 }
1535 /* print only if available */
1536                                 if ((e == 'p') && (strlen(printcmd) == 0))
1537                                         e = 0;
1538 /* can't file if not allowed */
1539                                     if ((e == 'f')
1540                                         && (rc_allow_attachments == 0))
1541                                         e = 0;
1542 /* link only if browser avail*/
1543                                     if ((e == 'u')
1544                                         && (strlen(rc_url_cmd) == 0))
1545                                         e = 0;
1546                                 if ((e == 'i')
1547                                         && (!strlen(imagecmd) || !has_images))
1548                                         e = 0;
1549                         } while ((e != 'a') && (e != 'n') && (e != 's')
1550                                  && (e != 'd') && (e != 'm') && (e != 'p')
1551                                  && (e != 'q') && (e != 'b') && (e != 'h')
1552                                  && (e != 'r') && (e != 'f') && (e != '?')
1553                                  && (e != 'u') && (e != 'c') && (e != 'y')
1554                                  && (e != 'i'));
1555                         switch (e) {
1556                         case 's':
1557                                 scr_printf("Stop");
1558                                 break;
1559                         case 'a':
1560                                 scr_printf("Again");
1561                                 break;
1562                         case 'd':
1563                                 scr_printf("Delete");
1564                                 break;
1565                         case 'm':
1566                                 scr_printf("Move");
1567                                 break;
1568                         case 'c':
1569                                 scr_printf("Copy");
1570                                 break;
1571                         case 'n':
1572                                 scr_printf("Next");
1573                                 break;
1574                         case 'p':
1575                                 scr_printf("Print");
1576                                 break;
1577                         case 'q':
1578                                 scr_printf("Quote");
1579                                 break;
1580                         case 'b':
1581                                 scr_printf("Back");
1582                                 break;
1583                         case 'h':
1584                                 scr_printf("Header");
1585                                 break;
1586                         case 'r':
1587                                 scr_printf("Reply");
1588                                 break;
1589                         case 'f':
1590                                 scr_printf("File");
1591                                 break;
1592                         case 'u':
1593                                 scr_printf("URL's");
1594                                 break;
1595                         case 'y':
1596                                 scr_printf("mY next");
1597                                 break;
1598                         case 'i':
1599                                 scr_printf("Images");
1600                                 break;
1601                         case '?':
1602                                 scr_printf("? <help>");
1603                                 break;
1604                         }
1605                         if (userflags & US_DISAPPEAR)
1606                                 scr_printf("\r%79s\r", "");
1607                         else
1608                                 scr_printf("\n");
1609                         scr_flush();
1610                 }
1611                 switch (e) {
1612                 case '?':
1613                         scr_printf("Options available here:\n"
1614                                 " ?  Help (prints this message)\n"
1615                                 " S  Stop reading immediately\n"
1616                                 " A  Again (repeats last message)\n"
1617                                 " N  Next (continue with next message)\n"
1618                                 " Y  My Next (continue with next message you authored)\n"
1619                                 " B  Back (go back to previous message)\n");
1620                         if ((is_room_aide)
1621                             || (room_flags & QR_MAILBOX)) {
1622                                 scr_printf(" D  Delete this message\n"
1623                                         " M  Move message to another room\n");
1624                         }
1625                         scr_printf(" C  Copy message to another room\n");
1626                         if (strlen(printcmd) > 0)
1627                                 scr_printf(" P  Print this message\n");
1628                         scr_printf(
1629                                 " Q  Quote portions of this message for your next post\n"
1630                                 " H  Headers (display message headers only)\n");
1631                         if (is_mail)
1632                                 scr_printf(" R  Reply to this message\n");
1633                         if (rc_allow_attachments)
1634                                 scr_printf
1635                                     (" F  (save attachments to a file)\n");
1636                         if (strlen(rc_url_cmd) > 0)
1637                                 scr_printf(" U  (list URL's for display)\n");
1638                         if (strlen(imagecmd) > 0 && has_images > 0)
1639                                 scr_printf(" I  Image viewer\n");
1640                         scr_printf("\n");
1641                         goto RMSGREAD;
1642                 case 'p':
1643                         scr_flush();
1644                         dest = fopen(prtfile, "w");
1645                         arcflag = 1;
1646                         hold_color = enable_color;
1647                         enable_color = 0;
1648                         goto RAGAIN;
1649                 case 'q':
1650                         scr_flush();
1651                         dest = fopen(temp2, "w");
1652                         quotflag = 1;
1653                         hold_color = enable_color;
1654                         enable_color = 0;
1655                         goto RAGAIN;
1656                 case 's':
1657                         return;
1658                 case 'a':
1659                         goto RAGAIN;
1660                 case 'b':
1661                         a = a - (rdir * 2);
1662                         break;
1663                 case 'm':
1664                 case 'c':
1665                         newprompt("Enter target room: ",
1666                                   targ, ROOMNAMELEN - 1);
1667                         if (strlen(targ) > 0) {
1668                                 r = CtdlIPCMoveMessage(ipc, (e == 'c' ? 1 : 0),
1669                                                        msg_arr[a], targ, cmd);
1670                                 scr_printf("%s\n", cmd);
1671                                 if (r / 100 == 2)
1672                                         msg_arr[a] = 0L;
1673                         } else {
1674                                 goto RMSGREAD;
1675                         }
1676                         if (r / 100 != 2)       /* r will be init'ed, FIXME */
1677                                 goto RMSGREAD;  /* the logic here sucks */
1678                         break;
1679                 case 'f':
1680                         newprompt("Which section? ", filename,
1681                                   ((sizeof filename) - 1));
1682                         r = CtdlIPCAttachmentDownload(ipc, msg_arr[a],
1683                                         filename, &attachment, progress, cmd);
1684                         if (r / 100 != 2) {
1685                                 scr_printf("%s\n", cmd);
1686                         } else {
1687                                 extract(filename, cmd, 2);
1688                                 /*
1689                                  * Part 1 won't have a filename; use the
1690                                  * subject of the message instead. IO
1691                                  */
1692                                 if (!strlen(filename))
1693                                         strcpy(filename, reply_subject);
1694                                 destination_directory(save_to, filename);
1695                                 save_buffer(attachment,
1696                                                 extract_unsigned_long(cmd, 0),
1697                                                 save_to);
1698                         }
1699                         if (attachment) free(attachment);
1700                         goto RMSGREAD;
1701                 case 'd':
1702                         scr_printf("*** Delete this message? ");
1703                         if (yesno() == 1) {
1704                                 r = CtdlIPCDeleteMessage(ipc, msg_arr[a], cmd);
1705                                 scr_printf("%s\n", cmd);
1706                                 if (r / 100 == 2)
1707                                         msg_arr[a] = 0L;
1708                         } else {
1709                                 goto RMSGREAD;
1710                         }
1711                         break;
1712                 case 'h':
1713                         read_message(ipc, msg_arr[a], READ_HEADER, NULL);
1714                         goto RMSGREAD;
1715                 case 'r':
1716                         savedpos = num_msgs;
1717                         entmsg(ipc, 1, (rc_reply_extedit ? 2 : 0));
1718                         num_msgs = savedpos;
1719                         goto RMSGREAD;
1720                 case 'u':
1721                         list_urls(ipc);
1722                         goto RMSGREAD;
1723                 case 'i':
1724                         image_view(ipc, msg_arr[a]);
1725                         goto RMSGREAD;
1726             case 'y':
1727           { /* hack hack hack */
1728             /* find the next message by me, stay here if we find nothing */
1729             int finda;
1730             int lasta = a;
1731             for (finda = a; ((finda < num_msgs) && (finda >= 0)); finda += rdir)
1732               {
1733                 /* This is repetitively dumb, but that's what computers are for.
1734                    We have to load up messages until we find one by us */
1735                 char buf[SIZ];
1736                 int founda = 0;
1737                 
1738                 /* read the header so we can get 'from=' */
1739                 snprintf(buf, sizeof buf, "MSG0 %ld|1", msg_arr[finda]);
1740                 CtdlIPC_putline(ipc, buf);
1741                 CtdlIPC_getline(ipc, buf);
1742                 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) 
1743                   {
1744                         if ((!strncasecmp(buf, "from=", 5)) && (finda != a)) /* Skip current message. */
1745                       { 
1746                         if (strcasecmp(buf+5, fullname) == 0)
1747                           {
1748                             a = lasta; /* meesa current */
1749                             founda = 1;
1750                           }
1751                           }
1752                   }
1753                     /* we are now in synch with the server */
1754                 if (founda)
1755                   break; /* for */
1756                 lasta = finda; /* keep one behind or we skip on the reentrance to the for */
1757               } /* for */
1758           } /* case 'y' */
1759       } /* switch */
1760         }                       /* end for loop */
1761 }                               /* end read routine */
1762
1763
1764
1765
1766 /*
1767  * View and edit a system message
1768  */
1769 void edit_system_message(CtdlIPC *ipc, char *which_message)
1770 {
1771         char desc[SIZ];
1772         char read_cmd[SIZ];
1773         char write_cmd[SIZ];
1774
1775         snprintf(desc, sizeof desc, "system message '%s'", which_message);
1776         snprintf(read_cmd, sizeof read_cmd, "MESG %s", which_message);
1777         snprintf(write_cmd, sizeof write_cmd, "EMSG %s", which_message);
1778         do_edit(ipc, desc, read_cmd, "NOOP", write_cmd);
1779 }
1780
1781
1782
1783
1784 /*
1785  * Verify the message base
1786  */
1787 void check_message_base(CtdlIPC *ipc)
1788 {
1789         char buf[SIZ];
1790         char *transcript = NULL;
1791         int r;          /* IPC response code */
1792
1793         scr_printf
1794             ("Please read the documentation before running this command.\n"
1795             "Having done so, do you still want to check the message base? ");
1796         if (yesno() == 0)
1797                 return;
1798
1799         r = CtdlIPCMessageBaseCheck(ipc, &transcript, buf);
1800         if (r / 100 != 1) {
1801                 scr_printf("%s\n", buf);
1802                 return;
1803         }
1804
1805         while (transcript && strlen(transcript)) {
1806                 lines_printed = 1;
1807                 extract_token(buf, transcript, 0, '\n');
1808                 remove_token(transcript, 0, '\n');
1809                 pprintf("%s\n", buf);
1810         }
1811         if (transcript) free(transcript);
1812         return;
1813 }
1814
1815
1816 /*
1817  * Loads the contents of a file into memory.  Caller must free the allocated
1818  * memory.
1819  */
1820 char *load_message_from_file(FILE *src)
1821 {
1822         size_t i;
1823         size_t got = 0;
1824         char *dest = NULL;
1825
1826         fseek(src, 0, SEEK_END);
1827         i = ftell(src);
1828         rewind(src);
1829
1830         dest = (char *)calloc(1, i + 1);
1831         if (!dest)
1832                 return NULL;
1833
1834         while (got < i) {
1835                 size_t g;
1836
1837                 g = fread(dest + got, 1, i - got, src);
1838                 got += g;
1839                 if (g < i - got) {
1840                         /* Interrupted system call, keep going */
1841                         if (errno == EINTR)
1842                                 continue;
1843                         /* At this point we have either EOF or error */
1844                         i = got;
1845                         break;
1846                 }
1847                 dest[i] = 0;
1848         }
1849
1850         return dest;
1851 }