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]; }
void SubmitFile::MessageStart() { bytecount=0; sizelimit=config_sizelimit(); addrlist_map.clear(); addrlist_gdbm.Close(); if (ctlfile.fd() < 0) openctl(); unlink(namefile("D", 1).c_str()); // Might be the GDBM file int nfd=open(namefile("D",0).c_str(), O_RDWR | O_CREAT | O_TRUNC, PERMISSION); if (nfd < 0) { clog_msg_start_err(); clog_msg_str(namefile("D",0).c_str()); clog_msg_str(": "); clog_msg_errno(); } datfile.fd(nfd); rwrfcptr=rfc2045_alloc_ac(); if (rwrfcptr == NULL) clog_msg_errno(); diskfull=checkfreespace(&diskspacecheck); }
void SubmitFile::closectl() { const char *p=config_get_local_vhost(); static const char *env_vars[]={ "RELAYCLIENT", "TCPREMOTEIP", 0, }; if (p && *p) ctlfile << COMCTLFILE_VHOST << p << std::endl; if (security.size() > 0) ctlfile << COMCTLFILE_SECURITY << security << std::endl; if (verpflag) ctlfile << COMCTLFILE_VERP << std::endl; if (warntime == 0) ctlfile << COMCTLFILE_WARNINGSENT << std::endl; if (authname) ctlfile << COMCTLFILE_AUTHNAME << authname << std::endl; for (size_t i=0; env_vars[i]; ++i) { const char *p=getenv(env_vars[i]); if (p) ctlfile << COMCTLFILE_ENVVAR << env_vars[i] << "=" << p << std::endl; } ctlfile << COMCTLFILE_MSGSOURCE << msgsource << std::endl << COMCTLFILE_EXPIRES << submit_time + queuetime << std::endl << COMCTLFILE_FAXEXPIRES << submit_time + faxqueuetime << std::endl << COMCTLFILE_WARNING << submit_time + warntime << std::endl << std::flush; if (suppressbackscatter) ctlfile << COMCTLFILE_TRACK << std::endl; if (ctlfile.fail()) clog_msg_errno(); #if EXPLICITSYNC ctlfile.sync(); fsync(ctlfile.fd()); #endif ctlfile.close(); if (ctlfile.fail()) clog_msg_errno(); }
int SubmitFile::ChkRecipient(const char *key) { receipient=key; // Ignore duplicate addresses. if (addrlist_gdbm.IsOpen()) { if (addrlist_gdbm.Exists(receipient)) return (-1); /* Already exists */ if (addrlist_gdbm.Store(receipient, "", "R")) clog_msg_errno(); } else { if (addrlist_map.find(receipient) != addrlist_map.end()) return (-1); /* Already exists */ addrlist_map.insert(receipient); if (addrlist_map.size() > SPILLLEVEL) { // // Store the GDBM file in the name that's reserved for the first hard link // link to the message file (which is never used). // std::string gdbmname=namefile("D",1); if (addrlist_gdbm.Open(gdbmname.c_str(), "N")) { clog_msg_start_err(); clog_msg_str(gdbmname.c_str()); clog_msg_str(": "); clog_msg_errno(); } std::set<std::string>::iterator b, e; for (b=addrlist_map.begin(), e=addrlist_map.end(); b != e; ++b) { if (addrlist_gdbm.Store(*b, std::string(""), "R")) clog_msg_errno(); } addrlist_map.clear(); } } return (0); }
void SubmitFile::trapsignals() { signal(SIGINT, sighandler); signal(SIGTERM, sighandler); signal(SIGHUP, sighandler); signal(SIGALRM, sighandler); if (atexit(SubmitFile::interrupt)) clog_msg_errno(); }
/* Common rewrite tail function to convert RFC822 tokens back into ** a text string. */ void rw_rewrite_print(struct rw_info *info) { char *p=rfc822_gettok(info->ptr); if (!p) clog_msg_errno(); ( (struct rw_info_rewrite *)info->udata)->buf=p; }
int main(int argc, char **argv) { struct moduledel *p; int waitstat; const char *cp; clog_open_syslog("courierfax"); if (chdir(getenv("COURIER_HOME"))) clog_msg_errno(); cp=getenv("MAXDELS"); if (!cp || atoi(cp) != 1) { clog_msg_start_err(); clog_msg_str("FATAL: courierfax module misconfiguration, MAXDELS must be 1"); clog_msg_send(); exit(0); } cp=getenv("MAXRCPT"); if (!cp || atoi(cp) != 1) { clog_msg_start_err(); clog_msg_str("FATAL: courierfax module misconfiguration, MAXRCPT must be 1"); clog_msg_send(); exit(0); } module_init(0); while ((p=module_getdel()) != NULL) { pid_t pid; unsigned delid; delid=atol(p->delid); if ((pid=module_fork(delid, 0)) == -1) { clog_msg_prerrno(); module_completed(delid, delid); continue; } if (pid == 0) { fax(p); exit(0); } } module_signal(SIGTERM); signal(SIGCHLD, SIG_DFL); signal(SIGALRM, killit); alarm(5); wait(&waitstat); alarm(0); return (0); }
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); }
struct rfc822t *rw_rewrite_tokenize(const char *address) { struct rfc822t *tokens=rfc822t_alloc_new(address, NULL, NULL); int i; if (!tokens) clog_msg_errno(); for (i=1; i<tokens->ntokens; i++) tokens->tokens[i-1].next=tokens->tokens+i; return (tokens); }
void SubmitFile::AddReceipient(const char *r, const char *orig, const char *dsn, int delivered) { // If # of receipients in the current control file exceeds the // defined batch size, close the current control file. if (rcptcount >= batchsize) { closectl(); // // The first time we create another control file, rename the first // control file to Cnnnn.1, which is a flag not to process Cnnnn.2, .3, etc... // // Cnnnn.2, .3, ... will be processed only after Cnnnn.1 is renamed to Cnnnn // if (num_control_files_created == 1) { std::string p=name1stctlfile(); std::string q=namefile("C", 1); if (rename(p.c_str(), q.c_str())) { clog_msg_start_err(); clog_msg_str(p.c_str()); clog_msg_str(" -> "); clog_msg_str(q.c_str()); clog_msg_str(": "); clog_msg_errno(); } } } // Open a new control file, if necessary. if (ctlfile.fd() < 0) openctl(); ctlfile << COMCTLFILE_RECEIPIENT << r << std::endl << COMCTLFILE_ORECEIPIENT << (orig ? orig:"") << std::endl << COMCTLFILE_DSN << (dsn ? dsn:"") << std::endl; if (delivered) { ctlfile << COMCTLFILE_DELINFO << rcptcount << ' ' << COMCTLFILE_DELINFO_REPLY << " 250 Ok - delivered to alias." << std::endl << COMCTLFILE_DELSUCCESS << rcptcount << ' ' << submit_time << " r" << std::endl; } ctlfile << std::flush; ++rcptcount; if (bofh_chkspamtrap(r)) { spamtrap_flag=1; } }
static void found_module(struct rw_info *rwi, struct rw_transport *t, const struct rfc822token *host, const struct rfc822token *addr) { struct getdelinfo_struct *p=(struct getdelinfo_struct *)rwi->udata; if (!t->udata) return; p->drvinfop=(drvinfo *)t->udata; char *s=rfc822_gettok(host); if (!s) clog_msg_errno(); p->host=s; free(s); s=rfc822_gettok(addr); if (!s) clog_msg_errno(); p->addr=s; free(s); }
void AliasSearch::Open( const char *module ) { char *p; modulename="local"; if (module) { modulename=SYSCONFDIR "/aliases-"; modulename += module; modulename += ".dat"; if (module_alias.Open(modulename, "R") && errno != ENOENT) clog_msg_errno(); modulename=module; } p=config_localfilename("aliases.dat"); if (local_alias.Open(p, "R") && errno != ENOENT) clog_msg_errno(); free(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); }
void SubmitFile::Message(const char *p) { size_t l=strlen(p); if (sizelimit && bytecount > sizelimit) return; bytecount += l; if (sizelimit && bytecount > sizelimit) return; if (diskfull) return; datfile << p; if (l > diskspacecheck) { if (checkfreespace(&diskspacecheck)) { diskfull=1; return; } diskspacecheck += l; } diskspacecheck -= l; if (datfile.fail()) clog_msg_errno(); rfc2045_parse(rwrfcptr, p, strlen(p)); }
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 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); }
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); }
int main(int argc, char **argv) { struct moduledel *p; clog_open_syslog("courierlocal"); if (chdir(courier_home=getenv("COURIER_HOME"))) clog_msg_errno(); if (atol(getenv("MAXRCPT")) > 1) { clog_msg_start_err(); clog_msg_str("Invalid configuration in config, set MAXRCPT=1"); clog_msg_send(); exit(1); } module_init(0); module_delivery_timeout(config_readtime("localtimeout", 15*60)); while ((p=module_getdel()) != NULL) { unsigned delid=atol(p->delid); unsigned rcptnum; struct ctlfile ctf; int pipe1[2], pipe2[2]; pid_t pid; int datfd; struct stat stat_buf; pid=module_fork(delid, 0); if (pid < 0) { clog_msg_prerrno(); module_completed(delid, delid); continue; } if (pid) continue; #if HAVE_SETPGRP #if SETPGRP_VOID setpgrp(); #else setpgrp(0, 0); #endif #endif if (p->nreceipients != 1) { clog_msg_start_err(); clog_msg_str("Invalid message from courierd."); clog_msg_send(); _exit(0); } rcptnum=atol(p->receipients[0]); if (ctlfile_openi(p->inum, &ctf, 0)) { clog_msg_errno(); _exit(0); } if (pipe(pipe1) < 0 || pipe(pipe2) < 0 || (pid=fork()) < 0) { clog_msg_prerrno(); ctlfile_append_reply(&ctf, rcptnum, "Can't run courierdeliver.", COMCTLFILE_DELDEFERRED, 0); ctlfile_close(&ctf); _exit(0); return (0); } if ((datfd=open(qmsgsdatname(p->inum), O_RDONLY)) < 0) { clog_msg_prerrno(); ctlfile_append_reply(&ctf, rcptnum, "Unable to read message file.", COMCTLFILE_DELDEFERRED, 0); ctlfile_close(&ctf); _exit(0); return (0); } if (pid == 0) { const char *host, *homedir, *maildir, *quota; char *buf, *s; char *username, *ext; uid_t u; gid_t g; close(pipe1[0]); close(pipe2[0]); dup2(pipe2[1], 2); close(pipe2[1]); dup2(pipe1[1], 1); close(pipe1[1]); close(0); if (dup(datfd) != 0) { clog_msg_start_err(); clog_msg_str("Unable to read message file."); clog_msg_send(); _exit(EX_TEMPFAIL); } close(ctf.fd); close(datfd); /* Contents of host: */ /* acct!ext!uid!gid!homedir!maildir!quota */ host=p->host; buf=strdup(host); if (!buf) { clog_msg_prerrno(); _exit(EX_TEMPFAIL); } s=strchr(buf, '!'); username=buf; if (s) *s++=0; ext=s; if (s) s=strchr(s, '!'); if (s) *s++=0; u=0; while (s && *s != '!') { if (isdigit((int)(unsigned char)*s)) u=u*10 + (*s - '0'); ++s; } if (s) *s++=0; g=0; while (s && *s != '!') { if (isdigit((int)(unsigned char)*s)) g=g*10 + (*s - '0'); ++s; } if (s) *s++=0; homedir=s; if (s) s=strchr(s, '!'); if (s) *s++=0; maildir=s; if (s) s=strchr(s, '!'); if (s) *s++=0; quota=s; if (!s) { clog_msg_start_err(); clog_msg_str("Invalid local recipient address."); clog_msg_send(); _exit(EX_TEMPFAIL); } if (chdir(homedir)) { clog_msg_str(homedir); clog_msg_str(": "); clog_msg_prerrno(); _exit(EX_TEMPFAIL); } libmail_changeuidgid(u, g); execl( MODULEDIR "/local/courierdeliver", "courierdeliver", username, homedir, ext, ctf.receipients[rcptnum], verp_getsender(&ctf, ctf.receipients[rcptnum]), quota, maildir, (const char *)0); clog_msg_start_err(); clog_msg_prerrno(); clog_msg_send(); _exit(EX_TEMPFAIL); } close(pipe1[1]); close(pipe2[1]); libmail_changeuidgid(MAILUID, MAILGID); if (fstat(datfd, &stat_buf) == 0) ctf.msgsize=stat_buf.st_size; close(datfd); wait_delivery(pid, &ctf, rcptnum, pipe2[0], pipe1[0], p->host, p->receipients[1]); ctlfile_close(&ctf); _exit(0); } return (0); }
void SubmitFile::do_datafilter( unsigned &last_error, int &flag, int fd, struct rw_transport *driver, std::string host, std::string address, unsigned rcptnum, unsigned num_control_file, unsigned num_receipient, const char *okmsg, unsigned nrcpts) { char buf[2048]; int ctf; int rc; if (driver->rw_ptr->filter_msg == 0) return; buf[0]=0; if (lseek(fd, 0L, SEEK_SET) < 0) clog_msg_errno(); // Call the driver's filter function. rc=driver->rw_ptr->filter_msg ? (*driver->rw_ptr->filter_msg)(sending_module, fd, host.c_str(), address.c_str(), sender.c_str(), buf, sizeof(buf)):0; if (rc == 0) return; if (buf[0] == 0) // Error but no msg, make one up strcpy(buf, "Access denied."); ctf=open ( (num_control_files_created == 1 ? name1stctlfile(): namefile( "C", num_control_file+1)) .c_str(), O_WRONLY|O_APPEND); if (ctf < 0) clog_msg_errno(); /* ** Mark the recipient as delivered, so no more processing is ** done. */ ctlfile_append_replyfd(ctf, num_receipient, buf, COMCTLFILE_DELSUCCESS, 0); close(ctf); /* ** We are now required to return an EXDATA extended error. If there ** were any good recipients up until now, we need to return an OK ** message for those recipients. */ while (last_error < rcptnum) { print_xerror(0, okmsg, 0); ++last_error; } print_xerror( address.c_str(), buf, rcptnum + 1 == nrcpts); ++last_error; flag=1; }
int SubmitFile::datafilter(const char *datfilename, unsigned nrcpts, const char *okmsg) { int fd=open(datfilename, O_RDONLY); unsigned last_error=0; int flag=0; if (fd < 0) clog_msg_errno(); if (rcptfilterlist_file.is_open()) { // List of receipients was large enough to be dumped into a // file. rcptfilterlist_file.seekg(0); if (rcptfilterlist_file.bad()) clog_msg_errno(); // Read recipient list from the dump file, and filter each // one. RcptFilterInfo r; struct rw_transport *rw; std::string buf; while (!std::getline(rcptfilterlist_file, buf).fail()) { if (sscanf(buf.c_str(), "%u", &r.num_control_file) != 1 || std::getline(rcptfilterlist_file, buf).fail() || sscanf(buf.c_str(), "%u", &r.num_receipient) != 1 || std::getline(rcptfilterlist_file, buf).fail()) clog_msg_errno(); for (rw=rw_transport_first; rw; rw=rw->next) if (rw->name == buf) break; if (!rw) clog_msg_errno(); r.driver=rw; if ( std::getline(rcptfilterlist_file, r.host).fail() || std::getline(rcptfilterlist_file, r.address).fail() || std::getline(rcptfilterlist_file, buf).fail() || sscanf(buf.c_str(), "%u", &r.rcptnum) != 1) clog_msg_errno(); do_datafilter(last_error, flag, fd, r.driver, r.host, r.address, r.rcptnum, r.num_control_file, r.num_receipient, okmsg, nrcpts); } if ( rcptfilterlist_file.bad()) clog_msg_errno(); rcptfilterlist_file.close(); } else { std::list<RcptFilterInfo>::iterator ab, ae; for (ab=rcptfilterlist.begin(), ae=rcptfilterlist.end(); ab != ae; ++ab) { RcptFilterInfo &r= *ab; do_datafilter(last_error, flag, fd, r.driver, r.host, r.address, r.rcptnum, r.num_control_file, r.num_receipient, okmsg, nrcpts); } } close(fd); while (last_error && last_error < nrcpts) { print_xerror(0, okmsg, ++last_error == nrcpts); } std::cout << std::flush; return (flag); }
int msgq::queuescan2(std::string s) { DIR *tmpdir=opendir(s.c_str()); struct dirent *de; std::list<std::string> filename_list; if (!tmpdir) clog_msg_errno(); while ((de=readdir(tmpdir)) != 0) { const char *p=de->d_name; if (*p++ != 'C') continue; ino_t inum=strtoino(p); // Perhaps this one's in the queue already if (findq(inum)) continue; // Already in msgq while (isdigit(*p)) p++; if (*p++ != '.') continue; while (isdigit(*p)) ++p; if (*p) continue; filename_list.push_back(de->d_name); } closedir(tmpdir); if (filename_list.size() == 0) return (1); // Pretend we added something, so keep going std::vector<std::string> filename_array; filename_array.reserve(filename_list.size()); filename_array.insert(filename_array.end(), filename_list.begin(), filename_list.end()); int rc; int flag=0; std::sort(filename_array.begin(), filename_array.end(), std::ptr_fun(sort_by_qtime)); std::vector<std::string>::iterator b, e; b=filename_array.begin(); e=filename_array.end(); while (b != e) { rc=queuescan3(s, *b++, 0); if (rc < 0) return (-1); if (rc) break; flag=1; } return (flag); }
char *makeerrmsgtext(int errnum, const char *errtext) { char errnumbuf[4]; int i; const char *p; char *buf, *q, *r; int lastchar; if (!errtext || !*errtext) errtext="Failed."; errnumbuf[3]=0; for (i=2; i >= 0; --i) { errnumbuf[i]= (errnum % 10)+'0'; errnum /= 10; } /* ** Calculate size of buffer we'll need. For each line, we ** add four bytes for the error number. ** One line errors may need \n appended. */ i=6+strlen(errtext); /* ** First line's toll, plus optional trailing \r\n, ** and null byte. */ for (p=errtext; *p; p++) if (*p == '\n') i += 4; if ((buf=malloc(i)) == 0) clog_msg_errno(); for (q=r=buf, p=errtext; *p; ) { r=q; strcpy(q, errnumbuf); q += 3; *q++ = '-'; if (isdigit((int)(unsigned char)p[0]) && isdigit((int)(unsigned char)p[1]) && isdigit((int)(unsigned char)p[2]) && (p[3] == '-' || p[3] == ' ')) p += 4; /* Remove old error message number */ while (*p) { lastchar=*q++ = *p++; if (lastchar != '\n' && *p == '\0') { *q++='\n'; } if (lastchar == '\n') break; } } r[3]=' '; *q=0; return (buf); }
void SubmitFile::openctl() { std::string filename; ctlfile.close(); if (num_control_files_created == 0) // First recipient { for (;;) { const char *timeptr, *pidptr, *hostnameptr; getnewtmpfilenameargs(&timeptr, &pidptr, &hostnameptr); filename=name1stctlfile(); current_submit_file=this; int nfd=open(filename.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_EXCL | O_APPEND, PERMISSION); if (nfd >= 0) ctlfile.fd(nfd); if (nfd >= 0 || errno != EEXIST) break; current_submit_file=0; sleep(3); } ++num_control_files_created; if (ctlfile.fd() < 0) { // // One reason why we may not be able to create it // would be if the subdirectory, based on current time, // does not exist, so fork a tiny program to create it, // with the right permissions // pid_t p=fork(); pid_t w; int wait_stat; if (p == -1) { clog_msg_start_err(); clog_msg_str("fork: "); clog_msg_errno(); } if (p == 0) { filename=std::string(filename.begin(), std::find(filename.begin() , filename.end() , '/')); execl(LIBEXECDIR "/courier/submitmkdir", "submitmkdir", filename.c_str(), (char *)0); exit(0); } while ((w=wait(&wait_stat)) != p) if (w == -1 && errno == ECHILD) break; int nfd=open(filename.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_EXCL | O_APPEND, PERMISSION); if (nfd >= 0) ctlfile.fd(nfd); } } else { ++num_control_files_created; filename=namefile("C", num_control_files_created); int nfd=open(filename.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_EXCL | O_APPEND, PERMISSION); if (nfd >= 0) ctlfile.fd(nfd); } if (ctlfile.fd() < 0) clog_msg_errno(); struct stat stat_buf; char ino_buf[sizeof(ino_t)*2+1]; if (fstat(ctlfile.fd(), &stat_buf) != 0) clog_msg_errno(); rcptcount=0; if (num_control_files_created == 1) { char time_buf[sizeof(time_t)*2+1]; char pid_buf[sizeof(time_t)*2+1]; char msgidbuf[sizeof(time_buf)+sizeof(pid_buf)]; ctltimestamp=stat_buf.st_mtime; ctlpid=getpid(); strcat(strcat(strcpy(msgidbuf, libmail_strh_time_t(ctltimestamp, time_buf)), "."), libmail_strh_pid_t(getpid(), pid_buf)); basemsgid=msgidbuf; ctlinodenum=stat_buf.st_ino; } libmail_strh_ino_t( stat_buf.st_ino, ino_buf ); ctlfile << COMCTLFILE_SENDER << sender << std::endl << COMCTLFILE_FROMMTA << frommta << std::endl << COMCTLFILE_ENVID << envid << std::endl << COMCTLFILE_DSNFORMAT << dsnformat << std::endl << COMCTLFILE_MSGID << ino_buf << '.' << basemsgid << std::endl; if (submitdelay) ctlfile << COMCTLFILE_SUBMITDELAY << submitdelay << std::endl; ctlfile << std::flush; }
int msgq::tmpscan() { int found=0; time(¤t_time); DIR *tmpdir=opendir(TMPDIR); if (!tmpdir) clog_msg_errno(); struct dirent *de; std::string subdir; std::string ctlfile, datfile, newctlfile, newdatfile; struct stat stat_buf; std::string qdir, qname; while ((de=readdir(tmpdir)) != 0) { const char *p; for (p=de->d_name; *p; p++) if (!isdigit((int)(unsigned char)*p)) break; if (*p) continue; // Subdirs must be named all digits subdir=TMPDIR "/"; subdir += de->d_name; DIR *subde=opendir(subdir.c_str()); while ((de=readdir(subde)) != 0 && found < 100) { if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue; ctlfile=subdir; ctlfile += '/'; ctlfile += de->d_name; int i=-1; if (stat(ctlfile.c_str(), &stat_buf)) continue; size_t j=ctlfile.size(); while (j) { if (ctlfile[--j] == '/') break; if (ctlfile[j] == '.') { i=j; break; } } ino_t inum=stat_buf.st_ino; // Cleanup timeout. Should always be sufficiently longer than // the submit timeout set in submit.C via alarm_timeout. if (stat_buf.st_mtime < current_time - 48 * 60 * 60) { if (de->d_name[0] == 'D') { datfile=subdir + "/C" + (de->d_name+1); if (stat(datfile.c_str(), &stat_buf) == 0) continue; // Wait until the C file is // purged // Be carefull with Cnnnn.x files, because // Cnnnn.1 is used to hold submission of all // of them. A race condition can leave // Cnnnn.1 in a submittable state, so if it // is removed, other copies of this message // can become submittable, during a very small // window. } else if (de->d_name[0] == 'C' && i >= 0) { // This race condition is handled simply // by not removing Cxxxxx.n if Cxxxxx.n+1 // exists. char nbuf[NUMBUFSIZE]; datfile=ctlfile.substr(0, i); size_t n=atoi( ctlfile.c_str()+i+1); ++n; datfile += '.'; datfile += libmail_str_size_t(n, nbuf); if (stat(datfile.c_str(), &stat_buf) == 0) continue; // Wait until the C.1 file is // purged unlink(ctlfile.c_str()); ctlfile=subdir + "/D" + (de->d_name+1); /* FALLTHRU */ } unlink(ctlfile.c_str()); continue; } if (de->d_name[0] != 'C') continue; if (i >= 0) { datfile=ctlfile.substr(0, i); datfile += ".1"; if (ctlfile == datfile || stat(datfile.c_str(), &stat_buf) == 0) continue; } datfile=subdir + "/D" + (de->d_name+1); newdatfile=qmsgsdatname(inum); newctlfile=qmsgsctlname(inum); struct ctlfile ctf; if ((access(datfile.c_str(), 0) == 0 && access(newdatfile.c_str(), 0) == 0) || access(newctlfile.c_str(), 0) == 0 || ctlfile_openfn(ctlfile.c_str(), &ctf, 0, 0)) { clog_msg_start_err(); clog_msg_str("QUEUE FILE CORRUPTION: inode "); clog_msg_uint(inum); clog_msg_send(); utime(ctlfile.c_str(), 0); // Keep it around continue; } time_t nextattempt=current_time+delaytime; if ((i=ctlfile_searchfirst(&ctf, COMCTLFILE_SUBMITDELAY)) >= 0) nextattempt=current_time+ atoi(ctf.lines[i]+1); qname=qmsgqname(inum, nextattempt); ctlfile_nextattempt(&ctf, nextattempt); if (link(ctlfile.c_str(), qname.c_str())) { mkdir(qmsgqdir(current_time),0755); if (link(ctlfile.c_str(), qname.c_str()) && errno != EEXIST) clog_msg_errno(); } if (rename(datfile.c_str(), newdatfile.c_str())) { mkdir(qmsgsdir(inum), 0755); // We may need to create this dir rename(datfile.c_str(), newdatfile.c_str()); } if (rename(ctlfile.c_str(), newctlfile.c_str())) clog_msg_errno(); for (i=qname.size(); i; ) { if (qname[--i] == '/') break; } qdir=qname.substr(0, i); qname=qname.substr(i+1); ++found; ctlfile_close(&ctf); queuescan3(qdir, qname, de->d_name); } closedir(subde); if (stat(subdir.c_str(), &stat_buf) == 0 && stat_buf.st_mtime < current_time - 2 * 60 * 60) rmdir(subdir.c_str()); // Just give it a try } closedir(tmpdir); return (found); }
void msgq::queuescan() { static int queuescan_flag=0; if (queuescan_flag) return; // Recursive invocation if message just pulled into queue // has been delivered to all of its recipients. nextqueuefill=0; #if 0 clog_msg_start_info(); clog_msg_str("queue scan"); clog_msg_send(); #endif try { std::list<std::string> subdirlist; DIR *tmpdir=opendir(MSGQDIR); struct dirent *de; std::string s; queuescan_flag=1; if (!tmpdir) clog_msg_errno(); time(¤t_time); while ((de=readdir(tmpdir)) != 0) { const char *p; for (p=de->d_name; *p; p++) if (!isdigit((int)(unsigned char)*p)) break; if (*p) continue; // Subdirs must be named all digits p=de->d_name; time_t n=strtotime(p); std::list<std::string>::iterator sb, se; sb=subdirlist.begin(); se=subdirlist.end(); while (sb != se) { if (strtotime(sb->c_str()) > n) break; ++sb; } subdirlist.insert(sb, de->d_name); } closedir(tmpdir); while (!subdirlist.empty()) { s=MSGQDIR "/"; s += subdirlist.front(); subdirlist.pop_front(); if (queuescan2(s) <= 0) break; // Stop if we can't add any more msgs } queuescan_flag=0; } catch (...) { queuescan_flag=0; throw; } }
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); }
int SubmitFile::MessageEnd(unsigned rcptnum, int iswhitelisted, int filter_enabled) { int is8bit=0, dorewrite=0, rwmode=0; const char *mime=getenv("MIME"); unsigned n; struct stat stat_buf; if (sizelimit && bytecount > sizelimit) { std::cout << "523 Message length (" << sizelimit << " bytes) exceeds administrative limit." << std::endl << std::flush; return (1); } if (diskfull) { std::cout << "431 Mail system full." << std::endl << std::flush; return (1); } if (spamtrap_flag) { std::cout << "550 Spam refused." << std::endl << std::flush; return (1); } if (rwrfcptr->rfcviolation & RFC2045_ERR2COMPLEX) { std::cout << "550 Message MIME complexity exceeds the policy maximum." << std::endl << std::flush; return (1); } datfile << std::flush; if (datfile.fail()) clog_msg_errno(); ctlfile << std::flush; if (ctlfile.fail()) clog_msg_errno(); /* Run global filters for this message */ std::string dfile=namefile("D", 0); if (!mime || strcmp(mime, "none")) { if (mime && strcmp(mime, "7bit") == 0) { rwmode=RFC2045_RW_7BIT; is8bit=0; } if (mime && strcmp(mime, "8bit") == 0) rwmode=RFC2045_RW_8BIT; if (rfc2045_ac_check(rwrfcptr, rwmode)) dorewrite=1; } else (void)rfc2045_ac_check(rwrfcptr, 0); if (rwrfcptr->has8bitchars) is8bit=1; unlink(namefile("D", 1).c_str()); // Might be the GDBM file // if receipients read from headers. if (dorewrite) { int fd1=dup(datfile.fd()); int fd2; if (fd1 < 0) clog_msg_errno(); datfile.close(); if (datfile.fail()) clog_msg_errno(); if ((fd2=open(namefile("D", 1).c_str(), O_RDWR|O_CREAT|O_TRUNC, PERMISSION)) < 0) clog_msg_errno(); if (call_rfc2045_rewrite(rwrfcptr, fd1, fd2, PACKAGE " " VERSION)) { clog_msg_errno(); std::cout << "431 Mail system full." << std::endl << std::flush; return (1); } close(fd1); #if EXPLICITSYNC fsync(fd2); #endif fstat(fd2, &stat_buf); close(fd2); std::string p=namefile("D", 0); unlink(p.c_str()); if (rename(namefile("D", 1).c_str(), p.c_str()) != 0) clog_msg_errno(); } else { datfile.sync(); #if EXPLICITSYNC fsync(datfile.fd()); #endif fstat(datfile.fd(), &stat_buf); datfile.close(); if (datfile.fail()) clog_msg_errno(); } if (is8bit) { ctlfile << COMCTLFILE_8BIT << "\n" << std::flush; closectl(); if (num_control_files_created > 1) { for (n=1; n < num_control_files_created; n++) { std::string p=namefile("C", n); int nfd=open(p.c_str(), O_WRONLY | O_APPEND); if (nfd < 0) clog_msg_errno(); ctlfile.fd(nfd); ctlfile << COMCTLFILE_8BIT << "\n" << std::flush; if (ctlfile.fail()) clog_msg_errno(); #if EXPLICITSYNC ctlfile.sync(); fsync(ctlfile.fd()); #endif ctlfile.close(); if (ctlfile.fail()) clog_msg_errno(); } } } else { closectl(); } SubmitFile *voidp=this; if (filter_enabled && run_filter(dfile.c_str(), num_control_files_created, iswhitelisted, &SubmitFile::get_msgid_for_filtering, &voidp)) return (1); std::string cfile=namefile("C", 0); for (n=2; n <= num_control_files_created; n++) { if (link(dfile.c_str(), namefile("D", n).c_str()) != 0) clog_msg_errno(); } std::string okmsg("250 Ok. "); okmsg += basemsgid; int hasxerror=datafilter(dfile.c_str(), rcptnum, okmsg.c_str()); current_submit_file=0; if (num_control_files_created == 1) { if (rename(name1stctlfile().c_str(), cfile.c_str()) != 0) clog_msg_errno(); } else { if (rename(namefile("C", 1).c_str(), cfile.c_str()) != 0) clog_msg_errno(); } if (!hasxerror) { #if EXPLICITDIRSYNC size_t p=cfile.rfind('/'); if (p != std::string::npos) { std::string dir=cfile.substr(0, p); int fd=open(dir.c_str(), O_RDONLY); if (fd >= 0) { fsync(fd); close(fd); } } #endif std::cout << okmsg << std::endl << std::flush; } trigger(TRIGGER_NEWMSG); return (0); }
int courierbmain() { fd_set fds, fdc; struct mybuf trigger_buf; clog_open_syslog("courierd"); rw_init_verbose(1); if (rw_init_courier(0)) return (1); drvinfo::init(); libmail_changeuidgid(MAILUID, MAILGID); signal(SIGPIPE, SIG_IGN); respawnlo=config_time_respawnlo(); respawnhi=config_time_respawnhi(); retryalpha=config_time_retryalpha(); retrybeta=config_retrybeta(); retrygamma=config_time_retrygamma(); retrymaxdelta=config_retrymaxdelta(); flushtime=0; delaytime=config_time_submitdelay(); queuefill=config_time_queuefill(); nextqueuefill=0; purgedir(MSGQDIR); purgedir(MSGSDIR); rmdir(MSGQDIR); mkdir(MSGQDIR, 0755); trackpurge(); if ((triggerw=open(triggername, O_WRONLY, 0)) < 0 || (triggerr=open(triggername, O_RDONLY, 0)) < 0) clog_msg_errno(); time(&start_time); courierbstart=start_time; msgq::init(queuehi); shutdown_flag=0; FD_ZERO(&fds); unsigned nd; int maxfd=triggerr; FD_SET(maxfd, &fds); mybuf_init(&trigger_buf, triggerr); for (nd=0; nd<drvinfo::nmodules; nd++) { int fd=drvinfo::modules[nd].module_from.fd; if (fd > maxfd) maxfd=fd; FD_SET(fd, &fds); } ++maxfd; time_t current_time, next_time, shutdown_time; current_time=start_time; shutdown_time=0; msgq::queuescan(); // Initial queue scan int doscantmp=1; //////////////////////////////////////////////////////////////////////////// // // Main loop: // // A) Scan tmp for new messages. // // B) Schedule messages for delivery, when their time comes. // // C) Update statistics. // // D) Stop scheduling messages when restart signal received. // // E) Restart if there's no activity for 5 mins after we've been running // for respawnlo amount of times. // // F) When we've been running for respawnhi amount of time, stop scheduling // messages, restart when all deliveries have been completed. // // G) Receive delivery completion messages from modules. // //////////////////////////////////////////////////////////////////////////// for (;;) { msgq *mp; if (shutdown_flag) doscantmp=0; if (doscantmp) doscantmp=msgq::tmpscan(); if (shutdown_flag && msgq::inprogress == 0) break; if (current_time >= start_time + respawnhi && !shutdown_flag) { clog_msg_start_info(); clog_msg_str("SHUTDOWN: respawnhi limit reached."); clog_msg_send(); shutdown_flag=1; continue; } if (diskspacelow() && msgq::queuehead && msgq::queuehead->nextsenddel <= current_time) { clog_msg_start_err(); clog_msg_str("Low on disk space."); clog_msg_send(); for (mp=msgq::queuehead; mp && mp->nextsenddel <= current_time + 300; mp=mp->next) mp->nextsenddel=current_time+300; } if (!shutdown_flag) { while ((mp=msgq::queuehead) != 0 && mp->nextsenddel <= current_time) { mp->removewq(); mp->start_message(); } } if (nextqueuefill && nextqueuefill <= current_time) { nextqueuefill=0; if (!shutdown_flag) { msgq::queuescan(); } } next_time=0; if ( msgq::queuehead && msgq::queuehead->nextsenddel > current_time) next_time=msgq::queuehead->nextsenddel; if (!shutdown_flag && msgq::inprogress == 0 && shutdown_time == 0) { shutdown_time=current_time + 5 * 60; if (shutdown_time < start_time + respawnlo) shutdown_time=start_time + respawnlo; } if (shutdown_time && (next_time == 0 || shutdown_time < next_time)) next_time=shutdown_time; if (nextqueuefill) { /* Or, wake up next time we need to refill the queue */ if (next_time == 0 || nextqueuefill < next_time) { next_time=nextqueuefill; } } fdc=fds; struct timeval tv; if (next_time) { tv.tv_sec= next_time > current_time ? next_time - current_time:0; tv.tv_usec=0; } #if 1 { char buf[40]; clog_msg_start_info(); clog_msg_str("Waiting. shutdown time="); strcpy(buf, shutdown_time ? ctime(&shutdown_time):"none\n"); *strchr(buf, '\n')=0; clog_msg_str(buf); clog_msg_str(", wakeup time="); strcpy(buf, next_time ? ctime(&next_time):"none\n"); *strchr(buf, '\n')=0; clog_msg_str(buf); clog_msg_str(", queuedelivering="); clog_msg_int(msgq::queuedelivering); clog_msg_str(", inprogress="); clog_msg_int(msgq::inprogress); clog_msg_send(); } #endif while (select(maxfd, &fdc, (fd_set *)0, (fd_set *)0, (next_time && doscantmp == 0 ? &tv:(struct timeval *)0)) < 0) { if (errno != EINTR) clog_msg_errno(); } time(¤t_time); if (shutdown_time) { if (current_time >= shutdown_time && msgq::inprogress == 0) { clog_msg_start_info(); clog_msg_str("SHUTDOWN: respawnlo limit reached, system inactive."); clog_msg_send(); shutdown_flag=1; } shutdown_time=0; } /* Handle commands sent via the trigger pipe */ if (FD_ISSET(triggerr, &fdc)) { unsigned l; char cmdbuf[256]; int c; do { l=0; do { c=mybuf_get(&trigger_buf); if (l < sizeof(cmdbuf)-1) cmdbuf[l++]=c; } while (c != '\n'); cmdbuf[l]=0; if (strcmp(cmdbuf, TRIGGER_NEWMSG) == 0) doscantmp=1; else if (strcmp(cmdbuf, TRIGGER_RESTART) == 0) { shutdown_flag=1; clog_msg_start_info(); clog_msg_str("SHUTDOWN: Restarting..."); clog_msg_send(); } else if (strcmp(cmdbuf, TRIGGER_STOP) == 0) { clog_msg_start_info(); clog_msg_str("SHUTDOWN: Stopping..."); clog_msg_send(); _exit(99); } else if (strcmp(cmdbuf, TRIGGER_FLUSH) == 0) { flushtime=current_time; for (mp=msgq::queuehead; mp; mp=mp->next) mp->nextsenddel=current_time; } else if (strncmp(cmdbuf, TRIGGER_FLUSHMSG, sizeof(TRIGGER_FLUSHMSG)-1) == 0) { char *p; ino_t n; if ((p=strchr(cmdbuf, '\n')) != 0) *p=0; p=cmdbuf+sizeof(TRIGGER_FLUSHMSG)-1; while (*p == ' ') ++p; if (comparseqid(p, &n) == 0 && !shutdown_flag) msgq::flushmsg(n, p); } } while (mybuf_more(&trigger_buf)); } for (nd=0; nd<drvinfo::nmodules; nd++) { struct mybuf *p= &drvinfo::modules[nd].module_from; size_t delnum; int c; if (!FD_ISSET(p->fd, &fdc)) continue; do { delnum=0; while (errno=0, ((c=mybuf_get(p)) != '\n')) { if (c < 0) { if (errno == EINTR) continue; clog_msg_start_err(); clog_msg_str("MODULE "); clog_msg_str( drvinfo::modules[nd] .module->name); clog_msg_str(" ABORTED."); clog_msg_send(); _exit(1); } if (c >= '0' && c <= '9') delnum=delnum * 10 + (c-'0'); } msgq::completed(drvinfo::modules[nd], delnum); } while (mybuf_more(p)); if (!shutdown_flag) shutdown_time=0; } } close(triggerr); close(triggerw); drvinfo::cleanup(); return (0); }
void SubmitFile::ReceipientFilter(struct rw_transport *rw, const char *host, const char *addr, unsigned rcptnum) { if (rcptfilterlist_file.is_open()) { rcptfilterlist_file << num_control_files_created - 1 << std::endl << rcptcount - 1 << std::endl << rw->name << std::endl << host << std::endl << addr << std::endl << rcptnum << std::endl; return; } rcptfilterlist.push_back(RcptFilterInfo()); RcptFilterInfo &last_pos=rcptfilterlist.back(); last_pos.num_control_file=num_control_files_created - 1; last_pos.num_receipient=rcptcount - 1; last_pos.driver=rw; last_pos.host=host; last_pos.address=addr; last_pos.rcptnum=rcptnum; if (rcptfilterlist.size() > SPILLLEVEL) { std::string filename=namefile("R", 0); rcptfilterlist_file.open(filename.c_str(), std::ios::in | std::ios::out | std::ios::trunc); if (!rcptfilterlist_file.is_open()) { clog_msg_start_err(); clog_msg_str(filename.c_str()); clog_msg_str(": "); clog_msg_errno(); } unlink(filename.c_str()); /* Immediately delete it */ std::list<RcptFilterInfo>::iterator ab, ae; for (ab=rcptfilterlist.begin(), ae=rcptfilterlist.end(); ab != ae; ++ab) { RcptFilterInfo &p= *ab; rcptfilterlist_file << p.num_control_file << std::endl << p.num_receipient << std::endl << p.driver->name << std::endl << p.host << std::endl << p.address << std::endl << p.rcptnum << std::endl; } rcptfilterlist.clear(); } }
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)); } }