xref: /illumos-gate/usr/src/cmd/mail/printmail.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include "mail.h"
34 /*
35  *	Print mail entries
36  */
37 void
38 printmail()
39 {
40 	static char pn[] = "printmail";
41 	int	flg, curlet, showlet, k, print, aret, stret, rc;
42 	int	nsmbox = 0;	/* 1 ==> mailbox is in non-standard place */
43 	int	sav_j = -1;
44 	char	*p, *getarg();
45 	struct	stat stbuf;
46 	struct	stat *stbufp;
47 	int ttyf = isatty(1) ? TTY : ORDINARY;
48 	char	readbuf[LSIZE];	/* holds user's response in interactive mode */
49 	char	*resp;
50 	gid_t	savedegid;
51 
52 	stbufp = &stbuf;
53 
54 	/*
55 	 *	create working directory mbox name
56 	 */
57 	if ((hmbox = malloc(strlen(home) + strlen(mbox) + 1)) == NULL) {
58 		errmsg(E_MBOX, "");
59 		return;
60 	}
61 	cat(hmbox, home, mbox);
62 
63 	/*
64 	 *	If we are not using an alternate mailfile, then get
65 	 *	the $MAIL value and build the filename for the mailfile.
66 	 *	If $MAIL is set, but is NOT the 'standard' place, then
67 	 *	use it but set flgf to circumvent :saved processing.
68 	 */
69 	if (!flgf) {
70 		if ((p = malloc(strlen(maildir) + strlen(my_name) + 1))
71 								== NULL) {
72 			errmsg(E_MEM, "");
73 			return;
74 		}
75 		cat(p, maildir, my_name);
76 		if (((mailfile = getenv("MAIL")) == NULL) ||
77 		    (strlen(mailfile) == 0)) {
78 			/* $MAIL not set, use standard path to mailfile */
79 			mailfile = p;
80 		} else {
81 			if (strcmp(mailfile, p) != 0) {
82 			    flgf = 1;
83 			    nsmbox = 1;
84 			    Dout(pn, 0, "$MAIL ('%s') != standard path\n",
85 				mailfile);
86 			    Dout("", 0, "\tSetting flgf to 1.\n");
87 			}
88 			free(p);
89 		}
90 	}
91 
92 	/*
93 	 *	Get ACCESS and MODIFICATION times of mailfile BEFORE we
94 	 *	use it. This allows us to put them back when we are
95 	 *	done. If we didn't, the shell would think NEW mail had
96 	 *	arrived since the file times would have changed.
97 	 */
98 	stret = CERROR;
99 	if (access(mailfile, A_EXIST) == A_OK) {
100 		if ((stret = stat(mailfile, stbufp)) != A_OK) {
101 			errmsg(E_FILE, "Cannot stat mailfile");
102 			return;
103 		}
104 		mf_gid = stbufp->st_gid;
105 		mf_uid = stbufp->st_uid;
106 		utimep->actime = stbufp->st_atime;
107 		utimep->modtime = stbufp->st_mtime;
108 		file_size = stbufp->st_size;
109 	}
110 
111 	/* Open the file as the real gid */
112 	savedegid = getegid();
113 	(void) setegid(getgid());
114 	malf = fopen(mailfile, "r");
115 	(void) setegid(savedegid);
116 	/*
117 	 *	stat succeeded, but we cannot access the mailfile
118 	 */
119 	if (stret == CSUCCESS && malf == NULL) {
120 		char buf[MAXFILENAME+50];
121 		(void) snprintf(buf, sizeof (buf),
122 		    "Invalid permissions on %s", mailfile);
123 		errmsg(E_PERM, buf);
124 		return;
125 	} else
126 	/*
127 	 *	using an alternate mailfile, but we failed on access
128 	 */
129 	if (!nsmbox && flgf && (malf == NULL)) {
130 		errmsg(E_FILE, "Cannot open mailfile");
131 		return;
132 	}
133 	/*
134 	 *	we failed to access OR the file is empty
135 	 */
136 	else if ((malf == NULL) || (stbuf.st_size == 0)) {
137 		if (!flge && !flgE) {
138 			printf("No mail.\n");
139 		}
140 		error = E_FLGE;
141 		Dout(pn, 0, "error set to %d\n", error);
142 		return;
143 	}
144 	if (flge)
145 		return;
146 
147 	if (flgE) {
148 		if (utimep->modtime < utimep->actime) {
149 			error = E_FLGE_OM;
150 			Dout(pn, 0, "error set to %d\n", error);
151 		}
152 		return;
153 	}
154 	/*
155 	 *	Secure the mailfile to guarantee integrity
156 	 */
157 	lock(my_name);
158 
159 	/*
160 	 *	copy mail to temp file and mark each letter in the
161 	 *	let array --- mailfile is still locked !!!
162 	 */
163 	mktmp();
164 	copymt(malf, tmpf);
165 	onlet = nlet;
166 	fclose(malf);
167 	fclose(tmpf);
168 	unlock();	/* All done, OK to unlock now */
169 	tmpf = doopen(lettmp, "r+", E_TMP);
170 	changed = 0;
171 	print = 1;
172 	curlet = 0;
173 	while (curlet < nlet) {
174 		/*
175 		 *	reverse order ?
176 		 */
177 		showlet = flgr ? curlet : nlet - curlet - 1;
178 
179 		if (setjmp(sjbuf) == 0 && print != 0) {
180 				/* -h says to print the headers first */
181 				if (flgh) {
182 					gethead(showlet, 0);
183 					flgh = 0;	/* Only once */
184 					/* set letter # to invalid # */
185 					curlet--;
186 					showlet =
187 					    flgr ? curlet : nlet - curlet - 1;
188 				} else {
189 					if (showlet != sav_j) {
190 						/* Looking at new message. */
191 						/* Reset flag to override */
192 						/* non-display of binary */
193 						/* contents */
194 						sav_j = showlet;
195 						pflg = 0;
196 						Pflg = flgP;
197 					}
198 					copylet(showlet, stdout, ttyf);
199 				}
200 		}
201 
202 		/*
203 		 *	print only
204 		 */
205 		if (flgp) {
206 			curlet++;
207 			continue;
208 		}
209 		/*
210 		 *	Interactive
211 		 */
212 		interactive = 1;
213 		setjmp(sjbuf);
214 		stat(mailfile, stbufp);
215 		if (stbufp->st_size != file_size) {
216 			/*
217 			 *	New mail has arrived, load it
218 			 */
219 			k = nlet;
220 			lock(my_name);
221 			malf = doopen(mailfile, "r", E_FILE);
222 			fclose(tmpf);
223 			tmpf = doopen(lettmp, "a", E_TMP);
224 			fseek(malf, let[nlet].adr, 0);
225 			copymt(malf, tmpf);
226 			file_size = stbufp->st_size;
227 			fclose(malf);
228 			fclose(tmpf);
229 			unlock();
230 			tmpf = doopen(lettmp, "r+", E_TMP);
231 			if (++k < nlet)
232 				printf("New mail loaded into letters %d - %d\n",
233 				    k, nlet);
234 			else
235 				printf("New mail loaded into letter %d\n",
236 				    nlet);
237 		}
238 
239 		/* read the command */
240 		printf("? ");
241 		fflush(stdout);
242 		fflush(stderr);
243 		if (fgets(readbuf, sizeof (readbuf), stdin) == NULL) break;
244 		resp = readbuf;
245 		while (*resp == ' ' || *resp == '\t') resp++;
246 		print = 1;
247 		Dout(pn, 0, "resp = '%s'\n", resp);
248 		if ((rc = atoi(resp)) != 0) {
249 			if (!validmsg(rc)) print = 0;
250 			else curlet = flgr ? rc - 1 : nlet - rc;
251 		} else switch (resp[0]) {
252 			default:
253 				printf("Usage:\n");
254 			/*
255 			 *	help
256 			 */
257 			case '?':
258 				print = 0;
259 				for (rc = 0; help[rc]; rc++)
260 					printf("%s", help[rc]);
261 				break;
262 			/*
263 			 *	print message number of current message
264 			 */
265 			case '#':
266 				print = 0;
267 				if ((showlet == nlet) || (showlet < 0)) {
268 					printf("No message selected yet.\n");
269 				} else {
270 					printf("Current message number is %d\n",
271 					    showlet+1);
272 				}
273 				break;
274 			/*
275 			 *	headers
276 			 */
277 			case 'h':
278 				print = 0;
279 				if (resp[2] != 'd' &&
280 				    resp[2] != 'a' &&
281 				    (rc = getnumbr(resp+1)) > 0) {
282 					showlet = rc - 1;
283 					curlet = flgr ? rc - 1 : nlet - rc- 1;
284 				}
285 				if (rc == -1 && resp[2] != 'a' &&
286 				    resp[2] != 'd')
287 					break;
288 				if (resp[2] == 'a') rc = 1;
289 				else if (resp[2] == 'd') rc = 2;
290 					else rc = 0;
291 
292 /*
293  *				if (!validmsg(showlet)) break;
294  */
295 				gethead(showlet, rc);
296 				break;
297 			/*
298 			 *	skip entry
299 			 */
300 			case '+':
301 			case 'n':
302 			case '\n':
303 				curlet++;
304 				break;
305 			case 'P':
306 				Pflg++;
307 				break;
308 			case 'p':
309 				pflg++;
310 				break;
311 			case 'x':
312 				changed = 0;
313 			case 'q':
314 				goto donep;
315 			/*
316 			 *	Previous entry
317 			 */
318 			case '^':
319 			case '-':
320 				if (--curlet < 0) curlet = 0;
321 				break;
322 			/*
323 			 *	Save in file without header
324 			 */
325 			case 'y':
326 			case 'w':
327 			/*
328 			 *	Save mail with header
329 			 */
330 			case 's':
331 				print = 0;
332 				if (!validmsg(curlet)) break;
333 				if (resp[1] == '\n' || resp[1] == '\0') {
334 					cat(resp+1, hmbox, "");
335 				} else if (resp[1] != ' ') {
336 					printf("Invalid command\n");
337 					break;
338 				}
339 				umask(umsave);
340 				flg = 0;
341 				if (getarg(lfil, resp + 1) == NULL) {
342 					cat(resp + 1, hmbox, "");
343 				}
344 				malf = (FILE *)NULL;
345 				p = resp + 1;
346 				while ((p = getarg(lfil, p)) != NULL) {
347 					if (flg) {
348 					    fprintf(stderr,
349 						"%s: File '%s' skipped\n",
350 						program, lfil);
351 					    continue;
352 					}
353 					malf = NULL;
354 					if ((aret = legal(lfil))) {
355 						malf = fopen(lfil, "a");
356 					}
357 					if ((malf == NULL) || (aret == 0)) {
358 					    fprintf(stderr,
359 						"%s: Cannot append to %s\n",
360 						program, lfil);
361 					    flg++;
362 					} else if (aret == 2) {
363 						chown(lfil, my_euid, my_gid);
364 					}
365 					if (!flg &&
366 					    copylet(showlet, malf, resp[0] ==
367 					    's'? ORDINARY: ZAP) == FALSE) {
368 						fprintf(stderr,
369 					    "%s: Cannot save mail to '%s'\n",
370 						    program, lfil);
371 						flg++;
372 					} else
373 						Dout(pn, 0, "!saved\n");
374 					if (malf != (FILE *)NULL) {
375 						fclose(malf);
376 					}
377 				}
378 				umask(7);
379 				if (!flg) {
380 					setletr(showlet, resp[0]);
381 					print = 1;
382 					curlet++;
383 				}
384 				break;
385 			/*
386 			 *	Reply to a letter
387 			 */
388 			case 'r':
389 				print = 0;
390 				if (!validmsg(curlet)) break;
391 				replying = 1;
392 				for (k = 1; resp[k] == ' ' || resp[k] == '\t';
393 				    ++k);
394 				resp[strlen(resp)-1] = '\0';
395 				(void) strlcpy(m_sendto, resp+k,
396 				    sizeof (m_sendto));
397 				goback(showlet);
398 				replying = 0;
399 				setletr(showlet, resp[0]);
400 				break;
401 			/*
402 			 *	Undelete
403 			 */
404 			case 'u':
405 				print = 0;
406 				if ((k = getnumbr(resp+1)) <= 0) k = showlet;
407 				else k--;
408 				if (!validmsg(k)) break;
409 				setletr(k, ' ');
410 				break;
411 			/*
412 			 *	Mail letter to someone else
413 			 */
414 			case 'm':
415 				{
416 				reciplist list;
417 				print = 0;
418 				if (!validmsg(curlet)) break;
419 				new_reciplist(&list);
420 				flg = 0;
421 				k = 0;
422 				if (substr(resp, " -") != -1 ||
423 					substr(resp, "\t-") != -1) {
424 					printf("Only users may be specified\n");
425 					break;
426 				}
427 				p = resp + 1;
428 				while ((p = getarg(lfil, p)) != NULL) {
429 					char *env;
430 					if (lfil[0] == '$') {
431 						if (!(env = getenv(&lfil[1]))) {
432 							fprintf(stderr,
433 				"%s: %s has no value or is not exported.\n",
434 							    program, lfil);
435 							flg++;
436 						} else
437 							add_recip(&list, env,
438 							    FALSE);
439 						k++;
440 					} else if (lfil[0] != '\0') {
441 						add_recip(&list, lfil, FALSE);
442 						k++;
443 					}
444 				}
445 				(void) strlcpy(Rpath, my_name, sizeof (Rpath));
446 				sending = TRUE;
447 				flg += sendlist(&list, showlet, 0);
448 				sending = FALSE;
449 				if (k) {
450 					if (!flg) {
451 						setletr(showlet, 'm');
452 						print = 1;
453 						curlet++;
454 					}
455 				} else
456 					printf("Invalid command\n");
457 				del_reciplist(&list);
458 				break;
459 				}
460 			/*
461 			 *	Read new letters
462 			 */
463 			case 'a':
464 				if (onlet == nlet) {
465 					printf("No new mail\n");
466 					print = 0;
467 					break;
468 				}
469 				curlet = 0;
470 				print = 1;
471 				break;
472 			/*
473 			 *	Escape to shell
474 			 */
475 			case '!':
476 				systm(resp + 1);
477 				printf("!\n");
478 				print = 0;
479 				break;
480 			/*
481 			 *	Delete an entry
482 			 */
483 			case 'd':
484 				print = 0;
485 				k = 0;
486 				if (strncmp("dq", resp, 2) != SAME &&
487 					strncmp("dp", resp, 2) != SAME)
488 					if ((k = getnumbr(resp+1)) == -1) break;
489 				if (k == 0) {
490 					k = showlet;
491 					if (!validmsg(curlet)) break;
492 					print = 1;
493 					curlet++;
494 				} else	k--;
495 
496 				setletr(k, 'd');
497 				if (resp[1] == 'p') print = 1;
498 				else if (resp[1] == 'q') goto donep;
499 				break;
500 		}
501 	}
502 	/*
503 	 *	Copy updated mailfile back
504 	 */
505 donep:
506 	if (changed) {
507 		copyback();
508 		stamp();
509 	}
510 }
511