home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / ONLINE / ELM23-2 / ELM23-2.ZIP / src / showmsg.c < prev    next >
C/C++ Source or Header  |  1996-08-04  |  19KB  |  628 lines

  1.  
  2. static char rcsid[] = "@(#)$Id: showmsg.c,v 4.1.1.1 90/07/12 23:04:26 syd Exp $";
  3.  
  4. /*******************************************************************************
  5.  *  The Elm Mail System  -  $Revision: 4.1.1.1 $   $State: Exp $
  6.  *
  7.  *             Copyright (c) 1986, 1987 Dave Taylor
  8.  *             Copyright (c) 1988, 1989, 1990 USENET Community Trust
  9.  *******************************************************************************
  10.  * Bug reports, patches, comments, suggestions should be sent to:
  11.  *
  12.  *    Syd Weinstein, Elm Coordinator
  13.  *    elm@DSI.COM            dsinc!elm
  14.  *
  15.  *******************************************************************************
  16.  * $Log:    showmsg.c,v $
  17.  * Revision 4.1.1.1  90/07/12  23:04:26  syd
  18.  * Fix MMDF case, where MSG_SEPERATOR has newline, buffer check
  19.  * didnt, thus it didnt detect the MSG_SEPERATOR.
  20.  * From: jbwaters@bsu-cs.bsu.edu (J. Brian Waters)
  21.  *
  22.  * Revision 4.1  90/04/28  22:44:06  syd
  23.  * checkin of Elm 2.3 as of Release PL0
  24.  *
  25.  *
  26.  ******************************************************************************/
  27.  
  28. /** This file contains all the routines needed to display the specified
  29.     message.
  30. **/
  31.  
  32. #include "headers.h"
  33. #include <ctype.h>
  34. #include <errno.h>
  35.  
  36. #ifdef BSD
  37. # include <sys/wait.h>
  38. # undef       tolower
  39. #endif
  40.  
  41. #ifdef OS2
  42. # include <sys/wait.h>
  43. #else
  44. extern int errno;
  45. #endif
  46.  
  47. char *error_name(), *strcat(), *strcpy();
  48. void   _exit();
  49.  
  50. int    memory_lock = FALSE;    /* is it available?? */
  51. int    pipe_abort  = FALSE;    /* did we receive a SIGNAL(SIGPIPE)? */
  52.  
  53. FILE *pipe_wr_fp;        /* file pointer to write to external pager */
  54. extern int lines_displayed,    /* defined in "builtin" */
  55.        lines_put_on_screen;    /*    ditto too!        */
  56.  
  57. int
  58. show_msg(number)
  59. int number;
  60. {
  61.     /*** Display number'th message.  Get starting and ending lines
  62.          of message from headers data structure, then fly through
  63.          the file, displaying only those lines that are between the
  64.          two!
  65.  
  66.          Return 0 to return to the index screen or a character entered
  67.          by the user to initiate a command without returning to
  68.          the index screen (to be processed via process_showmsg_cmd()).
  69.     ***/
  70.  
  71.     char title1[SLEN], title2[SLEN], title3[SLEN], titlebuf[SLEN];
  72.     char who[LONG_STRING], buffer[VERY_LONG_STRING];
  73. #if defined(BSD) && !defined(WEXITSTATUS)
  74.     union wait wait_stat;
  75. #else
  76.     int wait_stat;
  77. #endif
  78.  
  79.     int crypted = 0;            /* encryption */
  80.     int weed_header, weeding_out = 0;    /* weeding    */
  81.     int using_to,                /* misc use   */
  82.         pipe_fd[2],                /* pipe file descriptors */
  83.         new_pipe_fd,            /* dup'ed pipe fil des */
  84.         lines,                /* num lines in msg */
  85.         fork_ret,                /* fork return value */
  86.         wait_ret,                /* wait return value */
  87.         form_letter = FALSE,        /* Form ltr?  */
  88.         form_letter_section = 0,        /* section    */
  89.         padding = 0,            /*   counter  */
  90.         builtin = FALSE,            /* our pager? */
  91.         val = 0,                /* return val */
  92.         buf_len;                /* line length */
  93.     struct header_rec *current_header = headers[number-1];
  94.  
  95.     FILE *msgfile = mailfile;
  96.     char clearfile[SLEN];
  97.  
  98.     lines = current_header->lines;
  99.  
  100.     dprint(4, (debugfile,"displaying %d lines from message %d using %s\n",
  101.         lines, number, pager));
  102.  
  103.     if (number > message_count || number < 1)
  104.       return(val);
  105.  
  106.     if(ison(current_header->status, NEW)) {
  107.       clearit(current_header->status, NEW);   /* it's been read now! */
  108.       current_header->status_chgd = TRUE;
  109.     }
  110.     if(ison(current_header->status, UNREAD)) {
  111.       clearit(current_header->status, UNREAD);   /* it's been read now! */
  112.       current_header->status_chgd = TRUE;
  113.     }
  114.  
  115.     memory_lock = FALSE;
  116.  
  117.     /* some explanation for that last one - We COULD use memory locking
  118.        to speed up the paging, but the action of "ClearScreen" on a screen
  119.        with memory lock turned on seems to vary considerably (amazingly so)
  120.        so it's safer to only allow memory lock to be a viable bit of
  121.        trickery when dumping text to the screen in scroll mode.
  122.        Philosophical arguments should be forwarded to Bruce at the
  123.        University of Walamazoo, Australia, via ACSNet  *wry chuckle* */
  124.  
  125.     if (fseek(mailfile, current_header->offset, 0) == -1) {
  126.       dprint(1, (debugfile,
  127.           "Error: seek %d bytes into file, errno %s (show_message)\n",
  128.           current_header->offset, error_name(errno)));
  129.       error2("ELM failed seeking %d bytes into file (%s).",
  130.           current_header->offset, error_name(errno));
  131.       return(val);
  132.     }
  133.     if(current_header->encrypted)
  134.       getkey(OFF);
  135.  
  136.     if (filter)
  137.       lines += perhaps_pgp_decode(number, clearfile);
  138.     fseek(mailfile, current_header->offset, 0); /* again */
  139.  
  140.     if(builtin=(first_word(pager,"builtin")||first_word(pager,"internal")))
  141.  
  142.       start_builtin(lines);
  143.  
  144.     else {
  145.  
  146.       /* put terminal out of raw mode so external pager has normal env */
  147.       Raw(OFF);
  148.  
  149. #ifdef OS2
  150.           if ( (pipe_wr_fp = popen(pager, "w")) == NULL )
  151.             return -1;
  152. #else
  153.       /* create pipe for external pager and fork */
  154.  
  155.       if(pipe(pipe_fd) == -1) {
  156.         dprint(1, (debugfile, "Error: pipe failed, errno %s (show_msg)\n",
  157.           error_name(errno)));
  158.         error1("Could not prepare for external pager(pipe()-%s).",
  159.           error_name(errno));
  160.         Raw(ON);
  161.         return(val);
  162.       }
  163.  
  164.       if((fork_ret = fork()) == -1) {
  165.  
  166.         dprint(1, (debugfile, "Error: fork failed, errno %s (show_msg)\n",
  167.           error_name(errno)));
  168.         error1("Could not prepare for external pager(fork()-%s).",
  169.           error_name(errno));
  170.         Raw(ON);
  171.         return(val);
  172.  
  173.       } else if (fork_ret == 0) {
  174.  
  175.         /* child fork */
  176.  
  177.         /* close write-only pipe fd and fit read-only pipe fd to stdin */
  178.  
  179.         close(pipe_fd[1]);
  180.         close(fileno(stdin));
  181.         if((new_pipe_fd = dup(pipe_fd[0])) == -1) {
  182.           dprint(1, (debugfile, "Error: dup failed, errno %s (show_msg)\n",
  183.         error_name(errno)));
  184.           error1("Could not prepare for external pager(dup()-%s).",
  185.         error_name(errno));
  186.           _exit(errno);
  187.         }
  188.         close(pipe_fd[0]);    /* original pipe fd no longer needed */
  189.  
  190.         /* use stdio on new pipe fd */
  191.         if(fdopen(new_pipe_fd, "r") == NULL) {
  192.           dprint(1,
  193.         (debugfile, "Error: child fdopen failed, errno %s (show_msg)\n",
  194.         error_name(errno)));
  195.           error1("Could not prepare for external pager(child fdopen()-%s).",
  196.         error_name(errno));
  197.           _exit(errno);
  198.         }
  199.  
  200.         /* now execute pager and exit */
  201.  
  202.         /* system_call() will return user to user's normal permissions.
  203.          * This is what makes this pipe secure - user won't have elm's
  204.          * special setgid permissions (if so configured) and will only
  205.          * be able to execute a pager that user normally has permission
  206.          * to execute */
  207.  
  208.         _exit(system_call(pager, SH, TRUE, TRUE));
  209.  
  210.       } /* else this is the parent fork */
  211.  
  212.       /* close read-only pipe fd and do write-only with stdio */
  213.       close(pipe_fd[0]);
  214.  
  215.       if((pipe_wr_fp = fdopen(pipe_fd[1], "w")) == NULL) {
  216.         dprint(1,
  217.           (debugfile, "Error: parent fdopen failed, errno %s (show_msg)\n",
  218.           error_name(errno)));
  219.         error1("Could not prepare for external pager(parent fdopen()-%s).",
  220.           error_name(errno));
  221.  
  222.         /* Failure - must close pipe and wait for child */
  223.         close(pipe_fd[1]);
  224.         while ((wait_ret = wait(&wait_stat)) != fork_ret && wait_ret!= -1)
  225.           ;
  226.  
  227.         Raw(OFF);
  228.         return(val);    /* pager may have already touched the screen */
  229.       }
  230. #endif
  231.  
  232.       /* and that's it! */
  233.       lines_displayed = 0;
  234.     }
  235.  
  236.     ClearScreen();
  237.  
  238.     if (cursor_control) transmit_functions(OFF);
  239.  
  240.     pipe_abort = FALSE;
  241.  
  242.     if (form_letter = (current_header->status&FORM_LETTER)) {
  243.       if (filter)
  244.         form_letter_section = 1;    /* initialize to section 1 */
  245.     }
  246.  
  247.     if (title_messages && filter) {
  248.  
  249.       using_to =
  250.         tail_of(current_header->from, who, current_header->to);
  251.  
  252.       sprintf(title1, "%s %d/%d  ",
  253.             headers[current-1]->status & DELETED ? "[deleted]" :
  254.             form_letter ? "Form": "Message",
  255.             number, message_count);
  256.       sprintf(title2, "%s %s", using_to? "To" : "From", who);
  257.       sprintf(title3, "  %s %s '%d at %s %s",
  258.                 current_header->month, current_header->day,
  259.                atoi(current_header->year), current_header->time,
  260.            current_header->time_zone);
  261.  
  262.       /* truncate or pad title2 portion on the right
  263.        * so that line fits exactly */
  264.       padding =
  265.         COLUMNS - 1 -
  266.         (strlen(title1) + (buf_len=strlen(title2)) + strlen(title3));
  267.  
  268.       sprintf(titlebuf, "%s%-*.*s%s\n", title1, buf_len+padding,
  269.           buf_len+padding, title2, title3);
  270.  
  271.       if (builtin)
  272.         display_line(titlebuf);
  273.       else
  274.         fprintf(pipe_wr_fp, "%s", titlebuf);
  275.  
  276.       /** if there's a subject, let's output it next,
  277.           centered if it fits on a single line.  **/
  278.  
  279.       if ((buf_len = strlen(current_header->subject)) > 0 &&
  280.         matches_weedlist("Subject:")) {
  281.         padding = (buf_len < COLUMNS ? COLUMNS - buf_len : 0);
  282.         sprintf(buffer, "%*s%s\n", padding/2, "", current_header->subject);
  283.       } else
  284.         strcpy(buffer, "\n");
  285.  
  286.       if (builtin)
  287.         display_line(buffer);
  288.       else
  289.         fprintf(pipe_wr_fp, "%s", buffer);
  290.  
  291.       /** was this message address to us?  if not, then to whom? **/
  292.  
  293.       if (! using_to && matches_weedlist("To:") && filter &&
  294.           strcmp(current_header->to,username) != 0 &&
  295.           strlen(current_header->to) > 0) {
  296.         if (strlen(current_header->to) > 60)
  297.           sprintf(buffer, "%s(message addressed to %.60s)\n",
  298.                 strlen(current_header->subject) > 0 ? "\n" : "",
  299.             current_header->to);
  300.         else
  301.           sprintf(buffer, "%s(message addressed to %s)\n",
  302.                 strlen(current_header->subject) > 0 ? "\n" : "",
  303.             current_header->to);
  304.         if (builtin)
  305.           display_line(buffer);
  306.         else
  307.           fprintf(pipe_wr_fp, "%s", buffer);
  308.       }
  309.  
  310.       /** The test above is: if we didn't originally send the mail
  311.           (e.g. we're not reading "mail.sent") AND the user is currently
  312.           weeding out the "To:" line (otherwise they'll get it twice!)
  313.           AND the user is actually weeding out headers AND the message
  314.           wasn't addressed to the user AND the 'to' address is non-zero
  315.           (consider what happens when the message doesn't HAVE a "To:"
  316.           line...the value is NULL but it doesn't match the username
  317.           either.  We don't want to display something ugly like
  318.           "(message addressed to )" which will just clutter up the
  319.           screen!).
  320.  
  321.           And you thought programming was EASY!!!!
  322.       **/
  323.  
  324.       /** one more friendly thing - output a line indicating what sort
  325.           of status the message has (e.g. Urgent etc).  Mostly added
  326.           for X.400 support, this is nonetheless generally useful to
  327.           include...
  328.       **/
  329.  
  330.       buffer[0] = '\0';
  331.  
  332.       /* we want to flag Urgent, Confidential, Private and Expired tags */
  333.  
  334.       if (current_header->status & PRIVATE)
  335.         strcpy(buffer, "\n(** This message is tagged Private");
  336.       else if (current_header->status & CONFIDENTIAL)
  337.         strcpy(buffer, "\n(** This message is tagged Company Confidential");
  338.  
  339.       if (current_header->status & URGENT) {
  340.         if (buffer[0] == '\0')
  341.           strcpy(buffer, "\n(** This message is tagged Urgent");
  342.         else if (current_header->status & EXPIRED)
  343.           strcat(buffer, ", Urgent");
  344.         else
  345.           strcat(buffer, " and Urgent");
  346.       }
  347.  
  348.       if (current_header->status & EXPIRED) {
  349.         if (buffer[0] == '\0')
  350.           strcpy(buffer, "\n(** This message has Expired");
  351.         else
  352.           strcat(buffer, ", and has Expired");
  353.       }
  354.  
  355.       if (buffer[0] != '\0') {
  356.         strcat(buffer, " **)\n");
  357.         if (builtin)
  358.           display_line(buffer);
  359.         else
  360.           fprintf(pipe_wr_fp, buffer);
  361.       }
  362.  
  363.       if (builtin)            /* this is for a one-line blank    */
  364.         display_line("\n");        /*   separator between the title   */
  365.       else                /*   stuff and the actual message  */
  366.         fprintf(pipe_wr_fp, "\n");    /*   we're trying to display       */
  367.  
  368.     }
  369.  
  370.     weed_header = filter;    /* allow us to change it after header */
  371.  
  372.     while (lines > 0 && pipe_abort == FALSE) {
  373.  
  374.         if (fgets(buffer, VERY_LONG_STRING, msgfile) == NULL) {
  375.           
  376.           if (msgfile == mailfile) {
  377.         dprint(1, (debugfile,
  378.           "Premature end of file! Lines left = %d msg = %s (show_msg)\n",
  379.           lines, number));
  380.  
  381.         error("Premature end of file!");
  382.         sleep(2);
  383.         break;
  384.           }
  385.           else {
  386.         fclose(msgfile);
  387.         unlink(clearfile);
  388.         clearfile[0] = 0;
  389.         msgfile = mailfile;
  390.         continue;
  391.           }
  392.         }
  393.         if ((buf_len=strlen(buffer)) > 0)  {
  394.           if(buffer[buf_len - 1] == '\n') {
  395.         lines--;
  396.             lines_displayed++;
  397.         }
  398.               no_ret(buffer);
  399.         }
  400.  
  401.           if (strlen(buffer) == 0) {
  402.           weed_header = 0;        /* past header! */
  403.           weeding_out = 0;
  404.         }
  405.  
  406.         if (form_letter && weed_header)
  407.         /* skip it.  NEVER display random headers in forms! */;
  408.         else if (weed_header && matches_weedlist(buffer))
  409.           weeding_out = 1;     /* aha!  We don't want to see this! */
  410.         else if (buffer[0] == '[') {
  411.           if (strcmp(buffer, START_ENCODE)==0)
  412.             crypted = ON;
  413.           else if (strcmp(buffer, END_ENCODE)==0)
  414.             crypted = OFF;
  415.           else if (crypted) {
  416.                 encode(buffer);
  417.             val = show_line(buffer, builtin);
  418.           }
  419.           else if (strlen(buffer) >= 5 && strncmp(buffer, "[pgp-", 5) == 0
  420.             && buffer[strlen(buffer) - 1] == ']')
  421.         /* skip */ ;
  422.           else
  423.             val = show_line(buffer, builtin);
  424.         }
  425.         else if (crypted) {
  426.           encode(buffer);
  427.           val = show_line(buffer, builtin);
  428.         }
  429.         else if (weeding_out) {
  430.           weeding_out = (whitespace(buffer[0]));    /* 'n' line weed */
  431.           if (! weeding_out)     /* just turned on! */
  432.             val = show_line(buffer, builtin);
  433.         }
  434.         else if (form_letter && first_word(buffer,"***") && filter) {
  435.           strcpy(buffer,
  436. "\n------------------------------------------------------------------------------\n");
  437.           val = show_line(buffer, builtin);    /* hide '***' */
  438.           form_letter_section++;
  439.         }
  440.         else if (form_letter_section == 1 || form_letter_section == 3)
  441.           /** skip this stuff - we can't deal with it... **/;
  442.         else if (strcmp(buffer, "-----BEGIN PGP MESSAGE-----") == 0
  443.              && filter) 
  444.         {
  445.           /* skip PGP block and insert cleartext file for it */
  446.           do 
  447.           {
  448.         if (fgets(buffer, VERY_LONG_STRING, msgfile) == NULL)
  449.           break;
  450.         if ((buf_len=strlen(buffer)) > 0)
  451.           no_ret(buffer);
  452.           } while (strcmp(buffer, "-----END PGP MESSAGE-----") != 0);
  453.           if (strlen(clearfile) > 0)
  454.         msgfile = fopen(clearfile, "r");
  455.         }
  456.         else
  457.           val = show_line(buffer, builtin);
  458.  
  459.         if (val != 0)    /* discontinue the display */
  460.           break;
  461.     }
  462.  
  463.         if (cursor_control) transmit_functions(ON);
  464.  
  465.     if (msgfile != mailfile) {
  466.       fclose(msgfile);
  467.       unlink(clearfile);
  468.     }
  469.  
  470.     if (!builtin) {
  471. #ifdef OS2
  472.       wait_stat = pclose(pipe_wr_fp);
  473.       val = WIFEXITED(wait_stat) ? WEXITSTATUS(wait_stat) : 0;
  474. #else
  475.       fclose(pipe_wr_fp);
  476.       while ((wait_ret = wait(&wait_stat)) != fork_ret
  477.           && wait_ret!= -1)
  478.         ;
  479. #endif
  480.       /* turn raw on **after** child terminates in case child
  481.        * doesn't put us back to cooked mode after we return ourselves
  482.        * to raw.
  483.        */
  484.       Raw(ON);
  485.       EndBold();
  486.       ClearScreen();
  487.     }
  488.  
  489.     /* If we are to prompt for a user input command and we don't
  490.      * already have one */
  491.     if ((prompt_after_pager || builtin) && val == 0) {
  492.       MoveCursor(LINES,0);
  493.       StartBold();
  494.       Write_to_screen(" Command ('i' to return to index): ", 0);
  495.       EndBold();
  496.       fflush(stdout);
  497.       val = ReadCh();
  498.     }
  499.  
  500.     if (memory_lock) EndMemlock();    /* turn it off!! */
  501.  
  502.     /* 'q' means quit current operation and pop back up to previous level -
  503.      * in this case it therefore means return to index screen.
  504.      */
  505.     return(val == 'i' || val == 'q' ? 0 : val);
  506. }
  507.  
  508. int
  509. show_line(buffer, builtin)
  510. char *buffer;
  511. int  builtin;
  512. {
  513.     /** Hands the given line to the output pipe.  'builtin' is true if
  514.         we're using the builtin pager.
  515.         Return the character entered by the user to indicate
  516.         a command other than continuing with the display (only possible
  517.         with the builtin pager), otherwise 0. **/
  518.  
  519.     strcat(buffer, "\n");
  520. #ifdef MMDF
  521.     if (strcmp(buffer, MSG_SEPERATOR) == 0)
  522.       return(0);    /* no reason to show the ending terminator */
  523. #endif /* MMDF */
  524.     if (builtin) {
  525.       return(display_line(buffer));
  526.     }
  527.     errno = 0;
  528.     fprintf(pipe_wr_fp, "%s", buffer);
  529.     if (errno != 0)
  530.       dprint(1, (debugfile, "\terror %s hit!\n", error_name(errno)));
  531.     return(0);
  532. }
  533.  
  534.  
  535. perhaps_pgp_decode(number, filename)
  536. int number;
  537. char *filename;
  538. {
  539.   char buffer[VERY_LONG_STRING], cryptname[SLEN], clearname[SLEN];
  540.   int lines = headers[number-1] -> lines;
  541.   int oldlines = 0, pgp = 0, sign = 0;
  542.   FILE *tempfile;
  543.  
  544.   filename[0] = 0;
  545.   sprintf(cryptname, "%s%d%s", temp_dir, getpid(), temp_file);
  546.   sprintf(clearname, "%s%d.msg", temp_dir, getpid());
  547.       
  548.   if ((tempfile = fopen(cryptname, "w")) == NULL) 
  549.   {
  550.     if(batch_only)
  551.       printf("Could not create file %s (%s).\n", cryptname, error_name(errno));
  552.     else
  553.       error2("Could not create file %s (%s).", cryptname, error_name(errno));
  554.     return 0;
  555.   }
  556.  
  557.   while (lines--) 
  558.   {
  559.     if (fgets(buffer, VERY_LONG_STRING, mailfile) == NULL)
  560.       return 0;
  561.  
  562.     no_ret(buffer);
  563.  
  564.     if (pgp)
  565.       oldlines++;
  566.  
  567.     if (strcmp(buffer, "-----BEGIN PGP MESSAGE-----") == 0)
  568.       pgp = 1;
  569.     else if (strcmp(buffer, "-----BEGIN PGP SIGNED MESSAGE-----") == 0)
  570.       pgp = sign = 1;
  571.  
  572.     if (pgp)
  573.       fprintf(tempfile, "%s\n", buffer);
  574.  
  575.     if (strcmp(buffer, "-----END PGP MESSAGE-----") == 0)
  576.       pgp = 0;
  577.     else if (strcmp(buffer, "-----END PGP SIGNATURE-----") == 0)
  578.       pgp = 0;
  579.   }
  580.  
  581.   fclose(tempfile);
  582.  
  583.   if (oldlines == 0)
  584.   {
  585.     unlink(cryptname);
  586.     return 0;
  587.   }
  588.  
  589.   if (sign)
  590.     strcpy(clearname, "nul");
  591.  
  592.   sprintf(buffer, "pgp -f <%s >%s", cryptname, clearname);
  593.   os2path(buffer);
  594.   puts("\r\n");
  595.  
  596.   if (system_call(buffer, SH, FALSE, FALSE)) 
  597.   {
  598.     if (batch_only)
  599.       printf("Error while decrypting. Try again.\n");
  600.     else
  601.       error2("Error while decrypting. Try again.");
  602.     return 0;
  603.   }
  604.  
  605.   if (sign)
  606.   {
  607.     puts("");
  608.     PutLine0(LINES,0,"Please Press any key to continue.");
  609.     (void) ReadCh();
  610.     return 0;
  611.   }
  612.  
  613.   if ((tempfile = fopen(clearname, "r")) == NULL) 
  614.   {
  615.     if(batch_only)
  616.       printf("Could not read file %s (%s).\n", clearname, error_name(errno));
  617.     else
  618.       error2("Could not read file %s (%s).", clearname, error_name(errno));
  619.     return 0;
  620.   }
  621.  
  622.   for (lines = 0; fgets(buffer, VERY_LONG_STRING, tempfile) != NULL; lines++);
  623.   fclose(tempfile);
  624.  
  625.   strcpy(filename, clearname);
  626.   return lines - oldlines;
  627. }
  628.