1 /* $OpenBSD: main.c,v 1.36 2024/11/05 16:14:39 sobrado Exp $ */
2 /* $NetBSD: main.c,v 1.7 1997/05/13 06:15:57 mikel Exp $ */
3
4 /*
5 * Copyright (c) 1980, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #include "rcv.h"
34 #include <fcntl.h>
35 #include <sys/ioctl.h>
36 #include "extern.h"
37
38 int msgCount; /* Count of messages read in */
39 int rcvmode; /* True if receiving mail */
40 int sawcom; /* Set after first command */
41 int senderr; /* An error while checking */
42 int edit; /* Indicates editing a file */
43 int readonly; /* Will be unable to rewrite file */
44 int noreset; /* String resets suspended */
45 int sourcing; /* Currently reading variant file */
46 int loading; /* Loading user definitions */
47 int cond; /* Current state of conditional exc. */
48 FILE *itf; /* Input temp file buffer */
49 FILE *otf; /* Output temp file buffer */
50 int image; /* File descriptor for image of msg */
51 FILE *input; /* Current command input file */
52 char mailname[PATHSIZE]; /* Name of current file */
53 char prevfile[PATHSIZE]; /* Name of previous file */
54 char *homedir; /* Path name of home directory */
55 const char
56 *myname; /* My login name */
57 off_t mailsize; /* Size of system mailbox */
58 int lexnumber; /* Number of TNUMBER from scan() */
59 char lexstring[STRINGLEN]; /* String from TSTRING, scan() */
60 int regretp; /* Pointer to TOS of regret tokens */
61 int regretstack[REGDEP]; /* Stack of regretted tokens */
62 char *string_stack[REGDEP]; /* Stack of regretted strings */
63 int numberstack[REGDEP]; /* Stack of regretted numbers */
64 struct message *dot; /* Pointer to current message */
65 struct message *message; /* The actual message structure */
66 struct var *variables[HSHSIZE]; /* Pointer to active var list */
67 struct grouphead *groups[HSHSIZE];/* Pointer to active groups */
68 struct ignoretab ignore[2]; /* ignored and retained fields
69 0 is ignore, 1 is retain */
70 struct ignoretab saveignore[2]; /* ignored and retained fields
71 on save to folder */
72 struct ignoretab ignoreall[2]; /* special, ignore all headers */
73 char **altnames; /* List of alternate names for user */
74 int debug; /* Debug flag set */
75 int screenwidth; /* Screen width, or best guess */
76 int screenheight; /* Screen height, or best guess,
77 for "header" command */
78 int realscreenheight; /* the real screen height */
79 int uflag; /* Are we in -u mode? */
80 sigset_t intset; /* Signal set that is just SIGINT */
81
82 /*
83 * The pointers for the string allocation routines,
84 * there are NSPACE independent areas.
85 * The first holds STRINGSIZE bytes, the next
86 * twice as much, and so on.
87 */
88 struct strings stringdope[NSPACE];
89
90 __dead void usage(void);
91 int main(int, char **);
92
93 /*
94 * Mail -- a mail program
95 *
96 * Startup -- interface with user.
97 */
98
99 int
main(int argc,char ** argv)100 main(int argc, char **argv)
101 {
102 int i;
103 struct name *to, *cc, *bcc, *smopts;
104 char *fromaddr;
105 char *subject;
106 char *ef;
107 char nosrc = 0;
108 char *rc;
109 extern const char version[];
110
111 if (pledge("stdio rpath wpath cpath getpw tmppath fattr tty flock proc exec",
112 NULL) == -1)
113 err(1, "pledge");
114
115 /*
116 * Set up a reasonable environment.
117 * Figure out whether we are being run interactively,
118 * start the SIGCHLD catcher, and so forth.
119 */
120 (void)signal(SIGCHLD, sigchild);
121 (void)signal(SIGPIPE, SIG_IGN);
122 if (isatty(0))
123 assign("interactive", "");
124 image = -1;
125 /*
126 * Now, determine how we are being used.
127 * We successively pick off - flags.
128 * If there is anything left, it is the base of the list
129 * of users to mail to. Argp will be set to point to the
130 * first of these users.
131 */
132 ef = NULL;
133 to = NULL;
134 cc = NULL;
135 bcc = NULL;
136 smopts = NULL;
137 fromaddr = NULL;
138 subject = NULL;
139 while ((i = getopt(argc, argv, "EINb:c:dfinr:s:u:v")) != -1) {
140 switch (i) {
141 case 'u':
142 /*
143 * Next argument is person to pretend to be.
144 */
145 if (strlen(optarg) >= LOGIN_NAME_MAX)
146 errx(1, "username `%s' too long", optarg);
147 unsetenv("MAIL");
148 myname = optarg;
149 uflag = 1;
150 break;
151 case 'i':
152 /*
153 * User wants to ignore interrupts.
154 * Set the variable "ignore"
155 */
156 assign("ignore", "");
157 break;
158 case 'd':
159 debug++;
160 break;
161 case 'r':
162 /*
163 * Set From: address
164 */
165 fromaddr = optarg;
166 break;
167 case 's':
168 /*
169 * Give a subject field for sending from
170 * non terminal
171 */
172 subject = optarg;
173 break;
174 case 'f':
175 /*
176 * User is specifying file to "edit" with Mail,
177 * as opposed to reading system mailbox.
178 * We read his mbox file unless another file
179 * is specified after the arguments.
180 */
181 ef = "&";
182 break;
183 case 'n':
184 /*
185 * User doesn't want to source /usr/lib/Mail.rc
186 */
187 nosrc = 1;
188 break;
189 case 'N':
190 /*
191 * Avoid initial header printing.
192 */
193 assign("noheader", "");
194 break;
195 case 'v':
196 /*
197 * Send mailer verbose flag
198 */
199 assign("verbose", "");
200 break;
201 case 'I':
202 /*
203 * We're interactive
204 */
205 assign("interactive", "");
206 break;
207 case 'c':
208 /*
209 * Get Carbon Copy Recipient list
210 */
211 cc = cat(cc, nalloc(optarg, GCC));
212 break;
213 case 'b':
214 /*
215 * Get Blind Carbon Copy Recipient list
216 */
217 bcc = cat(bcc, nalloc(optarg, GBCC));
218 break;
219 case 'E':
220 /*
221 * Don't send messages with an empty body.
222 */
223 assign("skipempty", "");
224 break;
225 default:
226 usage();
227 /*NOTREACHED*/
228 }
229 }
230 if (ef != NULL) {
231 /* Check for optional mailbox file name. */
232 if (optind < argc) {
233 ef = argv[optind++];
234 if (optind < argc)
235 errx(1, "Cannot give -f and people to send to");
236 }
237 } else {
238 for (i = optind; argv[i]; i++)
239 to = cat(to, nalloc(argv[i], GTO));
240 }
241 /*
242 * Check for inconsistent arguments.
243 */
244 if (to == NULL && (subject != NULL || cc != NULL || bcc != NULL ||
245 fromaddr != NULL))
246 errx(1, "You must specify direct recipients with -s, -c, -b, "
247 "or -r");
248 /*
249 * Block SIGINT except where we install an explicit handler for it.
250 */
251 sigemptyset(&intset);
252 sigaddset(&intset, SIGINT);
253 (void)sigprocmask(SIG_BLOCK, &intset, NULL);
254 /*
255 * Initialization.
256 */
257 tinit();
258 setscreensize();
259 input = stdin;
260 rcvmode = !to;
261 spreserve();
262 if (!nosrc)
263 load(_PATH_MASTER_RC);
264 /*
265 * Expand returns a savestr, but load only uses the file name
266 * for fopen, so it's safe to do this.
267 */
268 if ((rc = getenv("MAILRC")) == 0)
269 rc = "~/.mailrc";
270 load(expand(rc));
271 if (!rcvmode) {
272 mail(to, cc, bcc, smopts, fromaddr, subject);
273 /*
274 * why wait?
275 */
276 exit(senderr);
277 }
278 /*
279 * Ok, we are reading mail.
280 * Decide whether we are editing a mailbox or reading
281 * the system mailbox, and open up the right stuff.
282 */
283 if (ef == NULL)
284 ef = "%";
285 if (setfile(ef) < 0)
286 exit(1); /* error already reported */
287
288 if (value("quiet") == NULL)
289 (void)printf("Mail version %s. Type ? for help.\n",
290 version);
291 announce();
292 (void)fflush(stdout);
293 commands();
294 (void)ignoresig(SIGHUP, NULL, NULL);
295 (void)ignoresig(SIGINT, NULL, NULL);
296 (void)ignoresig(SIGQUIT, NULL, NULL);
297 quit();
298 exit(0);
299 }
300
301 /*
302 * Compute what the screen size for printing headers should be.
303 * We use the following algorithm for the height:
304 * If baud rate < 1200, use 9
305 * If baud rate = 1200, use 14
306 * If baud rate > 1200, use 24 or ws_row
307 * Width is either 80 or ws_col;
308 */
309 void
setscreensize(void)310 setscreensize(void)
311 {
312 struct termios tbuf;
313 struct winsize ws;
314 speed_t ospeed;
315
316 if (ioctl(1, TIOCGWINSZ, (char *) &ws) == -1)
317 ws.ws_col = ws.ws_row = 0;
318 if (tcgetattr(1, &tbuf) == -1)
319 ospeed = 9600;
320 else
321 ospeed = cfgetospeed(&tbuf);
322 if (ospeed < B1200)
323 screenheight = 9;
324 else if (ospeed == B1200)
325 screenheight = 14;
326 else if (ws.ws_row != 0)
327 screenheight = ws.ws_row;
328 else
329 screenheight = 24;
330 if ((realscreenheight = ws.ws_row) == 0)
331 realscreenheight = 24;
332 if ((screenwidth = ws.ws_col) == 0)
333 screenwidth = 80;
334 }
335
336 __dead void
usage(void)337 usage(void)
338 {
339 fprintf(stderr, "usage: %s [-dEIinv] [-b list] [-c list] "
340 "[-r from-addr] [-s subject]\n"
341 "%*s to-addr ...\n", __progname, (int)strlen(__progname) + 7, "");
342 fprintf(stderr, " %s [-dEIiNnv] -f [file]\n", __progname);
343 fprintf(stderr, " %s [-dEIiNnv] [-u user]\n", __progname);
344 exit(1);
345 }
346