📄 deliver.c
字号:
usrerr(statmsg); } /* ** Final cleanup. ** Log a record of the transaction. Compute the new ** ExitStat -- if we already had an error, stick with ** that. Save the new envelope message unless it is temporary ** and we already have a permanent error. */ if (LogLevel > ((stat == 0 || stat == EX_TEMPFAIL) ? 3 : 2)) logdelivery(&statmsg[4]); if (stat != EX_TEMPFAIL) setstat(stat); if (stat != EX_OK && (stat != EX_TEMPFAIL || !bitset(EF_TIMEOUT|EF_FATALERRS, e->e_flags))) { if (e->e_message != NULL) free(e->e_message); e->e_message = newstr(&statmsg[4]); } errno = 0;}/*** LOGDELIVERY -- log the delivery in the system log**** Parameters:** stat -- the message to print for the status**** Returns:** none**** Side Effects:** none*/logdelivery(stat) char *stat;{ extern char *pintvl();# ifdef LOG if (AlreadyKnown) return; /* * Syslog should really check boundaries instead but it doesn't. */#define MAX_TO_SYSLOG 800 if (strlen(CurEnv->e_to) > MAX_TO_SYSLOG) CurEnv->e_to[MAX_TO_SYSLOG] = '\0'; syslog(LOG_INFO, "%s: to=%s, delay=%s, stat=%s", CurEnv->e_id, CurEnv->e_to, pintvl(curtime() - CurEnv->e_ctime, TRUE), stat);# endif LOG}/*** PUTFROMLINE -- output a UNIX-style from line (or whatever)**** This can be made an arbitrary message separator by changing $l**** One of the ugliest hacks seen by human eyes is contained herein:** UUCP wants those stupid "remote from <host>" lines. Why oh why** does a well-meaning programmer such as myself have to deal with** this kind of antique garbage????**** Parameters:** fp -- the file to output to.** m -- the mailer describing this entry.**** Returns:** none**** Side Effects:** outputs some text to fp.*/putfromline(fp, m) register FILE *fp; register MAILER *m;{ char *template = "\001l\n"; char buf[MAXLINE]; if (bitnset(M_NHDR, m->m_flags)) return;# ifdef UGLYUUCP if (bitnset(M_UGLYUUCP, m->m_flags)) { char *bang; char xbuf[MAXLINE]; expand("\001g", buf, &buf[sizeof buf - 1], CurEnv); bang = index(buf, '!'); if (bang == NULL) { bang = index(buf, '@'); if (bang == NULL) (void) sprintf(xbuf, "From %s \001d remote from \001w\n", buf); else { *bang++ = '\0'; (void) sprintf(xbuf, "From %s!%s \001d remote from \001w\n", bang, buf); } } else { *bang++ = '\0'; (void) sprintf(xbuf, "From %s \001d remote from %s\n", bang, buf); } template = xbuf; }# endif UGLYUUCP expand(template, buf, &buf[sizeof buf - 1], CurEnv); putline(buf, fp, m);}/*** PUTBODY -- put the body of a message.**** Parameters:** fp -- file to output onto.** m -- a mailer descriptor to control output format.** e -- the envelope to put out.**** Returns:** none.**** Side Effects:** The message is written onto fp.*/putbody(fp, m, e) FILE *fp; MAILER *m; register ENVELOPE *e;{ char buf[MAXLINE]; /* ** Output the body of the message */ if (e->e_dfp == NULL) { if (e->e_df != NULL) { e->e_dfp = fopen(e->e_df, "r"); if (e->e_dfp == NULL) syserr("Cannot open %s", e->e_df); } else putline("<<< No Message Collected >>>", fp, m); } if (e->e_dfp != NULL) { rewind(e->e_dfp); while (!ferror(fp) && fgets(buf, sizeof buf, e->e_dfp) != NULL) { if (buf[0] == 'F' && bitnset(M_ESCFROM, m->m_flags) && strncmp(buf, "From", 4) == 0) (void) putc('>', fp);#ifdef INTER putline_8bit(buf, fp, m);#else putline(buf, fp, m);#endif } if (ferror(e->e_dfp)) { syserr("putbody: read error"); ExitStat = EX_IOERR; } } (void) fflush(fp); if (ferror(fp) && errno != EPIPE) { syserr("putbody: write error"); ExitStat = EX_IOERR; } errno = 0;}/*** MAILFILE -- Send a message to a file.**** If the file has the setuid/setgid bits set, but NO execute** bits, sendmail will try to become the owner of that file** rather than the real user. Obviously, this only works if** sendmail runs as root.**** This could be done as a subordinate mailer, except that it** is used implicitly to save messages in ~/dead.letter. We** view this as being sufficiently important as to include it** here. For example, if the system is dying, we shouldn't have** to create another process plus some pipes to save the message.**** Parameters:** filename -- the name of the file to send to.** ctladdr -- the controlling address header -- includes** the userid/groupid to be when sending.**** Returns:** The exit code associated with the operation.**** Side Effects:** none.*/mailfile(filename, ctladdr) char *filename; ADDRESS *ctladdr;{ register FILE *f; register int pid; ENVELOPE *e = CurEnv; /* ** Fork so we can change permissions here. ** Note that we MUST use fork, not vfork, because of ** the complications of calling subroutines, etc. ** Also, make sure the body file is open BEFORE we setuid to someone ** besides root. */ if (CurEnv->e_dfp == NULL && CurEnv->e_df != NULL) CurEnv->e_dfp = fopen(CurEnv->e_df, "r");# ifdef SIGCHLD (void) signal(SIGCHLD, SIG_DFL);# endif SIGCHLD DOFORK(fork); if (pid < 0) return (EX_OSERR); else if (pid == 0) { /* child -- actually write to file */ struct stat stb; (void) signal(SIGINT, SIG_DFL); (void) signal(SIGHUP, SIG_DFL); (void) signal(SIGTERM, SIG_DFL); (void) umask(OldUmask); if (stat(filename, &stb) < 0) { errno = 0; stb.st_mode = 0666; } if (bitset(0111, stb.st_mode)) exit(EX_CANTCREAT); if (ctladdr == NULL) ctladdr = &CurEnv->e_from; if (!bitset(S_ISGID, stb.st_mode) || setgid(stb.st_gid) < 0) { if (ctladdr->q_uid == 0) { (void) setgid(DefGid); (void) initgroups(DefUser, DefGid); } else { (void) setgid(ctladdr->q_gid); (void) initgroups(ctladdr->q_ruser? ctladdr->q_ruser: ctladdr->q_user, ctladdr->q_gid); } } if (!bitset(S_ISUID, stb.st_mode) || setuid(stb.st_uid) < 0) { if (ctladdr->q_uid == 0) (void) setuid(DefUid); else (void) setuid(ctladdr->q_uid); } f = dfopen(filename, "a"); if (f == NULL) exit(EX_CANTCREAT); putfromline(f, ProgMailer); (*CurEnv->e_puthdr)(f, ProgMailer, CurEnv); putline("\n", f, ProgMailer); (*CurEnv->e_putbody)(f, ProgMailer, CurEnv); putline("\n", f, ProgMailer); (void) fclose(f); (void) fflush(stdout); /* reset ISUID & ISGID bits for paranoid systems */ (void) chmod(filename, (int) stb.st_mode); exit(EX_OK); /*NOTREACHED*/ } else { /* parent -- wait for exit status */ int st; st = waitfor(pid); if ((st & 0377) != 0) return (EX_UNAVAILABLE); else return ((st >> 8) & 0377); /*NOTREACHED*/ }}/*** SENDALL -- actually send all the messages.**** Parameters:** e -- the envelope to send.** mode -- the delivery mode to use. If SM_DEFAULT, use** the current SendMode.**** Returns:** none.**** Side Effects:** Scans the send lists and sends everything it finds.** Delivers any appropriate error messages.** If we are running in a non-interactive mode, takes the** appropriate action.*/sendall(e, mode) ENVELOPE *e; char mode;{ register ADDRESS *q; bool oldverbose; int pid; int checkpointCount;#ifdef DEBUG if (tTd(13, 1)) { printf("\nSENDALL: mode %c, sendqueue:\n", mode); printaddr(e->e_sendqueue, TRUE); }#endif DEBUG /* ** Do any preprocessing necessary for the mode we are running. ** Check to make sure the hop count is reasonable. ** Delete sends to the sender in mailing lists. */ CurEnv = e; if (e->e_hopcount > Maxhop) { extern char Arpa_Usrerr[]; e->e_to = e->e_sendqueue->q_paddr; message(Arpa_Usrerr,"Mail loop detected"); syserr("sendall: too many hops (%d max)", Maxhop); return; } if (!MeToo) { extern ADDRESS *recipient(); e->e_from.q_flags |= QDONTSEND; (void) recipient(&e->e_from, &e->e_sendqueue); } if (SendMode == SM_VERIFY) { mode = SM_VERIFY; }# ifdef QUEUE else if (!bitset(EF_INQUEUE, e->e_flags)) { /* * Add in extra penalty points for messages to lots of * recipients (like big mailing lists). */ checkpointCount = 0; for (q = e->e_sendqueue; q != NULL; q = q->q_next) { if (!bitset(QDONTSEND, q->q_flags)) checkpointCount++; } e->e_msgpriority += WKRECIPFACT*checkpointCount; /* * Determine actual delivery mode: * if we are over the threshold and the message is too "big" * then we just queue it up. * If message is too "small" then complain. * Messages with no recipients are an error. */ if (RecipThreshold > 0 && checkpointCount > RecipThreshold && e->e_bodysize < REJECT_MIN) { syserr("Too many recipients for no message body"); return; } if (checkpointCount == 0 && !ForceMail && mode != SM_DELIVER) { e->e_to = e->e_sendqueue->q_paddr; message(Arpa_Usrerr,"Possible alias loop"); syserr("No valid recipients"); return; } if (mode == SM_DEFAULT) { if (shouldqueue(e->e_msgpriority)) mode = SM_QUEUE; else mode = SendMode; } queueup(e, TRUE, mode == SM_QUEUE); }#endif QUEUE oldverbose = Verbose; switch (mode) { case SM_VERIFY: Verbose = TRUE; break; case SM_QUEUE: e->e_flags |= EF_INQUEUE|EF_KEEPQUEUE; return; case SM_FORK: if (e->e_xfp != NULL) (void) fflush(e->e_xfp); pid = fork(); if (pid < 0) { mode = SM_DELIVER; break; } else if (pid > 0) { /* be sure we leave the temp files to our child */ e->e_id = e->e_df = NULL; return; } /* double fork to avoid zombies */ if (fork() > 0) exit(EX_OK); /* be sure we are immune from the terminal */ disconnect(FALSE); break; } /* ** Run through the list and send everything. */ checkpointCount = 0; for (q = e->e_sendqueue; q != NULL; q = q->q_next) { if (mode == SM_VERIFY) { e->e_to = q->q_paddr; if (!bitset(QDONTSEND|QBADADDR, q->q_flags)) message(Arpa_Info, "deliverable"); } else (void) deliver(e, q); /* * Checkpoint the queue every once in a while, * so we don't get a lot of duplicate messages * if we crash during delivery of a big list. */ if (++checkpointCount >= CheckPointLimit) { checkpoint(e,q->q_next); checkpointCount = 0; } } Verbose = oldverbose; /* ** Now run through and check for errors. */ if (mode == SM_VERIFY) return; for (q = e->e_sendqueue; q != NULL; q = q->q_next) { register ADDRESS *qq;# ifdef DEBUG if (tTd(13, 3)) { printf("Checking "); printaddr(q, FALSE); }# endif DEBUG /* only send errors if the message failed */ if (!bitset(QBADADDR|QQUEUEUP, q->q_flags)) continue; /* we have an address that failed -- find the parent */ for (qq = q; qq != NULL; qq = qq->q_alias) { char obuf[MAXNAME + 6]; extern char *aliaslookup(); /* we can only have owners for local addresses */ if (!bitnset(M_LOCAL, qq->q_mailer->m_flags)) continue; /* see if the owner list exists */ (void) strcpy(obuf, "owner-"); if (strncmp(qq->q_user, "owner-", 6) == 0) (void) strcat(obuf, "owner"); else (void) strcat(obuf, qq->q_user); if (aliaslookup(obuf) == NULL) continue;# ifdef DEBUG if (tTd(13, 4)) printf("Errors to %s\n", obuf);# endif DEBUG /* owner list exists -- add it to the error queue */ sendtolist(obuf, (ADDRESS *) NULL, &e->e_errorqueue); ErrorMode = EM_MAIL; break; } } if (mode == SM_FORK) finis();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -