cdesktopenv/cde/programs/dtmail/libDtMail/Common/Session.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