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