📄 util.c
字号:
/* * Copyright (c) 1983 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char sccsid[] = "@(#)util.c 8.39 (Berkeley) 4/14/94";#endif /* not lint */# include "sendmail.h"# include <sysexits.h>/*** STRIPQUOTES -- Strip quotes & quote bits from a string.**** Runs through a string and strips off unquoted quote** characters and quote bits. This is done in place.**** Parameters:** s -- the string to strip.**** Returns:** none.**** Side Effects:** none.**** Called By:** deliver*/stripquotes(s) char *s;{ register char *p; register char *q; register char c; if (s == NULL) return; p = q = s; do { c = *p++; if (c == '\\') c = *p++; else if (c == '"') continue; *q++ = c; } while (c != '\0');}/*** XALLOC -- Allocate memory and bitch wildly on failure.**** THIS IS A CLUDGE. This should be made to give a proper** error -- but after all, what can we do?**** Parameters:** sz -- size of area to allocate.**** Returns:** pointer to data region.**** Side Effects:** Memory is allocated.*/char *xalloc(sz) register int sz;{ register char *p; /* some systems can't handle size zero mallocs */ if (sz <= 0) sz = 1; p = malloc((unsigned) sz); if (p == NULL) { syserr("Out of memory!!"); abort(); /* exit(EX_UNAVAILABLE); */ } return (p);}/*** COPYPLIST -- copy list of pointers.**** This routine is the equivalent of newstr for lists of** pointers.**** Parameters:** list -- list of pointers to copy.** Must be NULL terminated.** copycont -- if TRUE, copy the contents of the vector** (which must be a string) also.**** Returns:** a copy of 'list'.**** Side Effects:** none.*/char **copyplist(list, copycont) char **list; bool copycont;{ register char **vp; register char **newvp; for (vp = list; *vp != NULL; vp++) continue; vp++; newvp = (char **) xalloc((int) (vp - list) * sizeof *vp); bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp); if (copycont) { for (vp = newvp; *vp != NULL; vp++) *vp = newstr(*vp); } return (newvp);}/*** COPYQUEUE -- copy address queue.**** This routine is the equivalent of newstr for address queues** addresses marked with QDONTSEND aren't copied**** Parameters:** addr -- list of address structures to copy.**** Returns:** a copy of 'addr'.**** Side Effects:** none.*/ADDRESS *copyqueue(addr) ADDRESS *addr;{ register ADDRESS *newaddr; ADDRESS *ret; register ADDRESS **tail = &ret; while (addr != NULL) { if (!bitset(QDONTSEND, addr->q_flags)) { newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS)); STRUCTCOPY(*addr, *newaddr); *tail = newaddr; tail = &newaddr->q_next; } addr = addr->q_next; } *tail = NULL; return ret;}/*** PRINTAV -- print argument vector.**** Parameters:** av -- argument vector.**** Returns:** none.**** Side Effects:** prints av.*/printav(av) register char **av;{ while (*av != NULL) { if (tTd(0, 44)) printf("\n\t%08x=", *av); else (void) putchar(' '); xputs(*av++); } (void) putchar('\n');}/*** LOWER -- turn letter into lower case.**** Parameters:** c -- character to turn into lower case.**** Returns:** c, in lower case.**** Side Effects:** none.*/charlower(c) register char c;{ return((isascii(c) && isupper(c)) ? tolower(c) : c);}/*** XPUTS -- put string doing control escapes.**** Parameters:** s -- string to put.**** Returns:** none.**** Side Effects:** output to stdout*/xputs(s) register char *s;{ register int c; register struct metamac *mp; extern struct metamac MetaMacros[]; if (s == NULL) { printf("<null>"); return; } while ((c = (*s++ & 0377)) != '\0') { if (!isascii(c)) { if (c == MATCHREPL || c == MACROEXPAND) { putchar('$'); continue; } for (mp = MetaMacros; mp->metaname != '\0'; mp++) { if ((mp->metaval & 0377) == c) { printf("$%c", mp->metaname); break; } } if (mp->metaname != '\0') continue; (void) putchar('\\'); c &= 0177; } if (isprint(c)) { putchar(c); continue; } /* wasn't a meta-macro -- find another way to print it */ switch (c) { case '\0': continue; case '\n': c = 'n'; break; case '\r': c = 'r'; break; case '\t': c = 't'; break; default: (void) putchar('^'); (void) putchar(c ^ 0100); continue; } } (void) fflush(stdout);}/*** MAKELOWER -- Translate a line into lower case**** Parameters:** p -- the string to translate. If NULL, return is** immediate.**** Returns:** none.**** Side Effects:** String pointed to by p is translated to lower case.**** Called By:** parse*/makelower(p) register char *p;{ register char c; if (p == NULL) return; for (; (c = *p) != '\0'; p++) if (isascii(c) && isupper(c)) *p = tolower(c);}/*** BUILDFNAME -- build full name from gecos style entry.**** This routine interprets the strange entry that would appear** in the GECOS field of the password file.**** Parameters:** p -- name to build.** login -- the login name of this user (for &).** buf -- place to put the result.**** Returns:** none.**** Side Effects:** none.*/buildfname(gecos, login, buf) register char *gecos; char *login; char *buf;{ register char *p; register char *bp = buf; int l; if (*gecos == '*') gecos++; /* find length of final string */ l = 0; for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) { if (*p == '&') l += strlen(login); else l++; } /* now fill in buf */ for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) { if (*p == '&') { (void) strcpy(bp, login); *bp = toupper(*bp); while (*bp != '\0') bp++; } else *bp++ = *p; } *bp = '\0';}/*** SAFEFILE -- return true if a file exists and is safe for a user.**** Parameters:** fn -- filename to check.** uid -- user id to compare against.** gid -- group id to compare against.** uname -- user name to compare against (used for group** sets).** flags -- modifiers:** SFF_MUSTOWN -- "uid" must own this file.** SFF_NOSLINK -- file cannot be a symbolic link.** mode -- mode bits that must match.**** Returns:** 0 if fn exists, is owned by uid, and matches mode.** An errno otherwise. The actual errno is cleared.**** Side Effects:** none.*/#include <grp.h>#ifndef S_IXOTH# define S_IXOTH (S_IEXEC >> 6)#endif#ifndef S_IXGRP# define S_IXGRP (S_IEXEC >> 3)#endif#ifndef S_IXUSR# define S_IXUSR (S_IEXEC)#endifintsafefile(fn, uid, gid, uname, flags, mode) char *fn; uid_t uid; gid_t gid; char *uname; int flags; int mode;{ register char *p; register struct group *gr = NULL; struct stat stbuf; if (tTd(54, 4)) printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n", fn, uid, gid, flags, mode); errno = 0; for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/') { *p = '\0'; if (stat(fn, &stbuf) < 0) break; if (uid == 0 && !bitset(SFF_ROOTOK, flags)) { if (bitset(S_IXOTH, stbuf.st_mode)) continue; break; } if (stbuf.st_uid == uid && bitset(S_IXUSR, stbuf.st_mode)) continue; if (stbuf.st_gid == gid && bitset(S_IXGRP, stbuf.st_mode)) continue;#ifndef NO_GROUP_SET if (uname != NULL && ((gr != NULL && gr->gr_gid == stbuf.st_gid) || (gr = getgrgid(stbuf.st_gid)) != NULL)) { register char **gp; for (gp = gr->gr_mem; *gp != NULL; gp++) if (strcmp(*gp, uname) == 0) break; if (*gp != NULL && bitset(S_IXGRP, stbuf.st_mode)) continue; }#endif if (!bitset(S_IXOTH, stbuf.st_mode)) break; } if (p != NULL) { int ret = errno; if (ret == 0) ret = EACCES; if (tTd(54, 4)) printf("\t[dir %s] %s\n", fn, errstring(ret)); *p = '/'; return ret; }#ifdef HASLSTAT if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, &stbuf) : stat(fn, &stbuf)) < 0)#else if (stat(fn, &stbuf) < 0)#endif { int ret = errno; if (tTd(54, 4)) printf("\t%s\n", errstring(ret)); errno = 0; return ret; }#ifdef S_ISLNK if (bitset(SFF_NOSLINK, flags) && S_ISLNK(stbuf.st_mode)) { if (tTd(54, 4)) printf("\t[slink mode %o]\tEPERM\n", stbuf.st_mode); return EPERM; }#endif if (uid == 0 && !bitset(SFF_ROOTOK, flags)) mode >>= 6; else if (stbuf.st_uid != uid) { mode >>= 3; if (stbuf.st_gid == gid) ;#ifndef NO_GROUP_SET else if (uname != NULL && ((gr != NULL && gr->gr_gid == stbuf.st_gid) || (gr = getgrgid(stbuf.st_gid)) != NULL)) { register char **gp; for (gp = gr->gr_mem; *gp != NULL; gp++) if (strcmp(*gp, uname) == 0) break; if (*gp == NULL) mode >>= 3; }#endif else mode >>= 3; } if (tTd(54, 4)) printf("\t[uid %d, stat %o, mode %o] ", stbuf.st_uid, stbuf.st_mode, mode); if ((stbuf.st_uid == uid || stbuf.st_uid == 0 || !bitset(SFF_MUSTOWN, flags)) && (stbuf.st_mode & mode) == mode) { if (tTd(54, 4)) printf("\tOK\n"); return 0; } if (tTd(54, 4)) printf("\tEACCES\n"); return EACCES;}/*** FIXCRLF -- fix <CR><LF> in line.**** Looks for the <CR><LF> combination and turns it into the** UNIX canonical <NL> character. It only takes one line,** i.e., it is assumed that the first <NL> found is the end** of the line.**** Parameters:** line -- the line to fix.** stripnl -- if true, strip the newline also.**** Returns:** none.**** Side Effects:** line is changed in place.*/fixcrlf(line, stripnl) char *line; bool stripnl;{ register char *p; p = strchr(line, '\n'); if (p == NULL) return; if (p > line && p[-1] == '\r') p--; if (!stripnl) *p++ = '\n'; *p = '\0';}/*** DFOPEN -- determined file open**** This routine has the semantics of fopen, except that it will** keep trying a few times to make this happen. The idea is that** on very loaded systems, we may run out of resources (inodes,** whatever), so this tries to get around it.*/#ifndef O_ACCMODE# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)#endifstruct omodes{ int mask; int mode; char *farg;} OpenModes[] ={ O_ACCMODE, O_RDONLY, "r", O_ACCMODE|O_APPEND, O_WRONLY, "w", O_ACCMODE|O_APPEND, O_WRONLY|O_APPEND, "a", O_TRUNC, 0, "w+", O_APPEND, O_APPEND, "a+", 0, 0, "r+",};FILE *dfopen(filename, omode, cmode) char *filename; int omode; int cmode;{ register int tries; int fd; register struct omodes *om; struct stat st; for (om = OpenModes; om->mask != 0; om++) if ((omode & om->mask) == om->mode) break; for (tries = 0; tries < 10; tries++) { sleep((unsigned) (10 * tries)); errno = 0; fd = open(filename, omode, cmode); if (fd >= 0) break; switch (errno) { case ENFILE: /* system file table full */ case EINTR: /* interrupted syscall */#ifdef ETXTBSY case ETXTBSY: /* Apollo: net file locked */#endif continue; } break; } if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode)) { int locktype; /* lock the file to avoid accidental conflicts */ if ((omode & O_ACCMODE) != O_RDONLY) locktype = LOCK_EX; else locktype = LOCK_SH; (void) lockfile(fd, filename, NULL, locktype); errno = 0; } if (fd < 0) return NULL; else return fdopen(fd, om->farg);}/*** PUTLINE -- put a line like fputs obeying SMTP conventions**** This routine always guarantees outputing a newline (or CRLF,** as appropriate) at the end of the string.**** Parameters:** l -- line to put.** mci -- the mailer connection information.**** Returns:** none**** Side Effects:** output of l to fp.*/putline(l, mci) register char *l; register MCI *mci;{ register char *p; register char svchar; int slop = 0; /* strip out 0200 bits -- these can look like TELNET protocol */ if (bitset(MCIF_7BIT, mci->mci_flags)) { for (p = l; (svchar = *p) != '\0'; ++p) if (bitset(0200, svchar)) *p = svchar &~ 0200; } do { /* find the end of the line */ p = strchr(l, '\n'); if (p == NULL) p = &l[strlen(l)]; if (TrafficLogFile != NULL) fprintf(TrafficLogFile, "%05d >>> ", getpid()); /* check for line overflow */ while (mci->mci_mailer->m_linelimit > 0 && (p - l + slop) > mci->mci_mailer->m_linelimit) { register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1]; svchar = *q; *q = '\0'; if (l[0] == '.' && slop == 0 && bitnset(M_XDOT, mci->mci_mailer->m_flags)) { (void) putc('.', mci->mci_out); if (TrafficLogFile != NULL)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -