Back to index

citadel  8.12
messages.c
Go to the documentation of this file.
00001 /*
00002  * Text client functions for reading and writing of messages
00003  *
00004  * Copyright (c) 1987-2012 by the citadel.org team
00005  *
00006  * This program is open source software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License version 3.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  */
00014 
00015 #include "sysdep.h"
00016 #include <stdlib.h>
00017 #include <unistd.h>
00018 #include <sys/types.h>
00019 #include <fcntl.h>
00020 #include <stdio.h>
00021 #include <ctype.h>
00022 #include <string.h>
00023 #include <signal.h>
00024 #include <errno.h>
00025 #include <limits.h>
00026 #include <sys/wait.h>
00027 #include <sys/stat.h>
00028 
00029 #if TIME_WITH_SYS_TIME
00030 # include <sys/time.h>
00031 # include <time.h>
00032 #else
00033 # if HAVE_SYS_TIME_H
00034 #  include <sys/time.h>
00035 # else
00036 #  include <time.h>
00037 # endif
00038 #endif
00039 
00040 #ifdef HAVE_PTHREAD_H
00041 #include <pthread.h>
00042 #endif
00043 
00044 #include <stdarg.h>
00045 #include <libcitadel.h>
00046 #include "citadel.h"
00047 #include "citadel_ipc.h"
00048 #include "citadel_decls.h"
00049 #include "messages.h"
00050 #include "commands.h"
00051 #include "tuiconfig.h"
00052 #include "rooms.h"
00053 #ifndef HAVE_SNPRINTF
00054 #include "snprintf.h"
00055 #endif
00056 #include "screen.h"
00057 
00058 #define MAXWORDBUF SIZ
00059 #define NO_REPLY_TO  "nobody ... xxxxxx"
00060 
00061 char reply_to[SIZ];
00062 char reply_subject[SIZ];
00063 char reply_references[SIZ];
00064 char reply_inreplyto[SIZ];
00065 
00066 struct cittext {
00067        struct cittext *next;
00068        char text[MAXWORDBUF];
00069 };
00070 
00071 void stty_ctdl(int cmd);
00072 int haschar(const char *st, int ch);
00073 int file_checksum(char *filename);
00074 void progress(CtdlIPC* ipc, unsigned long curr, unsigned long cmax);
00075 
00076 unsigned long *msg_arr = NULL;
00077 int msg_arr_size = 0;
00078 int num_msgs;
00079 extern char room_name[];
00080 extern char tempdir[];
00081 extern unsigned room_flags;
00082 extern unsigned room_flags2;
00083 extern int entmsg_ok;
00084 extern long highest_msg_read;
00085 extern char temp[];
00086 extern char temp2[];
00087 extern int screenwidth;
00088 extern int screenheight;
00089 extern long maxmsgnum;
00090 extern char is_mail;
00091 extern char is_aide;
00092 extern char is_room_aide;
00093 extern char fullname[];
00094 extern char axlevel;
00095 extern unsigned userflags;
00096 extern char sigcaught;
00097 extern char printcmd[];
00098 extern int rc_allow_attachments;
00099 extern int rc_display_message_numbers;
00100 extern int rc_force_mail_prompts;
00101 extern int editor_pid;
00102 extern CtdlIPC *ipc_for_signal_handlers;  /* KLUDGE cover your eyes */
00103 int num_urls = 0;
00104 char urls[MAXURLS][SIZ];
00105 char imagecmd[SIZ];
00106 int has_images = 0;                       /* Current msg has images */
00107 struct parts *last_message_parts = NULL;  /* Parts from last msg */
00108 
00109 
00110 
00111 void ka_sigcatch(int signum)
00112 {
00113        alarm(S_KEEPALIVE);
00114        signal(SIGALRM, ka_sigcatch);
00115        CtdlIPCNoop(ipc_for_signal_handlers);
00116 }
00117 
00118 
00119 /*
00120  * server keep-alive version of wait() (needed for external editor)
00121  */
00122 pid_t ka_wait(int *kstatus)
00123 {
00124        pid_t p;
00125 
00126        alarm(S_KEEPALIVE);
00127        signal(SIGALRM, ka_sigcatch);
00128        do {
00129               errno = 0;
00130               p = wait(kstatus);
00131        } while (errno == EINTR);
00132        signal(SIGALRM, SIG_IGN);
00133        alarm(0);
00134        return (p);
00135 }
00136 
00137 
00138 /*
00139  * version of system() that uses ka_wait()
00140  */
00141 int ka_system(char *shc)
00142 {
00143        pid_t childpid;
00144        pid_t waitpid;
00145        int retcode;
00146 
00147        childpid = fork();
00148        if (childpid < 0) {
00149               color(BRIGHT_RED);
00150               perror("Cannot fork");
00151               color(DIM_WHITE);
00152               return ((pid_t) childpid);
00153        }
00154 
00155        if (childpid == 0) {
00156               execlp("/bin/sh", "sh", "-c", shc, NULL);
00157               exit(127);
00158        }
00159 
00160        if (childpid > 0) {
00161               do {
00162                      waitpid = ka_wait(&retcode);
00163               } while (waitpid != childpid);
00164               return (retcode);
00165        }
00166 
00167        return (-1);
00168 }
00169 
00170 
00171 
00172 /*
00173  * add a newline to the buffer...
00174  */
00175 void add_newline(struct cittext *textlist)
00176 {
00177        struct cittext *ptr;
00178 
00179        ptr = textlist;
00180        while (ptr->next != NULL)
00181               ptr = ptr->next;
00182 
00183        while (ptr->text[strlen(ptr->text) - 1] == 32)
00184               ptr->text[strlen(ptr->text) - 1] = 0;
00185        /* strcat(ptr->text,"\n"); */
00186 
00187        ptr->next = (struct cittext *)
00188            malloc(sizeof(struct cittext));
00189        ptr = ptr->next;
00190        ptr->next = NULL;
00191        strcpy(ptr->text, "");
00192 }
00193 
00194 
00195 /*
00196  * add a word to the buffer...
00197  */
00198 void add_word(struct cittext *textlist, char *wordbuf)
00199 {
00200        struct cittext *ptr;
00201 
00202        ptr = textlist;
00203        while (ptr->next != NULL)
00204               ptr = ptr->next;
00205 
00206        if (3 + strlen(ptr->text) + strlen(wordbuf) > screenwidth) {
00207               ptr->next = (struct cittext *)
00208                   malloc(sizeof(struct cittext));
00209               ptr = ptr->next;
00210               ptr->next = NULL;
00211               strcpy(ptr->text, "");
00212        }
00213 
00214        strcat(ptr->text, wordbuf);
00215        strcat(ptr->text, " ");
00216 }
00217 
00218 
00219 /*
00220  * begin editing of an opened file pointed to by fp
00221  */
00222 void citedit(FILE *fp)
00223 {
00224        int a, prev, finished, b, last_space;
00225        int appending = 0;
00226        struct cittext *textlist = NULL;
00227        struct cittext *ptr;
00228        char wordbuf[MAXWORDBUF];
00229        int rv = 0;
00230 
00231        /* first, load the text into the buffer */
00232        fseek(fp, 0L, 0);
00233        textlist = (struct cittext *) malloc(sizeof(struct cittext));
00234        textlist->next = NULL;
00235        strcpy(textlist->text, "");
00236 
00237        strcpy(wordbuf, "");
00238        prev = (-1);
00239        while (a = getc(fp), a >= 0) {
00240               appending = 1;
00241               if ((a == 32) || (a == 9) || (a == 13) || (a == 10)) {
00242                      add_word(textlist, wordbuf);
00243                      strcpy(wordbuf, "");
00244                      if ((prev == 13) || (prev == 10)) {
00245                             add_word(textlist, "\n");
00246                             add_newline(textlist);
00247                             add_word(textlist, "");
00248                      }
00249               } else {
00250                      wordbuf[strlen(wordbuf) + 1] = 0;
00251                      wordbuf[strlen(wordbuf)] = a;
00252               }
00253               if (strlen(wordbuf) + 3 > screenwidth) {
00254                      add_word(textlist, wordbuf);
00255                      strcpy(wordbuf, "");
00256               }
00257               prev = a;
00258        }
00259 
00260        /* get text */
00261        finished = 0;
00262        prev = (appending ? 13 : (-1));
00263        strcpy(wordbuf, "");
00264        async_ka_start();
00265        do {
00266               a = inkey();
00267               if (a == 10)
00268                      a = 13;
00269               if (a == 9)
00270                      a = 32;
00271               if (a == 127)
00272                      a = 8;
00273 
00274               if ((a != 32) && (prev == 13)) {
00275                      add_word(textlist, "\n");
00276                      scr_printf(" ");
00277               }
00278 
00279               if ((a == 32) && (prev == 13)) {
00280                      add_word(textlist, "\n");
00281                      add_newline(textlist);
00282               }
00283 
00284               if (a == 8) {
00285                      if (!IsEmptyStr(wordbuf)) {
00286                             wordbuf[strlen(wordbuf) - 1] = 0;
00287                             scr_putc(8);
00288                             scr_putc(32);
00289                             scr_putc(8);
00290                      }
00291               } else if (a == 23) {
00292                      do {
00293                             wordbuf[strlen(wordbuf) - 1] = 0;
00294                             scr_putc(8);
00295                             scr_putc(32);
00296                             scr_putc(8);
00297                      } while (!IsEmptyStr(wordbuf) && wordbuf[strlen(wordbuf) - 1] != ' ');
00298               } else if (a == 13) {
00299                      scr_printf("\n");
00300                      if (IsEmptyStr(wordbuf))
00301                             finished = 1;
00302                      else {
00303                             for (b = 0; b < strlen(wordbuf); ++b)
00304                                    if (wordbuf[b] == 32) {
00305                                           wordbuf[b] = 0;
00306                                           add_word(textlist,
00307                                                   wordbuf);
00308                                           strcpy(wordbuf,
00309                                                  &wordbuf[b + 1]);
00310                                           b = 0;
00311                                    }
00312                             add_word(textlist, wordbuf);
00313                             strcpy(wordbuf, "");
00314                      }
00315               } else {
00316                      scr_putc(a);
00317                      wordbuf[strlen(wordbuf) + 1] = 0;
00318                      wordbuf[strlen(wordbuf)] = a;
00319               }
00320               if ((strlen(wordbuf) + 3) > screenwidth) {
00321                      last_space = (-1);
00322                      for (b = 0; b < strlen(wordbuf); ++b)
00323                             if (wordbuf[b] == 32)
00324                                    last_space = b;
00325                      if (last_space >= 0) {
00326                             for (b = 0; b < strlen(wordbuf); ++b)
00327                                    if (wordbuf[b] == 32) {
00328                                           wordbuf[b] = 0;
00329                                           add_word(textlist,
00330                                                   wordbuf);
00331                                           strcpy(wordbuf,
00332                                                  &wordbuf[b + 1]);
00333                                           b = 0;
00334                                    }
00335                             for (b = 0; b < strlen(wordbuf); ++b) {
00336                                    scr_putc(8);
00337                                    scr_putc(32);
00338                                    scr_putc(8);
00339                             }
00340                             scr_printf("\n%s", wordbuf);
00341                      } else {
00342                             add_word(textlist, wordbuf);
00343                             strcpy(wordbuf, "");
00344                             scr_printf("\n");
00345                      }
00346               }
00347               prev = a;
00348        } while (finished == 0);
00349        async_ka_end();
00350 
00351        /* write the buffer back to disk */
00352        fseek(fp, 0L, 0);
00353        for (ptr = textlist; ptr != NULL; ptr = ptr->next) {
00354               fprintf(fp, "%s", ptr->text);
00355        }
00356        putc(10, fp);
00357        fflush(fp);
00358        rv = ftruncate(fileno(fp), ftell(fp));
00359        if (rv < 0)
00360               scr_printf("failed to set message buffer: %s\n", strerror(errno));
00361 
00362        
00363        /* and deallocate the memory we used */
00364        while (textlist != NULL) {
00365               ptr = textlist->next;
00366               free(textlist);
00367               textlist = ptr;
00368        }
00369 }
00370 
00371 
00372 /*
00373  * Free the struct parts
00374  */
00375 void free_parts(struct parts *p)
00376 {
00377        struct parts *a_part = p;
00378 
00379        while (a_part) {
00380               struct parts *q;
00381 
00382               q = a_part;
00383               a_part = a_part->next;
00384               free(q);
00385        }
00386 }
00387 
00388 
00389 /*
00390  * This is a mini RFC2047 decoder.
00391  * It only handles strings encoded from UTF-8 as Quoted-printable.
00392  */
00393 void mini_2047_decode(char *s) {
00394        if (!s) return;
00395 
00396        char *qstart = strstr(s, "=?UTF-8?Q?");
00397        if (!qstart) return;
00398 
00399        char *qend = strstr(s, "?=");
00400        if (!qend) return;
00401 
00402        if (qend <= qstart) return;
00403 
00404        strcpy(qstart, &qstart[10]);
00405        qend -= 10;
00406 
00407        char *p = qstart;
00408        while (p < qend) {
00409 
00410               if (p[0] == '=') {
00411 
00412                      char ch[3];
00413                      ch[0] = p[1];
00414                      ch[1] = p[2];
00415                      ch[2] = p[3];
00416                      int c;
00417                      sscanf(ch, "%02x", &c);
00418                      p[0] = c;
00419                      strcpy(&p[1], &p[3]);
00420                      qend -= 2;
00421               }
00422 
00423               if (p[0] == '_') {
00424                      p[0] = ' ';
00425               }
00426               
00427               ++p;
00428        }
00429 
00430        strcpy(qend, &qend[2]);
00431 }
00432 
00433 /*
00434  * Read a message from the server
00435  */
00436 int read_message(CtdlIPC *ipc,
00437        long num,   /* message number */
00438        int pagin, /* 0 = normal read, 1 = read with pagination, 2 = header */
00439        FILE *dest) /* Destination file, NULL for screen */
00440 {
00441        char buf[SIZ];
00442        char now[SIZ];
00443        int format_type = 0;
00444        int fr = 0;
00445        int nhdr = 0;
00446        struct ctdlipcmessage *message = NULL;
00447        int r;                      /* IPC response code */
00448        char *converted_text = NULL;
00449        char *lineptr;
00450        char *nextline;
00451        char *searchptr;
00452        int i;
00453        char ch;
00454        int linelen;
00455        int final_line_is_blank = 0;
00456 
00457        has_images = 0;
00458 
00459        sigcaught = 0;
00460        stty_ctdl(1);
00461 
00462        strcpy(reply_to, NO_REPLY_TO);
00463        strcpy(reply_subject, "");
00464        strcpy(reply_references, "");
00465        strcpy(reply_inreplyto, "");
00466 
00467        r = CtdlIPCGetSingleMessage(ipc, num, (pagin == READ_HEADER ? 1 : 0), 4, &message, buf);
00468        if (r / 100 != 1) {
00469               scr_printf("*** msg #%ld: %d %s\n", num, r, buf);
00470               stty_ctdl(0);
00471               free(message->text);
00472               free_parts(message->attachments);
00473               free(message);
00474               return (0);
00475        }
00476 
00477        if (dest) {
00478               fprintf(dest, "\n ");
00479        } else {
00480               scr_printf("\n");
00481               if (pagin != 2)
00482                      scr_printf(" ");
00483        }
00484        if (pagin == 1 && !dest) {
00485               color(BRIGHT_CYAN);
00486        }
00487 
00488        /* View headers only */
00489        if (pagin == 2) {
00490               scr_printf("nhdr=%s\nfrom=%s\ntype=%d\nmsgn=%s\n",
00491                             message->nhdr ? "yes" : "no",
00492                             message->author, message->type,
00493                             message->msgid);
00494               if (!IsEmptyStr(message->subject)) {
00495                      scr_printf("subj=%s\n", message->subject);
00496               }
00497               if (!IsEmptyStr(message->email)) {
00498                      scr_printf("rfca=%s\n", message->email);
00499               }
00500               scr_printf("hnod=%s\nroom=%s\nnode=%s\ntime=%s",
00501                             message->hnod, message->room,
00502                             message->node, 
00503                             asctime(localtime(&message->time)));
00504               if (!IsEmptyStr(message->recipient)) {
00505                      scr_printf("rcpt=%s\n", message->recipient);
00506               }
00507               if (message->attachments) {
00508                      struct parts *ptr;
00509 
00510                      for (ptr = message->attachments; ptr; ptr = ptr->next) {
00511                             scr_printf("part=%s|%s|%s|%s|%s|%ld\n",
00512                                    ptr->name, ptr->filename, ptr->number,
00513                                    ptr->disposition, ptr->mimetype,
00514                                    ptr->length
00515                             );
00516                      }
00517               }
00518               scr_printf("\n");
00519               stty_ctdl(0);
00520               free(message->text);
00521               free_parts(message->attachments);
00522               free(message);
00523               return (0);
00524        }
00525 
00526        if (rc_display_message_numbers) {
00527               if (dest) {
00528                      fprintf(dest, "[#%s] ", message->msgid);
00529               } else {
00530                      color(DIM_WHITE);
00531                      scr_printf("[");
00532                      color(BRIGHT_WHITE);
00533                      scr_printf("#%s", message->msgid);
00534                      color(DIM_WHITE);
00535                      scr_printf("] ");
00536               }
00537        }
00538        if (nhdr == 1 && !is_room_aide) {
00539               if (dest) {
00540                      fprintf(dest, " ****");
00541               } else {
00542                      scr_printf(" ****");
00543               }
00544        } else {
00545               fmt_date(now, sizeof now, message->time, 0);
00546               if (dest) {
00547                      fprintf(dest, "%s from %s ", now, message->author);
00548                      if (!IsEmptyStr(message->email)) {
00549                             fprintf(dest, "<%s> ", message->email);
00550                      }
00551               } else {
00552                      color(BRIGHT_CYAN);
00553                      scr_printf("%s ", now);
00554                      color(DIM_WHITE);
00555                      scr_printf("from ");
00556                      color(BRIGHT_CYAN);
00557                      scr_printf("%s ", message->author);
00558                      if (!IsEmptyStr(message->email)) {
00559                             color(DIM_WHITE);
00560                             scr_printf("<");
00561                             color(BRIGHT_BLUE);
00562                             scr_printf("%s", message->email);
00563                                    color(DIM_WHITE);
00564                             scr_printf("> ");
00565                      }
00566               }
00567               if (!IsEmptyStr(message->node)) {
00568                      if ((room_flags & QR_NETWORK)
00569                          || ((strcasecmp(message->node, ipc->ServInfo.nodename)
00570                           && (strcasecmp(message->node, ipc->ServInfo.fqdn))))) {
00571                             if (IsEmptyStr(message->email)) {
00572                                    if (dest) {
00573                                           fprintf(dest, "@%s ", message->node);
00574                                    } else {
00575                                           color(DIM_WHITE);
00576                                           scr_printf("@");
00577                                           color(BRIGHT_YELLOW);
00578                                           scr_printf("%s ", message->node);
00579                                    }
00580                             }
00581                      }
00582               }
00583               if (strcasecmp(message->hnod, ipc->ServInfo.humannode)
00584                   && (!IsEmptyStr(message->hnod)) && (IsEmptyStr(message->email))) {
00585                      if (dest) {
00586                             fprintf(dest, "(%s) ", message->hnod);
00587                      } else {
00588                             color(DIM_WHITE);
00589                             scr_printf("(");
00590                             color(BRIGHT_WHITE);
00591                             scr_printf("%s", message->hnod);
00592                             color(DIM_WHITE);
00593                             scr_printf(") ");
00594                      }
00595               }
00596               if (strcasecmp(message->room, room_name) && (IsEmptyStr(message->email))) {
00597                      if (dest) {
00598                             fprintf(dest, "in %s> ", message->room);
00599                      } else {
00600                             color(DIM_WHITE);
00601                             scr_printf("in ");
00602                             color(BRIGHT_MAGENTA);
00603                             scr_printf("%s> ", message->room);
00604                      }
00605               }
00606               if (!IsEmptyStr(message->recipient)) {
00607                      if (dest) {
00608                             fprintf(dest, "to %s ", message->recipient);
00609                      } else {
00610                             color(DIM_WHITE);
00611                             scr_printf("to ");
00612                             color(BRIGHT_CYAN);
00613                             scr_printf("%s ", message->recipient);
00614                      }
00615               }
00616        }
00617        
00618        if (dest) {
00619               fprintf(dest, "\n");
00620        } else {
00621               scr_printf("\n");
00622        }
00623 
00624        /* Set the reply-to address to an Internet e-mail address if possible
00625         */
00626        if ((message->email != NULL) && (!IsEmptyStr(message->email))) {
00627               if (!IsEmptyStr(message->author)) {
00628                      snprintf(reply_to, sizeof reply_to, "%s <%s>", message->author, message->email);
00629               }
00630               else {
00631                      safestrncpy(reply_to, message->email, sizeof reply_to);
00632               }
00633        }
00634 
00635        /* But if we can't do that, set it to a Citadel address.
00636         */
00637        if (!strcmp(reply_to, NO_REPLY_TO)) {
00638               snprintf(reply_to, sizeof(reply_to), "%s @ %s",
00639                       message->author, message->node);
00640        }
00641 
00642        if (message->msgid != NULL) {
00643               safestrncpy(reply_inreplyto, message->msgid, sizeof reply_inreplyto);
00644        }
00645 
00646        if (message->references != NULL) if (!IsEmptyStr(message->references)) {
00647               safestrncpy(reply_references, message->references, sizeof reply_references);
00648        }
00649 
00650        if (message->subject != NULL) {
00651               safestrncpy(reply_subject, message->subject, sizeof reply_subject);
00652               if (!IsEmptyStr(message->subject)) {
00653                      if (dest) {
00654                             fprintf(dest, "Subject: %s\n", message->subject);
00655                      } else {
00656                             color(DIM_WHITE);
00657                             scr_printf("Subject: ");
00658                             color(BRIGHT_CYAN);
00659                             mini_2047_decode(message->subject);
00660                             scr_printf("%s\n", message->subject);
00661                      }
00662               }
00663        }
00664 
00665        if (pagin == 1 && !dest) {
00666               color(BRIGHT_WHITE);
00667        }
00668 
00669        /******* end of header output, start of message text output *******/
00670 
00671        /*
00672         * Convert HTML to plain text, formatting for the actual width
00673         * of the client screen.
00674         */
00675        if (!strcasecmp(message->content_type, "text/html")) {
00676               converted_text = html_to_ascii(message->text, 0, screenwidth, 0);
00677               if (converted_text != NULL) {
00678                      free(message->text);
00679                      message->text = converted_text;
00680                      format_type = 1;
00681               }
00682        }
00683 
00684        /* Text/plain is a different type */
00685        if (!strcasecmp(message->content_type, "text/plain")) {
00686               format_type = 1;
00687        }
00688 
00689        /* Extract URL's */
00690        num_urls = 0; /* Start with a clean slate */
00691        searchptr = message->text;
00692        while ( (searchptr != NULL) && (num_urls < MAXURLS) ) {
00693               searchptr = strstr(searchptr, "http://");
00694               if (searchptr != NULL) {
00695                      safestrncpy(urls[num_urls], searchptr, sizeof(urls[num_urls]));
00696                      for (i = 0; i < strlen(urls[num_urls]); i++) {
00697                             ch = urls[num_urls][i];
00698                             if (ch == '>' || ch == '\"' || ch == ')' ||
00699                                 ch == ' ' || ch == '\n') {
00700                                    urls[num_urls][i] = 0;
00701                                    break;
00702                             }
00703                      }
00704                      num_urls++;
00705                      ++searchptr;
00706               }
00707        }
00708 
00709        /*
00710         * Here we go
00711         */
00712        if (format_type == 0) {
00713               fr = fmout(screenwidth, NULL, message->text, dest, 1);
00714        } else {
00715               /* renderer for text/plain */
00716 
00717               lineptr = message->text;
00718 
00719               do {
00720                      nextline = strchr(lineptr, '\n');
00721                      if (nextline != NULL) {
00722                             *nextline = 0;
00723                             ++nextline;
00724                             if (*nextline == 0) nextline = NULL;
00725                      }
00726 
00727                      if (sigcaught == 0) {
00728                             linelen = strlen(lineptr);
00729                             if (linelen && (lineptr[linelen-1] == '\r')) {
00730                                    lineptr[--linelen] = 0;
00731                             }
00732                             if (dest) {
00733                                    fprintf(dest, "%s\n", lineptr);
00734                             } else {
00735                                    scr_printf("%s\n", lineptr);
00736                             }
00737                      }
00738                      if (lineptr[0] == 0) final_line_is_blank = 1;
00739                      else final_line_is_blank = 0;
00740                      lineptr = nextline;
00741               } while (nextline);
00742               fr = sigcaught;
00743        }
00744        if (!final_line_is_blank) {
00745               if (dest) {
00746                      fprintf(dest, "\n");
00747               }
00748               else {
00749                      scr_printf("\n");
00750                      fr = sigcaught;             
00751               }
00752        }
00753 
00754        /* Enumerate any attachments */
00755        if ( (pagin == 1) && (message->attachments) ) {
00756               struct parts *ptr;
00757 
00758               for (ptr = message->attachments; ptr; ptr = ptr->next) {
00759                      if ( (!strcasecmp(ptr->disposition, "attachment"))
00760                         || (!strcasecmp(ptr->disposition, "inline"))
00761                         || (!strcasecmp(ptr->disposition, ""))
00762                      ) {
00763                             if ( (strcasecmp(ptr->number, message->mime_chosen))
00764                                && (!IsEmptyStr(ptr->mimetype))
00765                             ) {
00766                                    color(DIM_WHITE);
00767                                    scr_printf("Part ");
00768                                    color(BRIGHT_MAGENTA);
00769                                    scr_printf("%s", ptr->number);
00770                                    color(DIM_WHITE);
00771                                    scr_printf(": ");
00772                                    color(BRIGHT_CYAN);
00773                                    scr_printf("%s", ptr->filename);
00774                                    color(DIM_WHITE);
00775                                    scr_printf(" (%s, %ld bytes)\n", ptr->mimetype, ptr->length);
00776                                    if (!strncmp(ptr->mimetype, "image/", 6)) {
00777                                           has_images++;
00778                                    }
00779                             }
00780                      }
00781               }
00782        }
00783 
00784        /* Save the attachments info for later */
00785        last_message_parts = message->attachments;
00786 
00787        /* Now we're done */
00788        free(message->text);
00789        free(message);
00790 
00791        if (pagin == 1 && !dest)
00792               color(DIM_WHITE);
00793        stty_ctdl(0);
00794        return (fr);
00795 }
00796 
00797 /*
00798  * replace string function for the built-in editor
00799  */
00800 void replace_string(char *filename, long int startpos)
00801 {
00802        char buf[512];
00803        char srch_str[128];
00804        char rplc_str[128];
00805        FILE *fp;
00806        int a;
00807        long rpos, wpos;
00808        char *ptr;
00809        int substitutions = 0;
00810        long msglen = 0L;
00811        int rv;
00812 
00813        newprompt("Enter test to be replaced: ", srch_str, (sizeof(srch_str)-1) );
00814        if (IsEmptyStr(srch_str)) {
00815               return;
00816        }
00817 
00818        newprompt("Enter text to replace it with: ", rplc_str, (sizeof(rplc_str)-1) );
00819 
00820        fp = fopen(filename, "r+");
00821        if (fp == NULL) {
00822               return;
00823        }
00824 
00825        wpos = startpos;
00826        fseek(fp, startpos, 0);
00827        strcpy(buf, "");
00828        while (a = getc(fp), a > 0) {
00829               ++msglen;
00830               buf[strlen(buf) + 1] = 0;
00831               buf[strlen(buf)] = a;
00832               if (strlen(buf) >= strlen(srch_str)) {
00833                      ptr = (&buf[strlen(buf) - strlen(srch_str)]);
00834                      if (!strncmp(ptr, srch_str, strlen(srch_str))) {
00835                             strcpy(ptr, rplc_str);
00836                             ++substitutions;
00837                      }
00838               }
00839               if (strlen(buf) > 384) {
00840                      rpos = ftell(fp);
00841                      fseek(fp, wpos, 0);
00842                      rv = fwrite((char *) buf, 128, 1, fp);
00843                      if (rv < 0) {
00844                             scr_printf("failed to replace string: %s\n", strerror(errno));
00845                             break; /*whoopsi! */
00846                      }
00847                      strcpy(buf, &buf[128]);
00848                      wpos = ftell(fp);
00849                      fseek(fp, rpos, 0);
00850               }
00851        }
00852        fseek(fp, wpos, 0);
00853        if (!IsEmptyStr(buf)) {
00854               rv = fwrite((char *) buf, strlen(buf), 1, fp);
00855        }
00856        wpos = ftell(fp);
00857        fclose(fp);
00858        rv = truncate(filename, wpos);
00859        scr_printf("<R>eplace made %d substitution(s).\n\n", substitutions);
00860 }
00861 
00862 /*
00863  * Function to begin composing a new message
00864  */
00865 int client_make_message(CtdlIPC *ipc,
00866                      char *filename,             /* temporary file name */
00867                      char *recipient,     /* NULL if it's not mail */
00868                      int is_anonymous,
00869                      int format_type,
00870                      int mode,
00871                      char *subject,              /* buffer to store subject line */
00872                      int subject_required
00873 ) {
00874        FILE *fp;
00875        int a, b, e_ex_code;
00876        long beg;
00877        char datestr[SIZ];
00878        char header[SIZ];
00879        int cksum = 0;
00880 
00881        if ( (mode == 2) && (IsEmptyStr(editor_path)) ) {
00882               scr_printf("*** No editor available; using built-in editor.\n");
00883               mode = 0;
00884        }
00885 
00886        fmt_date(datestr, sizeof datestr, time(NULL), 0);
00887        header[0] = 0;
00888 
00889        if (room_flags & QR_ANONONLY && !recipient) {
00890               snprintf(header, sizeof header, " ****");
00891        }
00892        else {
00893               snprintf(header, sizeof header,
00894                      " %s from %s",
00895                      datestr,
00896                      (is_anonymous ? "[anonymous]" : fullname)
00897                      );
00898               if (!IsEmptyStr(recipient)) {
00899                      size_t tmp = strlen(header);
00900                      snprintf(&header[tmp], sizeof header - tmp,
00901                             " to %s", recipient);
00902               }
00903        }
00904        scr_printf("%s\n", header);
00905        if (subject != NULL) if (!IsEmptyStr(subject)) {
00906               scr_printf("Subject: %s\n", subject);
00907        }
00908        
00909        if ( (subject_required) && (IsEmptyStr(subject)) ) {
00910               newprompt("Subject: ", subject, 70);
00911        }
00912 
00913        if (mode == 1) {
00914               scr_printf("(Press ctrl-d when finished)\n");
00915        }
00916 
00917        if (mode == 0) {
00918               fp = fopen(filename, "r");
00919               if (fp != NULL) {
00920                      fmout(screenwidth, fp, NULL, NULL, 0);
00921                      beg = ftell(fp);
00922                      if (beg < 0)
00923                             scr_printf("failed to get stream position %s\n", 
00924                                       strerror(errno));
00925                      fclose(fp);
00926               } else {
00927                      fp = fopen(filename, "w");
00928                      if (fp == NULL) {
00929                             scr_printf("*** Error opening temp file!\n    %s: %s\n",
00930                                    filename, strerror(errno)
00931                             );
00932                      return(1);
00933                      }
00934                      fclose(fp);
00935               }
00936        }
00937 
00938 ME1:   switch (mode) {
00939 
00940        case 0:
00941               fp = fopen(filename, "r+");
00942               if (fp == NULL) {
00943                      scr_printf("*** Error opening temp file!\n    %s: %s\n",
00944                             filename, strerror(errno)
00945                      );
00946                      return(1);
00947               }
00948               citedit(fp);
00949               fclose(fp);
00950               goto MECR;
00951 
00952        case 1:
00953               fp = fopen(filename, "a");
00954               if (fp == NULL) {
00955                      scr_printf("*** Error opening temp file!\n"
00956                             "    %s: %s\n",
00957                             filename, strerror(errno));
00958                      return(1);
00959               }
00960               do {
00961                      a = inkey();
00962                      if (a == 255)
00963                             a = 32;
00964                      if (a == 13)
00965                             a = 10;
00966                      if (a != 4) {
00967                             putc(a, fp);
00968                             scr_putc(a);
00969                      }
00970                      if (a == 10)
00971                             scr_putc(10);
00972               } while (a != 4);
00973               fclose(fp);
00974               break;
00975 
00976        case 2:
00977        default:      /* allow 2+ modes */
00978               e_ex_code = 1;       /* start with a failed exit code */
00979               stty_ctdl(SB_RESTORE);
00980               editor_pid = fork();
00981               cksum = file_checksum(filename);
00982               if (editor_pid == 0) {
00983                      char tmp[SIZ];
00984 
00985                      chmod(filename, 0600);
00986                      snprintf(tmp, sizeof tmp, "WINDOW_TITLE=%s", header);
00987                      putenv(tmp);
00988                      execlp(editor_path, editor_path, filename, NULL);
00989                      exit(1);
00990               }
00991               if (editor_pid > 0)
00992                      do {
00993                             e_ex_code = 0;
00994                             b = ka_wait(&e_ex_code);
00995                      } while ((b != editor_pid) && (b >= 0));
00996               editor_pid = (-1);
00997               stty_ctdl(0);
00998               break;
00999        }
01000 
01001 MECR:  if (mode >= 2) {
01002               if (file_checksum(filename) == cksum) {
01003                      scr_printf("*** Aborted message.\n");
01004                      e_ex_code = 1;
01005               }
01006               if (e_ex_code == 0) {
01007                      goto MEFIN;
01008               }
01009               goto MEABT2;
01010        }
01011 
01012        b = keymenu("Entry command (? for options)",
01013               "<A>bort|"
01014               "<C>ontinue|"
01015               "<S>ave message|"
01016               "<P>rint formatted|"
01017               "add s<U>bject|"
01018               "<R>eplace string|"
01019               "<H>old message"
01020        );
01021 
01022        if (b == 'a') goto MEABT;
01023        if (b == 'c') goto ME1;
01024        if (b == 's') goto MEFIN;
01025        if (b == 'p') {
01026               scr_printf(" %s from %s", datestr, fullname);
01027               if (!IsEmptyStr(recipient)) {
01028                      scr_printf(" to %s", recipient);
01029               }
01030               scr_printf("\n");
01031               if (subject != NULL) if (!IsEmptyStr(subject)) {
01032                      scr_printf("Subject: %s\n", subject);
01033               }
01034               fp = fopen(filename, "r");
01035               if (fp != NULL) {
01036                      fmout(screenwidth, fp, NULL, NULL, 0);
01037                      beg = ftell(fp);
01038                      if (beg < 0)
01039                             scr_printf("failed to get stream position %s\n", 
01040                                       strerror(errno));
01041                      fclose(fp);
01042               }
01043               goto MECR;
01044        }
01045        if (b == 'r') {
01046               replace_string(filename, 0L);
01047               goto MECR;
01048        }
01049        if (b == 'h') {
01050               return (2);
01051        }
01052        if (b == 'u') {
01053               if (subject != NULL) {
01054                      newprompt("Subject: ", subject, 70);
01055               }
01056               goto MECR;
01057        }
01058 
01059 MEFIN: return (0);
01060 
01061 MEABT: scr_printf("Are you sure? ");
01062        if (yesno() == 0) {
01063               goto ME1;
01064        }
01065 MEABT2:       unlink(filename);
01066        return (2);
01067 }
01068 
01069 
01070 /*
01071  * Make sure there's room in msg_arr[] for at least one more.
01072  */
01073 void check_msg_arr_size(void) {
01074        if ((num_msgs + 1) > msg_arr_size) {
01075               msg_arr_size += 512;
01076               msg_arr = realloc(msg_arr,
01077                      ((sizeof(long)) * msg_arr_size) );
01078        }
01079 }
01080 
01081 
01082 /*
01083  * break_big_lines()  -  break up lines that are >1024 characters
01084  *                       otherwise the server will truncate
01085  */
01086 void break_big_lines(char *msg) {
01087        char *ptr;
01088        char *break_here;
01089 
01090        if (msg == NULL) {
01091               return;
01092        }
01093 
01094        ptr = msg;
01095        while (strlen(ptr) > 1000) {
01096               break_here = strchr(&ptr[900], ' ');
01097               if ((break_here == NULL) || (break_here > &ptr[999])) {
01098                      break_here = &ptr[999];
01099               }
01100               *break_here = '\n';
01101               ptr = break_here++;
01102        }
01103 }
01104 
01105 
01106 /*
01107  * entmsg()  -  edit and create a message
01108  *              returns 0 if message was saved
01109  */
01110 int entmsg(CtdlIPC *ipc,
01111               int is_reply, /* nonzero if this was a <R>eply command */
01112               int c,        /* mode */
01113               int masquerade       /* prompt for a non-default display name? */
01114 ) {
01115        char buf[SIZ];
01116        int a, b;
01117        int need_recp = 0;
01118        int mode;
01119        long highmsg = 0L;
01120        FILE *fp;
01121        char subject[SIZ];
01122        struct ctdlipcmessage message;
01123        unsigned long *msgarr = NULL;
01124        int r;               /* IPC response code */
01125        int subject_required = 0;
01126 
01127        if (!entmsg_ok) {
01128               scr_printf("You may not enter messages in this type of room.\n");
01129               return(1);
01130        }
01131 
01132        if (c > 0) {
01133               mode = 1;
01134        }
01135        else {
01136               mode = 0;
01137        }
01138 
01139        strcpy(subject, "");
01140 
01141        /*
01142         * First, check to see if we have permission to enter a message in
01143         * this room.  The server will return an error code if we can't.
01144         */
01145        strcpy(message.recipient, "");
01146        strcpy(message.author, "");
01147        strcpy(message.subject, "");
01148        strcpy(message.references, "");
01149        message.text = "";          /* point to "", changes later */
01150        message.anonymous = 0;
01151        message.type = mode;
01152 
01153        if (masquerade) {
01154               newprompt("Display name for this message: ", message.author, 40);
01155        }
01156 
01157        if (is_reply) {
01158 
01159               if (!IsEmptyStr(reply_subject)) {
01160                      if (!strncasecmp(reply_subject,
01161                         "Re: ", 3)) {
01162                             strcpy(message.subject, reply_subject);
01163                      }
01164                      else {
01165                             snprintf(message.subject,
01166                                    sizeof message.subject,
01167                                    "Re: %s",
01168                                    reply_subject);
01169                      }
01170               }
01171 
01172               /* Trim down excessively long lists of thread references.  We eliminate the
01173                * second one in the list so that the thread root remains intact.
01174                */
01175               int rrtok = num_tokens(reply_references, '|');
01176               int rrlen = strlen(reply_references);
01177               if ( ((rrtok >= 3) && (rrlen > 900)) || (rrtok > 10) ) {
01178                      remove_token(reply_references, 1, '|');
01179               }
01180 
01181               snprintf(message.references, sizeof message.references, "%s%s%s",
01182                      reply_references,
01183                      (IsEmptyStr(reply_references) ? "" : "|"),
01184                      reply_inreplyto
01185               );
01186        }
01187 
01188        r = CtdlIPCPostMessage(ipc, 0, &subject_required, &message, buf);
01189 
01190        if (r / 100 != 2 && r / 10 != 57) {
01191               scr_printf("%s\n", buf);
01192               return (1);
01193        }
01194 
01195        /* Error code 570 is special.  It means that we CAN enter a message
01196         * in this room, but a recipient needs to be specified.
01197         */
01198        need_recp = 0;
01199        if (r / 10 == 57) {
01200               need_recp = 1;
01201        }
01202 
01203        /* If the user is a dumbass, tell them how to type. */
01204        if ((userflags & US_EXPERT) == 0) {
01205               formout(ipc, "entermsg");
01206        }
01207 
01208        /* Handle the selection of a recipient, if necessary. */
01209        strcpy(buf, "");
01210        if (need_recp == 1) {
01211               if (axlevel >= AxProbU) {
01212                      if (is_reply) {
01213                             strcpy(buf, reply_to);
01214                      } else {
01215                             newprompt("Enter recipient: ", buf, SIZ-100);
01216                             if (IsEmptyStr(buf)) {
01217                                    return (1);
01218                             }
01219                      }
01220               } else
01221                      strcpy(buf, "sysop");
01222        }
01223        strcpy(message.recipient, buf);
01224 
01225        if (room_flags & QR_ANONOPT) {
01226               scr_printf("Anonymous (Y/N)? ");
01227               if (yesno() == 1)
01228                      message.anonymous = 1;
01229        }
01230 
01231        /* If it's mail, we've got to check the validity of the recipient... */
01232        if (!IsEmptyStr(message.recipient)) {
01233               r = CtdlIPCPostMessage(ipc, 0, &subject_required,  &message, buf);
01234               if (r / 100 != 2) {
01235                      scr_printf("%s\n", buf);
01236                      return (1);
01237               }
01238        }
01239 
01240        /* Learn the number of the newest message in in the room, so we can
01241         * tell upon saving whether someone else has posted too.
01242         */
01243        num_msgs = 0;
01244        r = CtdlIPCGetMessages(ipc, LastMessages, 1, NULL, &msgarr, buf);
01245        if (r / 100 != 1) {
01246               scr_printf("%s\n", buf);
01247        } else {
01248               for (num_msgs = 0; msgarr[num_msgs]; num_msgs++)
01249                      ;
01250        }
01251 
01252        /* Now compose the message... */
01253        if (client_make_message(ipc, temp, message.recipient,
01254           message.anonymous, 0, c, message.subject, subject_required) != 0) {
01255            if (msgarr) free(msgarr);      
01256               return (2);
01257        }
01258 
01259        /* Reopen the temp file that was created, so we can send it */
01260        fp = fopen(temp, "r");
01261 
01262        if (!fp || !(message.text = load_message_from_file(fp))) {
01263               scr_printf("*** Internal error while trying to save message!\n"
01264                      "%s: %s\n",
01265                      temp, strerror(errno));
01266               unlink(temp);
01267               return(errno);
01268        }
01269 
01270        if (fp) fclose(fp);
01271 
01272        /* Break lines that are >1024 characters, otherwise the server
01273         * will truncate them.
01274         */
01275        break_big_lines(message.text);
01276 
01277        /* Transmit message to the server */
01278        r = CtdlIPCPostMessage(ipc, 1, NULL, &message, buf);
01279        if (r / 100 != 4) {
01280               scr_printf("%s\n", buf);
01281               return (1);
01282        }
01283 
01284        /* Yes, unlink it now, so it doesn't stick around if we crash */
01285        unlink(temp);
01286 
01287        if (num_msgs >= 1) highmsg = msgarr[num_msgs - 1];
01288 
01289        if (msgarr) free(msgarr);
01290        msgarr = NULL;
01291        r = CtdlIPCGetMessages(ipc, NewMessages, 0, NULL, &msgarr, buf);
01292        if (r / 100 != 1) {
01293               scr_printf("%s\n", buf);
01294        } else {
01295               for (num_msgs = 0; msgarr[num_msgs]; num_msgs++)
01296                      ;
01297        }
01298 
01299        /* get new highest message number in room to set lrp for goto... */
01300        maxmsgnum = msgarr[num_msgs - 1];
01301 
01302        /* now see if anyone else has posted in here */
01303        b = (-1);
01304        for (a = 0; a < num_msgs; ++a) {
01305               if (msgarr[a] > highmsg) {
01306                      ++b;
01307               }
01308        }
01309        if (msgarr) free(msgarr);
01310        msgarr = NULL;
01311 
01312        /* In the Mail> room, this algorithm always counts one message
01313         * higher than in public rooms, so we decrement it by one.
01314         */
01315        if (need_recp) {
01316               --b;
01317        }
01318 
01319        if (b == 1) {
01320               scr_printf("*** 1 additional message has been entered "
01321                      "in this room by another user.\n");
01322        }
01323        else if (b > 1) {
01324               scr_printf("*** %d additional messages have been entered "
01325                      "in this room by other users.\n", b);
01326        }
01327     free(message.text);
01328 
01329        return(0);
01330 }
01331 
01332 /*
01333  * Do editing on a quoted file
01334  */
01335 void process_quote(void)
01336 {
01337        FILE *qfile, *tfile;
01338        char buf[128];
01339        int line, qstart, qend;
01340 
01341        /* Unlink the second temp file as soon as it's opened, so it'll get
01342         * deleted even if the program dies
01343         */
01344        qfile = fopen(temp2, "r");
01345        unlink(temp2);
01346 
01347        /* Display the quotable text with line numbers added */
01348        line = 0;
01349        if (fgets(buf, 128, qfile) == NULL) {
01350               /* we're skipping a line here */
01351        }
01352        while (fgets(buf, 128, qfile) != NULL) {
01353               scr_printf("%3d %s", ++line, buf);
01354        }
01355 
01356        qstart = intprompt("Begin quoting at", 1, 1, line);
01357        qend = intprompt("  End quoting at", line, qstart, line);
01358 
01359        rewind(qfile);
01360        line = 0;
01361        if (fgets(buf, 128, qfile) == NULL) {
01362               /* we're skipping a line here */
01363        }
01364        tfile = fopen(temp, "w");
01365        while (fgets(buf, 128, qfile) != NULL) {
01366               if ((++line >= qstart) && (line <= qend))
01367                      fprintf(tfile, " >%s", buf);
01368        }
01369        fprintf(tfile, " \n");
01370        fclose(qfile);
01371        fclose(tfile);
01372        chmod(temp, 0666);
01373 }
01374 
01375 
01376 
01377 /*
01378  * List the URL's which were embedded in the previous message
01379  */
01380 void list_urls(CtdlIPC *ipc)
01381 {
01382        int i;
01383        char cmd[SIZ];
01384        int rv;
01385 
01386        if (num_urls == 0) {
01387               scr_printf("There were no URL's in the previous message.\n\n");
01388               return;
01389        }
01390 
01391        for (i = 0; i < num_urls; ++i) {
01392               scr_printf("%3d %s\n", i + 1, urls[i]);
01393        }
01394 
01395        if ((i = num_urls) != 1)
01396               i = intprompt("Display which one", 1, 1, num_urls);
01397 
01398        snprintf(cmd, sizeof cmd, rc_url_cmd, urls[i - 1]);
01399        rv = system(cmd);
01400        if (rv != 0) 
01401               scr_printf("failed to '%s' by %d\n", cmd, rv);
01402        scr_printf("\n");
01403 }
01404 
01405 
01406 /*
01407  * Run image viewer in background
01408  */
01409 int do_image_view(const char *filename)
01410 {
01411        char cmd[SIZ];
01412        pid_t childpid;
01413 
01414        snprintf(cmd, sizeof cmd, imagecmd, filename);
01415        childpid = fork();
01416        if (childpid < 0) {
01417               unlink(filename);
01418               return childpid;
01419        }
01420 
01421        if (childpid == 0) {
01422               int retcode;
01423               pid_t grandchildpid;
01424 
01425               grandchildpid = fork();
01426               if (grandchildpid < 0) {
01427                      return grandchildpid;
01428               }
01429 
01430               if (grandchildpid == 0) {
01431                      int nullfd;
01432                      int outfd = -1;
01433                      int errfd = -1;
01434 
01435                      nullfd = open("/dev/null", O_WRONLY);
01436                      if (nullfd > -1) {
01437                             dup2(1, outfd);
01438                             dup2(2, errfd);
01439                             dup2(nullfd, 1);
01440                             dup2(nullfd, 2);
01441                      }
01442                      retcode = system(cmd);
01443                      if (nullfd > -1) {
01444                             dup2(outfd, 1);
01445                             dup2(errfd, 2);
01446                             close(nullfd);
01447                      }
01448                      unlink(filename);
01449                      exit(retcode);
01450               }
01451 
01452               if (grandchildpid > 0) {
01453                      exit(0);
01454               }
01455        }
01456 
01457        if (childpid > 0) {
01458               int retcode;
01459 
01460               waitpid(childpid, &retcode, 0);
01461               return retcode;
01462        }
01463        
01464        return -1;
01465 }
01466 
01467 
01468 /*
01469  * View an image attached to a message
01470  */
01471 void image_view(CtdlIPC *ipc, unsigned long msg)
01472 {
01473        struct parts *ptr = last_message_parts;
01474        char part[SIZ];
01475        int found = 0;
01476 
01477        /* Run through available parts */
01478        for (ptr = last_message_parts; ptr; ptr = ptr->next) {
01479               if ((!strcasecmp(ptr->disposition, "attachment")
01480                  || !strcasecmp(ptr->disposition, "inline"))
01481                  && !strncmp(ptr->mimetype, "image/", 6)) {
01482                      found++;
01483                      if (found == 1) {
01484                             strcpy(part, ptr->number);
01485                      }
01486               }
01487        }
01488 
01489        while (found > 0) {
01490               if (found > 1)
01491                      strprompt("View which part (0 when done)", part, SIZ-1);
01492               found = -found;
01493               for (ptr = last_message_parts; ptr; ptr = ptr->next) {
01494                      if ((!strcasecmp(ptr->disposition, "attachment")
01495                         || !strcasecmp(ptr->disposition, "inline"))
01496                         && !strncmp(ptr->mimetype, "image/", 6)
01497                         && !strcasecmp(ptr->number, part)) {
01498                             char tmp[PATH_MAX];
01499                             char buf[SIZ];
01500                             void *file = NULL; /* The downloaded file */
01501                             int r;
01502        
01503                             /* view image */
01504                             found = -found;
01505                             r = CtdlIPCAttachmentDownload(ipc, msg, ptr->number, &file, progress, buf);
01506                             if (r / 100 != 2) {
01507                                    scr_printf("%s\n", buf);
01508                             } else {
01509                                    size_t len;
01510        
01511                                    len = (size_t)extract_long(buf, 0);
01512                                    progress(ipc, len, len);
01513                                    scr_flush();
01514                                    CtdlMakeTempFileName(tmp, sizeof tmp);
01515                                    strcat(tmp, ptr->filename);
01516                                    save_buffer(file, len, tmp);
01517                                    free(file);
01518                                    do_image_view(tmp);
01519                             }
01520                             break;
01521                      }
01522               }
01523               if (found == 1)
01524                      break;
01525        }
01526 }
01527  
01528 
01529 /*
01530  * Read the messages in the current room
01531  */
01532 void readmsgs(CtdlIPC *ipc,
01533        enum MessageList c,         /* see listing in citadel_ipc.h */
01534        enum MessageDirection rdir, /* 1=Forward (-1)=Reverse */
01535        int q         /* Number of msgs to read (if c==3) */
01536 ) {
01537        int a, e, f, g, start;
01538        int savedpos;
01539        int hold_sw = 0;
01540        char arcflag = 0;
01541        char quotflag = 0;
01542        int hold_color = 0;
01543        char prtfile[PATH_MAX];
01544        char pagin;
01545        char cmd[SIZ];
01546        char targ[ROOMNAMELEN];
01547        char filename[PATH_MAX];
01548        char save_to[PATH_MAX];
01549        void *attachment = NULL;    /* Downloaded attachment */
01550        FILE *dest = NULL;          /* Alternate destination other than screen */
01551        int r;                      /* IPC response code */
01552        static int att_seq = 0;            /* Attachment download sequence number */
01553        int rv = 0;                 /* silence the stupid warn_unused_result warnings */
01554 
01555        CtdlMakeTempFileName(prtfile, sizeof prtfile);
01556 
01557        if (msg_arr) {
01558               free(msg_arr);
01559               msg_arr = NULL;
01560        }
01561        r = CtdlIPCGetMessages(ipc, c, q, NULL, &msg_arr, cmd);
01562        if (r / 100 != 1) {
01563               scr_printf("%s\n", cmd);
01564        } else {
01565               for (num_msgs = 0; msg_arr[num_msgs]; num_msgs++)
01566                      ;
01567        }
01568 
01569        if (num_msgs == 0) { /* TODO look at this later */
01570               if (c == LastMessages) return;
01571               scr_printf("*** There are no ");
01572               if (c == NewMessages) scr_printf("new ");
01573               if (c == OldMessages) scr_printf("old ");
01574               scr_printf("messages in this room.\n");
01575               return;
01576        }
01577 
01578        /* this loop cycles through each message... */
01579        start = ((rdir == 1) ? 0 : (num_msgs - 1));
01580        for (a = start; ((a < num_msgs) && (a >= 0)); a = a + rdir) {
01581               while (msg_arr[a] == 0L) {
01582                      a = a + rdir;
01583                      if ((a == num_msgs) || (a == (-1)))
01584                             return;
01585               }
01586 
01587 RAGAIN:              pagin = ((arcflag == 0)
01588                       && (quotflag == 0)
01589                       && (userflags & US_PAGINATOR)) ? 1 : 0;
01590 
01591               /* If we're doing a quote, set the screenwidth to 72 */
01592               if (quotflag) {
01593                      hold_sw = screenwidth;
01594                      screenwidth = 72;
01595               }
01596 
01597               /* If printing or archiving, set the screenwidth to 80 */
01598               if (arcflag) {
01599                      hold_sw = screenwidth;
01600                      screenwidth = 80;
01601               }
01602 
01603               /* clear parts list */
01604               free_parts(last_message_parts);
01605               last_message_parts = NULL;
01606 
01607               /* now read the message... */
01608               e = read_message(ipc, msg_arr[a], pagin, dest);
01609 
01610               /* ...and set the screenwidth back if we have to */
01611               if ((quotflag) || (arcflag)) {
01612                      screenwidth = hold_sw;
01613               }
01614 RMSGREAD:
01615               highest_msg_read = msg_arr[a];
01616               if (quotflag) {
01617                      fclose(dest);
01618                      dest = NULL;
01619                      quotflag = 0;
01620                      enable_color = hold_color;
01621                      process_quote();
01622                      e = 'r';
01623                      goto DONE_QUOTING;
01624               }
01625               if (arcflag) {
01626                      fclose(dest);
01627                      dest = NULL;
01628                      arcflag = 0;
01629                      enable_color = hold_color;
01630                      f = fork();
01631                      if (f == 0) {
01632                             if (freopen(prtfile, "r", stdin) == NULL) {
01633                                    /* we probably should handle the error condition here */
01634                             }
01635                             stty_ctdl(SB_RESTORE);
01636                             ka_system(printcmd);
01637                             stty_ctdl(SB_NO_INTR);
01638                             unlink(prtfile);
01639                             exit(0);
01640                      }
01641                      if (f > 0)
01642                             do {
01643                                    g = wait(NULL);
01644                             } while ((g != f) && (g >= 0));
01645                      scr_printf("Message printed.\n");
01646               }
01647               if (e == SIGQUIT)
01648                      return;
01649               if (((userflags & US_NOPROMPT) || (e == SIGINT))
01650                      && (((room_flags & QR_MAILBOX) == 0)
01651                      || (rc_force_mail_prompts == 0))) {
01652                      e = 'n';
01653               } else {
01654                      color(DIM_WHITE);
01655                      scr_printf("(");
01656                      color(BRIGHT_WHITE);
01657                      scr_printf("%d", num_msgs - a - 1);
01658                      color(DIM_WHITE);
01659                      scr_printf(") ");
01660 
01661                      keyopt("<B>ack <A>gain <R>eply reply<Q>uoted <N>ext <S>top ");
01662                      if (rc_url_cmd[0] && num_urls)
01663                             keyopt("<U>RLview ");
01664                      if (has_images > 0 && !IsEmptyStr(imagecmd))
01665                             keyopt("<I>mages ");
01666                      keyopt("<?>help -> ");
01667 
01668                      do {
01669                             e = (inkey() & 127);
01670                             e = tolower(e);
01671 /* return key same as <N> */ if (e == 10)
01672                                    e = 'n';
01673 /* space key same as <N> */ if (e == 32)
01674                                    e = 'n';
01675 /* del/move for aides only */
01676                                 if (  (!is_room_aide)
01677                                    && ((room_flags & QR_MAILBOX) == 0)
01678                                    && ((room_flags2 & QR2_COLLABDEL) == 0)
01679                                    ) {
01680                                    if ((e == 'd') || (e == 'm'))
01681                                           e = 0;
01682                             }
01683 /* print only if available */
01684                             if ((e == 'p') && (IsEmptyStr(printcmd)))
01685                                    e = 0;
01686 /* can't file if not allowed */
01687                                 if ((e == 'f')
01688                                    && (rc_allow_attachments == 0))
01689                                    e = 0;
01690 /* link only if browser avail*/
01691                                 if ((e == 'u')
01692                                    && (IsEmptyStr(rc_url_cmd)))
01693                                    e = 0;
01694                             if ((e == 'i')
01695                                    && (IsEmptyStr(imagecmd) || !has_images))
01696                                    e = 0;
01697                      } while ((e != 'a') && (e != 'n') && (e != 's')
01698                              && (e != 'd') && (e != 'm') && (e != 'p')
01699                              && (e != 'q') && (e != 'b') && (e != 'h')
01700                              && (e != 'r') && (e != 'f') && (e != '?')
01701                              && (e != 'u') && (e != 'c') && (e != 'y')
01702                              && (e != 'i') && (e != 'o') );
01703                      switch (e) {
01704                      case 's':
01705                             scr_printf("Stop");
01706                             break;
01707                      case 'a':
01708                             scr_printf("Again");
01709                             break;
01710                      case 'd':
01711                             scr_printf("Delete");
01712                             break;
01713                      case 'm':
01714                             scr_printf("Move");
01715                             break;
01716                      case 'c':
01717                             scr_printf("Copy");
01718                             break;
01719                      case 'n':
01720                             scr_printf("Next");
01721                             break;
01722                      case 'p':
01723                             scr_printf("Print");
01724                             break;
01725                      case 'q':
01726                             scr_printf("reply Quoted");
01727                             break;
01728                      case 'b':
01729                             scr_printf("Back");
01730                             break;
01731                      case 'h':
01732                             scr_printf("Header");
01733                             break;
01734                      case 'r':
01735                             scr_printf("Reply");
01736                             break;
01737                      case 'o':
01738                             scr_printf("Open attachments");
01739                             break;
01740                      case 'f':
01741                             scr_printf("File");
01742                             break;
01743                      case 'u':
01744                             scr_printf("URL's");
01745                             break;
01746                      case 'y':
01747                             scr_printf("mY next");
01748                             break;
01749                      case 'i':
01750                             break;
01751                      case '?':
01752                             scr_printf("? <help>");
01753                             break;
01754                      }
01755                      if (userflags & US_DISAPPEAR || e == 'i')
01756                             scr_printf("\r%79s\r", "");
01757                      else
01758                             scr_printf("\n");
01759               }
01760 DONE_QUOTING: switch (e) {
01761               case '?':
01762                      scr_printf("Options available here:\n"
01763                             " ?  Help (prints this message)\n"
01764                             " S  Stop reading immediately\n"
01765                             " A  Again (repeats last message)\n"
01766                             " N  Next (continue with next message)\n"
01767                             " Y  My Next (continue with next message you authored)\n"
01768                             " B  Back (go back to previous message)\n");
01769                      if (  (is_room_aide)
01770                         || (room_flags & QR_MAILBOX)
01771                         || (room_flags2 & QR2_COLLABDEL)
01772                      ) {
01773                             scr_printf(" D  Delete this message\n"
01774                                    " M  Move message to another room\n");
01775                      }
01776                      scr_printf(" C  Copy message to another room\n");
01777                      if (!IsEmptyStr(printcmd))
01778                             scr_printf(" P  Print this message\n");
01779                      scr_printf(
01780                             " Q  Reply to this message, quoting portions of it\n"
01781                             " H  Headers (display message headers only)\n");
01782                      if (is_mail)
01783                             scr_printf(" R  Reply to this message\n");
01784                      if (rc_allow_attachments) {
01785                             scr_printf(" O  (Open attachments)\n");
01786                             scr_printf(" F  (save attachments to a File)\n");
01787                      }
01788                      if (!IsEmptyStr(rc_url_cmd))
01789                             scr_printf(" U  (list URL's for display)\n");
01790                      if (!IsEmptyStr(imagecmd) && has_images > 0)
01791                             scr_printf(" I  Image viewer\n");
01792                      scr_printf("\n");
01793                      goto RMSGREAD;
01794               case 'p':
01795                      scr_flush();
01796                      dest = fopen(prtfile, "w");
01797                      arcflag = 1;
01798                      hold_color = enable_color;
01799                      enable_color = 0;
01800                      goto RAGAIN;
01801               case 'q':
01802                      scr_flush();
01803                      dest = fopen(temp2, "w");
01804                      quotflag = 1;
01805                      hold_color = enable_color;
01806                      enable_color = 0;
01807                      goto RAGAIN;
01808               case 's':
01809                      return;
01810               case 'a':
01811                      goto RAGAIN;
01812               case 'b':
01813                      a = a - (rdir * 2);
01814                      break;
01815               case 'm':
01816               case 'c':
01817                      newprompt("Enter target room: ",
01818                               targ, ROOMNAMELEN - 1);
01819                      if (!IsEmptyStr(targ)) {
01820                             r = CtdlIPCMoveMessage(ipc, (e == 'c' ? 1 : 0),
01821                                                  msg_arr[a], targ, cmd);
01822                             scr_printf("%s\n", cmd);
01823                             if (r / 100 == 2)
01824                                    msg_arr[a] = 0L;
01825                      } else {
01826                             goto RMSGREAD;
01827                      }
01828                      if (r / 100 != 2)    /* r will be init'ed, FIXME */
01829                             goto RMSGREAD;       /* the logic here sucks */
01830                      break;
01831               case 'o':
01832               case 'f':
01833                      newprompt("Which section? ", filename, ((sizeof filename) - 1));
01834                      r = CtdlIPCAttachmentDownload(ipc, msg_arr[a],
01835                                    filename, &attachment, progress, cmd);
01836                      if (r / 100 != 2) {
01837                             scr_printf("%s\n", cmd);
01838                      } else {
01839                             extract_token(filename, cmd, 2, '|', sizeof filename);
01840                             /*
01841                              * Part 1 won't have a filename; use the
01842                              * subject of the message instead. IO
01843                              */
01844                             if (IsEmptyStr(filename)) {
01845                                    strcpy(filename, reply_subject);
01846                             }
01847                             if (e == 'o') {             /* open attachment */
01848                                    mkdir(tempdir, 0700);
01849                                    snprintf(save_to, sizeof save_to, "%s/%04x.%s",
01850                                           tempdir,
01851                                           ++att_seq,
01852                                           filename);
01853                                    save_buffer(attachment, extract_unsigned_long(cmd, 0), save_to);
01854                                    snprintf(cmd, sizeof cmd, rc_open_cmd, save_to);
01855                                    rv = system(cmd);
01856                                    if (rv != 0)
01857                                           scr_printf("failed to save %s Reason %d\n", cmd, rv);
01858                             }
01859                             else {               /* save attachment to disk */
01860                                    destination_directory(save_to, filename);
01861                                    save_buffer(attachment, extract_unsigned_long(cmd, 0), save_to);
01862                             }
01863                      }
01864                      if (attachment) {
01865                             free(attachment);
01866                             attachment = NULL;
01867                      }
01868                      goto RMSGREAD;
01869               case 'd':
01870                      scr_printf("*** Delete this message? ");
01871                      if (yesno() == 1) {
01872                             r = CtdlIPCDeleteMessage(ipc, msg_arr[a], cmd);
01873                             scr_printf("%s\n", cmd);
01874                             if (r / 100 == 2)
01875                                    msg_arr[a] = 0L;
01876                      } else {
01877                             goto RMSGREAD;
01878                      }
01879                      break;
01880               case 'h':
01881                      read_message(ipc, msg_arr[a], READ_HEADER, NULL);
01882                      goto RMSGREAD;
01883               case 'r':
01884                      savedpos = num_msgs;
01885                      entmsg(ipc, 1, ((userflags & US_EXTEDIT) ? 2 : 0), 0);
01886                      num_msgs = savedpos;
01887                      goto RMSGREAD;
01888               case 'u':
01889                      list_urls(ipc);
01890                      goto RMSGREAD;
01891               case 'i':
01892                      image_view(ipc, msg_arr[a]);
01893                      goto RMSGREAD;
01894            case 'y':
01895           { /* hack hack hack */
01896             /* find the next message by me, stay here if we find nothing */
01897             int finda;
01898             int lasta = a;
01899             for (finda = (a + rdir); ((finda < num_msgs) && (finda >= 0)); finda += rdir)
01900               {
01901               /* This is repetitively dumb, but that's what computers are for.
01902                  We have to load up messages until we find one by us */
01903               char buf[SIZ];
01904               int founda = 0;
01905               struct ctdlipcmessage *msg = NULL;
01906                 
01907               /* read the header so we can get 'from=' */
01908               r = CtdlIPCGetSingleMessage(ipc, msg_arr[finda], 1, 0, &msg, buf);
01909               if (!strncasecmp(msg->author, fullname, sizeof(fullname))) {
01910                      a = lasta; /* meesa current */
01911                      founda = 1;
01912               }
01913 
01914               free(msg);
01915 
01916               if (founda)
01917                      break; /* for */
01918               lasta = finda; /* keep one behind or we skip on the reentrance to the for */
01919               } /* for */
01920           } /* case 'y' */
01921       } /* switch */
01922        }                    /* end for loop */
01923 }                           /* end read routine */
01924 
01925 
01926 
01927 
01928 /*
01929  * View and edit a system message
01930  */
01931 void edit_system_message(CtdlIPC *ipc, char *which_message)
01932 {
01933        char desc[SIZ];
01934        char read_cmd[SIZ];
01935        char write_cmd[SIZ];
01936 
01937        snprintf(desc, sizeof desc, "system message '%s'", which_message);
01938        snprintf(read_cmd, sizeof read_cmd, "MESG %s", which_message);
01939        snprintf(write_cmd, sizeof write_cmd, "EMSG %s", which_message);
01940        do_edit(ipc, desc, read_cmd, "NOOP", write_cmd);
01941 }
01942 
01943 
01944 
01945 
01946 /*
01947  * Loads the contents of a file into memory.  Caller must free the allocated
01948  * memory.
01949  */
01950 char *load_message_from_file(FILE *src)
01951 {
01952        size_t i;
01953        size_t got = 0;
01954        char *dest = NULL;
01955 
01956        fseek(src, 0, SEEK_END);
01957        i = ftell(src);
01958        rewind(src);
01959 
01960        dest = (char *)calloc(1, i + 1);
01961        if (!dest)
01962               return NULL;
01963 
01964        while (got < i) {
01965               size_t g;
01966 
01967               g = fread(dest + got, 1, i - got, src);
01968               got += g;
01969               if (g < i - got) {
01970                      /* Interrupted system call, keep going */
01971                      if (errno == EINTR)
01972                             continue;
01973                      /* At this point we have either EOF or error */
01974                      i = got;
01975                      break;
01976               }
01977               dest[i] = 0;
01978        }
01979 
01980        return dest;
01981 }