1 
2 static char rcsid[] = "@(#)$Id: showmsg.c,v 1.8 1999/03/24 14:04:05 wfp5p Exp $";
3 
4 /*******************************************************************************
5  *  The Elm Mail System  -  $Revision: 1.8 $   $State: Exp $
6  *
7  *                      Copyright (c) 1988-1995 USENET Community Trust
8  * 			Copyright (c) 1986,1987 Dave Taylor
9  *******************************************************************************
10  * Bug reports, patches, comments, suggestions should be sent to:
11  *
12  *      Bill Pemberton, Elm Coordinator
13  *      flash@virginia.edu
14  *
15  *******************************************************************************
16  * $Log: showmsg.c,v $
17  * Revision 1.8  1999/03/24  14:04:05  wfp5p
18  * elm 2.5PL0
19  *
20  * Revision 1.7  1996/10/28  16:58:10  wfp5p
21  * Beta 1
22  *
23  * Revision 1.6  1996/08/08  19:49:30  wfp5p
24  * Alpha 11
25  *
26  * Revision 1.5  1996/05/09  15:51:27  wfp5p
27  * Alpha 10
28  *
29  * Revision 1.4  1996/03/14  17:29:51  wfp5p
30  * Alpha 9
31  *
32  * Revision 1.3  1995/09/29  17:42:27  wfp5p
33  * Alpha 8 (Chip's big changes)
34  *
35  * Revision 1.2  1995/06/12  20:33:37  wfp5p
36  * Alpha 2 clean up
37  *
38  * Revision 1.1.1.1  1995/04/19  20:38:38  wfp5p
39  * Initial import of elm 2.4 PL0 as base for elm 2.5.
40  *
41  ******************************************************************************/
42 
43 /** This file contains all the routines needed to display the specified
44     message.
45 **/
46 
47 #include "elm_defs.h"
48 #include "elm_globals.h"
49 #include "s_elm.h"
50 #include "port_wait.h"
51 
52 #ifndef I_UNISTD
53 void _exit();
54 #endif
55 
56 extern char *elm_date_str();
57 
58 int    pipe_abort  = FALSE;	/* did we receive a SIGNAL(SIGPIPE)? */
59 
60 FILE *pipe_wr_fp;		/* file pointer to write to external pager */
61 extern int lines_displayed,	/* defined in "builtin" */
62 	   lines_put_on_screen;	/*    ditto too!        */
63 
64 /*
65  * FOO - I believe the SIGWINCH handling is botched.  By ignoring it,
66  * it is causing Elm to lose track of screen changes.  I don't understand
67  * why this needs to be done, the SIGWINCH handler should be harmless.
68  * For the time being, I'm removing SIGWINCH, which will prevent the
69  * routine from dinking around with it.
70  */
71 #ifdef SIGWINCH
72 # undef SIGWINCH
73 #endif
74 
75 int
show_msg(number)76 show_msg(number)
77 int number;
78 {
79 	/*** Display number'th message.  Get starting and ending lines
80 	     of message from headers data structure, then fly through
81 	     the file, displaying only those lines that are between the
82 	     two!
83 
84 	     Return 0 to return to the index screen or a character entered
85 	     by the user to initiate a command without returning to
86 	     the index screen (to be processed via process_showmsg_cmd()).
87 	***/
88 
89 	char title1[SLEN], title2[SLEN], title3[SLEN], titlebuf[SLEN];
90 	char who[LONG_STRING], buffer[VERY_LONG_STRING];
91 	waitstatus_t wait_stat;
92 
93 	int crypted = 0;			/* encryption */
94 	static int start_encode_len = 0, end_encode_len = 0;
95 	int weed_header, weeding_out = 0;	/* weeding    */
96 	int using_to,				/* misc use   */
97 	    pipe_fd[2],				/* pipe file descriptors */
98 	    new_pipe_fd,			/* dup'ed pipe fil des */
99 	    lines,				/* num lines in msg */
100 	    fork_ret,				/* fork return value */
101 	    wait_ret,				/* wait return value */
102 	    form_letter = FALSE,		/* Form ltr?  */
103 	    form_letter_section = 0,		/* section    */
104 	    padding = 0,			/*   counter  */
105 	    builtin = FALSE,			/* our pager? */
106 	    val = 0,				/* return val */
107 	    buf_len,				/* line length */
108 	    err;				/* place holder for errno */
109 	struct header_rec *current_header = curr_folder.headers[number-1];
110 #ifdef	SIGTSTP
111 	SIGHAND_TYPE	(*oldstop)(), (*oldcont)();
112 #endif
113 #ifdef	SIGWINCH
114 	SIGHAND_TYPE	(*oldwinch)();
115 #endif
116 
117 	/** set up lengths of encode/decode strings so we don't have to
118 	 ** strlen() every time */
119 	if (start_encode_len == 0) {
120 	    start_encode_len = strlen(MSSG_START_ENCODE);
121 	    end_encode_len = strlen(MSSG_END_ENCODE);
122 	}
123 
124 	lines = current_header->lines;
125 
126 	dprint(4, (debugfile,"displaying %d lines from message %d using %s\n",
127 		lines, number, pager));
128 
129 	if (number > curr_folder.num_mssgs || number < 1)
130 	  return(val);
131 
132 	if(ison(current_header->status, NEW)) {
133 	  clearit(current_header->status, NEW);   /* it's been read now! */
134 	  current_header->status_chgd = TRUE;
135 	}
136 	if(ison(current_header->status, UNREAD)) {
137 	  clearit(current_header->status, UNREAD);   /* it's been read now! */
138 	  current_header->status_chgd = TRUE;
139 	}
140 
141 #ifdef MIME_RECV
142 	if ((current_header->status & MIME_MESSAGE) &&
143 	    ((current_header->status & MIME_NEEDDECOD) ||
144 	     (current_header->status & MIME_NOTPLAIN)) &&
145 	    !getenv("NOMETAMAIL") ) {
146 	    char fname[STRING], Cmd[SLEN], line[VERY_LONG_STRING];
147 	    int code, err;
148 	    long lines = current_header->lines;
149 	    FILE *fpout;
150 
151 	    if (fseek(curr_folder.fp, current_header->offset, 0) != -1) {
152 		sprintf(fname, "%semm.%d.%d", temp_dir, getpid(), getuid());
153 		if ((fpout = fopen(fname, "w")) != NULL) {
154 		    copy_message(fpout, curr_folder.curr_mssg, CM_DECODE);
155 		    (void) fclose (fpout);
156  	            sprintf(Cmd, "metamail -p -z -m Elm %s", fname);
157 		    Raw(OFF);
158 		    softkeys_off();
159 		    EnableFkeys(OFF);
160 		    ClearScreen();
161 		    code = system_call(Cmd, SY_ENAB_SIGINT);
162 		    Raw(ON | NO_TITE);	/* Raw on but don't switch screen */
163 		    (void) unlink (fname);
164 		    if (code == 0) {
165 		      PutLine0(LINES,0, catgets(elm_msg_cat, ElmSet, ElmPressAnyKeyIndex,
166 			     "Press any key to return to index."));
167 		      (void) GetKey(0);
168 		      NewLine();
169 		      Raw(OFF | NO_TITE); /* Raw off so raw on takes effect */
170 		      Raw(ON); /* Finally raw on and switch screen */
171  	              softkeys_on();
172 		      EnableFkeys(ON);
173 
174 		      return(0);
175 		    }
176 		}
177 	    }
178 	}
179 #endif
180 
181 	if (fseek(curr_folder.fp, current_header->offset, 0) == -1) {
182 	  err = errno;
183 	  dprint(1, (debugfile,
184 		  "Error: seek %d bytes into file, errno %s (show_message)\n",
185 		  current_header->offset, strerror(err)));
186 	  error2(catgets(elm_msg_cat, ElmSet, ElmSeekFailedFile,
187 		  "ELM [seek] couldn't read %d bytes into file (%s)."),
188 		  current_header->offset, strerror(err));
189 	  return(val);
190 	}
191 	if(current_header->encrypted)
192 	  get_encode_key(OFF);
193 
194 	builtin = (
195 	  (strbegConst(pager,"builtin")|| strbegConst(pager,"internal"))
196 	  || ( builtin_lines < 0
197 	      ? lines < LINES + builtin_lines : lines < builtin_lines)
198 	);
199 
200 	if (builtin) {
201 
202 	  start_builtin(lines);
203 
204 	} else {
205 
206 	  /* put terminal out of raw mode so external pager has normal env */
207 	  Raw(OFF);
208 
209 	  /* create pipe for external pager and fork */
210 
211 	  if(pipe(pipe_fd) == -1) {
212 	    err = errno;
213 	    dprint(1, (debugfile, "Error: pipe failed, errno %s (show_msg)\n",
214 	      strerror(err)));
215 	    error1(catgets(elm_msg_cat, ElmSet, ElmPreparePagerPipe,
216 	      "Could not prepare for external pager(pipe()-%s)."),
217 	      strerror(err));
218 	    Raw(ON);
219 	    return(val);
220 	  }
221 
222 	  if((fork_ret = fork()) == -1) {
223 
224 	    err = errno;
225 	    dprint(1, (debugfile, "Error: fork failed, errno %s (show_msg)\n",
226 	      strerror(err)));
227 	    error1(catgets(elm_msg_cat, ElmSet, ElmPreparePagerFork,
228 	      "Could not prepare for external pager(fork()-%s)."),
229 	      strerror(err));
230 	    Raw(ON);
231 	    return(val);
232 
233 	  } else if (fork_ret == 0) {
234 
235 	    /* child fork */
236 
237 	    /* close write-only pipe fd and fit read-only pipe fd to stdin */
238 
239 	    close(pipe_fd[1]);
240 	    close(fileno(stdin));
241 	    if((new_pipe_fd = dup(pipe_fd[0])) == -1) {
242 	      err = errno;
243 	      dprint(1, (debugfile, "Error: dup failed, errno %s (show_msg)\n",
244 		strerror(err)));
245 	      error1(catgets(elm_msg_cat, ElmSet, ElmPreparePagerDup,
246 	        "Could not prepare for external pager(dup()-%s)."),
247 		strerror(err));
248 	      _exit(err);
249 	    }
250 	    close(pipe_fd[0]);	/* original pipe fd no longer needed */
251 
252 	    /* use stdio on new pipe fd */
253 	    if(fdopen(new_pipe_fd, "r") == NULL) {
254 	      err = errno;
255 	      dprint(1,
256 		(debugfile, "Error: child fdopen failed, errno %s (show_msg)\n",
257 		strerror(err)));
258 	      error1(catgets(elm_msg_cat, ElmSet, ElmPreparePagerChildFdopen,
259 		"Could not prepare for external pager(child fdopen()-%s)."),
260 		strerror(err));
261 	      _exit(err);
262 	    }
263 
264 	    /* now execute pager and exit */
265 
266 	    /* system_call() will return user to user's normal permissions.
267 	     * This is what makes this pipe secure - user won't have elm's
268 	     * special SETGID permissions (if so configured) and will only
269 	     * be able to execute a pager that user normally has permission
270 	     * to execute */
271 
272 	    _exit(system_call(pager, SY_ENAB_SIGINT));
273 
274 	  } /* else this is the parent fork */
275 
276 	  /* close read-only pipe fd and do write-only with stdio */
277 	  close(pipe_fd[0]);
278 
279 	  if((pipe_wr_fp = fdopen(pipe_fd[1], "w")) == NULL) {
280 	    err = errno;
281 	    dprint(1,
282 	      (debugfile, "Error: parent fdopen failed, errno %s (show_msg)\n",
283 	      strerror(err)));
284 	    error1(catgets(elm_msg_cat, ElmSet, ElmPreparePagerParentFdopen,
285 	      "Could not prepare for external pager(parent fdopen()-%s)."),
286 	      strerror(err));
287 
288 	    /* Failure - must close pipe and wait for child */
289 	    close(pipe_fd[1]);
290 	    while ((wait_ret = wait(&wait_stat)) != fork_ret && wait_ret!= -1)
291 	      ;
292 
293 	    Raw(OFF);
294 	    return(val);	/* pager may have already touched the screen */
295 	  }
296 
297 	  /* and that's it! */
298 	  lines_displayed = 0;
299 #ifdef	SIGTSTP
300 	  oldstop = signal(SIGTSTP,SIG_DFL);
301 	  oldcont = signal(SIGCONT,SIG_DFL);
302 #endif
303 #ifdef	SIGWINCH
304 	  oldwinch = signal(SIGWINCH,SIG_DFL);
305 #endif
306 	  EnableFkeys(OFF);
307 	}
308 
309 	ClearScreen();
310 
311 	pipe_abort = FALSE;
312 
313 	if (form_letter = (current_header->status&FORM_LETTER)) {
314 	  if (filter)
315 	    form_letter_section = 1;	/* initialize to section 1 */
316 	}
317 
318 	if (title_messages && filter) {
319 
320 	  using_to =
321 	    tail_of(current_header->from, who, current_header->to);
322 
323 	  MCsprintf(title1, "%s %d/%d  ",
324 		    curr_folder.headers[curr_folder.curr_mssg-1]->status & DELETED
325 			  ? nls_deleted : form_letter ? nls_form : nls_message,
326 		    number, curr_folder.num_mssgs);
327 	  MCsprintf(title2, "%s %s", using_to? nls_to : nls_from, who);
328 	  elm_date_str(title3, current_header, FALSE);
329 	  strcat(title3, " ");
330 	  strcat(title3, current_header->time_zone);
331 
332 	  /* truncate or pad title2 portion on the right
333 	   * so that line fits exactly */
334 	  padding =
335 	    COLS - (strlen(title1) + (buf_len=strlen(title2)) + strlen(title3));
336 
337 	  sprintf(titlebuf, "%s%-*.*s%s\n", title1, buf_len+padding,
338 	      buf_len+padding, title2, title3);
339 
340 	  if (builtin)
341 	    display_line(titlebuf, strlen(titlebuf));
342 	  else
343 	    fprintf(pipe_wr_fp, "%s", titlebuf);
344 
345 	  /** if there's a subject, let's output it next,
346 	      centered if it fits on a single line.  **/
347 
348 	  if ((buf_len = strlen(current_header->subject)) > 0 &&
349 		matchInList(weedlist,weedcount,"Subject:",TRUE)) {
350 	    padding = (buf_len < COLS ? COLS - buf_len : 0);
351 	    sprintf(buffer, "%*s%s\n", padding/2, "", current_header->subject);
352 	  } else
353 	    strcpy(buffer, "\n");
354 
355 	  if (builtin)
356 	    display_line(buffer, strlen(buffer));
357 	  else
358 	    fprintf(pipe_wr_fp, "%s", buffer);
359 
360 	  /** was this message address to us?  if not, then to whom? **/
361 
362 	  if (! using_to && matchInList(weedlist,weedcount,"To:",TRUE) && filter &&
363 	      strcmp(current_header->to, user_name) != 0 &&
364 	      strlen(current_header->to) > 0) {
365 	    sprintf(buffer, catgets(elm_msg_cat, ElmSet, ElmMessageAddressedTo,
366 		  "%s(message addressed to %.60s)\n"),
367 	          strlen(current_header->subject) > 0 ? "\n" : "",
368 		  current_header->to);
369 	    if (builtin)
370 	      display_line(buffer, strlen(buffer));
371 	    else
372 	      fprintf(pipe_wr_fp, "%s", buffer);
373 	  }
374 
375 	  /** The test above is: if we didn't originally send the mail
376 	      (e.g. we're not reading "mail.sent") AND the user is currently
377 	      weeding out the "To:" line (otherwise they'll get it twice!)
378 	      AND the user is actually weeding out headers AND the message
379 	      wasn't addressed to the user AND the 'to' address is non-zero
380 	      (consider what happens when the message doesn't HAVE a "To:"
381 	      line...the value is NULL but it doesn't match the username
382 	      either.  We don't want to display something ugly like
383 	      "(message addressed to )" which will just clutter up the
384 	      screen!).
385 
386 	      And you thought programming was EASY!!!!
387 	  **/
388 
389 	  /** one more friendly thing - output a line indicating what sort
390 	      of status the message has (e.g. Urgent etc).  Mostly added
391 	      for X.400 support, this is nonetheless generally useful to
392 	      include...
393 	  **/
394 
395 	  buffer[0] = '\0';
396 
397 	  /* we want to flag Urgent, Confidential, Private and Expired tags */
398 
399 	  if (current_header->status & PRIVATE)
400 	    strcpy(buffer, catgets(elm_msg_cat, ElmSet, ElmTaggedPrivate,
401 		"\n(** This message is tagged Private"));
402 	  else if (current_header->status & CONFIDENTIAL)
403 	    strcpy(buffer, catgets(elm_msg_cat, ElmSet, ElmTaggedCompanyConfidential,
404 		"\n(** This message is tagged Company Confidential"));
405 
406 	  if (current_header->status & URGENT) {
407 	    if (buffer[0] == '\0')
408 	      strcpy(buffer, catgets(elm_msg_cat, ElmSet, ElmTaggedUrgent,
409 		"\n(** This message is tagged Urgent"));
410 	    else if (current_header->status & EXPIRED)
411 	      strcat(buffer, catgets(elm_msg_cat, ElmSet, ElmCommaUrgent, ", Urgent"));
412 	    else
413 	      strcat(buffer, catgets(elm_msg_cat, ElmSet, ElmAndUrgent, " and Urgent"));
414 	  }
415 
416 	  if (current_header->status & EXPIRED) {
417 	    if (buffer[0] == '\0')
418 	      strcpy(buffer, catgets(elm_msg_cat, ElmSet, ElmMessageHasExpired,
419 		"\n(** This message has Expired"));
420 	    else
421 	      strcat(buffer, catgets(elm_msg_cat, ElmSet, ElmAndHasExpired,
422 		", and has Expired"));
423 	  }
424 
425 	  if (buffer[0] != '\0') {
426 	    strcat(buffer, " **)\n");
427 	    if (builtin)
428 	      display_line(buffer, strlen(buffer));
429 	    else
430 	      fprintf(pipe_wr_fp, buffer);
431 	  }
432 
433 	  if (builtin)			/* this is for a one-line blank    */
434 	    display_line("\n",1);	/*   separator between the title   */
435 	  else				/*   stuff and the actual message  */
436 	    fprintf(pipe_wr_fp, "\n");	/*   we're trying to display       */
437 
438 	}
439 
440 	weed_header = filter;	/* allow us to change it after header */
441 
442 	while (lines > 0 && pipe_abort == FALSE) {
443 
444 	    if ((buf_len = mail_gets(buffer, VERY_LONG_STRING, curr_folder.fp)) == 0) {
445 
446 	      dprint(1, (debugfile,
447 		"Premature end of file! Lines left = %d msg = %s (show_msg)\n",
448 		lines, number));
449 
450 	      error(catgets(elm_msg_cat, ElmSet, ElmPrematureEndOfFile,
451 		"Premature end of file!"));
452 	      if (sleepmsg > 0)
453 		    sleep(sleepmsg);
454 	      break;
455 	    }
456 	    if (buf_len > 0)  {
457 	      if(buffer[buf_len - 1] == '\n') {
458 	        lines--;
459 	        lines_displayed++;
460 		}
461 	      while(buf_len > 0 && (buffer[buf_len - 1] == '\n'
462 				    ||buffer[buf_len - 1] == '\r'))
463 		--buf_len;
464 	    }
465 
466   	    if (buf_len == 0) {
467 	      weed_header = 0;		/* past header! */
468 	      weeding_out = 0;
469 	    }
470 
471 	    if (form_letter && weed_header)
472 		/* skip it.  NEVER display random headers in forms! */;
473 	    else if (weed_header && matchInList(weedlist,weedcount,buffer,TRUE))
474 	      weeding_out = 1;	 /* aha!  We don't want to see this! */
475 	    else if (buffer[0] == '[') {
476 	      if (strncmp(buffer, MSSG_START_ENCODE, start_encode_len)==0)
477 	        crypted = ON;
478 	      else if (strncmp(buffer, MSSG_END_ENCODE, end_encode_len)==0)
479 	        crypted = OFF;
480 	      else if (crypted)
481                 encode(buffer);
482 	      val = show_line(buffer, buf_len, builtin);
483 	    }
484 	    else if (crypted) {
485 	      encode(buffer);
486 	      val = show_line(buffer, buf_len, builtin);
487 	    }
488 	    else if (weeding_out) {
489 	      weeding_out = (whitespace(buffer[0]));	/* 'n' line weed */
490 	      if (! weeding_out) 	/* just turned on! */
491 	        val = show_line(buffer, buf_len, builtin);
492 	    }
493 	    else if (form_letter && strbegConst(buffer,"***") && filter) {
494 	      strcpy(buffer,
495 "\n------------------------------------------------------------------------------\n");
496 	      val = show_line(buffer, buf_len, builtin);	/* hide '***' */
497 	      form_letter_section++;
498 	    }
499 	    else if (form_letter_section == 1 || form_letter_section == 3)
500 	      /** skip this stuff - we can't deal with it... **/;
501 	    else
502 	      val = show_line(buffer, buf_len, builtin);
503 
504 	    if (val != 0)	/* discontinue the display */
505 	      break;
506 	}
507 
508 	if (!builtin) {
509 /*	  EnableFkeys(ON);*/
510 	  fclose(pipe_wr_fp);
511 	  while ((wait_ret = wait(&wait_stat)) != fork_ret
512 		  && wait_ret!= -1)
513 	    ;
514 	  /* turn raw on **after** child terminates in case child
515 	   * doesn't put us back to cooked mode after we return ourselves
516 	   * to raw.
517 	   */
518 	  Raw(ON);
519           EnableFkeys(ON);
520 #ifdef	SIGTSTP
521 	  (void)signal(SIGTSTP,oldstop);
522 	  (void)signal(SIGCONT,oldcont);
523 #endif
524 #ifdef	SIGWINCH
525 	  (void)signal(SIGWINCH,oldwinch);
526 #endif
527 	}
528 
529 	/* If we are to prompt for a user input command and we don't
530 	 * already have one */
531 	if ((prompt_after_pager || builtin) && val == 0) {
532 	  MoveCursor(LINES,0);
533 	  if (Term.status & TERM_CAN_SO)
534 	      StartStandout();
535 	  PutLine0(-1, -1, catgets(elm_msg_cat, ElmSet, ElmCommandIToReturn,
536 		" Command ('i' to return to index): "));
537 	  if (Term.status & TERM_CAN_SO)
538 	      EndStandout();
539 	  val = GetKey(0);
540 	}
541 
542 	/* 'q' means quit current operation and pop back up to previous level -
543 	 * in this case it therefore means return to index screen.
544 	 */
545 	return(val == 'i' || val == 'q' ? 0 : val);
546 }
547 
548 int
show_line(buffer,buf_len,builtin)549 show_line(buffer, buf_len, builtin)
550 char *buffer;
551 int  buf_len;
552 int  builtin;
553 {
554 	/** Hands the given line to the output pipe.  'builtin' is true if
555 	    we're using the builtin pager.
556 	    Return the character entered by the user to indicate
557 	    a command other than continuing with the display (only possible
558 	    with the builtin pager), otherwise 0. **/
559 
560 	strcpy(buffer + buf_len, "\n");
561 	++buf_len;
562 #ifdef MMDF
563 	if (strncmp(buffer, MSG_SEPARATOR, buf_len) == 0)
564 	  return(0);	/* no reason to show the ending terminator */
565 #endif /* MMDF */
566 	if (builtin) {
567 	  return(display_line(buffer, buf_len));
568 	}
569 	errno = 0;
570 	fprintf(pipe_wr_fp, "%s", buffer);
571 	if (errno != 0)
572 	  dprint(1, (debugfile, "\terror %s hit!\n", strerror(errno)));
573 	return(0);
574 }
575 
576