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