cdesktopenv/cde/programs/dtmail/libDtMail/RFC/RFCTransport.C

1287 lines
31 KiB
C

/*
* CDE - Common Desktop Environment
*
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these libraries and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/*
*+SNOTICE
*
*
* $TOG: RFCTransport.C /main/18 1998/07/24 16:09:46 mgreess $
*
* RESTRICTED CONFIDENTIAL INFORMATION:
*
* The information in this document is subject to special
* restrictions in a confidential disclosure agreement between
* HP, IBM, Sun, USL, SCO and Univel. Do not distribute this
* document outside HP, IBM, Sun, USL, SCO, or Univel without
* Sun's specific written approval. This document and all copies
* and derivative works thereof must be returned or destroyed at
* Sun's request.
*
* Copyright 1993, 1994, 1995 Sun Microsystems, Inc. All rights reserved.
*
*+ENOTICE
*/
#ifndef I_HAVE_NO_IDENT
#endif
#ifdef __ppc
#include <DtMail/Buffer.hh>
#endif
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <pwd.h>
#include <time.h>
#include <signal.h>
#include <assert.h>
#include <DtMail/DtMail.hh>
#include <DtMail/DtMailP.hh>
#include "RFCImpl.hh"
#include "SigChldImpl.hh"
#include "RFCMIME.hh"
#include "SunV3.hh"
#include "AliasExpand.hh"
#include <DtMail/Threads.hh>
#include <DtMail/IO.hh>
#ifdef HAS_VFORK
#define DTMAIL_FORK vfork
#else
#define DTMAIL_FORK fork
#endif
// pipe between childHandler and XtAppAddInput
static int _transfds[2];
// list of children that have been forked
static waitentry_t *_waitlist;
// Note on error handling in wroteToFileDesc: this is used
// to write e-mail to all open *files* (e.g. recording to
// file, mail to file), so we attempt to write to all file
//
static unsigned long
writeToFileDesc(const char * buf, int len, va_list args)
{
int * fds = va_arg(args, int *);
int cnt = va_arg(args, int);
DtMailBoolean strip = (DtMailBoolean)va_arg(args, int);
unsigned long saveErrno = 0; // Initially no error recorded
for (int fd = 0; fd < cnt; fd++) {
int status = 0;
do {
if (strip)
status = SafeWriteStrip(fds[fd], buf, len);
else
status = SafeWrite(fds[fd], buf, len);
} while (status < 0 && errno == EAGAIN);
if (status < 0 && errno != 0) // Did an error occur??
saveErrno = (unsigned long)errno; // Yes: remember "last" errno
}
return(saveErrno); // return last error recorded
}
RFCTransport::RFCTransport(DtMailEnv & error,
DtMail::Session * session,
DtMailStatusCallback cb,
void * cb_data,
const char * impl)
: DtMail::Transport(error, session, cb, cb_data)
{
error.clear();
_object_valid = new Condition;
_object_valid->setTrue();
_impl = strdup(impl);
// Set up the handlers so that we are notified when a child
// process exits and do the right thing.
signalRegister();
_error_proc = NULL;
_smd = NULL;
}
RFCTransport::~RFCTransport(void)
{
_object_valid->setFalse();
free(_impl);
}
DtMailOperationId
RFCTransport::submit(DtMailEnv & error, DtMail::Message * msg, DtMailBoolean log_msg)
{
waitentry_t * new_we;
int child_pid;
// Create a list of information about all child processes.
if ((new_we = (waitentry_t *) malloc(sizeof(waitentry_t))) == NULL)
{
error.setError (DTME_NoMemory);
return(NULL);
}
// fork a new process
switch(child_pid = (int) fork()) {
case -1:
{
// if the fork fails, cleanup
free (new_we);
error.setError (DTME_NoMemory);
break;
}
case 0: /* child */
{
// reset all signal handlers
for (int sig = 1; sig < NSIG; sig++)
{
(void) signal(sig, SIG_DFL);
}
DtMailEnv tmp_error;
tmp_error.clear();
format(tmp_error, msg, log_msg);
_exit((int)((DTMailError_t)tmp_error));
}
default: /* parent */
{
// Save information about each child process (sendmail)
// that we fork/exec.
new_we->pid = child_pid;
new_we->proc = this->_error_proc;
new_we->data = this->_smd;
new_we->next = _waitlist;
_waitlist = new_we;
return(NULL);
}
}
return(NULL);
}
void
RFCTransport::format(DtMailEnv & error, DtMail::Message * dtmsg, DtMailBoolean log_msg)
{
// Clean up the message addressing for delivery.
//
// DtMailEnv * thr_error = new DtMailEnv();
// thr_error->clear();
DtMailValueSeq value;
char * to = NULL;
char * cc = NULL;
char * bcc = NULL;
DtMail::Envelope * env = dtmsg->getEnvelope(error);
DtMailAddressSeq addr_tokens(32);
DtMailEnv tmp_error;
tmp_error.clear();
env->getHeader(tmp_error, DtMailMessageTo, DTM_TRUE, value);
if (tmp_error.isNotSet()) {
to = concatValue(value);
DtMailAddressSeq to_tokens(16);
arpaPhrase(to, to_tokens);
rfcAliasExpand(error, *_session->mailRc(error), to_tokens);
appendAddrs(addr_tokens, to_tokens);
skinFiles(to_tokens);
char * new_to = addrToString(to_tokens);
env->setHeader(tmp_error, "To", DTM_TRUE, new_to);
delete [] new_to;
free(to);
}
// Clear the error structure before passing it to a function.
tmp_error.clear();
value.clear();
env->getHeader(tmp_error, DtMailMessageCc, DTM_TRUE, value);
if (tmp_error.isNotSet()) {
cc = concatValue(value);
DtMailAddressSeq cc_tokens(16);
arpaPhrase(cc, cc_tokens);
rfcAliasExpand(error, *_session->mailRc(error), cc_tokens);
appendAddrs(addr_tokens, cc_tokens);
skinFiles(cc_tokens);
char * new_cc = addrToString(cc_tokens);
env->setHeader(tmp_error, "Cc", DTM_TRUE, new_cc);
delete [] new_cc;
free(cc);
}
// Clear the error structure before passing it to a function.
tmp_error.clear();
value.clear();
env->getHeader(tmp_error, DtMailMessageBcc, DTM_TRUE, value);
if (tmp_error.isNotSet()) {
bcc = concatValue(value);
DtMailAddressSeq bcc_tokens(16);
arpaPhrase(bcc, bcc_tokens);
rfcAliasExpand(error, *_session->mailRc(error), bcc_tokens);
appendAddrs(addr_tokens, bcc_tokens);
free(bcc);
}
value.clear();
// Note for now we ignore all errors from "rfcAliasExpand"
//
error.clear();
// Now we format the message.
// These messages do NOT include content-length as they are intended
// only for the delivery agent. If we are writing to local files,
// after remote delivery the local delivery will generate its own
// messages that have content-length included
//
RFCFormat * fmt;
RFCFormat * logFmt;
if (strcmp(_impl, "Internet MIME") == 0) {
fmt = new RFCMIME(_session);
logFmt = new RFCMIME(_session);
}
else {
fmt = new SunV3(_session);
logFmt = new SunV3(_session);
}
// Prepare to fire up the delivery agent
// The delivery agent preprocessor examines each addressee and if it is
// really a local file (eg folders) the local file is opened and its file
// descriptor is appended to the "log_files" array, which is allocated
// by the delivery agent preprocessor via 'new'. Upon return to us, if
// the log_count is > 0 we must format the message to be stored in a
// local file (include content length) and cause the message to be
// written to each open file
//
int *log_files = 0;
int log_count = 0;
{ // Create local scope to contain headers/bodies so that they go away
// as soon as they are no longer needed, before we potentially
// create a duplicate set for writing to local files
//
BufferMemory headers(1024);
BufferMemory bodies(8192);
error.clear();
//call unsetIsWriteBcc so that the Bcc field will not write to headers
// buffer (see aix defect 177089
fmt->unsetIsWriteBcc();
fmt->msgToBuffer(error, *dtmsg, DTM_FALSE, DTM_FALSE, DTM_FALSE,
headers, bodies);
if (!error.isSet())
deliver(error, addr_tokens, headers, bodies, log_msg, &log_files, log_count);
}
delete fmt;
// If any log files have been opened, format this message for local
// storage and cause the message to be written to all open files
//
tmp_error.clear(); // Used to cache error from logging to files
if (log_count) {
BufferMemory logHeaders(1024);
BufferMemory logBodies(8192);
assert(log_files != NULL); // if log files open, must have array
//
// Mark the message as having been read.
//
env->setHeader(error, "Status", DTM_TRUE, "RO");
//call setIsWriteBcc so that the Bcc field will write to logHeaders
// buffer (see aix defect 177089)
logFmt->setIsWriteBcc();
logFmt->msgToBuffer(tmp_error, *dtmsg, DTM_TRUE, DTM_FALSE, DTM_FALSE,
logHeaders, logBodies);
if (!tmp_error.isSet()) {
unsigned long iterateErrno, iterateErrno1;
iterateErrno = logHeaders.iterate(writeToFileDesc, log_files, log_count, DTM_TRUE);
iterateErrno1 = logBodies.iterate(writeToFileDesc, log_files, log_count, DTM_TRUE);
if ( (iterateErrno != 0) || (iterateErrno1 != 0) )
tmp_error.setError(DTME_ObjectCreationFailed);
}
closeLogFiles(log_files, log_count);
delete log_files;
}
delete logFmt;
/* Comment out this code because it's part of old thread code. Why
execute code that does nothing if we don't have to?
// Tell the requestor we're all done now.
//
DtMailEventPacket packet;
packet.key = _key;
packet.target = DTM_TARGET_TRANSPORT;
packet.target_object = this;
packet.operation = (void *)ThreadSelf();
packet.argument = thr_error;
packet.event_time = time(NULL);
if (_object_valid->state()) {
_session->writeEventData(error, &packet, sizeof(DtMailEventPacket));
}
*/
// If no error occurred during the deliver invocation, but
// an error occurred during local file delivery, propagate
// that error into the error returned to the caller
//
if (!error.isSet() && tmp_error.isSet())
error.setError((DTMailError_t)tmp_error);
return;
}
void
RFCTransport::deliver(DtMailEnv & error,
DtMailAddressSeq & addr_tokens,
Buffer & headers,
Buffer & bodies,
DtMailBoolean log_msg,
int **log_files,
int & log_count)
{
DtMailEnv merror;
merror.clear();
const char * value;
assert(log_files != NULL); // must provide -> log_files ->
// We want to make an argv list that is big enough to hold all
// of the addresses. Of course, this may need to be expanded
// because of local aliases, but we'll work on that.
//
char ** argv = (char **)malloc(sizeof(char *) * (addr_tokens.length() + 5));
int argc = 1;
*log_files = 0;
log_count = 0;
if (log_msg == DTM_TRUE) {
const char * log;
char *buf = NULL;
_session->mailRc(merror)->getValue(merror, "record", &log);
if (merror.isSet()) {
log = "~/sent.mail";
}
if (*log != '/' && *log != '~' && *log != '+' && *log != '$') {
_session->mailRc(merror)->getValue(merror, "outfolder", &value);
buf = (char *)malloc(strlen(log) + 3);
if (buf != NULL) {
if (merror.isSet()) {
// "outfolder" is not set. Relative to home directory
strcpy(buf, "~/");
merror.clear();
} else {
// "outfolder" is set. Relative to folder directory
strcpy(buf, "+");
}
strcat(buf, log);
log = buf;
}
}
int fd = openLogFile(log);
if (fd >= 0) {
if (!*log_files)
* log_files = new int[addr_tokens.length() + 4];
(*log_files)[log_count++] = fd;
}
if (buf != NULL) {
free(buf);
}
}
// We add the correct parameter to sendmail so that it will ignore
// lines with a single dot(.) in them. We also check to see
// if we should add a couple of optional parameters: metoo and
// verbose.
argv[argc++] = "-oi";
_session->mailRc(merror)->getValue(merror, "metoo", &value);
if (merror.isNotSet()) {
// "metoo" is set.
argv[argc++] = "-m";
} else {
merror.clear();
}
_session->mailRc(merror)->getValue(merror, "verbose", &value);
if (merror.isNotSet()) {
// verbose is set.
argv[argc++] = "-v";
} else {
merror.clear();
}
// We now look at each address. If the name starts with a + or
// "/" then look further. If the name contains an "@" we assume
// it is probably an email address. Otherwise we assume it is
// a file and copy it to the file system.
//
int is_addr;
const char * at;
for (int addr = 0; addr < addr_tokens.length(); addr++) {
switch (*(addr_tokens[addr]->dtm_address)) {
case '+':
case '/':
is_addr = 0;
for (at = addr_tokens[addr]->dtm_address; *at; at++) {
if (*at == '@') {
is_addr = 1;
break;
}
}
if (is_addr) {
argv[argc++] = addr_tokens[addr]->dtm_address;
}
else {
int fd = openLogFile(addr_tokens[addr]->dtm_address);
if (fd >= 0) {
if (!*log_files)
* log_files = new int[addr_tokens.length() + 4];
(*log_files)[log_count++] = fd;
}
}
break;
default:
argv[argc++] = addr_tokens[addr]->dtm_address;
}
}
argv[argc] = NULL;
launchSendmail(error, headers, bodies, argv);
assert((!log_count && ! *log_files) || (log_count && *log_files));
free(argv);
}
static void
skin_comma(char * buf)
{
char * last_c = &buf[strlen(buf) - 1];
while(last_c > buf && isspace((unsigned char)*last_c)) {
*last_c = 0;
last_c -= 1;
}
if (last_c > buf && *last_c == ',') {
*last_c = 0;
}
}
void
RFCTransport::arpaPhrase(const char * name, DtMailAddressSeq & tokens)
{
char c;
const char *cp;
char *cp2;
char *nbufp;
char *bufend;
int gotlt, lastsp, didq, rem;
int nesting, extra;
int token = 1;
int comma = 0;
DtMailValueAddress * addr;
if (name == (char *) 0) {
return;
}
/* count comma's; we may need up to one extra space per comma... */
extra = 1;
cp = name;
for (;;) {
cp = strchr(cp, ',');
if (!cp) break;
for(cp += 1; *cp && isspace((unsigned char)*cp); cp++) {
extra++;
}
}
nbufp = (char *)malloc(strlen(name) + extra);
char * tok_start = nbufp;
int tok_len;
int count_at_comma = 0;
gotlt = 0;
lastsp = 0;
bufend = nbufp;
cp = name;
for (cp = name, cp2 = bufend; (c = *cp++) != 0;) {
switch (c) {
case '(':
/*
Start of a comment, ignore it.
*/
nesting = 1;
while ((c = *cp) != 0) {
cp++;
switch(c) {
case '\\':
if (*cp == 0) goto outcm;
cp++;
break;
case '(':
nesting++;
break;
case ')':
--nesting;
break;
}
if (nesting <= 0) break;
}
outcm:
lastsp = 0;
break;
case '"':
/*
Start a quoted string.
Copy it in its entirety.
*/
didq = 0;
while ((c = *cp) != 0) {
cp++;
switch (c) {
case '\\':
if ((c = *cp) == 0) goto outqs;
cp++;
break;
case '"':
goto outqs;
}
if (gotlt == 0 || gotlt == '<') {
if (lastsp) {
lastsp = 0;
*cp2++ = ' ';
}
if (!didq) {
*cp2++ = '"';
didq++;
}
*cp2++ = c;
}
}
outqs:
if (didq)
*cp2++ = '"';
lastsp = 0;
break;
case ' ':
case '\t':
case '\n':
if (token && (!comma || c == '\n')) {
done:
addr = new DtMailValueAddress;
tok_len = cp2 - tok_start;
addr->dtm_address = (char *)malloc(tok_len + 1);
memcpy(addr->dtm_address, tok_start, tok_len);
addr->dtm_address[tok_len] = 0;
addr->dtm_person = NULL;
addr->dtm_namespace = strdup(DtMailAddressDefault);
skin_comma(addr->dtm_address);
tokens.append(addr);
while(*cp && isspace((unsigned char)*cp)) {
cp++;
}
tok_start = cp2;
}
break;
case ',':
*cp2++ = c;
if (gotlt != '<') {
bufend = cp2 + 1;
count_at_comma = tokens.length();
gotlt = 0;
if (token) {
count_at_comma++;
goto done;
}
}
break;
case '<':
cp2 = bufend;
for (rem = (tokens.length() - 1); tokens.length() > count_at_comma;
rem = (tokens.length() - 1)) {
DtMailValueAddress * bad_addr = tokens[rem];
delete bad_addr;
tokens.remove(rem);
}
tok_start = cp2;
gotlt = c;
lastsp = 0;
break;
case '>':
if (gotlt == '<') {
gotlt = c;
break;
}
/* FALLTHROUGH . . . */
default:
if (gotlt == 0 || gotlt == '<') {
if (lastsp) {
lastsp = 0;
*cp2++ = ' ';
}
*cp2++ = c;
}
break;
}
}
*cp2 = 0;
if (cp2 > tok_start) {
addr = new DtMailValueAddress;
addr->dtm_address = strdup(tok_start);
addr->dtm_person = NULL;
addr->dtm_namespace = strdup(DtMailAddressDefault);
skin_comma(addr->dtm_address);
tokens.append(addr);
}
free(nbufp);
}
//
// SendMsgDialog has some info that is needed here.
//
void
RFCTransport::initTransportData(int fds[2], SubProcessFinishedProc proc,
void *ptr)
{
_transfds[0] = fds[0];
_transfds[1] = fds[1];
_smd = ptr;
_error_proc = proc;
}
//
// Pass a ptr to sendmailReturnProc to SendMsgDialog so that it can
// call it in XtAppAddInput
//
void *
RFCTransport::getSendmailReturnProc(void)
{
return ((void *)(&RFCTransport::sendmailReturnProc));
}
void
RFCTransport::launchSendmail(DtMailEnv & error,
Buffer & headers,
Buffer & bodies,
char ** argv)
{
// We need to retrieve the name of the sendmail program.
// if none is set then we use the default mailer.
//
const char * mailer;
_session->mailRc(error)->getValue(error, "sendmail", &mailer);
if (error.isSet()) {
#if defined(__OpenBSD__)
mailer = "/usr/sbin/sendmail";
#else
mailer = "/usr/lib/sendmail";
#endif
}
error.clear();
argv[0] = (char *)mailer;
// If we have only one arg, then everything goes to the log files.
// Don't do the fork and exec.
//
if (argv[1] == NULL)
return;
// We need a pipe to write the message to sendmail.
//
int inputPipe[2];
const int pipeReader = 0; // pipe[0] is read side of pipe
const int pipeWriter = 1; // pipe[1] is write side of pipe
if (pipe(inputPipe)==-1) { // Attempt to get a pipe
error.setError(DTME_NoMemory); // this must be really bad...
return;
}
// We need to fork and send the data to sendmail.
// Use vfork when available because the only purpose
// of the child is to do an exec
//
pid_t childPid = DTMAIL_FORK();
if (childPid == 0) { // The child **********
// Need to clean up a bit before exec()ing the child
// Close all non-essential open files, signals, etc.
// NOTE: probably reduce priv's to invoking user too???
//
long maxOpenFiles = sysconf(_SC_OPEN_MAX);
if (maxOpenFiles < 32) // less than 32 descriptors?
maxOpenFiles = 1024; // don't believe it--assume lots
for (int sig = 1; sig < NSIG; sig++)
(void) signal(sig, SIG_DFL); // REset all signal handlers
// input pipe reader is stdin
if (SafeDup2(inputPipe[pipeReader], STDIN_FILENO) == -1)
_exit (1); // ERROR: exit with bad status
// NOTE: we leave standard output and standard error output open
(void) SafeClose(inputPipe[pipeWriter]); // input pipe writer n/a
for (int cfd = 3; cfd < maxOpenFiles; cfd++)
(void) SafeClose(cfd); // close all open file descriptors
#if 0
printf("Command: %s\n",mailer);
int k=0;
while (NULL != argv[k])
{
printf("Command line %d: %s\n", k, argv[k]);
k++;
}
#endif
(void) execvp(mailer, (char * const *)argv);
_exit(1); // Should never get here!
}
// The parent
//
if (childPid < 0) { // did the fork fail??
error.setError(DTME_NoMemory); // yes: bail
return;
}
(void) SafeClose(inputPipe[pipeReader]); // input pipe reader n/a
// Sendmail files
//
headers.iterate(writeToFileDesc, &inputPipe[pipeWriter], 1, DTM_FALSE);
bodies.iterate(writeToFileDesc, &inputPipe[pipeWriter], 1, DTM_FALSE);
(void) SafeClose(inputPipe[pipeWriter]); // force EOF on mail age nt's input
// Now we wait on the condition variable until the child's
// process status is reported.
//
int status;
int ret_status;
int err_ret = SafeWaitpid(childPid, &status, 0);
if (err_ret < 0) {
// Somebody beat us to the status of the child.
// Just assume the best possible outcome.
//
error.clear();
}
// If the low byte of the status code returned from wait
// is 0, then the high byte is the status returned from
// the child. If the low byte is anything else, there
// was an error.
else if (lowByte(status) == 0)
{
ret_status = highByte(status);
switch (ret_status) {
case 67:
case 68:
error.setError(DTME_BadMailAddress);
break;
case 0:
error.clear();
break;
default:
error.setError(DTME_TransportFailed);
}
}
else
{
status = -1;
}
}
void
RFCTransport::sendmailReturnProc(void)
{
pipedata_t new_pd;
waitentry_t * ptr, ** prev;
int status;
DtMailEnv error;
// Now that the child process (sendmail) has finished, read
// its pid and status from the transfds pipe.
if(read(_transfds[0], &new_pd,
sizeof(new_pd))!=sizeof(new_pd)) {
// ERROR - can't read pipe so just return?
error.setError (DTME_NoMemory);
error.logError(DTM_TRUE,
"RFCTransport: sendmailReturnProc(): Failed to read pipe\n");
return;
}
ptr = _waitlist;
prev = &_waitlist;
// Loop through the waitlist which is a list of all the
// child processes (sendmail) that the parent exec'd. When
// the pid in the list matches the child process that has
// exited, we've found it.
while(ptr) {
if(ptr->pid == new_pd.pid)
break;
prev = &(ptr->next);
ptr = ptr->next;
}
// We couldn't match the pid so just do nothing.
if(!ptr) {
// ERROR just return
return;
}
*prev = ptr->next;
// If the low byte of the status code returned from wait
// is 0, then the high byte is the status returned from
// the child. If the low byte is anything else, there
// was an error.
if (lowByte(new_pd.status) == 0)
{
status = highByte(new_pd.status);
}
else
{
status = -1;
}
// Now that we've identified the child process, call the proc
// associated with it, pass the child's pid and status and any
// extra data.
ptr->proc(new_pd.pid, status, ptr->data);
// Cleanup
free((void*)ptr);
}
//
// When a child process finishes, child_handler writes its pid
// and status onto the transfds pipe. XtAppAddInput calls SendmailReturnProc
// to read from this pipe.
//
// The reason we don't just call SendmailReturnProc directly is twofold.
// The amount of processing should be kept to a minimum in a
// signal handler and we don't really know where X is in it's
// processing. It may not be appropriate to pop a dialog up.
void
RFCTransport::childHandler(void)
{
pipedata_t d;
// Now that the child is finished, its a zombie process
// until we do the wait. wait for the child and get its
// pid and return status. write these to the transfds pipe.
// Be sure to reap all processes so that none get lost.
while (d.pid = (int) waitpid ((pid_t) -1, &d.status, WNOHANG))
{
if (d.pid > 0)
SafeWrite(_transfds[1], &d, sizeof(d));
else
{
if (errno == ECHILD)
break;
}
}
}
//
// Listen for the child processes when they exit. On exit from a
// child process, call child_handler and have it write to
// XtAppAddInput which will call SendmailReturnProc.
void
RFCTransport::signalRegister(void)
{
static int initialized = 0;
struct sigaction act;
if (initialized) return;
initialized = 1;
act.sa_handler = (void (*)(int))&RFCTransport::childHandler;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask, SIGCHLD);
act.sa_flags = 0;
sigaction(SIGCHLD, &act, NULL);
return;
}
//
// fork/exec the child process and return the child process pid
//
int
RFCTransport::startSubprocess(DtMailEnv &error, char * cmd,
Buffer & headers, Buffer & bodies, char ** argv)
{
int child_pid;
int sendfds[2];
error.clear();
// Create a pipe to write any necessary information
// from the parent process to the child process.
if(pipe(sendfds) < 0)
{
error.setError (DTME_NoMemory);
return(-1);
}
// fork a new process
// Use vfork when available because the only purpose
// of the child is to do an exec
switch(child_pid = (int) DTMAIL_FORK()) {
case -1:
{
// if the fork fails, cleanup
SafeClose(sendfds[0]);
SafeClose(sendfds[1]);
error.setError (DTME_NoMemory);
break;
}
case 0: /* child */
{
// Need to clean up a bit before execing the child
// Close all non-essential open files, signals, etc.
// NOTE: probably reduce priv's to invoking user too???
long maxOpenFiles = sysconf(_SC_OPEN_MAX);
// less than 32 descriptors?
if (maxOpenFiles < 32)
{
// don't believe it--assume lots
maxOpenFiles = 1024;
}
// reset all signal handlers
for (int sig = 1; sig < NSIG; sig++)
{
(void) signal(sig, SIG_DFL);
}
// The child process (sendmail) needs to read info
// across the pipe from the parent process (dtmail).
// input pipe reader is stdin
// SafeDup2 will close stdin and dup sendfds[0] to stdin
// then close sendfds[0]
if (SafeDup2(sendfds[0], STDIN_FILENO) == -1)
{
// ERROR: exit with bad status
_exit (1);
}
// We need to close the write end of the pipe.
// NOTE: we leave standard output and standard error output
// open, input pipe writer n/a
(void) SafeClose(sendfds[1]);
// close all open file descriptors
for (int cfd = 3; cfd < maxOpenFiles; cfd++)
{
(void) SafeClose(cfd);
}
// exec sendmail
(void) SafeExecvp(cmd, (char *const *)argv);
// Should never get here!
_exit(1);
}
default: /* parent */
{
// Close the input pipe reader
(void) SafeClose(sendfds[0]);
// Write the mail message to the pipe. The child process
// (sendmail) will read from the pipe and send the message.
unsigned long iterateErrno;
iterateErrno = headers.iterate(writeToFileDesc, &sendfds[1],
1, DTM_FALSE);
if (iterateErrno == 0)
{
iterateErrno = bodies.iterate(writeToFileDesc, &sendfds[1],
1, DTM_FALSE);
}
if (iterateErrno != 0)
error.setError(DTME_ObjectCreationFailed);
// When we are done sending the message,
// force EOF on mail agent's input
(void) SafeClose(sendfds[1]);
// Don't wait for the child process (sendmail) to return.
// Instead, we've registered for notification (SIGCHLD)
// when the child exits and will invoke a handler to
// do the right thing.
return(child_pid);
}
}
return(-1);
}
char *
RFCTransport::concatValue(DtMailValueSeq & value)
{
// Count the string sizes.
//
int tot_size = 0;
int valueLength = value.length();
for (int n = 0; n < valueLength; n++) {
tot_size += strlen(*(value[n]));
tot_size += 5; // Fudge for null, commas, and space.
}
char * str = new char[tot_size];
*str = 0;
for (int cp = 0; cp < valueLength; cp++) {
strcat(str, *(value[cp]));
if (cp != (valueLength - 1)) {
strcat(str, ", ");
}
}
return(str);
}
void
RFCTransport::appendAddrs(DtMailAddressSeq & to, DtMailAddressSeq & from)
{
int fromLength = from.length();
for (int f = 0; f < fromLength; f++) {
to.append(new DtMailValueAddress(*from[f]));
}
}
void
RFCTransport::skinFiles(DtMailAddressSeq & addrs)
{
for (int a = 0; a < addrs.length(); a++) {
DtMailValueAddress * t_addr = addrs[a];
if (*t_addr->dtm_address == '+' ||
*t_addr->dtm_address == '/') {
int is_addr = 0;
for (const char * c = t_addr->dtm_address; *c; c++) {
if (*c == '@') {
is_addr = 1;
break;
}
}
if (!is_addr) {
addrs.remove(a);
a -= 1;
}
}
}
}
char *
RFCTransport::addrToString(DtMailAddressSeq & addrs)
{
// Compute worse case string size.
//
int len = 0;
int addrsLength = addrs.length();
for (int s = 0; s < addrsLength; s++) {
DtMailValueAddress * s_addr = addrs[s];
len += strlen(s_addr->dtm_address) + 5;
}
len += 20;
char * addr_str = new char[len];
*addr_str = 0;
for (int c = 0; c < addrsLength; c++) {
DtMailValueAddress * c_addr = addrs[c];
if (c) {
strcat(addr_str, ", ");
}
strcat(addr_str, c_addr->dtm_address);
}
return(addr_str);
}
int
RFCTransport::openLogFile(const char * path)
{
DtMailEnv error;
error.clear();
char * exp_path = _session->expandPath(error, path);
if (error.isSet())
return(-1);
int fd = SafeOpen(exp_path, O_RDWR | O_APPEND | O_CREAT, 0600);
free(exp_path);
if (fd < 0) {
return(fd);
}
// Generate the Unix From line...
//
passwd pw;
GetPasswordEntry(pw);
char *from_buf = new char[256];
char *time_buf = new char[256];
time_t now = time(NULL);
SafeCtime(&now, time_buf, sizeof(time_buf));
sprintf(from_buf, "From %s %s", pw.pw_name, time_buf);
SafeWrite(fd, from_buf, strlen(from_buf));
delete [] from_buf;
delete [] time_buf;
return(fd);
}
void
RFCTransport::closeLogFiles(int * files, int file_cnt)
{
for (int fd = 0; fd < file_cnt; fd++) {
SafeWrite(files[fd], "\n", 1);
SafeClose(files[fd]);
}
}
void
RFCWriteMessage(DtMailEnv & error,
DtMail::Session * session,
const char * path,
DtMail::Message * msg)
{
RFCFormat * fmt;
BufferMemory headers(1024);
BufferMemory bodies(8192);
fmt = new RFCMIME(session);
//call setIsWriteBcc so that the Bcc field will write to headers
// buffer (see aix defect 177089)
fmt->setIsWriteBcc();
fmt->msgToBuffer(error, *msg, DTM_TRUE, DTM_TRUE, DTM_FALSE,
headers, bodies);
if (error.isSet()) {
return;
}
int fd = SafeOpen(path, O_RDWR | O_CREAT | O_TRUNC, 0600);
if (fd < 0) {
error.setError(DTME_ObjectCreationFailed);
delete fmt;
return;
}
char *fsname=(char *)error.getClient();
int len=headers.getSize()+bodies.getSize();
/*
printf("\n message body len=%d",len);
*/
if( error.isSet() || !FileSystemSpace(path, len,&fsname) )
{
error.setError(DTME_OutOfSpace);
error.setClient((void *)fsname);
close(fd);
remove(path);
return;
}
unsigned long iterateErrno;
iterateErrno = headers.iterate(writeToFileDesc, &fd, 1, DTM_FALSE);
if (iterateErrno == 0)
iterateErrno = bodies.iterate(writeToFileDesc, &fd, 1, DTM_FALSE);
if (iterateErrno != 0)
error.setError(DTME_ObjectCreationFailed);
delete fmt;
close(fd);
}