]> git.cameronkatri.com Git - apple_cmds.git/blob - mail_cmds/mail/aux.c
libpcap-98.40.1
[apple_cmds.git] / mail_cmds / mail / aux.c
1 /*
2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)aux.c 8.1 (Berkeley) 6/6/93";
37 #endif
38 static const char rcsid[] =
39 "$FreeBSD: src/usr.bin/mail/aux.c,v 1.13 2002/08/25 13:22:47 charnier Exp $";
40 #endif /* not lint */
41
42 #include <sys/cdefs.h>
43 #include <sys/time.h>
44
45 #include "rcv.h"
46 #include "extern.h"
47
48 /*
49 * Mail -- a mail program
50 *
51 * Auxiliary functions.
52 */
53
54 static char *save2str(char *, char *);
55
56 /*
57 * Return a pointer to a dynamic copy of the argument.
58 */
59 char *
60 savestr(str)
61 char *str;
62 {
63 char *new;
64 int size = strlen(str) + 1;
65
66 if ((new = salloc(size)) != NULL)
67 bcopy(str, new, size);
68 return (new);
69 }
70
71 /*
72 * Make a copy of new argument incorporating old one.
73 */
74 char *
75 save2str(str, old)
76 char *str, *old;
77 {
78 char *new;
79 int newsize = strlen(str) + 1;
80 int oldsize = old ? strlen(old) + 1 : 0;
81
82 if ((new = salloc(newsize + oldsize)) != NULL) {
83 if (oldsize) {
84 bcopy(old, new, oldsize);
85 new[oldsize - 1] = ' ';
86 }
87 bcopy(str, new + oldsize, newsize);
88 }
89 return (new);
90 }
91
92 /*
93 * Touch the named message by setting its MTOUCH flag.
94 * Touched messages have the effect of not being sent
95 * back to the system mailbox on exit.
96 */
97 void
98 touch(mp)
99 struct message *mp;
100 {
101
102 mp->m_flag |= MTOUCH;
103 if ((mp->m_flag & MREAD) == 0)
104 mp->m_flag |= MREAD|MSTATUS;
105 }
106
107 /*
108 * Test to see if the passed file name is a directory.
109 * Return true if it is.
110 */
111 int
112 isdir(name)
113 char name[];
114 {
115 struct stat sbuf;
116
117 if (stat(name, &sbuf) < 0)
118 return (0);
119 return (S_ISDIR(sbuf.st_mode));
120 }
121
122 /*
123 * Count the number of arguments in the given string raw list.
124 */
125 int
126 argcount(argv)
127 char **argv;
128 {
129 char **ap;
130
131 for (ap = argv; *ap++ != NULL;)
132 ;
133 return (ap - argv - 1);
134 }
135
136 /*
137 * Return the desired header line from the passed message
138 * pointer (or NULL if the desired header field is not available).
139 */
140 char *
141 hfield(field, mp)
142 const char *field;
143 struct message *mp;
144 {
145 FILE *ibuf;
146 char linebuf[LINESIZE];
147 int lc;
148 char *hfield;
149 char *colon, *oldhfield = NULL;
150
151 ibuf = setinput(mp);
152 if ((lc = mp->m_lines - 1) < 0)
153 return (NULL);
154 if (readline(ibuf, linebuf, LINESIZE) < 0)
155 return (NULL);
156 while (lc > 0) {
157 if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0)
158 return (oldhfield);
159 if ((hfield = ishfield(linebuf, colon, field)) != NULL)
160 oldhfield = save2str(hfield, oldhfield);
161 }
162 return (oldhfield);
163 }
164
165 /*
166 * Return the next header field found in the given message.
167 * Return >= 0 if something found, < 0 elsewise.
168 * "colon" is set to point to the colon in the header.
169 * Must deal with \ continuations & other such fraud.
170 */
171 int
172 gethfield(f, linebuf, rem, colon)
173 FILE *f;
174 char linebuf[];
175 int rem;
176 char **colon;
177 {
178 char line2[LINESIZE];
179 char *cp, *cp2;
180 int c;
181
182 for (;;) {
183 if (--rem < 0)
184 return (-1);
185 if ((c = readline(f, linebuf, LINESIZE)) <= 0)
186 return (-1);
187 for (cp = linebuf; isprint((unsigned char)*cp) && *cp != ' ' && *cp != ':';
188 cp++)
189 ;
190 if (*cp != ':' || cp == linebuf)
191 continue;
192 /*
193 * I guess we got a headline.
194 * Handle wraparounding
195 */
196 *colon = cp;
197 cp = linebuf + c;
198 for (;;) {
199 while (--cp >= linebuf && (*cp == ' ' || *cp == '\t'))
200 ;
201 cp++;
202 if (rem <= 0)
203 break;
204 ungetc(c = getc(f), f);
205 if (c != ' ' && c != '\t')
206 break;
207 if ((c = readline(f, line2, LINESIZE)) < 0)
208 break;
209 rem--;
210 for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++)
211 ;
212 c -= cp2 - line2;
213 if (cp + c >= linebuf + LINESIZE - 2)
214 break;
215 *cp++ = ' ';
216 bcopy(cp2, cp, c);
217 cp += c;
218 }
219 *cp = 0;
220 return (rem);
221 }
222 /* NOTREACHED */
223 }
224
225 /*
226 * Check whether the passed line is a header line of
227 * the desired breed. Return the field body, or 0.
228 */
229
230 char*
231 ishfield(linebuf, colon, field)
232 char linebuf[];
233 char *colon;
234 const char *field;
235 {
236 char *cp = colon;
237
238 *cp = 0;
239 if (strcasecmp(linebuf, field) != 0) {
240 *cp = ':';
241 return (0);
242 }
243 *cp = ':';
244 for (cp++; *cp == ' ' || *cp == '\t'; cp++)
245 ;
246 return (cp);
247 }
248
249 /*
250 * Copy a string and lowercase the result.
251 * dsize: space left in buffer (including space for NULL)
252 */
253 void
254 istrncpy(dest, src, dsize)
255 char *dest;
256 const char *src;
257 size_t dsize;
258 {
259
260 strlcpy(dest, src, dsize);
261 while (*dest) {
262 *dest = tolower((unsigned char)*dest);
263 dest++;
264 }
265 }
266
267 /*
268 * The following code deals with input stacking to do source
269 * commands. All but the current file pointer are saved on
270 * the stack.
271 */
272
273 static int ssp; /* Top of file stack */
274 struct sstack {
275 FILE *s_file; /* File we were in. */
276 int s_cond; /* Saved state of conditionals */
277 int s_loading; /* Loading .mailrc, etc. */
278 };
279 #define SSTACK_SIZE 64 /* XXX was NOFILE. */
280 static struct sstack sstack[SSTACK_SIZE];
281
282 /*
283 * Pushdown current input file and switch to a new one.
284 * Set the global flag "sourcing" so that others will realize
285 * that they are no longer reading from a tty (in all probability).
286 */
287 int
288 source(arglist)
289 char **arglist;
290 {
291 FILE *fi;
292 char *cp;
293
294 if ((cp = expand(*arglist)) == NULL)
295 return (1);
296 if ((fi = Fopen(cp, "r")) == NULL) {
297 warn("%s", cp);
298 return (1);
299 }
300 if (ssp >= SSTACK_SIZE - 1) {
301 printf("Too much \"sourcing\" going on.\n");
302 (void)Fclose(fi);
303 return (1);
304 }
305 sstack[ssp].s_file = input;
306 sstack[ssp].s_cond = cond;
307 sstack[ssp].s_loading = loading;
308 ssp++;
309 loading = 0;
310 cond = CANY;
311 input = fi;
312 sourcing++;
313 return (0);
314 }
315
316 /*
317 * Pop the current input back to the previous level.
318 * Update the "sourcing" flag as appropriate.
319 */
320 int
321 unstack()
322 {
323 if (ssp <= 0) {
324 printf("\"Source\" stack over-pop.\n");
325 sourcing = 0;
326 return (1);
327 }
328 (void)Fclose(input);
329 if (cond != CANY)
330 printf("Unmatched \"if\"\n");
331 ssp--;
332 cond = sstack[ssp].s_cond;
333 loading = sstack[ssp].s_loading;
334 input = sstack[ssp].s_file;
335 if (ssp == 0)
336 sourcing = loading;
337 return (0);
338 }
339
340 /*
341 * Touch the indicated file.
342 * This is nifty for the shell.
343 */
344 void
345 alter(name)
346 char *name;
347 {
348 struct stat sb;
349 struct timeval tv[2];
350
351 if (stat(name, &sb))
352 return;
353 (void)gettimeofday(&tv[0], (struct timezone *)NULL);
354 tv[0].tv_sec++;
355 TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec);
356 (void)utimes(name, tv);
357 }
358
359 /*
360 * Get sender's name from this message. If the message has
361 * a bunch of arpanet stuff in it, we may have to skin the name
362 * before returning it.
363 */
364 char *
365 nameof(mp, reptype)
366 struct message *mp;
367 int reptype;
368 {
369 char *cp, *cp2;
370
371 cp = skin(name1(mp, reptype));
372 if (reptype != 0 || charcount(cp, '!') < 2)
373 return (cp);
374 cp2 = strrchr(cp, '!');
375 cp2--;
376 while (cp2 > cp && *cp2 != '!')
377 cp2--;
378 if (*cp2 == '!')
379 return (cp2 + 1);
380 return (cp);
381 }
382
383 /*
384 * Start of a "comment".
385 * Ignore it.
386 */
387 char *
388 skip_comment(cp)
389 char *cp;
390 {
391 int nesting = 1;
392
393 for (; nesting > 0 && *cp; cp++) {
394 switch (*cp) {
395 case '\\':
396 if (cp[1])
397 cp++;
398 break;
399 case '(':
400 nesting++;
401 break;
402 case ')':
403 nesting--;
404 break;
405 }
406 }
407 return (cp);
408 }
409
410 /*
411 * Skin an arpa net address according to the RFC 822 interpretation
412 * of "host-phrase."
413 */
414 char *
415 skin(name)
416 char *name;
417 {
418 char *nbuf, *bufend, *cp, *cp2;
419 int c, gotlt, lastsp;
420
421 if (name == NULL)
422 return (NULL);
423 if (strchr(name, '(') == NULL && strchr(name, '<') == NULL
424 && strchr(name, ' ') == NULL)
425 return (name);
426
427 /* We assume that length(input) <= length(output) */
428 if ((nbuf = malloc(strlen(name) + 1)) == NULL)
429 err(1, "Out of memory");
430 gotlt = 0;
431 lastsp = 0;
432 bufend = nbuf;
433 for (cp = name, cp2 = bufend; (c = *cp++) != '\0'; ) {
434 switch (c) {
435 case '(':
436 cp = skip_comment(cp);
437 lastsp = 0;
438 break;
439
440 case '"':
441 /*
442 * Start of a "quoted-string".
443 * Copy it in its entirety.
444 */
445 while ((c = *cp) != '\0') {
446 cp++;
447 if (c == '"')
448 break;
449 if (c != '\\')
450 *cp2++ = c;
451 else if ((c = *cp) != '\0') {
452 *cp2++ = c;
453 cp++;
454 }
455 }
456 lastsp = 0;
457 break;
458
459 case ' ':
460 if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
461 cp += 3, *cp2++ = '@';
462 else
463 if (cp[0] == '@' && cp[1] == ' ')
464 cp += 2, *cp2++ = '@';
465 else
466 lastsp = 1;
467 break;
468
469 case '<':
470 cp2 = bufend;
471 gotlt++;
472 lastsp = 0;
473 break;
474
475 case '>':
476 if (gotlt) {
477 gotlt = 0;
478 while ((c = *cp) != '\0' && c != ',') {
479 cp++;
480 if (c == '(')
481 cp = skip_comment(cp);
482 else if (c == '"')
483 while ((c = *cp) != '\0') {
484 cp++;
485 if (c == '"')
486 break;
487 if (c == '\\' && *cp != '\0')
488 cp++;
489 }
490 }
491 lastsp = 0;
492 break;
493 }
494 /* FALLTHROUGH */
495
496 default:
497 if (lastsp) {
498 lastsp = 0;
499 *cp2++ = ' ';
500 }
501 *cp2++ = c;
502 if (c == ',' && *cp == ' ' && !gotlt) {
503 *cp2++ = ' ';
504 while (*++cp == ' ')
505 ;
506 lastsp = 0;
507 bufend = cp2;
508 }
509 }
510 }
511 *cp2 = '\0';
512
513 if ((cp = realloc(nbuf, strlen(nbuf) + 1)) != NULL)
514 nbuf = cp;
515 return (nbuf);
516 }
517
518 /*
519 * Fetch the sender's name from the passed message.
520 * Reptype can be
521 * 0 -- get sender's name for display purposes
522 * 1 -- get sender's name for reply
523 * 2 -- get sender's name for Reply
524 */
525 char *
526 name1(mp, reptype)
527 struct message *mp;
528 int reptype;
529 {
530 char namebuf[LINESIZE];
531 char linebuf[LINESIZE];
532 char *cp, *cp2;
533 FILE *ibuf;
534 int first = 1;
535
536 if ((cp = hfield("from", mp)) != NULL)
537 return (cp);
538 if (reptype == 0 && (cp = hfield("sender", mp)) != NULL)
539 return (cp);
540 ibuf = setinput(mp);
541 namebuf[0] = '\0';
542 if (readline(ibuf, linebuf, LINESIZE) < 0)
543 return (savestr(namebuf));
544 newname:
545 for (cp = linebuf; *cp != '\0' && *cp != ' '; cp++)
546 ;
547 for (; *cp == ' ' || *cp == '\t'; cp++)
548 ;
549 for (cp2 = &namebuf[strlen(namebuf)];
550 *cp != '\0' && *cp != ' ' && *cp != '\t' &&
551 cp2 < namebuf + LINESIZE - 1;)
552 *cp2++ = *cp++;
553 *cp2 = '\0';
554 if (readline(ibuf, linebuf, LINESIZE) < 0)
555 return (savestr(namebuf));
556 if ((cp = strchr(linebuf, 'F')) == NULL)
557 return (savestr(namebuf));
558 if (strncmp(cp, "From", 4) != 0)
559 return (savestr(namebuf));
560 while ((cp = strchr(cp, 'r')) != NULL) {
561 if (strncmp(cp, "remote", 6) == 0) {
562 if ((cp = strchr(cp, 'f')) == NULL)
563 break;
564 if (strncmp(cp, "from", 4) != 0)
565 break;
566 if ((cp = strchr(cp, ' ')) == NULL)
567 break;
568 cp++;
569 if (first) {
570 cp2 = namebuf;
571 first = 0;
572 } else
573 cp2 = strrchr(namebuf, '!') + 1;
574 strlcpy(cp2, cp, sizeof(namebuf) - (cp2 - namebuf) - 1);
575 strcat(namebuf, "!");
576 goto newname;
577 }
578 cp++;
579 }
580 return (savestr(namebuf));
581 }
582
583 /*
584 * Count the occurances of c in str
585 */
586 int
587 charcount(str, c)
588 char *str;
589 int c;
590 {
591 char *cp;
592 int i;
593
594 for (i = 0, cp = str; *cp != '\0'; cp++)
595 if (*cp == c)
596 i++;
597 return (i);
598 }
599
600 /*
601 * See if the given header field is supposed to be ignored.
602 */
603 int
604 isign(field, ignore)
605 const char *field;
606 struct ignoretab ignore[2];
607 {
608 char realfld[LINESIZE];
609
610 if (ignore == ignoreall)
611 return (1);
612 /*
613 * Lower-case the string, so that "Status" and "status"
614 * will hash to the same place.
615 */
616 istrncpy(realfld, field, sizeof(realfld));
617 if (ignore[1].i_count > 0)
618 return (!member(realfld, ignore + 1));
619 else
620 return (member(realfld, ignore));
621 }
622
623 int
624 member(realfield, table)
625 char *realfield;
626 struct ignoretab *table;
627 {
628 struct ignore *igp;
629
630 for (igp = table->i_head[hash(realfield)]; igp != NULL; igp = igp->i_link)
631 if (*igp->i_field == *realfield &&
632 equal(igp->i_field, realfield))
633 return (1);
634 return (0);
635 }