2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
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)
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
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
26 * $TOG: SunV3.C /main/9 1998/07/24 16:10:05 mgreess $
28 * RESTRICTED CONFIDENTIAL INFORMATION:
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
38 * Copyright 1993, 1994, 1995 Sun Microsystems, Inc. All rights reserved.
43 #include <EUSCompat.h>
56 ** file included for INFTIM
58 #if defined(SunOS) || defined (USL)
62 #elif defined(_AIX) || defined(linux)
63 #define INFTIM (-1) /* Infinite timeout */
71 #include <DtMail/DtMailP.hh>
72 #include <DtMail/IO.hh>
73 #include "str_utils.h"
76 SunV3::SunV3(DtMail::Session * session)
86 is7bit(const char * bp, unsigned long len)
88 for (const unsigned char * scan = (const unsigned char *)bp;
89 ((const char *)scan) < (bp + len); scan++) {
90 if (*scan != (*scan & 0x7f)) {
99 countLines(const char * bp, const unsigned long len)
102 for (const char * scan = bp; scan < (bp + len); scan++) {
112 SunV3::formatBodies(DtMailEnv & error,
113 DtMail::Message & msg,
114 DtMailBoolean include_content_length,
115 char ** extra_headers,
119 char *from_cs = NULL, *to_cs = NULL;
120 DtMailBoolean sevenbit = DTM_TRUE;
121 // End of For CHARSET
123 char charset_name[64];
127 // We will use a buffer to track the extra headers we put
130 BufferMemory hdr_buf(1024);
132 int body_count = msg.getBodyCount(error);
137 // If this is a single part message, then formatting is much
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.
148 hdr_buf.appendData("Content-Type: text", 18);
151 unsigned long bp_len;
152 const char * tmp_ptr;
153 char * bp_contents=NULL;
155 DtMail::BodyPart * bp = msg.getFirstBodyPart(error);
159 bp->getContents(error,
160 (const void **)&tmp_ptr,
168 if (name != NULL) free(name);
173 bp_contents = (char*)malloc((unsigned int)bp_len);
174 memcpy(bp_contents, (char*)tmp_ptr, (size_t)bp_len);
177 getV3Type(bp, v3type);
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);
184 if (strcasecmp(v3type, "text") == 0) {
185 if (sevenbit == DTM_FALSE) {
188 from_cs = _session->locToConvName();
190 to_cs = _session->targetConvName();
191 (void) _session->csConvert((char **)&bp_contents,
192 bp_len, 1, from_cs, to_cs);
199 getCharSet(charset_name, "V3");
201 hdr_buf.appendData("X-Sun-Charset: ", 15);
202 hdr_buf.appendData(charset_name, strlen(charset_name));
204 hdr_buf.appendData("X-Sun-Charset: ", 15);
205 hdr_buf.appendData("us-ascii", 8);
209 // End of For CHARSET
211 buf.appendData(bp_contents, (int) bp_len);
216 // This is a Sun V3 multipart message. We need to set the global
217 // headers to indicate this.
219 hdr_buf.appendData("Content-Type: X-Sun-Attachment", 30);
222 // We will simply loop through each part and process it in turn.
224 DtMail::BodyPart *bp = msg.getFirstBodyPart(error);
228 for ( ;bp && error.isNotSet(); bp = msg.getNextBodyPart(error, bp)) {
230 // Skip this body part if it is deleted.
231 if (bp->flagIsSet(error, DtMailBodyPartDeletePending))
234 // First, put out the message seperator. It is a very
235 // weak line of 10 dashes.
237 buf.appendData("----------", 10);
239 unsigned long bp_len;
240 const char * tmp_ptr;
241 char * bp_contents=NULL;
243 bp->getContents(error,
244 (const void **)&tmp_ptr,
251 // What can be done if getContents returns an error???
253 if (name != NULL) free(name);
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.
261 if (bp_len == 0 || !tmp_ptr) {
262 if (name) free(name);
266 bp_contents = (char*)malloc((unsigned int)bp_len);
267 memcpy(bp_contents, (char*)tmp_ptr, (size_t)bp_len);
269 // Now we need to write the content headers.
271 getV3Type(bp, v3type);
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);
278 if ( strcasecmp(v3type, "text") == 0 ) {
279 if ( sevenbit == DTM_FALSE ) {
281 from_cs = _session->locToConvName();
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);
289 } // End of if csConvert()
295 buf.appendData("X-Sun-Charset: ", 15);
296 appendString(buf, "us-ascii");
300 // End of For CHARSET
302 buf.appendData("X-Sun-Data-Type: ", 17);
303 appendString(buf, v3type);
306 buf.appendData("X-Sun-Data-Description: ", 24);
307 appendString(buf, v3type);
311 name = strdup("Attachment");
314 buf.appendData("X-Sun-Data-Name: ", 17);
315 appendString(buf, name);
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;
325 if (bp_contents[bp_len - 1] != '\n') {
327 need_trailing_crlf = DTM_TRUE;
331 sprintf(tmp_xl, "%d", lines);
333 buf.appendData("X-Sun-Content-Lines: ", 21);
334 appendString(buf, tmp_xl);
339 buf.appendData(bp_contents, (int) bp_len);
341 if (need_trailing_crlf) {
346 // We need to encode the buffer, to get the length,
347 // then we insert it into the output buffer.
349 BufferMemory len_buf(8192);
350 uuencode(len_buf, name, bp_contents, bp_len);
352 int clen = len_buf.getSize();
354 char * cbuf = new char[clen];
356 error.setError(DTME_NoMemory);
360 BufReader * rd = len_buf.getReader();
361 rd->getData(cbuf, clen);
363 int lines = countLines(cbuf, clen);
365 buf.appendData("X-Sun-Encoding-Info: uuencode", 29);
367 buf.appendData("X-Sun-Content-Lines: ", 21);
369 sprintf(tmp_cl, "%d", lines);
370 appendString(buf, tmp_cl);
374 buf.appendData(cbuf, clen);
389 if (include_content_length) {
390 hdr_buf.appendData("Content-Length: ", 16);
392 sprintf(tmpbuf, "%d", buf.getSize());
393 hdr_buf.appendData(tmpbuf, strlen(tmpbuf));
397 *extra_headers = new char[hdr_buf.getSize() + 1];
399 BufReader * rdr = hdr_buf.getReader();
401 rdr->getData(*extra_headers, hdr_buf.getSize());
402 (*extra_headers)[hdr_buf.getSize()] = 0;
407 static const char * block_headers[] = {
417 SunV3::formatHeaders(DtMailEnv & error,
418 DtMail::Message & msg,
419 DtMailBoolean include_unix_from,
420 const char * extra_headers,
425 // We can use the parent comment RFC header formatter with our list
426 // of headers to suppress.
428 writeHeaders(error, msg, include_unix_from, extra_headers, block_headers, buf);
431 // SunV3::decode -- perform any decodings and return clear text
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
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
443 // This function will decode the specified source message according to the
444 // encoding information supplied.
447 SunV3::decode(const char * enc_info,
450 const char * inputBp,
451 const unsigned long inputLen)
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.
458 char *enc_str = strdup(enc_info); // V3 did not have that many schemes.
461 char * cur = enc_str;
464 while(*cur && (end = strchr(cur, ','))) {
466 encodings[n_enc++] = cur;
468 while(*cur && isspace(*cur)) {
474 encodings[n_enc++] = cur;
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.
481 char * interimBp = (char *)inputBp;
482 unsigned long interimLen = inputLen;
484 for (int enc = n_enc - 1; enc >= 0; enc--) {
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
493 assert (interimBp != NULL);
497 if (interimBp != inputBp)
503 interimBp = *outputBp;
505 interimLen = outputLen;
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
514 if (strcasecmp(encodings[enc], "uuencode") == 0) {
515 if (uudecode(outputBp, outputLen, interimBp, interimLen) == 0)
519 else if ( (strcasecmp(encodings[enc], "compress") == 0)
520 || (strcasecmp(encodings[enc], "default-compress") == 0)) {
521 if (uncompress(outputBp, outputLen, interimBp, interimLen) == 0)
525 // An encoding we cant handle?? PUNT!
528 if (interimBp != inputBp) { // in an interim buffer - slosh to output side
529 *outputBp = interimBp;
531 outputLen = (int) interimLen;
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;
541 assert(outputBp != NULL);
542 assert(*outputBp != NULL);
543 assert(outputLen >= 0);
545 // Done decoding the input
546 // free up space used to hold copies of encoding rules and return
551 // decode_uue_char -- decode a single uuencoded character
553 // const int val -- uuencoded character
555 // return char -- un-uencoded character
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
563 decode_uue_char(const int val)
565 return((val - ' ') & 077);
569 fill_copy(unsigned char * out,
570 const unsigned char * in,
571 const unsigned char * in_end,
574 int in_size = in_end - in + 1;
575 memcpy(out, in, in_size);
577 memset(out + in_size, ' ', size_needed - in_size);
578 out[size_needed - 1] = 0;
581 const unsigned char *
582 decode_uue_line(char * buf,
584 const unsigned char * encodedBp,
585 const unsigned long decodedLen,
586 const unsigned char * encodedEndBp)
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;
595 // See if the line ends with a new line, and is within the range
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.
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??)
611 for (nl = encodedBp; nl <= encodedEndBp && *nl != '\n'; nl++) {
615 if (nl < (encodedBp + encodedLen)) {
616 assert(sizeof(line_buf) >= encodedLen);
617 fill_copy(line_buf, encodedBp, nl, (int) encodedLen);
620 // We will ignore extra characters at the end of the line.
625 nextEncodedLineStart = nl + 1;
629 nextEncodedLineStart = encodedBp + encodedLen + 1;
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.
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));
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;
650 else if (countDown >= 1) {
651 *lbp++ = decode_uue_char(*enc) << 2 | decode_uue_char(*(enc + 1)) >> 4;
654 off += (int) decodedLen; // bump offset by the # of bytes we appended
655 return(nextEncodedLineStart);
658 // SunV3::uncompress -- decode compressed data stream into clear text data stream
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
665 // outputBp -- contain -> allocated storage containing clear text results
666 // outputLen -- number of bytes of clear text results contained at outputBp
668 // == 0 -- successful
669 // != 0 -- not successful
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.
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.
682 SunV3::uncompress(char ** outputBp, int & outputLen, const char * inputBp, const unsigned long inputLen)
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
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
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;
704 // Fork off the uncompress function so its ready to process the data
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
714 if (pipe(inputPipe) == -1) // obtain pipe for child's input
716 if (pipe(outputPipe) == -1) { // obtain pipe for child's output
717 (void) SafeClose(inputPipe[pipeReader]);
718 (void) SafeClose(inputPipe[pipeWriter]);
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???
727 long maxOpenFiles = sysconf(_SC_OPEN_MAX);
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
747 if (childPid == -1) // fork failed??
748 return(1); // yes: bail
750 (void) SafeClose(inputPipe[pipeReader]); // input pipe reader n/a
751 (void) SafeClose(outputPipe[pipeWriter]); // output pipe writer n/a
753 #if defined(O_NONBLOCK)
754 fcntl(inputPipe[pipeWriter], F_SETFL, O_NONBLOCK); // we dont want to block writing to child
756 (void) fcntl(inputPipe[pipeWriter], F_SETFL, FNBIO); // we dont want to block writing to child
759 // Ok, uncompress is out there spinning its wheels waiting for us
760 // enter a poll loop responding to file descriptor events
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
770 unsigned char * currentInputBp = (unsigned char *)inputBp; // track -> input
771 unsigned long currentInputCount = inputLen; // input bytes left to process
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
778 // process reading output from the child
779 // Expand buffer as necessary to contain further data
781 if (pollFDS[childOutputFD].revents & POLLIN) {
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
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;
795 // attempt to read as much data from the child as possible
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);
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
808 // process writing input to the child
810 if (pollFDS[childInputFD].revents & POLLOUT) {
812 // child input queue has foom for more data
813 // if no more data to send, close the child input pipe down
815 if (currentInputCount == 0) {
816 (void) SafeClose(pollFDS[childInputFD].fd);
820 // attempt to send as much data as the child will accept
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);
827 currentInputCount -= writeCount; // bump back by # bytes child accepted
828 currentInputBp += writeCount; // bump forward index into input stream
832 // if both the input and output file descriptors become invalid, then
833 // we are done processing the data stream; otherwise, continue to spin
835 if ( (pollFDS[childInputFD].revents & (POLLHUP|POLLNVAL)) && (pollFDS[childOutputFD].revents & (POLLHUP|POLLNVAL)) )
839 // all done processing data -- perform cleanup work
841 (void) SafeClose(inputPipe[pipeWriter]); // make sure child input pipe closed
842 (void) SafeClose(outputPipe[pipeReader]); // make sure child output pipe closed
844 while (SafeWaitpid(childPid, &childStatus, 0) >= 0) // retrieve child status
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
851 if (childStatus != 0) {
852 assert(interimOutputBuffer != NULL);
853 free((char *)interimOutputBuffer); // toss any output
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
862 assert(interimOutputLength == (interimBp-interimOutputBuffer));
863 if (interimOutputLength < interimOutputLimit)
864 interimOutputBuffer = (unsigned char *)realloc(interimOutputBuffer, (size_t)(interimOutputLength+1));
865 assert(interimOutputBuffer != NULL);
867 outputLen = (int)interimOutputLength; // stuff output length in caller's variable
868 *outputBp = (char *)interimOutputBuffer; // stuff -> clear text in caller's variable
872 // SunV3::uudecode -- decode uuencoded data stream into clear text data stream
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
879 // outputBp -- contain -> allocated storage containing clear text results
880 // outputLen -- number of bytes of clear text results contained at outputBp
882 // == 0 -- successful
883 // != 0 -- not successful
885 // Decode the input data stream using the uudecode algorithm, returning
886 // the clear text results of the operation.
890 SunV3::uudecode(char ** outputBp, int & outputLen, const char * inputBp, const unsigned long inputLen)
892 // We are not really interested in the "begin <mode> <name>" line,
893 // so let's blow by it.
895 const char * line_1 = inputBp;
897 if (strncmp(inputBp, "begin ", 6) == 0) {
898 for (line_1 = inputBp; line_1 < (inputBp + inputLen); line_1++) {
899 if (*line_1 == '\n') {
906 if (line_1 >= (inputBp + inputLen)) {
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.
918 const int totalOutputLen = (int)((inputLen/4)*3);
919 *outputBp = (char *)malloc(totalOutputLen);
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.
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.
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@"
936 // "Janice Anthes, SE janice.anthes@west.sun.COM"
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)
943 for (const unsigned char * encodedBp = (const unsigned char *)line_1;
944 (const char *)encodedBp < (inputBp + inputLen);) {
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
955 int decodedLen = decode_uue_char(*encodedBp); // # of DECODED bytes
957 if (decodedLen <= 0 || decodedLen > 45) {
958 // End of the buffer.
962 encodedBp += 1; // bop past the # decoded bytes character
963 encodedBp = decode_uue_line(*outputBp,
967 (const unsigned char *)(inputBp + inputLen));
968 assert(outputLen <= totalOutputLen);
971 // Done decoding the entire source - reduce size of allocated buffer
972 // so that unused space is returned to the free pool
975 if (outputLen < totalOutputLen)
976 *outputBp = (char *)realloc(*outputBp, outputLen+1);
978 return(0); // success
982 encode_uue_char(const int val)
984 return((val & 077) + ' ');
988 SunV3::encode_uue_line(Buffer & buf,
989 const unsigned char * unencodedBp,
990 const unsigned long unencodedLen)
992 unsigned long triplets = unencodedLen - (unencodedLen % 3);
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);
1007 SunV3::uuencode(Buffer & buf,
1010 const unsigned long len)
1012 const unsigned char * ubp = (const unsigned char *) bp;
1013 const unsigned char * cur;
1015 // Write out the initial information.
1017 buf.appendData("begin 600 ", 10);
1018 buf.appendData(path, strlen(path));
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.
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);
1031 DtMailProcessClientEvents();
1035 // Write the partial line.
1037 int left_over = (int)(len % 45);
1038 char size_char = encode_uue_char(left_over);
1039 buf.appendData(&size_char, 1);
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.
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));
1051 encode_uue_line(buf, safe_buf, left_over);
1054 buf.appendData("end", 3);
1059 SunV3::getV3Type(DtMail::BodyPart * bp, char * v3type)
1061 // Get the Dt type name from the body part.
1066 bp->getContents(error,
1074 // Look it up in the data typing system. Hopefully we will
1075 // get a db based mime name.
1077 char * db_type = DtDtsDataTypeToAttributeValue(type,
1081 // See if we call this text. If so, then it will be text/plain,
1082 // if not then application/octet-stream
1084 char * text_type = DtDtsDataTypeToAttributeValue(type,
1089 strcpy(v3type, db_type);
1092 if (text_type && strcasecmp(text_type, "true") == 0) {
1093 strcpy(v3type, "text");
1096 strcpy(v3type, "default");