1631 lines
39 KiB
C
1631 lines
39 KiB
C
/*
|
|
* CDE - Common Desktop Environment
|
|
*
|
|
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
|
|
*
|
|
* These libraries and programs are free software; you can
|
|
* redistribute them and/or modify them under the terms of the GNU
|
|
* Lesser General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* These libraries and programs are distributed in the hope that
|
|
* they will be useful, but WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU Lesser General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with these libraries and programs; if not, write
|
|
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
|
* Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
/*
|
|
*+SNOTICE
|
|
*
|
|
*
|
|
* $TOG: Session.C /main/16 1998/07/24 16:08:39 mgreess $
|
|
*
|
|
* RESTRICTED CONFIDENTIAL INFORMATION:
|
|
*
|
|
* The information in this document is subject to special
|
|
* restrictions in a confidential disclosure agreement bertween
|
|
* HP, IBM, Sun, USL, SCO and Univel. Do not distribute this
|
|
* document outside HP, IBM, Sun, USL, SCO, or Univel wihtout
|
|
* Sun's specific written approval. This documment and all copies
|
|
* and derivative works thereof must be returned or destroyed at
|
|
* Sun's request.
|
|
*
|
|
* Copyright 1993 Sun Microsystems, Inc. All rights reserved.
|
|
*
|
|
*+ENOTICE
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <pwd.h>
|
|
#include <sys/socket.h>
|
|
#include <ctype.h>
|
|
|
|
#include <DtMail/DtMail.hh>
|
|
|
|
#include <EUSDebug.hh>
|
|
#include "DynamicLib.hh"
|
|
#include <DtMail/Threads.hh>
|
|
#include <DtMail/ImplDriver.hh>
|
|
#include "ImplConfigTable.hh"
|
|
#include "SigChldImpl.hh"
|
|
#include <DtMail/IO.hh>
|
|
#include <DtMail/Common.h>
|
|
|
|
// For CHARSET
|
|
//-------------------------------------
|
|
// HACK ALERT
|
|
// Any code change within "For CHARSET" should be changed in
|
|
// RFCBodyPart and Session because the same methods are duplicated
|
|
// in both of these classes.
|
|
// See RFCImpl.hh or DtMail/DtMail.hh for more explanation.
|
|
//-------------------------------------
|
|
#include <locale.h>
|
|
#include <time.h>
|
|
#include <Dt/LocaleXlate.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
#include <ctype.h>
|
|
|
|
#ifndef True
|
|
#define True 1
|
|
#endif
|
|
#ifndef False
|
|
#define False 0
|
|
#endif
|
|
|
|
#if defined(SunOS) && (SunOS < 55)
|
|
extern "C" {
|
|
#endif
|
|
|
|
#include <iconv.h>
|
|
|
|
#if defined(SunOS) && (SunOS < 55)
|
|
}
|
|
#endif
|
|
|
|
//End of CHARSET
|
|
|
|
DtVirtArray<SigChldInfo *> *DtMailSigChldList;
|
|
void * _DtMutex;
|
|
|
|
static const int SessionSignature = 0xef9421cc;
|
|
static const int MaxImpls = 16;
|
|
|
|
static const int MAXIMUM_PATH_LENGTH = 2048;
|
|
|
|
DtMail::Session::Session(DtMailEnv & error, const char * app_name)
|
|
: _events(16), _valid_keys(2048)
|
|
{
|
|
_DtMutex = MutexInit();
|
|
|
|
error.clear();
|
|
|
|
_object_signature = 0;
|
|
_cur_key = 0;
|
|
|
|
// Create the ToolTalk session for managing file locking,
|
|
// if one doesn't exist.
|
|
_tt_channel = tt_default_procid();
|
|
if (tt_pointer_error(_tt_channel) != TT_OK) {
|
|
_tt_channel = ttdt_open(&_tt_fd, app_name, "SunSoft", "%I", 0);
|
|
if (tt_pointer_error(_tt_channel) != TT_OK) {
|
|
error.setError(DTME_TTFailure);
|
|
DebugPrintf(1,
|
|
"DtMail::createSession - ttdt_open returns %s\n",
|
|
tt_status_message(tt_pointer_error(_tt_channel)));
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
_tt_fd = tt_fd();
|
|
}
|
|
|
|
// The event_fd is how we allow async behavior to occur in a
|
|
// compatible way. We use a Unix domain socket as the file descriptor.
|
|
// The client will watch for activity on this file descriptor, and
|
|
// call our event routine when there is activity (either from XtMainLoop,
|
|
// or through some other method).
|
|
//
|
|
pipe(_event_fd);
|
|
|
|
_app_name = strdup(app_name);
|
|
|
|
DtMailEnv b_error;
|
|
|
|
_mail_rc = new MailRc(error, this);
|
|
|
|
buildImplTable(error);
|
|
if (error.isSet()) {
|
|
return;
|
|
}
|
|
|
|
_obj_mutex = MutexInit();
|
|
|
|
// The default implementation is specified via the DEFAULT_BACKEND
|
|
// variable. If this is not set in the .mailrc, then choose entry
|
|
// zero.
|
|
//
|
|
const char * value;
|
|
_mail_rc->getValue(b_error, "DEFAULT_BACKEND", &value);
|
|
if (b_error.isNotSet()) {
|
|
_default_impl = lookupImpl(value);
|
|
if (_default_impl < 0) {
|
|
_default_impl = 0;
|
|
}
|
|
}
|
|
else {
|
|
b_error.clear();
|
|
_default_impl = 0;
|
|
}
|
|
|
|
DtMailSigChldList = new DtVirtArray<SigChldInfo *>(8);
|
|
|
|
_busy_cb = NULL;
|
|
_busy_cb_data = NULL;
|
|
_canAutoSave = DTM_TRUE;
|
|
|
|
_object_signature = SessionSignature;
|
|
|
|
return;
|
|
}
|
|
|
|
DtMail::Session::~Session(void)
|
|
{
|
|
if (_object_signature != SessionSignature) { // Been here, did that!
|
|
return;
|
|
}
|
|
|
|
{
|
|
MutexLock lock_scope(_obj_mutex);
|
|
|
|
_object_signature = 0;
|
|
|
|
ttdt_close(_tt_channel, NULL, 1);
|
|
|
|
DtMailEnv b_error;
|
|
|
|
// Close off the dynamic libraries and free the impl table.
|
|
for (int tbl = 0; tbl < _num_impls; tbl++) {
|
|
free(_impls[tbl].impl_name);
|
|
DynamicLib * dl = (DynamicLib *)_impls[tbl].impl_lib;
|
|
delete dl;
|
|
}
|
|
|
|
free(_impls);
|
|
|
|
lock_scope.unlock_and_destroy();
|
|
delete _mail_rc;
|
|
|
|
close(_event_fd[0]);
|
|
close(_event_fd[1]);
|
|
}
|
|
|
|
}
|
|
|
|
const char **
|
|
DtMail::Session::enumerateImpls(DtMailEnv & error)
|
|
{
|
|
error.clear();
|
|
return(_impl_names);
|
|
}
|
|
|
|
void
|
|
DtMail::Session::setDefaultImpl(DtMailEnv & error, const char * impl)
|
|
{
|
|
int slot = lookupImpl(impl);
|
|
|
|
if (slot < 0) {
|
|
error.setError(DTME_NoSuchImplementation);
|
|
return;
|
|
}
|
|
|
|
MutexLock lock_scope(_obj_mutex);
|
|
|
|
_default_impl = slot;
|
|
error.clear();
|
|
}
|
|
|
|
const char *
|
|
DtMail::Session::getDefaultImpl(DtMailEnv & error)
|
|
{
|
|
if (_num_impls == 0) {
|
|
error.setError(DTME_NoImplementations);
|
|
return(NULL);
|
|
}
|
|
|
|
error.clear();
|
|
|
|
return(_impl_names[_default_impl]);
|
|
}
|
|
|
|
void
|
|
DtMail::Session::queryImpl(DtMailEnv & error,
|
|
const char * impl,
|
|
const char * capability,
|
|
...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, capability);
|
|
queryImplV(error, impl, capability, args);
|
|
va_end(args);
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
DtMail::Session::queryImplV(DtMailEnv & error,
|
|
const char * impl,
|
|
const char * capability,
|
|
va_list args)
|
|
{
|
|
int slot = lookupImpl(impl);
|
|
|
|
if (slot < 0) {
|
|
error.setError(DTME_NoSuchImplementation);
|
|
return;
|
|
}
|
|
|
|
error.clear();
|
|
|
|
// We need to retrieve the QueryImpl entry point for the implementation.
|
|
//
|
|
QueryImplEntry qie;
|
|
|
|
qie = (QueryImplEntry)_impls[slot].impl_meta_factory(QueryImplEntryOp);
|
|
if (!qie) {
|
|
error.setError(DTME_ImplFailure);
|
|
return;
|
|
}
|
|
|
|
qie(*this, error, capability, args);
|
|
|
|
return;
|
|
}
|
|
|
|
DtMail::MailBox *
|
|
DtMail::Session::mailBoxConstruct(DtMailEnv & error,
|
|
DtMailObjectSpace space,
|
|
void * arg,
|
|
DtMailCallback open_callback,
|
|
void * client_data,
|
|
const char * impl_name)
|
|
{
|
|
// If the client specified an implementation of choice, then that
|
|
// is the only thing we use.
|
|
//
|
|
int primary_impl = _default_impl;
|
|
|
|
if (impl_name) {
|
|
int sl = lookupImpl(impl_name);
|
|
if (sl < 0) {
|
|
error.setError(DTME_NoSuchImplementation);
|
|
return(NULL);
|
|
}
|
|
|
|
primary_impl = sl;
|
|
}
|
|
|
|
// First thing we will do is see if the default implementation
|
|
// can open this file. If so, then we will create a mail box object
|
|
// based on the default implementation.
|
|
//
|
|
int const_impl;
|
|
QueryOpenEntry qoe;
|
|
|
|
qoe = (QueryOpenEntry)
|
|
_impls[primary_impl].impl_meta_factory(QueryOpenEntryOp);
|
|
if (!qoe) {
|
|
error.setError(DTME_ImplFailure);
|
|
return(NULL);
|
|
}
|
|
|
|
if (qoe(*this, error, space, arg) == DTM_FALSE) {
|
|
// Don't go on if the client specified an implementation.
|
|
//
|
|
if (impl_name) {
|
|
error.setError(DTME_ImplFailure);
|
|
return(NULL);
|
|
}
|
|
|
|
// Oh well, let's walk through the list of impls and see if any of
|
|
// them will take ownership of this file.
|
|
//
|
|
MutexLock lock_scope(_obj_mutex);
|
|
|
|
const_impl = -1;
|
|
for(int slot = 0; slot < _num_impls; slot++) {
|
|
qoe = (QueryOpenEntry)
|
|
_impls[slot].impl_meta_factory(QueryOpenEntryOp);
|
|
if (!qoe) {
|
|
// Just skip this implementation.
|
|
continue;
|
|
}
|
|
if (qoe(*this, error, space, arg) == DTM_TRUE) {
|
|
const_impl = slot;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we didn't find an impl, then we have to give up.
|
|
//
|
|
if (const_impl < 0) {
|
|
error.setError(DTME_NotMailBox);
|
|
return(NULL);
|
|
}
|
|
}
|
|
else {
|
|
const_impl = primary_impl;
|
|
}
|
|
|
|
// At this point we have an implementation that is willing to work
|
|
// with the path. Get its mail box constructor and build a mailbox
|
|
// from it.
|
|
//
|
|
MailBoxConstructEntry mbce;
|
|
|
|
mbce = (MailBoxConstructEntry)
|
|
_impls[const_impl].impl_meta_factory(MailBoxConstructEntryOp);
|
|
if (!mbce) {
|
|
error.setError(DTME_ImplFailure);
|
|
return(NULL);
|
|
}
|
|
|
|
return(mbce(*this, error, space, arg, open_callback, client_data));
|
|
}
|
|
|
|
DtMail::Message *
|
|
DtMail::Session::messageConstruct(DtMailEnv & error,
|
|
DtMailObjectSpace space,
|
|
void * arg,
|
|
DtMailCallback open_callback,
|
|
void * client_data,
|
|
const char * impl_name)
|
|
{
|
|
// If the client specified an implementation of choice, then that
|
|
// is the only thing we use.
|
|
//
|
|
int primary_impl = _default_impl;
|
|
|
|
if (impl_name) {
|
|
int sl = lookupImpl(impl_name);
|
|
if (sl < 0) {
|
|
error.setError(DTME_NoSuchImplementation);
|
|
return(NULL);
|
|
}
|
|
|
|
primary_impl = sl;
|
|
}
|
|
|
|
int const_impl;
|
|
QueryMessageEntry qoe;
|
|
|
|
qoe = (QueryMessageEntry)
|
|
_impls[primary_impl].impl_meta_factory(QueryMessageEntryOp);
|
|
if (!qoe) {
|
|
error.setError(DTME_ImplFailure);
|
|
return(NULL);
|
|
}
|
|
|
|
if (qoe(*this, error, space, arg) == DTM_FALSE) {
|
|
// Don't go on if the client specified an implementation.
|
|
//
|
|
if (impl_name) {
|
|
error.setError(DTME_ImplFailure);
|
|
return(NULL);
|
|
}
|
|
|
|
// Oh well, let's walk through the list of impls and see if any of
|
|
// them will take ownership of this file.
|
|
//
|
|
MutexLock lock_scope(_obj_mutex);
|
|
|
|
const_impl = -1;
|
|
for(int slot = 0; slot < _num_impls; slot++) {
|
|
qoe = (QueryMessageEntry)
|
|
_impls[slot].impl_meta_factory(QueryMessageEntryOp);
|
|
if (!qoe) {
|
|
// Just skip this implementation.
|
|
continue;
|
|
}
|
|
if (qoe(*this, error, space, arg) == DTM_TRUE) {
|
|
const_impl = slot;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we didn't find an impl, then we have to give up.
|
|
//
|
|
if (const_impl < 0) {
|
|
error.setError(DTME_ImplFailure);
|
|
return(NULL);
|
|
}
|
|
}
|
|
else {
|
|
const_impl = primary_impl;
|
|
}
|
|
|
|
// At this point we have an implementation that is willing to work
|
|
// with the path. Get its mail box constructor and build a mailbox
|
|
// from it.
|
|
//
|
|
MessageConstructEntry mbce;
|
|
|
|
mbce = (MessageConstructEntry)
|
|
_impls[const_impl].impl_meta_factory(MessageConstructEntryOp);
|
|
if (!mbce) {
|
|
error.setError(DTME_ImplFailure);
|
|
return(NULL);
|
|
}
|
|
|
|
return(mbce(*this, error, space, arg, open_callback, client_data));
|
|
}
|
|
|
|
DtMail::Transport *
|
|
DtMail::Session::transportConstruct(DtMailEnv & error,
|
|
const char * impl,
|
|
DtMailStatusCallback call_back,
|
|
void * client_data)
|
|
{
|
|
// We need to find the implementation for starters.
|
|
//
|
|
int slot = lookupImpl(impl);
|
|
if (slot < 0) {
|
|
error.setError(DTME_NoSuchImplementation);
|
|
return(NULL);
|
|
}
|
|
|
|
TransportConstructEntry tce;
|
|
tce = (TransportConstructEntry)
|
|
_impls[slot].impl_meta_factory(TransportConstructEntryOp);
|
|
|
|
if (!tce) {
|
|
error.setError(DTME_NotSupported);
|
|
return(NULL);
|
|
}
|
|
|
|
return(tce(*this, error, call_back, client_data));
|
|
}
|
|
|
|
DtMail::MailRc *
|
|
DtMail::Session::mailRc(DtMailEnv & error)
|
|
{
|
|
error.clear();
|
|
|
|
return(_mail_rc);
|
|
}
|
|
|
|
//
|
|
// NEEDS TO BE DELETED .. SHOULD NO LONGER BE USED...
|
|
//
|
|
void
|
|
DtMail::Session::setError(DtMailEnv & error, const DTMailError_t minor_code)
|
|
{
|
|
error.setError(minor_code);
|
|
//DtMail::setError(*this, error, minor_code);
|
|
}
|
|
|
|
DtMailBoolean
|
|
DtMail::Session::pollRequired(DtMailEnv & error)
|
|
{
|
|
error.clear();
|
|
|
|
#if defined(POSIX_THREADS)
|
|
return(DTM_FALSE);
|
|
#else
|
|
return(DTM_TRUE);
|
|
#endif
|
|
|
|
}
|
|
|
|
int
|
|
DtMail::Session::eventFileDesc(DtMailEnv & error)
|
|
{
|
|
error.clear();
|
|
|
|
return(_event_fd[0]);
|
|
}
|
|
|
|
void
|
|
DtMail::Session::poll(DtMailEnv & error)
|
|
{
|
|
error.clear();
|
|
|
|
#if defined(POSIX_THREADS)
|
|
return; // A thread does this job.
|
|
#else
|
|
|
|
// We will grab the time and determine what needs to run.
|
|
// Any events that have expired since the last time we were
|
|
// called are automatically ran. If the event returns DTM_TRUE,
|
|
// then reset last_ran to the current time to cause the event
|
|
// to not run for the full wait interval; otherwise, refrain
|
|
// and let the event happen again at the next poll interval
|
|
//
|
|
time_t now = time(NULL);
|
|
|
|
for (int ev = 0; ev < _events.length(); ev++) {
|
|
EventRoutine * event = _events[ev];
|
|
if ((now - event->last_ran) > event->interval) {
|
|
if (event->routine(event->client_data) == DTM_TRUE)
|
|
event->last_ran = now;
|
|
}
|
|
}
|
|
|
|
return;
|
|
#endif
|
|
|
|
}
|
|
|
|
char *
|
|
DtMail::Session::expandPath(DtMailEnv & error, const char * path)
|
|
{
|
|
const char * fold_path;
|
|
|
|
if (path == NULL) {
|
|
error.setError(DTME_BadArg);
|
|
return(NULL);
|
|
}
|
|
|
|
error.clear();
|
|
|
|
char * exp_name = (char *)malloc(MAXIMUM_PATH_LENGTH);
|
|
if (exp_name == NULL) {
|
|
error.setError(DTME_NoMemory);
|
|
return(NULL);
|
|
}
|
|
if (strchr(path, '$') != NULL) {
|
|
sprintf (exp_name, "echo %s", path);
|
|
FILE *fp;
|
|
if ((fp = popen(exp_name, "r")) != NULL) {
|
|
exp_name[0] = '\0';
|
|
if (fgets(exp_name, MAXIMUM_PATH_LENGTH, fp) != NULL &&
|
|
exp_name[0] != '\0')
|
|
// get rid of \n at end of string
|
|
exp_name[strlen(exp_name)-1] = '\0';
|
|
else
|
|
strcpy(exp_name, path);
|
|
pclose(fp);
|
|
}
|
|
else
|
|
strcpy(exp_name, path);
|
|
}
|
|
else
|
|
strcpy(exp_name, path);
|
|
|
|
char * exp_name2 = (char *)malloc(MAXIMUM_PATH_LENGTH);
|
|
exp_name2[0] = '\0';
|
|
|
|
switch (exp_name[0]) {
|
|
case '+':
|
|
// This is relative to the folder path. Figure out what that is.
|
|
{
|
|
_mail_rc->getValue(error, "folder", &fold_path);
|
|
if (error.isNotSet()) {
|
|
if (*fold_path != '/' && *fold_path != '.' &&
|
|
*fold_path != '~' && *fold_path != '$')
|
|
strcpy(exp_name2, "~/");
|
|
|
|
strcat(exp_name2, fold_path);
|
|
strcat(exp_name2, "/");
|
|
}
|
|
else // Use the default folder
|
|
strcpy(exp_name2, "~/");
|
|
|
|
strcat(exp_name2, &exp_name[1]);
|
|
|
|
// We need to call ourselves again to deal with
|
|
// relative paths in the folder directory.
|
|
//
|
|
char * old_exp = exp_name2;
|
|
exp_name2 = expandPath(error, old_exp);
|
|
free(old_exp);
|
|
break;
|
|
}
|
|
|
|
case '~':
|
|
// This is relative to the user's home directory.
|
|
{
|
|
passwd pw;
|
|
const char * start;
|
|
|
|
if (exp_name[1] == '/' || exp_name[1] == '\0') {
|
|
GetPasswordEntry(pw);
|
|
start = &exp_name[1];
|
|
}
|
|
else {
|
|
passwd * pw_p;
|
|
|
|
char * slash = strchr(&exp_name[1], '/');
|
|
if (slash == NULL) {
|
|
error.clear();
|
|
error.setError(DTME_NoSuchFile);
|
|
break;
|
|
}
|
|
|
|
int len = slash - &exp_name[1];
|
|
char * name = new char[len + 1];
|
|
strncpy(name, &exp_name[1], len);
|
|
name[len] = 0;
|
|
pw_p = getpwnam(name);
|
|
|
|
if (!pw_p) {
|
|
error.clear();
|
|
error.setError(DTME_NoSuchFile);
|
|
break;
|
|
}
|
|
pw = *pw_p;
|
|
|
|
delete [] name;
|
|
|
|
start = slash;
|
|
}
|
|
|
|
strcpy(exp_name2, pw.pw_dir);
|
|
strcat(exp_name2, start);
|
|
break;
|
|
}
|
|
|
|
// We have a directory or no specials. Just copy the path and
|
|
// return.
|
|
case '.':
|
|
case '/':
|
|
default:
|
|
strcpy(exp_name2, exp_name);
|
|
break;
|
|
|
|
}
|
|
free(exp_name);
|
|
return(exp_name2);
|
|
}
|
|
|
|
// This routine takes a path and checks to see if the path can be
|
|
// expressed relative to the "folder" path. If it can, it returns
|
|
// the relative path; otherwise, it returns the original path.
|
|
char *
|
|
DtMail::Session::getRelativePath(DtMailEnv & error, const char * path)
|
|
{
|
|
const char * fold_path;
|
|
|
|
if (path == NULL) {
|
|
error.setError(DTME_BadArg);
|
|
return(NULL);
|
|
}
|
|
|
|
error.clear();
|
|
|
|
char * exp_name = (char *)malloc(MAXIMUM_PATH_LENGTH);
|
|
if (!exp_name) {
|
|
error.setError(DTME_NoMemory);
|
|
return(NULL);
|
|
}
|
|
exp_name[0] = '\0'; // Just for errors.
|
|
|
|
switch (path[0]) {
|
|
case '/':
|
|
// This is an absolute path, so there is a chance that
|
|
// we can trim it down to a relative path if it goes down
|
|
// the same way as the folder path.
|
|
{
|
|
_mail_rc->getValue(error, "folder", &fold_path);
|
|
if (error.isNotSet()) {
|
|
strcpy(exp_name, fold_path);
|
|
|
|
// We need to call ourselves again to deal with
|
|
// relative paths in the folder directory.
|
|
//
|
|
char * old_exp = exp_name;
|
|
exp_name = expandPath(error, old_exp);
|
|
free(old_exp);
|
|
|
|
// Check to see if the path starts with the folder path.
|
|
char * matched_path = const_cast<char *>(strstr(path, exp_name));
|
|
if (matched_path == path) {
|
|
// Yes it does, make it a relative path to the folder dir.
|
|
int folder_path_length = strlen(exp_name);
|
|
while (path[folder_path_length] == '/')
|
|
folder_path_length++;
|
|
strcpy(exp_name, &path[folder_path_length]);
|
|
break;
|
|
} else {
|
|
strcpy(exp_name, path);
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
// There is no folder variable so just fall through to the
|
|
// default.
|
|
error.clear();
|
|
}
|
|
}
|
|
case '+':
|
|
// This is relative to the folder path. Leave it alone.
|
|
// The only time we are likely to see a leading '+' is
|
|
// when the path was carried over from mailtool in the .mailrc.
|
|
case '~':
|
|
// This is relative to the user's home directory. Leave it alone.
|
|
// The only time we are likely to see a leading '~' is
|
|
// when the path was carried over from mailtool in the .mailrc.
|
|
case '.':
|
|
// This is relative to the current directory where dtmail is
|
|
// running. Leave it alone. The only time we are likely to see
|
|
// a leading '.' is when the path was carried over from mailtool
|
|
// in the .mailrc.
|
|
default:
|
|
{
|
|
strcpy(exp_name, path);
|
|
break;
|
|
}
|
|
}
|
|
return(exp_name);
|
|
|
|
}
|
|
|
|
|
|
void
|
|
DtMail::Session::addEventRoutine(DtMailEnv & error,
|
|
DtMailEventFunc routine,
|
|
void * client_data,
|
|
time_t interval)
|
|
{
|
|
error.clear();
|
|
|
|
EventRoutine * er = new EventRoutine;
|
|
|
|
er->routine = routine;
|
|
er->client_data = client_data;
|
|
er->interval = interval;
|
|
er->last_ran = 0; // This will case this routine to run immediately.
|
|
|
|
_events.append(er);
|
|
}
|
|
|
|
void
|
|
DtMail::Session::removeEventRoutine(DtMailEnv & error,
|
|
DtMailEventFunc routine,
|
|
void * client_data)
|
|
{
|
|
error.clear();
|
|
|
|
for (int slot = 0; slot < _events.length(); slot++) {
|
|
EventRoutine * event = _events[slot];
|
|
if (event->routine == routine &&
|
|
event->client_data == client_data) {
|
|
delete event;
|
|
_events.remove(slot);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
DtMail::Session::writeEventData(DtMailEnv&,
|
|
const void * buf,
|
|
const unsigned long size)
|
|
{
|
|
int status = SafeWrite(_event_fd[1], buf, (int)size);
|
|
}
|
|
|
|
DtMailBoolean
|
|
DtMail::Session::validObjectKey(DtMailObjectKey key)
|
|
{
|
|
return(_valid_keys.indexof(key) < 0 ? DTM_FALSE : DTM_TRUE);
|
|
}
|
|
|
|
DtMailObjectKey
|
|
DtMail::Session::newObjectKey(void)
|
|
{
|
|
MutexLock lock_scope(_obj_mutex);
|
|
|
|
_cur_key += 1;
|
|
_valid_keys.append(_cur_key);
|
|
|
|
return(_cur_key);
|
|
}
|
|
|
|
void
|
|
DtMail::Session::removeObjectKey(DtMailObjectKey key)
|
|
{
|
|
MutexLock lock_scope(_obj_mutex);
|
|
|
|
int slot = _valid_keys.indexof(key);
|
|
if (slot >= 0) {
|
|
_valid_keys.remove(slot);
|
|
}
|
|
}
|
|
|
|
void
|
|
DtMail::Session::registerDisableGroupPrivilegesCallback(
|
|
DisableGroupPrivilegesCallback cb,
|
|
void * cb_data)
|
|
{
|
|
_disableGroupPrivileges_cb = cb;
|
|
_disableGroupPrivileges_cb_data = cb_data;
|
|
}
|
|
|
|
void
|
|
DtMail::Session::disableGroupPrivileges(void)
|
|
{
|
|
if (_disableGroupPrivileges_cb) {
|
|
_disableGroupPrivileges_cb(_disableGroupPrivileges_cb_data);
|
|
}
|
|
}
|
|
|
|
void
|
|
DtMail::Session::registerEnableGroupPrivilegesCallback(
|
|
EnableGroupPrivilegesCallback cb,
|
|
void * cb_data)
|
|
{
|
|
_enableGroupPrivileges_cb = cb;
|
|
_enableGroupPrivileges_cb_data = cb_data;
|
|
}
|
|
|
|
void
|
|
DtMail::Session::enableGroupPrivileges(void)
|
|
{
|
|
if (_enableGroupPrivileges_cb) {
|
|
_enableGroupPrivileges_cb(_enableGroupPrivileges_cb_data);
|
|
}
|
|
}
|
|
|
|
void
|
|
DtMail::Session::registerBusyCallback(DtMailEnv&,
|
|
BusyApplicationCallback cb,
|
|
void * cb_data)
|
|
{
|
|
_busy_cb = cb;
|
|
_busy_cb_data = cb_data;
|
|
}
|
|
|
|
void
|
|
DtMail::Session::setBusyState(DtMailEnv &error, DtMailBusyState busy_state)
|
|
{
|
|
if (_busy_cb) {
|
|
_busy_cb(error, busy_state, _busy_cb_data);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
DtMail::Session::registerLastInteractiveEventTimeCallback(
|
|
LastInteractiveEventTimeCallback cb,
|
|
void * cb_data)
|
|
{
|
|
_interactive_time_cb = cb;
|
|
_interactive_time_cb_data = cb_data;
|
|
}
|
|
|
|
long
|
|
DtMail::Session::lastInteractiveEventTime(void)
|
|
{
|
|
if (_interactive_time_cb) {
|
|
return(_interactive_time_cb(_interactive_time_cb_data));
|
|
}
|
|
else
|
|
return(0);
|
|
}
|
|
|
|
void
|
|
DtMail::Session::buildImplTable(DtMailEnv & error)
|
|
{
|
|
error.clear();
|
|
|
|
// Let's pick a ridiculous number of implementations.
|
|
_impls = (Impls *)malloc(sizeof(Impls) * MaxImpls);
|
|
_impl_names = (const char **)malloc(sizeof(char *) * (MaxImpls + 1));
|
|
|
|
// We will simply walk through the default implementations
|
|
// to start, adding them to the impl table.
|
|
int tbl;
|
|
for (tbl = 0, _num_impls = 0; initial_impls[tbl].meta_entry_point; tbl++) {
|
|
// Get the library handle.
|
|
DynamicLib * dl = CreatePlatformDl(initial_impls[tbl].lib_name);
|
|
|
|
if (dl) { // We are only interested in libraries we can load.
|
|
_impls[_num_impls].impl_lib = dl;
|
|
_impls[_num_impls].impl_meta_factory =
|
|
(MetaImplFactory)dl->getSym(initial_impls[tbl].meta_entry_point);
|
|
if (_impls[_num_impls].impl_meta_factory == NULL) {
|
|
delete dl;
|
|
continue;
|
|
}
|
|
_impls[_num_impls].impl_name = strdup(initial_impls[tbl].impl_name);
|
|
_impl_names[_num_impls] = _impls[_num_impls].impl_name;
|
|
_num_impls += 1;
|
|
}
|
|
}
|
|
|
|
_impl_names[_num_impls] = NULL;
|
|
|
|
if (_num_impls == 0) {
|
|
error.setError(DTME_NoImplementations);
|
|
}
|
|
}
|
|
|
|
int
|
|
DtMail::Session::lookupImpl(const char * impl)
|
|
{
|
|
MutexLock lock_scope(_obj_mutex);
|
|
for(int i = 0; i < _num_impls; i++) {
|
|
if (strcmp(_impls[i].impl_name, impl) == 0) {
|
|
return(i);
|
|
}
|
|
}
|
|
|
|
return(-1);
|
|
}
|
|
|
|
void
|
|
DtMail::Session::setAutoSaveFlag(
|
|
DtMailBoolean flag
|
|
)
|
|
{
|
|
_canAutoSave = flag;
|
|
}
|
|
|
|
DtMailBoolean
|
|
DtMail::Session::getAutoSaveFlag()
|
|
{
|
|
return(_canAutoSave);
|
|
}
|
|
|
|
extern "C" void
|
|
ChildExitNotify(const int pid, const int status)
|
|
{
|
|
// We need to lookup the child, and set the status of the
|
|
// correct condition variable. We will remove the slot, but
|
|
// the thread will destroy the buffer in the slot.
|
|
//
|
|
for (int slot = 0; slot < DtMailSigChldList->length(); slot++) {
|
|
if ((*DtMailSigChldList)[slot]->pid == pid) {
|
|
(*DtMailSigChldList)[slot]->cond = status;
|
|
DtMailSigChldList->remove(slot);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Not finding the pid is not a problem. Just means it wasn't
|
|
// one of ours.
|
|
//
|
|
return;
|
|
}
|
|
|
|
|
|
// For CHARSET
|
|
/*
|
|
* Wrapper functions taken from libHelp/CEUtil.c
|
|
*
|
|
* We took these functions and renamed them because
|
|
* 1. Originally these are called _DtHelpCeXlate* and thus they are private
|
|
* to libHelp and not exported to outside of libHelp.
|
|
* 2. When these functions are moved to another library, then users of these
|
|
* functions would only need to link with a different library. The caller
|
|
* doesn't have to modify code.
|
|
*/
|
|
|
|
static const char *DfltStdCharset = "us-ascii";
|
|
static const char *DfltStdLang = "C";
|
|
|
|
static char MyPlatform[_DtPLATFORM_MAX_LEN+1];
|
|
static _DtXlateDb MyDb = NULL;
|
|
static char MyProcess = False;
|
|
static char MyFirst = True;
|
|
static int ExecVer;
|
|
static int CompVer;
|
|
|
|
|
|
/******************************************************************************
|
|
* Function: static int OpenLcxDb ()
|
|
*
|
|
* Parameters: none
|
|
*
|
|
* Return Value: 0: ok
|
|
* -1: error
|
|
*
|
|
* errno Values:
|
|
*
|
|
* Purpose: Opens the Ce-private Lcx database
|
|
*
|
|
*****************************************************************************/
|
|
int
|
|
DtMail::Session::OpenLcxDb (void)
|
|
{
|
|
time_t time1 = 0;
|
|
time_t time2 = 0;
|
|
|
|
while (MyProcess == True)
|
|
{
|
|
/* if time out, return */
|
|
if (time(&time2) == (time_t)-1)
|
|
return -1;
|
|
|
|
if (time1 == 0)
|
|
time1 = time2;
|
|
else if (time2 - time1 >= (time_t)30)
|
|
return -1;
|
|
}
|
|
|
|
if (MyFirst == True)
|
|
{
|
|
MyProcess = True;
|
|
if (_DtLcxOpenAllDbs(&MyDb) == 0 &&
|
|
_DtXlateGetXlateEnv(MyDb,MyPlatform,&ExecVer,&CompVer) != 0)
|
|
{
|
|
_DtLcxCloseDb(&MyDb);
|
|
MyDb = NULL;
|
|
}
|
|
MyFirst = False;
|
|
MyProcess = False;
|
|
}
|
|
|
|
return (MyDb == NULL ? -1 : 0 );
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Function: int DtXlateOpToStdLocale(char *operation, char *opLocale,
|
|
* char **ret_stdLocale,
|
|
* char **ret_stdLang,
|
|
* char **ret_stdSet)
|
|
*
|
|
* Parameters:
|
|
* operation Operation associated with the locale value
|
|
* opLocale An operation-specific locale string
|
|
* ret_locale Returns the std locale
|
|
* Caller must free this string.
|
|
* ret_stdLang Returns the std language & territory string.
|
|
* Caller must free this string.
|
|
* ret_stdSet Returns the std code set string.
|
|
* Caller must free this string.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Purpose: Gets the standard locale given an operation and its locale
|
|
*
|
|
*****************************************************************************/
|
|
void
|
|
DtMail::Session::DtXlateOpToStdLocale (
|
|
char *operation,
|
|
char *opLocale,
|
|
char **ret_stdLocale,
|
|
char **ret_stdLang,
|
|
char **ret_stdSet)
|
|
{
|
|
int result = OpenLcxDb();
|
|
|
|
if (result == 0) {
|
|
(void) _DtLcxXlateOpToStd(
|
|
MyDb, MyPlatform, CompVer,
|
|
operation, opLocale,
|
|
ret_stdLocale, ret_stdLang, ret_stdSet, NULL);
|
|
}
|
|
|
|
/* if failed, give default values */
|
|
if (ret_stdLocale != NULL && (result != 0 || *ret_stdLocale == NULL))
|
|
{
|
|
*ret_stdLocale =
|
|
(char *)malloc(strlen(DfltStdLang)+strlen(DfltStdCharset)+3);
|
|
sprintf(*ret_stdLocale,"%s.%s",DfltStdLang,DfltStdCharset);
|
|
}
|
|
|
|
if (ret_stdLang != NULL && (result != 0 || *ret_stdLang == NULL))
|
|
*ret_stdLang = (char *)strdup(DfltStdLang);
|
|
if (ret_stdSet != NULL && (result != 0 || *ret_stdSet == NULL))
|
|
*ret_stdSet = (char *)strdup(DfltStdCharset);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Function: int DtXlateStdToOpLocale(char *operation,
|
|
* char *stdLocale,
|
|
* char *dflt_opLocale,
|
|
* char **ret_opLocale)
|
|
*
|
|
* Parameters:
|
|
* operation operation whose locale value will be retrieved
|
|
* stdLocale standard locale value
|
|
* dflt_opLocale operation-specific locale-value
|
|
* This is the default value used in error case
|
|
* ret_opLocale operation-specific locale-value placed here
|
|
* Caller must free this string.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Purpose: Gets an operation-specific locale string given the standard string
|
|
*
|
|
*****************************************************************************/
|
|
void
|
|
DtMail::Session::DtXlateStdToOpLocale (
|
|
char *operation,
|
|
char *stdLocale,
|
|
char *dflt_opLocale,
|
|
char **ret_opLocale)
|
|
{
|
|
int result = this->OpenLcxDb();
|
|
|
|
if (ret_opLocale)
|
|
*ret_opLocale = NULL;
|
|
|
|
if (result == 0) {
|
|
(void) _DtLcxXlateStdToOp(
|
|
MyDb, MyPlatform, CompVer,
|
|
operation, stdLocale,
|
|
NULL, NULL, NULL,
|
|
ret_opLocale);
|
|
}
|
|
|
|
/* if translation fails, use a default value */
|
|
if (ret_opLocale && (result != 0 || *ret_opLocale == NULL))
|
|
{
|
|
if (dflt_opLocale) *ret_opLocale = (char *)strdup(dflt_opLocale);
|
|
else if (stdLocale) *ret_opLocale = (char *)strdup(stdLocale);
|
|
}
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* Function: int DtXlateStdToOpCodeset (
|
|
* char *operation,
|
|
* char *stdCodeset,
|
|
* char *dflt_opCodeset,
|
|
* char **ret_opCodeset)
|
|
*
|
|
* Parameters:
|
|
* operation operation whose codeset value will be retrieved
|
|
* stdCodeset standard codeset value
|
|
* dflt_opCodeset operation-specific codeset-value
|
|
* This is the default value used in error case
|
|
* ret_opCodeset operation-specific codeset-value placed here
|
|
* Caller must free this string.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Purpose: Gets an operation-specific locale string given the standard string
|
|
*
|
|
*****************************************************************************/
|
|
void
|
|
DtMail::Session::DtXlateStdToOpCodeset (
|
|
char *operation,
|
|
char *stdCodeset,
|
|
char *dflt_opCodeset,
|
|
char **ret_opCodeset)
|
|
{
|
|
int result = this->OpenLcxDb();
|
|
|
|
if (ret_opCodeset)
|
|
*ret_opCodeset = NULL;
|
|
|
|
if (result == 0)
|
|
{
|
|
(void) _DtLcxXlateStdToOp(
|
|
MyDb, MyPlatform, CompVer,
|
|
operation,
|
|
NULL, NULL, stdCodeset, NULL,
|
|
ret_opCodeset);
|
|
}
|
|
|
|
/* if translation fails, use a default value */
|
|
if (ret_opCodeset && (result != 0 || *ret_opCodeset == NULL))
|
|
{
|
|
if (dflt_opCodeset) *ret_opCodeset = (char *)strdup(dflt_opCodeset);
|
|
else if (stdCodeset) *ret_opCodeset = (char *)strdup(stdCodeset);
|
|
}
|
|
}
|
|
|
|
void
|
|
DtMail::Session::DtXlateMimeToIconv(
|
|
const char *mimeId,
|
|
const char *defaultCommonCS,
|
|
const char *defaultIconvCS,
|
|
char **ret_commonCS,
|
|
char **ret_platformIconv)
|
|
{
|
|
int exists = -1;
|
|
|
|
this->OpenLcxDb();
|
|
|
|
exists = _DtLcxXlateOpToStd(
|
|
MyDb, MyPlatform, CompVer,
|
|
DtLCX_OPER_MIME, mimeId,
|
|
NULL, NULL, ret_commonCS, NULL);
|
|
|
|
if (exists == -1)
|
|
{
|
|
exists = _DtLcxXlateOpToStd(
|
|
MyDb, "CDE", 0,
|
|
DtLCX_OPER_MIME, mimeId,
|
|
NULL, NULL, ret_commonCS, NULL);
|
|
if (exists == -1)
|
|
*ret_commonCS = (char *)strdup(defaultCommonCS);
|
|
}
|
|
|
|
exists = _DtLcxXlateStdToOp(
|
|
MyDb, MyPlatform, CompVer,
|
|
DtLCX_OPER_ICONV3,
|
|
NULL, NULL, *ret_commonCS, NULL,
|
|
ret_platformIconv);
|
|
if (exists == -1)
|
|
*ret_platformIconv = (char *)strdup(defaultIconvCS);
|
|
}
|
|
|
|
void
|
|
DtMail::Session::DtXlateLocaleToMime(
|
|
const char * locale,
|
|
const char * defaultCommonCS,
|
|
const char * defaultMimeCS,
|
|
char ** ret_mimeCS)
|
|
{
|
|
char * commonCS = NULL;
|
|
|
|
this->OpenLcxDb();
|
|
|
|
/* look for platform-specific locale to CDE translation */
|
|
_DtLcxXlateOpToStd(
|
|
MyDb, MyPlatform, CompVer,
|
|
DtLCX_OPER_SETLOCALE, locale,
|
|
NULL, NULL, &commonCS, NULL);
|
|
if (!commonCS)
|
|
commonCS = (char *)strdup(defaultCommonCS);
|
|
|
|
/* look for platform-specific MIME types; by default, there is none */
|
|
_DtLcxXlateStdToOp(
|
|
MyDb, MyPlatform, CompVer,
|
|
DtLCX_OPER_MIME,
|
|
NULL, NULL, commonCS, NULL,
|
|
ret_mimeCS);
|
|
if (!(*ret_mimeCS))
|
|
{
|
|
_DtLcxXlateStdToOp(
|
|
MyDb, "CDE", 0,
|
|
DtLCX_OPER_MIME,
|
|
NULL, NULL, commonCS, NULL,
|
|
ret_mimeCS);
|
|
if (!(*ret_mimeCS))
|
|
*ret_mimeCS = (char *)strdup(defaultMimeCS);
|
|
}
|
|
|
|
if (commonCS)
|
|
free(commonCS);
|
|
}
|
|
|
|
// Return iconv name of the given codeset.
|
|
// If iconv name does not exist, return NULL.
|
|
char *
|
|
DtMail::Session::csToConvName(char *cs)
|
|
{
|
|
int exists = -1;
|
|
char *commonCS = NULL;
|
|
char *convName = NULL;
|
|
char *ret_target = NULL;
|
|
|
|
this->OpenLcxDb();
|
|
|
|
// Convert charset to upper case first because charset table is
|
|
// case sensitive.
|
|
if (cs)
|
|
{
|
|
int len_cs = strlen(cs);
|
|
for ( int num_cs = 0; num_cs < len_cs; num_cs++ )
|
|
*(cs+num_cs) = toupper(*(cs+num_cs));
|
|
}
|
|
exists = _DtLcxXlateOpToStd(
|
|
MyDb, MyPlatform, CompVer,
|
|
DtLCX_OPER_MIME, cs,
|
|
NULL, NULL, &commonCS, NULL);
|
|
if (exists == -1) {
|
|
exists = _DtLcxXlateOpToStd(
|
|
MyDb, "CDE", 0,
|
|
DtLCX_OPER_MIME, cs,
|
|
NULL, NULL, &commonCS, NULL);
|
|
if (exists == -1)
|
|
return NULL;
|
|
}
|
|
|
|
DtXlateStdToOpCodeset(DtLCX_OPER_INTERCHANGE_CODESET,
|
|
commonCS,
|
|
NULL,
|
|
&ret_target);
|
|
DtXlateStdToOpCodeset(DtLCX_OPER_ICONV3,
|
|
ret_target,
|
|
NULL,
|
|
&convName);
|
|
|
|
if ( ret_target )
|
|
free( ret_target );
|
|
if ( commonCS )
|
|
free( commonCS );
|
|
|
|
// Workaround for libDtHelp
|
|
// Case of no iconv name for a particular locale, eg. C,
|
|
// check for empty string.
|
|
if ( convName != NULL )
|
|
{
|
|
if ( strlen(convName) > 0 )
|
|
return convName;
|
|
else
|
|
free( convName );
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// Return current locale's iconv name.
|
|
char *
|
|
DtMail::Session::locToConvName()
|
|
{
|
|
char *ret_locale = NULL;
|
|
char *ret_lang = NULL;
|
|
char *ret_codeset = NULL;
|
|
|
|
DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
|
|
setlocale(LC_CTYPE, NULL),
|
|
&ret_locale,
|
|
&ret_lang,
|
|
&ret_codeset);
|
|
|
|
if (ret_codeset) {
|
|
free(ret_codeset);
|
|
ret_codeset = NULL;
|
|
}
|
|
|
|
if (ret_lang) {
|
|
free(ret_lang);
|
|
ret_lang = NULL;
|
|
}
|
|
|
|
DtXlateStdToOpLocale(DtLCX_OPER_ICONV3,
|
|
ret_locale,
|
|
NULL,
|
|
&ret_codeset);
|
|
|
|
if (ret_locale)
|
|
free(ret_locale);
|
|
|
|
// Workaround for libDtHelp
|
|
// Case of no iconv name for a particular locale, eg. C,
|
|
// check for empty string.
|
|
if ( ret_codeset != NULL )
|
|
{
|
|
if ( strlen(ret_codeset) > 0 )
|
|
return ret_codeset;
|
|
else
|
|
free(ret_codeset);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// Return target codeset's iconv name.
|
|
char *
|
|
DtMail::Session::targetConvName()
|
|
{
|
|
char *ret_locale = NULL;
|
|
char *ret_lang = NULL;
|
|
char *ret_codeset = NULL;
|
|
char *ret_target = NULL;
|
|
char *ret_convName = NULL;
|
|
|
|
DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
|
|
setlocale(LC_CTYPE, NULL),
|
|
&ret_locale,
|
|
&ret_lang,
|
|
&ret_codeset);
|
|
DtXlateStdToOpLocale(DtLCX_OPER_INTERCHANGE_CODESET,
|
|
ret_locale,
|
|
NULL,
|
|
&ret_target);
|
|
// Or do I call csToConvName() here??
|
|
DtXlateStdToOpCodeset(DtLCX_OPER_ICONV3,
|
|
ret_target,
|
|
NULL,
|
|
&ret_convName);
|
|
|
|
if (ret_locale)
|
|
free(ret_locale);
|
|
if (ret_lang)
|
|
free(ret_lang);
|
|
if (ret_codeset)
|
|
free(ret_codeset);
|
|
if (ret_target)
|
|
free(ret_target);
|
|
|
|
// Workaround for libDtHelp
|
|
// Case of no iconv name for a particular locale, eg. C,
|
|
// check for empty string.
|
|
if ( ret_convName != NULL )
|
|
{
|
|
if ( strlen(ret_convName) > 0 )
|
|
return ret_convName;
|
|
else
|
|
free(ret_convName);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// Return target codeset's MIME (tag) name.
|
|
char *
|
|
DtMail::Session::targetTagName()
|
|
{
|
|
char *ret_locale = NULL;
|
|
char *ret_lang = NULL;
|
|
char *ret_codeset = NULL;
|
|
char *ret_target = NULL;
|
|
|
|
DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
|
|
setlocale(LC_CTYPE, NULL),
|
|
&ret_locale,
|
|
&ret_lang,
|
|
NULL);
|
|
DtXlateStdToOpLocale(DtLCX_OPER_INTERCHANGE_CODESET,
|
|
ret_locale,
|
|
NULL,
|
|
&ret_target);
|
|
DtXlateStdToOpCodeset(DtLCX_OPER_MIME,
|
|
ret_target,
|
|
NULL,
|
|
&ret_codeset);
|
|
|
|
if (ret_locale)
|
|
free(ret_locale);
|
|
if (ret_lang)
|
|
free(ret_lang);
|
|
if (ret_target)
|
|
free(ret_target);
|
|
|
|
return ret_codeset;
|
|
}
|
|
|
|
// Given an extension to the interchange codeset name.
|
|
// Return target codeset's MIME (tag) name.
|
|
// The extension is for Sun V3 backward compatibility so that
|
|
// reverse mapping of out-going charset tag is the same as
|
|
// OpenWindows Mailtool.
|
|
char *
|
|
DtMail::Session::targetTagName(char *special)
|
|
{
|
|
char *ret_locale = NULL;
|
|
char *ret_lang = NULL;
|
|
char *ret_codeset = NULL;
|
|
char *ret_target = NULL;
|
|
|
|
DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
|
|
setlocale(LC_CTYPE, NULL),
|
|
&ret_locale,
|
|
&ret_lang,
|
|
&ret_codeset);
|
|
|
|
// Allocate two more bytes for "." and null terminator.
|
|
char *special_locale;
|
|
|
|
special_locale = (char *)calloc(
|
|
strlen(ret_locale) + strlen(special) + 2,
|
|
sizeof(char));
|
|
sprintf(special_locale, "%s%s%s", ret_locale, ".", special);
|
|
|
|
DtXlateStdToOpLocale(DtLCX_OPER_INTERCHANGE_CODESET,
|
|
special_locale,
|
|
NULL,
|
|
&ret_target);
|
|
DtXlateStdToOpCodeset(DtLCX_OPER_MIME,
|
|
ret_target,
|
|
NULL,
|
|
&ret_codeset);
|
|
|
|
free(ret_locale);
|
|
if (ret_lang)
|
|
free(ret_lang);
|
|
if (ret_target)
|
|
free(ret_target);
|
|
|
|
return ret_codeset;
|
|
}
|
|
|
|
// Given a message text and codesets
|
|
// Convert message text from one codeset to another
|
|
// Return 1 if conversion is successful else return 0.
|
|
|
|
int
|
|
DtMail::Session::csConvert(char **bp, unsigned long &bp_len, int free_bp,
|
|
char *from_cs, char *to_cs)
|
|
{
|
|
DtMailEnv error;
|
|
iconv_t cd;
|
|
size_t ileft = (size_t) bp_len, oleft = (size_t) bp_len, ret = 0;
|
|
#if defined(_AIX) || defined(sun) || defined(CSRG_BASED)
|
|
const char *ip = (const char *) *bp;
|
|
#else
|
|
char *ip = *bp;
|
|
#endif
|
|
char *op = NULL;
|
|
char *op_start = NULL;
|
|
int mb_ret = 0;
|
|
size_t delta;
|
|
|
|
if ( *bp == NULL || **bp == '\0' || bp_len <= 0 )
|
|
return 0;
|
|
if ( to_cs == NULL || from_cs == NULL )
|
|
return 0;
|
|
if ( (cd = iconv_open(to_cs, from_cs)) == (iconv_t) -1 ) {
|
|
switch (errno) {
|
|
case EINVAL:
|
|
error.logError(DTM_FALSE,
|
|
"DtMail: Conversion from %s to %s is not supported.\n",
|
|
from_cs, to_cs);
|
|
break;
|
|
} // end of switch statement
|
|
return 0;
|
|
}
|
|
// Caller will set _must_free_body to DTM_TRUE if this routine
|
|
// succeeds. Then this space will be freed appropriately.
|
|
// Add 1 to buffer size for null terminator.
|
|
op_start = op = (char *)calloc((unsigned int) bp_len + 1, sizeof(char));
|
|
|
|
// When ileft finally reaches 0, the conversion still might not be
|
|
// complete. Here's why we also need to check for E2BIG: Let's
|
|
// say we're converting from eucJP to ISO-2022-JP, and there's just
|
|
// enough room in the output buffer for the last input character,
|
|
// but not enough room for the trailing "ESC ( B" (for switching
|
|
// back to ASCII). In that case, iconv() will convert the last
|
|
// input character, decrement ileft to zero, and then set errno to
|
|
// E2BIG to tell us that it still needs more room for the "ESC ( B".
|
|
errno = 0;
|
|
while ( ileft > 0 || errno == E2BIG ) {
|
|
errno = 0;
|
|
if ((ret = iconv(cd, &ip, &ileft, &op, &oleft)) == (size_t) -1) {
|
|
switch (errno) {
|
|
case E2BIG: // increase output buffer size
|
|
delta = ileft ? ileft : 3;
|
|
bp_len += delta;
|
|
op_start = (char *)realloc(
|
|
(char *)op_start,
|
|
(unsigned int) bp_len + 1);
|
|
op = op_start + bp_len - delta - oleft;
|
|
oleft += delta;
|
|
// realloc does not clear out unused space.
|
|
// Therefore, garbage shows up in output buffer.
|
|
memset(op, 0, oleft + 1);
|
|
break;
|
|
case EILSEQ: // input byte does not belong to input codeset
|
|
case EINVAL: // invalid input
|
|
mb_ret = mblen(ip, MB_LEN_MAX);
|
|
if ( (mb_ret > 0) && (oleft >= mb_ret) ) {
|
|
strncat(op_start, ip, mb_ret);
|
|
ip += mb_ret;
|
|
op += mb_ret;
|
|
oleft -= mb_ret;
|
|
ileft -= mb_ret;
|
|
mb_ret = 0;
|
|
} else {
|
|
// mb_ret is either 0 or -1 at this point,
|
|
// then skip one byte
|
|
// and try conversion again.
|
|
ip++;
|
|
ileft--;
|
|
}
|
|
break;
|
|
case EBADF: // bad conversion descriptor
|
|
break;
|
|
} // end of switch statement
|
|
}
|
|
} // end of while loop
|
|
iconv_close(cd);
|
|
|
|
// Is this necessary?? Is _body_decode_len == strlen(_body)??
|
|
// Or can _body_decode_len contain spaces??
|
|
|
|
// Check to see if a body had been allocated by prior decoding.
|
|
if (free_bp) {
|
|
free(*bp);
|
|
}
|
|
*bp = op_start;
|
|
bp_len = strlen(*bp);
|
|
|
|
return 1;
|
|
}
|
|
|
|
// End of For CHARSET
|