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