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
27 * $TOG: RFCBodyPart.C /main/16 1998/04/06 13:27:40 mgreess $
29 * RESTRICTED CONFIDENTIAL INFORMATION:
31 * The information in this document is subject to special
32 * restrictions in a confidential disclosure agreement bertween
33 * HP, IBM, Sun, USL, SCO and Univel. Do not distribute this
34 * document outside HP, IBM, Sun, USL, SCO, or Univel wihtout
35 * Sun's specific written approval. This documment and all copies
36 * and derivative works thereof must be returned or destroyed at
39 * Copyright 1993 Sun Microsystems, Inc. All rights reserved.
44 #ifndef I_HAVE_NO_IDENT
53 #include <DtMail/DtMail.hh>
55 #include <DtMail/Threads.hh>
58 //-------------------------------------
60 // Any code change within "For CHARSET" should be changed in
61 // RFCBodyPart and Session because the same methods are duplicated
62 // in both of these classes.
63 // See RFCImpl.hh or DtMail/DtMail.hh for more explanation.
64 //-------------------------------------
67 #include <DtHelp/LocaleXlate.h>
80 #if defined(SunOS) && (SunOS < 55)
84 #if !defined(__linux__)
85 // Iconv not defined for linux. Use the EUSCompat stubs instead. */
88 # include <EUSCompat.h>
92 #if defined(SunOS) && (SunOS < 55)
98 RFCBodyPart::RFCBodyPart(DtMailEnv & error,
99 DtMail::Message * parent,
102 RFCEnvelope * body_env)
103 : DtMail::BodyPart(error, parent)
105 _body_lock = MutexInit();
107 _body_start = _body_text = start;
108 _body_len = len > 0 ? len : 0;
110 // If we have no body to start, then we will want to create
111 // a body envelope. We do this to make sure we can always
112 // set headers for the body part.
114 if (_body_start == NULL) {
115 _body_env = new RFCEnvelope(error, parent, NULL, 0);
119 _body_env = body_env;
120 _my_env = _body_env ? DTM_FALSE : DTM_TRUE;
124 _body_decoded_len = 0;
126 _must_free_body = DTM_FALSE;
129 RFCBodyPart::~RFCBodyPart(void)
131 if (_my_env == DTM_TRUE) {
135 if (_must_free_body) {
145 RFCBodyPart::getContents(DtMailEnv & error,
146 const void ** contents,
147 unsigned long * length,
153 // First things first, for each possible return, zero out the
154 // pointer so that if the caller does not check for errors it
155 // will be caught by a null dereference
160 *description = (char *)0;
164 *contents = (void *)0;
166 // The caller can ask for a hodge podge of information.
167 // The only real rule here is that you can not ask for
168 // the contents without requesting the length.
170 if (contents && !length) {
171 error.setError(DTME_OperationInvalid);
175 // No need to clear the error here because it should be clear already.
178 // MIME currently doesn't have body part names and RFC
179 // doesn't let us at arbitrary message headers. Name
180 // therefore can not be retrieved.
183 *name = getName(error);
185 // don't care about the error condition returned by getName()
186 // because it returns a valid string no matter what error
199 // getDescription should not be passed a DtMailEnv object.
200 // Neither implementation of getDescription (MIME or V3)
201 // sets the error before returning. Besides being unnecessary,
202 // it requires the caller of getDescription to check the error
203 // status upon its return.
205 *description = getDescription(error);
207 // Don't care about the error condition returned by getDescription
208 // because it returns a valid string no matter what error
214 // Types for DtMail are in the Dt name space. RFC returns
215 // mime types. We will have to convert from the RFC name
216 // space to the Dt name space.
221 // Do we want to propogate this error back up to the
222 // function that called us? If not, we need to call
223 // error.clear() before returning.
229 *type = strdup(_body_type);
233 *length = getLength(error);
235 // propogate the error back up to the caller
241 *contents = getBody(error);
243 // propogate the error back up to the caller
250 RFCBodyPart::setContents(DtMailEnv & error,
251 const void * contents,
252 const unsigned long length,
256 const char *) // description)
260 MutexLock lock_scope(_body_lock);
263 setName(error, name);
266 if (contents && !length) {
267 error.setError(DTME_OperationInvalid);
270 if (!contents && !length) {
271 if (_body && _must_free_body) {
274 _must_free_body = DTM_FALSE;
276 _body_decoded_len = _body_len = 0;
281 // _must_free_body = DTM_FALSE;
286 if (_body && _must_free_body) {
291 _body = (char *)malloc((int)length);
292 memcpy(_body, contents, (int)length);
293 _body_decoded_len = _body_len = (int)length;
294 _must_free_body = DTM_TRUE;
296 // Reset the type. We don't know what it is!
306 _body_type = strdup(type);
311 RFCBodyPart::lockContents(DtMailEnv & error, const DtMailLock)
317 RFCBodyPart::unlockContents(DtMailEnv & error)
323 RFCBodyPart::getHeader(DtMailEnv & error,
325 const DtMailBoolean abstract,
326 DtMailValueSeq & value)
330 // If this is not our envelope, then we will not set the flag.
332 if (_my_env == DTM_FALSE) {
333 error.setError(DTME_NotSupported);
337 if (_body_env == (RFCEnvelope *)NULL) {
338 error.setError(DTME_NoObjectValue);
342 _body_env->getHeader(error, name, abstract, value);
346 RFCBodyPart::setFlag(DtMailEnv & error,
347 DtMailBodyPartState state)
351 // If this is not our envelope, then we will not set the flag.
353 if (_my_env == DTM_FALSE) {
354 error.setError(DTME_NotSupported);
363 case DtMailBodyPartDeletePending:
365 sprintf(str_time, "%08lX", (long)now);
366 _body_env->setHeader(my_error, RFCDeleteHeader, DTM_TRUE, str_time);
370 error.setError(DTME_OperationInvalid);
375 RFCBodyPart::resetFlag(DtMailEnv & error,
376 DtMailBodyPartState state)
380 // If this is not our envelope, then we will not set the flag.
382 if (_my_env == DTM_FALSE) {
383 error.setError(DTME_NotSupported);
390 case DtMailBodyPartDeletePending:
391 _body_env->removeHeader(my_error, RFCDeleteHeader);
395 error.setError(DTME_OperationInvalid);
400 RFCBodyPart::flagIsSet(DtMailEnv & error,
401 DtMailBodyPartState state)
405 // If this is not our envelope, then we will not set the flag.
407 if (_my_env == DTM_FALSE) {
408 error.setError(DTME_NotSupported);
413 DtMailValueSeq value;
414 DtMailBoolean answer;
417 case DtMailBodyPartDeletePending:
418 _body_env->getHeader(my_error, RFCDeleteHeader, DTM_FALSE, value);
419 if (my_error.isNotSet()) {
428 error.setError(DTME_OperationInvalid);
435 RFCBodyPart::getDeleteTime(DtMailEnv & error)
437 time_t delete_time = 0;
439 DtMailValueSeq value;
440 _body_env->getHeader(error, RFCDeleteHeader, DTM_FALSE, value);
441 if (error.isNotSet()) {
442 delete_time = (time_t) strtol(*(value[0]), NULL, 16);
452 RFCBodyPart::adjustBodyPartsLocation(char * start)
454 MutexLock lock_scope(_body_lock);
456 _body_text = (_body_text - _body_start) + start;
459 if (_must_free_body == DTM_FALSE) {
460 //_body = (char *)_body_text;
464 if (_body_env && _my_env == DTM_TRUE) {
466 // start points at the body part separator. Need to
467 // Skip separator. Put in a sanity check until we know
468 // this is the right fix
469 if (*start != '-' && *(start + 1) != '-') {
472 "RFCBodyPart::adjustBodyPartLocation(%.20s): Not a separator\n",
475 while (*start != '\n')
479 // End Of fix for 2807
480 _body_env->adjustHeaderLocation(start, (int)(_body_text-_body_start));
485 RFCBodyPart::isTerm(const char * start)
487 if (*start == '\n' || (*start == '\r' && *(start + 1) == '\n')) {
496 RFCBodyPart::getBody(DtMailEnv & error)
511 * Wrapper functions taken from libHelp/CEUtil.c
513 * We took these functions and renamed them because
514 * 1. Originally these are called _DtHelpCeXlate* and thus they are private
515 * to libHelp and not exported to outside of libHelp.
516 * 2. When these functions are moved to another library, then users of these
517 * functions would only need to link with a different library. The caller
518 * doesn't have to modify code.
521 static const char *DfltStdCharset = "us-ascii";
522 static const char *DfltStdLang = "C";
524 static char MyPlatform[_DtPLATFORM_MAX_LEN+1];
525 static _DtXlateDb MyDb = NULL;
526 static char MyProcess = False;
527 static char MyFirst = True;
532 /******************************************************************************
533 * Function: static int OpenLcxDb ()
537 * Return Value: 0: ok
542 * Purpose: Opens the Ce-private Lcx database
544 *****************************************************************************/
546 RFCBodyPart::OpenLcxDb (void)
551 while (MyProcess == True)
553 /* if time out, return */
554 if (time(&time2) == (time_t)-1)
559 else if (time2 - time1 >= (time_t)30)
566 if (_DtLcxOpenAllDbs(&MyDb) == 0 &&
567 _DtXlateGetXlateEnv(MyDb,MyPlatform,&ExecVer,&CompVer) != 0)
569 _DtLcxCloseDb(&MyDb);
576 return (MyDb == NULL ? -1 : 0 );
579 /******************************************************************************
580 * Function: int DtXlateOpToStdLocale(char *operation, char *opLocale,
581 * char **ret_stdLocale,
582 * char **ret_stdLang, char **ret_stdSet)
585 * operation Operation associated with the locale value
586 * opLocale An operation-specific locale string
587 * ret_locale Returns the std locale
588 * Caller must free this string.
589 * ret_stdLang Returns the std language & territory string.
590 * Caller must free this string.
591 * ret_stdSet Returns the std code set string.
592 * Caller must free this string.
596 * Purpose: Gets the standard locale given an operation and its locale
598 *****************************************************************************/
600 RFCBodyPart::DtXlateOpToStdLocale (
603 char **ret_stdLocale,
607 int result = OpenLcxDb();
610 (void) _DtLcxXlateOpToStd(
611 MyDb, MyPlatform, CompVer,
613 ret_stdLocale, ret_stdLang, ret_stdSet, NULL);
616 /* if failed, give default values */
617 if (ret_stdLocale != NULL && (result != 0 || *ret_stdLocale == NULL))
620 (char *)malloc(strlen(DfltStdLang)+strlen(DfltStdCharset)+3);
621 sprintf(*ret_stdLocale,"%s.%s",DfltStdLang,DfltStdCharset);
624 if (ret_stdLang != NULL && (result != 0 || *ret_stdLang == NULL))
625 *ret_stdLang = (char *)strdup(DfltStdLang);
626 if (ret_stdSet != NULL && (result != 0 || *ret_stdSet == NULL))
627 *ret_stdSet = (char *)strdup(DfltStdCharset);
630 /******************************************************************************
631 * Function: int DtXlateStdToOpLocale ( char *operation, char *stdLocale,
632 * char *dflt_opLocale, char **ret_opLocale)
635 * operation operation whose locale value will be retrieved
636 * stdLocale standard locale value
637 * dflt_opLocale operation-specific locale-value
638 * This is the default value used in error case
639 * ret_opLocale operation-specific locale-value placed here
640 * Caller must free this string.
644 * Purpose: Gets an operation-specific locale string given the standard string
646 *****************************************************************************/
648 RFCBodyPart::DtXlateStdToOpLocale (
654 int result = this->OpenLcxDb();
657 *ret_opLocale = NULL;
661 (void) _DtLcxXlateStdToOp(
662 MyDb, MyPlatform, CompVer,
664 stdLocale, NULL, NULL, NULL,
668 /* if translation fails, use a default value */
669 if (ret_opLocale && (result != 0 || *ret_opLocale == NULL))
671 if (dflt_opLocale) *ret_opLocale = (char *)strdup(dflt_opLocale);
672 else if (stdLocale) *ret_opLocale = (char *)strdup(stdLocale);
677 /******************************************************************************
678 * Function: int DtXlateStdToOpCodeset (
681 * char *dflt_opCodeset,
682 * char **ret_opCodeset)
685 * operation operation whose codeset value will be retrieved
686 * stdCodeset standard codeset value
687 * dflt_opCodeset operation-specific codeset-value
688 * This is the default value used in error case
689 * ret_opCodeset operation-specific codeset-value placed here
690 * Caller must free this string.
694 * Purpose: Gets an operation-specific locale string given the standard string
696 *****************************************************************************/
698 RFCBodyPart::DtXlateStdToOpCodeset (
701 char *dflt_opCodeset,
702 char **ret_opCodeset)
704 int result = this->OpenLcxDb();
707 *ret_opCodeset = NULL;
711 (void) _DtLcxXlateStdToOp(
712 MyDb, MyPlatform, CompVer,
714 NULL, NULL, stdCodeset, NULL,
718 /* if translation fails, use a default value */
719 if (ret_opCodeset && (result != 0 || *ret_opCodeset == NULL))
721 if (dflt_opCodeset) *ret_opCodeset = (char *)strdup(dflt_opCodeset);
722 else if (stdCodeset) *ret_opCodeset = (char *)strdup(stdCodeset);
727 RFCBodyPart::DtXlateMimeToIconv(
729 const char *defaultCommonCS,
730 const char *defaultIconvCS,
732 char **ret_platformIconv)
738 exists = _DtLcxXlateOpToStd(
739 MyDb, MyPlatform, CompVer,
740 DtLCX_OPER_MIME, mimeId,
741 NULL, NULL, ret_commonCS, NULL);
745 exists = _DtLcxXlateOpToStd(
747 DtLCX_OPER_MIME, mimeId,
748 NULL, NULL, ret_commonCS, NULL);
750 *ret_commonCS = (char *)strdup(defaultCommonCS);
753 exists = _DtLcxXlateStdToOp(
754 MyDb, MyPlatform, CompVer,
756 NULL, NULL, *ret_commonCS, NULL,
759 *ret_platformIconv = (char *)strdup(defaultIconvCS);
763 RFCBodyPart::DtXlateLocaleToMime(
765 const char * defaultCommonCS,
766 const char * defaultMimeCS,
769 char * commonCS = NULL;
773 /* look for platform-specific locale to CDE translation */
775 MyDb, MyPlatform, CompVer,
776 DtLCX_OPER_SETLOCALE, locale,
777 NULL, NULL, &commonCS, NULL);
779 commonCS = (char *)strdup(defaultCommonCS);
781 /* look for platform-specific MIME types; by default, there is none */
783 MyDb, MyPlatform, CompVer,
785 NULL, NULL, commonCS, NULL,
792 NULL, NULL, commonCS, NULL,
795 *ret_mimeCS = (char *)strdup(defaultMimeCS);
802 // Return iconv name of the given codeset.
803 // If iconv name does not exist, return NULL.
805 RFCBodyPart::csToConvName(char *cs)
808 char *commonCS = NULL;
809 char *convName = NULL;
810 char *ret_target = NULL;
814 // Convert charset to upper case first because charset table is
818 int len_cs = strlen(cs);
819 for (int num_cs = 0; num_cs < len_cs; num_cs++)
820 *(cs+num_cs) = toupper(*(cs+num_cs));
822 exists = _DtLcxXlateOpToStd(
823 MyDb, MyPlatform, CompVer,
825 NULL, NULL, &commonCS, NULL);
827 exists = _DtLcxXlateOpToStd(
830 NULL, NULL, &commonCS, NULL);
835 DtXlateStdToOpCodeset(DtLCX_OPER_INTERCHANGE_CODESET,
839 DtXlateStdToOpCodeset(DtLCX_OPER_ICONV3,
849 // Workaround for libDtHelp
850 // Case of no iconv name for a particular locale, eg. C,
851 // check for empty string.
852 if ( convName != NULL )
854 if ( strlen(convName) > 0 )
862 // Return current locale's iconv name.
864 RFCBodyPart::locToConvName()
866 char *ret_locale = NULL;
867 char *ret_lang = NULL;
868 char *ret_codeset = NULL;
870 DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
871 setlocale(LC_CTYPE, NULL),
886 DtXlateStdToOpLocale(DtLCX_OPER_ICONV3,
894 // Workaround for libDtHelp
895 // Case of no iconv name for a particular locale, eg. C,
896 // check for empty string.
897 if ( ret_codeset != NULL ) {
898 if (strlen(ret_codeset) > 0)
906 // Return target codeset's iconv name.
908 RFCBodyPart::targetConvName()
910 char *ret_locale = NULL;
911 char *ret_lang = NULL;
912 char *ret_codeset = NULL;
913 char *ret_target = NULL;
914 char *ret_convName = NULL;
916 DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
917 setlocale(LC_CTYPE, NULL),
921 DtXlateStdToOpLocale(DtLCX_OPER_INTERCHANGE_CODESET,
925 // Or do I call csToConvName() here??
926 DtXlateStdToOpCodeset(DtLCX_OPER_ICONV3,
941 // Workaround for libDtHelp
942 // Case of no iconv name for a particular locale, eg. C,
943 // check for empty string.
944 if ( ret_convName != NULL )
946 if (strlen(ret_convName) > 0)
954 // Return target codeset's MIME (tag) name.
956 RFCBodyPart::targetTagName()
958 char *ret_locale = NULL;
959 char *ret_lang = NULL;
960 char *ret_codeset = NULL;
961 char *ret_target = NULL;
963 DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
964 setlocale(LC_CTYPE, NULL),
968 DtXlateStdToOpLocale(DtLCX_OPER_INTERCHANGE_CODESET,
972 DtXlateStdToOpCodeset(DtLCX_OPER_MIME,
987 // Given a message text and codesets
988 // Convert message text from one codeset to another
989 // Return 1 if conversion is successful else return 0.
991 RFCBodyPart::csConvert(char **bp, unsigned long &bp_len, int free_bp,
992 char *from_cs, char *to_cs)
996 size_t ileft = (size_t) bp_len, oleft = (size_t) bp_len, ret = 0;
997 #if defined(_aix) || defined(sun) || defined(CSRG_BASED)
998 const char *ip = (const char *) *bp;
1003 char *op_start = NULL;
1007 if ( *bp == NULL || **bp == '\0' || bp_len <= 0 )
1009 if ( to_cs == NULL || from_cs == NULL )
1011 if ( (cd = iconv_open(to_cs, from_cs)) == (iconv_t) -1 ) {
1014 error.logError(DTM_FALSE,
1015 "DtMail: Conversion from %s to %s is not supported.\n",
1018 } // end of switch statement
1021 // Caller will set _must_free_body to DTM_TRUE if this routine
1022 // succeeds. Then this space will be freed appropriately.
1023 // Add 1 to buffer size for null terminator.
1024 op_start = op = (char *)calloc((unsigned int) bp_len + 1, sizeof(char));
1026 // When ileft finally reaches 0, the conversion still might not be
1027 // complete. Here's why we also need to check for E2BIG: Let's
1028 // say we're converting from eucJP to ISO-2022-JP, and there's just
1029 // enough room in the output buffer for the last input character,
1030 // but not enough room for the trailing "ESC ( B" (for switching
1031 // back to ASCII). In that case, iconv() will convert the last
1032 // input character, decrement ileft to zero, and then set errno to
1033 // E2BIG to tell us that it still needs more room for the "ESC ( B".
1035 while ( ileft > 0 || errno == E2BIG ) {
1037 ret = iconv(cd, &ip, &ileft, &op, &oleft);
1038 if ( ret == (size_t) -1 ) {
1040 case E2BIG: // increase output buffer size
1041 delta = ileft ? ileft : 3;
1043 op_start = (char *)realloc(
1045 (unsigned int) bp_len + 1);
1046 op = op_start + bp_len - delta - oleft;
1048 // realloc does not clear out unused space.
1049 // Therefore, garbage shows up in output buffer.
1050 memset(op, 0, oleft + 1);
1052 case EILSEQ: // input byte does not belong to input codeset
1053 case EINVAL: // invalid input
1054 mb_ret = mblen(ip, MB_LEN_MAX);
1055 if ( (mb_ret > 0) && (oleft >= mb_ret) ) {
1056 strncat(op_start, ip, mb_ret);
1063 // mb_ret is either 0 or -1 at this point,
1064 // then skip one byte
1065 // and try conversion again.
1070 case EBADF: // bad conversion descriptor
1072 } // end of switch statement
1074 } // end of while loop
1077 // Is this necessary?? Is _body_decode_len == strlen(_body)??
1078 // Or can _body_decode_len contain spaces??
1080 // Check to see if a body had been allocated by prior decoding.
1085 bp_len = strlen(*bp);
1090 // End of For CHARSET