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