dthelp: Change to ANSI function definitions
[oweals/cde.git] / cde / programs / dtmail / libDtMail / RFC / RFCBodyPart.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  *
27  *      $TOG: RFCBodyPart.C /main/16 1998/04/06 13:27:40 mgreess $
28  *
29  *      RESTRICTED CONFIDENTIAL INFORMATION:
30  *      
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
37  *      Sun's request.
38  *
39  *      Copyright 1993 Sun Microsystems, Inc.  All rights reserved.
40  *
41  *+ENOTICE
42  */
43
44 #ifndef I_HAVE_NO_IDENT
45 #endif
46
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50
51 #include <Dt/Dts.h>
52
53 #include <DtMail/DtMail.hh>
54 #include "RFCImpl.hh"
55 #include <DtMail/Threads.hh>
56
57 // For CHARSET
58 //-------------------------------------
59 // HACK ALERT
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 //-------------------------------------
65 #include <locale.h>
66 #include <time.h>
67 #include <DtHelp/LocaleXlate.h>
68 #include <errno.h>
69 #include <stdlib.h>
70 #include <limits.h>
71 #include <ctype.h>
72  
73 #ifndef True
74 #define True 1
75 #endif
76 #ifndef False
77 #define False 0
78 #endif
79
80 #if defined(SunOS) && (SunOS < 55)
81 extern "C" {
82 #endif
83
84 #if !defined(__linux__)
85    // Iconv not defined for linux.  Use the EUSCompat stubs instead. */
86 #  include <iconv.h>
87 #else
88 #  include <EUSCompat.h>
89 #endif
90
91
92 #if defined(SunOS) && (SunOS < 55)
93 }
94 #endif
95
96 // End of For CHARSET
97
98 RFCBodyPart::RFCBodyPart(DtMailEnv & error,
99                          DtMail::Message * parent,
100                          const char * start,
101                          const int len,
102                          RFCEnvelope * body_env)
103 : DtMail::BodyPart(error, parent)
104 {
105     _body_lock = MutexInit();
106
107     _body_start = _body_text = start;
108     _body_len = len > 0 ? len : 0;
109
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.
113     //
114     if (_body_start == NULL) {
115         _body_env = new RFCEnvelope(error, parent, NULL, 0);
116         _my_env = DTM_TRUE;
117     }
118     else {
119         _body_env = body_env;
120         _my_env = _body_env ? DTM_FALSE : DTM_TRUE;
121     }
122
123     _body = NULL;
124     _body_decoded_len = 0;
125     _body_type = NULL;
126     _must_free_body = DTM_FALSE;
127 }
128
129 RFCBodyPart::~RFCBodyPart(void)
130 {
131     if (_my_env == DTM_TRUE) {
132         delete _body_env;
133     }
134
135     if (_must_free_body) {
136         free(_body);
137     }
138
139     if (_body_type) {
140         free(_body_type);
141     }
142 }
143
144 void
145 RFCBodyPart::getContents(DtMailEnv & error,
146                           const void ** contents,
147                           unsigned long * length,
148                           char ** type,
149                           char ** name,
150                           int * mode,
151                           char ** description)
152 {
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
156   //
157   if (name)
158     *name = (char *)0;
159   if (description)
160     *description = (char *)0;
161   if (type)
162     *type = (char *)0;
163   if (contents)
164     *contents = (void *)0;
165   
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.
169
170   if (contents && !length) {
171         error.setError(DTME_OperationInvalid);
172         return;
173     }
174
175 // No need to clear the error here because it should be clear already.
176 //    error.clear();
177
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.
181     //
182     if (name) {
183         *name = getName(error);
184         if (error.isSet()) {
185             // don't care about the error condition returned by getName()
186             // because it returns a valid string no matter what error
187             // condition occurs.
188             error.clear();
189         }
190     }
191
192     // Ditto for mode.
193     //
194     if (mode) {
195         *mode = 0;
196     }
197
198     if (description) {
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.
204         //
205         *description = getDescription(error);
206         if (error.isSet()) {
207             // Don't care about the error condition returned by getDescription
208             // because it returns a valid string no matter what error
209             // condition occurs.
210             error.clear();
211         }
212     }
213
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.
217     //
218     if (type) {
219         if (!_body_type) {
220             getDtType(error);
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.
224             if (error.isSet()) {
225                 return;
226             }
227         }
228
229         *type = strdup(_body_type);
230     }
231
232     if (length) {
233         *length = getLength(error);
234         if (error.isSet()) {
235             // propogate the error back up to the caller
236             return;
237         }
238     }
239
240     if (contents) {
241         *contents = getBody(error);
242         if (error.isSet()) {
243             // propogate the error back up to the caller
244             return;
245         }
246     }
247 }
248
249 void
250 RFCBodyPart::setContents(DtMailEnv & error,
251                           const void * contents,
252                           const unsigned long length,
253                           const char * type,
254                           const char * name,
255                           const int, // mode,
256                           const char *) // description)
257 {
258     error.clear();
259
260     MutexLock lock_scope(_body_lock);
261
262     if (name) {
263         setName(error, name);
264     }
265
266     if (contents && !length) {
267         error.setError(DTME_OperationInvalid);
268         return;
269     }
270     if (!contents && !length) {
271         if (_body && _must_free_body) {
272                 free(_body);
273                 _body = NULL;
274                 _must_free_body = DTM_FALSE;
275         }
276         _body_decoded_len = _body_len = 0;
277         if (_body_type) {
278                 free(_body_type);
279                 _body_type = NULL;
280         }
281 //      _must_free_body = DTM_FALSE;
282         return;
283     }
284
285     if (contents) {
286         if (_body && _must_free_body) {
287             free(_body);
288             _body = NULL;
289         }
290
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;
295
296         // Reset the type. We don't know what it is!
297         if (_body_type) {
298             free(_body_type);
299             _body_type = NULL;
300         }
301     }
302
303     if (type) {
304         if (_body_type) 
305             free(_body_type);
306         _body_type = strdup(type);
307     }
308 }
309
310 void
311 RFCBodyPart::lockContents(DtMailEnv & error, const DtMailLock)
312 {
313     error.clear();
314 }
315
316 void
317 RFCBodyPart::unlockContents(DtMailEnv & error)
318 {
319     error.clear();
320 }
321
322 void
323 RFCBodyPart::getHeader(DtMailEnv & error,
324                       const char * name,
325                       const DtMailBoolean abstract,
326                       DtMailValueSeq & value)
327 {
328     error.clear();
329
330     // If this is not our envelope, then we will not set the flag.
331     //
332     if (_my_env == DTM_FALSE) {
333        error.setError(DTME_NotSupported);
334        return;
335     }
336
337     if (_body_env == (RFCEnvelope *)NULL) {
338        error.setError(DTME_NoObjectValue);
339        return;
340     }
341
342     _body_env->getHeader(error, name, abstract, value);
343 }
344
345 void
346 RFCBodyPart::setFlag(DtMailEnv & error,
347                      DtMailBodyPartState state)
348 {
349     error.clear();
350
351     // If this is not our envelope, then we will not set the flag.
352     //
353     if (_my_env == DTM_FALSE) {
354         error.setError(DTME_NotSupported);
355         return;
356     }
357
358     DtMailEnv my_error;
359     time_t now;
360     char str_time[40];
361
362     switch (state) {
363       case DtMailBodyPartDeletePending:
364         now = time(NULL);
365         sprintf(str_time, "%08lX", (long)now);
366         _body_env->setHeader(my_error, RFCDeleteHeader, DTM_TRUE, str_time);
367         break;
368
369       default:
370         error.setError(DTME_OperationInvalid);
371     }
372 }
373
374 void
375 RFCBodyPart::resetFlag(DtMailEnv & error,
376                        DtMailBodyPartState state)
377 {
378     error.clear();
379
380     // If this is not our envelope, then we will not set the flag.
381     //
382     if (_my_env == DTM_FALSE) {
383         error.setError(DTME_NotSupported);
384         return;
385     }
386
387     DtMailEnv my_error;
388
389     switch (state) {
390       case DtMailBodyPartDeletePending:
391         _body_env->removeHeader(my_error, RFCDeleteHeader);
392         break;
393
394       default:
395         error.setError(DTME_OperationInvalid);
396     }
397 }
398
399 DtMailBoolean
400 RFCBodyPart::flagIsSet(DtMailEnv & error,
401                        DtMailBodyPartState state)
402 {
403     error.clear();
404
405     // If this is not our envelope, then we will not set the flag.
406     //
407     if (_my_env == DTM_FALSE) {
408         error.setError(DTME_NotSupported);
409         return(DTM_FALSE);
410     }
411
412     DtMailEnv my_error;
413     DtMailValueSeq value;
414     DtMailBoolean answer;
415
416     switch (state) {
417       case DtMailBodyPartDeletePending:
418         _body_env->getHeader(my_error, RFCDeleteHeader, DTM_FALSE, value);
419         if (my_error.isNotSet()) {
420             answer = DTM_TRUE;
421         }
422         else {
423             answer = DTM_FALSE;
424         }
425         break;
426
427       default:
428         error.setError(DTME_OperationInvalid);
429     }
430
431     return(answer);
432 }
433
434 time_t
435 RFCBodyPart::getDeleteTime(DtMailEnv & error)
436 {
437     time_t      delete_time = 0;
438
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);
443     }
444
445     error.clear();
446
447     return(delete_time);
448 }
449
450
451 void
452 RFCBodyPart::adjustBodyPartsLocation(char * start)
453 {
454     MutexLock lock_scope(_body_lock);
455
456     _body_text = (_body_text - _body_start) + start;
457     _body_start = start;
458
459     if (_must_free_body == DTM_FALSE) {
460         //_body = (char *)_body_text;
461         _body = NULL;
462     }
463
464     if (_body_env && _my_env == DTM_TRUE) {
465         // CMVC bug 2807
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) != '-') {
470           fprintf(
471                 stderr,
472                 "RFCBodyPart::adjustBodyPartLocation(%.20s): Not a separator\n",
473                 start);
474         } else {
475                 while (*start != '\n')
476                         start++;
477                 start++;
478         }
479         // End Of fix for 2807
480         _body_env->adjustHeaderLocation(start, (int)(_body_text-_body_start));
481     }
482 }
483
484 DtMailBoolean
485 RFCBodyPart::isTerm(const char * start)
486 {
487     if (*start == '\n' || (*start == '\r' && *(start + 1) == '\n')) {
488         return(DTM_TRUE);
489     }
490     else {
491         return(DTM_FALSE);
492     }
493 }
494
495 const void *
496 RFCBodyPart::getBody(DtMailEnv & error)
497 {
498     error.clear();
499     if (!_body) {
500         loadBody(error);
501         if (error.isSet()) {
502             return(NULL);
503         }
504     }
505
506     return(_body);
507 }
508
509 // For CHARSET
510 /*
511  * Wrapper functions taken from libHelp/CEUtil.c
512  *
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.
519  */
520
521 static const char *DfltStdCharset = "us-ascii";
522 static const char *DfltStdLang = "C";
523
524 static char       MyPlatform[_DtPLATFORM_MAX_LEN+1];
525 static _DtXlateDb MyDb = NULL;
526 static char       MyProcess = False;
527 static char       MyFirst   = True;
528 static int        ExecVer;
529 static int        CompVer;
530
531
532 /******************************************************************************
533  * Function:    static int OpenLcxDb ()
534  *
535  * Parameters:   none
536  *
537  * Return Value:  0: ok
538  *               -1: error
539  *
540  * errno Values:
541  *
542  * Purpose: Opens the Ce-private Lcx database
543  *
544  *****************************************************************************/
545 int
546 RFCBodyPart::OpenLcxDb (void)
547 {
548     time_t      time1  = 0;
549     time_t      time2  = 0;
550
551     while (MyProcess == True) 
552       {
553         /* if time out, return */
554         if (time(&time2) == (time_t)-1)
555             return -1;
556
557         if (time1 == 0)
558             time1 = time2;
559         else if (time2 - time1 >= (time_t)30)
560             return -1;
561       }
562
563     if (MyFirst == True)
564       {
565         MyProcess = True;
566         if (_DtLcxOpenAllDbs(&MyDb) == 0 &&
567             _DtXlateGetXlateEnv(MyDb,MyPlatform,&ExecVer,&CompVer) != 0)
568           {
569             _DtLcxCloseDb(&MyDb);
570             MyDb = NULL;
571           }
572         MyFirst = False;
573         MyProcess = False;
574       }
575
576     return (MyDb == NULL ? -1 : 0 );
577 }
578
579 /******************************************************************************
580  * Function:    int DtXlateOpToStdLocale(char *operation, char *opLocale,
581  *                                       char **ret_stdLocale,
582  *                                       char **ret_stdLang, char **ret_stdSet)
583  *
584  * Parameters:
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.
593  *
594  * Return Value:
595  *
596  * Purpose:  Gets the standard locale given an operation and its locale
597  *
598  *****************************************************************************/
599 void
600 RFCBodyPart::DtXlateOpToStdLocale (
601      char       *operation,
602      char       *opLocale,
603      char       **ret_stdLocale,
604      char       **ret_stdLang,
605      char       **ret_stdSet)
606 {
607     int result = OpenLcxDb();
608
609     if (result == 0) {
610         (void) _DtLcxXlateOpToStd(
611                         MyDb, MyPlatform, CompVer,
612                         operation, opLocale,
613                         ret_stdLocale, ret_stdLang, ret_stdSet, NULL);
614     }
615
616     /* if failed, give default values */
617     if (ret_stdLocale != NULL && (result != 0 || *ret_stdLocale == NULL))
618     {
619         *ret_stdLocale =
620             (char *)malloc(strlen(DfltStdLang)+strlen(DfltStdCharset)+3);
621         sprintf(*ret_stdLocale,"%s.%s",DfltStdLang,DfltStdCharset);
622     }
623
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);
628 }
629
630 /******************************************************************************
631  * Function:    int DtXlateStdToOpLocale ( char *operation, char *stdLocale,
632  *                                    char *dflt_opLocale, char **ret_opLocale)
633  *
634  * Parameters:
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.
641  *
642  * Return Value:
643  *
644  * Purpose: Gets an operation-specific locale string given the standard string
645  *
646  *****************************************************************************/
647 void
648 RFCBodyPart::DtXlateStdToOpLocale (
649      char       *operation,
650      char       *stdLocale,
651      char       *dflt_opLocale,
652      char       **ret_opLocale)
653 {
654     int result = this->OpenLcxDb();
655
656     if (ret_opLocale)
657       *ret_opLocale = NULL;
658
659     if (result == 0)
660     {
661         (void) _DtLcxXlateStdToOp(
662                                 MyDb, MyPlatform, CompVer,
663                                 operation,
664                                 stdLocale, NULL, NULL, NULL,
665                                 ret_opLocale);
666     }
667
668     /* if translation fails, use a default value */
669     if (ret_opLocale && (result != 0 || *ret_opLocale == NULL))
670     {
671        if (dflt_opLocale) *ret_opLocale = (char *)strdup(dflt_opLocale);
672        else if (stdLocale) *ret_opLocale = (char *)strdup(stdLocale);
673     }
674 }
675
676
677 /******************************************************************************
678  * Function:    int DtXlateStdToOpCodeset (
679  *                              char *operation,
680  *                              char *stdCodeset,
681  *                              char *dflt_opCodeset,
682  *                              char **ret_opCodeset)
683  *
684  * Parameters:
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.
691  *
692  * Return Value:
693  *
694  * Purpose: Gets an operation-specific locale string given the standard string
695  *
696  *****************************************************************************/
697 void
698 RFCBodyPart::DtXlateStdToOpCodeset (
699      char       *operation,
700      char       *stdCodeset,
701      char       *dflt_opCodeset,
702      char       **ret_opCodeset)
703 {
704     int result = this->OpenLcxDb();
705
706     if (ret_opCodeset)
707       *ret_opCodeset = NULL;
708
709     if (result == 0)
710     {
711         (void) _DtLcxXlateStdToOp(
712                                 MyDb, MyPlatform, CompVer,
713                                 operation,
714                                 NULL, NULL, stdCodeset, NULL,
715                                 ret_opCodeset);
716     }
717
718     /* if translation fails, use a default value */
719     if (ret_opCodeset && (result != 0 || *ret_opCodeset == NULL))
720     {
721        if (dflt_opCodeset) *ret_opCodeset = (char *)strdup(dflt_opCodeset);
722        else if (stdCodeset) *ret_opCodeset = (char *)strdup(stdCodeset);
723     }
724 }
725
726 void
727 RFCBodyPart::DtXlateMimeToIconv(
728         const char *mimeId,
729         const char *defaultCommonCS,
730         const char *defaultIconvCS,
731         char **ret_commonCS,
732         char **ret_platformIconv)
733 {
734     int exists = -1;
735
736     this->OpenLcxDb();
737    
738     exists = _DtLcxXlateOpToStd(
739                                 MyDb, MyPlatform, CompVer,
740                                 DtLCX_OPER_MIME, mimeId,
741                                 NULL, NULL, ret_commonCS, NULL);
742
743     if (exists == -1)
744     {
745         exists = _DtLcxXlateOpToStd(
746                                 MyDb, "CDE", 0,
747                                 DtLCX_OPER_MIME, mimeId,
748                                 NULL, NULL, ret_commonCS, NULL);
749        if (exists == -1)
750          *ret_commonCS = (char *)strdup(defaultCommonCS);
751     }
752
753     exists = _DtLcxXlateStdToOp(
754                                 MyDb, MyPlatform, CompVer,
755                                 DtLCX_OPER_ICONV3,
756                                 NULL, NULL, *ret_commonCS, NULL,
757                                 ret_platformIconv);
758     if (exists == -1)
759       *ret_platformIconv = (char *)strdup(defaultIconvCS);
760 }
761
762 void
763 RFCBodyPart::DtXlateLocaleToMime(
764         const char * locale,
765         const char * defaultCommonCS,
766         const char * defaultMimeCS,
767         char ** ret_mimeCS)
768 {
769    char * commonCS = NULL;
770
771    this->OpenLcxDb();
772
773   /* look for platform-specific locale to CDE translation */
774   _DtLcxXlateOpToStd(
775                 MyDb, MyPlatform, CompVer,
776                 DtLCX_OPER_SETLOCALE, locale,
777                 NULL, NULL, &commonCS, NULL);
778   if (!commonCS)
779       commonCS = (char *)strdup(defaultCommonCS);
780
781   /* look for platform-specific MIME types; by default, there is none */
782   _DtLcxXlateStdToOp(
783                 MyDb, MyPlatform, CompVer,
784                 DtLCX_OPER_MIME,
785                 NULL, NULL, commonCS, NULL,
786                 ret_mimeCS);
787   if (!(*ret_mimeCS))
788   {
789      _DtLcxXlateStdToOp(
790                 MyDb, "CDE", 0,
791                 DtLCX_OPER_MIME,
792                 NULL, NULL, commonCS, NULL,
793                 ret_mimeCS);
794      if (!(*ret_mimeCS))
795         *ret_mimeCS = (char *)strdup(defaultMimeCS);
796   }
797
798   if (commonCS)
799       free(commonCS);
800 }
801
802 // Return iconv name of the given codeset.
803 // If iconv name does not exist, return NULL.
804 char *
805 RFCBodyPart::csToConvName(char *cs)
806 {
807    int exists = -1;
808    char *commonCS = NULL;
809    char *convName = NULL;
810    char *ret_target = NULL;
811  
812    this->OpenLcxDb();
813  
814    // Convert charset to upper case first because charset table is
815    // case sensitive.
816    if (cs)
817    {
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));
821    }
822    exists = _DtLcxXlateOpToStd(
823                         MyDb, MyPlatform, CompVer,
824                         DtLCX_OPER_MIME, cs,
825                         NULL, NULL, &commonCS, NULL);
826    if (exists == -1) {
827       exists = _DtLcxXlateOpToStd(
828                         MyDb, "CDE", 0,
829                         DtLCX_OPER_MIME, cs,
830                         NULL, NULL, &commonCS, NULL);
831       if  (exists == -1)
832         return NULL;
833    }
834  
835    DtXlateStdToOpCodeset(DtLCX_OPER_INTERCHANGE_CODESET,
836       commonCS,
837       NULL,
838       &ret_target);
839    DtXlateStdToOpCodeset(DtLCX_OPER_ICONV3,
840       ret_target,
841       NULL,
842       &convName);
843
844    if ( ret_target )
845      free( ret_target );
846    if ( commonCS )
847      free( commonCS );
848
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 )
853    {
854       if ( strlen(convName) > 0 )
855         return convName;
856       else
857         free( convName );
858    }
859    return NULL;
860 }
861
862 // Return current locale's iconv name.
863 char *
864 RFCBodyPart::locToConvName()
865 {
866    char *ret_locale = NULL;
867    char *ret_lang = NULL;
868    char *ret_codeset = NULL;
869  
870    DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
871       setlocale(LC_CTYPE, NULL),
872       &ret_locale,
873       &ret_lang,
874       &ret_codeset);
875
876    if (ret_codeset) {
877        free(ret_codeset);
878        ret_codeset = NULL;
879    }
880    
881    if (ret_lang) {
882        free(ret_lang);
883        ret_lang = NULL;
884    }
885    
886    DtXlateStdToOpLocale(DtLCX_OPER_ICONV3,
887       ret_locale,
888       NULL,
889       &ret_codeset);
890
891    if (ret_locale)
892      free(ret_locale);
893
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)
899         return ret_codeset;
900       else
901         free(ret_codeset);
902    } 
903    return NULL;
904 }
905
906 // Return target codeset's iconv name.
907 char *
908 RFCBodyPart::targetConvName()
909 {
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;
915  
916    DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
917       setlocale(LC_CTYPE, NULL),
918       &ret_locale,
919       &ret_lang,
920       &ret_codeset);
921    DtXlateStdToOpLocale(DtLCX_OPER_INTERCHANGE_CODESET,
922       ret_locale,
923       NULL,
924       &ret_target);
925    // Or do I call csToConvName() here??
926    DtXlateStdToOpCodeset(DtLCX_OPER_ICONV3,
927       ret_target,
928       NULL,
929       &ret_convName);
930  
931
932    if (ret_locale)
933      free(ret_locale);
934    if (ret_lang)
935      free(ret_lang);
936    if (ret_codeset)
937      free(ret_codeset);
938    if (ret_target)
939      free(ret_target);
940
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 )
945    {
946       if (strlen(ret_convName) > 0)
947         return ret_convName;
948       else
949         free(ret_convName);
950    }
951    return NULL;
952 }
953
954 // Return target codeset's MIME (tag) name.
955 char *
956 RFCBodyPart::targetTagName()
957 {
958    char *ret_locale = NULL;
959    char *ret_lang = NULL;
960    char *ret_codeset = NULL;
961    char *ret_target = NULL;
962
963    DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
964           setlocale(LC_CTYPE, NULL),
965           &ret_locale,
966           &ret_lang,
967           &ret_codeset);
968    DtXlateStdToOpLocale(DtLCX_OPER_INTERCHANGE_CODESET,
969           ret_locale,
970           NULL,
971           &ret_target);
972    DtXlateStdToOpCodeset(DtLCX_OPER_MIME,
973           ret_target,
974           NULL,
975           &ret_codeset);
976
977    if (ret_locale)
978      free(ret_locale);
979    if (ret_lang)
980      free(ret_lang);
981    if (ret_target)
982      free(ret_target);
983
984    return ret_codeset;
985 }
986
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.
990 int
991 RFCBodyPart::csConvert(char **bp, unsigned long &bp_len, int free_bp,
992 char *from_cs, char *to_cs)
993 {
994    DtMailEnv error;
995    iconv_t cd;
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;
999 #else
1000    char *ip = *bp;
1001 #endif
1002    char *op = NULL;
1003    char *op_start = NULL;
1004    int mb_ret = 0;
1005    size_t delta;
1006
1007    if ( *bp == NULL  ||  **bp == '\0'  ||  bp_len <= 0 )
1008           return 0;
1009    if ( to_cs == NULL  ||  from_cs == NULL )
1010           return 0;
1011    if ( (cd = iconv_open(to_cs, from_cs)) == (iconv_t) -1 ) {
1012           switch (errno) {
1013                 case EINVAL:
1014                   error.logError(DTM_FALSE,
1015                   "DtMail: Conversion from %s to %s is not supported.\n",
1016                   from_cs, to_cs);
1017           break;
1018           }   // end of switch statement
1019       return 0;
1020    }
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));
1025
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".
1034    errno = 0;
1035    while ( ileft > 0 || errno == E2BIG ) {
1036       errno = 0;
1037       ret = iconv(cd, &ip, &ileft, &op, &oleft);
1038       if ( ret == (size_t) -1 ) {
1039              switch (errno) {
1040                    case E2BIG:   // increase output buffer size
1041                          delta = ileft ? ileft : 3;
1042                          bp_len += delta;
1043                          op_start = (char *)realloc(
1044                                                 (char *)op_start,
1045                                                 (unsigned int) bp_len + 1); 
1046                          op = op_start + bp_len - delta - oleft;
1047                          oleft += delta;
1048                          // realloc does not clear out unused space.
1049                          // Therefore, garbage shows up in output buffer.
1050                          memset(op, 0, oleft + 1);
1051                          break;
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);
1057                            ip += mb_ret;
1058                            op += mb_ret;
1059                            oleft -= mb_ret;
1060                            ileft -= mb_ret;
1061                            mb_ret = 0;
1062                          } else {
1063                            //  mb_ret is either 0 or -1 at this point,
1064                            //  then skip one byte
1065                            //  and try conversion again.
1066                            ip++;
1067                            ileft--;
1068                          }
1069                          break;
1070                    case EBADF:   // bad conversion descriptor
1071                          break;
1072              }   // end of switch statement
1073           }
1074    }  // end of while loop
1075    iconv_close(cd);
1076
1077    // Is this necessary??  Is _body_decode_len == strlen(_body)??
1078    // Or can _body_decode_len contain spaces??
1079
1080    // Check to see if a body had been allocated by prior decoding.
1081    if (free_bp) {
1082       free(*bp);
1083    }
1084    *bp = op_start;
1085    bp_len = strlen(*bp); 
1086
1087    return 1;
1088 }
1089
1090 // End of For CHARSET