| @@ -1,654 +1,669 @@ | | | @@ -1,654 +1,669 @@ |
1 | /* $NetBSD: vacation.c,v 1.37 2019/05/05 23:08:37 pgoyette Exp $ */ | | 1 | /* $NetBSD: vacation.c,v 1.38 2023/04/11 10:34:52 hauke Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 1983, 1987, 1993 | | 4 | * Copyright (c) 1983, 1987, 1993 |
5 | * The Regents of the University of California. All rights reserved. | | 5 | * The Regents of the University of California. All rights reserved. |
6 | * | | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
9 | * are met: | | 9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright | | 10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. | | 11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright | | 12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the | | 13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. | | 14 | * documentation and/or other materials provided with the distribution. |
15 | * 3. Neither the name of the University nor the names of its contributors | | 15 | * 3. Neither the name of the University nor the names of its contributors |
16 | * may be used to endorse or promote products derived from this software | | 16 | * may be used to endorse or promote products derived from this software |
17 | * without specific prior written permission. | | 17 | * without specific prior written permission. |
18 | * | | 18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | | 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | | 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
29 | * SUCH DAMAGE. | | 29 | * SUCH DAMAGE. |
30 | */ | | 30 | */ |
31 | | | 31 | |
32 | #include <sys/cdefs.h> | | 32 | #include <sys/cdefs.h> |
33 | | | 33 | |
34 | #ifndef lint | | 34 | #ifndef lint |
35 | __COPYRIGHT("@(#) Copyright (c) 1983, 1987, 1993\ | | 35 | __COPYRIGHT("@(#) Copyright (c) 1983, 1987, 1993\ |
36 | The Regents of the University of California. All rights reserved."); | | 36 | The Regents of the University of California. All rights reserved."); |
37 | #endif /* not lint */ | | 37 | #endif /* not lint */ |
38 | | | 38 | |
39 | #ifndef lint | | 39 | #ifndef lint |
40 | #if 0 | | 40 | #if 0 |
41 | static char sccsid[] = "@(#)vacation.c 8.2 (Berkeley) 1/26/94"; | | 41 | static char sccsid[] = "@(#)vacation.c 8.2 (Berkeley) 1/26/94"; |
42 | #endif | | 42 | #endif |
43 | __RCSID("$NetBSD: vacation.c,v 1.37 2019/05/05 23:08:37 pgoyette Exp $"); | | 43 | __RCSID("$NetBSD: vacation.c,v 1.38 2023/04/11 10:34:52 hauke Exp $"); |
44 | #endif /* not lint */ | | 44 | #endif /* not lint */ |
45 | | | 45 | |
46 | /* | | 46 | /* |
47 | ** Vacation | | 47 | ** Vacation |
48 | ** Copyright (c) 1983 Eric P. Allman | | 48 | ** Copyright (c) 1983 Eric P. Allman |
49 | ** Berkeley, California | | 49 | ** Berkeley, California |
50 | */ | | 50 | */ |
51 | | | 51 | |
52 | #include <sys/param.h> | | 52 | #include <sys/param.h> |
53 | #include <sys/stat.h> | | 53 | #include <sys/stat.h> |
54 | | | 54 | |
55 | #include <ctype.h> | | 55 | #include <ctype.h> |
56 | #include <db.h> | | 56 | #include <db.h> |
57 | #include <err.h> | | 57 | #include <err.h> |
58 | #include <errno.h> | | 58 | #include <errno.h> |
59 | #include <fcntl.h> | | 59 | #include <fcntl.h> |
60 | #include <paths.h> | | 60 | #include <paths.h> |
61 | #include <pwd.h> | | 61 | #include <pwd.h> |
62 | #include <stdio.h> | | 62 | #include <stdio.h> |
63 | #include <stdlib.h> | | 63 | #include <stdlib.h> |
64 | #include <string.h> | | 64 | #include <string.h> |
65 | #include <syslog.h> | | 65 | #include <syslog.h> |
66 | #include <time.h> | | 66 | #include <time.h> |
67 | #include <tzfile.h> | | 67 | #include <tzfile.h> |
68 | #include <unistd.h> | | 68 | #include <unistd.h> |
69 | | | 69 | |
70 | /* | | 70 | /* |
71 | * VACATION -- return a message to the sender when on vacation. | | 71 | * VACATION -- return a message to the sender when on vacation. |
72 | * | | 72 | * |
73 | * This program is invoked as a message receiver. It returns a | | 73 | * This program is invoked as a message receiver. It returns a |
74 | * message specified by the user to whomever sent the mail, taking | | 74 | * message specified by the user to whomever sent the mail, taking |
75 | * care not to return a message too often to prevent "I am on | | 75 | * care not to return a message too often to prevent "I am on |
76 | * vacation" loops. | | 76 | * vacation" loops. |
77 | */ | | 77 | */ |
78 | | | 78 | |
79 | #define MAXLINE 1024 /* max line from mail header */ | | 79 | #define MAXLINE 1024 /* max line from mail header */ |
80 | | | 80 | |
81 | static const char *dbprefix = ".vacation"; /* dbm's database sans .db */ | | 81 | static const char *dbprefix = ".vacation"; /* dbm's database sans .db */ |
82 | static const char *msgfile = ".vacation.msg"; /* vacation message */ | | 82 | static const char *msgfile = ".vacation.msg"; /* vacation message */ |
83 | | | 83 | |
84 | typedef struct alias { | | 84 | typedef struct alias { |
85 | struct alias *next; | | 85 | struct alias *next; |
86 | const char *name; | | 86 | const char *name; |
87 | } alias_t; | | 87 | } alias_t; |
88 | static alias_t *names; | | 88 | static alias_t *names; |
89 | | | 89 | |
90 | static DB *db; | | 90 | static DB *db; |
91 | static char from[MAXLINE]; | | 91 | static char from[MAXLINE]; |
92 | static char subject[MAXLINE]; | | 92 | static char subject[MAXLINE]; |
93 | | | 93 | |
94 | static int iflag = 0; /* Initialize the database */ | | 94 | static int iflag = 0; /* Initialize the database */ |
95 | | | 95 | |
96 | static int tflag = 0; | | 96 | static int tflag = 0; |
97 | #define APPARENTLY_TO 1 | | 97 | #define APPARENTLY_TO 1 |
98 | #define DELIVERED_TO 2 | | 98 | #define DELIVERED_TO 2 |
99 | | | 99 | |
100 | static int fflag = 0; | | 100 | static int fflag = 0; |
101 | #define FROM_FROM 1 | | 101 | #define FROM_FROM 1 |
102 | #define RETURN_PATH_FROM 2 | | 102 | #define RETURN_PATH_FROM 2 |
103 | #define SENDER_FROM 4 | | 103 | #define SENDER_FROM 4 |
104 | | | 104 | |
105 | static int toanybody = 0; /* Don't check if we appear in the to or cc */ | | 105 | static int toanybody = 0; /* Don't check if we appear in the to or cc */ |
106 | | | 106 | |
107 | static int debug = 0; | | 107 | static int debug = 0; |
108 | | | 108 | |
109 | static void opendb(void); | | 109 | static void opendb(void); |
110 | static int junkmail(const char *); | | 110 | static int junkmail(const char *); |
111 | static int nsearch(const char *, const char *); | | 111 | static int nsearch(const char *, const char *); |
112 | static int readheaders(void); | | 112 | static int readheaders(void); |
113 | static int recent(void); | | 113 | static int recent(void); |
114 | static void getfrom(char *); | | 114 | static void getfrom(char *); |
115 | static void sendmessage(const char *); | | 115 | static void sendmessage(const char *); |
116 | static void setinterval(time_t); | | 116 | static void setinterval(time_t); |
117 | static void setreply(void); | | 117 | static void setreply(void); |
118 | static void usage(void) __dead; | | 118 | static void usage(void) __dead; |
119 | | | 119 | |
120 | int | | 120 | int |
121 | main(int argc, char **argv) | | 121 | main(int argc, char **argv) |
122 | { | | 122 | { |
123 | struct passwd *pw; | | 123 | struct passwd *pw; |
124 | alias_t *cur; | | 124 | alias_t *cur; |
125 | long interval; | | 125 | long interval; |
126 | int ch, rv; | | 126 | int ch, rv; |
127 | char *p; | | 127 | char *p; |
128 | | | 128 | |
129 | setprogname(argv[0]); | | 129 | setprogname(argv[0]); |
130 | opterr = 0; | | 130 | opterr = 0; |
131 | interval = -1; | | 131 | interval = -1; |
132 | openlog(getprogname(), 0, LOG_USER); | | 132 | openlog(getprogname(), 0, LOG_USER); |
133 | while ((ch = getopt(argc, argv, "a:df:F:Iijm:r:s:t:T:")) != -1) | | 133 | while ((ch = getopt(argc, argv, "a:df:F:Iijm:r:s:t:T:")) != -1) |
134 | switch((char)ch) { | | 134 | switch((char)ch) { |
135 | case 'a': /* alias */ | | 135 | case 'a': /* alias */ |
136 | if (!(cur = (alias_t *)malloc((size_t)sizeof(alias_t)))) | | 136 | if (!(cur = (alias_t *)malloc((size_t)sizeof(alias_t)))) |
137 | break; | | 137 | break; |
138 | cur->name = optarg; | | 138 | cur->name = optarg; |
139 | cur->next = names; | | 139 | cur->next = names; |
140 | names = cur; | | 140 | names = cur; |
141 | break; | | 141 | break; |
142 | case 'd': | | 142 | case 'd': |
143 | debug++; | | 143 | debug++; |
144 | break; | | 144 | break; |
145 | case 'F': | | 145 | case 'F': |
146 | for (p = optarg; *p; p++) | | 146 | for (p = optarg; *p; p++) |
147 | switch (*p) { | | 147 | switch (*p) { |
148 | case 'F': | | 148 | case 'F': |
149 | fflag |= FROM_FROM; | | 149 | fflag |= FROM_FROM; |
150 | break; | | 150 | break; |
151 | case 'R': | | 151 | case 'R': |
152 | fflag |= RETURN_PATH_FROM; | | 152 | fflag |= RETURN_PATH_FROM; |
153 | break; | | 153 | break; |
154 | case 'S': | | 154 | case 'S': |
155 | fflag |= SENDER_FROM; | | 155 | fflag |= SENDER_FROM; |
156 | break; | | 156 | break; |
157 | default: | | 157 | default: |
158 | errx(1, "Unknown -f option `%c'", *p); | | 158 | errx(1, "Unknown -f option `%c'", *p); |
159 | } | | 159 | } |
160 | break; | | 160 | break; |
161 | case 'f': | | 161 | case 'f': |
162 | dbprefix = optarg; | | 162 | dbprefix = optarg; |
163 | break; | | 163 | break; |
164 | case 'I': /* backward compatible */ | | 164 | case 'I': /* backward compatible */ |
165 | case 'i': /* init the database */ | | 165 | case 'i': /* init the database */ |
166 | iflag = 1; | | 166 | iflag = 1; |
167 | break; | | 167 | break; |
168 | case 'j': | | 168 | case 'j': |
169 | toanybody = 1; | | 169 | toanybody = 1; |
170 | break; | | 170 | break; |
171 | case 'm': | | 171 | case 'm': |
172 | msgfile = optarg; | | 172 | msgfile = optarg; |
173 | break; | | 173 | break; |
174 | case 'r': | | 174 | case 'r': |
175 | case 't': /* Solaris compatibility */ | | 175 | case 't': /* Solaris compatibility */ |
176 | if (!isdigit((unsigned char)*optarg)) { | | 176 | if (!isdigit((unsigned char)*optarg)) { |
177 | interval = LONG_MAX; | | 177 | interval = LONG_MAX; |
178 | break; | | 178 | break; |
179 | } | | 179 | } |
180 | if (*optarg == '\0') | | 180 | if (*optarg == '\0') |
181 | goto bad; | | 181 | goto bad; |
182 | interval = strtol(optarg, &p, 0); | | 182 | interval = strtol(optarg, &p, 0); |
183 | if (errno == ERANGE && | | 183 | if (errno == ERANGE && |
184 | (interval == LONG_MAX || interval == LONG_MIN)) | | 184 | (interval == LONG_MAX || interval == LONG_MIN)) |
185 | err(1, "Bad interval `%s'", optarg); | | 185 | err(1, "Bad interval `%s'", optarg); |
186 | switch (*p) { | | 186 | switch (*p) { |
187 | case 's': | | 187 | case 's': |
188 | break; | | 188 | break; |
189 | case 'm': | | 189 | case 'm': |
190 | interval *= SECSPERMIN; | | 190 | interval *= SECSPERMIN; |
191 | break; | | 191 | break; |
192 | case 'h': | | 192 | case 'h': |
193 | interval *= SECSPERHOUR; | | 193 | interval *= SECSPERHOUR; |
194 | break; | | 194 | break; |
195 | case 'd': | | 195 | case 'd': |
196 | case '\0': | | 196 | case '\0': |
197 | interval *= SECSPERDAY; | | 197 | interval *= SECSPERDAY; |
198 | break; | | 198 | break; |
199 | case 'w': | | 199 | case 'w': |
200 | interval *= DAYSPERWEEK * SECSPERDAY; | | 200 | interval *= DAYSPERWEEK * SECSPERDAY; |
201 | break; | | 201 | break; |
202 | default: | | 202 | default: |
203 | bad: | | 203 | bad: |
204 | errx(1, "Invalid interval `%s'", optarg); | | 204 | errx(1, "Invalid interval `%s'", optarg); |
205 | } | | 205 | } |
206 | if (interval < 0 || (*p && p[1])) | | 206 | if (interval < 0 || (*p && p[1])) |
207 | goto bad; | | 207 | goto bad; |
208 | break; | | 208 | break; |
209 | case 's': | | 209 | case 's': |
210 | (void)strlcpy(from, optarg, sizeof(from)); | | 210 | (void)strlcpy(from, optarg, sizeof(from)); |
211 | break; | | 211 | break; |
212 | case 'T': | | 212 | case 'T': |
213 | for (p = optarg; *p; p++) | | 213 | for (p = optarg; *p; p++) |
214 | switch (*p) { | | 214 | switch (*p) { |
215 | case 'A': | | 215 | case 'A': |
216 | tflag |= APPARENTLY_TO; | | 216 | tflag |= APPARENTLY_TO; |
217 | break; | | 217 | break; |
218 | case 'D': | | 218 | case 'D': |
219 | tflag |= DELIVERED_TO; | | 219 | tflag |= DELIVERED_TO; |
220 | break; | | 220 | break; |
221 | default: | | 221 | default: |
222 | errx(1, "Unknown -T option `%c'", *p); | | 222 | errx(1, "Unknown -T option `%c'", *p); |
223 | } | | 223 | } |
224 | break; | | 224 | break; |
225 | case '?': | | 225 | case '?': |
226 | default: | | 226 | default: |
227 | usage(); | | 227 | usage(); |
228 | } | | 228 | } |
229 | argc -= optind; | | 229 | argc -= optind; |
230 | argv += optind; | | 230 | argv += optind; |
231 | | | 231 | |
232 | if (argc != 1) { | | 232 | if (argc != 1) { |
233 | if (!iflag) | | 233 | if (!iflag) |
234 | usage(); | | 234 | usage(); |
235 | if (!(pw = getpwuid(getuid()))) { | | 235 | if (!(pw = getpwuid(getuid()))) { |
236 | syslog(LOG_ERR, "%s: no such user uid %u.", | | 236 | syslog(LOG_ERR, "%s: no such user uid %u.", |
237 | getprogname(), getuid()); | | 237 | getprogname(), getuid()); |
238 | exit(1); | | 238 | exit(1); |
239 | } | | 239 | } |
240 | } | | 240 | } |
241 | else if (!(pw = getpwnam(*argv))) { | | 241 | else if (!(pw = getpwnam(*argv))) { |
242 | syslog(LOG_ERR, "%s: no such user %s.", | | 242 | syslog(LOG_ERR, "%s: no such user %s.", |
243 | getprogname(), *argv); | | 243 | getprogname(), *argv); |
244 | exit(1); | | 244 | exit(1); |
245 | } | | 245 | } |
246 | if (chdir(pw->pw_dir) == -1 && | | 246 | if (chdir(pw->pw_dir) == -1 && |
247 | (dbprefix[0] != '/' || msgfile[0] != '/')) { | | 247 | (dbprefix[0] != '/' || msgfile[0] != '/')) { |
248 | syslog(LOG_ERR, "%s: no such directory %s.", | | 248 | syslog(LOG_ERR, "%s: no such directory %s.", |
249 | getprogname(), pw->pw_dir); | | 249 | getprogname(), pw->pw_dir); |
250 | exit(1); | | 250 | exit(1); |
251 | } | | 251 | } |
252 | | | 252 | |
253 | opendb(); | | 253 | opendb(); |
254 | | | 254 | |
255 | if (interval != -1) | | 255 | if (interval != -1) |
256 | setinterval((time_t)interval); | | 256 | setinterval((time_t)interval); |
257 | | | 257 | |
258 | if (iflag) { | | 258 | if (iflag) { |
259 | (void)(db->close)(db); | | 259 | (void)(db->close)(db); |
260 | exit(0); | | 260 | exit(0); |
261 | } | | 261 | } |
262 | | | 262 | |
263 | if (!(cur = malloc((size_t)sizeof(alias_t)))) { | | 263 | if (!(cur = malloc((size_t)sizeof(alias_t)))) { |
264 | syslog(LOG_ERR, "%s: %m", getprogname()); | | 264 | syslog(LOG_ERR, "%s: %m", getprogname()); |
265 | (void)(db->close)(db); | | 265 | (void)(db->close)(db); |
266 | exit(1); | | 266 | exit(1); |
267 | } | | 267 | } |
268 | cur->name = pw->pw_name; | | 268 | cur->name = pw->pw_name; |
269 | cur->next = names; | | 269 | cur->next = names; |
270 | names = cur; | | 270 | names = cur; |
271 | | | 271 | |
272 | if ((rv = readheaders()) != -1) { | | 272 | if ((rv = readheaders()) != -1) { |
273 | (void)(db->close)(db); | | 273 | (void)(db->close)(db); |
274 | exit(rv); | | 274 | exit(rv); |
275 | } | | 275 | } |
276 | | | 276 | |
277 | if (!recent()) { | | 277 | if (!recent()) { |
278 | setreply(); | | 278 | setreply(); |
279 | (void)(db->close)(db); | | 279 | (void)(db->close)(db); |
280 | sendmessage(pw->pw_name); | | 280 | sendmessage(pw->pw_name); |
281 | } | | 281 | } |
282 | else | | 282 | else |
283 | (void)(db->close)(db); | | 283 | (void)(db->close)(db); |
284 | exit(0); | | 284 | exit(0); |
285 | /* NOTREACHED */ | | 285 | /* NOTREACHED */ |
286 | } | | 286 | } |
287 | | | 287 | |
288 | static void | | 288 | static void |
289 | opendb(void) | | 289 | opendb(void) |
290 | { | | 290 | { |
291 | char path[MAXPATHLEN]; | | 291 | char path[MAXPATHLEN]; |
292 | | | 292 | |
293 | (void)snprintf(path, sizeof(path), "%s.db", dbprefix); | | 293 | (void)snprintf(path, sizeof(path), "%s.db", dbprefix); |
294 | db = dbopen(path, O_CREAT|O_RDWR | (iflag ? O_TRUNC : 0), | | 294 | db = dbopen(path, O_CREAT|O_RDWR | (iflag ? O_TRUNC : 0), |
295 | S_IRUSR|S_IWUSR, DB_HASH, NULL); | | 295 | S_IRUSR|S_IWUSR, DB_HASH, NULL); |
296 | | | 296 | |
297 | if (!db) { | | 297 | if (!db) { |
298 | syslog(LOG_ERR, "%s: %s: %m", getprogname(), path); | | 298 | syslog(LOG_ERR, "%s: %s: %m", getprogname(), path); |
299 | exit(1); | | 299 | exit(1); |
300 | } | | 300 | } |
301 | } | | 301 | } |
302 | | | 302 | |
303 | /* | | 303 | /* |
304 | * readheaders -- | | 304 | * readheaders -- |
305 | * read mail headers | | 305 | * read mail headers |
306 | */ | | 306 | */ |
307 | static int | | 307 | static int |
308 | readheaders(void) | | 308 | readheaders(void) |
309 | { | | 309 | { |
310 | alias_t *cur; | | 310 | alias_t *cur; |
311 | char *p; | | 311 | char *p; |
312 | int tome, cont; | | 312 | int tome, cont; |
313 | char buf[MAXLINE]; | | 313 | char buf[MAXLINE]; |
314 | | | 314 | |
315 | cont = tome = 0; | | 315 | cont = tome = 0; |
316 | #define COMPARE(a, b) strncmp(a, b, sizeof(b) - 1) | | 316 | #define COMPARE(a, b) strncmp(a, b, sizeof(b) - 1) |
317 | #define CASECOMPARE(a, b) strncasecmp(a, b, sizeof(b) - 1) | | 317 | #define CASECOMPARE(a, b) strncasecmp(a, b, sizeof(b) - 1) |
318 | while (fgets(buf, sizeof(buf), stdin) && *buf != '\n') | | 318 | while (fgets(buf, sizeof(buf), stdin) && *buf != '\n') |
319 | switch(*buf) { | | 319 | switch(*buf) { |
320 | case 'F': /* "From " or "From:" */ | | 320 | case 'F': /* "From " or "From:" */ |
321 | cont = 0; | | 321 | cont = 0; |
322 | if (COMPARE(buf, "From ") == 0) | | 322 | if (COMPARE(buf, "From ") == 0) |
323 | getfrom(buf + sizeof("From ") - 1); | | 323 | getfrom(buf + sizeof("From ") - 1); |
324 | if ((fflag & FROM_FROM) != 0 && | | 324 | if ((fflag & FROM_FROM) != 0 && |
325 | COMPARE(buf, "From:") == 0) | | 325 | COMPARE(buf, "From:") == 0) |
326 | getfrom(buf + sizeof("From:") - 1); | | 326 | getfrom(buf + sizeof("From:") - 1); |
327 | break; | | 327 | break; |
328 | case 'P': /* "Precedence:" */ | | 328 | case 'P': /* "Precedence:" rfc 2076 ch 3.9 */ |
329 | cont = 0; | | 329 | cont = 0; |
330 | if (CASECOMPARE(buf, "Precedence") != 0 || | | 330 | if (CASECOMPARE(buf, "Precedence") != 0 || |
331 | (buf[10] != ':' && buf[10] != ' ' && | | 331 | (buf[10] != ':' && buf[10] != ' ' && |
332 | buf[10] != '\t')) | | 332 | buf[10] != '\t')) |
333 | break; | | 333 | break; |
334 | if ((p = strchr(buf, ':')) == NULL) | | 334 | if ((p = strchr(buf, ':')) == NULL) |
335 | break; | | 335 | break; |
336 | while (*++p && isspace((unsigned char)*p)) | | 336 | while (*++p && isspace((unsigned char)*p)) |
337 | continue; | | 337 | continue; |
338 | if (!*p) | | 338 | if (!*p) |
339 | break; | | 339 | break; |
340 | if (CASECOMPARE(p, "junk") == 0 || | | 340 | if (CASECOMPARE(p, "junk") == 0 || |
341 | CASECOMPARE(p, "bulk") == 0|| | | 341 | CASECOMPARE(p, "bulk") == 0|| |
342 | CASECOMPARE(p, "list") == 0) | | 342 | CASECOMPARE(p, "list") == 0) |
343 | exit(0); | | 343 | exit(0); |
344 | break; | | 344 | break; |
345 | case 'C': /* "Cc:" */ | | 345 | case 'C': /* "Cc:" */ |
346 | if (COMPARE(buf, "Cc:")) | | 346 | if (COMPARE(buf, "Cc:")) |
347 | break; | | 347 | break; |
348 | cont = 1; | | 348 | cont = 1; |
349 | goto findme; | | 349 | goto findme; |
350 | case 'T': /* "To:" */ | | 350 | case 'T': /* "To:" */ |
351 | if (COMPARE(buf, "To:")) | | 351 | if (COMPARE(buf, "To:")) |
352 | break; | | 352 | break; |
353 | cont = 1; | | 353 | cont = 1; |
354 | goto findme; | | 354 | goto findme; |
355 | case 'A': /* "Apparently-To:" */ | | 355 | case 'A': |
356 | if ((tflag & APPARENTLY_TO) == 0 || | | 356 | /* "Apparently-To:" */ |
357 | COMPARE(buf, "Apparently-To:") != 0) | | 357 | if ((tflag & APPARENTLY_TO) != 0 && |
| | | 358 | COMPARE(buf, "Apparently-To:") == 0) { |
| | | 359 | cont = 1; |
| | | 360 | goto findme; |
| | | 361 | } |
| | | 362 | /* "Auto-Submitted:" rfc 3834 ch 5 */ |
| | | 363 | cont = 0; |
| | | 364 | if (CASECOMPARE(buf, "Auto-Submitted") != 0 || |
| | | 365 | (buf[14] != ':' && buf[14] != ' ' && |
| | | 366 | buf[14] != '\t')) |
358 | break; | | 367 | break; |
359 | cont = 1; | | 368 | if ((p = strchr(buf, ':')) == NULL) |
360 | goto findme; | | 369 | break; |
| | | 370 | while (*++p && isspace((unsigned char)*p)) |
| | | 371 | continue; |
| | | 372 | if (CASECOMPARE(p, "no") != 0 ) |
| | | 373 | exit(0); |
| | | 374 | break; |
361 | case 'D': /* "Delivered-To:" */ | | 375 | case 'D': /* "Delivered-To:" */ |
362 | if ((tflag & DELIVERED_TO) == 0 || | | 376 | if ((tflag & DELIVERED_TO) == 0 || |
363 | COMPARE(buf, "Delivered-To:") != 0) | | 377 | COMPARE(buf, "Delivered-To:") != 0) |
364 | break; | | 378 | break; |
365 | cont = 1; | | 379 | cont = 1; |
366 | goto findme; | | 380 | goto findme; |
367 | case 'R': /* "Return-Path:" */ | | 381 | case 'R': /* "Return-Path:" */ |
368 | cont = 0; | | 382 | cont = 0; |
369 | if ((fflag & RETURN_PATH_FROM) != 0 && | | 383 | if ((fflag & RETURN_PATH_FROM) != 0 && |
370 | COMPARE(buf, "Return-Path:") == 0) | | 384 | COMPARE(buf, "Return-Path:") == 0) |
371 | getfrom(buf + sizeof("Return-Path:") - 1); | | 385 | getfrom(buf + sizeof("Return-Path:") - 1); |
372 | break; | | 386 | break; |
373 | case 'S': /* "Sender:" */ | | 387 | case 'S': /* "Sender:" */ |
374 | cont = 0; | | 388 | cont = 0; |
375 | if (COMPARE(buf, "Subject:") == 0) { | | 389 | if (COMPARE(buf, "Subject:") == 0) { |
376 | /* trim leading blanks */ | | 390 | /* trim leading blanks */ |
377 | char *s = NULL; | | 391 | char *s = NULL; |
378 | for (p = buf + sizeof("Subject:") - 1; *p; p++) | | 392 | for (p = buf + sizeof("Subject:") - 1; *p; p++) |
379 | if (s == NULL && | | 393 | if (s == NULL && |
380 | !isspace((unsigned char)*p)) | | 394 | !isspace((unsigned char)*p)) |
381 | s = p; | | 395 | s = p; |
382 | /* trim trailing blanks */ | | 396 | /* trim trailing blanks */ |
383 | if (s) { | | 397 | if (s) { |
384 | for (--p; p != s; p--) | | 398 | for (--p; p != s; p--) |
385 | if (!isspace((unsigned char)*p)) | | 399 | if (!isspace((unsigned char)*p)) |
386 | break; | | 400 | break; |
387 | *++p = '\0'; | | 401 | *++p = '\0'; |
388 | } | | 402 | } |
389 | if (s) { | | 403 | if (s) { |
390 | (void)strlcpy(subject, s, sizeof(subject)); | | 404 | (void)strlcpy(subject, s, sizeof(subject)); |
391 | } else { | | 405 | } else { |
392 | subject[0] = '\0'; | | 406 | subject[0] = '\0'; |
393 | } | | 407 | } |
394 | } | | 408 | } |
395 | if ((fflag & SENDER_FROM) != 0 && | | 409 | if ((fflag & SENDER_FROM) != 0 && |
396 | COMPARE(buf, "Sender:") == 0) | | 410 | COMPARE(buf, "Sender:") == 0) |
397 | getfrom(buf + sizeof("Sender:") - 1); | | 411 | getfrom(buf + sizeof("Sender:") - 1); |
398 | break; | | 412 | break; |
399 | default: | | 413 | default: |
400 | if (!isspace((unsigned char)*buf) || !cont || tome) { | | 414 | if (!isspace((unsigned char)*buf) || !cont || tome) { |
401 | cont = 0; | | 415 | cont = 0; |
402 | break; | | 416 | break; |
403 | } | | 417 | } |
404 | findme: for (cur = names; !tome && cur; cur = cur->next) | | 418 | findme: for (cur = names; !tome && cur; cur = cur->next) |
405 | tome += nsearch(cur->name, buf); | | 419 | tome += nsearch(cur->name, buf); |
406 | } | | 420 | } |
407 | if (!toanybody && !tome) | | 421 | if (!toanybody && !tome) |
408 | return 0; | | 422 | return 0; |
409 | if (!*from) { | | 423 | if (!*from) { |
410 | syslog(LOG_ERR, "%s: no initial \"From\" line.", | | 424 | syslog(LOG_ERR, "%s: no initial \"From\" line.", |
411 | getprogname()); | | 425 | getprogname()); |
412 | return 1; | | 426 | return 1; |
413 | } | | 427 | } |
414 | return -1; | | 428 | return -1; |
415 | } | | 429 | } |
416 | | | 430 | |
417 | /* | | 431 | /* |
418 | * nsearch -- | | 432 | * nsearch -- |
419 | * do a nice, slow, search of a string for a substring. | | 433 | * do a nice, slow, search of a string for a substring. |
420 | */ | | 434 | */ |
421 | static int | | 435 | static int |
422 | nsearch(const char *name, const char *str) | | 436 | nsearch(const char *name, const char *str) |
423 | { | | 437 | { |
424 | size_t len; | | 438 | size_t len; |
425 | | | 439 | |
426 | for (len = strlen(name); *str; ++str) | | 440 | for (len = strlen(name); *str; ++str) |
427 | if (!strncasecmp(name, str, len)) | | 441 | if (!strncasecmp(name, str, len)) |
428 | return(1); | | 442 | return(1); |
429 | return(0); | | 443 | return(0); |
430 | } | | 444 | } |
431 | | | 445 | |
432 | /* | | 446 | /* |
433 | * getfrom -- | | 447 | * getfrom -- |
434 | * return the first string in the buffer, stripping leading and trailing | | 448 | * return the first string in the buffer, stripping leading and trailing |
435 | * blanks and <>. | | 449 | * blanks and <>. |
436 | */ | | 450 | */ |
437 | void | | 451 | void |
438 | getfrom(char *buf) | | 452 | getfrom(char *buf) |
439 | { | | 453 | { |
440 | char *s, *p; | | 454 | char *s, *p; |
441 | | | 455 | |
442 | if ((s = strchr(buf, '<')) != NULL) | | 456 | if ((s = strchr(buf, '<')) != NULL) |
443 | s++; | | 457 | s++; |
444 | else | | 458 | else |
445 | s = buf; | | 459 | s = buf; |
446 | | | 460 | |
447 | for (; *s && isspace((unsigned char)*s); s++) | | 461 | for (; *s && isspace((unsigned char)*s); s++) |
448 | continue; | | 462 | continue; |
449 | for (p = s; *p && !isspace((unsigned char)*p); p++) | | 463 | for (p = s; *p && !isspace((unsigned char)*p); p++) |
450 | continue; | | 464 | continue; |
451 | | | 465 | |
452 | if (*--p == '>') | | 466 | if (*--p == '>') |
453 | *p = '\0'; | | 467 | *p = '\0'; |
454 | else | | 468 | else |
455 | *++p = '\0'; | | 469 | *++p = '\0'; |
456 | | | 470 | |
457 | if (junkmail(s)) | | 471 | if (junkmail(s)) |
458 | exit(0); | | 472 | exit(0); |
459 | | | 473 | |
460 | if (!*from) | | 474 | if (!*from) |
461 | (void)strlcpy(from, s, sizeof(from)); | | 475 | (void)strlcpy(from, s, sizeof(from)); |
462 | } | | 476 | } |
463 | | | 477 | |
464 | /* | | 478 | /* |
465 | * junkmail -- | | 479 | * junkmail -- |
466 | * read the header and return if automagic/junk/bulk/list mail | | 480 | * read the header and return if automagic/junk/bulk/list mail |
467 | */ | | 481 | */ |
468 | static int | | 482 | static int |
469 | junkmail(const char *addr) | | 483 | junkmail(const char *addr) |
470 | { | | 484 | { |
471 | static struct ignore { | | 485 | static struct ignore { |
472 | const char *name; | | 486 | const char *name; |
473 | size_t len; | | 487 | size_t len; |
474 | } ignore[] = { | | 488 | } ignore[] = { |
475 | #define INIT(a) { a, sizeof(a) - 1 } | | 489 | #define INIT(a) { a, sizeof(a) - 1 } |
476 | INIT("-request"), | | 490 | INIT("-request"), |
477 | INIT("postmaster"), | | 491 | INIT("postmaster"), |
478 | INIT("uucp"), | | 492 | INIT("uucp"), |
479 | INIT("mailer-daemon"), | | 493 | INIT("mailer-daemon"), |
480 | INIT("mailer"), | | 494 | INIT("mailer"), |
481 | INIT("-relay"), | | 495 | INIT("-relay"), |
482 | {NULL, 0 } | | 496 | {NULL, 0 } |
483 | }; | | 497 | }; |
484 | struct ignore *cur; | | 498 | struct ignore *cur; |
485 | size_t len; | | 499 | size_t len; |
486 | const char *p; | | 500 | const char *p; |
487 | | | 501 | |
488 | /* | | 502 | /* |
489 | * This is mildly amusing, and I'm not positive it's right; trying | | 503 | * This is mildly amusing, and I'm not positive it's right; trying |
490 | * to find the "real" name of the sender, assuming that addresses | | 504 | * to find the "real" name of the sender, assuming that addresses |
491 | * will be some variant of: | | 505 | * will be some variant of: |
492 | * | | 506 | * |
493 | * From site!site!SENDER%site.domain%site.domain@site.domain | | 507 | * From site!site!SENDER%site.domain%site.domain@site.domain |
494 | */ | | 508 | */ |
495 | if (!(p = strchr(addr, '%'))) | | 509 | if (!(p = strchr(addr, '%'))) |
496 | if (!(p = strchr(addr, '@'))) { | | 510 | if (!(p = strchr(addr, '@'))) { |
497 | if ((p = strrchr(addr, '!')) != NULL) | | 511 | if ((p = strrchr(addr, '!')) != NULL) |
498 | ++p; | | 512 | ++p; |
499 | else | | 513 | else |
500 | p = addr; | | 514 | p = addr; |
501 | for (; *p; ++p) | | 515 | for (; *p; ++p) |
502 | continue; | | 516 | continue; |
503 | } | | 517 | } |
504 | len = p - addr; | | 518 | len = p - addr; |
505 | for (cur = ignore; cur->name; ++cur) | | 519 | for (cur = ignore; cur->name; ++cur) |
506 | if (len >= cur->len && | | 520 | if (len >= cur->len && |
507 | !strncasecmp(cur->name, p - cur->len, cur->len)) | | 521 | !strncasecmp(cur->name, p - cur->len, cur->len)) |
508 | return(1); | | 522 | return(1); |
509 | return(0); | | 523 | return(0); |
510 | } | | 524 | } |
511 | | | 525 | |
512 | #define VIT "__VACATION__INTERVAL__TIMER__" | | 526 | #define VIT "__VACATION__INTERVAL__TIMER__" |
513 | | | 527 | |
514 | /* | | 528 | /* |
515 | * recent -- | | 529 | * recent -- |
516 | * find out if user has gotten a vacation message recently. | | 530 | * find out if user has gotten a vacation message recently. |
517 | * use memmove for machines with alignment restrictions | | 531 | * use memmove for machines with alignment restrictions |
518 | */ | | 532 | */ |
519 | static int | | 533 | static int |
520 | recent(void) | | 534 | recent(void) |
521 | { | | 535 | { |
522 | DBT key, data; | | 536 | DBT key, data; |
523 | time_t then, next; | | 537 | time_t then, next; |
524 | | | 538 | |
525 | /* get interval time */ | | 539 | /* get interval time */ |
526 | key.data = (void *)(intptr_t)VIT; | | 540 | key.data = (void *)(intptr_t)VIT; |
527 | key.size = sizeof(VIT); | | 541 | key.size = sizeof(VIT); |
528 | if ((db->get)(db, &key, &data, 0)) | | 542 | if ((db->get)(db, &key, &data, 0)) |
529 | next = SECSPERDAY * DAYSPERWEEK; | | 543 | next = SECSPERDAY * DAYSPERWEEK; |
530 | else | | 544 | else |
531 | (void)memmove(&next, data.data, sizeof(next)); | | 545 | (void)memmove(&next, data.data, sizeof(next)); |
532 | | | 546 | |
533 | /* get record for this address */ | | 547 | /* get record for this address */ |
534 | key.data = from; | | 548 | key.data = from; |
535 | key.size = strlen(from); | | 549 | key.size = strlen(from); |
536 | if (!(db->get)(db, &key, &data, 0)) { | | 550 | if (!(db->get)(db, &key, &data, 0)) { |
537 | (void)memmove(&then, data.data, sizeof(then)); | | 551 | (void)memmove(&then, data.data, sizeof(then)); |
538 | if (next == (time_t)LONG_MAX || /* XXX */ | | 552 | if (next == (time_t)LONG_MAX || /* XXX */ |
539 | then + next > time(NULL)) | | 553 | then + next > time(NULL)) |
540 | return(1); | | 554 | return(1); |
541 | } | | 555 | } |
542 | return(0); | | 556 | return(0); |
543 | } | | 557 | } |
544 | | | 558 | |
545 | /* | | 559 | /* |
546 | * setinterval -- | | 560 | * setinterval -- |
547 | * store the reply interval | | 561 | * store the reply interval |
548 | */ | | 562 | */ |
549 | static void | | 563 | static void |
550 | setinterval(time_t interval) | | 564 | setinterval(time_t interval) |
551 | { | | 565 | { |
552 | DBT key, data; | | 566 | DBT key, data; |
553 | | | 567 | |
554 | key.data = (void *)(intptr_t)VIT; | | 568 | key.data = (void *)(intptr_t)VIT; |
555 | key.size = sizeof(VIT); | | 569 | key.size = sizeof(VIT); |
556 | data.data = &interval; | | 570 | data.data = &interval; |
557 | data.size = sizeof(interval); | | 571 | data.size = sizeof(interval); |
558 | (void)(db->put)(db, &key, &data, 0); | | 572 | (void)(db->put)(db, &key, &data, 0); |
559 | } | | 573 | } |
560 | | | 574 | |
561 | /* | | 575 | /* |
562 | * setreply -- | | 576 | * setreply -- |
563 | * store that this user knows about the vacation. | | 577 | * store that this user knows about the vacation. |
564 | */ | | 578 | */ |
565 | static void | | 579 | static void |
566 | setreply(void) | | 580 | setreply(void) |
567 | { | | 581 | { |
568 | DBT key, data; | | 582 | DBT key, data; |
569 | time_t now; | | 583 | time_t now; |
570 | | | 584 | |
571 | key.data = from; | | 585 | key.data = from; |
572 | key.size = strlen(from); | | 586 | key.size = strlen(from); |
573 | (void)time(&now); | | 587 | (void)time(&now); |
574 | data.data = &now; | | 588 | data.data = &now; |
575 | data.size = sizeof(now); | | 589 | data.size = sizeof(now); |
576 | (void)(db->put)(db, &key, &data, 0); | | 590 | (void)(db->put)(db, &key, &data, 0); |
577 | } | | 591 | } |
578 | | | 592 | |
579 | /* | | 593 | /* |
580 | * sendmessage -- | | 594 | * sendmessage -- |
581 | * exec sendmail to send the vacation file to sender | | 595 | * exec sendmail to send the vacation file to sender |
582 | */ | | 596 | */ |
583 | static void | | 597 | static void |
584 | sendmessage(const char *myname) | | 598 | sendmessage(const char *myname) |
585 | { | | 599 | { |
586 | FILE *mfp, *sfp; | | 600 | FILE *mfp, *sfp; |
587 | int i; | | 601 | int i; |
588 | int pvect[2]; | | 602 | int pvect[2]; |
589 | char buf[MAXLINE]; | | 603 | char buf[MAXLINE]; |
590 | | | 604 | |
591 | mfp = fopen(msgfile, "r"); | | 605 | mfp = fopen(msgfile, "r"); |
592 | if (mfp == NULL) { | | 606 | if (mfp == NULL) { |
593 | syslog(LOG_ERR, "%s: no `%s' file for `%s'.", getprogname(), | | 607 | syslog(LOG_ERR, "%s: no `%s' file for `%s'.", getprogname(), |
594 | myname, msgfile); | | 608 | myname, msgfile); |
595 | exit(1); | | 609 | exit(1); |
596 | } | | 610 | } |
597 | | | 611 | |
598 | if (debug) { | | 612 | if (debug) { |
599 | sfp = stdout; | | 613 | sfp = stdout; |
600 | } else { | | 614 | } else { |
601 | if (pipe(pvect) < 0) { | | 615 | if (pipe(pvect) < 0) { |
602 | syslog(LOG_ERR, "%s: pipe: %m", getprogname()); | | 616 | syslog(LOG_ERR, "%s: pipe: %m", getprogname()); |
603 | exit(1); | | 617 | exit(1); |
604 | } | | 618 | } |
605 | i = vfork(); | | 619 | i = vfork(); |
606 | if (i < 0) { | | 620 | if (i < 0) { |
607 | syslog(LOG_ERR, "%s: fork: %m", getprogname()); | | 621 | syslog(LOG_ERR, "%s: fork: %m", getprogname()); |
608 | exit(1); | | 622 | exit(1); |
609 | } | | 623 | } |
610 | if (i == 0) { | | 624 | if (i == 0) { |
611 | (void)dup2(pvect[0], 0); | | 625 | (void)dup2(pvect[0], 0); |
612 | (void)close(pvect[0]); | | 626 | (void)close(pvect[0]); |
613 | (void)close(pvect[1]); | | 627 | (void)close(pvect[1]); |
614 | (void)close(fileno(mfp)); | | 628 | (void)close(fileno(mfp)); |
615 | (void)execl(_PATH_SENDMAIL, "sendmail", "-f", myname, | | 629 | (void)execl(_PATH_SENDMAIL, "sendmail", "-f", myname, |
616 | "--", from, NULL); | | 630 | "--", from, NULL); |
617 | syslog(LOG_ERR, "%s: can't exec %s: %m", | | 631 | syslog(LOG_ERR, "%s: can't exec %s: %m", |
618 | getprogname(), _PATH_SENDMAIL); | | 632 | getprogname(), _PATH_SENDMAIL); |
619 | _exit(1); | | 633 | _exit(1); |
620 | } | | 634 | } |
621 | (void)close(pvect[0]); | | 635 | (void)close(pvect[0]); |
622 | sfp = fdopen(pvect[1], "w"); | | 636 | sfp = fdopen(pvect[1], "w"); |
623 | if (sfp == NULL) { | | 637 | if (sfp == NULL) { |
624 | syslog(LOG_ERR, "%s: can't fdopen %d: %m", | | 638 | syslog(LOG_ERR, "%s: can't fdopen %d: %m", |
625 | getprogname(), pvect[1]); | | 639 | getprogname(), pvect[1]); |
626 | _exit(1); | | 640 | _exit(1); |
627 | } | | 641 | } |
628 | } | | 642 | } |
629 | (void)fprintf(sfp, "To: %s\n", from); | | 643 | (void)fprintf(sfp, "To: %s\n", from); |
630 | (void)fputs("Auto-Submitted: auto-replied\n", sfp); | | 644 | (void)fputs("Auto-Submitted: auto-replied\n", sfp); |
| | | 645 | (void)fputs("Precedence: bulk\n", sfp); |
631 | while (fgets(buf, sizeof buf, mfp) != NULL) { | | 646 | while (fgets(buf, sizeof buf, mfp) != NULL) { |
632 | char *p; | | 647 | char *p; |
633 | if ((p = strstr(buf, "$SUBJECT")) != NULL) { | | 648 | if ((p = strstr(buf, "$SUBJECT")) != NULL) { |
634 | *p = '\0'; | | 649 | *p = '\0'; |
635 | (void)fputs(buf, sfp); | | 650 | (void)fputs(buf, sfp); |
636 | (void)fputs(subject, sfp); | | 651 | (void)fputs(subject, sfp); |
637 | p += sizeof("$SUBJECT") - 1; | | 652 | p += sizeof("$SUBJECT") - 1; |
638 | (void)fputs(p, sfp); | | 653 | (void)fputs(p, sfp); |
639 | } else | | 654 | } else |
640 | (void)fputs(buf, sfp); | | 655 | (void)fputs(buf, sfp); |
641 | } | | 656 | } |
642 | (void)fclose(mfp); | | 657 | (void)fclose(mfp); |
643 | if (sfp != stdout) | | 658 | if (sfp != stdout) |
644 | (void)fclose(sfp); | | 659 | (void)fclose(sfp); |
645 | } | | 660 | } |
646 | | | 661 | |
647 | static void | | 662 | static void |
648 | usage(void) | | 663 | usage(void) |
649 | { | | 664 | { |
650 | | | 665 | |
651 | syslog(LOG_ERR, "uid %u: Usage: %s [-dIij] [-a alias] [-f database_file] [-F F|R|S] [-m message_file] [-s sender] [-t interval] [-T A|D]" | | 666 | syslog(LOG_ERR, "uid %u: Usage: %s [-dIij] [-a alias] [-f database_file] [-F F|R|S] [-m message_file] [-s sender] [-t interval] [-T A|D]" |
652 | " login", getuid(), getprogname()); | | 667 | " login", getuid(), getprogname()); |
653 | exit(1); | | 668 | exit(1); |
654 | } | | 669 | } |