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