1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
23 /* All Rights Reserved */
24
25
26 /*
27 * Copyright 1985-2002 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
29 */
30
31 /*
32 * University Copyright- Copyright (c) 1982, 1986, 1988
33 * The Regents of the University of California
34 * All Rights Reserved
35 *
36 * University Acknowledgment- Portions of this document are derived from
37 * software developed by the University of California, Berkeley, and its
38 * contributors.
39 */
40
41 #include "rcv.h"
42 #include <locale.h>
43
44 /*
45 * mailx -- a modified version of a University of California at Berkeley
46 * mail program
47 *
48 * Auxiliary functions.
49 */
50
51 static char *phrase(char *name, int token, int comma);
52 static char *ripoff(register char *buf);
53
54 /*
55 * Return a pointer to a dynamic copy of the argument.
56 */
57
58 char *
savestr(char * str)59 savestr(char *str)
60 {
61 register char *cp, *cp2, *top;
62
63 for (cp = str; *cp; cp++)
64 ;
65 top = (char *)salloc((unsigned)(cp-str + 1));
66 if (top == NOSTR)
67 return(NOSTR);
68 for (cp = str, cp2 = top; *cp; cp++)
69 *cp2++ = *cp;
70 *cp2 = 0;
71 return(top);
72 }
73
74 /*
75 * Announce a fatal error and die.
76 */
77
78 void
panic(char * str)79 panic(char *str)
80 {
81 fprintf(stderr, gettext("mailx: Panic - %s\n"), str);
82 exit(1);
83 /* NOTREACHED */
84 }
85
86 /*
87 * Touch the named message by setting its MTOUCH flag.
88 * Touched messages have the effect of not being sent
89 * back to the system mailbox on exit.
90 */
91
92 void
touch(int mesg)93 touch(int mesg)
94 {
95 register struct message *mp;
96
97 if (mesg < 1 || mesg > msgCount)
98 return;
99 mp = &message[mesg-1];
100 mp->m_flag |= MTOUCH;
101 if ((mp->m_flag & MREAD) == 0)
102 mp->m_flag |= MREAD|MSTATUS;
103 }
104
105 /*
106 * Test to see if the passed file name is a directory.
107 * Return true if it is.
108 */
109
110 int
isdir(char name[])111 isdir(char name[])
112 {
113 struct stat sbuf;
114
115 if (stat(name, &sbuf) < 0)
116 return(0);
117 return((sbuf.st_mode & S_IFMT) == S_IFDIR);
118 }
119
120 /*
121 * Count the number of arguments in the given string raw list.
122 */
123
124 int
argcount(char ** argv)125 argcount(char **argv)
126 {
127 register char **ap;
128
129 for (ap = argv; *ap != NOSTR; ap++)
130 ;
131 return(ap-argv);
132 }
133
134 /*
135 * Return the desired header line from the passed message
136 * pointer (or NOSTR if the desired header field is not available).
137 * Read all the header lines and concatenate multiple instances of
138 * the requested header.
139 */
140
141 char *
hfield(char field[],struct message * mp,char * (* add)(char *,char *))142 hfield(char field[], struct message *mp, char *(*add)(char *, char *))
143 {
144 register FILE *ibuf;
145 char linebuf[LINESIZE];
146 register long lc;
147 char *r = NOSTR;
148
149 ibuf = setinput(mp);
150 if ((lc = mp->m_lines) <= 0)
151 return(NOSTR);
152 if (readline(ibuf, linebuf) < 0)
153 return(NOSTR);
154 lc--;
155 while ((lc = gethfield(ibuf, linebuf, lc)) >= 0)
156 if (ishfield(linebuf, field))
157 r = (*add)(r, hcontents(linebuf));
158 return r;
159 }
160
161 /*
162 * Return the next header field found in the given message.
163 * Return > 0 if something found, <= 0 elsewise.
164 * Must deal with \ continuations & other such fraud.
165 */
166
167 int
gethfield(register FILE * f,char linebuf[],register long rem)168 gethfield(
169 register FILE *f,
170 char linebuf[],
171 register long rem)
172 {
173 char line2[LINESIZE];
174 register char *cp, *cp2;
175 register int c;
176
177 for (;;) {
178 if (rem <= 0)
179 return(-1);
180 if (readline(f, linebuf) < 0)
181 return(-1);
182 rem--;
183 if (strlen(linebuf) == 0)
184 return(-1);
185 if (isspace(linebuf[0]))
186 continue;
187 if (!headerp(linebuf))
188 return(-1);
189
190 /*
191 * I guess we got a headline.
192 * Handle wraparounding
193 */
194
195 for (;;) {
196 if (rem <= 0)
197 break;
198 c = getc(f);
199 ungetc(c, f);
200 if (!isspace(c) || c == '\n')
201 break;
202 if (readline(f, line2) < 0)
203 break;
204 rem--;
205 cp2 = line2;
206 for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
207 ;
208 if (strlen(linebuf) + strlen(cp2) >=
209 (unsigned)LINESIZE-2)
210 break;
211 cp = &linebuf[strlen(linebuf)];
212 while (cp > linebuf &&
213 (isspace(cp[-1]) || cp[-1] == '\\'))
214 cp--;
215 *cp++ = ' ';
216 for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
217 ;
218 nstrcpy(cp, LINESIZE - (cp - linebuf), cp2);
219 }
220 if ((c = strlen(linebuf)) > 0) {
221 cp = &linebuf[c-1];
222 while (cp > linebuf && isspace(*cp))
223 cp--;
224 *++cp = 0;
225 }
226 return(rem);
227 }
228 /* NOTREACHED */
229 }
230
231 /*
232 * Check whether the passed line is a header line of
233 * the desired breed.
234 */
235
236 int
ishfield(char linebuf[],char field[])237 ishfield(char linebuf[], char field[])
238 {
239 register char *cp;
240
241 if ((cp = strchr(linebuf, ':')) == NOSTR)
242 return(0);
243 if (cp == linebuf)
244 return(0);
245 *cp = 0;
246 if (icequal(linebuf, field)) {
247 *cp = ':';
248 return(1);
249 }
250 *cp = ':';
251 return(0);
252 }
253
254 /*
255 * Extract the non label information from the given header field
256 * and return it.
257 */
258
259 char *
hcontents(char hfield[])260 hcontents(char hfield[])
261 {
262 register char *cp;
263
264 if ((cp = strchr(hfield, ':')) == NOSTR)
265 return(NOSTR);
266 cp++;
267 while (*cp && isspace(*cp))
268 cp++;
269 return(cp);
270 }
271
272 /*
273 * Compare two strings, ignoring case.
274 */
275
276 int
icequal(register char * s1,register char * s2)277 icequal(register char *s1, register char *s2)
278 {
279
280 while (toupper(*s1++) == toupper(*s2))
281 if (*s2++ == 0)
282 return(1);
283 return(0);
284 }
285
286 /*
287 * Copy a string, lowercasing it as we go. Here dstsize is the size of
288 * the destination buffer dst.
289 */
290 void
istrcpy(char * dst,int dstsize,char * src)291 istrcpy(char *dst, int dstsize, char *src)
292 {
293 register char *cp, *cp2;
294
295 cp2 = dst;
296 cp = src;
297
298 while (--dstsize > 0 && *cp != '\0')
299 *cp2++ = tolower(*cp++);
300 *cp2 = '\0';
301 }
302
303 /*
304 * The following code deals with input stacking to do source
305 * commands. All but the current file pointer are saved on
306 * the stack.
307 */
308
309 static int ssp = -1; /* Top of file stack */
310 static struct sstack {
311 FILE *s_file; /* File we were in. */
312 int s_cond; /* Saved state of conditionals */
313 int s_loading; /* Loading .mailrc, etc. */
314 } *sstack;
315
316 /*
317 * Pushdown current input file and switch to a new one.
318 * Set the global flag "sourcing" so that others will realize
319 * that they are no longer reading from a tty (in all probability).
320 */
321
322 int
source(char name[])323 source(char name[])
324 {
325 register FILE *fi;
326 register char *cp;
327
328 if ((cp = expand(name)) == NOSTR)
329 return(1);
330 if ((fi = fopen(cp, "r")) == NULL) {
331 printf(gettext("Unable to open %s\n"), cp);
332 return(1);
333 }
334
335 if (!maxfiles) {
336 if ((maxfiles = (int)ulimit(4, 0)) < 0)
337 #ifndef _NFILE
338 # define _NFILE 20
339 #endif
340 maxfiles = _NFILE;
341 sstack = (struct sstack *)calloc(maxfiles, sizeof(struct sstack));
342 if (sstack == NULL) {
343 printf(gettext(
344 "Couldn't allocate memory for sourcing stack\n"));
345 fclose(fi);
346 return(1);
347 }
348 }
349
350 sstack[++ssp].s_file = input;
351 sstack[ssp].s_cond = cond;
352 sstack[ssp].s_loading = loading;
353 loading = 0;
354 cond = CANY;
355 input = fi;
356 sourcing++;
357 return(0);
358 }
359
360 /*
361 * Pop the current input back to the previous level.
362 * Update the "sourcing" flag as appropriate.
363 */
364
365 int
unstack(void)366 unstack(void)
367 {
368 if (ssp < 0) {
369 printf(gettext("\"Source\" stack over-pop.\n"));
370 sourcing = 0;
371 return(1);
372 }
373 fclose(input);
374 if (cond != CANY)
375 printf(gettext("Unmatched \"if\"\n"));
376 cond = sstack[ssp].s_cond;
377 loading = sstack[ssp].s_loading;
378 input = sstack[ssp--].s_file;
379 if (ssp < 0)
380 sourcing = loading;
381 return(0);
382 }
383
384 /*
385 * Touch the indicated file.
386 * This is nifty for the shell.
387 * If we have the utime() system call, this is better served
388 * by using that, since it will work for empty files.
389 * On non-utime systems, we must sleep a second, then read.
390 */
391
392 void
alter(char name[])393 alter(char name[])
394 {
395 int rc = utime(name, utimep);
396 extern int errno;
397
398 if (rc != 0) {
399 fprintf(stderr, gettext("Cannot utime %s in aux:alter\n"),
400 name);
401 fprintf(stderr, gettext("Errno: %d\n"), errno);
402 }
403 }
404
405 /*
406 * Examine the passed line buffer and
407 * return true if it is all blanks and tabs.
408 */
409
410 int
blankline(const char linebuf[])411 blankline(const char linebuf[])
412 {
413 register const char *cp;
414
415 for (cp = linebuf; *cp; cp++)
416 if (!any(*cp, " \t"))
417 return(0);
418 return(1);
419 }
420
421 /*
422 * Skin an arpa net address according to the RFC 822 interpretation
423 * of "host-phrase."
424 */
425 static char *
phrase(char * name,int token,int comma)426 phrase(char *name, int token, int comma)
427 {
428 register char c;
429 register char *cp, *cp2;
430 char *bufend, *nbufp;
431 int gotlt, lastsp, didq;
432 char nbuf[LINESIZE];
433 int nesting;
434
435 if (name == NOSTR)
436 return(NOSTR);
437 if (strlen(name) >= (unsigned)LINESIZE)
438 nbufp = (char *)salloc(strlen(name));
439 else
440 nbufp = nbuf;
441 gotlt = 0;
442 lastsp = 0;
443 bufend = nbufp;
444 for (cp = name, cp2 = bufend; (c = *cp++) != 0;) {
445 switch (c) {
446 case '(':
447 /*
448 Start of a comment, ignore it.
449 */
450 nesting = 1;
451 while ((c = *cp) != 0) {
452 cp++;
453 switch(c) {
454 case '\\':
455 if (*cp == 0) goto outcm;
456 cp++;
457 break;
458 case '(':
459 nesting++;
460 break;
461 case ')':
462 --nesting;
463 break;
464 }
465 if (nesting <= 0) break;
466 }
467 outcm:
468 lastsp = 0;
469 break;
470 case '"':
471 /*
472 Start a quoted string.
473 Copy it in its entirety.
474 */
475 didq = 0;
476 while ((c = *cp) != 0) {
477 cp++;
478 switch (c) {
479 case '\\':
480 if ((c = *cp) == 0) goto outqs;
481 cp++;
482 break;
483 case '"':
484 goto outqs;
485 }
486 if (gotlt == 0 || gotlt == '<') {
487 if (lastsp) {
488 lastsp = 0;
489 *cp2++ = ' ';
490 }
491 if (!didq) {
492 *cp2++ = '"';
493 didq++;
494 }
495 *cp2++ = c;
496 }
497 }
498 outqs:
499 if (didq)
500 *cp2++ = '"';
501 lastsp = 0;
502 break;
503
504 case ' ':
505 case '\t':
506 case '\n':
507 if (token && (!comma || c == '\n')) {
508 done:
509 cp[-1] = 0;
510 return cp;
511 }
512 lastsp = 1;
513 break;
514
515 case ',':
516 *cp2++ = c;
517 if (gotlt != '<') {
518 if (token)
519 goto done;
520 bufend = cp2;
521 gotlt = 0;
522 }
523 break;
524
525 case '<':
526 cp2 = bufend;
527 gotlt = c;
528 lastsp = 0;
529 break;
530
531 case '>':
532 if (gotlt == '<') {
533 gotlt = c;
534 break;
535 }
536
537 /* FALLTHROUGH . . . */
538
539 default:
540 if (gotlt == 0 || gotlt == '<') {
541 if (lastsp) {
542 lastsp = 0;
543 *cp2++ = ' ';
544 }
545 *cp2++ = c;
546 }
547 break;
548 }
549 }
550 *cp2 = 0;
551 return (token ? --cp : equal(name, nbufp) ? name :
552 nbufp == nbuf ? savestr(nbuf) : nbufp);
553 }
554
555 char *
skin(char * name)556 skin(char *name)
557 {
558 return phrase(name, 0, 0);
559 }
560
561 /*
562 * Here sz is the buffer size of word.
563 */
564 char *
yankword(char * name,char * word,int sz,int comma)565 yankword(char *name, char *word, int sz, int comma)
566 {
567 char *cp;
568
569 if (name == 0)
570 return 0;
571 while (isspace(*name))
572 name++;
573 if (*name == 0)
574 return 0;
575 cp = phrase(name, 1, comma);
576 nstrcpy(word, sz, name);
577 return cp;
578 }
579
580 int
docomma(char * s)581 docomma(char *s)
582 {
583 return s && strpbrk(s, "(<,");
584 }
585
586 /*
587 * Fetch the sender's name from the passed message.
588 */
589
590 char *
nameof(register struct message * mp)591 nameof(register struct message *mp)
592 {
593 char namebuf[LINESIZE];
594 char linebuf[LINESIZE];
595 register char *cp, *cp2;
596 register FILE *ibuf;
597 int first = 1, wint = 0;
598 char *tmp;
599
600 if (value("from") && (cp = hfield("from", mp, addto)) != NOSTR)
601 return ripoff(cp);
602 ibuf = setinput(mp);
603 copy("", namebuf);
604 if (readline(ibuf, linebuf) <= 0)
605 return(savestr(namebuf));
606 newname:
607 for (cp = linebuf; *cp != ' '; cp++)
608 ;
609 while (any(*cp, " \t"))
610 cp++;
611 for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") &&
612 cp2-namebuf < LINESIZE-1; *cp2++ = *cp++)
613 ;
614 *cp2 = '\0';
615 for (;;) {
616 if (readline(ibuf, linebuf) <= 0)
617 break;
618 if (substr(linebuf,"forwarded by ") != -1)
619 continue;
620 if (linebuf[0] == 'F')
621 cp = linebuf;
622 else if (linebuf[0] == '>')
623 cp = linebuf + 1;
624 else
625 break;
626 if (strncmp(cp, "From ", 5) != 0)
627 break;
628 if ((wint = substr(cp, "remote from ")) != -1) {
629 cp += wint + 12;
630 if (first) {
631 copy(cp, namebuf);
632 first = 0;
633 } else {
634 tmp = strrchr(namebuf, '!') + 1;
635 nstrcpy(tmp,
636 sizeof (namebuf) - (tmp - namebuf),
637 cp);
638 }
639 nstrcat(namebuf, sizeof (namebuf), "!");
640 goto newname;
641 } else
642 break;
643 }
644 for (cp = namebuf; *cp == '!'; cp++);
645 while (ishost(host, cp))
646 cp = strchr(cp, '!') + 1;
647 if (value("mustbang") && !strchr(cp, '!')) {
648 snprintf(linebuf, sizeof (linebuf), "%s!%s",
649 host, cp);
650 cp = linebuf;
651 }
652 if (cp2 = hfield("from", mp, addto))
653 return(splice(cp, cp2));
654 else
655 return(savestr(cp));
656 }
657
658 /*
659 * Splice an address into a commented recipient header.
660 */
661 char *
splice(char * addr,char * hdr)662 splice(char *addr, char *hdr)
663 {
664 char buf[LINESIZE];
665 char *cp, *cp2;
666
667 if (cp = strchr(hdr, '<')) {
668 cp2 = strchr(cp, '>');
669 if (cp2 == NULL) {
670 nstrcpy(buf, sizeof (buf), addr);
671 } else {
672 snprintf(buf, sizeof (buf), "%.*s%s%s",
673 cp - hdr + 1, hdr, addr, cp2);
674 }
675 } else if (cp = strchr(hdr, '(')) {
676 snprintf(buf, sizeof (buf), "%s %s",
677 addr, cp);
678 } else
679 nstrcpy(buf, sizeof (buf), addr);
680 return savestr(ripoff(buf));
681 }
682
683 static char *
ripoff(register char * buf)684 ripoff(register char *buf)
685 {
686 register char *cp;
687
688 cp = buf + strlen(buf);
689 while (--cp >= buf && isspace(*cp));
690 if (cp >= buf && *cp == ',')
691 cp--;
692 *++cp = 0;
693 return buf;
694 }
695
696 /*
697 * Are any of the characters in the two strings the same?
698 */
699
700 int
anyof(register char * s1,register char * s2)701 anyof(register char *s1, register char *s2)
702 {
703 register int c;
704
705 while ((c = *s1++) != 0)
706 if (any(c, s2))
707 return(1);
708 return(0);
709 }
710
711 /*
712 * See if the given header field is supposed to be ignored.
713 * Fields of the form "Content-*" can't be ignored when saving.
714 */
715 int
isign(char * field,int saving)716 isign(char *field, int saving)
717 {
718 char realfld[BUFSIZ];
719
720 /*
721 * Lower-case the string, so that "Status" and "status"
722 * will hash to the same place.
723 */
724 istrcpy(realfld, sizeof (realfld), field);
725
726 if (saving && strncmp(realfld, "content-", 8) == 0)
727 return (0);
728
729 if (nretained > 0)
730 return (!member(realfld, retain));
731 else
732 return (member(realfld, ignore));
733 }
734
735 int
member(register char * realfield,register struct ignore ** table)736 member(register char *realfield, register struct ignore **table)
737 {
738 register struct ignore *igp;
739
740 for (igp = table[hash(realfield)]; igp != 0; igp = igp->i_link)
741 if (equal(igp->i_field, realfield))
742 return (1);
743
744 return (0);
745 }
746
747 /*
748 * This routine looks for string2 in string1.
749 * If found, it returns the position string2 is found at,
750 * otherwise it returns a -1.
751 */
752 int
substr(char * string1,char * string2)753 substr(char *string1, char *string2)
754 {
755 int i, j, len1, len2;
756
757 len1 = strlen(string1);
758 len2 = strlen(string2);
759 for (i = 0; i < len1 - len2 + 1; i++) {
760 for (j = 0; j < len2 && string1[i+j] == string2[j]; j++)
761 ;
762 if (j == len2)
763 return(i);
764 }
765 return(-1);
766 }
767
768 /*
769 * Copies src to the dstsize buffer at dst. The copy will never
770 * overflow the destination buffer and the buffer will always be null
771 * terminated.
772 */
773 char *
nstrcpy(char * dst,int dstsize,char * src)774 nstrcpy(char *dst, int dstsize, char *src)
775 {
776 char *cp, *cp2;
777
778 cp2 = dst;
779 cp = src;
780
781 while (--dstsize > 0 && *cp != '\0')
782 *cp2++ = *cp++;
783 *cp2 = '\0';
784 return(dst);
785 }
786
787 /*
788 * Appends src to the dstsize buffer at dst. The append will never
789 * overflow the destination buffer and the buffer will always be null
790 * terminated.
791 */
792 char *
nstrcat(char * dst,int dstsize,char * src)793 nstrcat(char *dst, int dstsize, char *src)
794 {
795 char *cp, *cp2;
796
797 cp2 = dst;
798 cp = src;
799
800 while (*cp2 != '\0') {
801 cp2++;
802 dstsize--;
803 }
804 while (--dstsize > 0 && *cp != '\0')
805 *cp2++ = *cp++;
806 *cp2 = '\0';
807 return(dst);
808 }
809