Remove Unixware and openserver support
[oweals/cde.git] / cde / programs / dtmail / libDtMail / RFC / SunV3.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  *      $TOG: SunV3.C /main/9 1998/07/24 16:10:05 mgreess $
27  *
28  *      RESTRICTED CONFIDENTIAL INFORMATION:
29  *      
30  *      The information in this document is subject to special
31  *      restrictions in a confidential disclosure agreement bertween
32  *      HP, IBM, Sun, USL, SCO and Univel.  Do not distribute this
33  *      document outside HP, IBM, Sun, USL, SCO, or Univel wihtout
34  *      Sun's specific written approval.  This documment and all copies
35  *      and derivative works thereof must be returned or destroyed at
36  *      Sun's request.
37  *
38  *      Copyright 1993, 1994, 1995 Sun Microsystems, Inc.  All rights reserved.
39  *
40  *+ENOTICE
41  */
42
43 #include <EUSCompat.h>
44 #include <stdio.h>
45 #include <unistd.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <ctype.h>
49 #include <poll.h>
50 #include <fcntl.h>
51 #include <assert.h>
52 #include <signal.h>
53 #include <sys/wait.h>
54
55 /*
56 ** file included for INFTIM
57 */ 
58 #if defined(SunOS)
59 #include <stropts.h>
60 #elif defined(HPUX)
61 #include <sys/poll.h>
62 #elif defined(_AIX) || defined(linux)
63 #define INFTIM (-1)             /* Infinite timeout */
64 #endif
65
66 #include <sys/wait.h>
67
68 #include <Dt/Dts.h>
69
70 #include "SunV3.hh"
71 #include <DtMail/DtMailP.hh>
72 #include <DtMail/IO.hh>
73 #include "str_utils.h"
74
75
76 SunV3::SunV3(DtMail::Session * session)
77 : RFCFormat(session)
78 {
79 }
80
81 SunV3::~SunV3(void)
82 {
83 }
84
85 static DtMailBoolean
86 is7bit(const char * bp, unsigned long len)
87 {
88     for (const unsigned char * scan = (const unsigned char *)bp; 
89          ((const char *)scan) < (bp + len); scan++) {
90         if (*scan != (*scan & 0x7f)) {
91             return(DTM_FALSE);
92         }
93     }
94
95     return(DTM_TRUE);
96 }
97
98 static int
99 countLines(const char * bp, const unsigned long len)
100 {
101     int lines = 0;
102     for (const char * scan = bp; scan < (bp + len); scan++) {
103         if (*scan == '\n') {
104             lines += 1;
105         }
106     }
107
108     return(lines);
109 }
110
111 void
112 SunV3::formatBodies(DtMailEnv & error,
113                     DtMail::Message & msg,
114                     DtMailBoolean include_content_length,
115                     char ** extra_headers,
116                     Buffer & buf)
117 {
118 // For CHARSET
119     char *from_cs = NULL, *to_cs = NULL;
120         DtMailBoolean sevenbit = DTM_TRUE;
121 // End of For CHARSET
122         char v3type[64];
123         char charset_name[64];
124
125     error.clear();
126
127     // We will use a buffer to track the extra headers we put
128     // on a message.
129     //
130     BufferMemory hdr_buf(1024);
131
132     int body_count = msg.getBodyCount(error);
133     if (error.isSet()) {
134         return;
135     }
136
137     // If this is a single part message, then formatting is much
138     // easier.
139     //
140     if (body_count <= 1) {
141         // This is where we deviate a little from Sun V3 format. In
142         // SunV3, you can have a single part, that is an attachment.
143         // Our semantics say that any message with an attachment has
144         // at least 2 parts. Given this semantic, we can be a little
145         // more relaxed here because we know this first part must       
146         // be something like text.
147         //
148         hdr_buf.appendData("Content-Type: text", 18);
149         crlf(hdr_buf);
150
151         unsigned long bp_len;
152         const char * tmp_ptr;
153         char * bp_contents=NULL;
154         char * name=NULL;
155         DtMail::BodyPart * bp = msg.getFirstBodyPart(error);
156         if (error.isSet()) {
157             return;
158         }
159         bp->getContents(error,
160                         (const void **)&tmp_ptr,
161                         &bp_len,
162                         NULL,
163                         &name,
164                         NULL,
165                         NULL);
166
167         if (error.isSet()) {
168             if (name != NULL) free(name);
169             return;
170         }
171
172         if (bp_len > 0) {
173                 bp_contents = (char*)malloc((unsigned int)bp_len);
174                 memcpy(bp_contents, (char*)tmp_ptr, (size_t)bp_len);
175         }
176
177         getV3Type(bp, v3type);
178
179         // Should only call is7bit once for every body part.
180         // Calling is7bit more than once means scanning an entire body part again.
181         sevenbit = is7bit(bp_contents, bp_len);
182
183 // For CHARSET
184         if (strcasecmp(v3type, "text") == 0) {
185                 if (sevenbit == DTM_FALSE) {
186                         if (bp_contents) {
187                                 from_cs = NULL;
188                                 from_cs = _session->locToConvName();
189                                 to_cs = NULL;
190                                 to_cs = _session->targetConvName();
191                                 (void) _session->csConvert((char **)&bp_contents, 
192                                         bp_len, 1, from_cs, to_cs);
193                                 if ( from_cs )
194                                   free( from_cs );
195                                 if ( to_cs )
196                                   free( to_cs );
197                         }
198         
199                         getCharSet(charset_name, "V3");
200                 
201                         hdr_buf.appendData("X-Sun-Charset: ", 15);
202                         hdr_buf.appendData(charset_name, strlen(charset_name));
203                 } else {   // 7 bit
204                         hdr_buf.appendData("X-Sun-Charset: ", 15);
205                         hdr_buf.appendData("us-ascii", 8);
206                 }
207                 crlf(hdr_buf);
208         }
209 // End of For CHARSET
210
211         buf.appendData(bp_contents, (int) bp_len);
212
213         free(name);
214     }
215     else {
216         // This is a Sun V3 multipart message. We need to set the global
217         // headers to indicate this.
218         //
219         hdr_buf.appendData("Content-Type: X-Sun-Attachment", 30);
220         crlf(hdr_buf);
221
222         // We will simply loop through each part and process it in turn.
223         //
224         DtMail::BodyPart *bp = msg.getFirstBodyPart(error);
225         if (error.isSet()) {
226             return;
227         }
228         for ( ;bp && error.isNotSet(); bp = msg.getNextBodyPart(error, bp)) {
229
230             // Skip this body part if it is deleted.
231             if (bp->flagIsSet(error, DtMailBodyPartDeletePending))
232                 continue;
233
234             // First, put out the message seperator. It is a very
235             // weak line of 10 dashes.
236             //
237             buf.appendData("----------", 10);
238             crlf(buf);
239             unsigned long bp_len;
240             const char * tmp_ptr;
241             char * bp_contents=NULL;
242             char * name=NULL;
243             bp->getContents(error,
244                             (const void **)&tmp_ptr,
245                             &bp_len,
246                             NULL,
247                             &name,
248                             NULL,
249                             NULL);
250
251             // What can be done if getContents returns an error???
252             if (error.isSet()) {
253                 if (name != NULL) free(name);
254                 return;
255             }
256
257             // Check for an empty bodypart, bp_len = 0 or
258             // bp_contents is NULL.  Actually, if the bodypart is 
259             // empty, then both conditions should hold.
260             //
261             if (bp_len == 0 || !tmp_ptr) {
262                 if (name) free(name);
263                 continue;
264             }
265
266             bp_contents = (char*)malloc((unsigned int)bp_len);
267             memcpy(bp_contents, (char*)tmp_ptr, (size_t)bp_len);
268
269             // Now we need to write the content headers.
270             //
271             getV3Type(bp, v3type);
272
273            // Should only call is7bit once for every body part.
274            // Calling is7bit more than once means scanning an entire body part again.
275            sevenbit = is7bit(bp_contents, bp_len);
276
277 // For CHARSET
278        if ( strcasecmp(v3type, "text") == 0 ) {
279            if ( sevenbit == DTM_FALSE ) {
280                 from_cs = NULL;
281                 from_cs = _session->locToConvName();
282                 to_cs = NULL;
283                 to_cs = _session->targetConvName();
284                 if (_session->csConvert((char **)&bp_contents, bp_len, 1, from_cs, to_cs)) {
285                         getCharSet(charset_name, "V3");
286                         buf.appendData("X-Sun-Charset: ", 15);
287                         appendString(buf, charset_name);
288                         crlf(buf);
289                 }   // End of if csConvert()
290                 if ( from_cs )
291                   free( from_cs );
292                 if ( to_cs )
293                   free( to_cs );
294            } else {   // 7 bit
295                 buf.appendData("X-Sun-Charset: ", 15);
296                 appendString(buf, "us-ascii");
297                 crlf(buf);
298            }
299         }
300 // End of For CHARSET
301
302             buf.appendData("X-Sun-Data-Type: ", 17);
303             appendString(buf, v3type);
304             crlf(buf);
305
306             buf.appendData("X-Sun-Data-Description: ", 24);
307             appendString(buf, v3type);
308             crlf(buf);
309
310             if (!name) {
311                 name = strdup("Attachment");
312             }
313
314             buf.appendData("X-Sun-Data-Name: ", 17);
315             appendString(buf, name);
316             crlf(buf);
317
318                 // V3 compatible with OW Mailtool
319                 // 1. Do not uuencode text attachment containing 8 bit
320             if ( strncmp(bp_contents, "From ", 5) != 0 &&
321                          strcmp(v3type, "default-app") != 0 ) {
322                 int lines = countLines(bp_contents, bp_len);
323                 DtMailBoolean need_trailing_crlf = DTM_FALSE;
324
325                 if (bp_contents[bp_len - 1] != '\n') {
326                     lines += 1;
327                     need_trailing_crlf = DTM_TRUE;
328                 }
329
330                 char tmp_xl[20];
331                 sprintf(tmp_xl, "%d", lines);
332
333                 buf.appendData("X-Sun-Content-Lines: ", 21);
334                 appendString(buf, tmp_xl);
335
336                 crlf(buf);
337                 crlf(buf);
338
339                 buf.appendData(bp_contents, (int) bp_len);
340
341                 if (need_trailing_crlf) {
342                     crlf(buf);
343                 }
344             }
345             else {
346                 // We need to encode the buffer, to get the length,
347                 // then we insert it into the output buffer.
348                 //
349                 BufferMemory len_buf(8192);
350                 uuencode(len_buf, name, bp_contents, bp_len);
351
352                 int clen = len_buf.getSize();
353
354                 char * cbuf = new char[clen];
355                 if (cbuf == NULL) {
356                     error.setError(DTME_NoMemory);
357                     return;
358                 }
359
360                 BufReader * rd = len_buf.getReader();
361                 rd->getData(cbuf, clen);
362
363                 int lines = countLines(cbuf, clen);
364
365                 buf.appendData("X-Sun-Encoding-Info: uuencode", 29);
366                 crlf(buf);
367                 buf.appendData("X-Sun-Content-Lines: ", 21);
368                 char tmp_cl[20];
369                 sprintf(tmp_cl, "%d", lines);
370                 appendString(buf, tmp_cl);
371                 crlf(buf);
372                 crlf(buf);
373
374                 buf.appendData(cbuf, clen);
375                 delete [] cbuf;
376                 delete rd;
377             }
378
379             free(bp_contents);
380             bp_contents = NULL;
381             
382             free(name);
383             name = NULL;
384         }
385     }
386
387     error.clear();
388
389     if (include_content_length) {
390         hdr_buf.appendData("Content-Length: ", 16);
391         char tmpbuf[20];
392         sprintf(tmpbuf, "%d", buf.getSize());
393         hdr_buf.appendData(tmpbuf, strlen(tmpbuf));
394         crlf(hdr_buf);
395     }
396
397     *extra_headers = new char[hdr_buf.getSize() + 1];
398
399     BufReader * rdr = hdr_buf.getReader();
400
401     rdr->getData(*extra_headers, hdr_buf.getSize());
402     (*extra_headers)[hdr_buf.getSize()] = 0;
403
404     delete rdr;
405 }
406
407 static const char * block_headers[] = {
408     "Mime-Version",
409     "Content-Type",
410     "Content-Length",
411     "Content-MD5",
412     "X-Sun-Charset",
413     NULL
414 };
415
416 void
417 SunV3::formatHeaders(DtMailEnv & error,
418                        DtMail::Message & msg,
419                        DtMailBoolean include_unix_from,
420                        const char * extra_headers,
421                        Buffer & buf)
422 {
423     error.clear();
424
425     // We can use the parent comment RFC header formatter with our list
426     // of headers to suppress.
427     //
428     writeHeaders(error, msg, include_unix_from, extra_headers, block_headers, buf);
429 }
430
431 // SunV3::decode -- perform any decodings and return clear text
432 // Arguments:
433 //  const char * enc_info -- Sun V3 encoding info specification
434 //  char ** outputBp    -- -> place where -> clear text is placed on output
435 //  int & outputLen     -- number of bytes of clear text placed on output
436 //  const char * inputBp -- -> encoded source text
437 //  const unsigned long inputLen -- number of bytes in encoded source text
438 // Outputs:
439 //  **outputBp  -- will contain a -> a newly allocated buffer containing
440 //              the decoded message - dont forget to deallocate when done
441 //  off         -- contains the number of bytes in the decoded message at **buf
442 // Description
443 //  This function will decode the specified source message according to the
444 //  encoding information supplied. 
445 //
446 void
447 SunV3::decode(const char * enc_info, 
448               char ** outputBp, 
449               int & outputLen, 
450               const char * inputBp, 
451               const unsigned long inputLen)
452 {
453     // First we have to determine how the message was encoded.
454     // The encoding methods are listed, left to right in the order
455     // they were performed. We have to reverse the order of operations
456     // to get back to the original decoded value.
457     //
458     char  *enc_str = strdup(enc_info); // V3 did not have that many schemes.
459     char * encodings[4];
460
461     char * cur = enc_str;
462     char * end;
463     int n_enc = 0;
464     while(*cur && (end = strchr(cur, ','))) {
465         *end = 0;
466         encodings[n_enc++] = cur;
467         cur = end + 1;
468         while(*cur && isspace(*cur)) {
469             cur += 1;
470         }
471     }
472
473     if (*cur) {
474         encodings[n_enc++] = cur;
475     }
476
477     // At this point we have a sequence of encodings that must be
478     // used in order to obtain the final clear text of the message.
479     //
480
481     char * interimBp = (char *)inputBp;
482     unsigned long interimLen = inputLen;
483
484     for (int enc = n_enc - 1; enc >= 0; enc--) {
485
486         // next pass through decoder - if output is non-null, then
487         // it is the output from a previous pass, in which case it
488         // becomes the input to the second pass - we deallocate any
489         // old interim results, and then use output as the new interim
490         // results for the next decoding pass
491         //
492
493         assert (interimBp != NULL);
494         assert (interimLen);
495         if (*outputBp) {
496             if (interimBp) {
497                 if (interimBp != inputBp)
498                         free(interimBp);
499                 interimBp = 0;
500                 interimLen = 0;
501             }
502             assert(outputLen);
503             interimBp = *outputBp;
504             *outputBp = 0;
505             interimLen = outputLen;
506             outputLen = 0;
507         }
508
509         // At this point, outputBp and outputLen are not set
510         // The various decoding routines must allocate their
511         // own final storage and set output/output_len accordingly
512         //
513         
514         if (strcasecmp(encodings[enc], "uuencode") == 0) {
515             if (uudecode(outputBp, outputLen, interimBp, interimLen) == 0)
516                 continue;
517         }
518
519         else if ( (strcasecmp(encodings[enc], "compress") == 0)
520                 || (strcasecmp(encodings[enc], "default-compress") == 0)) {
521             if (uncompress(outputBp, outputLen, interimBp, interimLen) == 0)
522                 continue;
523         }
524
525         // An encoding we cant handle?? PUNT!
526         //
527
528         if (interimBp != inputBp) {     // in an interim buffer - slosh to output side
529             *outputBp = interimBp;
530             interimBp = 0;
531             outputLen = (int) interimLen;
532             interimLen = 0;
533         }
534         else {  // original source - must copy into newly allocated buffer
535             *outputBp = (char *)malloc((size_t) inputLen);
536             memcpy(*outputBp, inputBp, (size_t) inputLen);
537             outputLen = (int) inputLen;
538         }
539     }
540
541     assert(outputBp != NULL);
542     assert(*outputBp != NULL);
543     assert(outputLen >= 0);
544
545     // Done decoding the input
546     // free up space used to hold copies of encoding rules and return
547     //
548     free(enc_str);
549 }
550
551 // decode_uue_char -- decode a single uuencoded character
552 // Arguments:
553 //  const int val       -- uuencoded character
554 // Outputs:
555 //  return char         -- un-uencoded character
556 // Description
557 //  Given a single uuencoded character (which is represented as
558 //  a 7-bit printable ascii character) return an unencoded 6-bit
559 //  character which can be used to assemble real encoded characters
560 //
561
562 static inline char
563 decode_uue_char(const int val)
564 {
565     return((val - ' ') & 077);
566 }
567
568 static inline void
569 fill_copy(unsigned char * out, 
570           const unsigned char * in, 
571           const unsigned char * in_end, 
572           int size_needed)
573 {
574     int in_size = in_end - in + 1;
575     memcpy(out, in, in_size);
576
577     memset(out + in_size, ' ', size_needed - in_size);
578     out[size_needed - 1] = 0;
579 }
580
581 const unsigned char *
582 decode_uue_line(char * buf,
583             int & off,
584             const unsigned char * encodedBp, 
585             const unsigned long decodedLen,
586             const unsigned char * encodedEndBp)
587 {
588     unsigned char line_buf[100];
589     const unsigned char * line = line_buf;
590     const unsigned char * nextEncodedLineStart;
591     const unsigned char * nl;
592     unsigned long encodedLen;
593     unsigned long countDown = decodedLen;
594
595     // See if the line ends with a new line, and is within the range
596     // of the buffer.
597     // First, compute the length of the encoded byte stream on this line.
598     // Given the decoded length, we can do this because the encodings are
599     // emitted in groups of four bytes for every three bytes encoded.
600     //
601     encodedLen = ((decodedLen/3)+((decodedLen%3)!=0))*4;
602     if ((encodedBp + encodedLen) > encodedEndBp || encodedBp[encodedLen] != '\n') {
603         // Well, looks like some trailing white space was lost in
604         // transmission. Lets copy the line and fill in what is needed.
605         // (note: if this does happen the source could be corrupted, as the
606         // encoded length is derived from the supposed number of decoded
607         // characters we have to generate; if the former is off, how can
608         // we yield correct bytes for the latter??)
609         //
610
611         for (nl = encodedBp; nl <= encodedEndBp && *nl != '\n'; nl++) {
612             continue;
613         }
614
615         if (nl < (encodedBp + encodedLen)) {
616             assert(sizeof(line_buf) >= encodedLen);
617             fill_copy(line_buf, encodedBp, nl, (int) encodedLen);
618         }
619         else {
620             // We will ignore extra characters at the end of the line.
621             // (great...)
622             line = encodedBp;
623         }
624         
625         nextEncodedLineStart = nl + 1;
626     }
627     else {
628         line = encodedBp;
629         nextEncodedLineStart = encodedBp + encodedLen + 1;
630     }
631
632     // Now the fun begins - obtain a -> the next free byte in the output stream
633     // and then spin through the input stream decoding quartets of uuencoded data
634     // into triplets of clear text, abstaining as necessary to provide for the
635     // last few bytes of the file which may not be mod 3 in length. This routine
636     // has been optimized for performance.
637     //
638     char *lbp = buf+off;        // -> next free byte in output stream
639     const unsigned char * encE = (encodedBp + encodedLen);      // -> past last input byte
640     for (const unsigned char * enc = encodedBp; enc < encE; enc += 4, countDown -= 3) {
641         if (countDown >= 3) {
642           *lbp++ = decode_uue_char(*enc) << 2 | decode_uue_char(*(enc + 1)) >> 4;
643           *lbp++ = decode_uue_char(*(enc + 1)) << 4 | decode_uue_char(*(enc + 2)) >> 2;
644           *lbp++ = decode_uue_char(*(enc + 2)) << 6 | decode_uue_char(*(enc + 3));
645         }
646         else if (countDown >= 2) {
647           *lbp++ = decode_uue_char(*enc) << 2 | decode_uue_char(*(enc + 1)) >> 4;
648           *lbp++ = decode_uue_char(*(enc + 1)) << 4 | decode_uue_char(*(enc + 2)) >> 2;
649         }
650         else if (countDown >= 1) {
651           *lbp++ = decode_uue_char(*enc) << 2 | decode_uue_char(*(enc + 1)) >> 4;
652         }
653     }
654     off += (int) decodedLen;    // bump offset by the # of bytes we appended 
655     return(nextEncodedLineStart);
656 }
657
658 // SunV3::uncompress -- decode compressed data stream into clear text data stream
659 // Arguments:
660 //  outputBp    -- -> location to receive -> clear text results
661 //  outputLen   -- will contain # of bytes of clear text results returned
662 //  inputBp     -- -> compressed data stream
663 //  inputLen    -- number of bytes of compressed data
664 // Outputs:
665 //  outputBp    -- contain -> allocated storage containing clear text results
666 //  outputLen   -- number of bytes of clear text results contained at outputBp
667 // Returns:
668 //  == 0 -- successful
669 //  != 0 -- not successful
670 // Description:
671 //  Decode the input data stream using the uncompress algorithm, returning
672 //  the clear text results of the operation. This is performed by forking
673 //  a copy of uncompress to allow it to perform the actual processing.
674 // Notes:
675 //  For the sake of convenience, implementation of uncompress is done here
676 //  in a single monolithic function. If another 4n process needs to be added
677 //  for other types of decoding, the fork and processing functions should
678 //  be broken out into their own individual routines.
679 //
680
681 int
682 SunV3::uncompress(char ** outputBp, int & outputLen, const char * inputBp, const unsigned long inputLen)
683 {
684     // We are passed a -> a place to store a pointer to the decoded information
685     // and a call-by-ref of a place to store the number of bytes stored. Must 
686     // allocate the clear text buffer. Unfortunately, the nature of the compression
687     // algorithm makes it imposible to predict ahead of time how many bytes are
688     // going to be needed to hold the results. So we allocate an initial buffer
689     // that is 2x the size of the input (allowing for 50% compression). We grow
690     // the buffer as necessary during the uncompression operation, and shrink it
691     // to fit when the final clear text stream size becomes known
692
693     struct pollfd pollFDS[2];                   // structure to use for poll() call
694     const int childInputFD = 0;                 // index into pollFDS to send input to child
695     const int childOutputFD = 1;                // index into pollFDS to read output from child
696
697     long int interimOutputLength = 0;
698     const long int blockSize = 4096;    // i/o done at least at this size
699     long int interimOutputLimit = (inputLen+(blockSize-(inputLen%blockSize)));
700     unsigned char *interimOutputBuffer = (unsigned char *)malloc((size_t)interimOutputLimit);
701     assert(interimOutputBuffer != NULL);
702     unsigned char *interimBp = interimOutputBuffer;
703
704     // Fork off the uncompress function so its ready to process the data
705     // 
706
707     int inputPipe[2];           // input pipe descriptors (from child point of view)
708     int outputPipe[2];          // output pipe descriptors (from child point of view)
709     const int pipeReader = 0;   // pipe[0] is read side of pipe
710     const int pipeWriter = 1;   // pipe[1] is write side of pipe
711     pid_t childPid;             // pid for child process
712     int childStatus;            // placeholder for child exit status
713
714     if (pipe(inputPipe) == -1)                  // obtain pipe for child's input
715         return(1);
716     if (pipe(outputPipe) == -1) {               // obtain pipe for child's output
717         (void) SafeClose(inputPipe[pipeReader]);
718         (void) SafeClose(inputPipe[pipeWriter]);
719         return(1);
720     }
721
722     if (!(childPid = fork())) {                 // child process
723         // Need to clean up a bit before exec()ing the child
724         // Close all non-essential open files, signals, etc.
725         // NOTE: probably reduce priv's to invoking user too???
726         //
727         long maxOpenFiles = sysconf(_SC_OPEN_MAX);
728
729         if (maxOpenFiles < 32)          // less than 32 descriptors?
730           maxOpenFiles = 1024;          // dont believe it--assume lots
731         for (int sig = 1; sig < NSIG; sig++)
732           (void) signal(sig, SIG_DFL);                  // reset all signal handlers
733         if (SafeDup2 (inputPipe[pipeReader], STDIN_FILENO) == -1)       // input pipe reader is stdin
734           _exit (1);                                    // ERROR: exit with bad status
735         (void) SafeClose(inputPipe[pipeWriter]);        // input pipe writer n/a
736         if (SafeDup2 (outputPipe[pipeWriter], STDOUT_FILENO) == -1)     // output pipe writer is stdout
737           _exit(1);                                     // ERROR: exit with bad status
738         (void) SafeClose(outputPipe[pipeReader]);       // output pipe reader n/a
739         // NOTE: we leave standard error output open
740         for (int cfd = 3; cfd < maxOpenFiles; cfd++)
741           (void) SafeClose(cfd);                        // close all open file descriptors
742         (void) execl("/usr/bin/uncompress", "uncompress", "-qc", (char *)0); // try direct route first
743         (void) execlp("uncompress", "uncompress", "-qc", (char *)0);    // failed - try via path
744         _exit (1);                                      // failed!? return error exit code
745     }
746
747     if (childPid == -1)                                 // fork failed??
748       return(1);                                        // yes: bail
749     
750     (void) SafeClose(inputPipe[pipeReader]);            // input pipe reader n/a
751     (void) SafeClose(outputPipe[pipeWriter]);           // output pipe writer n/a
752
753 #if defined(O_NONBLOCK)
754     fcntl(inputPipe[pipeWriter], F_SETFL, O_NONBLOCK);  // we dont want to block writing to child
755 #elif defined(FNBIO)
756     (void) fcntl(inputPipe[pipeWriter], F_SETFL, FNBIO);        // we dont want to block writing to child
757 #endif
758
759     // Ok, uncompress is out there spinning its wheels waiting for us
760     // enter a poll loop responding to file descriptor events
761     //
762
763     pollFDS[childInputFD].fd = inputPipe[pipeWriter];   // write input for child process here
764     pollFDS[childInputFD].events = POLLOUT;     // tell us when data may be written w/o blocking
765     pollFDS[childInputFD].revents = 0;          // no events pending 
766     pollFDS[childOutputFD].fd = outputPipe[pipeReader]; // read output from child process here
767     pollFDS[childOutputFD].events = POLLIN;     // tell us when data may be read w/o blocking
768     pollFDS[childOutputFD].revents = 0;         // no events pending
769
770     unsigned char * currentInputBp = (unsigned char *)inputBp; // track -> input
771     unsigned long currentInputCount = inputLen; // input bytes left to process
772
773     while (poll(pollFDS, 2, INFTIM) > 0) {
774         // process events on file descriptors
775         // in case two events happen at once, handle the reading from the
776         // process first, to make room for data that then may be written
777
778         // process reading output from the child
779         // Expand buffer as necessary to contain further data
780         //
781         if (pollFDS[childOutputFD].revents & POLLIN) {
782
783             // if there is less than blockSize free bytes left in clear text stream,
784             // expand the buffer by 10% before reading further data from child
785             //
786             if ((interimOutputLimit - interimOutputLength) < blockSize) {
787                 long int delta = (long int) (interimOutputLimit * 1.10);
788                 delta = delta+(blockSize-(delta%blockSize));
789                 interimOutputBuffer = (unsigned char *)realloc(interimOutputBuffer, (size_t)delta);
790                 assert(interimOutputBuffer != NULL);
791                 interimOutputLimit = delta;
792                 interimBp = interimOutputBuffer + interimOutputLength;
793             }
794
795             // attempt to read as much data from the child as possible
796             //
797             ssize_t readCount = SafeRead(pollFDS[childOutputFD].fd, interimBp, (size_t)(interimOutputLimit - interimOutputLength));
798             if (readCount == -1) {              // error - nuke child and bail
799                 (void) kill(childPid, SIGKILL);
800                 break;
801             }
802             interimOutputLength += readCount;
803             interimBp += readCount;
804             if (readCount == 0)                         // end of file from the child?
805                 (void) SafeClose(pollFDS[childOutputFD].fd);    // yes, close down child output pipe
806         }
807
808         // process writing input to the child
809         //
810         if (pollFDS[childInputFD].revents & POLLOUT) {
811
812           // child input queue has foom for more data
813           // if no more data to send, close the child input pipe down
814           //
815           if (currentInputCount == 0) {
816               (void) SafeClose(pollFDS[childInputFD].fd);
817               continue;
818           }
819
820           // attempt to send as much data as the child will accept
821           //
822           ssize_t writeCount = SafeWrite(pollFDS[childInputFD].fd, currentInputBp, (size_t)currentInputCount);
823           if (writeCount == -1) {               // error - nuke child and bail
824               (void) kill(childPid, SIGKILL);
825               break;
826           }
827           currentInputCount -= writeCount;      // bump back by # bytes child accepted
828           currentInputBp += writeCount;         // bump forward index into input stream
829           continue;
830         }
831
832         // if both the input and output file descriptors become invalid, then
833         // we are done processing the data stream; otherwise, continue to spin
834         //
835         if ( (pollFDS[childInputFD].revents & (POLLHUP|POLLNVAL)) && (pollFDS[childOutputFD].revents & (POLLHUP|POLLNVAL)) )
836             break;
837     }
838
839     // all done processing data -- perform cleanup work 
840     //
841     (void) SafeClose(inputPipe[pipeWriter]);    // make sure child input pipe closed
842     (void) SafeClose(outputPipe[pipeReader]);   // make sure child output pipe closed
843
844     while (SafeWaitpid(childPid, &childStatus, 0) >= 0) // retrieve child status
845         ;
846
847     // Hard choices follow: for some reason the uncompress function has exited
848     // with a non-zero status - *something* has failed. If so, toss any output
849     // that may have been generated, and return a failure indication
850     //
851     if (childStatus != 0) {
852         assert(interimOutputBuffer != NULL);
853         free((char *)interimOutputBuffer);      // toss any output
854         return(1);
855     }
856
857     // All is well - fixup callers output variables and return
858     // Also, reduce size of allocated buffer so that unused space
859     // is returned to the free pool
860     //
861
862     assert(interimOutputLength == (interimBp-interimOutputBuffer));
863     if (interimOutputLength < interimOutputLimit)
864       interimOutputBuffer = (unsigned char *)realloc(interimOutputBuffer, (size_t)(interimOutputLength+1));
865     assert(interimOutputBuffer != NULL);
866     
867     outputLen = (int)interimOutputLength;       // stuff output length in caller's variable
868     *outputBp = (char *)interimOutputBuffer;    // stuff -> clear text in caller's variable
869     return(0);
870 }
871
872 // SunV3::uudecode -- decode uuencoded data stream into clear text data stream
873 // Arguments:
874 //  outputBp    -- -> location to receive -> clear text results
875 //  outputLen   -- will contain # of bytes of clear text results returned
876 //  inputBp     -- -> uuencoded data stream
877 //  inputLen    -- number of bytes of uuencoded data
878 // Outputs:
879 //  outputBp    -- contain -> allocated storage containing clear text results
880 //  outputLen   -- number of bytes of clear text results contained at outputBp
881 // Returns:
882 //  == 0 -- successful
883 //  != 0 -- not successful
884 // Description:
885 //  Decode the input data stream using the uudecode algorithm, returning
886 //  the clear text results of the operation.
887 //
888
889 int
890 SunV3::uudecode(char ** outputBp, int & outputLen, const char * inputBp, const unsigned long inputLen)
891 {
892     // We are not really interested in the "begin <mode> <name>" line,
893     // so let's blow by it.
894     //
895     const char * line_1 = inputBp;
896
897     if (strncmp(inputBp, "begin ", 6) == 0) {
898         for (line_1 = inputBp; line_1 < (inputBp + inputLen); line_1++) {
899             if (*line_1 == '\n') {
900                 break;
901             }
902         }
903     }
904     line_1 += 1;
905
906     if (line_1 >= (inputBp + inputLen)) {
907         return(1);
908     }
909
910     // We are passed a -> a place to store a pointer to the decoded information
911     // and a call-by-ref of a place to store the number of bytes stored. Must
912     // allocate the buffer. Fortunately, the uuencoding algorithm is regular
913     // encoding 3 8-bit bytes into 4 6-bit bytes, so we can allocate a buffer
914     // that is big enough to hold the decoded results, plus a little slosh 
915     // because the length we compute from includes algrithmic overhead.
916     //
917
918     const int totalOutputLen = (int)((inputLen/4)*3);
919     *outputBp = (char *)malloc(totalOutputLen);
920     outputLen = 0;
921
922     // The first character of each line tells us how many characters
923     // to the next new line. We will loop through each line, and
924     // decode the characters. Any line shortages are made up with
925     // spaces for the decoding algorithm.
926     //
927     // Note: a properly uuencoded file is ended with two lines:
928     //  1- a line beginning with a space (which works out to 0 bytes)
929     //  2- the word "end" on a line by itself.
930     //
931     // In bug #1196898 the following end sequence was encountered:
932     //   "L9&%N8V4@=&AE('1I9&4N+BXB"CX@"0D)(" @(" @1RX@0G)O;VMS"CX@"@H@"
933     //   ""
934     //   "end"
935     //   ""
936     //   "Janice Anthes, SE                janice.anthes@west.sun.COM"
937     //
938     // Where there was a blank line with no leading space in it before
939     // the "end" line, so we need to add:
940     //  3- a completely blank line (which is an invalid uuencoded line)
941     //  
942     //
943     for (const unsigned char * encodedBp = (const unsigned char *)line_1; 
944          (const char *)encodedBp < (inputBp + inputLen);) {
945
946         if ( (*encodedBp == '\n')
947           || ( (*encodedBp == 'e')
948                && (*(encodedBp+1) == 'n')
949                && (*(encodedBp+2) == 'd')
950                && (*(encodedBp+3) == '\n') ) ) {
951           // fix for bug #1196899 - completely blank line
952           break;
953         }
954         
955         int decodedLen = decode_uue_char(*encodedBp);   // # of DECODED bytes
956
957         if (decodedLen <= 0 || decodedLen > 45) {
958             // End of the buffer.
959             break;
960         }
961
962         encodedBp += 1; // bop past the # decoded bytes character
963         encodedBp = decode_uue_line(*outputBp,
964                                  outputLen,
965                                  encodedBp,
966                                  decodedLen,
967                                  (const unsigned char *)(inputBp + inputLen));
968         assert(outputLen <= totalOutputLen);
969     }
970
971     // Done decoding the entire source - reduce size of allocated buffer
972     // so that unused space is returned to the free pool
973     //
974
975     if (outputLen < totalOutputLen)
976         *outputBp = (char *)realloc(*outputBp, outputLen+1);
977
978     return(0);  // success
979 }
980
981 static inline char
982 encode_uue_char(const int val)
983 {
984     return((val & 077) + ' ');
985 }
986
987 void
988 SunV3::encode_uue_line(Buffer & buf,
989                    const unsigned char * unencodedBp,
990                    const unsigned long unencodedLen)
991 {
992     unsigned long triplets = unencodedLen - (unencodedLen % 3);
993     char enc[4];
994
995     for (const unsigned char * cur = unencodedBp; cur < (unencodedBp + triplets - 2); cur += 3) {
996         enc[0] = encode_uue_char(*cur >> 2);
997         enc[1] = encode_uue_char((*cur << 4) & 060 | (*(cur + 1) >> 4) & 017);
998         enc[2] = encode_uue_char((*(cur + 1) << 2) & 074 | (*(cur + 2) >> 6) & 03);
999         enc[3] = encode_uue_char(*(cur + 2) & 077);
1000         buf.appendData(enc, 4);
1001     }
1002
1003     crlf(buf);
1004 }
1005
1006 void
1007 SunV3::uuencode(Buffer & buf,
1008                 const char * path,
1009                 const char * bp,
1010                 const unsigned long len)
1011 {
1012     const unsigned char * ubp = (const unsigned char *) bp;
1013     const unsigned char * cur;
1014
1015     // Write out the initial information.
1016     //
1017     buf.appendData("begin 600 ", 10);
1018     buf.appendData(path, strlen(path));
1019     crlf(buf);
1020
1021     // We will scan the input buffer, converting to the uuencoded
1022     // representation. Each line will have 45 characters, not including
1023     // the initial "M", or trailing line termination.
1024     //
1025     unsigned long whole_lines = len - (len % 45);
1026     unsigned long column = 0;
1027     for (cur = ubp; cur < (ubp + whole_lines - 2); cur += 45) {
1028         buf.appendData("M", 1);
1029         encode_uue_line(buf, cur, 45);
1030 #ifdef DEAD_WOOD
1031         DtMailProcessClientEvents();
1032 #endif
1033     }
1034
1035     // Write the partial line.
1036     //
1037     int left_over = (int)(len % 45);
1038     char size_char = encode_uue_char(left_over);
1039     buf.appendData(&size_char, 1);
1040
1041     // uuencode is really memory unfriendly. We need to make sure we
1042     // don't read past the end of any buffers. We also want to right
1043     // characters, so we will round the left_over size up to an even
1044     // grouping of 3 bytes.
1045     //
1046     unsigned char safe_buf[45];
1047     memset(safe_buf, 0, sizeof(safe_buf));
1048     memcpy(safe_buf, cur, left_over);
1049     left_over += (3 - (left_over % 3));
1050
1051     encode_uue_line(buf, safe_buf, left_over);
1052
1053         crlf(buf);
1054         buf.appendData("end", 3);
1055         crlf(buf);
1056 }
1057
1058 void
1059 SunV3::getV3Type(DtMail::BodyPart * bp, char * v3type)
1060 {
1061     // Get the Dt type name from the body part.
1062     //
1063     char * type;
1064     DtMailEnv error;
1065
1066     bp->getContents(error,
1067                     NULL,
1068                     NULL,
1069                     &type,
1070                     NULL,
1071                     NULL,
1072                     NULL);
1073
1074     // Look it up in the data typing system. Hopefully we will
1075     // get a db based mime name.
1076     //
1077     char * db_type = DtDtsDataTypeToAttributeValue(type,
1078                                                    "SUNV3_TYPE",
1079                                                    NULL);
1080
1081     // See if we call this text. If so, then it will be text/plain,
1082     // if not then application/octet-stream
1083     //
1084     char * text_type = DtDtsDataTypeToAttributeValue(type,
1085                                                      DtDTS_DA_IS_TEXT,
1086                                                      NULL);
1087
1088     if (db_type) {
1089         strcpy(v3type, db_type);
1090     }
1091     else {
1092         if (text_type && strcasecmp(text_type, "true") == 0) {
1093             strcpy(v3type, "text");
1094         }
1095         else {
1096             strcpy(v3type, "default");
1097         }
1098     }
1099
1100     free(type);
1101     if (db_type) {
1102         free(db_type);
1103     }
1104     if (text_type) {
1105         free(text_type);
1106     }
1107
1108     return;
1109 }