static int faxconvert_cleanup(int dummy, char *errmsg, void *vp) { struct faxconv_err_args *args=(struct faxconv_err_args *)vp; ctlfile_append_reply(args->ctf, args->nreceip, errmsg, COMCTLFILE_DELFAIL_NOTRACK, 0); return (-1); }
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); }
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 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); }
static void wait_delivery(pid_t pid, struct ctlfile *ctf, unsigned rcptnum, int msgpipe, int fwdpipe, const char *user, const char *ext) { fd_set fds; int maxfd= msgpipe > fwdpipe ? msgpipe:fwdpipe; char buf[BUFSIZ]; int waitstat; char *sender=verp_getsender(ctf, ctf->receipients[rcptnum]); const char *oreceipient; char *oreceipients=0; if ( ctf->oreceipients[rcptnum] == 0 || ctf->oreceipients[rcptnum][0]) { oreceipient=ctf->oreceipients[rcptnum]; } else { oreceipient=oreceipients=dsnencodeorigaddr( ctf->receipients[rcptnum]); } ++maxfd; msglen=0; fwdlen=0; while (msgpipe >= 0 && fwdpipe >= 0) { int l; FD_ZERO(&fds); if (msgpipe >= 0) FD_SET(msgpipe, &fds); if (fwdpipe >= 0) FD_SET(fwdpipe, &fds); if (select(maxfd, &fds, 0, 0, 0) < 0) { clog_msg_prerrno(); continue; } if (msgpipe >= 0 && FD_ISSET(msgpipe, &fds)) { if ( (l=read(msgpipe, buf, sizeof(buf))) <= 0) { close(msgpipe); msgpipe=-1; } else savemsg(buf, l); } if (fwdpipe >= 0 && FD_ISSET(fwdpipe, &fds)) { if ( (l=read(fwdpipe, buf, sizeof(buf))) <= 0) { close(fwdpipe); fwdpipe=-1; } else fwdmsg(buf, l, sender, ctf->receipients[rcptnum], oreceipient); } } free(sender); while ( wait(&waitstat) != pid) ; if (msglen) { char *p, *q; msgbuf[msglen]=0; for (p=msgbuf; *p; ) { for (q=p; *q; ) { if (*q == '\n') { *q++=0; break; } q++; } ctlfile_append_info(ctf, rcptnum, p); p=q; } } if (submit_started && !submit_err) { FILE *f=fopen(qmsgsdatname(ctf->n), "r"); if (!f) submit_err=1; else { int c; fprintf(submit_to, "\nDelivered-To: %s\n", ctf->receipients[rcptnum]); while ((c=getc(f)) != EOF) putc(c, submit_to); if (ferror(f) || fflush(submit_to) || fclose(submit_to)) { submit_cancel(); submit_err=1; } else if (submit_readrcprinterr()) { submit_wait(); submit_err=1; } else if (submit_wait()) submit_err=1; } } if (WIFEXITED(waitstat)) switch (WEXITSTATUS(waitstat)) { case 0: case 99: if (submit_err) { ctlfile_append_reply(ctf, rcptnum, errbuf, COMCTLFILE_DELFAIL, 0); } else { ctlfile_append_reply(ctf, rcptnum, "Message delivered.", COMCTLFILE_DELSUCCESS, " l"); } if (oreceipients) free (oreceipients); return; case EX_SOFTWARE: ctlfile_append_reply(ctf, rcptnum, "", COMCTLFILE_DELFAIL_NOTRACK, 0); if (oreceipients) free (oreceipients); return; case 64: case 65: case 67: case 68: case 69: case 76: case 77: case 78: case 100: case 112: break; default: ctlfile_append_reply(ctf, rcptnum, "", COMCTLFILE_DELDEFERRED, 0); if (oreceipients) free (oreceipients); return; } ctlfile_append_reply(ctf, rcptnum, "", COMCTLFILE_DELFAIL, 0); if (oreceipients) free (oreceipients); }
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); }