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); }
int rw_init_courier(const char *name) { unsigned i; int err=0; if (rw_init_verbose_flag) { clog_msg_start_info(); clog_msg_str("Loading STATIC transport module libraries."); clog_msg_send(); } if (rw_install_start()) return (-1); for (i=0; rw_static[i].name; i++) /* name != 0 really meaningless for static libs, but, ** what the heck... */ if ((name == 0 || strcmp(name, rw_static[i].name) == 0) && rw_install(rw_static[i].name, rw_static[i].rw_install, rw_static[i].rw_init)) err=1; if (err || rw_install_init()) { clog_msg_start_err(); clog_msg_str("Transport module installation has FAILED."); clog_msg_send(); return (-1); } return (0); }
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); }
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; } }
void msgq::init(unsigned qs) { queue.resize(qs); queuehashfirst.resize(qs); queuehashlast.resize(qs); queuehead=0; queuetail=0; queuefree=0; queuedelivering=0; queuewaiting=0; inprogress=0; backup_relay_driver=0; char *filename=config_localfilename("backuprelay"); char *buf=config_read1l(filename); free(filename); if (buf) { const char *h=strtok(buf, " \t\r\n"); if (h) { backup_relay=h; h=strtok(0, " \t\r\n"); if (!h) h="esmtp"; struct rw_transport *t=rw_search_transport(h); if (!t) { clog_msg_start_err(); clog_msg_str("BACKUP RELAY DRIVER NOT FOUND: "); clog_msg_str(h); clog_msg_send(); exit(1); } backup_relay_driver=(drvinfo *)t->udata; } } unsigned i; for (i=0; i<qs; i++) { queue[i].next=queuefree; queuefree=&queue[i]; queuehashfirst[i]=0; queuehashlast[i]=0; } }
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)); }
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); }
static void terminated_child(unsigned idx, unsigned delid) { if (ESMTP_TERMINATING(&info[idx])) { char *p; if ((p=info[idx].pendel) != 0) { char **cols=module_parsecols(info[idx].pendel); /* ** Clear to 0 to prevent infinite loop if fork fails ** in start_child. */ info[idx].pendel=0; start_child(idx, info[idx].pendel, cols); info[idx].pendel=p; send_child(idx, info[idx].pendel, cols); return; } info[idx].pid= -1; return; } /* Oops, something crashed. Clean it up */ clog_msg_start_err(); if (info[idx].pid < 0) { clog_msg_str("Unable to fork"); } else { clog_msg_str("Crashed child process "); clog_msg_uint(info[idx].pid); } if (ESMTP_BUSY(&info[idx])) { clog_msg_str(", while delivering to "); clog_msg_str(info[idx].host); module_completed(idx, delid); } clog_msg_send(); close(info[idx].cmdpipe); info[idx].cmdpipe= -1; info[idx].pid= -1; }
int cargs(int argc, char **argv, const struct courier_args *argp) { int argn; int i,l=0; const char *argval; if ((progname=strrchr(argv[0], '/')) != 0) ++progname; else progname=argv[0]; for (argn=1; argn<argc; argn++) { if (argv[argn][0] != '-') break; if (argv[argn][1] == 0) { ++argn; break; } if (argv[argn][0] != '-') break; for (i=0; argp[i].argname; i++) { l=strlen(argp[i].argname); if (strncmp(argv[argn]+1, argp[i].argname, l) == 0 && (argv[argn][l+1] == '=' || argv[argn][l+1] == '\0')) break; } if (!argp[i].argname) { clog_open_stderr(progname); clog_msg_start_err(); clog_msg_str("Invalid argument: "); clog_msg_str(argv[argn]); clog_msg_send(); exit(1); } if (*(argval=argv[argn]+1+l)) ++argval; if (argp[i].argval) *argp[i].argval=argval; if (argp[i].argfunc) (*argp[i].argfunc)(argval); } return (argn); }
static void reap_child(pid_t pid, int exit_status) { int n; fd_set fd0; struct timeval tv; FD_ZERO(&fd0); FD_SET(0, &fd0); tv.tv_sec=0; tv.tv_usec=0; if (select(1, &fd0, 0, 0, &tv) >= 0) { if (FD_ISSET(0, &fd0)) return; } n=wait_reforkchild(nchildren, children, pid); if (n < 0) { unsigned u; clog_open_syslog("perlfilter"); clog_msg_str("ERR: Unable to restart a child process" " - aborting."); clog_msg_send(); for (u=0; u<nchildren; u++) if (children[u] >= 0) kill(children[u], SIGKILL); exit(0); } if (n > 0) { wait_restore(); if (exit_status) { clog_open_syslog("perlfilter"); clog_msg_str("ERR: Child process "); clog_msg_uint(pid); clog_msg_str(" terminated - restarting."); clog_msg_send(); } perlfilter(); } }
static void kill1filter(struct filterinfo **p) { int waitstat; pid_t pid; struct filterinfo *pp= *p; *p=pp->next; clog_msg_start_info(); clog_msg_str("Stopping "); clog_msg_str(pp->filtername); clog_msg_send(); close(pp->fd0); while ((pid=wait(&waitstat)) >= 0 && pid != pp->p) ; free(pp->filtername); free(pp); }
void trackpurge(const char *trackdir) { struct stat stat_buf; if (stat(trackdir, &stat_buf) == 0 && stat_buf.st_uid != geteuid()) { clog_msg_start_err(); clog_msg_str("Warning: "); clog_msg_str(trackdir); clog_msg_str(" is not owned by the correct uid"); clog_msg_send(); } if (dopurge(trackdir)) { clog_msg_start_err(); clog_msg_str("Cannot open "); clog_msg_str(trackdir); clog_msg_send(); clog_msg_prerrno(); return; } }
static void purgedir(const char *dirname) { std::string buf; DIR *p; struct dirent *de; clog_msg_start_info(); clog_msg_str("Purging "); clog_msg_str(dirname); clog_msg_send(); p=opendir(dirname); if (!p) return; while ((de=readdir(p)) != NULL) { if (de->d_name[0] == '.') continue; buf=dirname; buf += '/'; buf += de->d_name; rmdir(buf.c_str()); // Just for the hell of it } closedir(p); }
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); }
/* ** sighup attempts to synchronize everything. */ static void sighup() { struct filterinfo **pp; DIR *dirp; struct dirent *de; /* Start any new filters */ dirp=opendir(FILTERACTIVEDIR); while (dirp && (de=readdir(dirp)) != 0) { char *n; int pipefd[2]; struct filterinfo *newp; if (de->d_name[0] == '.') continue; for (pp= &filterlist; *pp; pp= &(*pp)->next) if (strcmp( (*pp)->filtername, de->d_name) == 0) break; if (*pp) continue; n=malloc(sizeof(FILTERACTIVEDIR "/")+strlen(de->d_name)); if (!n) { perror("malloc"); exit(1); } strcat(strcpy(n, FILTERACTIVEDIR "/"), de->d_name); if ( (newp=(struct filterinfo *) malloc(sizeof(struct filterinfo))) == 0 || (newp->filtername=malloc(strlen(de->d_name)+1)) == 0) { perror("malloc"); exit(1); } strcpy(newp->filtername, de->d_name); newp->next=filterlist; filterlist=newp; if (pipe(pipefd) < 0) { perror("pipe"); exit(1); } newp->fd0=pipefd[1]; while ((newp->p=fork()) < 0) { perror("fork"); sleep(3); } if ( newp->p == 0) { dup2(pipefd[0], 0); close(pipefd[0]); close(pipefd[1]); execl(n, de->d_name, (char *)0); perror("exec"); exit(1); } free(n); close(pipefd[0]); if (fcntl(newp->fd0, F_SETFD, FD_CLOEXEC) < 0) { perror("fcntl"); exit(1); } clog_msg_start_info(); clog_msg_str("Starting "); clog_msg_str(newp->filtername); clog_msg_send(); } if (dirp) closedir(dirp); /* Then, kill any filters that should not be running any more */ for (pp= &filterlist; *pp; ) { char *n=activename( (*pp)->filtername ); if (access(n, 0) == 0) { free(n); pp= &(*pp)->next; continue; } free(n); kill1filter(pp); } /* Finally, remove any sockets for filters that don't run any more */ { static const char *dirs[2]={FILTERSOCKETDIR, ALLFILTERSOCKETDIR}; int i; for (i=0; i<2; i++) { dirp=opendir(dirs[i]); while (dirp && (de=readdir(dirp)) != 0) { char *n; if (de->d_name[0] == '.') continue; for (pp= &filterlist; *pp; pp= &(*pp)->next) if (strcmp( (*pp)->filtername, de->d_name) == 0) break; if (*pp) continue; n=malloc(strlen(dirs[i])+strlen(de->d_name)+2); if (!n) { perror("malloc"); exit(1); } strcat(strcat(strcpy(n, dirs[i]), "/"), de->d_name); unlink(n); free(n); } if (dirp) closedir(dirp); } } }
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); } }
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 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; } }
int submit_fork_virtual(char **args, char **env, void (*func)(const char *)) { int pipe0[2], pipe1[2]; printfunc=func; submit_error=1; log_error_prefix_func=0; if (pipe(pipe0)) { clog_msg_start_err(); clog_msg_str(LIBEXECDIR "/courier/submit: "); clog_msg_str(strerror(errno)); clog_msg_send(); return (-1); } if (pipe(pipe1)) { close(pipe0[0]); close(pipe0[1]); clog_msg_start_err(); clog_msg_str(LIBEXECDIR "/courier/submit: "); clog_msg_str(strerror(errno)); clog_msg_send(); return (-1); } submit_pid=fork(); if ( submit_pid == 0) { static char execfailed[]="400 Unable to submit message - service temporarily unavailable.\n"; close(pipe0[1]); close(pipe1[0]); dup2(pipe0[0], 0); dup2(pipe1[1], 1); close(pipe0[0]); close(pipe1[1]); if (chdir(courierdir())) exit(1); execve( LIBEXECDIR "/courier/submit", args, env ); clog_msg_start_err(); clog_msg_str(LIBEXECDIR "/courier/submit: "); clog_msg_str(strerror(errno)); clog_msg_send(); if (write(1, execfailed, sizeof(execfailed)-1) != 1) ; /* ignore */ exit(1); } close(pipe0[0]); close(pipe1[1]); if (submit_pid == -1) { clog_msg_start_err(); clog_msg_str(LIBEXECDIR "/courier/submit: "); clog_msg_str(strerror(errno)); clog_msg_send(); close(pipe0[1]); close(pipe1[0]); return (-1); } if (fcntl(pipe0[1], F_SETFD, FD_CLOEXEC) || (submit_to=fdopen(pipe0[1], "w")) == NULL) { clog_msg_start_err(); clog_msg_str(LIBEXECDIR "/courier/submit: "); clog_msg_str(strerror(errno)); clog_msg_send(); close(pipe0[1]); close(pipe1[0]); (void)submit_wait_noreset(); return (-1); } if (fcntl(pipe1[0], F_SETFD, FD_CLOEXEC) || (fromsubmit=fdopen(pipe1[0], "r")) == NULL) { clog_msg_start_err(); clog_msg_str(LIBEXECDIR "/courier/submit: "); clog_msg_str(strerror(errno)); clog_msg_send(); fclose(submit_to); close(pipe1[0]); (void)submit_wait_noreset(); return (-1); } submit_error=0; signal(SIGINT, sighandler); signal(SIGTERM, sighandler); signal(SIGHUP, sighandler); 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 msgq::start_message() { // // Schedule deliveries for this message. // #if 0 clog_msg_start_info(); clog_msg_str("Start message "); logmsgid(this); clog_msg_send(); #endif size_t n=rcptinfo_list.size(); if (n == 0) { done(this, 0); return; } size_t i; for (i=0; i<n; i++) { rcptinfo &ri=rcptinfo_list[i]; drvinfo *drvp=ri.delmodule; dlvrhost *hostp, *freehostp=0; // // Check if the same host has any deliveries in progress, or pending. // Search the hdlvrpfree/hdlvrplast link list for this host. // for (hostp=drvp->hdlvrplast; hostp; hostp=hostp->prev) { if (hostp->hostname == ri.delhost) { #if 0 clog_msg_start_info(); clog_msg_str("Found host "); clog_msg_str(ri.delhost); clog_msg_str(", # of deliveries="); clog_msg_uint(hostp->dlvrcount); clog_msg_send(); #endif break; } // // Just in case we don't find it, remember the last host at the end of the // MRU list which does not have any deliveries in progress. // if (!freehostp && drvp->hdlvrpfree == 0 && hostp->dlvrcount == 0) freehostp=hostp; // Recycle a host struct used longest ago } if (!hostp) { // // Did not find this host. // if (drvp->hdlvrpfree) { // // There's an unused host structure, take it. // hostp=drvp->hdlvrpfree; drvp->hdlvrpfree=hostp->next; } else if (freehostp) { // // Recycle a host structure for a host without any current deliveries, // remove it from the MRU list. // hostp=freehostp; if (hostp->next) hostp->next->prev=hostp->prev; else drvp->hdlvrplast=hostp->prev; if (hostp->prev) hostp->prev->next=hostp->next; else drvp->hdlvrpfirst=hostp->next; } if (hostp) { // // Ok, initialize this host, and stick it at the end of the MRU list. // hostp->hostname=ri.delhost; hostp->dlvrcount=0; hostp->next=0; if ((hostp->prev=drvp->hdlvrplast) != 0) drvp->hdlvrplast->next=hostp; else drvp->hdlvrpfirst=hostp; drvp->hdlvrplast=hostp; if (hostp->pending_list) hostp->pending_list->hostp=0; hostp->pending_list=0; } } // // Determine if we can start a new delivery. The conditions are: // A) We found an unused host structure, and the number of deliveries in // progress to this host is less than the maximum set for the module, // // AND // // B) The total number of current deliveries to this host is less than // the maximum set for this module (there's an available delivery slot // structure) // if (hostp && hostp->dlvrcount < drvp->maxhost && drvp->delpfreefirst) { delinfo *newdi=drvp->delpfreefirst; drvp->delpfreefirst=newdi->freenext; newdi->dlvrphost=hostp; newdi->rcptlist=&ri; ri.pending=0; hostp->dlvrcount++; startdelivery(drvp, newdi); continue; } // // We are unable to start the delivery at this time. At the recipient list // to the list of pending deliveries for this host. // // The first thing is to make sure that this host structure has the pending // list structure allocated. // pendelinfo *pi; if (hostp) { if ((pi=hostp->pending_list) == 0) { drvp->pendelinfo_list.push_back(pendelinfo()); std::list<pendelinfo>::iterator pos= --drvp->pendelinfo_list.end(); pi=& *pos; pi->pos=pos; pi->drvp=drvp; pi->hostname=ri.delhost; pi->hostp=hostp; hostp->pending_list=pi; } } else { std::list<pendelinfo>::iterator pos; // // We don't even have a host structure. // That's ok, search the pendelinfo_list of this module anyway. // pi=0; for (pos=drvp->pendelinfo_list.begin(); pos != drvp->pendelinfo_list.end(); ++pos) { pi=& *pos; if (pi->hostname == ri.delhost) break; pi=0; } if (!pi) { drvp->pendelinfo_list.push_back(pendelinfo()); pos= --drvp->pendelinfo_list.end(); pi=& *pos; pi->pos=pos; pi->drvp=drvp; pi->hostname=ri.delhost; pi->hostp=0; } } ri.pending=pi; pi->receipient_list.push_back(&ri); ri.pendingpos= --pi->receipient_list.end(); } }
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); }
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; }
static int faxsend_cleanup(int errcode, char *errmsg, void *vp) { struct faxconv_err_args *args=(struct faxconv_err_args *)vp; unsigned pages_sent=0; char *p, *q; int i; time_t now_time; unsigned coverpage_cnt=0; unsigned page_cnt=0; /* Check how many files sendfax renamed (were succesfully sent) */ while (args->file_list) { if (access(args->file_list->filename, 0) == 0) break; if (coverpage_cnt < args->n_cover_pages) ++coverpage_cnt; else ++pages_sent; args->file_list=args->file_list->next; } /* Strip out any blank lines in captured output from sendfax */ for (p=q=errmsg; *p; p++) { if (*p == '\n' && (p[1] == '\n' || p[1] == 0)) continue; *q++=*p; } *q=0; /* Find the last message from sendfax */ for (p=q=errmsg; *p; p++) { if (*p != '\n') continue; *p=0; /* Dump sendfax's output to the log */ if (*q) { clog_msg_start_info(); clog_msg_str("courierfax: " SENDFAX ": "); clog_msg_str(q); clog_msg_send(); } q=p+1; } if (*q) /* Last line of the error message */ { clog_msg_start_info(); clog_msg_str("courierfax: " SENDFAX ": "); clog_msg_str(q); clog_msg_send(); } else /* Default message */ { q=SENDFAX ": completed."; } /* ** Ugly hack: capture the following message from sendfax: ** ** /usr/sbin/sendfax: cannot access fax device(s) (locked?) */ #if 0 lockflag=0; p=strchr(q, ':'); if (p) { static const char msg1[]="cannot access fax device"; static const char msg2[]="locked"; ++p; while (*p && isspace((int)(unsigned char)*p)) p++; if (*p && strncasecmp(p, msg1, sizeof(msg1)-1) == 0) { p += sizeof(msg1); while (*p && !isspace((int)(unsigned char)*p)) ++p; p=strchr(p, '('); if (p && strncmp(p+1, msg2, sizeof(msg2)-1) == 0) { args->is_locked=1; clog_msg_start_info(); clog_msg_str("courierfax: detected locked" " modem line, sleeping..."); clog_msg_send(); sleep(60); return (-1); } } } #else if (errcode == 2) { args->is_locked=1; clog_msg_start_info(); clog_msg_str("courierfax: detected locked" " modem line, sleeping..."); clog_msg_send(); sleep(60); return (-1); } #endif ctlfile_append_connectioninfo(args->ctf, args->nreceip, COMCTLFILE_DELINFO_REPLY, q); sprintf(errmsg, "%u cover pages, %u document pages sent.", coverpage_cnt, page_cnt); i=ctlfile_searchfirst(args->ctf, COMCTLFILE_FAXEXPIRES); time(&now_time); ctlfile_append_reply(args->ctf, args->nreceip, errmsg, (pages_sent == 0 && i >= 0 && errcode < 10 && now_time < strtotime(args->ctf->lines[i]+1) ? COMCTLFILE_DELDEFERRED: COMCTLFILE_DELFAIL_NOTRACK), 0); return (-1); }
int msgq::queuescan3(std::string subdir, std::string name, const char *isnewmsg) { struct ctlfile ctlinfo; ino_t inum; time_t deltime, delsendtime; const char *p=name.c_str(); struct stat stat_buf; ++p; inum=strtoino(p); while (isdigit(*p)) p++; ++p; deltime=strtotime(p); name= subdir + '/' + name; if (ctlfile_openfn(name.c_str(), &ctlinfo, 0, 1)) { if (errno) { clog_msg_start_err(); clog_msg_str("Cannot read "); clog_msg_str(name.c_str()); clog_msg_send(); clog_msg_prerrno(); } return (0); } delsendtime=deltime; if (flushtime && ctlinfo.mtime < flushtime && flushtime < deltime) delsendtime=flushtime; if (!queuefree) { msgq *p; // // msgq array is full. See if we can remove the last message from the // pending queue. p=queuetail; if (p && p->nextsenddel > delsendtime) { #if 0 clog_msg_start_info(); clog_msg_str("Removing "); clog_msg_uint(p->msgnum); clog_msg_str(" to make room for this message."); clog_msg_send(); #endif p->removewq(); p->removeq(); } } msgq *newq=queuefree; if (!newq) { ctlfile_close(&ctlinfo); if (queuefill && nextqueuefill == 0) { nextqueuefill=current_time + queuefill; } return (1); } const char *cn=qmsgsctlname(inum); if ( stat(cn, &stat_buf) == -1 ) { unlink(name.c_str()); unlink(qmsgsdatname(inum)); unlink(cn); ctlfile_close(&ctlinfo); return (0); } #if 0 clog_msg_start_info(); clog_msg_str("Adding "); clog_msg_uint(inum); clog_msg_str(" to queue."); clog_msg_send(); #endif queuefree=newq->next; ++queuedelivering; ino_t hashbucket=inum % queuehashfirst.size(); if (queuehashfirst[hashbucket]) queuehashfirst[hashbucket]->prevhash=newq; else queuehashlast[hashbucket]=newq; newq->nexthash=queuehashfirst[hashbucket]; newq->prevhash=0; queuehashfirst[hashbucket]=newq; newq->nksize= (unsigned long)stat_buf.st_size; ctlinfo.starttime=stat_buf.st_mtime; int k=ctlfile_searchfirst(&ctlinfo, COMCTLFILE_MSGID); newq->msgid= k < 0 ? "": ctlinfo.lines[k]+1; newq->msgnum=inum; newq->nextdel=deltime; newq->nextsenddel=delsendtime; newq->rcptinfo_list.resize(0); newq->rcptcount=0; newq->cancelled=ctlinfo.cancelled; if (isnewmsg) { int auth; clog_msg_start_info(); clog_msg_str("newmsg,id="); logmsgid(newq); auth=ctlfile_searchfirst(&ctlinfo, COMCTLFILE_AUTHNAME); if (auth >= 0) { clog_msg_str(", auth="); clog_msg_str(ctlinfo.lines[auth]+1); } int m=ctlfile_searchfirst(&ctlinfo, COMCTLFILE_FROMMTA); if (m >= 0) { clog_msg_str(": "); clog_msg_str(ctlinfo.lines[m]+1); } clog_msg_send(); } unsigned i, j; std::string host, addr; k=ctlfile_searchfirst(&ctlinfo, COMCTLFILE_SENDER); struct rfc822t *sendert=rw_rewrite_tokenize(k < 0 ? "":ctlinfo.lines[k]+1); std::string errmsg; for (i=0; i<ctlinfo.nreceipients; i++) { for (j=0; ctlinfo.lines[j]; j++) { switch (ctlinfo.lines[j][0]) { case COMCTLFILE_DELSUCCESS: case COMCTLFILE_DELFAIL: if ((unsigned)atoi(ctlinfo.lines[j]+1) == i) break; // This one has been delivered default: continue; } break; } if (ctlinfo.lines[j]) continue; drvinfo *module=getdelinfo(sendert->tokens, ctlinfo.receipients[i], host, addr, errmsg); if (!module) { ctlfile_append_reply(&ctlinfo, i, errmsg.c_str(), SMTPREPLY_TYPE(errmsg.c_str()), 0); continue; } /* Check if it's time to move the message to a backup relay */ if (backup_relay_driver && ctlfile_searchfirst(&ctlinfo, COMCTLFILE_WARNINGSENT) >= 0 && strcmp(module->module->name, backup_relay_driver->module->name) == 0) { // module=backup_relay_driver; host=backup_relay; } /* Group all recipients for the same driver and host together */ for (j=0; j<newq->rcptcount; j++) if (strcmp(module->module->name, newq->rcptinfo_list[j].delmodule-> module->name) == 0 && newq->rcptinfo_list[j].delhost == host && newq->rcptinfo_list[j].addresses.size() < module->maxrcpt ) break; if (j == newq->rcptcount) { #if 0 clog_msg_start_info(); clog_msg_str("id="); logmsgid(newq); clog_msg_str(",new rcpt list - module="); clog_msg_str(module->module->name); clog_msg_str(", host="); clog_msg_str(host); clog_msg_send(); #endif newq->rcptinfo_list.resize(++newq->rcptcount); struct rw_info_rewrite rwir; struct rw_info rwi; rwir.buf=0; rwir.errmsg=0; rw_info_init(&rwi, sendert->tokens, rw_err_func); rwi.sender=0; rwi.mode=RW_OUTPUT|RW_ENVSENDER; rwi.udata= (void *)&rwir; rw_rewrite_module(module->module, &rwi, rw_rewrite_chksyn_print); char *address=((struct rw_info_rewrite *)rwi.udata)->buf; char *errmsg= ((struct rw_info_rewrite *)rwi.udata)->errmsg; if (!address) { ctlfile_append_reply(&ctlinfo, i, errmsg, SMTPREPLY_TYPE(errmsg), 0); newq->rcptinfo_list.resize(--newq->rcptcount); free(errmsg); continue; } if (errmsg) free(errmsg); newq->rcptinfo_list[j].init(newq, module, host, address); free(address); } #if 0 clog_msg_start_info(); clog_msg_str("id="); logmsgid(newq); clog_msg_str(",module="); clog_msg_str(module->module->name); clog_msg_str(", host="); clog_msg_str(host); clog_msg_str(", addr=<"); clog_msg_str(addr); clog_msg_str(">"); clog_msg_send(); #endif newq->rcptinfo_list[j].addresses.push_back(addr); newq->rcptinfo_list[j].addressesidx.push_back(i); } rfc822t_free(sendert); ctlfile_close(&ctlinfo); if (newq->nextsenddel <= current_time || /* ** If there are no more recipients, we want to call done() via ** start_message. HOWEVER, if DSN injection FAILED, we want to respect ** the rescheduled delivery attempt time. We can detect that if isnewmsg == 0 */ (newq->rcptinfo_list.size() == 0 && isnewmsg)) { newq->start_message(); return (0); } msgq *qp, *qprev; for (qprev=queuetail, qp=0; qprev; qp=qprev, qprev=qp->prev) if (qprev->nextsenddel < newq->nextsenddel) break; newq->next=qp; newq->prev=qprev; if (qprev) qprev->next=newq; else queuehead=newq; if (qp) qp->prev=newq; else queuetail=newq; ++queuewaiting; 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 int read_resp_oneline(char *errcode, int doprint) { static char linebuf[BUFSIZ]; char *c; int rc; char prev_errcode=errcode[0]; if (submit_error || fgets(linebuf, sizeof(linebuf), fromsubmit) == NULL) { const char *errmsg="Service temporarily unavailable."; if (!errcode[0]) strcpy(errcode, "432"); if (!submit_error) { submit_error=1; clog_msg_start_err(); clog_msg_str("submitclient: EOF from submit."); #if 0 { const char *p=getenv("TCPREMOTEIP"); if (p) { clog_msg_str(" (ip: "); clog_msg_str(p); clog_msg_str(")"); } } #endif clog_msg_send(); } if (doprint) { (*printfunc)(errcode); (*printfunc)(" "); (*printfunc)(errmsg); (*printfunc)(doprint & PRINT_CRLF ? "\r\n":"\n"); (*printfunc)(0); } return(-1); } if (!isdigit((int)(unsigned char)linebuf[0]) || !isdigit((int)(unsigned char)linebuf[1]) || !isdigit((int)(unsigned char)linebuf[2]) || (errcode[0] && strncmp(errcode, linebuf, 3)) || (linebuf[3] != ' ' && linebuf[3] != '-')) { char *p; clog_msg_start_err(); clog_msg_str("submitclient: bad response from submit: "); if ((p=strchr(linebuf, '\n')) != 0) *p=0; clog_msg_str(linebuf); if (p) *p='\n'; clog_msg_send(); if (!errcode[0]) strcpy(errcode, "432"); if (doprint) { (*printfunc)(errcode); (*printfunc)(" "); } submit_error=1; rc=1; } else { if (!errcode[0]) { memcpy(errcode, linebuf, 3); errcode[3]=0; } rc=0; if (linebuf[3] == ' ') rc=1; } if ((c=strchr(linebuf, '\n')) != NULL) *c=0; switch (errcode[0]) { case '1': case '2': case '3': if (doprint & PRINT_ERRORS) doprint=0; break; default: if (prev_errcode == 0 && errcode[0] == '5' && teergrubefunc) { (*teergrubefunc)(); } if (log_error_prefix_func) { clog_msg_start_err(); (*log_error_prefix_func)(); clog_msg_str(linebuf); clog_msg_send(); } } if (doprint) (*printfunc)(linebuf); if (!c) { int ch; int n=0; char buf[256]; while ((ch=getc(fromsubmit)) != '\n') { if (ch < 0) { submit_error=1; clog_msg_start_err(); clog_msg_str("submitclient: EOF from submit."); #if 0 { const char *p=getenv("TCPREMOTEIP"); if (p) { clog_msg_str(" (ip: "); clog_msg_str(p); clog_msg_str(")"); } } #endif clog_msg_send(); break; } if (doprint) { buf[n++]=ch; if (n == sizeof(buf)-1) { buf[n]=0; (*printfunc)(buf); n=0; } } } if (doprint) { buf[n]=0; (*printfunc)(buf); } } if (doprint) { (*printfunc)(doprint & PRINT_CRLF ? "\r\n":"\n"); (*printfunc)(0); } return (rc); }
static void perlfilter() { char *embedding[] = { "", WRAPPERPL }; char *args[] = { "", "0", NULL, NULL, NULL}; int exitstatus = 0; int sock; PerlInterpreter *my_perl; if((my_perl = perl_alloc()) == NULL) { fprintf(stderr, "no memory!"); exit(1); } perl_construct(my_perl); exitstatus = perl_parse(my_perl, xs_init, 2, embedding, NULL); if (exitstatus || (exitstatus=perl_run(my_perl)) != 0) { fprintf(stderr, "Cannot parse " WRAPPERPL "\n"); exit(exitstatus); } while ((sock=lf_accept(listen_sock)) >= 0) { char sockbuf[100]; args[0]=filter; sprintf(sockbuf, "%d", sock); args[2]=sockbuf; { dSP ; ENTER ; SAVETMPS ; perl_call_argv("Embed::Persistent::eval_file", G_VOID | G_DISCARD | G_EVAL, args); #ifdef ERRSV if(SvTRUE(ERRSV)) #else if(SvTRUE(GvSV(errgv))) #endif { clog_open_syslog("perlfilter"); clog_msg_start_err(); clog_msg_str("eval error: "); #ifdef ERRSV clog_msg_str(SvPV(ERRSV,PL_na)); #else clog_msg_str(SvPV(GvSV(errgv),na)); #endif clog_msg_send(); } FREETMPS ; LEAVE ; } close(sock); /* Just in case */ } #ifdef perl_destruct_level perl_destruct_level=0; #else PL_perl_destruct_level=0; #endif perl_destruct(my_perl); perl_free(my_perl); exit(0); }
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)); } }