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: V3BodyPart.C /main/8 1998/07/23 18:04:38 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 Sun Microsystems, Inc. All rights reserved.
43 #include <EUSCompat.h>
51 #include <DtMail/DtMail.hh>
54 #include <DtMail/Threads.hh>
55 #include "str_utils.h"
61 #include <DtHelp/LocaleXlate.h>
63 V3BodyPart::V3BodyPart(DtMailEnv & error,
64 DtMail::Message * parent,
67 RFCEnvelope * body_env)
68 : RFCBodyPart(error, parent, start, len, body_env)
70 // This constructor is used for single body parts. This
71 // includes generic RFC822 messages, i.e. no Mime-Version
74 // There really isn't anything we can do. We may have to
75 // apply a content encoding, but let's wait until the user
76 // expresses an interest.
83 // V3BodyPart:V3BodyPart -- retrieve next V3 message body part
85 // DtMailEnv & error --
86 // DtMail::Message * parent --
87 // const char * start -- -> beginning of boundary separator
88 // const char ** end -- -> end of message
90 // *end -- is set to the first byte past the end of the retrieved
91 // part. This either points to the next body part (if any) or
92 // is 1 byte past the end of the message if no further parts.
93 // _body_env -- RFCEnvelope defining entire body part
94 // _body_len -- true length of contained body part (minus headers)
95 // _body_text -- first byte of contained body part (minus headers)
97 // This constructor is used for multi-part body parts. Given a
98 // pointer to the beginning of the boundary separator and a
99 // pointer to the end of the message, reduce pointers to actual
100 // body part data (minus headers) and update class variables
104 V3BodyPart::V3BodyPart(DtMailEnv & error,
105 DtMail::Message * parent,
108 : RFCBodyPart(error, parent, start, 0, NULL)
110 // Get past the boundary separator.
112 const char * body_end;
113 for (body_end = start; body_end <= *end && *body_end != '\n'; body_end++) {
117 if (body_end > *end) {
118 // no data after separator?? Bogus body part!
124 // The body part headers begin here. We will look from here until
125 // the first blank line for the end of the headers.
127 body_end += 1; // Chew the newline.
128 const char * env_start = body_end;
129 for (; body_end <= *end; body_end++) {
130 if (*body_end == '\n') {
132 for (const char * blank = body_end + 1;
133 blank <= *end && *blank != '\n'; blank++) {
134 if (!isspace((unsigned char)*blank)) {
145 _my_env = DTM_TRUE; // indicate that we created this envelope
146 _body_env = new RFCEnvelope(error, parent, env_start, body_end - env_start + 1);
148 // Now we need to find the start of the body (the real start, not
149 // where the boundary begins. This is immediately after the newline
150 // that is placed after the end of the headers.
152 _body_text = body_end + 1;
153 for (; _body_text <= *end && *_body_text != '\n'; _body_text++) {
158 // We now need to figure out where the end of the attachment is.
159 // We have 2 choices. Content lines, or content length.
161 DtMailValueSeq value;
163 _body_env->getHeader(error, "x-sun-content-length",
165 if (error.isNotSet()) {
166 // Great! content-length is much easier.
167 // We do this by computing a -> where the end of the attachment
168 // is supposed to be as indicated by the specified content length.
169 // If we come to the end of the message altogether, or to the start
170 // of another header body, then we know we have bounded this body part.
172 int content_length = (int) strtol(*(value[0]), NULL, 10);
173 body_end = _body_text + content_length; // "supposed" end of body part
175 for (int i = 0; i < 2; i++) {
176 if (body_end == *end) { // at end of message??
177 // Yes, this is the last header body in this message
178 // We are at the end -- force this body part to end at the
179 // end of the message and use length as computed.
180 // Mark end of this body for caller as 1 byte past end of last body header
181 // to prevent from being called again.
183 _body_len = content_length;
188 if (body_end > *end) { // past end of message??
189 // Yes, this is the last header body in this message
190 // We are past the end -- force this body part to end at the
191 // end of the message and compute length based upon that assumption.
192 // Mark end of this body for caller as 1 byte past end of last body header
193 // to prevent from being called again.
196 _body_len = *end - _body_text;
200 // V3BodyParts have a "----------" (10 dashes) at the beginning of a
201 // line with no white space before them and a new line at the end to
202 // signal the beginning of an attachment.
204 if ((*(body_end-1) == '\n') &&
205 strncmp(body_end, "----------", 10) == 0 &&
206 isTerm(body_end + 10)) { // start of another body header?
207 // Yes, this is one of many header bodies in this message
208 // Mark end of this body for caller as first byte of next body header
210 assert(body_end < *end); // cant be last
211 _body_len = body_end - _body_text;
212 *end = body_end+1; // current end
216 // Chew through white space and test again.
218 while (isspace((unsigned char)*body_end)) {
227 // We didn't have content-length, or it was wrong or bogus.
228 // Try for content lines.
230 value.clear(); // clear out any previous header(s) retrieved
232 _body_env->getHeader(error, "x-sun-content-lines",
234 if (error.isNotSet()) {
235 // content-lines requires a bit o' muching first.
236 // We do this by marching through the body counting new lines
237 // until the specified number of lines have been passed. This
238 // is the determined end of the body part.
240 int content_lines = (int) strtol(*(value[0]), NULL, 10);
242 for (body_end = _body_text;
243 body_end <= *end && lines < content_lines; body_end++) {
244 if (*body_end == '\n') {
249 if (body_end >= *end) { // at end of message??
250 // Yes, this is the last header body in this message
251 // We are at or past the end -- force this body part to end at the
252 // end of the message and compute length based upon that assumption.
253 // Mark end of this body for caller as 1 byte past end of last body header
254 // to prevent from being called again.
257 _body_len = *end - _body_text;
261 // V3BodyParts have a "----------" (10 dashes) at the beginning of a
262 // line with no white space before them and a new line at the end to
263 // signal the beginning of an attachment.
265 if ((*(body_end-1) == '\n') &&
266 strncmp(body_end, "----------", 10) == 0 &&
267 isTerm(body_end + 10) == DTM_TRUE) { // start of another body header?
268 // Yes, this is one of many header bodies in this message
269 // Mark end of this body for caller as first byte of next body header
271 assert(body_end < *end); // cant be last
272 _body_len = body_end - _body_text;
273 *end = body_end+1; // current end
281 // We either didn't have a length, or number of lines, or they
282 // didn't work. We will now apply a dangerous but last resort
283 // effort to find the end of the body by looking for the 10 dash
285 // V3BodyParts have a "----------" (10 dashes) at the beginning of a
286 // line with no white space before them and a new line at the end to
287 // signal the beginning of an attachment.
290 for (body_end = _body_text; body_end <= (*end - 12); body_end++) {
291 if (*body_end == '\n' &&
292 strncmp(body_end + 1, "----------", 10) == 0 &&
293 isTerm(body_end + 11) == DTM_TRUE) {
294 // Dangerously by successfully located what appears to be the end
295 // of this attachment.
298 _body_len = body_end - _body_text;
303 // At this point we are at the end of the message and are without
304 // the benefit of content lines or content length or the discovery
305 // of what appears to be a message separator. Assume this body part
306 // consumes the remainder of the message. Mark end of this body for
307 // caller as 1 byte past end to prevent from being called again.
310 _body_len = *end - _body_text;
314 V3BodyPart::~V3BodyPart(void)
320 V3BodyPart::checksum(DtMailEnv & error)
324 return(DtMailCheckUnknown);
326 #endif /* DEAD_WOOD */
329 countTypes(char ** types)
333 if (NULL == types) return 0;
335 for (count = 0; *types; types++, count++) {
343 V3BodyPart::getContentType(DtMailEnv &error, char **v3_type)
345 MutexLock lock_scope(_obj_mutex);
346 MutexLock dt_lib_lock(_DtMutex);
347 DtMailValueSeq value;
350 *v3_type = (char *)0;
352 _body_env->getHeader(error, "Content-Type", DTM_FALSE, value);
353 if (error.isNotSet()) {
354 *v3_type = strdup(*(value[0]));
358 _body_env->getHeader(error, "X-Sun-Data-Type", DTM_FALSE, value);
359 if (error.isNotSet()) {
360 *v3_type = strdup(*(value[0]));
363 *v3_type = strdup("text");
370 V3BodyPart::getDtType(DtMailEnv & error)
372 MutexLock lock_scope(_obj_mutex);
373 MutexLock dt_lib_lock(_DtMutex);
375 // No need to clear the error object here because it should have
376 // been cleared by the object that instantiated it.
380 DtMailValueSeq value;
381 _body_env->getHeader(error, "Content-Type", DTM_FALSE, value);
382 if (error.isNotSet()) {
383 v3_type = strdup(*(value[0]));
388 _body_env->getHeader(error, "X-Sun-Data-Type", DTM_FALSE,
390 if (error.isNotSet()) {
391 v3_type = strdup(*(value[0]));
395 v3_type = strdup("text");
399 char ** types = DtDtsFindAttribute("SUNV3_TYPE", v3_type);
403 if (countTypes(types) == 1) {
404 // We will use the first name. It may be wrong, but
405 // it is the best we can do at this point.
407 _body_type = strdup(types[0]);
408 DtDtsFreeDataTypeNames(types);
412 DtDtsFreeDataTypeNames(types);
415 // We need the bits so we can type the buffer and get
416 // a type for the object. This is where things can get
417 // very slow for the user.
424 char * name = getName(error);
426 // don't care about error conditions returned by getName().
430 // If the name is "Attachment" and the type is "text", then
431 // use the type as the name to avoid the buffer being called
434 if (name && strcasecmp(name, "attachment") == 0 &&
435 strcasecmp(v3_type, "text") == 0) {
437 name = strdup(v3_type);
440 char * type = DtDtsBufferToDataType(_body, _body_decoded_len, name);
441 _body_type = strdup(type);
442 DtDtsFreeDataType(type);
450 V3BodyPart::loadBody(DtMailEnv &)
453 char *to_cs = NULL, *from_cs = NULL;
454 char *cs = new char[64];
456 // End of For CHARSET
464 // See if we are using an encoding.
465 DtMailValueSeq value;
467 _body_env->getHeader(lerror, "X-Sun-Encoding-Info",
469 if (lerror.isSet() || (_body_len == 0)) {
472 _body = (char *)_body_text;
473 _must_free_body = DTM_FALSE;
474 _body_decoded_len = _body_len;
475 // Before the codeset conversion code was put in, return here.
480 _body_decoded_len = 0;
481 _must_free_body = DTM_TRUE;
483 SunV3::decode(*(value[0]), &_body, _body_decoded_len,
484 _body_text, _body_len);
488 // If Content-Type is text, then get charset and do conversion.
489 // If Content-Type is X-sun-attachment, then if X-Sun-Data-Type is text,
490 // then get charset and do conversion.
491 // If Content-Type is missing, check if X-Sun-Data-Type exists, and
492 // if X-Sun-Data-Type exists and is text, then get charset and do conversion.
493 // If Content-Type and X-Sun-Data-Type are missing, then assume text so get
494 // charset and do conversion.
495 // Else don't need codeset conversion.
496 const char *cstmp = NULL;
497 const char *ct = NULL;
499 _body_env->getHeader(lerror, "Content-Type", DTM_FALSE, value);
500 if (lerror.isNotSet()) {
501 // Sun's V.3 Mail File Format requires a Content-Type header field to be
502 // "X-sun-attachment" for attachment message. X-Sun-Data-Type header
503 // field is also required for attachment message.
505 if ( strcasecmp(ct, "X-sun-attachment") == 0 ) {
507 _body_env->getHeader(lerror, "X-Sun-Data-Type", DTM_FALSE, value);
508 if (lerror.isNotSet()) {
510 if ( strcasecmp(ct, "text") == 0 ) {
511 // Get charset from X-Sun-Text-Type which contains the name of
512 // the codeset for the text-based body part. It is a mandatory
513 // header iff X-Sun-Data-Type is of type text.
515 _body_env->getHeader(lerror, "X-Sun-Text-Type", DTM_FALSE, value);
516 if (lerror.isNotSet()) {
520 // We are not returning yet. We'll be flexible here.
521 // Some mailers may not set this field. We'll try to obtain charset
522 // from X-Sun-Charset or use the locale default. See below.
525 } else { // Attachment not text type
530 // Required field for attachment message not set -- return!
534 } else if ( strcasecmp(ct, "text") != 0 ) {
538 } else { // Content-Type does not exist!
541 _body_env->getHeader(lerror, "X-Sun-Data-Type", DTM_FALSE, value);
542 if (lerror.isNotSet()) {
544 if ( strcasecmp(ct, "text") == 0 ) {
545 // Get charset from X-Sun-Text-Type which contains the name of
546 // the codeset for the text-based body part. It is a mandatory
547 // header iff X-Sun-Data-Type is of type text.
549 _body_env->getHeader(lerror, "X-Sun-Text-Type", DTM_FALSE, value);
550 if (lerror.isNotSet()) {
554 // We are not returning yet. We'll be flexible here.
555 // Some mailers may not set this field. We'll try to obtain charset
556 // from X-Sun-Charset or use the locale default. See below.
559 } else { // Attachment not text type
564 // Base on Sun's V.3 Mail File Format version 1.9: If no Content-Type,
565 // and X-Sun-Data-Type is not set (means no/not attachment,
566 // then assume text. Proceed with getting charset.
572 // Get charset from charset field
573 _body_env->getHeader(lerror, "X-Sun-Charset", DTM_FALSE, value);
574 if (lerror.isNotSet()) {
577 } else { // No Charset
578 // We'll be flexible here. If Content-Type is missing or if Content-Type
579 // is text, then we get charset but if charset is missing, we'll try to
580 // convert from the locale specific default codeset.
583 strcpy(cs, "DEFAULT");
584 DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
585 setlocale(LC_CTYPE, NULL),
589 strcpy(cs, "DEFAULT");
594 } // If cstmp is NULL
596 // Handle ISO-2022-INT, RFC approved, or private encoding names
597 if ( strcasecmp(cs, "ISO-2022-INT-1") == 0 ) {
598 // Need to obtain charset from encoding
599 } // RFC approved and private names are not treated differently.
601 // Get iconv name from charset - this is the "from" name.
603 from_cs = csToConvName(cs);
605 // Get current locale's iconv name - this is the "to" name.
607 to_cs = locToConvName();
609 if ( from_cs && to_cs ) {
610 if ( strcasecmp(from_cs, to_cs) != 0 ) {
611 unsigned long tmp_len = (unsigned long) _body_decoded_len;
612 if (csConvert(&_body, tmp_len, (int)_must_free_body, from_cs, to_cs)) {
613 _must_free_body = DTM_TRUE;
614 _body_decoded_len = (int) tmp_len;
623 // End of For CHARSET
628 V3BodyPart::getDescription(DtMailEnv & error)
630 // No need to clear the error object here, whoever created it, cleared it.
633 DtMailValueSeq value;
634 _body_env->getHeader(error, "X-Sun-Data-Description",
636 if (error.isNotSet()) {
637 return(strdup(*(value[0])));
639 error.clear(); // clear error so it doesn't propogate back up.
644 V3BodyPart::getName(DtMailEnv & error)
646 // No need to clear the error object here, it should be passed in already
650 DtMailValueSeq value;
651 _body_env->getHeader(error, "X-Sun-Data-Name",
653 if (error.isNotSet()) {
654 return(strdup(*(value[0])));
657 // Since we are returning a valid name in spite of the call to
658 // getHeader returning an error, we should clear the error.
661 return(strdup("Attachment"));
665 V3BodyPart::setName(DtMailEnv & error, const char * name)
667 _body_env->setHeader(error, "X-Sun-Data-Name", DTM_TRUE, name);
671 V3BodyPart::getLength(DtMailEnv & error)
673 MutexLock lock_scope(_obj_mutex);
677 // propogate the error back to the caller
681 return(_body_decoded_len);
685 V3BodyPart::rfcSize(const char *, DtMailBoolean &)
691 V3BodyPart::writeBodyParts(char *buf)
696 // Do not need to implement this method because getHeader already
697 // returns the X-Sun-Charset value.
699 V3BodyPart::csFromContentType(DtMailValueSeq&)