d7d7ffd03c56969bbac74355746f8022ab405fbb
[oweals/cde.git] / cde / programs / dtmail / libDtMail / RFC / RFCTransport.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  *+SNOTICE
25  *
26  *
27  *      $TOG: RFCTransport.C /main/18 1998/07/24 16:09:46 mgreess $
28  *
29  *      RESTRICTED CONFIDENTIAL INFORMATION:
30  *      
31  *      The information in this document is subject to special
32  *      restrictions in a confidential disclosure agreement between
33  *      HP, IBM, Sun, USL, SCO and Univel.  Do not distribute this
34  *      document outside HP, IBM, Sun, USL, SCO, or Univel without
35  *      Sun's specific written approval.  This document and all copies
36  *      and derivative works thereof must be returned or destroyed at
37  *      Sun's request.
38  *
39  *      Copyright 1993, 1994, 1995 Sun Microsystems, Inc.  All rights reserved.
40  *
41  *+ENOTICE
42  */
43
44 #ifndef I_HAVE_NO_IDENT
45 #endif
46
47 #ifdef __ppc
48 #include <DtMail/Buffer.hh>
49 #endif
50
51 #include <stdio.h>
52 #include <errno.h>
53 #include <ctype.h>
54 #include <unistd.h>
55 #include <stdlib.h>
56 #include <pwd.h>
57 #include <time.h>
58 #include <signal.h>
59 #include <assert.h>
60
61 #include <DtMail/DtMail.hh>
62 #include <DtMail/DtMailP.hh>
63 #include "RFCImpl.hh"
64 #include "SigChldImpl.hh"
65 #include "RFCMIME.hh"
66 #include "SunV3.hh"
67 #include "AliasExpand.hh"
68 #include <DtMail/Threads.hh>
69 #include <DtMail/IO.hh>
70
71 #ifdef HAS_VFORK
72   #define DTMAIL_FORK   vfork
73 #else
74   #define DTMAIL_FORK   fork
75 #endif
76
77 // pipe between childHandler and XtAppAddInput
78 static int _transfds[2];
79 // list of children that have been forked
80 static waitentry_t *_waitlist;
81
82 // Note on error handling in wroteToFileDesc: this is used
83 // to write e-mail to all open *files* (e.g. recording to
84 // file, mail to file), so we attempt to write to all file
85 //
86 static unsigned long
87 writeToFileDesc(const char * buf, int len, va_list args)
88 {
89     int * fds = va_arg(args, int *);
90     int cnt = va_arg(args, int);
91     DtMailBoolean strip = (DtMailBoolean)va_arg(args, int);
92
93     unsigned long saveErrno = 0;        // Initially no error recorded
94
95     for (int fd = 0; fd < cnt; fd++) {
96         int status = 0;
97         do {
98             if (strip)
99                 status = SafeWriteStrip(fds[fd], buf, len);
100             else
101                 status = SafeWrite(fds[fd], buf, len);
102         } while (status < 0 && errno == EAGAIN);
103
104         if (status < 0 && errno != 0)           // Did an error occur??
105           saveErrno = (unsigned long)errno;     // Yes: remember "last" errno 
106
107 #ifdef DEAD_WOOD
108         DtMailProcessClientEvents();
109 #endif /* DEAD_WOOD */
110     }
111
112     return(saveErrno);          // return last error recorded
113 }
114
115 RFCTransport::RFCTransport(DtMailEnv & error,
116                            DtMail::Session * session,
117                            DtMailStatusCallback cb,
118                            void * cb_data,
119                            const char * impl)
120 : DtMail::Transport(error, session, cb, cb_data)
121 {
122     error.clear();
123
124     _object_valid = new Condition;
125     _object_valid->setTrue();
126
127     _impl = strdup(impl);
128
129     // Set up the handlers so that we are notified when a child
130     // process exits and do the right thing.
131     signalRegister();
132     
133     _error_proc = NULL;
134     _smd = NULL;
135 }
136
137 RFCTransport::~RFCTransport(void)
138 {
139     _object_valid->setFalse();
140     free(_impl);
141 }
142
143 DtMailOperationId
144 RFCTransport::submit(DtMailEnv & error, DtMail::Message * msg, DtMailBoolean log_msg)
145 {
146     waitentry_t * new_we;
147     int child_pid;
148
149     // Create a list of information about all child processes.
150     if ((new_we = (waitentry_t *) malloc(sizeof(waitentry_t))) == NULL)
151     {
152         error.setError (DTME_NoMemory);
153         return(NULL);
154     }
155
156     // fork a new process
157     switch(child_pid = (int) fork()) {
158         case -1:
159         {
160             // if the fork fails, cleanup
161             free (new_we);
162             error.setError (DTME_NoMemory);
163             break;
164         }
165         case 0:         /* child */
166         {
167             // reset all signal handlers
168             for (int sig = 1; sig < NSIG; sig++)
169             {
170                 (void) signal(sig, SIG_DFL);            
171             }
172
173             DtMailEnv tmp_error;
174             tmp_error.clear();
175
176             format(tmp_error, msg, log_msg);
177             _exit((int)((DTMailError_t)tmp_error)); 
178         }
179         default:                /* parent */
180         {
181             // Save information about each child process (sendmail)
182             // that we fork/exec.
183             new_we->pid = child_pid;
184             new_we->proc = this->_error_proc;
185             new_we->data = this->_smd;
186             new_we->next = _waitlist;
187             _waitlist = new_we;
188             return(NULL);
189         }
190     }
191
192     return(NULL);
193 }
194
195 void
196 RFCTransport::format(DtMailEnv & error, DtMail::Message * dtmsg, DtMailBoolean log_msg)
197 {
198     // Clean up the message addressing for delivery.
199     //
200 //    DtMailEnv * thr_error = new DtMailEnv();
201 //    thr_error->clear();
202
203     DtMailValueSeq value;
204     char * to = NULL;
205     char * cc = NULL;
206     char * bcc = NULL;
207
208     DtMail::Envelope * env = dtmsg->getEnvelope(error);
209
210     DtMailAddressSeq addr_tokens(32);
211
212     DtMailEnv tmp_error;
213     tmp_error.clear();
214     
215     env->getHeader(tmp_error, DtMailMessageTo, DTM_TRUE, value);
216     if (tmp_error.isNotSet()) {
217         to = concatValue(value);
218         DtMailAddressSeq to_tokens(16);
219         arpaPhrase(to, to_tokens);
220         rfcAliasExpand(error, *_session->mailRc(error), to_tokens);
221         appendAddrs(addr_tokens, to_tokens);
222         skinFiles(to_tokens);
223         char * new_to = addrToString(to_tokens);
224         env->setHeader(tmp_error, "To", DTM_TRUE, new_to);
225         delete [] new_to;
226         free(to);
227     }
228     // Clear the error structure before passing it to a function.
229     tmp_error.clear();
230     value.clear();
231
232     env->getHeader(tmp_error, DtMailMessageCc, DTM_TRUE, value);
233     if (tmp_error.isNotSet()) {
234         cc = concatValue(value);
235         DtMailAddressSeq cc_tokens(16);
236         arpaPhrase(cc, cc_tokens);
237         rfcAliasExpand(error, *_session->mailRc(error), cc_tokens);
238         appendAddrs(addr_tokens, cc_tokens);
239         skinFiles(cc_tokens);
240         char * new_cc = addrToString(cc_tokens);
241         env->setHeader(tmp_error, "Cc", DTM_TRUE, new_cc);
242         delete [] new_cc;
243         free(cc);
244     }
245     // Clear the error structure before passing it to a function.
246     tmp_error.clear();
247     value.clear();
248
249     env->getHeader(tmp_error, DtMailMessageBcc, DTM_TRUE, value);
250     if (tmp_error.isNotSet()) {
251         bcc = concatValue(value);
252         DtMailAddressSeq bcc_tokens(16);
253         arpaPhrase(bcc, bcc_tokens);
254         rfcAliasExpand(error, *_session->mailRc(error), bcc_tokens);
255         appendAddrs(addr_tokens, bcc_tokens);
256         free(bcc);
257     }
258     value.clear();
259
260     // Note for now we ignore all errors from "rfcAliasExpand"
261     //
262     error.clear();
263
264     // Now we format the message.
265     // These messages do NOT include content-length as they are intended
266     // only for the delivery agent. If we are writing to local files,
267     // after remote delivery the local delivery will generate its own
268     // messages that have content-length included
269     //
270     RFCFormat * fmt;
271     RFCFormat * logFmt;
272
273     if (strcmp(_impl, "Internet MIME") == 0) {
274         fmt = new RFCMIME(_session);
275         logFmt = new RFCMIME(_session);
276       }
277     else {
278         fmt = new SunV3(_session);
279         logFmt = new SunV3(_session);
280       }
281
282     // Prepare to fire up the delivery agent
283     // The delivery agent preprocessor examines each addressee and if it is
284     // really a local file (eg folders) the local file is opened and its file
285     // descriptor is appended to the "log_files" array, which is allocated
286     // by the delivery agent preprocessor via 'new'. Upon return to us, if
287     // the log_count is > 0 we must format the message to be stored in a
288     // local file (include content length) and cause the message to be
289     // written to each open file
290     //
291     int *log_files = 0;
292     int log_count = 0;
293
294     { // Create local scope to contain headers/bodies so that they go away
295       // as soon as they are no longer needed, before we potentially
296       // create a duplicate set for writing to local files
297       //
298       BufferMemory headers(1024);
299       BufferMemory bodies(8192);
300
301       error.clear();
302       //call unsetIsWriteBcc so that the Bcc field will not write to headers
303       // buffer (see aix defect 177089
304       fmt->unsetIsWriteBcc(); 
305       fmt->msgToBuffer(error, *dtmsg, DTM_FALSE, DTM_FALSE, DTM_FALSE,
306         headers, bodies);
307
308       if (!error.isSet())
309         deliver(error, addr_tokens, headers, bodies, log_msg, &log_files, log_count);
310     }
311
312     delete fmt;
313
314     // If any log files have been opened, format this message for local
315     // storage and cause the message to be written to all open files
316     //
317     tmp_error.clear();          // Used to cache error from logging to files
318     
319     if (log_count) {
320       BufferMemory logHeaders(1024);
321       BufferMemory logBodies(8192);
322
323       assert(log_files != NULL);        // if log files open, must have array
324
325       //
326       // Mark the message as having been read.
327       //
328       env->setHeader(error, "Status", DTM_TRUE, "RO");
329
330       //call setIsWriteBcc so that the Bcc field will write to logHeaders
331       // buffer (see aix defect 177089)
332       logFmt->setIsWriteBcc(); 
333       logFmt->msgToBuffer(tmp_error, *dtmsg, DTM_TRUE, DTM_FALSE, DTM_FALSE,
334                             logHeaders, logBodies);
335
336       if (!tmp_error.isSet()) {
337         unsigned long iterateErrno, iterateErrno1;
338         
339         iterateErrno = logHeaders.iterate(writeToFileDesc, log_files, log_count, DTM_TRUE);
340         iterateErrno1 = logBodies.iterate(writeToFileDesc, log_files, log_count, DTM_TRUE);
341         if ( (iterateErrno != 0) || (iterateErrno1 != 0) )
342           tmp_error.setError(DTME_ObjectCreationFailed);
343
344       }
345
346       closeLogFiles(log_files, log_count);
347       delete log_files;
348     }
349
350     delete logFmt;
351
352 /* Comment out this code because it's part of old thread code.  Why
353    execute code that does nothing if we don't have to?
354     // Tell the requestor we're all done now.
355     //
356     DtMailEventPacket packet;
357     packet.key = _key;
358     packet.target = DTM_TARGET_TRANSPORT;
359     packet.target_object = this;
360     packet.operation = (void *)ThreadSelf();
361     packet.argument = thr_error;
362     packet.event_time = time(NULL);
363
364     if (_object_valid->state()) {
365         _session->writeEventData(error, &packet, sizeof(DtMailEventPacket));
366     }
367 */
368
369     // If no error occurred during the deliver invocation, but
370     // an error occurred during local file delivery, propagate
371     // that error into the error returned to the caller
372     //
373     if (!error.isSet() && tmp_error.isSet())
374       error.setError((DTMailError_t)tmp_error);
375     
376     return;
377 }
378
379 void
380 RFCTransport::deliver(DtMailEnv & error,
381                       DtMailAddressSeq & addr_tokens,
382                       Buffer & headers,
383                       Buffer & bodies,
384                       DtMailBoolean log_msg,
385                       int **log_files,
386                       int & log_count)
387 {
388     DtMailEnv merror;
389     merror.clear();
390
391     const char * value;
392
393     assert(log_files != NULL);          // must provide -> log_files ->
394     
395     // We want to make an argv list that is big enough to hold all
396     // of the addresses. Of course, this may need to be expanded
397     // because of local aliases, but we'll work on that.
398     //
399     char ** argv = (char **)malloc(sizeof(char *) * (addr_tokens.length() + 5));
400     int argc = 1;
401
402     *log_files = 0;
403     log_count = 0;
404
405     if (log_msg == DTM_TRUE) {
406         const char * log;
407         char *buf = NULL;
408
409         _session->mailRc(merror)->getValue(merror, "record", &log);
410         if (merror.isSet()) {
411             log = "~/sent.mail";
412         }
413
414         if (*log != '/' && *log != '~' && *log != '+' && *log != '$') {
415
416             _session->mailRc(merror)->getValue(merror, "outfolder", &value);
417             buf = (char *)malloc(strlen(log) + 3);
418             if (buf != NULL) {
419                 if (merror.isSet()) {
420                     // "outfolder" is not set. Relative to home directory
421                     strcpy(buf, "~/");
422                     merror.clear();
423                 } else {
424                     // "outfolder" is set. Relative to folder directory
425                     strcpy(buf, "+");
426                 }
427                 strcat(buf, log);
428                 log = buf;
429             }
430         }
431         int fd = openLogFile(log);
432         if (fd >= 0) {
433             if (!*log_files)
434               * log_files = new int[addr_tokens.length() + 4];
435             (*log_files)[log_count++] = fd;
436         }
437         
438         if (buf != NULL) {
439                 free(buf);
440         }
441     }
442
443     // We add the correct parameter to sendmail so that it will ignore
444     // lines with a single dot(.) in them.  We also check to see
445     // if we should add a couple of optional parameters: metoo and
446     // verbose.
447     argv[argc++] = "-oi";
448     _session->mailRc(merror)->getValue(merror, "metoo", &value);
449     if (merror.isNotSet()) {
450         // "metoo" is set.
451         argv[argc++] = "-m";
452     } else {
453         merror.clear();
454     }
455
456     _session->mailRc(merror)->getValue(merror, "verbose", &value);
457     if (merror.isNotSet()) {
458         // verbose is set.
459         argv[argc++] = "-v";
460     } else {
461         merror.clear();
462     }
463
464     // We now look at each address. If the name starts with a + or
465     // "/" then look further. If the name contains an "@" we assume
466     // it is probably an email address. Otherwise we assume it is
467     // a file and copy it to the file system.
468     //
469     int is_addr;
470     const char * at;
471     for (int addr = 0; addr < addr_tokens.length(); addr++) {
472         switch (*(addr_tokens[addr]->dtm_address)) {
473           case '+':
474           case '/':
475             is_addr = 0;
476             for (at = addr_tokens[addr]->dtm_address; *at; at++) {
477                 if (*at == '@') {
478                     is_addr = 1;
479                     break;
480                 }
481             }
482             if (is_addr) {
483                 argv[argc++] = addr_tokens[addr]->dtm_address;
484             }
485             else {
486                 int fd = openLogFile(addr_tokens[addr]->dtm_address);
487                 if (fd >= 0) {
488                     if (!*log_files)
489                       * log_files = new int[addr_tokens.length() + 4];
490                     (*log_files)[log_count++] = fd;
491                 }
492             }
493             break;
494             
495           default:
496             argv[argc++] = addr_tokens[addr]->dtm_address;
497         }
498     }
499     
500     argv[argc] = NULL;
501
502     launchSendmail(error, headers, bodies, argv);
503     
504     assert((!log_count && ! *log_files) || (log_count && *log_files));
505     
506     free(argv);
507 }
508
509 static void
510 skin_comma(char * buf)
511 {
512     char * last_c = &buf[strlen(buf) - 1];
513
514     while(last_c > buf && isspace((unsigned char)*last_c)) {
515         *last_c = 0;
516         last_c -= 1;
517     }
518
519     if (last_c > buf && *last_c == ',') {
520         *last_c = 0;
521     }
522 }
523
524 void
525 RFCTransport::arpaPhrase(const char * name, DtMailAddressSeq & tokens)
526 {
527     register char c;
528     register const char *cp;
529     char *cp2;
530     char *nbufp;
531     char *bufend;
532     int gotlt, lastsp, didq, rem;
533     int nesting, extra;
534     int token = 1;
535     int comma = 0;
536     DtMailValueAddress * addr;
537     
538     if (name == (char *) 0) {
539         return;
540     }
541     /* count comma's; we may need up to one extra space per comma... */
542     extra = 1;
543     cp = name;
544     for (;;) {
545         cp = strchr(cp, ',');
546         
547         if (!cp) break;
548         
549         for(cp += 1; *cp && isspace((unsigned char)*cp); cp++) {
550             extra++;
551         }
552     }
553     
554     nbufp = (char *)malloc(strlen(name) + extra);
555
556     char * tok_start = nbufp;
557     int tok_len;
558     int count_at_comma = 0;
559
560     gotlt = 0;
561     lastsp = 0;
562     bufend = nbufp;
563     cp = name;
564     for (cp = name, cp2 = bufend; (c = *cp++) != 0;) {
565         switch (c) {
566           case '(':
567             /*
568               Start of a comment, ignore it.
569               */
570             nesting = 1;
571             while ((c = *cp) != 0) {
572                 cp++;
573                 switch(c) {
574                   case '\\':
575                     if (*cp == 0) goto outcm;
576                     cp++;
577                     break;
578                   case '(':
579                     nesting++;
580                     break;
581                   case ')':
582                     --nesting;
583                     break;
584                 }
585                 if (nesting <= 0) break;
586             }
587           outcm:
588             lastsp = 0;
589             break;
590             
591           case '"':
592             /*
593               Start a quoted string.
594               Copy it in its entirety.
595               */
596             didq = 0;
597             while ((c = *cp) != 0) {
598                 cp++;
599                 switch (c) {
600                   case '\\':
601                     if ((c = *cp) == 0) goto outqs;
602                     cp++;
603                     break;
604                   case '"':
605                     goto outqs;
606                 }
607                 if (gotlt == 0 || gotlt == '<') {
608                     if (lastsp) {
609                         lastsp = 0;
610                         *cp2++ = ' ';
611                     }
612                     if (!didq) {
613                         *cp2++ = '"';
614                         didq++;
615                     }
616                     *cp2++ = c;
617                 }
618             }
619           outqs:
620             if (didq)
621                 *cp2++ = '"';
622             lastsp = 0;
623             break;
624             
625           case ' ':
626           case '\t':
627           case '\n':
628             if (token && (!comma || c == '\n')) {
629               done:
630                 addr = new DtMailValueAddress;
631                 tok_len = cp2 - tok_start;
632                 addr->dtm_address = (char *)malloc(tok_len + 1);
633                 memcpy(addr->dtm_address, tok_start, tok_len);
634                 addr->dtm_address[tok_len] = 0;
635                 addr->dtm_person = NULL;
636                 addr->dtm_namespace = strdup(DtMailAddressDefault);
637                 skin_comma(addr->dtm_address);
638                 tokens.append(addr);
639
640                 while(*cp && isspace((unsigned char)*cp)) {
641                     cp++;
642                 }
643
644                 tok_start = cp2;
645             }
646             break;
647             
648           case ',':
649             *cp2++ = c;
650             if (gotlt != '<') {
651                 bufend = cp2 + 1;
652                 count_at_comma = tokens.length();
653                 gotlt = 0;
654                 if (token) {
655                     count_at_comma++;
656                     goto done;
657                 }
658             }
659             break;
660             
661           case '<':
662             cp2 = bufend;
663             for (rem = (tokens.length() - 1); tokens.length() > count_at_comma;
664                  rem = (tokens.length() - 1)) {
665                 DtMailValueAddress * bad_addr = tokens[rem];
666                 delete bad_addr;
667                 tokens.remove(rem);
668             }
669             tok_start = cp2;
670             gotlt = c;
671             lastsp = 0;
672             break;
673             
674           case '>':
675             if (gotlt == '<') {
676                 gotlt = c;
677                 break;
678             }
679             
680             /* FALLTHROUGH . . . */
681             
682           default:
683             if (gotlt == 0 || gotlt == '<') {
684                 if (lastsp) {
685                     lastsp = 0;
686                     *cp2++ = ' ';
687                 }
688                 *cp2++ = c;
689             }
690             break;
691         }
692     }
693     *cp2 = 0;
694
695     if (cp2 > tok_start) {
696         addr = new DtMailValueAddress;
697         addr->dtm_address = strdup(tok_start);
698         addr->dtm_person = NULL;
699         addr->dtm_namespace = strdup(DtMailAddressDefault);
700         skin_comma(addr->dtm_address);
701         tokens.append(addr);
702     }
703
704     free(nbufp);
705 }
706
707
708 //
709 // SendMsgDialog has some info that is needed here.
710 //
711 void
712 RFCTransport::initTransportData(int fds[2], SubProcessFinishedProc proc,
713         void *ptr)
714 {
715         _transfds[0] = fds[0];
716         _transfds[1] = fds[1];
717         _smd = ptr;
718         _error_proc = proc;
719 }
720
721 //
722 // Pass a ptr to sendmailReturnProc to SendMsgDialog so that it can
723 // call it in XtAppAddInput
724 //
725 void *
726 RFCTransport::getSendmailReturnProc(void)
727 {
728         return ((void *)(&RFCTransport::sendmailReturnProc));
729 }
730
731
732 void
733 RFCTransport::launchSendmail(DtMailEnv & error,
734                              Buffer & headers,
735                              Buffer & bodies,
736                              char ** argv)
737 {
738     // We need to retrieve the name of the sendmail program.
739     // if none is set then we use the default mailer.
740     //
741     const char * mailer;
742     _session->mailRc(error)->getValue(error, "sendmail", &mailer);
743     if (error.isSet()) {
744 #if defined(USL) || defined(__uxp__)
745         mailer = "/usr/ucblib/sendmail";
746 #elif defined(__OpenBSD__)
747         mailer = "/usr/sbin/sendmail";
748 #else
749         mailer = "/usr/lib/sendmail";
750 #endif
751     }
752  
753     error.clear();
754  
755     argv[0] = (char *)mailer;
756  
757     // If we have only one arg, then everything goes to the log files.
758     // Don't do the fork and exec.
759     //
760     if (argv[1] == NULL)
761       return;
762  
763     // We need a pipe to write the message to sendmail.
764     //
765     int inputPipe[2];
766     const int pipeReader = 0;           // pipe[0] is read side of pipe
767     const int pipeWriter = 1;           // pipe[1] is write side of pipe
768  
769     if (pipe(inputPipe)==-1) {          // Attempt to get a pipe
770       error.setError(DTME_NoMemory);    // this must be really bad...
771       return;
772     }
773  
774     // We need to fork and send the data to sendmail.
775     // Use vfork when available because the only purpose
776     // of the child is to do an exec
777     //
778     pid_t childPid = DTMAIL_FORK();
779     if (childPid == 0) {                // The child **********
780
781
782         // Need to clean up a bit before exec()ing the child
783         // Close all non-essential open files, signals, etc.
784         // NOTE: probably reduce priv's to invoking user too???
785         //
786         long maxOpenFiles = sysconf(_SC_OPEN_MAX);
787  
788         if (maxOpenFiles < 32)          // less than 32 descriptors?
789           maxOpenFiles = 1024;          // dont believe it--assume lots
790  
791         for (int sig = 1; sig < NSIG; sig++)
792           (void) signal(sig, SIG_DFL);  // REset all signal handlers
793
794         // input pipe reader is stdin
795         if (SafeDup2(inputPipe[pipeReader], STDIN_FILENO) == -1)
796           _exit (1);                    // ERROR: exit with bad status
797
798         // NOTE: we leave standard output and standard error output open
799         (void) SafeClose(inputPipe[pipeWriter]); // input pipe writer n/a
800         for (int cfd = 3; cfd < maxOpenFiles; cfd++)
801           (void) SafeClose(cfd); // close all open file descriptors
802         
803 #if 0
804         printf("Command:  %s\n",mailer);
805         int k=0;
806         while (NULL != argv[k])
807         {
808           printf("Command line %d:  %s\n", k, argv[k]);
809           k++;
810         }
811 #endif
812         (void) execvp(mailer, (char * const *)argv);
813
814         _exit(1); // Should never get here!
815     }
816
817
818     // The parent
819     //
820     if (childPid < 0) {                                 // did the fork fail??
821       error.setError(DTME_NoMemory);                    // yes: bail
822       return;
823     }
824  
825     (void) SafeClose(inputPipe[pipeReader]); // input pipe reader n/a
826  
827     // Sendmail files
828     //
829     headers.iterate(writeToFileDesc, &inputPipe[pipeWriter], 1, DTM_FALSE);
830     bodies.iterate(writeToFileDesc, &inputPipe[pipeWriter], 1, DTM_FALSE);
831     (void) SafeClose(inputPipe[pipeWriter]); // force EOF on mail age nt's input
832  
833     // Now we wait on the condition variable until the child's
834     // process status is reported.
835     //
836     int status;
837     int ret_status;
838     int err_ret = SafeWaitpid(childPid, &status, 0);
839  
840     if (err_ret < 0) {
841         // Somebody beat us to the status of the child.
842         // Just assume the best possible outcome.
843         //
844         error.clear();
845     }
846     // If the low byte of the status code returned from wait
847     // is 0, then the high byte is the status returned from
848     // the child.  If the low byte is anything else, there
849     // was an error.
850     else if (lowByte(status) == 0)
851     {
852         ret_status = highByte(status);
853         switch (ret_status) {
854           case 67:
855           case 68:
856             error.setError(DTME_BadMailAddress);
857             break;
858  
859           case 0:
860             error.clear();
861             break;
862  
863           default:
864             error.setError(DTME_TransportFailed);
865         }
866     }
867     else
868     {
869         status = -1;
870     }
871 }
872
873
874 void
875 RFCTransport::sendmailReturnProc(void)
876 {
877         pipedata_t      new_pd;
878         waitentry_t     * ptr, ** prev;
879         int             status;
880         DtMailEnv       error;
881
882         // Now that the child process (sendmail) has finished, read
883         // its pid and status from the transfds pipe.
884         if(read(_transfds[0], &new_pd,
885                 sizeof(new_pd))!=sizeof(new_pd)) {
886                 // ERROR - can't read pipe so just return?
887                 error.setError (DTME_NoMemory);
888                 error.logError(DTM_TRUE,
889                   "RFCTransport: sendmailReturnProc(): Failed to read pipe\n");
890                 return; 
891         }
892                 
893         ptr =  _waitlist;
894         prev = &_waitlist;
895
896         // Loop through the waitlist which is a list of all the 
897         // child processes (sendmail) that the parent exec'd.  When
898         // the pid in the list matches the child process that has
899         // exited, we've found it.
900         while(ptr) {
901                 if(ptr->pid == new_pd.pid) 
902                     break;
903                 prev = &(ptr->next);
904                 ptr = ptr->next;
905         }
906
907         // We couldn't match the pid so just do nothing.
908         if(!ptr) {
909                 // ERROR just return
910                 return;
911         }
912         
913         *prev = ptr->next;
914
915         // If the low byte of the status code returned from wait
916         // is 0, then the high byte is the status returned from
917         // the child.  If the low byte is anything else, there
918         // was an error.
919         if (lowByte(new_pd.status) == 0)
920         {
921             status = highByte(new_pd.status);
922         }
923         else
924         {
925             status = -1;
926         }
927
928         // Now that we've identified the child process, call the proc
929         // associated with it, pass the child's pid and status and any
930         // extra data.
931         ptr->proc(new_pd.pid, status, ptr->data);
932
933         // Cleanup
934         free((void*)ptr);
935 }
936
937 //
938 // When a child process finishes, child_handler writes its pid
939 // and status onto the transfds pipe.  XtAppAddInput calls SendmailReturnProc
940 // to read from this pipe.
941 //
942 // The reason we don't just call SendmailReturnProc directly is twofold.
943 // The amount of processing should be kept to a minimum in a
944 // signal handler and we don't really know where X is in it's
945 // processing.  It may not be appropriate to pop a dialog up.
946 void 
947 RFCTransport::childHandler(void)
948 {
949         pipedata_t d;
950
951         // Now that the child is finished, its a zombie process
952         // until we do the wait.  wait for the child and get its
953         // pid and return status.  write these to the transfds pipe.
954         // Be sure to reap all processes so that none get lost.
955         while (d.pid = (int) waitpid ((pid_t) -1, &d.status, WNOHANG))
956         {
957             if (d.pid > 0)
958                 SafeWrite(_transfds[1], &d, sizeof(d));
959             else
960             {
961                 if (errno == ECHILD)
962                     break;
963             }
964         }
965 }
966
967 //
968 // Listen for the child processes when they exit.  On exit from a
969 // child process, call child_handler and have it write to 
970 // XtAppAddInput which will call SendmailReturnProc.
971 void
972 RFCTransport::signalRegister(void)
973 {
974     static int          initialized = 0;
975     struct sigaction    act;
976
977     if (initialized) return;
978     initialized = 1;
979
980 #if defined(hpux) || defined(_aix) || defined(__osf__) || defined(linux) || \
981     (defined(sun) && OSMAJORVERSION>=5 && OSMINORVERSION>4) || defined(CSRG_BASED)
982     // SunOS 5.5 and above defined prototype for signal handler
983     act.sa_handler = (void (*)(int))&RFCTransport::childHandler;
984 #else
985     // SunOS 5.4 and before defined prototype signal handler
986     act.sa_handler = (void (*)())&RFCTransport::childHandler;
987 #endif
988     sigemptyset(&act.sa_mask);
989     sigaddset(&act.sa_mask, SIGCHLD);
990     act.sa_flags = 0;
991    
992     sigaction(SIGCHLD, &act, NULL);
993
994     return;
995 }
996
997 //
998 // fork/exec the child process and return the child process pid
999 //
1000 int 
1001 RFCTransport::startSubprocess(DtMailEnv &error, char * cmd,
1002         Buffer & headers, Buffer & bodies, char ** argv)
1003 {
1004         int child_pid;
1005         int sendfds[2];
1006
1007         error.clear();
1008
1009         // Create a pipe to write any necessary information
1010         // from the parent process to the child process.
1011         if(pipe(sendfds) < 0) 
1012         {
1013             error.setError (DTME_NoMemory);
1014             return(-1);
1015         }
1016
1017         // fork a new process
1018         // Use vfork when available because the only purpose
1019         // of the child is to do an exec
1020         switch(child_pid = (int) DTMAIL_FORK()) {
1021
1022             case -1:
1023             {
1024                 // if the fork fails, cleanup
1025                 SafeClose(sendfds[0]);
1026                 SafeClose(sendfds[1]);
1027                 error.setError (DTME_NoMemory);
1028                 break;
1029             }
1030             case 0:             /* child */
1031             {
1032                 // Need to clean up a bit before execing the child
1033                 // Close all non-essential open files, signals, etc.
1034                 // NOTE: probably reduce priv's to invoking user too???
1035                 long maxOpenFiles = sysconf(_SC_OPEN_MAX);
1036
1037                 // less than 32 descriptors?
1038                 if (maxOpenFiles < 32)          
1039                 {
1040                     // dont believe it--assume lots
1041                     maxOpenFiles = 1024;                
1042                 }
1043
1044                 // reset all signal handlers
1045                 for (int sig = 1; sig < NSIG; sig++)
1046                 {
1047                     (void) signal(sig, SIG_DFL);                
1048                 }
1049
1050                 // The child process (sendmail) needs to read info
1051                 // across the pipe from the parent process (dtmail).
1052                 // input pipe reader is stdin
1053                 // SafeDup2 will close stdin and dup sendfds[0] to stdin
1054                 // then close sendfds[0]
1055                 if (SafeDup2(sendfds[0], STDIN_FILENO) == -1)
1056                 {
1057                     // ERROR: exit with bad status
1058                         _exit (1);
1059                 }
1060         
1061                 // We need to close the write end of the pipe.
1062                 // NOTE: we leave standard output and standard error output 
1063                 // open, input pipe writer n/a
1064                 (void) SafeClose(sendfds[1]);
1065         
1066                 // close all open file descriptors
1067                 for (int cfd = 3; cfd < maxOpenFiles; cfd++)
1068                 {
1069                     (void) SafeClose(cfd);                      
1070                 }
1071
1072                 // exec sendmail
1073                 (void) SafeExecvp(cmd, (char *const *)argv);
1074
1075                 // Should never get here!
1076                 _exit(1);                                       
1077
1078             }
1079             default:            /* parent */
1080             {
1081                 // Close the input pipe reader
1082                 (void) SafeClose(sendfds[0]);
1083
1084                 // Write the mail message to the pipe.  The child process
1085                 // (sendmail) will read from the pipe and send the message.
1086                 unsigned long iterateErrno;
1087     
1088                 iterateErrno = headers.iterate(writeToFileDesc, &sendfds[1],
1089                         1, DTM_FALSE);
1090                 if (iterateErrno == 0)
1091                 {
1092                     iterateErrno = bodies.iterate(writeToFileDesc, &sendfds[1],
1093                         1, DTM_FALSE);
1094                 }
1095
1096                 if (iterateErrno != 0)
1097                     error.setError(DTME_ObjectCreationFailed);
1098
1099                 // When we are done sending the message,
1100                 // force EOF on mail agent's input
1101                 (void) SafeClose(sendfds[1]);
1102
1103                 // Don't wait for the child process (sendmail) to return.
1104                 // Instead, we've registered for notification (SIGCHLD)
1105                 // when the child exits and will invoke a handler to
1106                 // do the right thing.
1107
1108                 return(child_pid);
1109             }
1110         }
1111
1112         return(-1);
1113 }
1114
1115 char *
1116 RFCTransport::concatValue(DtMailValueSeq & value)
1117 {
1118     // Count the string sizes.
1119     //
1120     int tot_size = 0;
1121     int valueLength = value.length();
1122     for (int n = 0; n < valueLength; n++) {
1123         tot_size += strlen(*(value[n]));
1124         tot_size += 5; // Fudge for null, commas, and space.
1125     }
1126
1127     char * str = new char[tot_size];
1128
1129     *str = 0;
1130     for (int cp = 0; cp < valueLength; cp++) {
1131         strcat(str, *(value[cp]));
1132         if (cp != (valueLength - 1)) {
1133             strcat(str, ", ");
1134         }
1135     }
1136
1137     return(str);
1138 }
1139
1140 void
1141 RFCTransport::appendAddrs(DtMailAddressSeq & to, DtMailAddressSeq & from)
1142 {
1143   int fromLength = from.length();
1144   
1145     for (int f = 0; f < fromLength; f++) {
1146         to.append(new DtMailValueAddress(*from[f]));
1147     }
1148 }
1149
1150 void
1151 RFCTransport::skinFiles(DtMailAddressSeq & addrs)
1152 {
1153     for (int a = 0; a < addrs.length(); a++) {
1154         DtMailValueAddress * t_addr = addrs[a];
1155         if (*t_addr->dtm_address == '+' ||
1156             *t_addr->dtm_address == '/') {
1157             int is_addr = 0;
1158             for (const char * c = t_addr->dtm_address; *c; c++) {
1159                 if (*c == '@') {
1160                     is_addr = 1;
1161                     break;
1162                 }
1163             }
1164
1165             if (!is_addr) {
1166                 addrs.remove(a);
1167                 a -= 1;
1168             }
1169         }
1170     }
1171 }
1172
1173 char *
1174 RFCTransport::addrToString(DtMailAddressSeq & addrs)
1175 {
1176     // Compute worse case string size.
1177     //
1178     int len = 0;
1179     int addrsLength = addrs.length();
1180     for (int s = 0; s < addrsLength; s++) {
1181         DtMailValueAddress * s_addr = addrs[s];
1182         len += strlen(s_addr->dtm_address) + 5;
1183     }
1184     len += 20;
1185
1186     char * addr_str = new char[len];
1187     *addr_str = 0;
1188
1189     for (int c = 0; c < addrsLength; c++) {
1190         DtMailValueAddress * c_addr = addrs[c];
1191         if (c) {
1192             strcat(addr_str, ", ");
1193         }
1194         strcat(addr_str, c_addr->dtm_address);
1195     }
1196
1197     return(addr_str);
1198 }
1199
1200 int
1201 RFCTransport::openLogFile(const char * path)
1202 {
1203     DtMailEnv error;
1204     error.clear();
1205     
1206     char * exp_path = _session->expandPath(error, path);
1207     if (error.isSet())
1208       return(-1);
1209
1210     int fd = SafeOpen(exp_path, O_RDWR | O_APPEND | O_CREAT, 0600);
1211     free(exp_path);
1212     if (fd < 0) {
1213         return(fd);
1214     }
1215
1216     // Generate the Unix From line...
1217     //
1218     passwd pw;
1219     GetPasswordEntry(pw);
1220
1221     char *from_buf = new char[256];
1222     char *time_buf = new char[256];
1223     time_t now = time(NULL);
1224     SafeCtime(&now, time_buf, sizeof(time_buf));
1225
1226     sprintf(from_buf, "From %s %s", pw.pw_name, time_buf);
1227     SafeWrite(fd, from_buf, strlen(from_buf));
1228
1229     delete [] from_buf;
1230     delete [] time_buf;
1231
1232     return(fd);
1233 }
1234
1235 void
1236 RFCTransport::closeLogFiles(int * files, int file_cnt)
1237 {
1238     for (int fd = 0; fd < file_cnt; fd++) {
1239         SafeWrite(files[fd], "\n", 1);
1240         SafeClose(files[fd]);
1241     }
1242 }
1243
1244 void
1245 RFCWriteMessage(DtMailEnv & error,
1246                 DtMail::Session * session,
1247                 const char * path,
1248                 DtMail::Message * msg)
1249 {
1250     RFCFormat * fmt;
1251     BufferMemory headers(1024);
1252     BufferMemory bodies(8192);
1253
1254     fmt = new RFCMIME(session);
1255       //call setIsWriteBcc so that the Bcc field will write to headers
1256       // buffer (see aix defect 177089)
1257     fmt->setIsWriteBcc(); 
1258     fmt->msgToBuffer(error, *msg, DTM_TRUE, DTM_TRUE, DTM_FALSE,
1259                      headers, bodies);
1260
1261     if (error.isSet()) {
1262         return;
1263     }
1264
1265     int fd = SafeOpen(path, O_RDWR | O_CREAT | O_TRUNC, 0600);
1266
1267     if (fd < 0) {
1268         error.setError(DTME_ObjectCreationFailed);
1269         delete fmt;
1270         return;
1271     }
1272
1273     char *fsname=(char *)error.getClient();
1274
1275     int len=headers.getSize()+bodies.getSize();
1276 /*
1277     printf("\n message body len=%d",len);
1278 */
1279     if( error.isSet() || !FileSystemSpace(path, len,&fsname) )
1280     {
1281         error.setError(DTME_OutOfSpace);
1282         error.setClient((void *)fsname);
1283         close(fd);
1284         remove(path);
1285         return;
1286     }
1287
1288     unsigned long iterateErrno;
1289     
1290     iterateErrno = headers.iterate(writeToFileDesc, &fd, 1, DTM_FALSE);
1291     if (iterateErrno == 0)
1292       iterateErrno = bodies.iterate(writeToFileDesc, &fd, 1, DTM_FALSE);
1293
1294     if (iterateErrno != 0)
1295       error.setError(DTME_ObjectCreationFailed);
1296     
1297     delete fmt;
1298     close(fd);
1299 }