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