b18ae69ebf026e57e15ec83e966ef2eefb0e7d0e
[oweals/cde.git] / cde / programs / dtmail / libDtMail / Common / Session.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: Session.C /main/16 1998/07/24 16:08:39 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 #if defined (__osf__)
45
46 // Workaround for CDExc19308
47 //
48 // This ifdef was added as a workaround for the
49 // bug in the OSF1 V3.2 148 /usr/include/sys/localedef.h
50 // header file, and should be removed as soon as the bug is
51 // fixed.
52
53 #include <iconv.h>
54 #include <locale.h>
55 #include <time.h>
56 #include <DtHelp/LocaleXlate.h>
57 #include <errno.h>
58 #include <stdlib.h>
59 #include <limits.h>
60 #include <ctype.h>
61 #include <stdlib.h>
62 #include <unistd.h>
63 #include <string.h>
64 #include <pwd.h>
65 #include <sys/socket.h>
66 #include <ctype.h>
67
68 #include <DtMail/DtMail.hh>
69 #include <EUSDebug.hh>
70 #include "DynamicLib.hh"
71 #include <DtMail/Threads.hh>
72 #include <DtMail/ImplDriver.hh>
73 #include "ImplConfigTable.hh"
74 #include "SigChldImpl.hh"
75 #include <DtMail/IO.hh>
76 #include <DtMail/Common.h>
77
78 #ifndef True
79 #define True 1
80 #endif
81 #ifndef False
82 #define False 0
83 #endif
84
85 #else
86
87 #include <stdlib.h>
88 #include <unistd.h>
89 #if defined(USL) && (OSMAJORVERSION == 2)
90 extern "C" {
91 #endif
92 #if defined(USL) && (OSMAJORVERSION == 2)
93 };
94 #endif
95 #include <string.h>
96 #include <pwd.h>
97 #include <sys/socket.h>
98 #include <ctype.h>
99
100 #include <DtMail/DtMail.hh>
101
102 #include <EUSDebug.hh>
103 #include "DynamicLib.hh"
104 #include <DtMail/Threads.hh>
105 #include <DtMail/ImplDriver.hh>
106 #include "ImplConfigTable.hh"
107 #include "SigChldImpl.hh"
108 #include <DtMail/IO.hh>
109 #include <DtMail/Common.h>
110
111 // For CHARSET
112 //-------------------------------------
113 // HACK ALERT
114 // Any code change within "For CHARSET" should be changed in
115 // RFCBodyPart and Session because the same methods are duplicated
116 // in both of these classes.
117 // See RFCImpl.hh or DtMail/DtMail.hh for more explanation.
118 //-------------------------------------
119 #include <locale.h>
120 #include <time.h>
121 #include <DtHelp/LocaleXlate.h>
122 #include <errno.h>
123 #include <stdlib.h>
124 #include <limits.h>
125 #include <ctype.h>
126  
127 #ifndef True
128 #define True 1
129 #endif
130 #ifndef False
131 #define False 0
132 #endif
133  
134 #if defined(SunOS) && (SunOS < 55)
135 extern "C" {
136 #endif
137
138 #if !defined(linux)
139 #  include <iconv.h>
140 #else
141    // Iconv not defined for linux.  Use the EUSCompat stubs instead.
142 #  include <EUSCompat.h>
143 #endif
144
145 #if defined(SunOS) && (SunOS < 55)
146 }
147 #endif
148
149 // End of For CHARSET
150 #endif  // ! defined(__osf__)
151
152 DtVirtArray<SigChldInfo *>      *DtMailSigChldList;
153 void *                          _DtMutex;
154
155 static const int SessionSignature = 0xef9421cc;
156 static const int MaxImpls = 16;
157
158 static const int MAXIMUM_PATH_LENGTH = 2048;
159
160 DtMail::Session::Session(DtMailEnv & error, const char * app_name)
161 : _events(16), _valid_keys(2048)
162 {
163     _DtMutex = MutexInit();
164
165     error.clear();
166
167     _object_signature = 0;
168     _cur_key = 0;
169
170     // Create the ToolTalk session for managing file locking,
171     // if one doesn't exist.
172     _tt_channel = tt_default_procid();
173     if (tt_pointer_error(_tt_channel) != TT_OK) {
174         _tt_channel = ttdt_open(&_tt_fd, app_name, "SunSoft", "%I", 0);
175         if (tt_pointer_error(_tt_channel) != TT_OK) {
176             error.setError(DTME_TTFailure);
177             DebugPrintf(1,
178                         "DtMail::createSession - ttdt_open returns %s\n",
179                         tt_status_message(tt_pointer_error(_tt_channel)));
180             return;
181         }
182     }
183     else {
184         _tt_fd = tt_fd();
185     }
186
187     // The event_fd is how we allow async behavior to occur in a
188     // compatible way. We use a Unix domain socket as the file descriptor.
189     // The client will watch for activity on this file descriptor, and
190     // call our event routine when there is activity (either from XtMainLoop,
191     // or through some other method).
192     //
193     pipe(_event_fd);
194
195     _app_name = strdup(app_name);
196
197     DtMailEnv b_error;
198
199     _mail_rc = new MailRc(error, this);
200
201     buildImplTable(error);
202     if (error.isSet()) {
203         return;
204     }
205
206     _obj_mutex = MutexInit();
207
208     // The default implementation is specified via the DEFAULT_BACKEND
209     // variable. If this is not set in the .mailrc, then choose entry
210     // zero.
211     //
212     const char * value;
213     _mail_rc->getValue(b_error, "DEFAULT_BACKEND", &value);
214     if (b_error.isNotSet()) {
215         _default_impl = lookupImpl(value);
216         if (_default_impl < 0) {
217             _default_impl = 0;
218         }
219     }
220     else {
221         b_error.clear();
222         _default_impl = 0;
223     }
224
225     DtMailSigChldList = new DtVirtArray<SigChldInfo *>(8);
226
227     _busy_cb = NULL;
228     _busy_cb_data = NULL;
229     _canAutoSave = DTM_TRUE;
230
231     _object_signature = SessionSignature;
232
233     return;
234 }
235
236 DtMail::Session::~Session(void)
237 {
238     if (_object_signature != SessionSignature) { // Been here, did that!
239         return;
240     }
241     
242   {
243       MutexLock lock_scope(_obj_mutex);
244       
245       _object_signature = 0;
246       
247       ttdt_close(_tt_channel, NULL, 1);
248       
249       DtMailEnv b_error;
250       
251       // Close off the dynamic libraries and free the impl table.
252       for (int tbl = 0; tbl < _num_impls; tbl++) {
253           free(_impls[tbl].impl_name);
254           DynamicLib * dl = (DynamicLib *)_impls[tbl].impl_lib;
255           delete dl;
256       }
257
258       free(_impls);
259
260       lock_scope.unlock_and_destroy();
261       delete _mail_rc;
262
263       close(_event_fd[0]);
264       close(_event_fd[1]);
265   }
266     
267 }
268
269 const char **
270 DtMail::Session::enumerateImpls(DtMailEnv & error)
271 {
272     error.clear();
273     return(_impl_names);
274 }
275
276 void
277 DtMail::Session::setDefaultImpl(DtMailEnv & error, const char * impl)
278 {
279     int slot = lookupImpl(impl);
280
281     if (slot < 0) {
282         error.setError(DTME_NoSuchImplementation);
283         return;
284     }
285
286     MutexLock lock_scope(_obj_mutex);
287
288     _default_impl = slot;
289     error.clear();
290 }
291
292 const char *
293 DtMail::Session::getDefaultImpl(DtMailEnv & error)
294 {
295     if (_num_impls == 0) {
296         error.setError(DTME_NoImplementations);
297         return(NULL);
298     }
299
300     error.clear();
301
302     return(_impl_names[_default_impl]);
303 }
304
305 void
306 DtMail::Session::queryImpl(DtMailEnv & error,
307                            const char * impl,
308                            const char * capability,
309                            ...)
310 {
311     va_list     args;
312
313     va_start(args, capability);
314     queryImplV(error, impl, capability, args);
315     va_end(args);
316
317     return;
318 }
319
320 void
321 DtMail::Session::queryImplV(DtMailEnv & error,
322                            const char * impl,
323                            const char * capability,
324                            va_list args)
325 {
326     int slot = lookupImpl(impl);
327
328     if (slot < 0) {
329         error.setError(DTME_NoSuchImplementation);
330         return;
331     }
332
333     error.clear();
334
335     // We need to retrieve the QueryImpl entry point for the implementation.
336     //
337     QueryImplEntry qie;
338     
339     qie = (QueryImplEntry)_impls[slot].impl_meta_factory(QueryImplEntryOp);
340     if (!qie) {
341         error.setError(DTME_ImplFailure);
342         return;
343     }
344
345     qie(*this, error, capability, args);
346
347     return;
348 }
349
350 DtMail::MailBox *
351 DtMail::Session::mailBoxConstruct(DtMailEnv & error,
352                                   DtMailObjectSpace space,
353                                   void * arg,
354                                   DtMailCallback open_callback,
355                                   void * client_data,
356                                   const char * impl_name)
357 {
358     // If the client specified an implementation of choice, then that
359     // is the only thing we use.
360     //
361     int primary_impl = _default_impl;
362
363     if (impl_name) {
364         int sl = lookupImpl(impl_name);
365         if (sl < 0) {
366             error.setError(DTME_NoSuchImplementation);
367             return(NULL);
368         }
369
370         primary_impl = sl;
371     }
372
373     // First thing we will do is see if the default implementation
374     // can open this file. If so, then we will create a mail box object
375     // based on the default implementation.
376     //
377     int         const_impl;
378     QueryOpenEntry qoe;
379     
380     qoe = (QueryOpenEntry)
381             _impls[primary_impl].impl_meta_factory(QueryOpenEntryOp);
382     if (!qoe) {
383         error.setError(DTME_ImplFailure);
384         return(NULL);
385     }
386
387     if (qoe(*this, error, space, arg) == DTM_FALSE) {
388         // Don't go on if the client specified an implementation.
389         //
390         if (impl_name) {
391             error.setError(DTME_ImplFailure);
392             return(NULL);
393         }
394
395         // Oh well, let's walk through the list of impls and see if any of
396         // them will take ownership of this file.
397         //
398         MutexLock lock_scope(_obj_mutex);
399
400         const_impl = -1;
401         for(int slot = 0; slot < _num_impls; slot++) {
402             qoe = (QueryOpenEntry)
403                     _impls[slot].impl_meta_factory(QueryOpenEntryOp);
404             if (!qoe) {
405                 // Just skip this implementation.
406                 continue;
407             }
408             if (qoe(*this, error, space, arg) == DTM_TRUE) {
409                 const_impl = slot;
410                 break;
411             }
412         }
413
414         // If we didn't find an impl, then we have to give up.
415         //
416         if (const_impl < 0) {
417             error.setError(DTME_NotMailBox);
418             return(NULL);
419         }
420     }
421     else {
422         const_impl = primary_impl;
423     }
424
425     // At this point we have an implementation that is willing to work
426     // with the path. Get its mail box constructor and build a mailbox
427     // from it.
428     //
429     MailBoxConstructEntry mbce;
430     
431     mbce = (MailBoxConstructEntry)
432              _impls[const_impl].impl_meta_factory(MailBoxConstructEntryOp);
433     if (!mbce) {
434         error.setError(DTME_ImplFailure);
435         return(NULL);
436     }
437
438     return(mbce(*this, error, space, arg, open_callback, client_data));
439 }
440
441 DtMail::Message *
442 DtMail::Session::messageConstruct(DtMailEnv & error,
443                                   DtMailObjectSpace space,
444                                   void * arg,
445                                   DtMailCallback open_callback,
446                                   void * client_data,
447                                   const char * impl_name)
448 {
449     // If the client specified an implementation of choice, then that
450     // is the only thing we use.
451     //
452     int primary_impl = _default_impl;
453
454     if (impl_name) {
455         int sl = lookupImpl(impl_name);
456         if (sl < 0) {
457             error.setError(DTME_NoSuchImplementation);
458             return(NULL);
459         }
460
461         primary_impl = sl;
462     }
463
464     int         const_impl;
465     QueryMessageEntry qoe;
466     
467     qoe = (QueryMessageEntry)
468               _impls[primary_impl].impl_meta_factory(QueryMessageEntryOp);
469     if (!qoe) {
470         error.setError(DTME_ImplFailure);
471         return(NULL);
472     }
473
474     if (qoe(*this, error, space, arg) == DTM_FALSE) {
475         // Don't go on if the client specified an implementation.
476         //
477         if (impl_name) {
478             error.setError(DTME_ImplFailure);
479             return(NULL);
480         }
481
482         // Oh well, let's walk through the list of impls and see if any of
483         // them will take ownership of this file.
484         //
485         MutexLock lock_scope(_obj_mutex);
486
487         const_impl = -1;
488         for(int slot = 0; slot < _num_impls; slot++) {
489             qoe = (QueryMessageEntry)
490                     _impls[slot].impl_meta_factory(QueryMessageEntryOp);
491             if (!qoe) {
492                 // Just skip this implementation.
493                 continue;
494             }
495             if (qoe(*this, error, space, arg) == DTM_TRUE) {
496                 const_impl = slot;
497                 break;
498             }
499         }
500
501         // If we didn't find an impl, then we have to give up.
502         //
503         if (const_impl < 0) {
504             error.setError(DTME_ImplFailure);
505             return(NULL);
506         }
507     }
508     else {
509         const_impl = primary_impl;
510     }
511
512     // At this point we have an implementation that is willing to work
513     // with the path. Get its mail box constructor and build a mailbox
514     // from it.
515     //
516     MessageConstructEntry mbce;
517     
518     mbce = (MessageConstructEntry)
519              _impls[const_impl].impl_meta_factory(MessageConstructEntryOp);
520     if (!mbce) {
521         error.setError(DTME_ImplFailure);
522         return(NULL);
523     }
524
525     return(mbce(*this, error, space, arg, open_callback, client_data));
526 }
527
528 DtMail::Transport *
529 DtMail::Session::transportConstruct(DtMailEnv & error,
530                                     const char * impl,
531                                     DtMailStatusCallback call_back,
532                                     void * client_data)
533 {
534     // We need to find the implementation for starters.
535     //
536     int slot = lookupImpl(impl);
537     if (slot < 0) {
538         error.setError(DTME_NoSuchImplementation);
539         return(NULL);
540     }
541
542     TransportConstructEntry tce;
543     tce = (TransportConstructEntry)
544             _impls[slot].impl_meta_factory(TransportConstructEntryOp);
545
546     if (!tce) {
547         error.setError(DTME_NotSupported);
548         return(NULL);
549     }
550
551     return(tce(*this, error, call_back, client_data));
552 }
553
554 DtMail::MailRc *
555 DtMail::Session::mailRc(DtMailEnv & error)
556 {
557     error.clear();
558
559     return(_mail_rc);
560 }
561
562 //
563 // NEEDS TO BE DELETED .. SHOULD NO LONGER BE USED...
564 //
565 void
566 DtMail::Session::setError(DtMailEnv & error, const DTMailError_t minor_code)
567 {
568   error.setError(minor_code);
569   //DtMail::setError(*this, error, minor_code);
570 }
571
572 DtMailBoolean
573 DtMail::Session::pollRequired(DtMailEnv & error)
574 {
575     error.clear();
576
577 #if defined(POSIX_THREADS)
578     return(DTM_FALSE);
579 #else
580     return(DTM_TRUE);
581 #endif
582
583 }
584
585 int
586 DtMail::Session::eventFileDesc(DtMailEnv & error)
587 {
588     error.clear();
589
590     return(_event_fd[0]);
591 }
592
593 void
594 DtMail::Session::poll(DtMailEnv & error)
595 {
596     error.clear();
597
598 #if defined(POSIX_THREADS)
599     return; // A thread does this job.
600 #else
601
602     // We will grab the time and determine what needs to run.
603     // Any events that have expired since the last time we were
604     // called are automatically ran. If the event returns DTM_TRUE,
605     // then reset last_ran to the current time to cause the event
606     // to not run for the full wait interval; otherwise, refrain
607     // and let the event happen again at the next poll interval
608     //
609     time_t now = time(NULL);
610
611     for (int ev = 0; ev < _events.length(); ev++) {
612         EventRoutine * event = _events[ev];
613         if ((now - event->last_ran) > event->interval) {
614             if (event->routine(event->client_data) == DTM_TRUE)
615               event->last_ran = now;
616         }
617     }
618
619     return;
620 #endif
621
622 }
623
624 char *
625 DtMail::Session::expandPath(DtMailEnv & error, const char * path)
626 {
627     const char * fold_path;
628
629     if (path == NULL) {
630         error.setError(DTME_BadArg);
631         return(NULL);
632     }
633
634     error.clear();
635
636     char * exp_name = (char *)malloc(MAXIMUM_PATH_LENGTH);
637     if (exp_name == NULL)  {
638         error.setError(DTME_NoMemory);
639         return(NULL);
640     }
641     if (strchr(path, '$') != NULL) {
642         sprintf (exp_name, "echo %s", path);
643         FILE *fp;
644         if ((fp = popen(exp_name, "r")) != NULL) {
645                 exp_name[0] = '\0';
646                 if (fgets(exp_name, MAXIMUM_PATH_LENGTH, fp) != NULL &&
647                         exp_name[0] != '\0') 
648                         // get rid of \n at end of string
649                         exp_name[strlen(exp_name)-1] = '\0';
650                 else
651                         strcpy(exp_name, path); 
652                 pclose(fp);
653         }
654         else 
655                 strcpy(exp_name, path);
656     }
657     else 
658         strcpy(exp_name, path);
659
660     char * exp_name2 = (char *)malloc(MAXIMUM_PATH_LENGTH);
661     exp_name2[0] = '\0';
662
663     switch (exp_name[0]) {
664       case '+':
665         // This is relative to the folder path. Figure out what that is.
666       {
667           _mail_rc->getValue(error, "folder", &fold_path);
668           if (error.isNotSet()) {
669                if (*fold_path != '/' && *fold_path != '.' && 
670                         *fold_path != '~' && *fold_path != '$') 
671                   strcpy(exp_name2, "~/");
672
673                 strcat(exp_name2, fold_path);
674                 strcat(exp_name2, "/");
675           }
676           else  // Use the default folder
677                 strcpy(exp_name2, "~/");
678                 
679           strcat(exp_name2, &exp_name[1]);
680       
681           // We need to call ourselves again to deal with 
682           // relative paths in the folder directory.
683           // 
684           char * old_exp = exp_name2;
685           exp_name2 = expandPath(error, old_exp);
686           free(old_exp);
687           break;
688       }
689         
690       case '~':
691         // This is relative to the user's home directory.
692       {
693           passwd pw;
694           const char * start;
695
696           if (exp_name[1] == '/' || exp_name[1] == '\0') {
697               GetPasswordEntry(pw);
698               start = &exp_name[1];
699           }
700           else {
701               passwd * pw_p;
702
703               char * slash = strchr(&exp_name[1], '/');
704               if (slash == NULL) {
705                    error.clear();
706                    error.setError(DTME_NoSuchFile);
707                    break;
708               }
709
710               int len = slash - &exp_name[1];
711               char * name = new char[len + 1];
712               strncpy(name, &exp_name[1], len);
713               name[len] = 0;
714               pw_p = getpwnam(name);
715
716               if (!pw_p) {
717                   error.clear();
718                   error.setError(DTME_NoSuchFile);
719                   break;
720               }
721               pw = *pw_p;
722
723               delete [] name;
724
725               start = slash;
726           }
727
728           strcpy(exp_name2, pw.pw_dir);
729           strcat(exp_name2, start);
730           break;
731       }
732
733       // We have a directory or no specials. Just copy the path and
734       // return.
735       case '.':
736       case '/':
737       default:
738         strcpy(exp_name2, exp_name);
739         break;
740
741     }
742     free(exp_name);
743     return(exp_name2);
744 }
745
746 // This routine takes a path and checks to see if the path can be
747 // expressed relative to the "folder" path.  If it can, it returns
748 // the relative path; otherwise, it returns the original path.
749 char *
750 DtMail::Session::getRelativePath(DtMailEnv & error, const char * path)
751 {
752     const char * fold_path;
753
754     if (path == NULL) {
755         error.setError(DTME_BadArg);
756         return(NULL);
757     }
758
759     error.clear();
760
761     char * exp_name = (char *)malloc(MAXIMUM_PATH_LENGTH);
762     if (!exp_name) {
763         error.setError(DTME_NoMemory);
764         return(NULL);
765     }
766     exp_name[0] = '\0'; // Just for errors.
767
768     switch (path[0]) {
769         case '/':
770             // This is an absolute path, so there is a chance that
771             // we can trim it down to a relative path if it goes down
772             // the same way as the folder path.
773         {
774             _mail_rc->getValue(error, "folder", &fold_path);
775             if (error.isNotSet()) {
776                 strcpy(exp_name, fold_path);
777               
778                 // We need to call ourselves again to deal with 
779                 // relative paths in the folder directory.
780                 // 
781                 char * old_exp = exp_name;
782                 exp_name = expandPath(error, old_exp);
783                 free(old_exp);
784
785                 // Check to see if the path starts with the folder path.
786                 char * matched_path = const_cast<char *>(strstr(path, exp_name));
787                 if (matched_path == path) {
788                     // Yes it does, make it a relative path to the folder dir.
789                     int folder_path_length = strlen(exp_name);
790                     while (path[folder_path_length] == '/')
791                       folder_path_length++;
792                     strcpy(exp_name, &path[folder_path_length]);
793                     break;
794                 } else {
795                     strcpy(exp_name, path);
796                     break;
797                 }
798             }
799             else {
800                 // There is no folder variable so just fall through to the
801                 // default.
802                 error.clear();
803             }
804         }
805         case '+':
806             // This is relative to the folder path. Leave it alone.
807             // The only time we are likely to see a leading '+' is
808             // when the path was carried over from mailtool in the .mailrc.
809         case '~':
810             // This is relative to the user's home directory.  Leave it alone.
811             // The only time we are likely to see a leading '~' is
812             // when the path was carried over from mailtool in the .mailrc.
813         case '.':
814             // This is relative to the current directory where dtmail is
815             // running.  Leave it alone.  The only time we are likely to see
816             // a leading '.' is when the path was carried over from mailtool
817             // in the .mailrc.
818         default:
819         {
820             strcpy(exp_name, path);
821             break;
822         }
823     }
824     return(exp_name);
825
826 }
827
828
829 void
830 DtMail::Session::addEventRoutine(DtMailEnv & error,
831                                  DtMailEventFunc routine,
832                                  void * client_data,
833                                  time_t interval)
834 {
835     error.clear();
836
837     EventRoutine * er = new EventRoutine;
838
839     er->routine = routine;
840     er->client_data = client_data;
841     er->interval = interval;
842     er->last_ran = 0; // This will case this routine to run immediately.
843
844     _events.append(er);
845 }
846
847 void
848 DtMail::Session::removeEventRoutine(DtMailEnv & error,
849                                     DtMailEventFunc routine,
850                                     void * client_data)
851 {
852     error.clear();
853
854     for (int slot = 0; slot < _events.length(); slot++) {
855         EventRoutine * event = _events[slot];
856         if (event->routine == routine &&
857             event->client_data == client_data) {
858             delete event;
859             _events.remove(slot);
860         }
861     }
862 }
863
864 void
865 DtMail::Session::writeEventData(DtMailEnv&,
866                                 const void * buf,
867                                 const unsigned long size)
868 {
869     int status = SafeWrite(_event_fd[1], buf, (int)size);
870 }
871
872 DtMailBoolean
873 DtMail::Session::validObjectKey(DtMailObjectKey key)
874 {
875     return(_valid_keys.indexof(key) < 0 ? DTM_FALSE : DTM_TRUE);
876 }
877
878 DtMailObjectKey
879 DtMail::Session::newObjectKey(void)
880 {
881     MutexLock lock_scope(_obj_mutex);
882
883     _cur_key += 1;
884     _valid_keys.append(_cur_key);
885
886     return(_cur_key);
887 }
888
889 void
890 DtMail::Session::removeObjectKey(DtMailObjectKey key)
891 {
892     MutexLock lock_scope(_obj_mutex);
893
894     int slot = _valid_keys.indexof(key);
895     if (slot >= 0) {
896         _valid_keys.remove(slot);
897     }
898 }
899
900 void
901 DtMail::Session::registerDisableGroupPrivilegesCallback(
902                                 DisableGroupPrivilegesCallback cb,
903                                 void * cb_data)
904 {
905     _disableGroupPrivileges_cb = cb;
906     _disableGroupPrivileges_cb_data = cb_data;
907 }
908
909 #ifdef DEAD_WOOD
910 void
911 DtMail::Session::unregisterDisableGroupPrivilegesCallback(void)
912 {
913     _disableGroupPrivileges_cb = NULL;
914     _disableGroupPrivileges_cb_data = NULL;
915 }
916 #endif /* DEAD_WOOD */
917
918 void
919 DtMail::Session::disableGroupPrivileges(void)
920 {
921     if (_disableGroupPrivileges_cb) {
922         _disableGroupPrivileges_cb(_disableGroupPrivileges_cb_data);
923     }
924 }
925
926 void
927 DtMail::Session::registerEnableGroupPrivilegesCallback(
928                                         EnableGroupPrivilegesCallback cb,
929                                         void * cb_data)
930 {
931     _enableGroupPrivileges_cb = cb;
932     _enableGroupPrivileges_cb_data = cb_data;
933 }
934
935 #ifdef DEAD_WOOD
936 void
937 DtMail::Session::unregisterEnableGroupPrivilegesCallback(void)
938 {
939     _enableGroupPrivileges_cb = NULL;
940     _enableGroupPrivileges_cb_data = NULL;
941 }
942 #endif /* DEAD_WOOD */
943
944 void
945 DtMail::Session::enableGroupPrivileges(void)
946 {
947     if (_enableGroupPrivileges_cb) {
948         _enableGroupPrivileges_cb(_enableGroupPrivileges_cb_data);
949     }
950 }
951
952 void
953 DtMail::Session::registerBusyCallback(DtMailEnv&,
954                                       BusyApplicationCallback cb,
955                                       void * cb_data)
956 {
957     _busy_cb = cb;
958     _busy_cb_data = cb_data;
959 }
960
961 #ifdef DEAD_WOOD
962 void
963 DtMail::Session::unregisterBusyCallback(DtMailEnv & error)
964 {
965     _busy_cb = NULL;
966     _busy_cb_data = NULL;
967 }
968 #endif /* DEAD_WOOD */
969
970 void
971 DtMail::Session::setBusyState(DtMailEnv &error, DtMailBusyState busy_state)
972 {
973     if (_busy_cb) {
974         _busy_cb(error, busy_state, _busy_cb_data);
975     }
976 }
977
978
979 void
980 DtMail::Session::registerLastInteractiveEventTimeCallback(
981                                         LastInteractiveEventTimeCallback cb,
982                                         void * cb_data)
983 {
984     _interactive_time_cb = cb;
985     _interactive_time_cb_data = cb_data;
986 }
987
988 long
989 DtMail::Session::lastInteractiveEventTime(void)
990 {
991     if (_interactive_time_cb) {
992       return(_interactive_time_cb(_interactive_time_cb_data));
993     }
994     else
995       return(0);
996 }
997
998 void
999 DtMail::Session::buildImplTable(DtMailEnv & error)
1000 {
1001   error.clear();
1002
1003     // Let's pick a ridiculous number of implementations.
1004     _impls = (Impls *)malloc(sizeof(Impls) * MaxImpls);
1005     _impl_names = (const char **)malloc(sizeof(char *) * (MaxImpls + 1));
1006
1007     // We will simply walk through the default implementations
1008     // to start, adding them to the impl table.
1009     int tbl;
1010     for (tbl = 0, _num_impls = 0; initial_impls[tbl].meta_entry_point; tbl++) {
1011         // Get the library handle.
1012         DynamicLib * dl = CreatePlatformDl(initial_impls[tbl].lib_name);
1013
1014         if (dl) { // We are only interested in libraries we can load.
1015             _impls[_num_impls].impl_lib = dl;
1016             _impls[_num_impls].impl_meta_factory =
1017               (MetaImplFactory)dl->getSym(initial_impls[tbl].meta_entry_point);
1018             if (_impls[_num_impls].impl_meta_factory == NULL) {
1019                 delete dl;
1020                 continue;
1021             }
1022             _impls[_num_impls].impl_name = strdup(initial_impls[tbl].impl_name);
1023             _impl_names[_num_impls] = _impls[_num_impls].impl_name;
1024             _num_impls += 1;
1025         }
1026     }
1027
1028     _impl_names[_num_impls] = NULL;
1029
1030     if (_num_impls == 0) {
1031         error.setError(DTME_NoImplementations);
1032     }
1033 }
1034
1035 int
1036 DtMail::Session::lookupImpl(const char * impl)
1037 {
1038     MutexLock lock_scope(_obj_mutex);
1039     for(int i = 0; i < _num_impls; i++) {
1040         if (strcmp(_impls[i].impl_name, impl) == 0) {
1041             return(i);
1042         }
1043     }
1044
1045     return(-1);
1046 }
1047
1048 void
1049 DtMail::Session::setAutoSaveFlag(
1050     DtMailBoolean flag
1051 )
1052 {
1053     _canAutoSave = flag;
1054 }
1055
1056 DtMailBoolean
1057 DtMail::Session::getAutoSaveFlag()
1058 {
1059     return(_canAutoSave);
1060 }
1061
1062 extern "C" void
1063 ChildExitNotify(const int pid, const int status)
1064 {
1065     // We need to lookup the child, and set the status of the
1066     // correct condition variable. We will remove the slot, but
1067     // the thread will destroy the buffer in the slot.
1068     //
1069     for (int slot = 0; slot < DtMailSigChldList->length(); slot++) {
1070         if ((*DtMailSigChldList)[slot]->pid == pid) {
1071             (*DtMailSigChldList)[slot]->cond = status;
1072             DtMailSigChldList->remove(slot);
1073             return;
1074         }
1075     }
1076
1077     // Not finding the pid is not a problem. Just means it wasn't
1078     // one of ours.
1079     //
1080     return;
1081 }
1082
1083
1084 // For CHARSET
1085 /*
1086  * Wrapper functions taken from libHelp/CEUtil.c
1087  *
1088  * We took these functions and renamed them because
1089  * 1. Originally these are called _DtHelpCeXlate* and thus they are private
1090  *    to libHelp and not exported to outside of libHelp.
1091  * 2. When these functions are moved to another library, then users of these
1092  *    functions would only need to link with a different library.  The caller
1093  *    doesn't have to modify code.
1094  */
1095
1096 static const char *DfltStdCharset = "us-ascii";
1097 static const char *DfltStdLang = "C";
1098
1099 static char       MyPlatform[_DtPLATFORM_MAX_LEN+1];
1100 static _DtXlateDb MyDb = NULL;
1101 static char       MyProcess = False;
1102 static char       MyFirst   = True;
1103 static int        ExecVer;
1104 static int        CompVer;
1105
1106
1107 /******************************************************************************
1108  * Function:    static int OpenLcxDb ()
1109  *
1110  * Parameters:   none
1111  *
1112  * Return Value:  0: ok
1113  *               -1: error
1114  *
1115  * errno Values:
1116  *
1117  * Purpose: Opens the Ce-private Lcx database
1118  *
1119  *****************************************************************************/
1120 int
1121 DtMail::Session::OpenLcxDb (void)
1122 {
1123     time_t      time1  = 0;
1124     time_t      time2  = 0;
1125
1126     while (MyProcess == True) 
1127       {
1128         /* if time out, return */
1129         if (time(&time2) == (time_t)-1)
1130             return -1;
1131
1132         if (time1 == 0)
1133             time1 = time2;
1134         else if (time2 - time1 >= (time_t)30)
1135             return -1;
1136       }
1137
1138     if (MyFirst == True)
1139       {
1140         MyProcess = True;
1141         if (_DtLcxOpenAllDbs(&MyDb) == 0 &&
1142             _DtXlateGetXlateEnv(MyDb,MyPlatform,&ExecVer,&CompVer) != 0)
1143           {
1144             _DtLcxCloseDb(&MyDb);
1145             MyDb = NULL;
1146           }
1147         MyFirst = False;
1148         MyProcess = False;
1149       }
1150
1151     return (MyDb == NULL ? -1 : 0 );
1152 }
1153
1154 /******************************************************************************
1155  * Function:    int DtXlateOpToStdLocale(char *operation, char *opLocale,
1156  *                                      char **ret_stdLocale,
1157  *                                      char **ret_stdLang,
1158  *                                      char **ret_stdSet)
1159  *
1160  * Parameters:
1161  *              operation       Operation associated with the locale value
1162  *              opLocale        An operation-specific locale string
1163  *              ret_locale      Returns the std locale
1164  *                              Caller must free this string.
1165  *              ret_stdLang        Returns the std language & territory string.
1166  *                              Caller must free this string.
1167  *              ret_stdSet         Returns the std code set string.
1168  *                              Caller must free this string.
1169  *
1170  * Return Value:
1171  *
1172  * Purpose:  Gets the standard locale given an operation and its locale
1173  *
1174  *****************************************************************************/
1175 void
1176 DtMail::Session::DtXlateOpToStdLocale (
1177      char       *operation,
1178      char       *opLocale,
1179      char       **ret_stdLocale,
1180      char       **ret_stdLang,
1181      char       **ret_stdSet)
1182 {
1183     int result = OpenLcxDb();
1184
1185     if (result == 0) {
1186         (void) _DtLcxXlateOpToStd(
1187                                 MyDb, MyPlatform, CompVer,
1188                                 operation, opLocale,
1189                                 ret_stdLocale, ret_stdLang, ret_stdSet, NULL);
1190     }
1191
1192     /* if failed, give default values */
1193     if (ret_stdLocale != NULL && (result != 0 || *ret_stdLocale == NULL))
1194     {
1195         *ret_stdLocale =
1196           (char *)malloc(strlen(DfltStdLang)+strlen(DfltStdCharset)+3);
1197         sprintf(*ret_stdLocale,"%s.%s",DfltStdLang,DfltStdCharset);
1198     }
1199
1200     if (ret_stdLang != NULL && (result != 0 || *ret_stdLang == NULL))
1201         *ret_stdLang = (char *)strdup(DfltStdLang);
1202     if (ret_stdSet != NULL && (result != 0 || *ret_stdSet == NULL))
1203         *ret_stdSet = (char *)strdup(DfltStdCharset);
1204 }
1205
1206 /******************************************************************************
1207  * Function:    int DtXlateStdToOpLocale(char *operation,
1208  *                                      char *stdLocale,
1209  *                                      char *dflt_opLocale,
1210  *                                      char **ret_opLocale)
1211  *
1212  * Parameters:
1213  *    operation         operation whose locale value will be retrieved
1214  *    stdLocale         standard locale value
1215  *    dflt_opLocale     operation-specific locale-value
1216  *                      This is the default value used in error case
1217  *    ret_opLocale      operation-specific locale-value placed here
1218  *                      Caller must free this string.
1219  *
1220  * Return Value:
1221  *
1222  * Purpose: Gets an operation-specific locale string given the standard string
1223  *
1224  *****************************************************************************/
1225 void
1226 DtMail::Session::DtXlateStdToOpLocale (
1227      char       *operation,
1228      char       *stdLocale,
1229      char       *dflt_opLocale,
1230      char       **ret_opLocale)
1231 {
1232     int result = this->OpenLcxDb();
1233
1234     if (ret_opLocale)
1235       *ret_opLocale = NULL;
1236
1237     if (result == 0) {
1238       (void) _DtLcxXlateStdToOp(
1239                                 MyDb, MyPlatform, CompVer,
1240                                 operation, stdLocale,
1241                                 NULL, NULL, NULL,
1242                                 ret_opLocale);
1243     }
1244
1245     /* if translation fails, use a default value */
1246     if (ret_opLocale && (result != 0 || *ret_opLocale == NULL))
1247     {
1248        if (dflt_opLocale) *ret_opLocale = (char *)strdup(dflt_opLocale);
1249        else if (stdLocale) *ret_opLocale = (char *)strdup(stdLocale);
1250     }
1251 }
1252
1253
1254 /******************************************************************************
1255  * Function:    int DtXlateStdToOpCodeset (
1256  *                              char *operation,
1257  *                              char *stdCodeset,
1258  *                              char *dflt_opCodeset,
1259  *                              char **ret_opCodeset)
1260  *
1261  * Parameters:
1262  *    operation         operation whose codeset value will be retrieved
1263  *    stdCodeset        standard codeset value
1264  *    dflt_opCodeset    operation-specific codeset-value
1265  *                      This is the default value used in error case
1266  *    ret_opCodeset     operation-specific codeset-value placed here
1267  *                      Caller must free this string.
1268  *
1269  * Return Value:
1270  *
1271  * Purpose: Gets an operation-specific locale string given the standard string
1272  *
1273  *****************************************************************************/
1274 void
1275 DtMail::Session::DtXlateStdToOpCodeset (
1276      char       *operation,
1277      char       *stdCodeset,
1278      char       *dflt_opCodeset,
1279      char       **ret_opCodeset)
1280 {
1281     int result = this->OpenLcxDb();
1282
1283     if (ret_opCodeset)
1284       *ret_opCodeset = NULL;
1285
1286     if (result == 0)
1287     {
1288         (void) _DtLcxXlateStdToOp(
1289                                 MyDb, MyPlatform, CompVer,
1290                                 operation,
1291                                 NULL, NULL, stdCodeset, NULL,
1292                                 ret_opCodeset);
1293     }
1294
1295     /* if translation fails, use a default value */
1296     if (ret_opCodeset && (result != 0 || *ret_opCodeset == NULL))
1297     {
1298        if (dflt_opCodeset) *ret_opCodeset = (char *)strdup(dflt_opCodeset);
1299        else if (stdCodeset) *ret_opCodeset = (char *)strdup(stdCodeset);
1300     }
1301 }
1302
1303 void
1304 DtMail::Session::DtXlateMimeToIconv(
1305         const char *mimeId,
1306         const char *defaultCommonCS,
1307         const char *defaultIconvCS,
1308         char **ret_commonCS,
1309         char **ret_platformIconv)
1310 {
1311     int exists = -1;
1312
1313     this->OpenLcxDb();
1314    
1315     exists = _DtLcxXlateOpToStd(
1316                                 MyDb, MyPlatform, CompVer,
1317                                 DtLCX_OPER_MIME, mimeId,
1318                                 NULL, NULL, ret_commonCS, NULL);
1319
1320     if (exists == -1)
1321     {
1322         exists = _DtLcxXlateOpToStd(
1323                                 MyDb, "CDE", 0,
1324                                 DtLCX_OPER_MIME, mimeId,
1325                                 NULL, NULL, ret_commonCS, NULL);
1326         if (exists == -1)
1327           *ret_commonCS = (char *)strdup(defaultCommonCS);
1328     }
1329
1330     exists = _DtLcxXlateStdToOp(
1331                                 MyDb, MyPlatform, CompVer,
1332                                 DtLCX_OPER_ICONV3,
1333                                 NULL, NULL, *ret_commonCS, NULL,
1334                                 ret_platformIconv);
1335     if (exists == -1)
1336       *ret_platformIconv = (char *)strdup(defaultIconvCS);
1337 }
1338
1339 void
1340 DtMail::Session::DtXlateLocaleToMime(
1341         const char * locale,
1342         const char * defaultCommonCS,
1343         const char * defaultMimeCS,
1344         char ** ret_mimeCS)
1345 {
1346    char * commonCS = NULL;
1347
1348    this->OpenLcxDb();
1349
1350   /* look for platform-specific locale to CDE translation */
1351   _DtLcxXlateOpToStd(
1352                 MyDb, MyPlatform, CompVer,
1353                 DtLCX_OPER_SETLOCALE, locale,
1354                 NULL, NULL, &commonCS, NULL);
1355   if (!commonCS)
1356       commonCS = (char *)strdup(defaultCommonCS);
1357
1358   /* look for platform-specific MIME types; by default, there is none */
1359   _DtLcxXlateStdToOp(
1360                 MyDb, MyPlatform, CompVer,
1361                 DtLCX_OPER_MIME,
1362                 NULL, NULL, commonCS, NULL,
1363                 ret_mimeCS);
1364   if (!(*ret_mimeCS))
1365   {
1366      _DtLcxXlateStdToOp(
1367                 MyDb, "CDE", 0,
1368                 DtLCX_OPER_MIME,
1369                 NULL, NULL, commonCS, NULL,
1370                 ret_mimeCS);
1371      if (!(*ret_mimeCS))
1372         *ret_mimeCS = (char *)strdup(defaultMimeCS);
1373   }
1374
1375   if (commonCS)
1376       free(commonCS);
1377 }
1378
1379 // Return iconv name of the given codeset.
1380 // If iconv name does not exist, return NULL.
1381 char *
1382 DtMail::Session::csToConvName(char *cs)
1383 {
1384    int exists = -1;
1385    char *commonCS = NULL;
1386    char *convName = NULL;
1387    char *ret_target = NULL;
1388  
1389    this->OpenLcxDb();
1390  
1391    // Convert charset to upper case first because charset table is
1392    // case sensitive.
1393    if (cs)
1394    {
1395        int len_cs = strlen(cs);
1396        for ( int num_cs = 0;  num_cs < len_cs;  num_cs++ )
1397          *(cs+num_cs) = toupper(*(cs+num_cs));
1398    }
1399    exists = _DtLcxXlateOpToStd(
1400                         MyDb, MyPlatform, CompVer,
1401                         DtLCX_OPER_MIME, cs,
1402                         NULL, NULL, &commonCS, NULL);
1403    if (exists == -1) {
1404       exists = _DtLcxXlateOpToStd(
1405                         MyDb, "CDE", 0,
1406                         DtLCX_OPER_MIME, cs,
1407                         NULL, NULL, &commonCS, NULL);
1408       if  (exists == -1)
1409         return NULL;
1410    }
1411  
1412   DtXlateStdToOpCodeset(DtLCX_OPER_INTERCHANGE_CODESET,
1413       commonCS,
1414       NULL,
1415       &ret_target);
1416    DtXlateStdToOpCodeset(DtLCX_OPER_ICONV3,
1417       ret_target,
1418       NULL,
1419       &convName);
1420
1421    if ( ret_target )
1422      free( ret_target );
1423    if ( commonCS )
1424      free( commonCS );
1425  
1426    // Workaround for libDtHelp
1427    // Case of no iconv name for a particular locale, eg. C,
1428    // check for empty string.
1429    if ( convName != NULL )
1430    {
1431       if ( strlen(convName) > 0 )
1432         return convName;
1433       else
1434         free( convName );
1435    }
1436    return NULL;
1437 }
1438
1439 // Return current locale's iconv name.
1440 char *
1441 DtMail::Session::locToConvName()
1442 {
1443    char *ret_locale = NULL;
1444    char *ret_lang = NULL;
1445    char *ret_codeset = NULL;
1446  
1447    DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
1448       setlocale(LC_CTYPE, NULL),
1449       &ret_locale,
1450       &ret_lang,
1451       &ret_codeset);
1452
1453    if (ret_codeset) {
1454        free(ret_codeset);
1455        ret_codeset = NULL;
1456    }
1457    
1458    if (ret_lang) {
1459        free(ret_lang);
1460        ret_lang = NULL;
1461    }
1462    
1463    DtXlateStdToOpLocale(DtLCX_OPER_ICONV3,
1464       ret_locale,
1465       NULL,
1466       &ret_codeset);
1467
1468    if (ret_locale) 
1469      free(ret_locale);
1470
1471    // Workaround for libDtHelp
1472    // Case of no iconv name for a particular locale, eg. C, 
1473    // check for empty string.
1474    if ( ret_codeset != NULL )
1475    {
1476       if ( strlen(ret_codeset) > 0 )
1477         return ret_codeset;
1478       else
1479         free(ret_codeset);
1480    }
1481    return NULL;
1482 }
1483
1484 // Return target codeset's iconv name.
1485 char *
1486 DtMail::Session::targetConvName()
1487 {
1488    char *ret_locale = NULL;
1489    char *ret_lang = NULL;
1490    char *ret_codeset = NULL;
1491    char *ret_target = NULL;
1492    char *ret_convName = NULL;
1493  
1494    DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
1495       setlocale(LC_CTYPE, NULL),
1496       &ret_locale,
1497       &ret_lang,
1498       &ret_codeset);
1499    DtXlateStdToOpLocale(DtLCX_OPER_INTERCHANGE_CODESET,
1500       ret_locale,
1501       NULL,
1502       &ret_target);
1503    // Or do I call csToConvName() here??
1504    DtXlateStdToOpCodeset(DtLCX_OPER_ICONV3,
1505       ret_target,
1506       NULL,
1507       &ret_convName);
1508  
1509    if (ret_locale)
1510      free(ret_locale);
1511    if (ret_lang)
1512      free(ret_lang);
1513    if (ret_codeset)
1514      free(ret_codeset);
1515    if (ret_target)
1516      free(ret_target);
1517
1518    // Workaround for libDtHelp
1519    // Case of no iconv name for a particular locale, eg. C,
1520    // check for empty string.
1521    if ( ret_convName != NULL )
1522    {
1523       if ( strlen(ret_convName) > 0 )
1524         return ret_convName;
1525       else
1526         free(ret_convName);
1527    }
1528    return NULL;
1529 }
1530
1531 // Return target codeset's MIME (tag) name.
1532 char *
1533 DtMail::Session::targetTagName()
1534 {
1535    char *ret_locale = NULL;
1536    char *ret_lang = NULL;
1537    char *ret_codeset = NULL;
1538    char *ret_target = NULL;
1539
1540    DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
1541           setlocale(LC_CTYPE, NULL),
1542           &ret_locale,
1543           &ret_lang,
1544           NULL);
1545    DtXlateStdToOpLocale(DtLCX_OPER_INTERCHANGE_CODESET,
1546           ret_locale,
1547           NULL,
1548           &ret_target);
1549    DtXlateStdToOpCodeset(DtLCX_OPER_MIME,
1550           ret_target,
1551           NULL,
1552           &ret_codeset);
1553
1554    if (ret_locale)
1555      free(ret_locale);
1556    if (ret_lang)
1557      free(ret_lang);
1558    if (ret_target)
1559      free(ret_target);
1560
1561    return ret_codeset;
1562 }
1563
1564 // Given an extension to the interchange codeset name.
1565 // Return target codeset's MIME (tag) name.
1566 // The extension is for Sun V3 backward compatibility so that
1567 // reverse mapping of out-going charset tag is the same as
1568 // OpenWindows Mailtool.
1569 char *
1570 DtMail::Session::targetTagName(char *special)
1571 {
1572    char *ret_locale = NULL;
1573    char *ret_lang = NULL;
1574    char *ret_codeset = NULL;
1575    char *ret_target = NULL;
1576  
1577    DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
1578       setlocale(LC_CTYPE, NULL),
1579       &ret_locale,
1580       &ret_lang,
1581       &ret_codeset);
1582  
1583    // Allocate two more bytes for "." and null terminator.
1584    char *special_locale;
1585
1586    special_locale = (char *)calloc(
1587                                 strlen(ret_locale) + strlen(special) + 2,
1588                                 sizeof(char));
1589    sprintf(special_locale, "%s%s%s", ret_locale, ".", special);
1590  
1591    DtXlateStdToOpLocale(DtLCX_OPER_INTERCHANGE_CODESET,
1592       special_locale,
1593       NULL,
1594       &ret_target);
1595    DtXlateStdToOpCodeset(DtLCX_OPER_MIME,
1596       ret_target,
1597       NULL,
1598       &ret_codeset);
1599  
1600    free(ret_locale);
1601    if (ret_lang)
1602      free(ret_lang);
1603    if (ret_target)
1604      free(ret_target);
1605
1606    return ret_codeset;
1607 }
1608
1609 // Given a message text and codesets
1610 // Convert message text from one codeset to another
1611 // Return 1 if conversion is successful else return 0.
1612
1613 int
1614 DtMail::Session::csConvert(char **bp, unsigned long &bp_len, int free_bp,
1615 char *from_cs, char *to_cs)
1616 {
1617    DtMailEnv error;
1618    iconv_t cd;
1619    size_t ileft = (size_t) bp_len, oleft = (size_t) bp_len, ret = 0;
1620 #if defined(_AIX) || defined(sun) || defined(CSRG_BASED)
1621    const char *ip = (const char *) *bp;
1622 #else
1623    char *ip = *bp;
1624 #endif
1625    char *op = NULL;
1626    char *op_start = NULL;
1627    int mb_ret = 0;
1628    size_t delta;
1629
1630    if ( *bp == NULL  ||  **bp == '\0'  ||  bp_len <= 0 )
1631           return 0;
1632    if ( to_cs == NULL  ||  from_cs == NULL )
1633           return 0;
1634    if ( (cd = iconv_open(to_cs, from_cs)) == (iconv_t) -1 ) {
1635           switch (errno) {
1636                 case EINVAL:
1637                   error.logError(DTM_FALSE,
1638                   "DtMail: Conversion from %s to %s is not supported.\n",
1639                   from_cs, to_cs);
1640           break;
1641           }   // end of switch statement
1642       return 0;
1643    }
1644    // Caller will set _must_free_body to DTM_TRUE if this routine
1645    // succeeds.  Then this space will be freed appropriately.
1646    // Add 1 to buffer size for null terminator.
1647    op_start = op = (char *)calloc((unsigned int) bp_len + 1, sizeof(char));
1648
1649    // When ileft finally reaches 0, the conversion still might not be
1650    // complete.  Here's why we also need to check for E2BIG:  Let's
1651    // say we're converting from eucJP to ISO-2022-JP, and there's just
1652    // enough room in the output buffer for the last input character,
1653    // but not enough room for the trailing "ESC ( B" (for switching
1654    // back to ASCII).  In that case, iconv() will convert the last
1655    // input character, decrement ileft to zero, and then set errno to
1656    // E2BIG to tell us that it still needs more room for the "ESC ( B".
1657    errno = 0;
1658    while ( ileft > 0 || errno == E2BIG ) {
1659       errno = 0;
1660       if ((ret = iconv(cd, &ip, &ileft, &op, &oleft)) == (size_t) -1) {
1661              switch (errno) {
1662                    case E2BIG:   // increase output buffer size
1663                          delta = ileft ? ileft : 3;
1664                          bp_len += delta;
1665                          op_start = (char *)realloc(
1666                                                 (char *)op_start,
1667                                                 (unsigned int) bp_len + 1); 
1668                          op = op_start + bp_len - delta - oleft;
1669                          oleft += delta;
1670                          // realloc does not clear out unused space.
1671                          // Therefore, garbage shows up in output buffer.
1672                          memset(op, 0, oleft + 1);
1673                          break;
1674                    case EILSEQ:  // input byte does not belong to input codeset
1675                    case EINVAL:  // invalid input
1676                          mb_ret = mblen(ip, MB_LEN_MAX);
1677                          if ( (mb_ret > 0) && (oleft >= mb_ret) ) {
1678                            strncat(op_start, ip, mb_ret);
1679                            ip += mb_ret;
1680                            op += mb_ret;
1681                            oleft -= mb_ret;
1682                            ileft -= mb_ret;
1683                            mb_ret = 0;
1684                          } else {
1685                            //  mb_ret is either 0 or -1 at this point,
1686                            //  then skip one byte
1687                            //  and try conversion again.
1688                            ip++;
1689                            ileft--;
1690                          }
1691                          break;
1692                    case EBADF:   // bad conversion descriptor
1693                          break;
1694              }   // end of switch statement
1695           }
1696    }  // end of while loop
1697    iconv_close(cd);
1698
1699    // Is this necessary??  Is _body_decode_len == strlen(_body)??
1700    // Or can _body_decode_len contain spaces??
1701
1702    // Check to see if a body had been allocated by prior decoding.
1703    if (free_bp) {
1704       free(*bp);
1705    }
1706    *bp = op_start;
1707    bp_len = strlen(*bp); 
1708
1709    return 1;
1710 }
1711
1712 // End of For CHARSET