static char *rewrite_env_sender(const char *from) { const char *host; if (!from) { if ((from=env("MAILSUSER")) == 0 && (from=env("MAILUSER")) == 0 && (from=env("LOGNAME")) == 0 && (from=env("USER")) == 0) { struct passwd *pw=mypwd(); from=pw ? pw->pw_name:"nobody"; } if ((host=env("MAILSHOST")) != 0 || (host=env("MAILHOST")) != 0) { char *p=courier_malloc(strlen(from)+strlen(host)+2); return (strcat(strcat(strcpy(p, from), "@"), host)); } } return (strcpy(courier_malloc(strlen(from)+1), from)); }
static char *sts_download(const char *domain) { char *url=courier_malloc(strlen(domain)+100); char *policy; char *v; struct sts_id temp_sts; if (!url) return 0; sprintf(url, "https://mta-sts.%s/.well-known/mta-sts.txt", domain); policy=sts_download1(url); free(url); memset(&temp_sts, 0, sizeof(temp_sts)); temp_sts.policy=policy; v=find_in_policy(&temp_sts, "version"); if (!v || strcmp(v, "STSv1")) { free(policy); policy=0; } return policy; }
static void start_child(unsigned i, char *line, char **cols) { int pipebuf[2]; const char *hostp; size_t hostplen; pid_t pid; hostp=MODULEDEL_HOST(cols); for (hostplen=0; hostp[hostplen] && hostp[hostplen] != '\t'; hostplen++) ; if (info[i].host) free(info[i].host); memcpy(info[i].host=courier_malloc(hostplen+1), hostp, hostplen); info[i].host[hostplen]=0; if (pipe(pipebuf) < 0) clog_msg_errno(); pid=module_fork_noblock(0, &i); if (pid == 0) { unsigned j; char buf[MAXLONGSIZE+1]; char *p; dup2(pipebuf[0], 0); close(pipebuf[0]); close(pipebuf[1]); fclose(childresultpipe); dup2(completionpipe[1], 1); close(completionpipe[1]); close(completionpipe[0]); for (j=0; j<module_nchildren; j++) if (info[j].cmdpipe >= 0) close(info[j].cmdpipe); for (j=0; MODULEDEL_HOST(cols)[j]; j++) if (MODULEDEL_HOST(cols)[j] == '\t') { MODULEDEL_HOST(cols)[j]=0; break; } p=buf+MAXLONGSIZE; *p=0; do { * --p = '0' + (i % 10); } while ( (i=i / 10) != 0); execl("courieresmtp", "courieresmtp", p, MODULEDEL_HOST(cols), (char *)0); clog_msg_errno(); _exit(1); } info[i].pid=pid; close (pipebuf[0]); info[i].cmdpipe=pipebuf[1]; }
int AliasSearch::Search(const char *address, AliasHandler &h) { char *p=0; const char *q; char *hostdomain=0; q=strrchr(address, '@'); /* islocal checks both local domains, and hosted domains, ** we want to replace domain with 'me' only for local domains. */ if (!q || (config_islocal(q+1, &hostdomain) && hostdomain == 0)) /* ** Make sure we look for aliases for ** "me" */ { const char *me=config_defaultdomain(); char *r; if (!q) q=address+strlen(address); p=(char *)courier_malloc(q-address+2+strlen(me)); if (!p) clog_msg_errno(); memcpy(p, address, q-address); strcat(strcpy(&p[q-address], "@"), me); r=udomainlower(p); /* For stupid people */ free(p); p=r; address=p; } if (hostdomain) free(hostdomain); if (Try(module_record, address, h) == 0 && TryVirtual(module_record, address, &h) == 0 && Try(local_record, address, h) == 0 && TryVirtual(local_record, address, &h) == 0) { int rc; #if HAVE_LDAP if (p) *strrchr(p, '@')=0; // Strip local domain from addresses passed to aliasd rc=TryAliasD(address, &h); #else rc=1; #endif if (p) free(p); return (rc); } if (p) free(p); return (0); }
static char *get_gecos() { struct passwd *pw=mypwd(); char *p, *q; if (!pw || !pw->pw_gecos) return (0); p=strcpy(courier_malloc(strlen(pw->pw_gecos)+1), pw->pw_gecos); if ((q=strchr(p, ',')) != 0) *q=0; return (p); }
void qmsgunlink(ino_t n, time_t rightnow) { const char *bigbrother=getenv("ARCHIVEDIR"); if (bigbrother && *bigbrother) { struct tm *tmptr=localtime(&rightnow); /* Archive this message */ if (tmptr) { const char *dname=qmsgsdatname(n); const char *tname=strrchr(dname, '/'); char *p=courier_malloc(strlen(bigbrother)+ strlen(tname)+40); sprintf(p, "%s/%04d.%02d.%02d%s", bigbrother, tmptr->tm_year + 1900, tmptr->tm_mon+1, tmptr->tm_mday, tname); if (bigbrother_create_rename(dname, p) == 0) { dname=qmsgsctlname(n); strcpy(strrchr(p, '/'), strrchr(dname, '/')); if (rename(dname, p) == 0) { free(p); return; } } clog_msg_start_err(); clog_msg_str("Unable to move "); clog_msg_str(dname); clog_msg_str(" to "); clog_msg_str(p); clog_msg_send(); clog_msg_prerrno(); free(p); } } unlink(qmsgsdatname(n)); unlink(qmsgsctlname(n)); }
char *ulocallower(const char *c) { static int islocallower=0; if (islocallower == 0) { char *s=config_localfilename("locallowercase"); islocallower = access(s, 0) == 0 ? 1:-1; free(s); } if ( islocallower > 0 || ( #if HAVE_STRNCASECMP strncasecmp(c, "postmaster", 10) #else strnicmp(c, "postmaster", 10) #endif == 0 && (c[10] == '\0' || c[10] == '@'))) { char *c_copy=courier_strdup(c); char *at=strchr(c_copy, '@'); char save; char *lower_username; if (!at) at=c_copy+strlen(c_copy); save=*at; *at=0; lower_username= unicode_convert_tocase(c_copy, "utf-8", unicode_lc, unicode_lc); *at=save; if (lower_username) { char *buf=courier_malloc(strlen(lower_username)+ strlen(at)+1); strcat(strcpy(buf, lower_username), at); free(c_copy); free(lower_username); return buf; } free(c_copy); } return courier_strdup(c); }
static void add2header(const char *p, unsigned l, struct rwmsginfo *msginfo) { if (l + msginfo->headerbuflen > msginfo->headerbufsize) { unsigned ll=(msginfo->headerbufsize + l + 1023) & ~1023; char *buf=courier_malloc(ll); if (msginfo->headerbufsize) memcpy(buf, msginfo->headerbuf, msginfo->headerbufsize); if (msginfo->headerbuf) free(msginfo->headerbuf); msginfo->headerbuf=buf; msginfo->headerbufsize=ll; } memcpy(msginfo->headerbuf+msginfo->headerbuflen, p, l); msginfo->headerbuflen += l; }
char *config_dsnfrom() { char *f=config_search("dsnfrom"); char *p=config_read1l(f); static const char defaultdsnfrom[]="\"Courier mail server at %s\" <@>"; free(f); if (!p) { const char *me=config_me(); p=courier_malloc(sizeof(defaultdsnfrom)+strlen(me)); sprintf(p, defaultdsnfrom, me); } return (p); }
static void do_rw_rewrite_chksyn_print(struct rw_info *info, struct rfc822token *t) { if (rw_syntaxchk(t)) { static const char errmsg[]="Syntax error: "; char *addr=rfc822_gettok(info->ptr); char *buf; if (!addr) clog_msg_errno(); buf=courier_malloc(strlen(addr)+sizeof(errmsg)); strcat(strcpy(buf, errmsg), addr); free(addr); (*info->err_func)(553, buf, info); free(buf); } else rw_rewrite_print(info); }
static char *policy_filename_for_domain(const char *domain) { char *filename; char *p; if (strchr(domain, '/')) return 0; if ((filename=courier_malloc(strlen(domain)+sizeof(STSDIR "/"))) == NULL) return 0; strcat(strcpy(filename, STSDIR "/"), domain); for (p=filename+sizeof(STSDIR); *p; ++p) if (*p >= 'A' && *p <= 'Z') *p += 'a'-'A'; return filename; }
char *config_dsnfrom() { char *f=config_localfilename("dsnfrom"); char *p=config_read1l(f); static const char defaultdsnfrom[]="Courier mail server at %s <@>"; free(f); if (!p) { const char *me=config_me(); char *me_encoded=rfc2047_encode_str(me, RFC2045CHARSET, rfc2047_qp_allow_word); if (me_encoded) me=me_encoded; p=courier_malloc(sizeof(defaultdsnfrom)+strlen(me)); sprintf(p, defaultdsnfrom, me); if (me_encoded) free(me_encoded); } return (p); }
char *udomainlower(const char *c) { const char *d; char *ud; char *s; d=strchr(c, '@'); if (!d) return courier_strdup(c); ud=unicode_convert_tocase(++d, "utf-8", unicode_lc, unicode_lc); if (!ud) return courier_strdup(c); s=courier_malloc(strlen(ud)+1+(d-c)); memcpy(s, c, d-c); strcpy(s+(d-c), ud); free(ud); return s; }
static char *udomainace2(const char *c) { const char *d; char *ud; char *s; d=strchr(c, '@'); if (!d) return courier_strdup(c); ++d; if (idna_to_ascii_8z(d, &s, 0) != IDNA_SUCCESS) { return courier_strdup(c); } ud=courier_malloc((d-c+1)+strlen(s)); memcpy(ud, c, d-c); strcpy(ud+(d-c), s); free(s); return ud; }
char *config_localfilename(const char *p) { char *c=courier_malloc(sizeof(SYSCONFDIR "/")+strlen(p)); return (strcat(strcpy(c, SYSCONFDIR "/"), p)); }
static void dofwd(const char *addr, const char *from, const char *fromuser, const char *origuser) { if (!submit_started) { char *args[5]; static const char *envvars[]={ "DSNNOTIFY", "DSNRET", "NOADDATE", "NOADDMSGID", "MIME", 0}; char *envs[sizeof(envvars)/sizeof(envvars[0])]; int i, j; for (i=j=0; envvars[i]; i++) { const char *p=getenv(envvars[i]); char *q; if (!p) continue; q=strcat(strcat(strcpy(courier_malloc( strlen(envvars[i])+strlen(p)+2), envvars[i]), "="), p); envs[j]=q; j++; } envs[j]=0; args[0]="submit"; args[1]="local"; args[2]="dns; localhost (localhost [127.0.0.1])"; args[3]=strcat(strcpy(courier_malloc( sizeof("forwarded by ")+strlen(fromuser)), "forwarded by "), fromuser); args[4]=0; errptr=errbuf; errleft=sizeof(errbuf)-1; save_submit_errmsg("The following error occured when trying to forward this message: \n"); strcpy(errptr, "UNKNOWN ERROR - no further description is available"); if (submit_fork(args, envs, save_submit_errmsg) || (submit_write_message(from), submit_readrcprinterr())) { submit_err=1; return; } while (j) { free(envs[--j]); } submit_err=0; submit_started=1; } if (!submit_err) { char *p; p=strcat(strcat(strcpy(courier_malloc( strlen(addr)+strlen(origuser)+4), addr), "\tF\t"), origuser); submit_write_message(p); free(p); if (submit_readrcprinterr()) { submit_err=1; } } }
static int sts_policy_override2(struct sts_id *id, const char *domain, const char *policy_filename, FILE *cached_policy_fp, int readwrite, void *mode_ptr) { char *new_policy; if (!cached_policy_fp || load(id, cached_policy_fp)) { printf("%s: cached policy not found.\n", domain); } else if (!readwrite) { printf("%s: no write permission.\n", policy_filename); } else if ((new_policy=courier_malloc(strlen(id->policy)+ strlen(domain)+1024)) != 0) { char *put_p=new_policy; const char *p; char lastc='\n'; int skipping=0; for (p=id->policy; *p; p++) { if (lastc == '\n') skipping=strncmp(p, "mode:", 5) == 0 || strncmp(p, "info:", 5) == 0; if (!skipping) *put_p++=*p; lastc=*p; } if (lastc != '\n') *put_p++='\n'; sprintf(put_p, "info: this policy has been manually overridden, " "as follows\n" "info: use \"testmxlookup --sts-purge %s\" to " "restore the default policy:\n", domain); switch (*(enum sts_mode *)mode_ptr) { case sts_mode_none: strcat(put_p, "mode: none\n"); break; case sts_mode_testing: strcat(put_p, "mode: testing\n"); break; case sts_mode_enforce: strcat(put_p, "mode: enforce\n"); break; } free(id->policy); id->policy=new_policy; save(id, cached_policy_fp); } return 0; }
int main(int argc, char **argv) { if (argc > 1 && strcmp(argv[1], "--version") == 0) { printf("%s\n", COURIER_COPYRIGHT); exit(0); } if (chdir(courierdir())) { perror("chdir"); return (1); } if (argc < 2) return (0); if (strcmp(argv[1], "stop") == 0) { int fd; trigger(TRIGGER_STOP); /* Wait until the exclusive lock goes away: */ signal(SIGHUP, SIG_DFL); if ((fd=open(TMPDIR "/courierd.lock", O_RDWR|O_CREAT, 0600)) < 0) clog_msg_errno(); alarm(15); /* But abort after 15 seconds. */ ll_lock_ex(fd); return (0); } if (strcmp(argv[1], "restart") == 0) { trigger(TRIGGER_RESTART); return (0); } if (strcmp(argv[1], "flush") == 0) { ino_t n; struct ctlfile ctf; if (getuid()) { /* ** We'll fail trying to open the pipe anyway, but let's ** give a meaningful error message now. */ fprintf(stderr, "courier flush can be executed only by the superuser.\n"); exit(1); } if (argc < 3) { trigger(TRIGGER_FLUSH); /* Everything */ exit(0); } if (comparseqid(argv[2], &n) == 0 && ctlfile_openi(n, &ctf, 1) == 0) { int c=ctlfile_searchfirst(&ctf, COMCTLFILE_MSGID); if (c >= 0 && strcmp(ctf.lines[c]+1, argv[2]) == 0) { char *s=courier_malloc(sizeof(TRIGGER_FLUSHMSG)+1+ strlen(argv[2])); strcat(strcat(strcpy(s, TRIGGER_FLUSHMSG), argv[2]), "\n"); trigger(s); ctlfile_close(&ctf); return (0); } ctlfile_close(&ctf); } fprintf(stderr, "No such message.\n"); exit(1); return (1); } /* Might as well... */ if (strcmp(argv[1], "start") == 0) { pid_t p; int waitstat; char dummy; /* ** Ok, courierd will close file descriptor 3 when it starts, so we ** put a pipe on there, and wait for it to close. */ int pipefd[2]; close(3); if (open("/dev/null", O_RDONLY) != 3 || pipe(pipefd)) { fprintf(stderr, "Cannot open pipe\n"); exit(1); } if (getuid()) { /* ** We'll fail trying to execute courierd anyway, but let's ** give a meaningful error message now. */ fprintf(stderr, "courier start can be executed only by the superuser.\n"); return (1); } signal(SIGCHLD, SIG_DFL); while ((p=fork()) == -1) { perror("fork"); sleep(10); } if (p == 0) { dup2(pipefd[1], 3); close(pipefd[1]); close(pipefd[0]); while ((p=fork()) == -1) { perror("fork"); sleep(10); } if (p == 0) { /* ** stdin from the bitbucket. stdout to ** /dev/null, or the bitbucket. */ signal(SIGHUP, SIG_IGN); close(0); open("/dev/null", O_RDWR); dup2(0, 1); dup2(0, 2); /* See if we can disconnect from the terminal */ #ifdef TIOCNOTTY { int fd=open("/dev/tty", O_RDWR); if (fd >= 0) { ioctl(fd, TIOCNOTTY, 0); close(fd); } } #endif /* Remove any process groups */ #if HAVE_SETPGRP #if SETPGRP_VOID setpgrp(); #else setpgrp(0, 0); #endif #endif execl( DATADIR "/courierctl.start", "courierctl.start", (char *)0); perror("exec"); _exit(1); } _exit(0); } close(pipefd[1]); while (wait(&waitstat) != p) ; if (read(pipefd[0], &dummy, 1) < 0) ; /* ignore */ close(pipefd[0]); close(3); return (0); } if (strcmp(argv[1], "clear") == 0 && argc > 2) { libmail_changeuidgid(MAILUID, MAILGID); if (strcmp(argv[2], "all") == 0) { courier_clear_all(); } else { track_save(argv[2], TRACK_ADDRACCEPTED); printf("%s cleared.\n", argv[2]); } return (0); } if (argc > 2 && strcmp(argv[1], "show") == 0 && strcmp(argv[2], "all") == 0) { libmail_changeuidgid(MAILUID, MAILGID); courier_show_all(); } return (0); }
static void rewrite_headers(const char *From) { int seen_from=0; char headerbuf[5000]; int c, i; const char *mailuser, *mailuser2, *mailhost; char *p; char *pfrom=From ? strcpy(courier_malloc(strlen(From)+1), From):0; if ((mailuser=env("MAILUSER")) == 0 && (mailuser=env("LOGNAME")) == 0) mailuser=env("USER"); mailuser2=env("MAILUSER"); mailhost=env("MAILHOST"); while (fgets(headerbuf, sizeof(headerbuf), stdin)) { char *p=strchr(headerbuf, '\n'); if (p) { *p=0; if (p == headerbuf || strcmp(headerbuf, "\r") == 0) break; } #if HAVE_STRNCASECMP if (strncasecmp(headerbuf, "from:", 5)) #else if (strnicmp(headerbuf, "from:", 5)) #endif { fprintf(submit_to, "%s", headerbuf); if (!p) while ((c=getchar()) != EOF && c != '\n') putc(c, submit_to); putc('\n', submit_to); continue; } if (!p) while ((c=getchar()) != EOF && c != '\n') ; /* I don't care */ if (seen_from) continue; /* Screwit */ seen_from=1; i=strlen(headerbuf); for (;;) { c=getchar(); if (c != EOF) ungetc(c, stdin); if (c == EOF || c == '\r' || c == '\n') break; if (!isspace((int)(unsigned char)c)) break; while ((c=getchar()) != EOF && c != '\n') { if (i < sizeof(headerbuf)-1) headerbuf[i++]=c; } headerbuf[i]=0; } p=rewrite_from(headerbuf+5, mailuser2, mailhost, pfrom); fprintf(submit_to, "From: %s\n", p); free(p); } if (!seen_from) { if (!mailuser) { struct passwd *pw=mypwd(); mailuser=pw ? pw->pw_name:"nobody"; } if (!pfrom) { if ( !(From=env("MAILNAME")) && !(From=env("NAME"))) { pfrom=get_gecos(); } else pfrom=strcpy(courier_malloc(strlen(From)+1), From); } p=rewrite_from(NULL, mailuser, mailhost, pfrom); fprintf(submit_to, "From: %s\n", p); free(p); } putc('\n', submit_to); if (pfrom) free(pfrom); }
static char *rewrite_from(const char *oldfrom, const char *newuser, const char *newhost, const char *newname) { struct rfc822t *rfct; struct rfc822a *rfca; struct rfc822t *usert, *hostt, *namet; struct rfc822token attoken, **tp; char *p; const char *q; char *gecosname=0; if (!oldfrom) { char *p=courier_malloc( (newuser ? strlen(newuser):0)+ (newhost ? strlen(newhost):0)+4); strcpy(p, "<"); if (newuser) strcat(p, newuser); if (newuser && newhost) strcat(strcat(p, "@"), newhost); strcat(p, ">"); if (newname) { char *q, *r; namet=tokenize_name(newname); q=rfc822_gettok(namet->tokens); rfc822t_free(namet); r=courier_malloc(strlen(p)+strlen(q)+2); strcat(strcat(strcpy(r, q), " "), p); free(p); p=r; free(q); } return (p); } if ((rfct=rfc822t_alloc_new(oldfrom, NULL, NULL)) == 0 || (rfca=rfc822a_alloc(rfct)) == 0) { clog_msg_errno(); return(0); } if ((q=env("MAILNAME")) || (q=env("NAME"))) newname=q; if (!newname && rfca->naddrs == 0) newname=gecosname=get_gecos(); if ((rfca->naddrs == 0 || rfca->addrs[0].tokens == 0) && newuser == 0) { struct passwd *pw=mypwd(); if (pw) newuser=pw->pw_name; } namet=newname ? tokenize_name(newname):0; usert=newuser ? rw_rewrite_tokenize(newuser):0; hostt=newhost ? rw_rewrite_tokenize(newhost):0; if (rfca->naddrs == 0 || rfca->addrs[0].tokens == 0) { struct rfc822addr a; struct rfc822a fakea; if (hostt) { struct rfc822token *t; attoken.token='@'; attoken.next=hostt->tokens; attoken.ptr=0; attoken.len=0; for (t=usert->tokens; t->next; t=t->next) ; t->next=&attoken; } fakea.naddrs=1; fakea.addrs= &a; if (!namet) namet=tokenize_name(""); if (!usert) usert=rw_rewrite_tokenize(""); a.name=namet->tokens; a.tokens=usert->tokens; p=rfc822_getaddrs(&fakea); } else { struct rfc822token *t, *u; rfca->naddrs=1; if (usert) { for (t=rfca->addrs[0].tokens; t; t=t->next) if (t->token == '@') break; for (u=usert->tokens; u->next; u=u->next) ; u->next=t; rfca->addrs[0].tokens=usert->tokens;; } if (hostt && rfca->addrs[0].tokens) { for (tp= &rfca->addrs[0].tokens; *tp; tp= &(*tp)->next) if ( (*tp)->token == '@') break; *tp=&attoken; attoken.token='@'; attoken.next=hostt->tokens; attoken.ptr=0; attoken.len=0; } if (namet) rfca->addrs[0].name=namet->tokens; p=rfc822_getaddrs(rfca); } if (!p) clog_msg_errno(); if (usert) rfc822t_free(usert); if (hostt) rfc822t_free(hostt); if (namet) rfc822t_free(namet); rfc822t_free(rfct); rfc822a_free(rfca); if (gecosname) free(gecosname); return (p); }
static char *fetch_smtproutes(const char *domain) { char *buf=get_control_smtproutes(); const char *p=buf; if (!buf) { struct dbobj d; char *p, *q; size_t l; p=config_search("esmtproutes.dat"); dbobj_init(&d); if (dbobj_open(&d, p, "R")) { free(p); return (0); } free(p); p=strcpy(courier_malloc(strlen(domain)+1), domain); for (q=p; *q; q++) *q=tolower(*q); q=dbobj_fetch(&d, p, strlen(p), &l, "D"); free(p); dbobj_close(&d); if (!q) return (0); p=courier_malloc(l+1); memcpy(p, q, l); p[l]=0; free(q); return (p); } while (*p) { unsigned i; for (i=0; p[i] && p[i] != '\n' && p[i] != '\r' && p[i] != ':'; ++i) ; if (p[i] == ':' && (i == 0 || config_domaincmp(domain, p, i) == 0)) { char *q; p += i; ++p; for (i=0; p[i] && p[i] != '\n' && p[i] != '\r'; i++) ; while (i && isspace((int)(unsigned char)p[i-1])) --i; while (i && isspace((int)(unsigned char)*p)) { ++p; --i; } if (i == 0) { free(buf); return (0); } q=courier_malloc(i+1); memcpy(q, p, i); q[i]=0; free(buf); return (q); } while (p[i]) { if (p[i] == '\n' || p[i] == '\r') { ++i; break; } ++i; } p += i; } free(buf); return (0); }
static int load2(struct sts_id *id, FILE *fp) { char buffer[1024]; char *p; long pos; struct stat stat_buf; size_t s; if (!fgets(buffer, sizeof(buffer), fp) || strcmp(buffer, "1\n")) return -1; if (id->id) { free(id->id); id->id=0; } id->tempfail=0; while (fgets(buffer, sizeof(buffer), fp)) { if (strcmp(buffer, "\n") == 0) break; if (strcmp(buffer, "tempfail") == 0) { id->tempfail=1; } if (strcmp(strtok(buffer, ": "), "id")) continue; if ((p=strtok(NULL, ": \r\n")) == 0) p=""; if ((id->id=courier_strdup(p)) == 0) break; } if (!id->id) return -1; while (strcmp(buffer, "\n")) { if (fgets(buffer, sizeof(buffer), fp) == 0) return -1; } if ((pos=ftell(fp)) < 0 || fstat(fileno(fp), &stat_buf) < 0) return -1; s=stat_buf.st_size-pos; if ((id->policy=courier_malloc(s+1)) == NULL || fread(id->policy, s, 1, fp) != 1) return -1; id->policy[s]=0; id->timestamp=stat_buf.st_mtime; id->expiration=id->timestamp+max_age(id); return 0; }
int main(int argc, char **argv) { const char *from=0; const char *From=0; const char *ret=0, *dsn=0, *security=0, *envid=0; int argn, argp; int errflag=0; int c; char *args[6]; char *envs[7]; int envp; int tostdout=0; char frombuf[NUMBUFSIZE+30]; int doverp=0; int bcconly=0; char ubuf[NUMBUFSIZE]; char *uucprmail=0; char *s; char ret_buf[2], security_buf[40]; int submit_errcode; /* ** Immediately drop uid, force GID to MAILGID ** The reason we can't just setgid ourselves to MAILGID is because ** that only sets the effective uid. We need to also set both ** real and effective GIDs, otherwise maildrop filtering will fail, ** because the real gid won't be trusted. */ setgid(MAILGID); setuid(getuid()); signal(SIGCHLD, SIG_DFL); signal(SIGPIPE, SIG_IGN); argn=1; putenv("AUTHMODULES="); /* See module.local/local.c */ putenv("LANG=en_US"); putenv("CHARSET=iso-8859-1"); putenv("MM_CHARSET=iso-8859-1"); #if HAVE_SETLOCALE setlocale(LC_ALL, "C"); #endif /* Only uucp can run rmail (this better be installed setgid) */ if ((s=strrchr(argv[0], '/')) != 0) ++s; else s=argv[0]; if (strcmp(s, "rmail") == 0) { struct passwd *p=mypwd(); char *uu_machine, *uu_user; if (!p || strcmp(p->pw_name, "uucp")) { fprintf(stderr, "rmail: permission denied.\n"); exit(EX_NOPERM); } uu_machine=getenv("UU_MACHINE"); uu_user=getenv("UU_USER"); if (!uu_machine || !uu_user) { fprintf(stderr, "rmail: UU_MACHINE!UU_USER required.\n"); exit(EX_NOPERM); } uucprmail=malloc(strlen(uu_machine)+strlen(uu_user)+2); if (!uucprmail) { perror("malloc"); exit(EX_TEMPFAIL); } strcat(strcat(strcpy(uucprmail, uu_machine), "!"), uu_user); } while (argn < argc) { const char *arg; int c; if (argv[argn][0] != '-') break; if (strcmp(argv[argn], "-") == 0) { ++argn; break; } if (uucprmail && strncmp(argv[argn], "-f", 2) /* ** Ok, obviously no UUCP version will give me the following ** options, must I can hope, can't I? */ && strcmp(argv[argn], "-verp") && strncmp(argv[argn], "-N", 2) && strncmp(argv[argn], "-R", 2) && strncmp(argv[argn], "-V", 2) /* Ignore the ignorable */ && strncmp(argv[argn], "-o", 2) && strncmp(argv[argn], "-t", 2) ) { fprintf(stderr, "rmail: invalid option %s\n", argv[argn]); exit(EX_NOPERM); } if (strcmp(argv[argn], "-verp") == 0) { doverp=1; ++argn; continue; } if (strcmp(argv[argn], "-bcc") == 0) { bcconly=1; ++argn; continue; } if (strcmp(argv[argn], "-bs") == 0) { esmtpd(); } switch (c=argv[argn][1]) { case 'o': case 'f': case 'F': case 'R': case 'N': case 'S': case 'V': break; case 'n': tostdout=1; ++argn; continue; default: ++argn; continue; } arg=argv[argn]+2; if (!*arg && argn+1 < argc) arg=argv[++argn]; ++argn; if (c == 'f') from=arg; else if (c == 'F') From=arg; else if (c == 'N') dsn=arg; else if (c == 'S') { char *q; if (strcasecmp(arg, "NONE") && strcasecmp(arg, "STARTTLS")) { fprintf(stderr, "sendmail: invalid option" " -S %s\n", arg); exit(EX_NOPERM); } strcpy(security_buf, arg); for (q=security_buf; *q; q++) *q=toupper((int)(unsigned char)*q); security=security_buf; } else if (c == 'R') { ret_buf[0]= toupper((int)(unsigned char)*arg); ret_buf[1]= 0; ret=ret_buf; } else if (c == 'V') envid=arg; } sprintf(frombuf, "uid %s", libmail_str_uid_t(getuid(), ubuf)); argp=0; args[argp++]="submit"; if (bcconly) args[argp++]="-bcc"; if (uucprmail) { if (argn >= argc) { fprintf(stderr, "rmail: missing recipients\n"); exit(EX_NOPERM); } args[argp++]="uucp"; s=malloc(sizeof("unknown; uucp ()")+strlen(uucprmail)); if (!s) { perror("malloc"); exit(EX_TEMPFAIL); } strcat(strcat(strcpy(s, "unknown; uucp ("), uucprmail), ")"); args[argp++]=s; } else { args[argp++]="local"; args[argp++]="dns; localhost (localhost [127.0.0.1])"; args[argp++]=frombuf; } args[argp++]=0; envp=0; clog_open_stderr("sendmail"); if (ret || security || doverp) { envs[envp]=strcat(strcat(strcpy(courier_malloc(strlen(ret ? ret:"")+strlen(security ? security:"") + 30), "DSNRET="), (ret ? ret:"")), (doverp ? "V":"")); if (security) strcat(strcat(strcat(envs[envp], "S{"), security), "}"); ++envp; } if (dsn) { envs[envp]=strcat(strcpy(courier_malloc(strlen(dsn)+11), "DSNNOTIFY="), dsn); ++envp; } if (envid) { envs[envp]=strcat(strcpy(courier_malloc(strlen(envid)+10), "DSNENVID="), envid); ++envp; } envs[envp++]="TCPREMOTEHOST=localhost"; envs[envp++]="TCPREMOTEIP=127.0.0.1"; if (!uucprmail) envs[envp++]=strcat(strcpy(courier_malloc(strlen(frombuf)+ sizeof("TCPREMOTEINFO=")), "TCPREMOTEINFO="), frombuf); envs[envp]=0; if (!tostdout && submit_fork(args, envs, submit_print_stdout)) { fprintf(stderr, "sendmail: Service temporarily unavailable.\n"); exit(EX_TEMPFAIL); } if (tostdout) submit_to=stdout; else { if (uucprmail) { submit_write_message(from ? from:""); } else { char *p; p=rewrite_env_sender(from); submit_write_message(p); free(p); } if ((submit_errcode=submit_readrcprinterr()) != 0) { exit_submit(submit_errcode); } } while (!tostdout && argn < argc) { submit_write_message(argv[argn]); if ((errflag=submit_readrcprinterr()) != 0) { fprintf(stderr, "%s: invalid address.\n", argv[argn]); } ++argn; } if (errflag) exit_submit(errflag); if (!tostdout) putc('\n', submit_to); if (!uucprmail) rewrite_headers(From); while ((c=getchar()) != EOF) { putc(c, submit_to); } fflush(submit_to); signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); signal(SIGHUP, SIG_IGN); errflag=0; if (ferror(submit_to) || (!tostdout && ( fclose(submit_to) || (errflag=submit_readrcprinterr()) || submit_wait())) ) { fprintf(stderr, "sendmail: Unable to submit message.\n"); if (errflag) exit_submit(errflag); exit(EX_TEMPFAIL); } exit(0); }
static void fax(struct moduledel *p) { struct ctlfile ctf; unsigned nreceipients=p->nreceipients; const char *host=p->host; const char *receipient; unsigned nrecipient=(unsigned)atol(p->receipients[0]); int pipefd[2]; pid_t child_proc; int faxopts; int n_cover_pages; FILE *fp; struct faxconv_err_args err_args; struct sort_file_list *page_list, *pp; #if HAVE_SETPGRP #if SETPGRP_VOID setpgrp(); #else setpgrp(0, 0); #endif #else #if HAVE_SETPGID setpgid(0, 0); #endif #endif if (comgetfaxopts(host, &faxopts)) { clog_msg_start_err(); clog_msg_str("courierfax: FATAL: invalid host"); clog_msg_send(); exit(1); } putenv(faxopts & FAX_LOWRES ? "FAXRES=lo":"FAXRES=hi"); if (nreceipients != 1) { clog_msg_start_err(); clog_msg_str("courierfax: FATAL: # receipients must be 1"); clog_msg_send(); exit(1); } receipient=p->receipients[1]; nrecipient=(unsigned)atol(p->receipients[0]); if (ctlfile_openi(p->inum, &ctf, 0)) clog_msg_errno(); /* Convert message to fax format, use a child process */ signal(SIGTERM, faxabort); if (pipe(pipefd) < 0) { clog_msg_errno(); } child_proc=fork(); if (child_proc < 0) { clog_msg_errno(); return; } if (child_proc == 0) { const char *fn; close(0); if (open("/dev/null", O_RDONLY) != 0) clog_msg_errno(); close(pipefd[0]); close(1); if (dup(pipefd[1]) != 1) { perror("dup"); exit(1); } close(2); if (dup(pipefd[1]) != 2) { fprintf(stderr, "ERROR: dup failed\n"); exit(1); } close(pipefd[1]); libmail_changeuidgid(MAILUID, MAILGID); fn=qmsgsdatname(p->inum); if (faxconvert(fn, faxopts, &n_cover_pages)) exit (1); if ((fp=fopen(FAXTMPDIR "/.ncoverpages", "w")) == NULL || fprintf(fp, "%d\n", n_cover_pages) < 0 || fflush(fp) < 0) exit(1); fclose(fp); exit(0); } close(pipefd[1]); err_args.ctf= &ctf; err_args.nreceip=nrecipient; if (read_childerrmsg(child_proc, pipefd[0], &faxconvert_cleanup, &err_args)) { ctlfile_close(&ctf); return; } /* Hit it */ if ((fp=fopen(FAXTMPDIR "/.ncoverpages", "r")) == NULL || fscanf(fp, "%d", &n_cover_pages) != 1) { if (fp) fclose(fp); ctlfile_append_reply(err_args.ctf, err_args.nreceip, "Internal error: cannot read number of cover pages", COMCTLFILE_DELFAIL, 0); ctlfile_close(&ctf); exit(0); } page_list=read_dir_sort_filenames(FAXTMPDIR, FAXTMPDIR "/"); if (!page_list) { clog_msg_start_err(); clog_msg_str("courierfax: INTERNAL ERROR - no pages to xmit."); clog_msg_send(); exit(1); } /* Keep trying until the modem line is unlocked */ do { if (pipe(pipefd) < 0) { clog_msg_errno(); } child_proc=fork(); if (child_proc < 0) { clog_msg_errno(); return; } if (child_proc == 0) { unsigned page_cnt=0; char **argvec; close(pipefd[0]); close(0); if (open("/dev/null", O_RDONLY) != 0) { perror("/dev/null"); exit(1); } close(1); if (dup(pipefd[1]) != 1) { perror("dup"); exit(1); } close(2); if (dup(pipefd[1]) != 2) { fprintf(stderr, "ERROR: dup failed\n"); exit(1); } close(pipefd[1]); for (pp=page_list; pp; pp=pp->next) ++page_cnt; #if 0 while (page_list) { unlink(page_list->filename); page_list=page_list->next; } exit(0); #endif argvec=(char **)courier_malloc(sizeof(char *)* (page_cnt+10)); argvec[0]=SENDFAX; argvec[1]="-v"; argvec[2]="-r"; page_cnt=3; if (faxopts & FAX_LOWRES) argvec[page_cnt++]="-n"; argvec[page_cnt++]=(char *)receipient; while (page_list) { argvec[page_cnt++]=page_list->filename; page_list=page_list->next; } argvec[page_cnt]=0; execv(SENDFAX, argvec); perror(SENDFAX); exit(1); } close(pipefd[1]); err_args.ctf= &ctf; err_args.nreceip=nrecipient; err_args.file_list=page_list; err_args.is_locked=0; err_args.n_cover_pages=n_cover_pages; if (read_childerrmsg(child_proc, pipefd[0], &faxsend_cleanup, &err_args) == 0) { size_t npages=0; char fmtbuf[NUMBUFSIZE]; char fmtbuf1[NUMBUFSIZE]; char fmtbuf2[NUMBUFSIZE+100]; for (pp=page_list; pp; pp=pp->next) ++npages; libmail_str_size_t(npages, fmtbuf); libmail_str_size_t(n_cover_pages, fmtbuf1); sprintf(fmtbuf2, "%s pages - including %s cover page(s)" " - sent by fax.", fmtbuf, fmtbuf1); ctlfile_append_reply(&ctf, (unsigned) nrecipient, fmtbuf2, COMCTLFILE_DELSUCCESS, " l"); break; } } while (err_args.is_locked); ctlfile_close(&ctf); rmdir_contents(FAXTMPDIR); }
static int docommand(const char *extension, const char *sender, const char *receipient, const char *defaultext, FILE *f, const char *username, const char *userhome, const char *command, const char *dtline, const char *rpline, const char *ufromline, const char *quota, const char *defaultmail, int recursion_level) { char *envs[19]; const char *p; const char *hostp; pid_t pid; int i; int wait_stat; int pipefd[2]; int isrecursive=0; char *newcommand=0; const char *maildropdefault=getenv("MAILDROPDEFAULT"); const char *shell=getenv("SHELL"); if (!maildropdefault) maildropdefault="./Maildir"; if (!shell) shell="/bin/sh"; envs[0]=courier_malloc(strlen(userhome)+sizeof("HOME=")); strcat(strcpy(envs[0], "HOME="), userhome); envs[1]=courier_malloc(strlen(username)+sizeof("USER="******"USER="******"SENDER=")); strcat(strcpy(envs[2], "SENDER="), sender); envs[3]=courier_malloc(strlen(receipient)+sizeof("RECIPIENT=")); strcat(strcpy(envs[3], "RECIPIENT="), receipient); p=strrchr(receipient, '@'); if (p) hostp=p+1; else { hostp="localhost"; p=receipient+strlen(receipient); } envs[4]=courier_malloc(strlen(hostp)+sizeof("HOST=")); strcat(strcpy(envs[4], "HOST="), hostp); envs[5]=courier_malloc(p-receipient + sizeof("LOCAL=")); strcpy(envs[5], "LOCAL="); memcpy(envs[5]+6, receipient, p-receipient); envs[5][6+(p-receipient)]=0; envs[6]=courier_malloc(strlen(extension)+sizeof("EXT=")); strcat(strcpy(envs[6], "EXT="), extension); p=strchr(extension, '-'); if (p) p++; envs[7]=courier_malloc((p ? strlen(p):0)+sizeof("EXT2=")); strcat(strcpy(envs[7], "EXT2="), p ? p:""); if (p) p=strchr(p, '-'); if (p) p++; envs[8]=courier_malloc((p ? strlen(p):0)+sizeof("EXT3=")); strcat(strcpy(envs[8], "EXT3="), p ? p:""); if (p) p=strchr(p, '-'); if (p) p++; envs[9]=courier_malloc((p ? strlen(p):0)+sizeof("EXT4=")); strcat(strcpy(envs[9], "EXT4="), p ? p:""); envs[10]=courier_malloc((defaultext ? strlen(defaultext):0)+sizeof("DEFAULT=")); strcat(strcpy(envs[10], "DEFAULT="), defaultext ? defaultext:""); envs[11]=courier_malloc(strlen(dtline)+sizeof("DTLINE=")); strcat(strcpy(envs[11], "DTLINE="), dtline); envs[12]=courier_malloc(strlen(rpline)+sizeof("RPLINE=")); strcat(strcpy(envs[12], "RPLINE="), rpline); envs[13]=courier_malloc(strlen(ufromline)+sizeof("UFLINE=")); strcat(strcpy(envs[13], "UFLINE="), ufromline); p=getenv("PATH"); if (!p) p="/bin:/usr/bin:/usr/local/bin"; envs[14]=courier_malloc(strlen(p)+sizeof("PATH=")); strcat(strcpy(envs[14], "PATH="), p); envs[15]=courier_malloc(strlen(quota)+sizeof("MAILDIRQUOTA=")); strcat(strcpy(envs[15], "MAILDIRQUOTA="), quota); envs[16]=courier_malloc(strlen(maildropdefault) +sizeof("MAILDROPDEFAULT=")); strcat(strcpy(envs[16], "MAILDROPDEFAULT="), maildropdefault); envs[17]=courier_malloc(strlen(shell) +sizeof("SHELL=")); strcat(strcpy(envs[17], "SHELL="), shell); envs[18]=0; if (*command == '|') { isrecursive=1; ++command; while (*command == ' ' || *command == '\t') ++command; if (pipe(pipefd) < 0) { clog_msg_prerrno(); exit(EX_TEMPFAIL); } } if ((pid=fork()) < 0) { clog_msg_prerrno(); exit(EX_TEMPFAIL); } if (pid == 0) { const char *args[8]; /* ** External commands have stdout redirected to stderr, ** and stdin set to the message. */ dup2(2, 1); if (fileno(f) != 0) { dup2(fileno(f), 0); fclose(f); } if (isrecursive) { const char *c; dup2(pipefd[1], 1); close(pipefd[0]); close(pipefd[1]); if (recursion_level >= 3) { fprintf(stderr, "Maximum recursion level for dynamic delivery instructions exceeded.\n"); fflush(stderr); _exit(EX_NOUSER); } if ((c=getenv("DYNAMICDELIVERIES")) != 0 && atoi(c) == 0) { fprintf(stderr, "Dynamic delivery instructions disabled by administrator.\n"); fflush(stderr); _exit(EX_NOUSER); } } if ((p=config_maildropmda()) != 0 && *p && strcmp(p, command) == 0) { /* Special magic for maildrop */ args[0]="maildrop"; args[1]="-A"; args[2]=dtline; args[3]="-A"; args[4]=rpline; args[5]="-f"; args[6]=sender; args[7]=0; } else { p=getenv("SHELL"); if (!p || !*p) p="/bin/sh"; args[0]=p; args[1]="-c"; args[2]=command; args[3]=0; } lseek(0, 0L, SEEK_SET); for (i=0; envs[i]; ++i) putenv(envs[i]); execv(p, (char **)args); fprintf(stderr, "Cannot run %s\n", p); _exit(EX_TEMPFAIL); } for (i=0; envs[i]; i++) free(envs[i]); if (isrecursive) { close(pipefd[1]); newcommand=read_command(pipefd[0]); close(pipefd[0]); } while (wait(&wait_stat) != pid) ; if (WIFEXITED(wait_stat)) { int rc=WEXITSTATUS(wait_stat); if (rc == 0 || rc == 99) { if (isrecursive) dodel(username, userhome, f, newcommand, extension, sender, receipient, defaultext, quota, defaultmail, recursion_level+1); } if (newcommand) free(newcommand); return (rc); } if (newcommand) free(newcommand); return (EX_NOPERM); }
void drvinfo::init() { unsigned cnt, i; struct rw_transport *p; module_dsn=0; queuelo=0; for (cnt=0, p=rw_transport_first; p; p=p->next) ++cnt; if ((modules=new drvinfo[cnt]) == 0) clog_msg_errno(); cnt=0; for (p=rw_transport_first; p; p=p->next) { char *namebuf=(char *)courier_malloc( sizeof( SYSCONFDIR "/module.")+strlen(p->name)); strcat(strcpy(namebuf, SYSCONFDIR "/module."), p->name); FILE *config=fopen(namebuf, "r"); if (!config) { clog_msg_start_err(); clog_msg_str("Cannot open "); clog_msg_str(namebuf); clog_msg_send(); exit(1); } free(namebuf); unsigned maxdels=1; unsigned maxhost=1; unsigned maxrcpt=1; char *prog=0; char buf[BUFSIZ]; while (fgets(buf, sizeof(buf), config) != 0) { char *p; if ((p=strchr(buf, '#')) != 0) *p=0; p=strtok(buf, " \t\r\n="); if (strcmp(p, "MAXDELS") == 0) { p=strtok(NULL, " \t\r\n="); if (p) maxdels=atoi(p); } else if (strcmp(p, "MAXHOST") == 0) { p=strtok(NULL, " \t\r\n="); if (p) maxhost=atoi(p); } else if (strcmp(p, "MAXRCPT") == 0) { p=strtok(NULL, " \t\r\n="); if (p) maxrcpt=atoi(p); } else if (strcmp(p, "PROG") == 0 && !prog) { p=strtok(NULL, "\t\r\n="); if (p) { prog=(char *) courier_malloc(strlen(p)+1); strcpy(prog, p); } } } fclose(config); if (!prog) continue; // Input only module modules[cnt].module=p; p->udata=(void *)&modules[cnt]; modules[cnt].delinfo_list.resize(maxdels); modules[cnt].hosts_list.resize(maxdels); queuelo += maxdels; modules[cnt].maxhost=maxhost; modules[cnt].maxrcpt=maxrcpt; modules[cnt].delpfreefirst=0; if (strcmp(p->name, DSN) == 0) module_dsn= &modules[cnt]; for (i=0; i<maxdels; i++) { modules[cnt].delinfo_list[i].delid=i; modules[cnt].delinfo_list[i].freenext= modules[cnt].delpfreefirst; modules[cnt].delpfreefirst= &modules[cnt].delinfo_list[i]; } modules[cnt].hdlvrpfree=0; for (i=0; i<maxdels; i++) { modules[cnt].hosts_list[i].next= modules[cnt].hdlvrpfree; modules[cnt].hdlvrpfree=&modules[cnt].hosts_list[i]; } modules[cnt].hdlvrpfirst=0; modules[cnt].hdlvrplast=0; int pipe0[2], pipe1[2]; if (pipe(pipe0) || pipe(pipe1)) clog_msg_errno(); switch (modules[cnt].module_pid=fork()) { case 0: #if 0 // replaced by close-on-exec, below for (i=cnt; i; ) { --i; fclose(modules[i].module_to); close(modules[i].module_from.fd); } #endif close(pipe0[1]); close(pipe1[0]); dup2(pipe0[0], 0); dup2(pipe1[1], 1); close(pipe0[0]); close(pipe1[1]); signal(SIGCHLD, SIG_DFL); if (chdir(MODULEDIR) || chdir(modules[cnt].module->name)) { clog_msg_start_err(); clog_msg_str(MODULEDIR "/"); clog_msg_str(modules[cnt].module->name); clog_msg_str(" does not exist."); clog_msg_send(); clog_msg_errno(); } { char maxdels_buf[MAXLONGSIZE+15]; char maxhost_buf[MAXLONGSIZE+15]; char maxrcpt_buf[MAXLONGSIZE+15]; const char *sh=getenv("SHELL"); sprintf(maxdels_buf, "MAXDELS=%ld", (long)maxdels); sprintf(maxhost_buf, "MAXHOST=%ld", (long)maxhost); sprintf(maxrcpt_buf, "MAXRCPT=%ld", (long)maxrcpt); putenv(maxdels_buf); putenv(maxhost_buf); putenv(maxrcpt_buf); const char *ch=courierdir(); char *p=(char *)courier_malloc(strlen(ch)+ sizeof("COURIER_HOME=")); strcat(strcpy(p,"COURIER_HOME="), ch); putenv(p); if (!sh) sh="/bin/sh"; execl(sh, sh, "-c", prog, (char *)0); clog_msg_start_err(); clog_msg_str(MODULEDIR "/"); clog_msg_str(modules[cnt].module->name); clog_msg_str(": "); clog_msg_str(prog); clog_msg_send(); clog_msg_errno(); } case -1: clog_msg_errno(); break; } close(pipe0[0]); close(pipe1[1]); if (fcntl(pipe0[1], F_SETFD, FD_CLOEXEC) || fcntl(pipe1[0], F_SETFD, FD_CLOEXEC)) clog_msg_errno(); if ((modules[cnt].module_to=fdopen(pipe0[1], "w")) == NULL) clog_msg_errno(); mybuf_init( &modules[cnt].module_from, pipe1[0] ); clog_msg_start_info(); clog_msg_str("Started "); clog_msg_str(prog); clog_msg_str(", pid="); clog_msg_int(modules[cnt].module_pid); clog_msg_str(", maxdels="); clog_msg_int(maxdels); clog_msg_str(", maxhost="); clog_msg_int(maxhost); clog_msg_str(", maxrcpt="); clog_msg_int(maxrcpt); clog_msg_send(); free(prog); cnt++; } nmodules=cnt; if (queuelo < 200) queuelo=200; char *cfname=config_localfilename("queuelo"); char *conf=config_read1l(cfname); if (conf) { queuelo=atoi(conf); free(conf); } free(cfname); if (queuelo < 20) queuelo=20; cfname=config_localfilename("queuehi"); conf=config_read1l(cfname); if (conf) { queuehi=atoi(conf); free(conf); } else queuehi=queuelo < 500 ? queuelo * 2:queuelo+1000; if (queuehi < queuelo) queuehi=queuelo; free(cfname); clog_msg_start_info(); clog_msg_str("queuelo="); clog_msg_int(queuelo); clog_msg_str(", queuehi="); clog_msg_int(queuehi); clog_msg_send(); if (module_dsn == 0) { clog_msg_start_err(); clog_msg_str("Driver dsn not found."); clog_msg_send(); exit(1); } }
static void esmtpparent() { unsigned i; fd_set fdc, fds; time_t current_time; libmail_changeuidgid(MAILUID, MAILGID); module_init(&terminated_child); if ((info=(struct esmtpchildinfo *)malloc(sizeof(*info)* module_nchildren)) == 0) clog_msg_errno(); for (i=0; i<module_nchildren; i++) { info[i].pid= -1; info[i].cmdpipe= -1; info[i].host=0; info[i].pendel=0; } if (pipe(completionpipe) < 0) clog_msg_errno(); if ((childresultpipe=fdopen(completionpipe[0], "r")) == 0) clog_msg_errno(); FD_ZERO(&fdc); FD_SET(0, &fdc); FD_SET(completionpipe[0], &fdc); mybuf_init(&courierdbuf, 0); mybuf_init(&childbuf, completionpipe[0]); module_blockset(); time(¤t_time); for (;;) { time_t wait_time; struct timeval tv; wait_time=0; for (i=0; i<module_nchildren; i++) { if (!ESMTP_IDLE(&info[i])) continue; if (info[i].termtime <= current_time) { close(info[i].cmdpipe); info[i].cmdpipe= -1; continue; } if (wait_time == 0 || info[i].termtime < wait_time) wait_time=info[i].termtime; } if (wait_time) { tv.tv_sec= wait_time - current_time; tv.tv_usec=0; } fds=fdc; module_blockclr(); while (select(completionpipe[0]+1, &fds, (fd_set *)0, (fd_set *)0, (wait_time ? &tv:(struct timeval *)0)) < 0) { if (errno != EINTR) clog_msg_errno(); } module_blockset(); time(¤t_time); if (FD_ISSET(completionpipe[0], &fds)) { char *line; do { pid_t p; line=module_getline( &call_mybuf_get, &childbuf); if (parse_ack(line, &i, &p) || i >= module_nchildren || (p == info[i].pid && !ESMTP_BUSY(&info[i]))) { clog_msg_start_err(); clog_msg_str("INVALID message from child process."); clog_msg_send(); _exit(0); } if (p != info[i].pid) continue; info[i].isbusy=0; info[i].termtime=current_time + esmtpkeepalive; if (info[i].pendel) { free(info[i].pendel); info[i].pendel=0; } module_completed(i, module_delids[i]); } while (mybuf_more(&childbuf)); } if (!FD_ISSET(0, &fds)) continue; do { char **cols; const char *hostp; size_t hostplen; time_t misctime; unsigned j; char *line; line=module_getline( &call_mybuf_get, &courierdbuf); if (!line) { module_restore(); /* ** If all processes are idle, wait for them ** to finish normally. Otherwise, kill ** the processes. */ for (j=0; j<module_nchildren; j++) if (ESMTP_BUSY(&info[j])) break; if (j < module_nchildren) { for (j=0; j<module_nchildren; j++) if (info[j].pid > 0) kill(info[j].pid, SIGTERM); } else { int waitstat; for (j=0; j<module_nchildren; j++) { if (info[j].cmdpipe > 0) { close(info[j].cmdpipe); info[j].cmdpipe= -1; } } while (wait(&waitstat) != -1 || errno == EINTR) ; } _exit(0); } cols=module_parsecols(line); if (!cols) _exit(0); hostp=MODULEDEL_HOST(cols); for (hostplen=0; hostp[hostplen] && hostp[hostplen] != '\t'; hostplen++) ; for (i=0; i<module_nchildren; i++) { if (!ESMTP_IDLE(&info[i])) continue; if (memcmp(info[i].host, hostp, hostplen) == 0 && info[i].host[hostplen] == 0) break; } if (i < module_nchildren) /* Reuse a process */ { send_child(i, line, cols); continue; } for (i=0; i<module_nchildren; i++) if (ESMTP_NOCHILD(&info[i])) break; if (i < module_nchildren) /* We can fork */ { start_child(i, line, cols); send_child(i, line, cols); continue; } /* ** Find a process that's been idled the longest, ** and reuse that one. */ misctime=0; j=0; for (i=0; i<module_nchildren; i++) { if (ESMTP_IDLE(&info[i]) && (misctime == 0 || misctime > info[i].termtime)) { j=i; misctime=info[i].termtime; } } if (misctime) { if (info[j].pendel) { clog_msg_start_err(); clog_msg_str("INTERNAL ERROR: unexpected scheduled delivery."); clog_msg_send(); _exit(1); } info[j].pendel=strcpy( courier_malloc(strlen(line)+1), line); close(info[j].cmdpipe); info[j].cmdpipe= -1; continue; } /* The ONLY remaining possibility is something in ** the TERMINATING stage, without another delivery ** already scheduled for that slot. */ for (i=0; i<module_nchildren; i++) { if (ESMTP_TERMINATING(&info[i]) && info[i].pendel == 0) break; } if (i < module_nchildren) { info[i].pendel=strcpy( courier_malloc(strlen(line)+1), line); continue; } clog_msg_start_err(); clog_msg_str("INTERNAL ERROR: unexpected delivery."); clog_msg_send(); _exit(1); } while (mybuf_more(&courierdbuf)); } }