Initial import of the CDE 2.1.30 sources from the Open Group.
This commit is contained in:
165
cde/programs/dtmail/libDtMail/RFC/AliasExpand.C
Normal file
165
cde/programs/dtmail/libDtMail/RFC/AliasExpand.C
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
*+SNOTICE
|
||||
*
|
||||
*
|
||||
* $XConsortium: AliasExpand.C /main/4 1996/04/21 19:49:19 drk $
|
||||
*
|
||||
* 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 <assert.h>
|
||||
|
||||
#include "AliasExpand.hh"
|
||||
#include "RFCImpl.hh"
|
||||
|
||||
AliasKey::AliasKey(const char * str)
|
||||
: ObjectKey("AliasKey")
|
||||
{
|
||||
_str = strdup(str);
|
||||
}
|
||||
|
||||
AliasKey::~AliasKey(void)
|
||||
{
|
||||
free(_str);
|
||||
}
|
||||
|
||||
int
|
||||
AliasKey::operator==(ObjectKey & other)
|
||||
{
|
||||
AliasKey * ok = (AliasKey *)&other;
|
||||
|
||||
return(strcmp(_str, ok->_str) == 0);
|
||||
}
|
||||
|
||||
int
|
||||
AliasKey::operator!=(ObjectKey & other)
|
||||
{
|
||||
AliasKey * ok = (AliasKey *)&other;
|
||||
|
||||
return(strcmp(_str, ok->_str) != 0);
|
||||
}
|
||||
|
||||
int
|
||||
AliasKey::operator<(ObjectKey & other)
|
||||
{
|
||||
AliasKey * ok = (AliasKey *)&other;
|
||||
|
||||
return(strcmp(_str, ok->_str) < 0);
|
||||
}
|
||||
|
||||
int
|
||||
AliasKey::operator<=(ObjectKey & other)
|
||||
{
|
||||
AliasKey * ok = (AliasKey *)&other;
|
||||
|
||||
return(strcmp(_str, ok->_str) <= 0);
|
||||
}
|
||||
|
||||
int
|
||||
AliasKey::operator>(ObjectKey & other)
|
||||
{
|
||||
AliasKey * ok = (AliasKey *)&other;
|
||||
|
||||
return(strcmp(_str, ok->_str) > 0);
|
||||
}
|
||||
|
||||
int
|
||||
AliasKey::operator>=(ObjectKey & other)
|
||||
{
|
||||
AliasKey * ok = (AliasKey *)&other;
|
||||
|
||||
return(strcmp(_str, ok->_str) >= 0);
|
||||
}
|
||||
|
||||
HashVal
|
||||
AliasKey::hashValue(void)
|
||||
{
|
||||
return(genericHashValue(_str, strlen(_str)));
|
||||
}
|
||||
|
||||
// deleteAllocatedKey: used to iterate through a hash table to remove
|
||||
// all key values that are allocated during the duration of rfcAliasExpand
|
||||
//
|
||||
static int
|
||||
deleteAllocatedKey(ObjectKey & object, AliasKey *, void *) {
|
||||
assert(&object != NULL);
|
||||
delete &object;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
rfcAliasExpand(DtMailEnv & error,
|
||||
DtMail::MailRc & mailrc,
|
||||
DtMailAddressSeq & addrs)
|
||||
{
|
||||
HashTable<DtMailBoolean> been_there(32);
|
||||
|
||||
error.clear();
|
||||
|
||||
// We will go through each item in the address table.
|
||||
// For each address, we will look it up in the alias
|
||||
// table. If it exists there, we will check to see if
|
||||
// we have expanded before. If not, then we expand and
|
||||
// note our visit.
|
||||
//
|
||||
DtMailEnv lerror;
|
||||
|
||||
for (int naddr = 0; naddr < addrs.length(); naddr++) {
|
||||
DtMailValueAddress * caddr = addrs[naddr];
|
||||
|
||||
const char * alias = mailrc.getAlias(lerror, caddr->dtm_address);
|
||||
if (alias) {
|
||||
AliasKey * key = new AliasKey(caddr->dtm_address);
|
||||
if (been_there.lookup(*key)) { // already visited address?
|
||||
delete key; // yes: cleanup unneeded key
|
||||
}
|
||||
else { // no: expand the alias
|
||||
DtMailAddressSeq exp_addrs(32);
|
||||
RFCTransport::arpaPhrase(alias, exp_addrs);
|
||||
|
||||
for (int cp = 0; cp < exp_addrs.length(); cp++) {
|
||||
DtMailValueAddress * new_addr = new DtMailValueAddress(*exp_addrs[cp]);
|
||||
addrs.append(new_addr);
|
||||
}
|
||||
|
||||
been_there.set(*key, DTM_TRUE);
|
||||
}
|
||||
|
||||
addrs.remove(naddr); // no longer need this address
|
||||
naddr -= 1; // ...
|
||||
delete caddr; // or what it pointed to.
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, as a courtesy to the recipients, we will throw away
|
||||
// duplicates that have resulted from our expansion.
|
||||
//
|
||||
HashTable<DtMailBoolean> done_that(32);
|
||||
for (int cur = 0; cur < addrs.length(); cur++) {
|
||||
DtMailValueAddress * caddr = addrs[cur];
|
||||
AliasKey * dup_key = new AliasKey(caddr->dtm_address);
|
||||
|
||||
if (done_that.lookup(*dup_key)) {
|
||||
addrs.remove(cur);
|
||||
delete caddr;
|
||||
delete dup_key;
|
||||
cur -= 1;
|
||||
}
|
||||
else {
|
||||
done_that.set(*dup_key, DTM_TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
50
cde/programs/dtmail/libDtMail/RFC/AliasExpand.hh
Normal file
50
cde/programs/dtmail/libDtMail/RFC/AliasExpand.hh
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
*+SNOTICE
|
||||
*
|
||||
*
|
||||
* $XConsortium: AliasExpand.hh /main/4 1996/04/21 19:49:22 drk $
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _ALIASEXPAND_HH
|
||||
#define _ALIASEXPAND_HH
|
||||
|
||||
#include <DtMail/DtMail.hh>
|
||||
#include <DtMail/HashTable.hh>
|
||||
|
||||
class AliasKey : public ObjectKey {
|
||||
public:
|
||||
AliasKey(const char * str);
|
||||
~AliasKey(void);
|
||||
|
||||
int operator==(ObjectKey &);
|
||||
int operator!=(ObjectKey &);
|
||||
int operator<(ObjectKey &);
|
||||
int operator>(ObjectKey &);
|
||||
int operator<=(ObjectKey &);
|
||||
int operator>=(ObjectKey &);
|
||||
|
||||
HashVal hashValue(void);
|
||||
|
||||
private:
|
||||
char * _str;
|
||||
};
|
||||
|
||||
void rfcAliasExpand(DtMailEnv &,
|
||||
DtMail::MailRc & mailrc,
|
||||
DtMailAddressSeq & addrs);
|
||||
|
||||
#endif
|
||||
53
cde/programs/dtmail/libDtMail/RFC/Imakefile
Normal file
53
cde/programs/dtmail/libDtMail/RFC/Imakefile
Normal file
@@ -0,0 +1,53 @@
|
||||
XCOMM $TOG: Imakefile /main/8 1998/08/05 13:25:32 mgreess $
|
||||
#define DoNormalLib YES
|
||||
#define DoSharedLib NO
|
||||
#define DoDebugLib NO
|
||||
#define DoProfileLib NO
|
||||
#define LibName DtMail
|
||||
#define LibHeaders NO
|
||||
#define LibCreate NO
|
||||
|
||||
#define CplusplusSource YES
|
||||
DEPEND_DEFINES = $(CXXDEPENDINCLUDES)
|
||||
|
||||
#include <Threads.tmpl>
|
||||
|
||||
INCLUDES = -I. -I../Common -I../../include -I../../include/utils -I$(CDELIBSRC)
|
||||
|
||||
#if HasVFork
|
||||
VFORK_DEFINES = -DHAS_VFORK
|
||||
#else
|
||||
VFORK_DEFINES =
|
||||
#endif
|
||||
|
||||
#ifndef DtMailDefines
|
||||
# define DtMailDefines
|
||||
#endif
|
||||
DEFINES = -DTTLOCK_OFF $(VFORK_DEFINES) DtMailDefines
|
||||
|
||||
SRCS = \
|
||||
AliasExpand.C MIMEBodyPart.C \
|
||||
MIMEPartial.C RFCBodyPart.C \
|
||||
RFCEnvelope.C RFCFormat.C \
|
||||
RFCMIME.C RFCMailBox.C \
|
||||
RFCMailValues.C RFCMessage.C \
|
||||
RFCTransport.C SunV3.C \
|
||||
V3BodyPart.C
|
||||
|
||||
/*
|
||||
* NOTE: All changes here must be mirrored in ../Imakefile's RFC_OBJS
|
||||
*/
|
||||
OBJS = \
|
||||
AliasExpand.o MIMEBodyPart.o \
|
||||
MIMEPartial.o RFCBodyPart.o \
|
||||
RFCEnvelope.o RFCFormat.o \
|
||||
RFCMailBox.o RFCMailValues.o \
|
||||
RFCMessage.o RFCMIME.o \
|
||||
RFCTransport.o SunV3.o \
|
||||
V3BodyPart.o
|
||||
|
||||
#include <Library.tmpl>
|
||||
|
||||
SubdirLibraryRule($(OBJS))
|
||||
|
||||
DependTarget()
|
||||
998
cde/programs/dtmail/libDtMail/RFC/MIMEBodyPart.C
Normal file
998
cde/programs/dtmail/libDtMail/RFC/MIMEBodyPart.C
Normal file
@@ -0,0 +1,998 @@
|
||||
/*
|
||||
*+SNOTICE
|
||||
*
|
||||
*
|
||||
* $TOG: MIMEBodyPart.C /main/11 1998/04/06 13:27:03 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 <EUSCompat.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <Dt/Dts.h>
|
||||
|
||||
#include <DtMail/DtMail.hh>
|
||||
#include "RFCImpl.hh"
|
||||
#include "RFCMIME.hh"
|
||||
#include <DtMail/Threads.hh>
|
||||
#include "md5.h"
|
||||
|
||||
// For CHARSET
|
||||
#include <locale.h>
|
||||
#include <DtHelp/LocaleXlate.h>
|
||||
#include "str_utils.h"
|
||||
|
||||
MIMEBodyPart::MIMEBodyPart(DtMailEnv & error,
|
||||
DtMail::Message * parent,
|
||||
const char * start,
|
||||
const int len,
|
||||
RFCEnvelope * body_env)
|
||||
: RFCBodyPart(error, parent, start, len, body_env)
|
||||
{
|
||||
// A single part message. We are done for now.
|
||||
//
|
||||
error.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
MIMEBodyPart::MIMEBodyPart(DtMailEnv & error,
|
||||
DtMail::Message * parent,
|
||||
const char * start,
|
||||
const char ** end,
|
||||
const char * boundary)
|
||||
: RFCBodyPart(error, parent, start, 0, NULL)
|
||||
{
|
||||
error.clear();
|
||||
|
||||
// We are sitting at the start of a boundary. We need to get
|
||||
// past the boundary to do the real processing.
|
||||
//
|
||||
const char * body_end;
|
||||
for (body_end = _body_text; body_end <= *end && *body_end != '\n'; body_end++) {
|
||||
continue;
|
||||
}
|
||||
body_end += 1;
|
||||
|
||||
if (body_end > *end) {
|
||||
// Don't know. Give up!
|
||||
*end = body_end;
|
||||
_body_len = *end - start + 1;
|
||||
|
||||
// Need a bogus envelope for other uses.
|
||||
//
|
||||
_body_env = new RFCEnvelope(error, parent, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const char * env_start = body_end;
|
||||
_my_env = DTM_TRUE;
|
||||
if (isTerm(env_start)) {
|
||||
_body_env = new RFCEnvelope(error, parent, env_start, 0);
|
||||
}
|
||||
else {
|
||||
// Find the blank line where the envelope ends.
|
||||
//
|
||||
for (; body_end <= *end; body_end++) {
|
||||
if (*body_end == '\n') {
|
||||
int blank_only = 1;
|
||||
for (const char * blank = body_end + 1;
|
||||
blank <= *end && *blank != '\n'; blank++) {
|
||||
if (!isspace((unsigned char)*blank)) {
|
||||
blank_only = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (blank_only) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_body_env = new RFCEnvelope(error,
|
||||
parent,
|
||||
env_start,
|
||||
body_end - env_start + 1);
|
||||
body_end += 1;
|
||||
}
|
||||
|
||||
// Chew everything up to the next new line, which should be only
|
||||
// a CRLF.
|
||||
//
|
||||
for (;body_end <= *end && *body_end != '\n'; body_end++) {
|
||||
continue;
|
||||
}
|
||||
body_end += 1;
|
||||
|
||||
_body_text = body_end; // This is where the body really starts.
|
||||
|
||||
// Now we need to find the end of the body. MIME doesn't have
|
||||
// any predefined length fields so we have to use the boundaries.
|
||||
//
|
||||
int bndry_len = strlen(boundary);
|
||||
for (;body_end <= *end; body_end++) {
|
||||
if (*body_end == '\n' &&
|
||||
*(body_end + 1) == '-' &&
|
||||
*(body_end + 2) == '-' &&
|
||||
strncmp(body_end + 3, boundary, bndry_len) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*(body_end - 1) == '\r') {
|
||||
body_end -= 1;
|
||||
}
|
||||
_body_len = body_end - _body_text + 1;
|
||||
|
||||
// MIME says the CRLF preceding a boundary belongs to the boundary.
|
||||
// We will pull it off here rather than do it on entry to this
|
||||
// method for the next message.
|
||||
for (;body_end <= *end && *body_end != '\n'; body_end++) {
|
||||
continue;
|
||||
}
|
||||
body_end += 1;
|
||||
|
||||
// Computing the end here is a little different. If the boundary
|
||||
// ends with a "--" as well, then we are at the real end of
|
||||
// the message.
|
||||
//
|
||||
const char * bndry_end = body_end + 2 + strlen(boundary);
|
||||
if (*bndry_end == '-' && *(bndry_end + 1) == '-') {
|
||||
*end = *end + 1;
|
||||
}
|
||||
else {
|
||||
*end = body_end;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
MIMEBodyPart::~MIMEBodyPart(void)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef DEAD_WOOD
|
||||
DtMailChecksumState
|
||||
MIMEBodyPart::checksum(DtMailEnv & error)
|
||||
{
|
||||
error.clear();
|
||||
|
||||
// Look for the Content-MD5 header. If it is not present, then
|
||||
// the state is unknown and we can punt.
|
||||
//
|
||||
DtMailEnv my_error;
|
||||
DtMailValueSeq value;
|
||||
_body_env->getHeader(my_error, "Content-MD5", DTM_FALSE, value);
|
||||
if (my_error.isSet()) {
|
||||
return(DtMailCheckUnknown);
|
||||
}
|
||||
|
||||
|
||||
if (_body_type == NULL) {
|
||||
getDtType(error);
|
||||
if (error.isSet()) {
|
||||
return(DtMailCheckUnknown);
|
||||
}
|
||||
}
|
||||
|
||||
char stored_digest[32];
|
||||
int stored_size = 0;
|
||||
RFCMIME::readBase64(stored_digest, stored_size,
|
||||
*(value[0]), strlen(*(value[0])));
|
||||
if (stored_size != 16) {
|
||||
// The MD5 sum must be 16 bytes, or we have a bad checksum.
|
||||
//
|
||||
return(DtMailCheckBad);
|
||||
}
|
||||
|
||||
// See if we call this text. We need to handle md5 checksums
|
||||
// different for text. They must be computed with CRLF line
|
||||
// termination.
|
||||
//
|
||||
char * text_type = DtDtsDataTypeToAttributeValue(_body_type,
|
||||
DtDTS_DA_IS_TEXT,
|
||||
NULL);
|
||||
|
||||
unsigned char digest[16];
|
||||
if (text_type && strcasecmp(text_type, "true") == 0) {
|
||||
RFCMIME::md5PlainText(_body, _body_decoded_len, digest);
|
||||
}
|
||||
else {
|
||||
MD5_CTX context;
|
||||
MD5Init(&context);
|
||||
MD5Update(&context, (unsigned char *)_body, _body_decoded_len);
|
||||
MD5Final(digest, &context);
|
||||
}
|
||||
|
||||
free(text_type);
|
||||
|
||||
if (memcmp(digest, stored_digest, sizeof(digest)) == 0) {
|
||||
return(DtMailCheckGood);
|
||||
}
|
||||
else {
|
||||
return(DtMailCheckBad);
|
||||
}
|
||||
}
|
||||
#endif /* DEAD_WOOD */
|
||||
|
||||
static int
|
||||
countTypes(char ** types)
|
||||
{
|
||||
int count;
|
||||
for (count = 0; *types; types++, count++) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return(count);
|
||||
}
|
||||
|
||||
void
|
||||
MIMEBodyPart::getContentType(DtMailEnv &error, char **mime_type)
|
||||
{
|
||||
DtMailValueSeq value;
|
||||
|
||||
if (mime_type)
|
||||
*mime_type = (char *)0;
|
||||
|
||||
if (_body_env)
|
||||
_body_env->getHeader(error, "Content-Type", DTM_FALSE, value);
|
||||
|
||||
if (_body_env && !error.isSet())
|
||||
{
|
||||
// Handle "Content-Type: text" problem with /usr/lib/mail.local
|
||||
if (strcasecmp(*(value[0]), "text")==0)
|
||||
*mime_type = strdup("text/plain");
|
||||
else
|
||||
*mime_type = strdup(*(value[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
error.clear();
|
||||
*mime_type = strdup("text/plain");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MIMEBodyPart::getDtType(DtMailEnv & error)
|
||||
{
|
||||
MutexLock lock_scope(_obj_mutex);
|
||||
MutexLock dt_lib_lock(_DtMutex);
|
||||
|
||||
// error.clear();
|
||||
|
||||
char * end;
|
||||
char * mime_type;
|
||||
DtMailValueSeq value;
|
||||
|
||||
if (_body_env) {
|
||||
_body_env->getHeader(error, "Content-Type", DTM_FALSE, value);
|
||||
}
|
||||
|
||||
if (_body_env && !error.isSet()) {
|
||||
// Handle "Content-Type: text" problem with /usr/lib/mail.local
|
||||
//
|
||||
if (strcasecmp(*(value[0]), "text")==0)
|
||||
mime_type = strdup("text/plain");
|
||||
else
|
||||
mime_type = strdup(*(value[0]));
|
||||
}
|
||||
else {
|
||||
error.clear();
|
||||
mime_type = strdup("text/plain");
|
||||
}
|
||||
|
||||
for (end = mime_type; *end; end++) {
|
||||
if (*end == ';' || isspace((unsigned char)*end)) {
|
||||
break;
|
||||
}
|
||||
if (isupper(*end)) {
|
||||
*end = tolower(*end);
|
||||
}
|
||||
}
|
||||
*end = 0;
|
||||
|
||||
char ** types = DtDtsFindAttribute(DtDTS_DA_MIME_TYPE, mime_type);
|
||||
|
||||
// We must have an exact 1:1 mapping between the mime type and the
|
||||
// CDE type to use this inverse mapping. If we have no hits then we
|
||||
// dont have any thing to use. If we have several hits, then we have
|
||||
// no idea which type is the correct type.
|
||||
//
|
||||
if (NULL != types) {
|
||||
if (countTypes(types) == 1) {
|
||||
// We will use the first name. It may be wrong, but
|
||||
// it is the best we can do at this point.
|
||||
//
|
||||
_body_type = strdup(types[0]);
|
||||
DtDtsFreeDataTypeNames(types);
|
||||
free(mime_type);
|
||||
return;
|
||||
}
|
||||
DtDtsFreeDataTypeNames(types);
|
||||
}
|
||||
|
||||
// We need the bits so we can type the buffer and get
|
||||
// a type for the object. This is where things can get
|
||||
// very slow for the user.
|
||||
//
|
||||
loadBody(error);
|
||||
if (error.isSet()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int istext = (strcasecmp(mime_type, "text/plain") == 0);
|
||||
char * name = getNameHeaderVal(error);
|
||||
char * type = DtDtsBufferToDataType(_body, _body_decoded_len, name);
|
||||
|
||||
// We have written a name pattern for text parts that will match
|
||||
// the name "text" as a TEXT part. If the first attempt to get the
|
||||
// data type fails and we have a MIMETYPE of text/plain, then we try
|
||||
// again using the name "text".
|
||||
//
|
||||
if ( (0 == strcasecmp(mime_type, "text/plain")) &&
|
||||
(NULL == type || 0 == strcasecmp(type, "DATA")) )
|
||||
{
|
||||
if (type)
|
||||
DtDtsFreeDataType(type);
|
||||
type = DtDtsBufferToDataType(_body, _body_decoded_len, "text");
|
||||
}
|
||||
|
||||
if (NULL != type)
|
||||
_body_type = strdup(type);
|
||||
else
|
||||
_body_type = strdup("UNKNOWN");
|
||||
|
||||
if (error.isSet())
|
||||
error.clear();
|
||||
if (type)
|
||||
DtDtsFreeDataType(type);
|
||||
if (mime_type)
|
||||
free(mime_type);
|
||||
if (name)
|
||||
free(name);
|
||||
}
|
||||
|
||||
void
|
||||
MIMEBodyPart::loadBody(DtMailEnv & error)
|
||||
{
|
||||
// For CHARSET
|
||||
char *cs = NULL, *to_cs = NULL, *from_cs = NULL;
|
||||
|
||||
// There is no reason to clear the error object because it is assumed
|
||||
// that whoever instantiated it, cleared it.
|
||||
// error.clear();
|
||||
|
||||
if (_body) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is any encoding done to the body, reverse it
|
||||
//
|
||||
DtMailValueSeq value;
|
||||
if (_body_env) {
|
||||
_body_env->getHeader(error, "Content-Transfer-Encoding",
|
||||
DTM_FALSE, value);
|
||||
}
|
||||
|
||||
if (_body_env && !error.isSet()) {
|
||||
const char * enc = *(value[0]);
|
||||
if (strcasecmp(enc, "base64") == 0) {
|
||||
// Decoded bodies will always be smaller than
|
||||
// encoded bodies.
|
||||
//_body = (char *)malloc(_body_len);
|
||||
|
||||
_body = (char *)malloc(_body_len+1);
|
||||
int size = _body_decoded_len = 0;
|
||||
_must_free_body = DTM_TRUE;
|
||||
RFCMIME::readBase64(_body, size, _body_text, _body_len);
|
||||
_body_decoded_len = size;
|
||||
|
||||
// Changed this temporarily until after release. We really should not
|
||||
// be null terminating these buffers.
|
||||
(_body)[_body_decoded_len] = 0;
|
||||
}
|
||||
else if (strcasecmp(enc, "quoted-printable") == 0) {
|
||||
_body = (char *)malloc(_body_len + 20);
|
||||
_must_free_body = DTM_TRUE;
|
||||
int size = _body_decoded_len = 0;
|
||||
RFCMIME::readQPrint(_body, size, _body_text, _body_len);
|
||||
_body_decoded_len = size;
|
||||
|
||||
// Changed this temporarily until after release. We really should not
|
||||
// be null terminating these buffers.
|
||||
(_body)[_body_decoded_len] = 0;
|
||||
}
|
||||
else {
|
||||
// Default case is no transfer encoding applies (7bit==8bit==binary)
|
||||
_body = (char *)_body_text;
|
||||
_must_free_body = DTM_FALSE;
|
||||
_body_decoded_len = _body_len;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Default case is no transfer encoding applies.
|
||||
error.clear();
|
||||
_body = (char *)_body_text;
|
||||
_must_free_body = DTM_FALSE;
|
||||
_body_decoded_len = _body_len;
|
||||
}
|
||||
|
||||
// For CHARSET
|
||||
// Get charset from content-type field
|
||||
char *ret = NULL;
|
||||
value.clear();
|
||||
_body_env->getHeader(error, "Content-Type", DTM_FALSE, value);
|
||||
if (error.isNotSet()) {
|
||||
cs = csFromContentType(value);
|
||||
if ( cs == NULL ) {
|
||||
// Random allocation for cs
|
||||
cs = (char *)calloc(128, sizeof(char));
|
||||
DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
|
||||
setlocale(LC_CTYPE, NULL),
|
||||
NULL,
|
||||
NULL,
|
||||
&ret);
|
||||
strcpy(cs, "DEFAULT");
|
||||
strcat(cs, ".");
|
||||
strcat(cs, ret);
|
||||
}
|
||||
} else { // No Content-Type
|
||||
// We'll be flexible here. If Content-Type header is missing, we'll
|
||||
// still try to convert from the locale specific default codeset.
|
||||
error.clear();
|
||||
// Random allocation for cs
|
||||
cs = (char *)calloc(128, sizeof(char));
|
||||
DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
|
||||
setlocale(LC_CTYPE, NULL),
|
||||
NULL,
|
||||
NULL,
|
||||
&ret);
|
||||
strcpy(cs, "DEFAULT");
|
||||
strcat(cs, ".");
|
||||
strcat(cs, ret);
|
||||
}
|
||||
|
||||
// Handle ISO-2022-INT, RFC approved, or private encoding names
|
||||
if ( strcasecmp(cs, "ISO-2022-INT-1") == 0 ) {
|
||||
// Need to obtain charset from encoding
|
||||
} // RFC approved and private names are not treated differently.
|
||||
|
||||
// Get iconv name from charset - this is the "from" name.
|
||||
from_cs = NULL;
|
||||
from_cs = csToConvName(cs);
|
||||
|
||||
// Get current locale's iconv name - this is the "to" name.
|
||||
to_cs = NULL;
|
||||
to_cs = locToConvName();
|
||||
|
||||
if ( from_cs && to_cs ) {
|
||||
if ( strcasecmp(from_cs, to_cs) != 0 ) {
|
||||
unsigned long tmp_len = (unsigned long) _body_decoded_len;
|
||||
if (csConvert(&_body, tmp_len, (int)_must_free_body, from_cs, to_cs)) {
|
||||
_must_free_body = DTM_TRUE;
|
||||
_body_decoded_len = (int) tmp_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( cs )
|
||||
free ( cs );
|
||||
if ( from_cs )
|
||||
free( from_cs );
|
||||
if ( to_cs )
|
||||
free ( to_cs );
|
||||
if ( ret )
|
||||
free( ret );
|
||||
|
||||
// End of For CHARSET
|
||||
|
||||
// Clear the error condition before proceeding. This is done
|
||||
// because functions that take a DtMailEnv object as a parameter
|
||||
// expect it to be cleared. (Already cleared above)
|
||||
// error.clear();
|
||||
|
||||
// If the body is text/enriched, convert it to text/plain
|
||||
// At some point the front end should be able to handle this via
|
||||
// data typing and do an 'intelligent' conversion, in which
|
||||
// case this code can be removed
|
||||
//
|
||||
|
||||
value.clear();
|
||||
if (_body_env) {
|
||||
_body_env->getHeader(error, "Content-Type", DTM_FALSE, value);
|
||||
}
|
||||
|
||||
if (_body_env && !error.isSet() &&
|
||||
((strncasecmp(*(value[0]), "text/enriched", 13) == 0)
|
||||
|| (strncasecmp(*(value[0]), "text/richtext", 13) == 0))) {
|
||||
char *new_body = (char *)malloc((unsigned)(_body_len*2));
|
||||
int size = 0;
|
||||
RFCMIME::readTextEnriched(new_body, size, _body, _body_decoded_len);
|
||||
new_body = (char *)realloc(new_body, size+2);
|
||||
if (_must_free_body == DTM_TRUE)
|
||||
free(_body);
|
||||
_must_free_body = DTM_TRUE;
|
||||
_body_decoded_len = size;
|
||||
_body = new_body;
|
||||
// Changed this temporarily until after release. We really should not
|
||||
// be null terminating these buffers.
|
||||
(_body)[_body_decoded_len] = 0;
|
||||
}
|
||||
error.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTES ON HANDLING OF "FILE NAMES" FOR BODY PARTS IN MIME COMPLIANT ENTITIES:
|
||||
// (see full description in evaluation for bug 1189035)
|
||||
//
|
||||
// The ability to provide a "file name" for a body part in a MIME compliant
|
||||
// entity is partially addressed in RFC 1341 [MIME: Multipurpose Internet Mail
|
||||
// Extentions] and RFC 1521 [which obsoleted RFC 1341].
|
||||
//
|
||||
// RFC 1341 proposed a solution to the file name problem (Content-Type:
|
||||
// type; name=name); RFC 1521 subsequently depreciated this solution in
|
||||
// favor of a to-be-defined future specification of "Content-Disposition".
|
||||
//
|
||||
// In essence there is an anticipation that "Content-Disposition" will
|
||||
// be defined in a future RFC such that the notion of a "file name" may
|
||||
// be given to a body part of a MIME compliant entity.
|
||||
//
|
||||
// OpenWindows mailtool currently recognizes "Content-Description" on MIME
|
||||
// compliant entities and uses that information as the "file name" for
|
||||
// an attachment (as per a loose interpretation of RFC 1521).
|
||||
//
|
||||
// CDE DtMail currently sends out and recognizes "X-Content-Name" on MIME
|
||||
// compliant entities and uses that information as the "file name" for an
|
||||
// attachment. This is essentially a non-standard header field as per RFC
|
||||
// 1521:
|
||||
//
|
||||
// "X-" fields may be created for experimental or private purposes,
|
||||
// with the recognition that the information they contain may be
|
||||
// lost at some gateways.
|
||||
//
|
||||
// Since OpenWindows mailtool does not understand "X-Content-Name", any
|
||||
// body part in e-mail originating from CDE DtMail does not appear to
|
||||
// have a file name when read by mailtool.
|
||||
//
|
||||
// Given these facts:
|
||||
//
|
||||
// . OpenWindows mailtool cannot be changed until at least the Solaris
|
||||
// 2.5 release (if at all).
|
||||
//
|
||||
// . OpenWindows mailtool is recognizing a valid MIME header field
|
||||
// which is essentially "free form" in nature.
|
||||
//
|
||||
// . CDE DtMail is currently using an experimental or private field
|
||||
// which is not part of the standard and is not guaranteed to survive
|
||||
// transport across gateways.
|
||||
//
|
||||
// . There is no officially proscribed method for providing the "file
|
||||
// name" of a body part in a MIME compliant entity in RFC 1521.
|
||||
//
|
||||
// The reasonable approach to take to solve this problem is:
|
||||
//
|
||||
// 1. Have DtMail use "Content-Description" to transmit the "file name"
|
||||
// for a body part - this achieves compatibility with mailtool.
|
||||
//
|
||||
// 2. Have DtMail also continue to use "X-Content-Name" to transmit the
|
||||
// "file name" - this maintains compatibility with previous versions
|
||||
// of DtMail which only recognize this header.
|
||||
//
|
||||
// 3. Have Dtmail recognize both "Content-Description" and "X-Content-Name"
|
||||
// as specifying the "file name" for a body part. In the case that both
|
||||
// fields are present, "Content-Description" takes precedence over
|
||||
// "X-Content-Name".
|
||||
//
|
||||
// 4. When "Content-Disposition" is properly defined and included as part
|
||||
// of an updated MIME specification, revisit this issue.
|
||||
//
|
||||
//
|
||||
// So, since dtmail currently *ignores* the "Content-Description" field,
|
||||
// we overload the "getNameHeaderVal" function to use "Content-Description"
|
||||
// first, then the older unofficial experimental "X-Content-Name". If
|
||||
// and when Content-Disposition takes hold, it will have to override
|
||||
// Content-Description when both are present.
|
||||
//
|
||||
|
||||
//
|
||||
// March 25, 1997
|
||||
// The Content-Disposition field has been designated as the primary
|
||||
// header field for transmitting file names. See RFC 1806. Therefore
|
||||
// the algorithm has bee updated as follows.
|
||||
//
|
||||
// 1. DtMail checks the following headers for the "filename" for a body part:
|
||||
// o The "filename" parameter of the "Content-Disposition" header field.
|
||||
// o The contents of the "Content-Description" header field.
|
||||
// o The "name" parameter of the "Content-Type" header field.
|
||||
// o The contents of the "Content-Name" header field.
|
||||
// o The contents of the "X-Content-Name" header field.
|
||||
//
|
||||
// 2. DtMail uses the following fields to specify the "filename" for a body
|
||||
// part in outgoing mail:
|
||||
// o The "filename" parameter of the "Content-Disposition" header field.
|
||||
// o The contents of the "Content-Description" header field.
|
||||
//
|
||||
|
||||
char *
|
||||
MIMEBodyPart::getDescription(DtMailEnv &)
|
||||
{
|
||||
// Dont have this return anything without checking
|
||||
// ramifications with getNameHeaderValue
|
||||
|
||||
// No need to clear error object here because we assume it has already
|
||||
// been cleared by the caller and nothing has touched it in this method.
|
||||
// error.clear();
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
char *
|
||||
MIMEBodyPart::getNameHeaderVal(DtMailEnv & error)
|
||||
{
|
||||
DtMailValueSeq value;
|
||||
|
||||
if (_body_env == NULL) {
|
||||
// No need to clear the error object...it is unchanged from the
|
||||
// state we received it in.
|
||||
//error.clear();
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
// The current standard seems to be to use the "Content-Disposition"
|
||||
// header as the primary mechanism for transmitting file names.
|
||||
//
|
||||
_body_env->getHeader(error, "Content-Disposition", DTM_FALSE, value);
|
||||
if (error.isNotSet()) {
|
||||
char *param = parameterValue(value, "filename", DTM_FALSE);
|
||||
if (NULL != param)
|
||||
return strdup(param);
|
||||
}
|
||||
error.clear();
|
||||
|
||||
// In keeping with the current undefined nature of
|
||||
// file names for body parts in RFC 1521, and to be
|
||||
// compatible with OpenWindows mailtool, the first
|
||||
// overriding "name" comes from "Content-Description"
|
||||
//
|
||||
_body_env->getHeader(error, "Content-Description", DTM_FALSE, value);
|
||||
if (error.isNotSet()) {
|
||||
return(strdup(*(value[0])));
|
||||
}
|
||||
error.clear();
|
||||
|
||||
// For backward compatibility with older mail agents, check the "Name"
|
||||
// parameter in the "Content-Type" header.
|
||||
//
|
||||
_body_env->getHeader(error, "Content-Type", DTM_FALSE, value);
|
||||
if (error.isNotSet()) {
|
||||
char *param = parameterValue(value, "name", DTM_FALSE);
|
||||
if (NULL != param)
|
||||
return strdup(param);
|
||||
}
|
||||
error.clear();
|
||||
|
||||
// Next we remain compatible with previous versions
|
||||
// of DtMail that used "X-Content-Name" instead of
|
||||
// "Content-Description" for the file name of a
|
||||
// body part (1-27-95)
|
||||
//
|
||||
_body_env->getHeader(error, "X-Content-Name",
|
||||
DTM_FALSE, value);
|
||||
if (error.isNotSet()) {
|
||||
return(strdup(*(value[0])));
|
||||
}
|
||||
error.clear();
|
||||
|
||||
// This code was in dtmail on 1-27-95 and even though
|
||||
// that version of dtmail did not send out "Content-Name"
|
||||
// (which is not part of RFC 1341 or 1521) it doesnt
|
||||
// hurt to recognize it if its the only header there.
|
||||
//
|
||||
_body_env->getHeader(error, "Content-Name",
|
||||
DTM_FALSE, value);
|
||||
if (error.isNotSet()) {
|
||||
return(strdup(*(value[0])));
|
||||
}
|
||||
error.clear(); // NULL is the real error here.
|
||||
|
||||
// No name for this body part
|
||||
//
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
char *
|
||||
MIMEBodyPart::getName(DtMailEnv & error)
|
||||
{
|
||||
char * h_name = getNameHeaderVal(error);
|
||||
// don't care about the error returned by getNameHeaderVal()
|
||||
error.clear();
|
||||
if (h_name) {
|
||||
return(h_name);
|
||||
}
|
||||
|
||||
if (!_body_type) {
|
||||
getDtType(error);
|
||||
if (error.isSet()) {
|
||||
error.clear();
|
||||
return(strdup("Attachment"));
|
||||
}
|
||||
}
|
||||
|
||||
char * pat = DtDtsDataTypeToAttributeValue(_body_type,
|
||||
"NAME_TEMPLATE",
|
||||
NULL);
|
||||
|
||||
if (pat) {
|
||||
int max_len = strlen(pat) + 20;
|
||||
char * name = (char*) malloc((size_t) max_len);
|
||||
sprintf(name, pat, "Attachment");
|
||||
DtDtsFreeAttributeValue(pat);
|
||||
return(name);
|
||||
}
|
||||
|
||||
return(strdup("Attachment"));
|
||||
}
|
||||
|
||||
void
|
||||
MIMEBodyPart::setName(DtMailEnv & error, const char * name)
|
||||
{
|
||||
if (_body_env) {
|
||||
_body_env->setHeader(error, "X-Content-Name", DTM_TRUE, name);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long
|
||||
MIMEBodyPart::getLength(DtMailEnv & error)
|
||||
{
|
||||
MutexLock lock_scope(_obj_mutex);
|
||||
|
||||
loadBody(error);
|
||||
if (error.isSet()) {
|
||||
// propogate the error back to the caller.
|
||||
return (0);
|
||||
}
|
||||
|
||||
// We have to treat external bodies differently. The headers on these
|
||||
// parts contain useful information to the client.
|
||||
//
|
||||
const char * mime_type;
|
||||
DtMailValueSeq value;
|
||||
|
||||
if (_body_env) {
|
||||
_body_env->getHeader(error, "Content-Type", DTM_FALSE, value);
|
||||
}
|
||||
|
||||
if (_body_env && !error.isSet()) {
|
||||
mime_type = *(value[0]);
|
||||
}
|
||||
else {
|
||||
// only need to clear the error object if getHeader returned
|
||||
// an error condition. We don't want to propogate the error
|
||||
// back up the calling sequence.
|
||||
error.clear();
|
||||
mime_type = "text/plain";
|
||||
}
|
||||
|
||||
unsigned long len;
|
||||
|
||||
if (strncasecmp(mime_type, "message/external-body", 21) == 0) {
|
||||
const char * contents = _body_start;
|
||||
for (;contents < (_body_text + _body_len); contents++) {
|
||||
if (*contents == '\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
contents += 1;
|
||||
|
||||
len = _body_len + (_body_text - contents);
|
||||
}
|
||||
else {
|
||||
len = _body_decoded_len;
|
||||
}
|
||||
|
||||
return(len);
|
||||
}
|
||||
|
||||
int
|
||||
MIMEBodyPart::rfcSize(const char *, DtMailBoolean &)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
char *
|
||||
MIMEBodyPart::writeBodyParts(char * buf)
|
||||
{
|
||||
return(buf);
|
||||
}
|
||||
|
||||
const void *
|
||||
MIMEBodyPart::getBody(DtMailEnv & error)
|
||||
{
|
||||
// No need to clear the error here, should be cleared by the object
|
||||
// that instantiated it.
|
||||
// error.clear();
|
||||
|
||||
if (!_body) {
|
||||
loadBody(error);
|
||||
// loadBody currently (version 1.31) clears the error before
|
||||
// returning, so the following check is not needed. We'll
|
||||
// leave it alone on the chance that loadBody() will someday
|
||||
// return an error condition.
|
||||
if (error.isSet()) {
|
||||
return(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// We have to treat external bodies differently. The headers on these
|
||||
// parts contain useful information to the client.
|
||||
//
|
||||
const char * mime_type;
|
||||
DtMailValueSeq value;
|
||||
|
||||
if (_body_env) {
|
||||
_body_env->getHeader(error, "Content-Type", DTM_FALSE, value);
|
||||
}
|
||||
|
||||
if (_body_env && !error.isSet()) {
|
||||
// Handle "Content-Type: text" problem with /usr/lib/mail.local
|
||||
//
|
||||
if (strcasecmp(*(value[0]), "text")==0)
|
||||
mime_type = "text/plain";
|
||||
else
|
||||
mime_type = *(value[0]);
|
||||
}
|
||||
else {
|
||||
error.clear();
|
||||
mime_type = "text/plain";
|
||||
}
|
||||
|
||||
const char * contents;
|
||||
|
||||
if (strncasecmp(mime_type, "message/external-body", 21) == 0) {
|
||||
contents = _body_start;
|
||||
for (;contents < (_body_text + _body_len); contents++) {
|
||||
if (*contents == '\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
contents += 1;
|
||||
}
|
||||
else {
|
||||
contents = _body;
|
||||
}
|
||||
|
||||
return(contents);
|
||||
}
|
||||
|
||||
// For CHARSET
|
||||
|
||||
// Given the Content-Type field, extract the charset value.
|
||||
// Returns one of the following:
|
||||
// 1) charset value,
|
||||
// Caller MUST FREE this return value.
|
||||
// 2) NULL if charset is not specified but Content-Type specifies
|
||||
// text/plain data. If this routine returns NULL, that means
|
||||
// charset is not specified but the data is text/plain. Therefore,
|
||||
// (as in the case of MIMEBodyPart::loadBody) caller
|
||||
// will call csToConvName("DEFAULT.<locale>") and csToConvName will return
|
||||
// a default iconv conversion name for the current locale.
|
||||
// This is the case because some (old) mailers do not set the charset value
|
||||
// but encodes the message in a "popular" codeset. The default conversion
|
||||
// name for a particular locale assumes the "popular" codeset used.
|
||||
// 3) non NULL but invalid charset value if Content-Type does not specify
|
||||
// text/plain data. Caller MUST FREE this return value.
|
||||
char *
|
||||
MIMEBodyPart::csFromContentType(DtMailValueSeq &value)
|
||||
{
|
||||
char *cs_str = NULL;
|
||||
char *val_ptr = NULL;
|
||||
int quoted = 0;
|
||||
|
||||
// value[0] should be valid else error would have occurred before
|
||||
// this routine ever gets called. And value index is 0 because
|
||||
// previous value stored should have been removed.
|
||||
const char *val = *(value[0]);
|
||||
|
||||
// Check to see if Content-Type field specifies text/plain data.
|
||||
// If so, look for charset value
|
||||
// else, returns value in Content-Type field
|
||||
if ( strstr(val, "text") == NULL ) {
|
||||
if ( strstr(val, "TEXT") == NULL ) {
|
||||
cs_str = strdup(val);
|
||||
return cs_str;
|
||||
}
|
||||
}
|
||||
// Get charset value
|
||||
val_ptr = strstr(val, "charset=");
|
||||
if ( val_ptr == NULL ) {
|
||||
val_ptr = strstr(val, "CHARSET=");
|
||||
}
|
||||
if ( val_ptr == NULL ) {
|
||||
return NULL;
|
||||
}
|
||||
val_ptr = val_ptr+8;
|
||||
|
||||
// Check if charset value is quoted
|
||||
if ( val_ptr[0] == '"' ) {
|
||||
val_ptr++;
|
||||
quoted = 1;
|
||||
}
|
||||
if ( quoted ) {
|
||||
cs_str = strdup(strtok(val_ptr, "\""));
|
||||
} else {
|
||||
cs_str = (char *)calloc(strlen(val_ptr)+1, sizeof(char));
|
||||
sscanf(val_ptr, "%s", cs_str);
|
||||
}
|
||||
|
||||
return cs_str;
|
||||
}
|
||||
// End of For CHARSET
|
||||
|
||||
|
||||
char *
|
||||
MIMEBodyPart::parameterValue(
|
||||
DtMailValueSeq &value,
|
||||
const char * parameter,
|
||||
DtMailBoolean isCaseSensitive)
|
||||
{
|
||||
char *lasts=NULL;
|
||||
char *ptok, *vtok;
|
||||
char *parm, *val;
|
||||
int rtn = 0;
|
||||
|
||||
val = strdup(*(value[0]));
|
||||
vtok = strrchr(val, ';');
|
||||
while (NULL != vtok)
|
||||
{
|
||||
*vtok = '\0';
|
||||
vtok++;
|
||||
|
||||
while(isspace(*vtok))
|
||||
vtok++;
|
||||
|
||||
if (isCaseSensitive)
|
||||
rtn = strncmp(vtok, parameter, sizeof(parameter));
|
||||
else
|
||||
rtn = strncasecmp(vtok, parameter, sizeof(parameter));
|
||||
|
||||
if (0 == rtn)
|
||||
{
|
||||
ptok = strrchr(vtok, '=');
|
||||
if (NULL == ptok)
|
||||
{
|
||||
free(val);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ptok++;
|
||||
if (*ptok == '"' )
|
||||
{
|
||||
ptok++;
|
||||
parm = strdup(strtok(ptok, (const char *) "\""));
|
||||
}
|
||||
else
|
||||
parm = strdup(ptok);
|
||||
|
||||
free(val);
|
||||
return parm;
|
||||
}
|
||||
|
||||
vtok = strrchr(val, ';');
|
||||
}
|
||||
|
||||
free(val);
|
||||
return NULL;
|
||||
}
|
||||
538
cde/programs/dtmail/libDtMail/RFC/MIMEPartial.C
Normal file
538
cde/programs/dtmail/libDtMail/RFC/MIMEPartial.C
Normal file
@@ -0,0 +1,538 @@
|
||||
/*
|
||||
*+SNOTICE
|
||||
*
|
||||
*
|
||||
* $TOG: MIMEPartial.C /main/6 1998/04/06 13:27:21 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 <EUSCompat.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <DtMail/DtMail.hh>
|
||||
#include "RFCImpl.hh"
|
||||
#include "str_utils.h"
|
||||
|
||||
static const char * contentType = "content-type";
|
||||
static const char * partial = "message/partial";
|
||||
|
||||
//
|
||||
// Get the named header out of out of the message.
|
||||
//
|
||||
static char *
|
||||
getNamedHeader(DtMailEnv & error,
|
||||
const char * headerName,
|
||||
RFCMessage * message)
|
||||
{
|
||||
DtMailValueSeq value;
|
||||
char * results = NULL;
|
||||
RFCEnvelope * env = (RFCEnvelope *)message->getEnvelope(error);
|
||||
|
||||
if (error.isNotSet()) {
|
||||
env->getHeader(error, headerName, DTM_FALSE, value);
|
||||
|
||||
if (error.isNotSet()) {
|
||||
results = strdup(*(value[0]));
|
||||
}
|
||||
}
|
||||
|
||||
return(results);
|
||||
}
|
||||
|
||||
//
|
||||
// Return the value to the right of the '=' in the named string.
|
||||
// As an INT.
|
||||
//
|
||||
static unsigned int
|
||||
getNamedValueInt(const char *string, const char *name)
|
||||
{
|
||||
int stringLen = strlen(string);
|
||||
int nameLen = strlen(name);
|
||||
int results = 0;
|
||||
register unsigned int offset;
|
||||
|
||||
for (offset = 0; offset < stringLen - nameLen; offset++) {
|
||||
if (strncasecmp(&string[offset], name, nameLen) == 0) {
|
||||
if (string[offset + nameLen] == '=') {
|
||||
results = atoi(&string[offset + nameLen + 1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return(results);
|
||||
}
|
||||
|
||||
//
|
||||
// Return the value to the right of the '=' in the named string.
|
||||
// As a const char *.
|
||||
//
|
||||
static const char *
|
||||
getNamedValueString(const char *string, const char *name)
|
||||
{
|
||||
int stringLen = strlen(string);
|
||||
int nameLen = strlen(name);
|
||||
const char * results;
|
||||
char * stringEnd;
|
||||
register unsigned int offset;
|
||||
|
||||
for (offset = 0; offset < stringLen - nameLen; offset++) {
|
||||
if (strncasecmp(&string[offset], name, nameLen) == 0) {
|
||||
if (string[offset + nameLen] == '=') {
|
||||
|
||||
//
|
||||
// We only want what is inside the '"' quotes.
|
||||
//
|
||||
results = strdup(&string[offset + nameLen + 1]);
|
||||
if (*results == '"') {
|
||||
results++;
|
||||
stringEnd = strchr(results, '"');
|
||||
if (stringEnd != NULL) {
|
||||
*stringEnd = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return(results);
|
||||
}
|
||||
|
||||
DtMailBoolean
|
||||
RFCMailBox::_isPartial(DtMailEnv & error,
|
||||
RFCMessage * message)
|
||||
{
|
||||
DtMailBoolean results = DTM_FALSE;
|
||||
RFCMessage * messageArray = NULL;
|
||||
|
||||
char * type;
|
||||
|
||||
if (message != NULL
|
||||
&& (type = getNamedHeader(error, contentType, message)) != NULL) {
|
||||
|
||||
if (error.isNotSet()) {
|
||||
if (strncasecmp(type, partial, 15) == 0) {
|
||||
|
||||
//
|
||||
// Add ourselves to the array.
|
||||
//
|
||||
_partialData * newData = new _partialData;
|
||||
|
||||
newData->id = getNamedValueString(type, "id");
|
||||
newData->number = getNamedValueInt(type, "number");
|
||||
newData->total = getNamedValueInt(type, "total");
|
||||
newData->msg = message;
|
||||
//
|
||||
// It has to have a number and an ID or it is not a valid
|
||||
// message/partial and there would be no way to put it
|
||||
// back together. Total is optional except for the last part
|
||||
// where it is required.
|
||||
//
|
||||
if (newData->number > 0 && newData->id != NULL) {
|
||||
_partialList = (_partialData **)realloc(_partialList,
|
||||
sizeof(_partialData *)
|
||||
*(_partialListCount+1));
|
||||
if (_partialList != NULL) {
|
||||
_partialList[_partialListCount] = newData;
|
||||
_partialListCount++;
|
||||
results = DTM_TRUE;
|
||||
}
|
||||
} else {
|
||||
delete newData; // Not a valid message/partial.
|
||||
}
|
||||
}
|
||||
free(type);
|
||||
}
|
||||
else {
|
||||
error.clear();
|
||||
}
|
||||
}
|
||||
return(results);
|
||||
}
|
||||
|
||||
RFCMessage *
|
||||
RFCMailBox::_assemblePartial(DtMailEnv & error,
|
||||
RFCMessage * message)
|
||||
{
|
||||
register unsigned int offset;
|
||||
unsigned int totalParts = 0;
|
||||
|
||||
RFCMessage * msg = message;
|
||||
|
||||
_partialData * data = NULL;
|
||||
|
||||
if (message != NULL) {
|
||||
char * type = getNamedHeader(error, contentType, message);
|
||||
|
||||
//
|
||||
// Find ourselfs in the list.
|
||||
//
|
||||
for (offset = 0 ; offset < _partialListCount ; offset++) {
|
||||
if (_partialList[offset]->msg == message) {
|
||||
data = _partialList[offset];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Now look for the total for this message-id.
|
||||
//
|
||||
if (data != NULL) {
|
||||
for (offset = 0 ; offset < _partialListCount ; offset++) {
|
||||
if (strcasecmp(_partialList[offset]->id, data->id) == 0) {
|
||||
totalParts = _partialList[offset]->total;
|
||||
if (totalParts > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// If we know how many parts we are to process - start looking.
|
||||
//
|
||||
// If we do not know the total, then we know that not all of the
|
||||
// parts have arrived. (the last partial message must have
|
||||
// the total in it).
|
||||
//
|
||||
if (totalParts > 0) {
|
||||
|
||||
char * dummy = (char *) calloc(1, totalParts * sizeof(RFCMessage *));
|
||||
RFCMessage ** messages = (RFCMessage **)dummy;
|
||||
|
||||
//
|
||||
// Now we go thru the list again, this time looking for
|
||||
// all of the parts. Place them in the correct order
|
||||
// in the list.
|
||||
//
|
||||
for (offset = 0 ; offset < _partialListCount ; offset++) {
|
||||
if (strcasecmp(_partialList[offset]->id, data->id) == 0) {
|
||||
messages[_partialList[offset]->number - 1]
|
||||
= _partialList[offset]->msg;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// If all of the parts are IN, then assemble them into
|
||||
// a new message.
|
||||
//
|
||||
int totalSize = 0; // Total size of all of the parts.
|
||||
char *messageLength;
|
||||
|
||||
for (offset = 0 ; offset < totalParts ; offset++) {
|
||||
if (messages[offset] == NULL) {
|
||||
break; // Found a missing part, do not assemble.
|
||||
}
|
||||
messageLength = getNamedHeader(error, "Content-Length",
|
||||
messages[offset]);
|
||||
if (messageLength == NULL || error.isSet()) {
|
||||
break; // No way to assemble if we do not know the size.
|
||||
}
|
||||
totalSize += atoi(messageLength); // Bump the total size.
|
||||
free(messageLength);
|
||||
}
|
||||
if (offset == totalParts) { // Did we find all of the parts ?
|
||||
|
||||
//
|
||||
// From RFC 1521, section 7.3.2:
|
||||
//
|
||||
// When generating and reassembling the parts of a message/partial
|
||||
// message, the headers of the encapsulated message must be merged with
|
||||
// the headers of the enclosing entities. In this process the
|
||||
// following rules must be observed:
|
||||
//
|
||||
// (1) All of the header fields from the initial enclosing entity
|
||||
// (part one), except those that start with "Content-" and the
|
||||
// specific header fields "Message-ID", "Encrypted", and
|
||||
// "MIME-Version", must be copied, in order, to the new message.
|
||||
//
|
||||
// (2) Only those header fields in the enclosed message which start
|
||||
// with "Content-" and "Message-ID", "Encrypted", and "MIME-Version"
|
||||
// must be appended, in order, to the header fields of the new
|
||||
// message. Any header fields in the enclosed message which do not
|
||||
// start with "Content-" (except for "Message-ID", "Encrypted", and
|
||||
// "MIME-Version") will be ignored.
|
||||
//
|
||||
// (3) All of the header fields from the second and any subsequent
|
||||
// messages will be ignored.
|
||||
|
||||
//
|
||||
// Pick 10% more space for the headers, or a random size for
|
||||
// the new message.
|
||||
//
|
||||
totalSize = (totalSize > 10*1024) ? (int) (totalSize*1.1) : (20*1024);
|
||||
|
||||
char * newMessage = (char *)malloc(totalSize);
|
||||
|
||||
newMessage[0] = '\0';
|
||||
|
||||
//
|
||||
// Copy over all of the headers from the outer 1st message
|
||||
// except the ones that we are not suppost to copy.
|
||||
//
|
||||
|
||||
RFCEnvelope * env
|
||||
= (RFCEnvelope *)messages[0]->getEnvelope(error);
|
||||
|
||||
DtMailHeaderHandle headerHandle;
|
||||
DtMailValueSeq headerValue;
|
||||
char * headerName;
|
||||
const char * unixFromLine = NULL;
|
||||
|
||||
unsigned int duplicateCount;
|
||||
int fromLen;
|
||||
int headerNumber = 0;
|
||||
register unsigned int dupOffset;
|
||||
|
||||
if (error.isNotSet()) {
|
||||
//
|
||||
// Get the first header.
|
||||
//
|
||||
headerHandle = env->getFirstHeader(error, &headerName, headerValue);
|
||||
|
||||
unixFromLine = env->unixFrom(error, fromLen);
|
||||
if (unixFromLine != NULL) {
|
||||
strncat(newMessage, unixFromLine, fromLen);
|
||||
newMessage[fromLen] = '\0';
|
||||
}
|
||||
if (error.isNotSet()) {
|
||||
|
||||
//
|
||||
// Keep geting more (all) of the headers.
|
||||
//
|
||||
do {
|
||||
if ((strncasecmp(headerName, "Content-", 8) != 0)
|
||||
&& (strcasecmp(headerName, "Message-ID") != 0)
|
||||
&& (strcasecmp(headerName, "Encrypted") != 0)
|
||||
&& (strcasecmp(headerName, "MIME-Version") != 0)
|
||||
&& (strcasecmp(headerName, "Status") != 0)) {
|
||||
|
||||
|
||||
//
|
||||
// If there is more than one value for each type,
|
||||
// then get all of the values.
|
||||
//
|
||||
duplicateCount = headerValue.length();
|
||||
for (dupOffset=0; dupOffset < duplicateCount; dupOffset++ ) {
|
||||
|
||||
//
|
||||
// Skip if the first line is a from.
|
||||
//
|
||||
if (unixFromLine != NULL && headerNumber == 0) {
|
||||
continue;
|
||||
}
|
||||
strcat(newMessage, headerName);
|
||||
strcat(newMessage, ": ");
|
||||
|
||||
strcat(newMessage, *(headerValue[dupOffset]));
|
||||
strcat(newMessage, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Clear out the current one, so that getNextHeader
|
||||
// does not append.
|
||||
//
|
||||
headerValue.clear();
|
||||
|
||||
headerHandle = env->getNextHeader(error,
|
||||
headerHandle,
|
||||
&headerName,
|
||||
headerValue);
|
||||
|
||||
headerNumber++;
|
||||
//
|
||||
// getNextHeader returns NULL where there are no more.
|
||||
//
|
||||
} while(error.isNotSet() && headerHandle != NULL);
|
||||
|
||||
//
|
||||
headerValue.clear();
|
||||
|
||||
//
|
||||
// Now add in the headers from the imbeded message in part 1.
|
||||
// To do this we need to convert the message to an envelope
|
||||
// and then get the headers from that.
|
||||
//
|
||||
// We then delete the envelope as it is not longer needed.
|
||||
//
|
||||
RFCBodyPart * bp
|
||||
= (RFCBodyPart *)messages[0]->getFirstBodyPart(error);
|
||||
|
||||
unsigned long length;
|
||||
|
||||
int embHeader1StLen;
|
||||
|
||||
const void * contents;
|
||||
const char * embHeader1St;
|
||||
char * endHeader;
|
||||
|
||||
//
|
||||
// Get the body part of the first message. It contains
|
||||
// the headers that we need to merge with.
|
||||
//
|
||||
if (error.isNotSet()) {
|
||||
bp->getContents(error, &contents, &length,
|
||||
NULL, NULL, NULL, NULL);
|
||||
|
||||
if (error.isNotSet()) {
|
||||
//
|
||||
// Turn the 1st messages body into an envelope.
|
||||
//
|
||||
// Headers end with two new lines.
|
||||
//
|
||||
embHeader1StLen = (int) length;
|
||||
embHeader1St = (const char *)contents;
|
||||
endHeader = strstr((const char *)contents, "\n\n");
|
||||
|
||||
if (endHeader != NULL) {
|
||||
RFCEnvelope embEnv(error,
|
||||
(DtMail::Message *)NULL,
|
||||
(const char *)contents,
|
||||
(int)((unsigned long)endHeader
|
||||
- (unsigned long)contents));
|
||||
|
||||
if (error.isNotSet()) {
|
||||
|
||||
//
|
||||
// Get the first header from the embeded message.
|
||||
//
|
||||
headerHandle = embEnv.getFirstHeader(error,
|
||||
&headerName,
|
||||
headerValue);
|
||||
|
||||
if (error.isNotSet()) {
|
||||
|
||||
//
|
||||
// Keep geting more (all) of the headers.
|
||||
//
|
||||
do {
|
||||
if ((strncasecmp(headerName, "Content-", 8) == 0)
|
||||
|| (strcasecmp(headerName, "Message-ID") == 0)
|
||||
|| (strcasecmp(headerName, "Encrypted") == 0)
|
||||
|| (strcasecmp(headerName, "MIME-Version") == 0)) {
|
||||
|
||||
//
|
||||
// If there is more than one value for each type,
|
||||
// then get all of the values.
|
||||
//
|
||||
duplicateCount = headerValue.length();
|
||||
for (dupOffset=0
|
||||
; dupOffset < duplicateCount
|
||||
; dupOffset++ ) {
|
||||
|
||||
strcat(newMessage, headerName);
|
||||
strcat(newMessage, ": ");
|
||||
strcat(newMessage, *(headerValue[dupOffset]));
|
||||
strcat(newMessage, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Clear out the current one, so that getNextHeader
|
||||
// does not append.
|
||||
//
|
||||
headerValue.clear();
|
||||
headerHandle = embEnv.getNextHeader(error,
|
||||
headerHandle,
|
||||
&headerName,
|
||||
headerValue);
|
||||
//
|
||||
// getNextHeader returns NULL where there are no more.
|
||||
//
|
||||
} while(error.isNotSet() && headerHandle != NULL);
|
||||
//
|
||||
headerValue.clear();
|
||||
strcat(newMessage, "\n"); // End of headers.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error.isNotSet()) {
|
||||
//
|
||||
// Now add the data parts into the one part.
|
||||
//
|
||||
size_t messageSize = strlen(newMessage);
|
||||
|
||||
length = embHeader1StLen - (&endHeader[2] - embHeader1St);
|
||||
|
||||
strncat(newMessage,
|
||||
(const char *)&endHeader[2], // After \n\n.
|
||||
(size_t) length);
|
||||
|
||||
messageSize += (size_t) length;
|
||||
newMessage[messageSize] = '\0';
|
||||
|
||||
//
|
||||
// Add in the 2nd -> Nth body parts.
|
||||
//
|
||||
for (offset = 1; offset < totalParts ; offset++) {
|
||||
bp =(RFCBodyPart *)messages[offset]->getFirstBodyPart(error);
|
||||
if (error.isSet()) {
|
||||
break;
|
||||
}
|
||||
|
||||
bp->getContents(error, &contents, &length,
|
||||
NULL, NULL, NULL, NULL);
|
||||
if (error.isSet()) {
|
||||
break;
|
||||
}
|
||||
|
||||
strncat(newMessage, (const char *)contents, (size_t) length);
|
||||
messageSize += (size_t) length;
|
||||
newMessage[messageSize] = '\0';
|
||||
}
|
||||
//
|
||||
// Make the new Message, we strdup() so we only use
|
||||
// as much space as needed (and free() old).
|
||||
//
|
||||
const char *msgResults = strdup(newMessage);
|
||||
free(newMessage);
|
||||
newMessage = (char *)msgResults;
|
||||
|
||||
msg = new RFCMessage(error, this,
|
||||
(const char **)&msgResults,
|
||||
(const char *)newMessage + messageSize);
|
||||
|
||||
for (offset = 0; offset < totalParts ; offset++) {
|
||||
//
|
||||
// Delete the old parts.
|
||||
//
|
||||
messages[offset]->setFlag(error, DtMailMessageDeletePending);
|
||||
|
||||
//
|
||||
// TODO - CALL EXPUNGE to get rid of the message that
|
||||
// we just marked for delete !!!!!
|
||||
//
|
||||
|
||||
messages[offset] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (messages != NULL) {
|
||||
free(messages);
|
||||
}
|
||||
}
|
||||
free(type);
|
||||
}
|
||||
return(msg); // This could be the new or the original message.
|
||||
}
|
||||
|
||||
1104
cde/programs/dtmail/libDtMail/RFC/RFCBodyPart.C
Normal file
1104
cde/programs/dtmail/libDtMail/RFC/RFCBodyPart.C
Normal file
File diff suppressed because it is too large
Load Diff
1077
cde/programs/dtmail/libDtMail/RFC/RFCEnvelope.C
Normal file
1077
cde/programs/dtmail/libDtMail/RFC/RFCEnvelope.C
Normal file
File diff suppressed because it is too large
Load Diff
224
cde/programs/dtmail/libDtMail/RFC/RFCFormat.C
Normal file
224
cde/programs/dtmail/libDtMail/RFC/RFCFormat.C
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
*+SNOTICE
|
||||
*
|
||||
*
|
||||
* $TOG: RFCFormat.C /main/11 1998/07/23 18:03:02 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 <ctype.h>
|
||||
#include <EUSCompat.h>
|
||||
#include <DtMail/IO.hh>
|
||||
#include "RFCFormat.hh"
|
||||
#include "str_utils.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
RFCFormat::RFCFormat(DtMail::Session * session)
|
||||
{
|
||||
_session = session;
|
||||
_is_write_bcc = DTM_FALSE;
|
||||
}
|
||||
|
||||
RFCFormat::~RFCFormat(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
RFCFormat::msgToBuffer(DtMailEnv & error,
|
||||
DtMail::Message & msg,
|
||||
DtMailBoolean include_content_length,
|
||||
DtMailBoolean include_unix_from,
|
||||
DtMailBoolean,
|
||||
Buffer & headers,
|
||||
Buffer & body)
|
||||
{
|
||||
error.clear();
|
||||
|
||||
_use_cr = DTM_TRUE;
|
||||
|
||||
if (include_content_length || include_unix_from) {
|
||||
_use_cr = DTM_FALSE;
|
||||
}
|
||||
|
||||
// We will format the bodies first, then the headers. The
|
||||
// reason we do this is that the result of formatting the body
|
||||
// is required for the headers. The content-type, content-length,
|
||||
// and other content based headers need to be computed before
|
||||
// being written.
|
||||
//
|
||||
char * extra_headers = NULL;
|
||||
formatBodies(error, msg, include_content_length, &extra_headers, body);
|
||||
if (error.isSet()) {
|
||||
return;
|
||||
}
|
||||
|
||||
formatHeaders(error, msg, include_unix_from, extra_headers, headers);
|
||||
free(extra_headers);
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
RFCFormat::writeHeaders(DtMailEnv & error,
|
||||
DtMail::Message & msg,
|
||||
DtMailBoolean include_unix_from,
|
||||
const char * extra_headers,
|
||||
const char ** suppress_headers,
|
||||
Buffer & buf)
|
||||
{
|
||||
error.clear();
|
||||
|
||||
// First we copy each header from the message to the
|
||||
// buffer. The headers may need encoding to put them away, so
|
||||
// we will apply RFC1522 if necessary.
|
||||
//
|
||||
DtMailHeaderHandle hnd;
|
||||
DtMail::Envelope * env = msg.getEnvelope(error);
|
||||
if (error.isSet()) {
|
||||
return;
|
||||
}
|
||||
|
||||
char * name = NULL;
|
||||
DtMailValueSeq value;
|
||||
|
||||
hnd = env->getFirstHeader(error, &name, value);
|
||||
if (!hnd || error.isSet()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (include_unix_from &&
|
||||
(error.isSet() || strcmp(name, "From") != 0)) {
|
||||
// We require a Unix from line, and we don't have one.
|
||||
// we will make one up using the sender, and the current
|
||||
// date.
|
||||
//
|
||||
char *unix_from = new char[100];
|
||||
strcpy(unix_from, "From ");
|
||||
|
||||
DtMailValueSeq sender;
|
||||
env->getHeader(error, DtMailMessageSender, DTM_TRUE, sender);
|
||||
if (error.isSet()) {
|
||||
// We no longer know who this is really from.
|
||||
//
|
||||
strcat(unix_from, "nobody@nowhere");
|
||||
}
|
||||
else {
|
||||
DtMailAddressSeq * addrSeq = sender[0]->toAddress();
|
||||
|
||||
strcat(unix_from, (*addrSeq)[0]->dtm_address);
|
||||
delete addrSeq;
|
||||
}
|
||||
error.clear();
|
||||
|
||||
time_t now = time(NULL);
|
||||
char time_format[30];
|
||||
|
||||
SafeCtime(&now, time_format, sizeof(time_format));
|
||||
|
||||
strcat(unix_from, " ");
|
||||
strcat(unix_from, time_format);
|
||||
buf.appendData(unix_from, strlen(unix_from));
|
||||
delete [] unix_from;
|
||||
}
|
||||
else {
|
||||
// Put out any header, except Unix From line
|
||||
//
|
||||
if (strcmp(name, "From") == 0) {
|
||||
value.clear();
|
||||
free(name);
|
||||
hnd = env->getNextHeader(error, hnd, &name, value);
|
||||
}
|
||||
}
|
||||
|
||||
for (; // First time is determined above.
|
||||
hnd && !error.isSet();
|
||||
value.clear(), hnd = env->getNextHeader(error, hnd, &name, value)) {
|
||||
|
||||
for (const char ** hdr = suppress_headers; *hdr; hdr++) {
|
||||
if (strcasecmp(name, *hdr) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
//add _is_write_bcc for fixing aix defect 177096
|
||||
if (*hdr || strcasecmp(name, "bcc") == 0 && !_is_write_bcc ) {
|
||||
free(name);
|
||||
continue; // We will generate these headers.
|
||||
}
|
||||
|
||||
int name_len = strlen(name);
|
||||
|
||||
for (int val = 0; val < value.length(); val++) {
|
||||
//
|
||||
// If the value is null or empty do not emit this field
|
||||
for (const char *valPtr = *(value[val]);
|
||||
*valPtr && (isspace((unsigned char)*valPtr));
|
||||
valPtr++)
|
||||
{}
|
||||
if (!*valPtr)
|
||||
continue;
|
||||
|
||||
buf.appendData(name, name_len);
|
||||
buf.appendData(": ", 2);
|
||||
rfc1522cpy(buf, *(value[val]));
|
||||
}
|
||||
|
||||
free(name);
|
||||
}
|
||||
error.clear();
|
||||
|
||||
buf.appendData(extra_headers, strlen(extra_headers));
|
||||
|
||||
// Add new line that terminates the headers.
|
||||
//
|
||||
crlf(buf);
|
||||
}
|
||||
|
||||
void
|
||||
RFCFormat::rfc1522cpy(Buffer & buf, const char * value)
|
||||
{
|
||||
buf.appendData(value, strlen(value));
|
||||
crlf(buf);
|
||||
}
|
||||
|
||||
void
|
||||
RFCFormat::getCharSet(char * charset)
|
||||
{
|
||||
char *mimeCS = NULL;
|
||||
|
||||
mimeCS = _session->targetTagName();
|
||||
|
||||
if (mimeCS) {
|
||||
strcpy(charset, mimeCS);
|
||||
free(mimeCS);
|
||||
} else {
|
||||
strcpy(charset, "us-ascii"); /* default MIME codeset */
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RFCFormat::getCharSet(char * charset, char *special)
|
||||
{
|
||||
char *mimeCS = NULL;
|
||||
|
||||
mimeCS = _session->targetTagName(special);
|
||||
|
||||
if (mimeCS) {
|
||||
strcpy(charset, mimeCS);
|
||||
free(mimeCS);
|
||||
} else {
|
||||
strcpy(charset, "us-ascii"); /* default MIME codeset */
|
||||
}
|
||||
}
|
||||
98
cde/programs/dtmail/libDtMail/RFC/RFCFormat.hh
Normal file
98
cde/programs/dtmail/libDtMail/RFC/RFCFormat.hh
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
*+SNOTICE
|
||||
*
|
||||
*
|
||||
* $XConsortium: RFCFormat.hh /main/4 1996/04/02 16:20:29 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
|
||||
*/
|
||||
|
||||
#ifndef I_HAVE_NO_IDENT
|
||||
#endif
|
||||
|
||||
#ifndef _RFCFORMAT_HH
|
||||
#define _RFCFORMAT_HH
|
||||
|
||||
#include <DtMail/DtMail.hh>
|
||||
#include <DtMail/Buffer.hh>
|
||||
|
||||
class RFCFormat : public DtCPlusPlusAllocator {
|
||||
public:
|
||||
RFCFormat(DtMail::Session * session);
|
||||
~RFCFormat(void);
|
||||
|
||||
virtual void msgToBuffer(DtMailEnv & error,
|
||||
DtMail::Message & msg,
|
||||
DtMailBoolean include_content_length,
|
||||
DtMailBoolean include_unix_from,
|
||||
DtMailBoolean process_ignores,
|
||||
Buffer & headers,
|
||||
Buffer & body);
|
||||
//call setIsWriteBcc when log message to files so that the BCC
|
||||
// field will log to those files.
|
||||
void setIsWriteBcc() { _is_write_bcc = DTM_TRUE;}
|
||||
void unsetIsWriteBcc() { _is_write_bcc = DTM_FALSE;}
|
||||
DtMailBoolean getIsWriteBcc() { return _is_write_bcc;}
|
||||
|
||||
protected:
|
||||
DtMail::Session *_session;
|
||||
DtMailBoolean _use_cr;
|
||||
DtMailBoolean _is_write_bcc; // if log to file: Yes
|
||||
// send out: No (RFC 822)
|
||||
|
||||
|
||||
virtual void formatBodies(DtMailEnv & error,
|
||||
DtMail::Message & msg,
|
||||
DtMailBoolean include_content_length,
|
||||
char ** extra_headers,
|
||||
Buffer & buf) = 0;
|
||||
|
||||
virtual void formatHeaders(DtMailEnv & error,
|
||||
DtMail::Message & msg,
|
||||
DtMailBoolean include_unix_from,
|
||||
const char * extra_headers,
|
||||
Buffer & buf) = 0;
|
||||
|
||||
void writeHeaders(DtMailEnv & error,
|
||||
DtMail::Message & msg,
|
||||
DtMailBoolean include_unix_from,
|
||||
const char * extra_headers,
|
||||
const char ** suppress_headers,
|
||||
Buffer & buf);
|
||||
|
||||
virtual void rfc1522cpy(Buffer & buf, const char * value);
|
||||
void getCharSet(char * charset);
|
||||
void getCharSet(char * charset, char *special);
|
||||
int OpenLcxDb(void);
|
||||
void DtXlateStdToOpLocale(char *op, char *std, char *dflt, char **ret);
|
||||
void DtXlateOpToStdLocale(char *op, char *opLoc, char **retLoc, char **ret_retLang, char **retSet);
|
||||
char *targetTagName();
|
||||
|
||||
void crlf(Buffer & buf)
|
||||
{
|
||||
const char * term = (_use_cr ? "\r\n" : "\n");
|
||||
int len = (_use_cr ? 2 : 1);
|
||||
buf.appendData(term, len);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
inline void
|
||||
appendString(Buffer & buf, const char * str)
|
||||
{
|
||||
buf.appendData(str, strlen(str));
|
||||
}
|
||||
|
||||
#endif
|
||||
895
cde/programs/dtmail/libDtMail/RFC/RFCImpl.hh
Normal file
895
cde/programs/dtmail/libDtMail/RFC/RFCImpl.hh
Normal file
@@ -0,0 +1,895 @@
|
||||
/*
|
||||
*+SNOTICE
|
||||
*
|
||||
*
|
||||
* $TOG: RFCImpl.hh /main/22 1998/10/01 17:28:08 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
|
||||
*/
|
||||
|
||||
#ifndef I_HAVE_NO_IDENT
|
||||
#endif
|
||||
|
||||
#ifndef _RFCIMPL_HH
|
||||
#define _RFCIMPL_HH
|
||||
|
||||
#include <DtMail/Buffer.hh>
|
||||
#include <DtMail/DtMail.hh>
|
||||
#include <DtMail/DtMailServer.hh>
|
||||
#include <DtMail/FileShare.hh>
|
||||
#include <DtMail/Threads.hh>
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#if defined(NEED_MMAP_WRAPPER)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <sys/mman.h>
|
||||
#if defined(NEED_MMAP_WRAPPER)
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Structs needed to exec sendmail
|
||||
typedef struct waitentry {
|
||||
struct waitentry * next;
|
||||
int pid;
|
||||
void * data;
|
||||
SubProcessFinishedProc * proc;
|
||||
} waitentry_t;
|
||||
|
||||
typedef struct pipedata {
|
||||
int pid;
|
||||
int status;
|
||||
} pipedata_t;
|
||||
|
||||
typedef enum {
|
||||
DTMBX_LONGLOCK_FAILED_CANCEL, // failed to obtain lock, cancel operation
|
||||
DTMBX_LONGLOCK_FAILED_READONLY, // failed to obtain lock, open read only
|
||||
DTMBX_LONGLOCK_FAILED_READWRITE, // failed to obtain lock,
|
||||
// open read write [user locking override]
|
||||
DTMBX_LONGLOCK_SUCCESSFUL // obtained the lock
|
||||
} DTMBX_LONGLOCK;
|
||||
|
||||
class RFCMailBox;
|
||||
class RFCBodyPart;
|
||||
|
||||
|
||||
class RFCMessage : public DtMail::Message {
|
||||
public:
|
||||
RFCMessage(DtMailEnv & error,
|
||||
DtMail::MailBox * parent,
|
||||
const char ** start, // Also returns the end of the message.
|
||||
const char * end_of_file);
|
||||
|
||||
RFCMessage(DtMailEnv & error,
|
||||
const char * alt_start,
|
||||
const char * alt_end);
|
||||
|
||||
RFCMessage(DtMailEnv & error,
|
||||
DtMail::Session * session,
|
||||
DtMailObjectSpace space,
|
||||
void * arg,
|
||||
DtMailCallback cb,
|
||||
void * client_data);
|
||||
|
||||
virtual ~RFCMessage(void);
|
||||
|
||||
virtual DtMail::Envelope * getEnvelope(DtMailEnv &);
|
||||
|
||||
// This should only be used when you REALLY need to know
|
||||
// how many body parts are present before actually traversing
|
||||
// the body parts (i.e. like when writing a MIME format message).
|
||||
virtual int getBodyCount(DtMailEnv &);
|
||||
|
||||
virtual DtMail::BodyPart * getFirstBodyPart(DtMailEnv &);
|
||||
virtual DtMail::BodyPart * getNextBodyPart(DtMailEnv &,
|
||||
DtMail::BodyPart *);
|
||||
|
||||
virtual DtMail::BodyPart * newBodyPart(DtMailEnv &,
|
||||
DtMail::BodyPart *);
|
||||
|
||||
#ifdef DEAD_WOOD
|
||||
virtual void newBodyPartOrder(DtMailEnv &,
|
||||
DtMail::BodyPart *,
|
||||
const int) { };
|
||||
#endif /* DEAD_WOOD */
|
||||
|
||||
virtual void setFlag(DtMailEnv &,
|
||||
const DtMailMessageState);
|
||||
|
||||
virtual void resetFlag(DtMailEnv &,
|
||||
const DtMailMessageState);
|
||||
|
||||
virtual DtMailBoolean flagIsSet(DtMailEnv &,
|
||||
const DtMailMessageState);
|
||||
|
||||
virtual time_t getDeleteTime(DtMailEnv &);
|
||||
|
||||
virtual void toBuffer(DtMailEnv & error, DtMailBuffer &);
|
||||
|
||||
virtual const char * impl(DtMailEnv &);
|
||||
|
||||
// Methods following this point are specific to RFCMessage.
|
||||
//
|
||||
void markDirty(const int delta);
|
||||
|
||||
void fixMessageLocation(char ** msgHeaderStart, long & msgHeaderLen,
|
||||
char ** msgBodyStart, long &msgBodyLen,
|
||||
int & msgTemporary, long & msgBodyOffset);
|
||||
|
||||
void unfixMessageLocation(char *msgStart, int msgTemporary);
|
||||
|
||||
void adjustMessageLocation(
|
||||
char * oldStart,
|
||||
char * newStart,
|
||||
long newLength,
|
||||
int msgTemporary,
|
||||
long newBodyOffset);
|
||||
|
||||
void pinMessageDown(char ** msgHeaderStart, long & msgHeaderLen,
|
||||
char ** msgBodyStart, long &msgBodyLen);
|
||||
|
||||
RFCMailBox * parent(void) { return (RFCMailBox *)_parent; }
|
||||
|
||||
protected:
|
||||
unsigned long _object_signature;
|
||||
const char * _msg_start;
|
||||
const char * _msg_end;
|
||||
const char * _body_start;
|
||||
SafeScalar<int> _dirty;
|
||||
DtMailBuffer *_msg_buf;
|
||||
|
||||
struct BodyPartCache : public DtCPlusPlusAllocator {
|
||||
RFCBodyPart *body;
|
||||
const char * body_start;
|
||||
};
|
||||
|
||||
DtVirtArray<BodyPartCache *> _bp_cache;
|
||||
|
||||
// _alternativeMultipart -- does message contain alternative messages?
|
||||
// If DTM_FALSE: this message does not contain alternative messages.
|
||||
// .. can ignore: _alt_msg_cache, _alternativeValid
|
||||
// If DTM_TRUE: this message does contain alternative messages.
|
||||
// The body part cache is allocated; however, the pointers within
|
||||
// the cache are copied from the latest valid alternative message
|
||||
// within the alternative message cache, and so should not be
|
||||
// deleted when a message is destroyed and this is true.
|
||||
// The alternative message cache (_alt_msg_cache) contains one or
|
||||
// more messages that map 1 to 1 with the corresponding alternative
|
||||
// messages contained within the entire message.
|
||||
//
|
||||
DtMailBoolean _alternativeMultipart;
|
||||
|
||||
// _alternativeMessage -- is this message an alternative message?
|
||||
// If DTM_FALSE: this is not an alternative message but rather a real
|
||||
// message -- _msg_start, _msg_end, _body_start, etc. are valid.
|
||||
// If DTM_TRUE: this message is an alternative message; _msg_start,
|
||||
// _msg_end, _body_start, etc. demark the alternative message within
|
||||
// the entire real message. The body part cache is fully allocated and
|
||||
// represents all body parts contained within this alternative message.
|
||||
//
|
||||
DtMailBoolean _alternativeMessage;
|
||||
|
||||
// _alternativeValid -- is this alternative message part valid?
|
||||
// If DTM_FALSE: this alternative cannot be displayed or otherwise
|
||||
// acted upon on this system
|
||||
// If DTM_TRUE: this alternative can be displayed or otherwise
|
||||
// acted upon on this system
|
||||
//
|
||||
DtMailBoolean _alternativeValid;
|
||||
|
||||
// _alt_msg_cache is the "alternative message cache", an array of
|
||||
// MIMEBodyParts an RFCMessages, which have _alternativeMessage
|
||||
// set, and contain only those body parts that are associated with a
|
||||
// single alternative message. There is one entry in the array for
|
||||
// each alternative message within the entire message. Each alternative
|
||||
// message will have _alternativeMessage == DTM_TRUE.
|
||||
// This cache is present only if _alternativeMultipart == DTM_TRUE.
|
||||
//
|
||||
struct AlternativeMessageCache : public DtCPlusPlusAllocator {
|
||||
RFCMessage * amc_msg; // -> RFCMessage for alternative
|
||||
RFCBodyPart * amc_body; // -> MIMEBodyPart for alternative
|
||||
const char * amc_body_start; // -> first byte of body
|
||||
const char * amc_body_end; // -> last byte of body
|
||||
};
|
||||
|
||||
DtVirtArray<AlternativeMessageCache *> _alt_msg_cache;
|
||||
|
||||
DtMail::BodyPart * bodyPart(DtMailEnv & error, const int slot);
|
||||
|
||||
int lookupByBody(DtMail::BodyPart *);
|
||||
|
||||
const char * parseMsg(DtMailEnv &, const char * end_of_file);
|
||||
const char * findMsgEnd(DtMailEnv &, const char * end_of_file);
|
||||
void parseBodies(DtMailEnv &);
|
||||
void parseMIMEBodies(DtMailEnv &);
|
||||
void parseMIMETextPlain(DtMailEnv &);
|
||||
void parseMIMEMultipartSubtype(DtMailEnv &, const char * subtype);
|
||||
void parseMIMEMessageSubtype(DtMailEnv &, const char * subtype);
|
||||
void parseMIMEMessageExternalBody(DtMailEnv &);
|
||||
void parseMIMEMultipartMixed(DtMailEnv &, const char * boundary);
|
||||
void parseMIMEMultipartAlternative(DtMailEnv &, const char * boundary);
|
||||
void parseV3Bodies(DtMailEnv &);
|
||||
|
||||
void writeMsg(char * buffer);
|
||||
|
||||
int sizeMIMEBodies(DtMailEnv &);
|
||||
int sizeV3Bodies(DtMailEnv &);
|
||||
|
||||
char * extractBoundary(const char * content_type);
|
||||
DtMailBoolean hasHeaders(const char * buf, const unsigned long size);
|
||||
|
||||
// This friend declaration is used temporarily by copyMsg to copy
|
||||
// messages from RFC->RFC. A more general solution is needed for
|
||||
// FCS.
|
||||
friend class RFCMailBox;
|
||||
};
|
||||
|
||||
class RFCEnvelope : public DtMail::Envelope {
|
||||
public:
|
||||
RFCEnvelope(DtMailEnv & error,
|
||||
DtMail::Message * parent,
|
||||
const char * start,
|
||||
const int len);
|
||||
|
||||
virtual ~RFCEnvelope(void);
|
||||
|
||||
virtual DtMailHeaderHandle getFirstHeader(DtMailEnv &,
|
||||
char ** name,
|
||||
DtMailValueSeq & value);
|
||||
|
||||
virtual DtMailHeaderHandle getNextHeader(DtMailEnv &,
|
||||
DtMailHeaderHandle last,
|
||||
char ** name,
|
||||
DtMailValueSeq & value);
|
||||
|
||||
virtual void getHeader(DtMailEnv &,
|
||||
const char * name,
|
||||
const DtMailBoolean abstract,
|
||||
DtMailValueSeq & value);
|
||||
|
||||
virtual void setHeaderSeq(DtMailEnv &,
|
||||
const char *,
|
||||
const DtMailValueSeq &) {} ;
|
||||
|
||||
// The last parameter is left to the client to provide
|
||||
// because it can not be done in a type safe manner.
|
||||
//
|
||||
virtual void setHeader(DtMailEnv &,
|
||||
const char *,
|
||||
const DtMailBoolean,
|
||||
const char *);
|
||||
|
||||
virtual void removeHeader(DtMailEnv &,
|
||||
const char *);
|
||||
|
||||
// Methods below this point are specific to RFCEnvelope.
|
||||
//
|
||||
int dirty(void) { return _dirty; }
|
||||
|
||||
void adjustHeaderLocation(char * headerStart, int headerLength);
|
||||
|
||||
char *writeHeaders(char *buf);
|
||||
|
||||
const char * headerLocation(void) { return _header_text; }
|
||||
|
||||
long headerLength(void) { return _header_len; }
|
||||
|
||||
const char * unixFrom(DtMailEnv &, int & length);
|
||||
//
|
||||
// fix for the defect 177527
|
||||
// when a reply-to field is in a message headers, the reply-to is
|
||||
// going to be displayed as the send in RMW's msg list scrolled window
|
||||
// instead of the real sender. The following three public methods are
|
||||
// going to access _use_reply_to.
|
||||
//
|
||||
void setUseReplyTo(void) { _use_reply_to = DTM_TRUE; }
|
||||
void unsetUseReplyTo(void) { _use_reply_to = DTM_FALSE; }
|
||||
DtMailBoolean getUseReplyTo(void) { return _use_reply_to; }
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
DtMailBoolean _use_reply_to;
|
||||
unsigned long _object_signature;
|
||||
SafeScalar<int> _dirty;
|
||||
|
||||
struct ParsedHeader : public DtCPlusPlusAllocator {
|
||||
ParsedHeader(void);
|
||||
~ParsedHeader(void);
|
||||
const char *name_start;
|
||||
int name_len;
|
||||
const char *value_start;
|
||||
int value_len;
|
||||
int alloc_mask; // Tracks updates to value.
|
||||
void * mutex;
|
||||
};
|
||||
|
||||
const char * _header_text;
|
||||
int _header_len;
|
||||
DtVirtArray<ParsedHeader *> _parsed_headers;
|
||||
void * _header_lock;
|
||||
|
||||
void getTransportHeader(DtMailEnv & error,
|
||||
const char * name,
|
||||
DtMailValueSeq & value);
|
||||
|
||||
DtMailBoolean matchName(const ParsedHeader &, const char *);
|
||||
|
||||
void parseUnixFrom(DtMailEnv &,
|
||||
const ParsedHeader &,
|
||||
DtMailValueSeq & value);
|
||||
|
||||
void parseUnixDate(DtMailEnv &,
|
||||
const ParsedHeader &,
|
||||
DtMailValueSeq & value);
|
||||
|
||||
void makeValue(DtMailEnv &,
|
||||
const ParsedHeader &,
|
||||
DtMailValueSeq & value);
|
||||
|
||||
void makeReply(DtMailEnv &,
|
||||
const char * name,
|
||||
DtMailValueSeq & value);
|
||||
|
||||
DtMailBoolean metooAddr(DtMailValueAddress & addr,
|
||||
DtMailAddressSeq & alts,
|
||||
DtMailBoolean allnet);
|
||||
|
||||
void parseHeaders(void);
|
||||
|
||||
int lookupHeader(const char * name, DtMailBoolean real_only = DTM_FALSE);
|
||||
const char * mapName(const char * name);
|
||||
};
|
||||
|
||||
class RFCBodyPart : public DtMail::BodyPart {
|
||||
public:
|
||||
RFCBodyPart(DtMailEnv &,
|
||||
DtMail::Message * parent,
|
||||
const char * start,
|
||||
const int len,
|
||||
RFCEnvelope * body_env);
|
||||
|
||||
virtual void lockContents(DtMailEnv &, const DtMailLock);
|
||||
virtual void unlockContents(DtMailEnv &);
|
||||
|
||||
virtual void getContents(DtMailEnv &,
|
||||
const void ** contents,
|
||||
unsigned long * length,
|
||||
char ** type,
|
||||
char ** name,
|
||||
int * mode,
|
||||
char ** description);
|
||||
|
||||
virtual void setContents(DtMailEnv &,
|
||||
const void * contents,
|
||||
const unsigned long length,
|
||||
const char * type,
|
||||
const char * name,
|
||||
const int mode,
|
||||
const char * description);
|
||||
|
||||
virtual void setFlag(DtMailEnv &,
|
||||
DtMailBodyPartState);
|
||||
|
||||
virtual void resetFlag(DtMailEnv &,
|
||||
DtMailBodyPartState);
|
||||
|
||||
virtual DtMailBoolean flagIsSet(DtMailEnv &,
|
||||
DtMailBodyPartState);
|
||||
|
||||
virtual time_t getDeleteTime(DtMailEnv &);
|
||||
|
||||
virtual void getHeader(DtMailEnv &,
|
||||
const char * name,
|
||||
const DtMailBoolean abstract,
|
||||
DtMailValueSeq & value);
|
||||
|
||||
// For CHARSET
|
||||
//---------------------------------------------------
|
||||
// These methods are duplicated in class BodyPart (also being implemented
|
||||
// in class Session) because RFCFormat and RFCBodyPart need to access
|
||||
// them. Duplicating routines like what is being done here is a workaround
|
||||
// for an implementation bug/hole because there is no class where global
|
||||
// routines can be defined (and be accessed by any class).
|
||||
// class Session is not a proper place to put (global) methods because
|
||||
// not every class can get at Session.
|
||||
// RFCFormat accesses these routines through its private Session handle.
|
||||
// RFCBodyPart does not have a Session handle. Hence the need to duplicate
|
||||
// the following routines.
|
||||
|
||||
virtual int OpenLcxDb(void);
|
||||
virtual void DtXlateStdToOpLocale(char *op, char *std, char *dflt,
|
||||
char **ret);
|
||||
virtual void DtXlateOpToStdLocale(char *op, char *opLoc, char **retLoc,
|
||||
char **ret_retLang, char **retSet);
|
||||
virtual void DtXlateStdToOpCodeset(char *op, char *std, char *dflt,
|
||||
char **ret);
|
||||
virtual void DtXlateMimeToIconv(const char *, const char *, const char *,
|
||||
char **, char **);
|
||||
virtual void DtXlateLocaleToMime(const char *, const char *,
|
||||
const char *, char **);
|
||||
|
||||
virtual char *csToConvName(char *);
|
||||
virtual char *locToConvName();
|
||||
virtual char *targetConvName();
|
||||
virtual char *targetTagName();
|
||||
virtual int csConvert(char **, unsigned long &, int, char *, char *);
|
||||
//---------------------------------------------------
|
||||
|
||||
// Implement in both MIMEBodyPart and V3BodyPart
|
||||
virtual char *csFromContentType(DtMailValueSeq & value) = 0;
|
||||
// End of For CHARSET
|
||||
|
||||
#ifdef DEAD_WOOD
|
||||
virtual DtMailChecksumState checksum(DtMailEnv &) = 0;
|
||||
#endif /* DEAD_WOOD */
|
||||
|
||||
// Methods below this point are specific to RFCBodyPart.
|
||||
//
|
||||
virtual char *writeBodyParts(char * buf) = 0;
|
||||
|
||||
void adjustBodyPartsLocation(char * start);
|
||||
|
||||
virtual int rfcSize(const char * boundary, DtMailBoolean &) = 0;
|
||||
|
||||
protected:
|
||||
~RFCBodyPart(void);
|
||||
DtMailBoolean isTerm(const char *); // Determines if we have an eol.
|
||||
|
||||
void * _body_lock;
|
||||
|
||||
// _body_start includes any boundaries.
|
||||
const char * _body_start;
|
||||
|
||||
// _body_text is where the text of the body begins, after
|
||||
// boundaries, headers, and other uninteresting stuff.
|
||||
//
|
||||
const char * _body_text;
|
||||
int _body_len;
|
||||
RFCEnvelope * _body_env;
|
||||
DtMailBoolean _my_env; // True if we made the envelope.
|
||||
SafeScalar<int> _dirty;
|
||||
|
||||
friend class RFCMessage;
|
||||
|
||||
// The following entries are a computed cache. They
|
||||
// are built in a lazy fashion, based on the caller's
|
||||
// requests. These entries can be very expensive to
|
||||
// compute so lazy is important.
|
||||
//
|
||||
char * _body;
|
||||
DtMailBoolean _must_free_body;
|
||||
int _body_decoded_len;
|
||||
char * _body_type;
|
||||
|
||||
// The sub classes implement these because they are the major difference
|
||||
// in the get/set contents methods.
|
||||
//
|
||||
virtual void getContentType(DtMailEnv &, char**) = 0;
|
||||
virtual void getDtType(DtMailEnv &) = 0;
|
||||
virtual void loadBody(DtMailEnv &) = 0;
|
||||
virtual char * getDescription(DtMailEnv &) = 0;
|
||||
virtual char * getName(DtMailEnv &) = 0;
|
||||
virtual void setName(DtMailEnv &, const char *) = 0;
|
||||
virtual unsigned long getLength(DtMailEnv &) = 0;
|
||||
virtual const void * getBody(DtMailEnv &);
|
||||
};
|
||||
|
||||
class MIMEBodyPart : public RFCBodyPart {
|
||||
public:
|
||||
MIMEBodyPart(DtMailEnv &,
|
||||
DtMail::Message * parent,
|
||||
const char * start,
|
||||
const int len,
|
||||
RFCEnvelope * body_env);
|
||||
|
||||
MIMEBodyPart(DtMailEnv &,
|
||||
DtMail::Message * parent,
|
||||
const char * start,
|
||||
const char ** end,
|
||||
const char * boundary);
|
||||
|
||||
#ifdef DEAD_WOOD
|
||||
virtual DtMailChecksumState checksum(DtMailEnv &);
|
||||
#endif /* DEAD_WOOD */
|
||||
|
||||
char *writeBodyParts(char * buf);
|
||||
|
||||
int rfcSize(const char * boundary, DtMailBoolean &);
|
||||
|
||||
// For CHARSET
|
||||
virtual char *csFromContentType(DtMailValueSeq & value);
|
||||
|
||||
char * parameterValue(
|
||||
DtMailValueSeq & value,
|
||||
const char* parameter,
|
||||
DtMailBoolean isCaseSensitive);
|
||||
|
||||
protected:
|
||||
friend RFCMessage::~RFCMessage(void);
|
||||
~MIMEBodyPart(void);
|
||||
|
||||
void getContentType(DtMailEnv &, char **);
|
||||
void getDtType(DtMailEnv &);
|
||||
void loadBody(DtMailEnv &);
|
||||
char * getDescription(DtMailEnv &);
|
||||
char * getName(DtMailEnv &);
|
||||
void setName(DtMailEnv &, const char *);
|
||||
char * getNameHeaderVal(DtMailEnv &);
|
||||
unsigned long getLength(DtMailEnv &);
|
||||
const void * getBody(DtMailEnv &);
|
||||
};
|
||||
|
||||
class V3BodyPart : public RFCBodyPart {
|
||||
public:
|
||||
V3BodyPart(DtMailEnv &,
|
||||
DtMail::Message * parent,
|
||||
const char * start,
|
||||
const int len,
|
||||
RFCEnvelope * body_env);
|
||||
|
||||
V3BodyPart(DtMailEnv &,
|
||||
DtMail::Message * parent,
|
||||
const char * start,
|
||||
const char ** end);
|
||||
|
||||
#ifdef DEAD_WOOD
|
||||
virtual DtMailChecksumState checksum(DtMailEnv &);
|
||||
#endif /* DEAD_WOOD */
|
||||
|
||||
char *writeBodyParts(char * buf);
|
||||
|
||||
int rfcSize(const char * boundary, DtMailBoolean &);
|
||||
|
||||
// For CHARSET
|
||||
virtual char *csFromContentType(DtMailValueSeq & value);
|
||||
|
||||
protected:
|
||||
friend RFCMessage::~RFCMessage(void);
|
||||
~V3BodyPart(void);
|
||||
|
||||
void getContentType(DtMailEnv &, char **);
|
||||
void getDtType(DtMailEnv &);
|
||||
void loadBody(DtMailEnv &);
|
||||
char * getDescription(DtMailEnv &);
|
||||
char * getName(DtMailEnv &);
|
||||
void setName(DtMailEnv &, const char *);
|
||||
unsigned long getLength(DtMailEnv &);
|
||||
};
|
||||
|
||||
class RFCMailBox : public DtMail::MailBox
|
||||
{
|
||||
public:
|
||||
|
||||
RFCMailBox(DtMailEnv &,
|
||||
DtMail::Session * session,
|
||||
DtMailObjectSpace space,
|
||||
void * arg,
|
||||
DtMailCallback cb,
|
||||
void * clientData,
|
||||
const char * impl_name);
|
||||
|
||||
virtual ~RFCMailBox(void);
|
||||
|
||||
static void appendCB(DtMailEnv&, char *buf, int len, void *clientData);
|
||||
virtual void append(DtMailEnv &error, char *buf, int len);
|
||||
virtual void create(DtMailEnv & error, mode_t = DTMAIL_DEFAULT_CREATE_MODE);
|
||||
virtual void open(DtMailEnv &,
|
||||
DtMailBoolean auto_create = DTM_TRUE,
|
||||
int open_mode = DTMAIL_DEFAULT_OPEN_MODE,
|
||||
mode_t create_mode = DTMAIL_DEFAULT_CREATE_MODE,
|
||||
DtMailBoolean lock_flag = DTM_TRUE,
|
||||
DtMailBoolean auto_parse = DTM_TRUE);
|
||||
virtual void lock();
|
||||
virtual void unlock();
|
||||
virtual void save();
|
||||
|
||||
virtual void callCallback(DtMailCallbackOp, void *);
|
||||
virtual void disableMailRetrieval()
|
||||
{ _mr_allowed = DTM_FALSE; }
|
||||
virtual void enableMailRetrieval()
|
||||
{ _mr_allowed = DTM_TRUE; }
|
||||
virtual void checkForMail(
|
||||
DtMailEnv &,
|
||||
const DtMailBoolean
|
||||
already_locked = DTM_FALSE);
|
||||
virtual void clearMessageSummary(DtMailHeaderLine &);
|
||||
virtual void copyMessage(DtMailEnv &, DtMail::Message *);
|
||||
virtual void copyMailBox(DtMailEnv &, DtMail::MailBox *);
|
||||
virtual void createMailRetrievalAgent(char *passwd=NULL);
|
||||
virtual void deleteMailRetrievalAgent();
|
||||
virtual void expunge(DtMailEnv &);
|
||||
virtual DtMail::Message *getFirstMessage(DtMailEnv &);
|
||||
virtual DtMailMessageHandle getFirstMessageSummary(
|
||||
DtMailEnv &,
|
||||
const DtMailHeaderRequest &,
|
||||
DtMailHeaderLine &);
|
||||
virtual DtMail::Message *getNextMessage(DtMailEnv &, DtMail::Message *);
|
||||
virtual DtMailMessageHandle getNextMessageSummary(
|
||||
DtMailEnv &,
|
||||
DtMailMessageHandle,
|
||||
const DtMailHeaderRequest &,
|
||||
DtMailHeaderLine &);
|
||||
virtual DtMail::Message *getMessage(DtMailEnv &, DtMailMessageHandle);
|
||||
virtual void getMessageSummary(
|
||||
DtMailEnv &,
|
||||
DtMailMessageHandle,
|
||||
const DtMailHeaderRequest &,
|
||||
DtMailHeaderLine &);
|
||||
virtual const char *impl(DtMailEnv & error);
|
||||
#ifdef DEAD_WOOD
|
||||
virtual int messageCount(DtMailEnv &);
|
||||
#endif /* DEAD_WOOD */
|
||||
virtual DtMail::Message *newMessage(DtMailEnv &);
|
||||
virtual DtMailCallbackOp retrieveNewMail(DtMailEnv&);
|
||||
virtual void updateMailRetrievalPassword(char *passwd=NULL);
|
||||
|
||||
// This is not used by any clients!
|
||||
struct MapRegion : public DtCPlusPlusAllocator
|
||||
{
|
||||
char * map_region;
|
||||
unsigned long map_size;
|
||||
char * file_region; // Where the parsing begins.
|
||||
unsigned long file_size;
|
||||
long offset;
|
||||
};
|
||||
|
||||
struct MessageCache : public DtCPlusPlusAllocator {
|
||||
RFCMessage *message;
|
||||
DtMailBoolean delete_pending;
|
||||
};
|
||||
|
||||
// Methods below this point are specific to RFCMailBox.
|
||||
void markDirty(const int delta);
|
||||
void startAutoSave(DtMailEnv & error, DtMailBoolean start=DTM_TRUE);
|
||||
|
||||
protected:
|
||||
|
||||
struct NewMailData : public DtCPlusPlusAllocator
|
||||
{
|
||||
RFCMailBox * self;
|
||||
Condition * object_valid;
|
||||
};
|
||||
|
||||
// Content-Type: message/partial support.
|
||||
struct _partialData
|
||||
{
|
||||
const char * id;
|
||||
unsigned int number;
|
||||
unsigned int total; // 0 == I do not know.
|
||||
RFCMessage * msg;
|
||||
};
|
||||
|
||||
// Keep track of how many messages, and whether we've hit the
|
||||
// EOF. The number of messages is a condition variable because
|
||||
// we might have multiple threads trying to read the state
|
||||
// while the parsing thread is reading the state.
|
||||
//
|
||||
Condition _at_eof;
|
||||
char *_buffer;
|
||||
SafeScalar<int> _dirty; // Write access/dirty state.
|
||||
DtMailBoolean _dot_lock_active;
|
||||
DtMailBoolean _errorLogging; // Extra logging done??
|
||||
int _fd;
|
||||
SafeScalar<unsigned long> _file_size;
|
||||
const char *_impl_name; // Might be V3 or MIME.
|
||||
int _last_check; // For polling only.
|
||||
time_t _last_poll; // For polling only.
|
||||
int _links;
|
||||
DtMailBoolean _lock_flag;
|
||||
FileShare *_lock_obj;
|
||||
DtMailBoolean _lockf_active; // lockf() is being held
|
||||
char * _lockFileName; // lock file name for mailbox
|
||||
DtMailBoolean _long_lock_active;
|
||||
Thread _mbox_daemon;
|
||||
void * _map_lock;
|
||||
DtVirtArray<MapRegion *> _mappings;
|
||||
unsigned long _object_signature;
|
||||
Condition *_object_valid;
|
||||
DtMailBoolean _parsed;
|
||||
char *_real_path;
|
||||
DtMailBoolean _mr_allowed;
|
||||
char *_mra_command;
|
||||
DtMailServer *_mra_server;
|
||||
char *_mra_serverpw;
|
||||
DtVirtArray<MessageCache *> _msg_list;
|
||||
_partialData **_partialList;
|
||||
unsigned int _partialListCount;
|
||||
struct stat _stinfo;
|
||||
NewMailData *_thread_info;
|
||||
DtMailBoolean _tt_lock;
|
||||
char * _uniqueLockId; // unique id for .lock files
|
||||
int _uniqueLockIdLength;
|
||||
DtMailBoolean _use_dot_lock;
|
||||
|
||||
// void CheckPointEvent(DtMailEnv & error);
|
||||
void CheckPointEvent();
|
||||
void ExpungeEvent(DtMailBoolean closing = DTM_FALSE);
|
||||
void NewMailEvent(const DtMailBoolean already_locked = DTM_FALSE);
|
||||
static DtMailBoolean
|
||||
PollEntry(void *);
|
||||
static void *ThreadNewMailEntry(void *);
|
||||
static void *ThreadParseEntry(void *);
|
||||
|
||||
RFCMessage *_assemblePartial(DtMailEnv &error,RFCMessage *Message);
|
||||
DtMailBoolean
|
||||
_isPartial(DtMailEnv &error, RFCMessage *message);
|
||||
DtMailBoolean
|
||||
addressIsMapped(void *addressToCheck);
|
||||
#if defined(linux)
|
||||
void alterPageMappingAdvice(MapRegion *map, int advice = 0);
|
||||
#else
|
||||
void alterPageMappingAdvice(
|
||||
MapRegion *map,
|
||||
int advice = MADV_SEQUENTIAL);
|
||||
#endif
|
||||
int createTemporaryMailboxFile(DtMailEnv &, char *tmp_name);
|
||||
void checkLockFileOwnership(DtMailEnv &);
|
||||
void dotDtmailLockFile(char *, int);
|
||||
void dotDtmailLock(DtMailEnv &);
|
||||
void dotDtmailUnlock(DtMailEnv &);
|
||||
void dumpMaps(const char *);
|
||||
char *generateLockFileName(void);
|
||||
char *generateUniqueLockId(void);
|
||||
void incorporate(
|
||||
DtMailEnv &,
|
||||
const DtMailBoolean already_locked = DTM_FALSE);
|
||||
time_t linkLockFile(DtMailEnv &, char *tempLockFileName);
|
||||
void lockFile(DtMailEnv &);
|
||||
void lockNewMailboxFile(int new_fd);
|
||||
DTMBX_LONGLOCK
|
||||
longLock(DtMailEnv &);
|
||||
void longUnlock(DtMailEnv &);
|
||||
int lookupByMsg(RFCMessage * msg);
|
||||
void mailboxAccessHide(char *prefix);
|
||||
void mailboxAccessShow(time_t mtime, char *prefix);
|
||||
void makeHeaderLine(DtMailEnv & error,
|
||||
int slot,
|
||||
const DtMailHeaderRequest & request,
|
||||
DtMailHeaderLine &);
|
||||
int mapFile(DtMailEnv & error,
|
||||
const DtMailBoolean already_locked = DTM_FALSE,
|
||||
mode_t create_mode = DTMAIL_DEFAULT_CREATE_MODE);
|
||||
MapRegion *mapNewRegion(DtMailEnv &, int fd, unsigned long bytesWritten);
|
||||
int nextNotDel(const int cur);
|
||||
void openRealFile(DtMailEnv &error, int mode, mode_t create_mode);
|
||||
int prevNotDel(const int cur);
|
||||
void parseFile(DtMailEnv & error, int slot);
|
||||
off_t realFileSize(DtMailEnv &error, struct stat *stat_buffer = NULL);
|
||||
void transferLock(int old_fd, int new_fd);
|
||||
void unlockOldMailboxFile(int old_fd);
|
||||
void unlockFile(DtMailEnv &, int fd);
|
||||
void waitForMsgs(int needed);
|
||||
void writeMailBox(DtMailEnv&, DtMailBoolean);
|
||||
void writeToDumpFile(const char* format, ...);
|
||||
};
|
||||
|
||||
class RFCTransport : public DtMail::Transport {
|
||||
public:
|
||||
RFCTransport(DtMailEnv &, DtMail::Session *, DtMailStatusCallback, void *,
|
||||
const char * impl);
|
||||
virtual ~RFCTransport(void);
|
||||
|
||||
virtual DtMailOperationId submit(DtMailEnv &,
|
||||
DtMail::Message *,
|
||||
DtMailBoolean log_msg);
|
||||
|
||||
static void arpaPhrase(const char * list, DtMailAddressSeq & tokens);
|
||||
|
||||
// SendMsgDialog initiates a send. It needs to set information
|
||||
// needed to exec sendmail by calling these initialization functions.
|
||||
// getSendmailReturnProc returns a handler so that SendMsgDialog
|
||||
// can use it in XtAppAddInput.
|
||||
virtual void initTransportData(int fds[2],
|
||||
SubProcessFinishedProc proc, void *ptr);
|
||||
virtual void *getSendmailReturnProc(void);
|
||||
|
||||
|
||||
protected:
|
||||
Condition * _object_valid;
|
||||
char * _impl;
|
||||
|
||||
struct ThreadSimpleData : public DtCPlusPlusAllocator {
|
||||
RFCTransport * self;
|
||||
const char * to;
|
||||
const char * cc;
|
||||
const char * bcc;
|
||||
const char * subject;
|
||||
const char * text;
|
||||
};
|
||||
static void * ThreadSimpleEntry(void * arg);
|
||||
|
||||
void format(DtMailEnv &, DtMail::Message * msg, DtMailBoolean log_msg);
|
||||
void formatSimple(DtMailEnv &,
|
||||
const char * to,
|
||||
const char * cc,
|
||||
const char * bcc,
|
||||
const char * subject,
|
||||
const char * text);
|
||||
|
||||
void deliver(DtMailEnv &,
|
||||
DtMailAddressSeq &,
|
||||
Buffer & headers,
|
||||
Buffer & bodies,
|
||||
DtMailBoolean log_msg,
|
||||
int **log_files,
|
||||
int & log_count);
|
||||
|
||||
void launchSendmail(DtMailEnv & error,
|
||||
Buffer & headers,
|
||||
Buffer & boides,
|
||||
char ** argv);
|
||||
|
||||
char * concatValue(DtMailValueSeq &);
|
||||
void appendAddrs(DtMailAddressSeq & to, DtMailAddressSeq & from);
|
||||
void skinFiles(DtMailAddressSeq &);
|
||||
char * addrToString(DtMailAddressSeq &);
|
||||
int openLogFile(const char * path);
|
||||
void closeLogFiles(int * files, int file_cnt);
|
||||
|
||||
// These functions are needed to exec sendmail
|
||||
static void childHandler(void);
|
||||
void signalRegister(void);
|
||||
int startSubprocess(DtMailEnv &error, char * cmd,
|
||||
Buffer & headers, Buffer & bodies, char **argv);
|
||||
static void sendmailReturnProc(void);
|
||||
|
||||
// Store info from SendMsgDialog
|
||||
// ptr to SendMsgDialog::SendmaliErrorProc
|
||||
SubProcessFinishedProc *_error_proc;
|
||||
// a pointer to SendMsgDialog
|
||||
void *_smd;
|
||||
// get the status from the low and hi bytes returned from wait()
|
||||
inline static int lowByte (int status) { return (status & 0377); }
|
||||
inline static int highByte (int status) { return (lowByte(status >> 8)); }
|
||||
};
|
||||
|
||||
extern const char * RFCDeleteHeader;
|
||||
|
||||
class RFCValue : public DtMailValue {
|
||||
public:
|
||||
RFCValue(const char * str, int size, DtMail::Session *s);
|
||||
RFCValue(const char * str, int size);
|
||||
~RFCValue(void);
|
||||
|
||||
virtual operator const char *(void);
|
||||
|
||||
virtual const char * operator= (const char *);
|
||||
|
||||
virtual DtMailValueDate toDate(void);
|
||||
#ifdef DEAD_WOOD
|
||||
virtual void fromDate(const DtMailValueDate &) { }
|
||||
#endif /* DEAD_WOOD */
|
||||
|
||||
virtual DtMailAddressSeq * toAddress(void);
|
||||
#ifdef DEAD_WOOD
|
||||
virtual void fromAddress(const DtMailAddressSeq &) { }
|
||||
#endif /* DEAD_WOOD */
|
||||
virtual const char * raw(void);
|
||||
|
||||
protected:
|
||||
DtMail::Session *_session;
|
||||
char * _decoded;
|
||||
|
||||
void decodeValue(void);
|
||||
};
|
||||
|
||||
#endif
|
||||
1877
cde/programs/dtmail/libDtMail/RFC/RFCMIME.C
Normal file
1877
cde/programs/dtmail/libDtMail/RFC/RFCMIME.C
Normal file
File diff suppressed because it is too large
Load Diff
117
cde/programs/dtmail/libDtMail/RFC/RFCMIME.hh
Normal file
117
cde/programs/dtmail/libDtMail/RFC/RFCMIME.hh
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
*+SNOTICE
|
||||
*
|
||||
*
|
||||
* $TOG: RFCMIME.hh /main/6 1997/03/28 12:10:13 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
|
||||
*/
|
||||
|
||||
#ifndef I_HAVE_NO_IDENT
|
||||
#endif
|
||||
|
||||
#ifndef _RFCMIME_HH
|
||||
#define _RFCMIME_HH
|
||||
|
||||
#include "RFCFormat.hh"
|
||||
|
||||
class RFCMIME : public RFCFormat {
|
||||
public:
|
||||
RFCMIME(DtMail::Session *);
|
||||
~RFCMIME(void);
|
||||
|
||||
static void readBase64(char * dest, int & size, const char * text,
|
||||
const unsigned long text_len);
|
||||
|
||||
static void readQPrint(char * dest, int & size, const char * text,
|
||||
const unsigned long text_len);
|
||||
|
||||
static void readTextEnriched(char * dest, int & size, const char * text,
|
||||
const unsigned long text_len);
|
||||
|
||||
void writeBase64(Buffer & buf, const char * bp,
|
||||
const unsigned long len);
|
||||
|
||||
void writeQPrint(Buffer & buf, const char * bp,
|
||||
const unsigned long len);
|
||||
|
||||
// digest is:
|
||||
// unsigned char digest[16];
|
||||
//
|
||||
static void md5PlainText(const char * bp,
|
||||
const unsigned long len,
|
||||
unsigned char * digest);
|
||||
|
||||
enum Encoding {
|
||||
MIME_7BIT,
|
||||
MIME_8BIT,
|
||||
MIME_QPRINT,
|
||||
MIME_BASE64
|
||||
};
|
||||
|
||||
protected:
|
||||
virtual void formatBodies(DtMailEnv & error,
|
||||
DtMail::Message & msg,
|
||||
DtMailBoolean include_content_length,
|
||||
char ** extra_headers,
|
||||
Buffer & buf);
|
||||
|
||||
virtual void formatHeaders(DtMailEnv & error,
|
||||
DtMail::Message & msg,
|
||||
DtMailBoolean include_unix_from,
|
||||
const char * extra_headers,
|
||||
Buffer & buf);
|
||||
|
||||
virtual void rfc1522cpy(Buffer & buf, const char * value);
|
||||
|
||||
private:
|
||||
|
||||
void getMIMEType(
|
||||
DtMail::BodyPart * bp,
|
||||
char * mime_type,
|
||||
DtMailBoolean & is_text);
|
||||
Encoding getHdrEncodingType(
|
||||
const char *,
|
||||
const unsigned int,
|
||||
DtMailBoolean,
|
||||
const char *);
|
||||
Encoding getEncodingType(
|
||||
const char *,
|
||||
const unsigned int,
|
||||
DtMailBoolean,
|
||||
int *);
|
||||
Encoding getClearEncoding(
|
||||
const char *,
|
||||
const unsigned int,
|
||||
int *);
|
||||
void writeContentHeaders(
|
||||
Buffer & buf,
|
||||
const char * type,
|
||||
const char * name,
|
||||
const Encoding,
|
||||
const char * digest,
|
||||
DtMailBoolean show_as_attachment,
|
||||
int is2022ASCII );
|
||||
|
||||
void writePlainText(
|
||||
Buffer & buf,
|
||||
const char * bp,
|
||||
const unsigned long len);
|
||||
void owCompat(
|
||||
Buffer & buf, char * type, char * name,
|
||||
char *contents, unsigned long len);
|
||||
};
|
||||
|
||||
#endif
|
||||
5042
cde/programs/dtmail/libDtMail/RFC/RFCMailBox.C
Normal file
5042
cde/programs/dtmail/libDtMail/RFC/RFCMailBox.C
Normal file
File diff suppressed because it is too large
Load Diff
823
cde/programs/dtmail/libDtMail/RFC/RFCMailValues.C
Normal file
823
cde/programs/dtmail/libDtMail/RFC/RFCMailValues.C
Normal file
@@ -0,0 +1,823 @@
|
||||
/*
|
||||
*+SNOTICE
|
||||
*
|
||||
*
|
||||
* $TOG: RFCMailValues.C /main/10 1998/09/03 07:01:06 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
|
||||
*/
|
||||
|
||||
#ifndef I_HAVE_NO_IDENT
|
||||
#endif
|
||||
|
||||
#include <EUSCompat.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#if !defined(USL) && !defined(__uxp__)
|
||||
#include <strings.h>
|
||||
#endif
|
||||
#include <ctype.h>
|
||||
|
||||
#include <DtMail/IO.hh>
|
||||
#include "RFCImpl.hh"
|
||||
#include "RFCMIME.hh"
|
||||
#include "str_utils.h"
|
||||
|
||||
// String values. These assume an RFC format for now. They will
|
||||
// apply RFC1522 coding rules to the strings for dealing with
|
||||
// non-ASCII text in RFC headers.
|
||||
//
|
||||
|
||||
|
||||
RFCValue::RFCValue(const char * str, int size) : DtMailValue(NULL)
|
||||
{
|
||||
_value = (char *)malloc(size + 1);
|
||||
memcpy(_value, str, size);
|
||||
_value[size] = 0;
|
||||
|
||||
_decoded = NULL;
|
||||
}
|
||||
|
||||
RFCValue::RFCValue(const char * str, int size, DtMail::Session *s) : DtMailValue(NULL)
|
||||
{
|
||||
_value = (char *)malloc(size + 1);
|
||||
memcpy(_value, str, size);
|
||||
_value[size] = 0;
|
||||
|
||||
_decoded = NULL;
|
||||
_session = s;
|
||||
}
|
||||
|
||||
RFCValue::~RFCValue(void)
|
||||
{
|
||||
if (_decoded) {
|
||||
free(_decoded);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *
|
||||
decode1522(const char * enc_start, const char * max_end, char **output, DtMail::Session *s)
|
||||
{
|
||||
// Find the end of the encoded region.
|
||||
//
|
||||
int qs = 0;
|
||||
const char *enc_end;
|
||||
for (enc_end = enc_start;
|
||||
*enc_end && enc_end < max_end;
|
||||
enc_end++) {
|
||||
|
||||
if (*enc_end == '?') {
|
||||
qs += 1;
|
||||
if (qs > 3 && *(enc_end + 1) == '=') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (*enc_end != '?') {
|
||||
return(enc_start);
|
||||
}
|
||||
|
||||
enc_end += 1;
|
||||
|
||||
// Pull off the char set name.
|
||||
//
|
||||
const char *cs_end;
|
||||
for (cs_end = enc_start + 2; *cs_end != '?'; cs_end++) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int cs_name_length = cs_end - enc_start - 2;
|
||||
char *cs_name = (char*) malloc(cs_name_length + 1);
|
||||
|
||||
strncpy(cs_name, enc_start + 2, cs_name_length);
|
||||
cs_name[cs_name_length] = 0;
|
||||
|
||||
// Set the encoding method and start of buffer.
|
||||
//
|
||||
char encoding = *(cs_end + 1);
|
||||
const char * buf_start = cs_end + 3;
|
||||
|
||||
switch (toupper(encoding)) {
|
||||
case 'Q':
|
||||
{
|
||||
int len = 0;
|
||||
RFCMIME::readQPrint(*output, len, buf_start, enc_end - buf_start - 1);
|
||||
(*output)[len] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'B':
|
||||
{
|
||||
int len = 0;
|
||||
RFCMIME::readBase64(*output, len, buf_start, enc_end - buf_start - 1);
|
||||
(*output)[len] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Invalid encoding. Assume a false match.
|
||||
if (cs_name != NULL) free(cs_name);
|
||||
return (enc_start);
|
||||
}
|
||||
|
||||
// Do codeset conversion if charset is present
|
||||
char *from_cs = s->csToConvName(cs_name);
|
||||
char *to_cs = s->locToConvName();
|
||||
if ( from_cs && to_cs ) {
|
||||
if ( strcasecmp(from_cs, to_cs) != 0 ) {
|
||||
unsigned long tmplen = (unsigned long) strlen(*output);
|
||||
(void) s->csConvert(&(*output), tmplen, 1, from_cs, to_cs);
|
||||
}
|
||||
}
|
||||
|
||||
if (NULL != from_cs)
|
||||
free( from_cs );
|
||||
if (NULL != to_cs)
|
||||
free ( to_cs );
|
||||
if (NULL != cs_name)
|
||||
free(cs_name);
|
||||
|
||||
return(enc_end);
|
||||
}
|
||||
|
||||
|
||||
RFCValue::operator const char *(void)
|
||||
{
|
||||
if (_decoded) {
|
||||
return(_decoded);
|
||||
}
|
||||
|
||||
decodeValue();
|
||||
|
||||
return(_decoded);
|
||||
}
|
||||
|
||||
const char *
|
||||
RFCValue::operator= (const char * str)
|
||||
{
|
||||
if (_decoded) {
|
||||
free(_decoded);
|
||||
_decoded = NULL;
|
||||
}
|
||||
|
||||
if (_value) {
|
||||
free(_value);
|
||||
}
|
||||
|
||||
_value = strdup(str);
|
||||
|
||||
return(_value);
|
||||
}
|
||||
|
||||
static const char * DaysOfTheWeek[] = {
|
||||
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
||||
};
|
||||
|
||||
static const char * MonthsOfTheYear[] = {
|
||||
"Jan", "Feb", "Mar",
|
||||
"Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep",
|
||||
"Oct", "Nov", "Dec"
|
||||
};
|
||||
|
||||
static int
|
||||
matchDay(const char * start, const char * end)
|
||||
{
|
||||
int len = end - start + 1;
|
||||
|
||||
for (int i = 0; i < 7; i++) {
|
||||
if (strncmp(DaysOfTheWeek[i], start, len) == 0) {
|
||||
return(i);
|
||||
}
|
||||
}
|
||||
|
||||
return(-1);
|
||||
}
|
||||
|
||||
static int
|
||||
matchMonth(const char * start, const char * end)
|
||||
{
|
||||
int len = end - start + 1;
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
if (strncmp(MonthsOfTheYear[i], start, len) == 0) {
|
||||
return(i);
|
||||
}
|
||||
}
|
||||
|
||||
return(-1);
|
||||
}
|
||||
|
||||
static void
|
||||
parseTime(const char * start, const char * end, tm & val)
|
||||
{
|
||||
int size = end - start + 1;
|
||||
|
||||
// Time will be in the form hh:mm:ss where seconds are optional.
|
||||
|
||||
char num_buf[10];
|
||||
strncpy(num_buf, start, 2);
|
||||
num_buf[2] = 0;
|
||||
|
||||
val.tm_hour = (int) strtol(num_buf, NULL, 10);
|
||||
|
||||
strncpy(num_buf, &start[3], 2);
|
||||
num_buf[2] = 0;
|
||||
|
||||
val.tm_min = (int) strtol(num_buf, NULL, 10);
|
||||
|
||||
if (size > 6) {
|
||||
strncpy(num_buf, &start[6], 2);
|
||||
num_buf[2] = 0;
|
||||
val.tm_sec = (int) strtol(num_buf, NULL, 10);
|
||||
}
|
||||
else {
|
||||
val.tm_sec = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static const char * TZNames[] = {
|
||||
"EST", "CST", "MST", "PST"
|
||||
};
|
||||
|
||||
static const char * TZNamesDST[] = {
|
||||
"EDT", "CDT", "MDT", "PDT"
|
||||
};
|
||||
|
||||
static time_t
|
||||
parseTZ(const char * start, const char * end)
|
||||
{
|
||||
int size = end - start + 1;
|
||||
|
||||
// There are at 3 possibilities that we understand. There
|
||||
// is the single letter military time zone. In that case
|
||||
// Z is 0 UTC. A-M is -1 to -12, skipping J. N-Y is +1 to +12
|
||||
// from UTC.
|
||||
//
|
||||
// Lets start with that one because it is the easiest.
|
||||
|
||||
if (size == 1) {
|
||||
int hours_from = 0;
|
||||
if (*start >= 'A' && *start <= 'I') {
|
||||
hours_from = *start - 'A' + 1;
|
||||
}
|
||||
else if (*start >= 'L' && *start <= 'M') {
|
||||
hours_from = *start - 'K' + 10;
|
||||
}
|
||||
else if (*start >= 'N' && *start <= 'Y') {
|
||||
hours_from = ('N' - *start) - 1;
|
||||
}
|
||||
|
||||
return(hours_from * 3600);
|
||||
}
|
||||
|
||||
// The next option is one of the ANSI standard time zones. These
|
||||
// are three letter abbrievations that tell us where DST in in effect.
|
||||
// So, if we have a length of three, lets see if it is in the table.
|
||||
if (size == 3) {
|
||||
// First normal zones.
|
||||
int i;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (strncmp(start, TZNames[i], 3) == 0) {
|
||||
return((5 + i) * -3600);
|
||||
}
|
||||
}
|
||||
|
||||
// Now DST zones
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (strncmp(start, TZNames[i], 3) == 0) {
|
||||
return((4 + i) * -3600);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally we understand +/- HHMM from UTC.
|
||||
if (size == 5) {
|
||||
int sign = (*start == '+') ? 1 : -1;
|
||||
|
||||
char num_buf[10];
|
||||
strncpy(num_buf, &start[1], 2);
|
||||
num_buf[2] = 0;
|
||||
int hours = (int) strtol(num_buf, NULL, 10);
|
||||
|
||||
strncpy(num_buf, &start[3], 2);
|
||||
num_buf[2] = 0;
|
||||
int minutes = (int) strtol(num_buf, NULL, 10);
|
||||
|
||||
return(sign * ((hours * 3600) + (minutes * 60)));
|
||||
}
|
||||
|
||||
// We have no idea at this point, and it is very unlikely that the
|
||||
// text is meaningful to the reader either. Set the zone to UTC and
|
||||
// punt. It is also possible that the text is "UT" or "GMT" in which
|
||||
// case offset 0 is the right answer.
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
DtMailValueDate
|
||||
RFCValue::toDate(void)
|
||||
{
|
||||
DtMailValueDate date;
|
||||
const char * pos = _value;
|
||||
tm new_time;
|
||||
|
||||
memset(&date, 0, sizeof(date));
|
||||
memset(&new_time, 0, sizeof(new_time));
|
||||
|
||||
date.dtm_date = 0;
|
||||
date.dtm_tz_offset_secs = 0;
|
||||
|
||||
// Before doing anything, check to see if _value is valid.
|
||||
// Some messages have no Date string. Return date with zeroed fields
|
||||
// in those cases.
|
||||
|
||||
if (!_value || (strlen(_value) == 0)) return (date);
|
||||
|
||||
// Find the first non-blank
|
||||
for (; *pos && isspace((unsigned char)*pos); pos++) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// There are usually no more than 6 tokens in an RFC date. We will
|
||||
// have a few extras just in case we are given a wierd string.
|
||||
const char *token_begin[12];
|
||||
const char *token_end[12];
|
||||
int n_tokens = 0;
|
||||
|
||||
// Look for the end of each token. Date tokens are white space
|
||||
// separated.
|
||||
while (*pos) {
|
||||
token_begin[n_tokens] = pos;
|
||||
for (; *pos && !isspace((unsigned char)*pos); pos++) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*pos) {
|
||||
token_end[n_tokens++] = pos - 1;
|
||||
}
|
||||
else {
|
||||
token_end[n_tokens++] = pos;
|
||||
}
|
||||
|
||||
for (; *pos && isspace((unsigned char)*pos); pos++) {
|
||||
continue;
|
||||
}
|
||||
// This means the message is most likely corrupted so just bail out
|
||||
if (n_tokens == 12)
|
||||
break;
|
||||
}
|
||||
|
||||
// Some dates will have a comma after the day of the week. We
|
||||
// want to remove that. It will always be the first token if
|
||||
// we have the day of the week.
|
||||
if (*token_end[0] == ',') {
|
||||
token_end[0]--;
|
||||
}
|
||||
|
||||
if (n_tokens < 2) {
|
||||
return(date);
|
||||
}
|
||||
|
||||
// There are two possible formats, and many variations, that we
|
||||
// will see in an RFC message. They are:
|
||||
//
|
||||
// Tue Oct 12 10:36:10 1993
|
||||
// Tue, 12 Oct 1993 10:35:05 PDT
|
||||
//
|
||||
// The first is the 821 format put on by sendmail. The second is
|
||||
// one of the many variants of the 822 format. The big difference
|
||||
// we must detect is "mon dd time year" vs "dd mon year time tz"
|
||||
//
|
||||
// The first qualifier is usually the day of the week. For our purposes,
|
||||
// we will simply throw it away. This information will be recomputed
|
||||
// based on the date and time.
|
||||
|
||||
int this_token = 0;
|
||||
|
||||
int day = matchDay(token_begin[this_token], token_end[this_token]);
|
||||
if (day >= 0) {
|
||||
// Ignore the day.
|
||||
this_token += 1;
|
||||
}
|
||||
|
||||
// This token should either be a numeric day, or an alpha month.
|
||||
// Lets see if it is a month. If so, we know what the rest of
|
||||
// the date will look like.
|
||||
|
||||
int month = matchMonth(token_begin[this_token], token_end[this_token]);
|
||||
if (month >= 0) {
|
||||
new_time.tm_mon = month;
|
||||
|
||||
// Now should be the day of the month.
|
||||
char num_buf[20];
|
||||
this_token += 1;
|
||||
|
||||
if (this_token == n_tokens) {
|
||||
return(date);
|
||||
}
|
||||
|
||||
strncpy(num_buf, token_begin[this_token], 2);
|
||||
num_buf[2] = 0;
|
||||
new_time.tm_mday = (int) strtol(num_buf, NULL, 10);
|
||||
|
||||
this_token += 1;
|
||||
if (this_token == n_tokens) {
|
||||
return(date);
|
||||
}
|
||||
|
||||
parseTime(token_begin[this_token], token_end[this_token], new_time);
|
||||
|
||||
this_token += 1;
|
||||
if (this_token == n_tokens) {
|
||||
return(date);
|
||||
}
|
||||
|
||||
|
||||
// Sometimes the Unix date will include the time zone.
|
||||
//
|
||||
if (isalpha(*token_begin[this_token])) {
|
||||
this_token += 1;
|
||||
if (this_token == n_tokens) {
|
||||
return(date);
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(num_buf, token_begin[this_token], 4);
|
||||
// Don't remove last digit from year and get bad dates in header.
|
||||
num_buf[token_end[this_token] - token_begin[this_token] + 1] = 0;
|
||||
new_time.tm_year = (int) strtol(num_buf, NULL, 10);
|
||||
if (new_time.tm_year > 1900) {
|
||||
new_time.tm_year -= 1900;
|
||||
}
|
||||
|
||||
new_time.tm_isdst = -1;
|
||||
date.dtm_date = SafeMktime(&new_time);
|
||||
date.dtm_tz_offset_secs = (int) timezone;
|
||||
}
|
||||
else {
|
||||
// In this format, we should have a day of the month.
|
||||
char num_buf[20];
|
||||
strncpy(num_buf, token_begin[this_token], 2);
|
||||
num_buf[2] = 0;
|
||||
new_time.tm_mday = (int) strtol(num_buf, NULL, 10);
|
||||
|
||||
this_token += 1;
|
||||
if (this_token == n_tokens) {
|
||||
return(date);
|
||||
}
|
||||
|
||||
// Now the month name.
|
||||
new_time.tm_mon = matchMonth(token_begin[this_token], token_end[this_token]);
|
||||
|
||||
this_token += 1;
|
||||
if (this_token == n_tokens) {
|
||||
return(date);
|
||||
}
|
||||
|
||||
// The year, which is either 2 or 4 digits.
|
||||
int t_size = token_end[this_token] - token_begin[this_token] + 1;
|
||||
strncpy(num_buf, token_begin[this_token], t_size);
|
||||
num_buf[t_size] = 0;
|
||||
new_time.tm_year = (int) strtol(num_buf, NULL, 10);
|
||||
if (new_time.tm_year > 1900) {
|
||||
new_time.tm_year -= 1900;
|
||||
}
|
||||
|
||||
this_token += 1;
|
||||
if (this_token == n_tokens) {
|
||||
return(date);
|
||||
}
|
||||
|
||||
// The time, in the specified time zone.
|
||||
parseTime(token_begin[this_token], token_end[this_token], new_time);
|
||||
|
||||
this_token += 1;
|
||||
if (this_token == n_tokens) {
|
||||
return(date);
|
||||
}
|
||||
|
||||
time_t offset = parseTZ(token_begin[this_token], token_end[this_token]);
|
||||
|
||||
time_t orig_zone = (time_t) timezone;
|
||||
timezone = offset;
|
||||
// Tell "mktime" to figure "dst" on or not.
|
||||
new_time.tm_isdst = -1;
|
||||
|
||||
date.dtm_date = SafeMktime(&new_time);
|
||||
date.dtm_tz_offset_secs = offset;
|
||||
|
||||
timezone = orig_zone;
|
||||
}
|
||||
|
||||
return(date);
|
||||
}
|
||||
|
||||
static char *
|
||||
findParenComment(const char * value)
|
||||
{
|
||||
int in_quote = 0;
|
||||
const char *sparen;
|
||||
for (sparen = value; *sparen; sparen++) {
|
||||
// We must ignore stuff in quotes.
|
||||
//
|
||||
if (*sparen == '"') {
|
||||
if (in_quote) {
|
||||
in_quote = 0;
|
||||
}
|
||||
else {
|
||||
in_quote = 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_quote) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*sparen == '(') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*sparen != '(') {
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
in_quote = 0;
|
||||
const char *lparen;
|
||||
for (lparen = (sparen + 1); *lparen; lparen++) {
|
||||
// We will support nested comments of the form (Joe (Hi) Blow)
|
||||
//
|
||||
if (*lparen == '(') {
|
||||
in_quote += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*lparen == ')') {
|
||||
in_quote -= 1;
|
||||
}
|
||||
|
||||
if (in_quote < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*lparen != ')') {
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
char * comment = (char *)malloc(lparen - sparen + 1);
|
||||
memcpy(comment, (sparen + 1), lparen - sparen - 1);
|
||||
comment[lparen - sparen - 1] = 0;
|
||||
|
||||
return(comment);
|
||||
}
|
||||
|
||||
static char *
|
||||
stripAngleAddr(const char * value)
|
||||
{
|
||||
int in_quote = 0;
|
||||
|
||||
const char *lt;
|
||||
for (lt = value; *lt; lt++)
|
||||
{
|
||||
if (*lt == '"')
|
||||
{
|
||||
if (in_quote) in_quote = 0;
|
||||
else in_quote = 1;
|
||||
continue;
|
||||
}
|
||||
if (in_quote) continue;
|
||||
if (*lt == '<') break;
|
||||
}
|
||||
|
||||
if (*lt != '<') return(NULL);
|
||||
|
||||
in_quote = 0;
|
||||
|
||||
const char *gt;
|
||||
for (gt = (lt + 1); *gt; gt++)
|
||||
{
|
||||
if (*gt == '"')
|
||||
{
|
||||
if (in_quote) in_quote = 0;
|
||||
else in_quote = 1;
|
||||
continue;
|
||||
}
|
||||
if (in_quote) continue;
|
||||
if (*gt == '>') break;
|
||||
|
||||
}
|
||||
|
||||
if (*gt != '>') return(NULL);
|
||||
|
||||
// Copy everything not in the angle brackets.
|
||||
//
|
||||
char * name = (char *)malloc(strlen(value) + 1);
|
||||
char * out = name;
|
||||
|
||||
for (const char * cp = value; *cp; cp++)
|
||||
{
|
||||
if (cp >= lt && cp <= gt) continue;
|
||||
|
||||
*out++ = *cp;
|
||||
}
|
||||
|
||||
*out = 0;
|
||||
|
||||
if (strlen(name) == 0)
|
||||
{
|
||||
free(name);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
return(name);
|
||||
}
|
||||
|
||||
static char *
|
||||
stripQuotesWhiteSpace(const char * value)
|
||||
{
|
||||
int found_alphanum = 0;
|
||||
char *name = NULL;
|
||||
char *out = NULL;
|
||||
|
||||
//
|
||||
// Skip past leading white space.
|
||||
//
|
||||
const char *cp = value;
|
||||
while (isspace(*cp)) cp++;
|
||||
|
||||
//
|
||||
// If there are no quotes, copy and return.
|
||||
//
|
||||
if (*cp != '"')
|
||||
{
|
||||
name = strdup(cp);
|
||||
return name;
|
||||
}
|
||||
|
||||
//
|
||||
// Strip out the quotes.
|
||||
//
|
||||
cp++;
|
||||
|
||||
out = name = (char*) malloc(strlen(value)+1);
|
||||
if (NULL == out) return NULL;
|
||||
|
||||
while (*cp != '"')
|
||||
{
|
||||
*out = *cp;
|
||||
out++;
|
||||
cp++;
|
||||
}
|
||||
|
||||
*out = 0;
|
||||
return name;
|
||||
}
|
||||
|
||||
DtMailAddressSeq *
|
||||
RFCValue::toAddress(void)
|
||||
{
|
||||
// Count the commas, to figure out how big to make the
|
||||
// sequence.
|
||||
//
|
||||
int commas = 3;
|
||||
for (const char * comma = _value; *comma; comma++)
|
||||
if (*comma == ',') commas += 1;
|
||||
|
||||
DtMailAddressSeq * seq = new DtMailAddressSeq(commas);
|
||||
|
||||
if (!_decoded) decodeValue();
|
||||
|
||||
RFCTransport::arpaPhrase(_decoded, *seq);
|
||||
|
||||
// If we have only one address, then let's try to find a comment
|
||||
// so the person can be set. This is trivial to do for one address
|
||||
// and can have a win for displaying the headers in the message
|
||||
// scrolling list.
|
||||
//
|
||||
if (seq->length() == 1)
|
||||
{
|
||||
// This is less than perfect, but we will look for (Name) and
|
||||
// use it first. If we can't find that, then see if we can
|
||||
// find something outside <addr>. If not that, then simply
|
||||
// give up.
|
||||
//
|
||||
DtMailValueAddress * addr = (*seq)[0];
|
||||
|
||||
addr->dtm_person = findParenComment(_decoded);
|
||||
if (!addr->dtm_person)
|
||||
{
|
||||
char *name = stripAngleAddr(_decoded);
|
||||
if (name)
|
||||
{
|
||||
addr->dtm_person = stripQuotesWhiteSpace(name);
|
||||
free(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(seq);
|
||||
}
|
||||
|
||||
const char *
|
||||
RFCValue::raw(void)
|
||||
{
|
||||
return(_value);
|
||||
}
|
||||
|
||||
void
|
||||
RFCValue::decodeValue(void)
|
||||
{
|
||||
// Create the output buffer. We will assume that it is
|
||||
// the header will only shrink by applying RFC1522.
|
||||
//
|
||||
int outleft = strlen(_value);
|
||||
char * output = (char *)malloc(outleft + 2);
|
||||
|
||||
*output = 0;
|
||||
char * cur_c = output;
|
||||
|
||||
char *buf = NULL;
|
||||
|
||||
// Scan the value, looking for =? which indicates the start
|
||||
// of a encoded string.
|
||||
//
|
||||
for (const char * in_c = _value; *in_c; in_c++) {
|
||||
if (*in_c == '=' && *(in_c + 1) == '?') {
|
||||
//
|
||||
// Decode the encoding. Return the last character so the loop
|
||||
// continues to work. Also reset cur_c because the output buffer
|
||||
// has been updated.
|
||||
//
|
||||
// Allocate space for buf to contain rest of output because it
|
||||
// is enough space for the decoded quoted-printable or base64.
|
||||
// If codeset conversion is done, then csConvert will re-allocate
|
||||
// enough space.
|
||||
//
|
||||
size_t _valueLen = strlen(_value);
|
||||
const char *in_c_sav = in_c;
|
||||
|
||||
buf = (char *)malloc(outleft + 2);
|
||||
strcpy(buf, in_c);
|
||||
in_c = decode1522(in_c, _value + _valueLen - 1, &buf, _session);
|
||||
|
||||
if (in_c > in_c_sav) {
|
||||
size_t bufLen = strlen(buf);
|
||||
if (bufLen > outleft) {
|
||||
output =
|
||||
(char*) realloc((char*)output, _valueLen + bufLen + 2);
|
||||
outleft += bufLen;
|
||||
}
|
||||
strncat(output, buf, bufLen);
|
||||
cur_c = output + strlen(output);
|
||||
outleft -= bufLen;
|
||||
free(buf);
|
||||
continue;
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
|
||||
// Just copy the byte and reset the null pointer, unless
|
||||
// we are dealing with carriage return.
|
||||
//
|
||||
if (*in_c != '\r') {
|
||||
if (outleft == 0) {
|
||||
output = (char*) realloc((char*) output, strlen(output) * 2);
|
||||
outleft = strlen(output);
|
||||
}
|
||||
*cur_c = *in_c;
|
||||
cur_c++;
|
||||
*cur_c = 0;
|
||||
outleft--;
|
||||
}
|
||||
}
|
||||
|
||||
// Kill any trailing white space.
|
||||
//
|
||||
*cur_c = 0;
|
||||
for (cur_c -= 1;
|
||||
cur_c >= output && isspace((unsigned char)*cur_c);
|
||||
cur_c--)
|
||||
{
|
||||
*cur_c = 0;
|
||||
}
|
||||
|
||||
_decoded = output;
|
||||
}
|
||||
1690
cde/programs/dtmail/libDtMail/RFC/RFCMessage.C
Normal file
1690
cde/programs/dtmail/libDtMail/RFC/RFCMessage.C
Normal file
File diff suppressed because it is too large
Load Diff
1272
cde/programs/dtmail/libDtMail/RFC/RFCTransport.C
Normal file
1272
cde/programs/dtmail/libDtMail/RFC/RFCTransport.C
Normal file
File diff suppressed because it is too large
Load Diff
1089
cde/programs/dtmail/libDtMail/RFC/SunV3.C
Normal file
1089
cde/programs/dtmail/libDtMail/RFC/SunV3.C
Normal file
File diff suppressed because it is too large
Load Diff
80
cde/programs/dtmail/libDtMail/RFC/SunV3.hh
Normal file
80
cde/programs/dtmail/libDtMail/RFC/SunV3.hh
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
*+SNOTICE
|
||||
*
|
||||
*
|
||||
* $XConsortium: SunV3.hh /main/4 1996/04/21 19:50:07 drk $
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _SUNV3_HH
|
||||
#define _SUNV3_HH
|
||||
|
||||
#include "RFCFormat.hh"
|
||||
|
||||
class SunV3 : public RFCFormat {
|
||||
public:
|
||||
SunV3(DtMail::Session *);
|
||||
~SunV3(void);
|
||||
|
||||
|
||||
static void decode(const char * enc_info,
|
||||
char ** outputBp,
|
||||
int & outputLen,
|
||||
const char * inputBp,
|
||||
const unsigned long inputLen);
|
||||
static void encode(Buffer & buf,
|
||||
const char * path,
|
||||
const char * bp,
|
||||
const unsigned long len);
|
||||
|
||||
protected:
|
||||
virtual void formatBodies(DtMailEnv & error,
|
||||
DtMail::Message & msg,
|
||||
DtMailBoolean include_content_length,
|
||||
char ** extra_headers,
|
||||
Buffer & buf);
|
||||
|
||||
virtual void formatHeaders(DtMailEnv & error,
|
||||
DtMail::Message & msg,
|
||||
DtMailBoolean include_unix_from,
|
||||
const char * extra_headers,
|
||||
Buffer & buf);
|
||||
|
||||
private:
|
||||
|
||||
static int uudecode(char ** outputBp,
|
||||
int & outputLen,
|
||||
const char * inputBp,
|
||||
const unsigned long inputLen);
|
||||
|
||||
static int uncompress(char ** outputBp,
|
||||
int & outputLen,
|
||||
const char * inputBp,
|
||||
const unsigned long inputLen);
|
||||
|
||||
void encode_uue_line(Buffer & buf,
|
||||
const unsigned char * unencodedBp,
|
||||
const unsigned long unencodedLen);
|
||||
|
||||
void uuencode(Buffer & buf,
|
||||
const char * path,
|
||||
const char * bp,
|
||||
const unsigned long len);
|
||||
|
||||
void getV3Type(DtMail::BodyPart *, char *);
|
||||
};
|
||||
|
||||
#endif
|
||||
680
cde/programs/dtmail/libDtMail/RFC/V3BodyPart.C
Normal file
680
cde/programs/dtmail/libDtMail/RFC/V3BodyPart.C
Normal file
@@ -0,0 +1,680 @@
|
||||
/*
|
||||
*+SNOTICE
|
||||
*
|
||||
* $TOG: V3BodyPart.C /main/8 1998/07/23 18:04:38 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 <EUSCompat.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <Dt/Dts.h>
|
||||
|
||||
#include <DtMail/DtMail.hh>
|
||||
#include "RFCImpl.hh"
|
||||
#include "SunV3.hh"
|
||||
#include <DtMail/Threads.hh>
|
||||
#include "str_utils.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
// For CHARSET
|
||||
#include <locale.h>
|
||||
#include <DtHelp/LocaleXlate.h>
|
||||
|
||||
V3BodyPart::V3BodyPart(DtMailEnv & error,
|
||||
DtMail::Message * parent,
|
||||
const char * start,
|
||||
const int len,
|
||||
RFCEnvelope * body_env)
|
||||
: RFCBodyPart(error, parent, start, len, body_env)
|
||||
{
|
||||
// This constructor is used for single body parts. This
|
||||
// includes generic RFC822 messages, i.e. no Mime-Version
|
||||
// field.
|
||||
//
|
||||
// There really isn't anything we can do. We may have to
|
||||
// apply a content encoding, but let's wait until the user
|
||||
// expresses an interest.
|
||||
//
|
||||
error.clear();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// V3BodyPart:V3BodyPart -- retrieve next V3 message body part
|
||||
// Arguments:
|
||||
// DtMailEnv & error --
|
||||
// DtMail::Message * parent --
|
||||
// const char * start -- -> beginning of boundary separator
|
||||
// const char ** end -- -> end of message
|
||||
// Outputs:
|
||||
// *end -- is set to the first byte past the end of the retrieved
|
||||
// part. This either points to the next body part (if any) or
|
||||
// is 1 byte past the end of the message if no further parts.
|
||||
// _body_env -- RFCEnvelope defining entire body part
|
||||
// _body_len -- true length of contained body part (minus headers)
|
||||
// _body_text -- first byte of contained body part (minus headers)
|
||||
// Description:
|
||||
// This constructor is used for multi-part body parts. Given a
|
||||
// pointer to the beginning of the boundary separator and a
|
||||
// pointer to the end of the message, reduce pointers to actual
|
||||
// body part data (minus headers) and update class variables
|
||||
// as appropriate.
|
||||
//
|
||||
|
||||
V3BodyPart::V3BodyPart(DtMailEnv & error,
|
||||
DtMail::Message * parent,
|
||||
const char * start,
|
||||
const char ** end)
|
||||
: RFCBodyPart(error, parent, start, 0, NULL)
|
||||
{
|
||||
// Get past the boundary separator.
|
||||
//
|
||||
const char * body_end;
|
||||
for (body_end = start; body_end <= *end && *body_end != '\n'; body_end++) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (body_end > *end) {
|
||||
// no data after separator?? Bogus body part!
|
||||
_body_len = 0;
|
||||
*end = body_end;
|
||||
return;
|
||||
}
|
||||
|
||||
// The body part headers begin here. We will look from here until
|
||||
// the first blank line for the end of the headers.
|
||||
//
|
||||
body_end += 1; // Chew the newline.
|
||||
const char * env_start = body_end;
|
||||
for (; body_end <= *end; body_end++) {
|
||||
if (*body_end == '\n') {
|
||||
int blank_only = 1;
|
||||
for (const char * blank = body_end + 1;
|
||||
blank <= *end && *blank != '\n'; blank++) {
|
||||
if (!isspace((unsigned char)*blank)) {
|
||||
blank_only = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (blank_only) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_my_env = DTM_TRUE; // indicate that we created this envelope
|
||||
_body_env = new RFCEnvelope(error, parent, env_start, body_end - env_start + 1);
|
||||
|
||||
// Now we need to find the start of the body (the real start, not
|
||||
// where the boundary begins. This is immediately after the newline
|
||||
// that is placed after the end of the headers.
|
||||
//
|
||||
_body_text = body_end + 1;
|
||||
for (; _body_text <= *end && *_body_text != '\n'; _body_text++) {
|
||||
continue;
|
||||
}
|
||||
_body_text += 1;
|
||||
|
||||
// We now need to figure out where the end of the attachment is.
|
||||
// We have 2 choices. Content lines, or content length.
|
||||
//
|
||||
DtMailValueSeq value;
|
||||
|
||||
_body_env->getHeader(error, "x-sun-content-length",
|
||||
DTM_FALSE, value);
|
||||
if (error.isNotSet()) {
|
||||
// Great! content-length is much easier.
|
||||
// We do this by computing a -> where the end of the attachment
|
||||
// is supposed to be as indicated by the specified content length.
|
||||
// If we come to the end of the message altogether, or to the start
|
||||
// of another header body, then we know we have bounded this body part.
|
||||
//
|
||||
int content_length = (int) strtol(*(value[0]), NULL, 10);
|
||||
body_end = _body_text + content_length; // "supposed" end of body part
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (body_end == *end) { // at end of message??
|
||||
// Yes, this is the last header body in this message
|
||||
// We are at the end -- force this body part to end at the
|
||||
// end of the message and use length as computed.
|
||||
// Mark end of this body for caller as 1 byte past end of last body header
|
||||
// to prevent from being called again.
|
||||
//
|
||||
_body_len = content_length;
|
||||
*end += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (body_end > *end) { // past end of message??
|
||||
// Yes, this is the last header body in this message
|
||||
// We are past the end -- force this body part to end at the
|
||||
// end of the message and compute length based upon that assumption.
|
||||
// Mark end of this body for caller as 1 byte past end of last body header
|
||||
// to prevent from being called again.
|
||||
//
|
||||
*end += 1;
|
||||
_body_len = *end - _body_text;
|
||||
return;
|
||||
}
|
||||
|
||||
// V3BodyParts have a "----------" (10 dashes) at the beginning of a
|
||||
// line with no white space before them and a new line at the end to
|
||||
// signal the beginning of an attachment.
|
||||
|
||||
if ((*(body_end-1) == '\n') &&
|
||||
strncmp(body_end, "----------", 10) == 0 &&
|
||||
isTerm(body_end + 10)) { // start of another body header?
|
||||
// Yes, this is one of many header bodies in this message
|
||||
// Mark end of this body for caller as first byte of next body header
|
||||
//
|
||||
assert(body_end < *end); // cant be last
|
||||
_body_len = body_end - _body_text;
|
||||
*end = body_end+1; // current end
|
||||
return;
|
||||
}
|
||||
|
||||
// Chew through white space and test again.
|
||||
//
|
||||
while (isspace((unsigned char)*body_end)) {
|
||||
body_end += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
error.clear();
|
||||
}
|
||||
|
||||
// We didn't have content-length, or it was wrong or bogus.
|
||||
// Try for content lines.
|
||||
//
|
||||
value.clear(); // clear out any previous header(s) retrieved
|
||||
|
||||
_body_env->getHeader(error, "x-sun-content-lines",
|
||||
DTM_FALSE, value);
|
||||
if (error.isNotSet()) {
|
||||
// content-lines requires a bit o' muching first.
|
||||
// We do this by marching through the body counting new lines
|
||||
// until the specified number of lines have been passed. This
|
||||
// is the determined end of the body part.
|
||||
//
|
||||
int content_lines = (int) strtol(*(value[0]), NULL, 10);
|
||||
int lines = 0;
|
||||
for (body_end = _body_text;
|
||||
body_end <= *end && lines < content_lines; body_end++) {
|
||||
if (*body_end == '\n') {
|
||||
lines += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (body_end >= *end) { // at end of message??
|
||||
// Yes, this is the last header body in this message
|
||||
// We are at or past the end -- force this body part to end at the
|
||||
// end of the message and compute length based upon that assumption.
|
||||
// Mark end of this body for caller as 1 byte past end of last body header
|
||||
// to prevent from being called again.
|
||||
//
|
||||
*end += 1;
|
||||
_body_len = *end - _body_text;
|
||||
return;
|
||||
}
|
||||
|
||||
// V3BodyParts have a "----------" (10 dashes) at the beginning of a
|
||||
// line with no white space before them and a new line at the end to
|
||||
// signal the beginning of an attachment.
|
||||
|
||||
if ((*(body_end-1) == '\n') &&
|
||||
strncmp(body_end, "----------", 10) == 0 &&
|
||||
isTerm(body_end + 10) == DTM_TRUE) { // start of another body header?
|
||||
// Yes, this is one of many header bodies in this message
|
||||
// Mark end of this body for caller as first byte of next body header
|
||||
//
|
||||
assert(body_end < *end); // cant be last
|
||||
_body_len = body_end - _body_text;
|
||||
*end = body_end+1; // current end
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
error.clear();
|
||||
}
|
||||
|
||||
// We either didn't have a length, or number of lines, or they
|
||||
// didn't work. We will now apply a dangerous but last resort
|
||||
// effort to find the end of the body by looking for the 10 dash
|
||||
// separator.
|
||||
// V3BodyParts have a "----------" (10 dashes) at the beginning of a
|
||||
// line with no white space before them and a new line at the end to
|
||||
// signal the beginning of an attachment.
|
||||
//
|
||||
|
||||
for (body_end = _body_text; body_end <= (*end - 12); body_end++) {
|
||||
if (*body_end == '\n' &&
|
||||
strncmp(body_end + 1, "----------", 10) == 0 &&
|
||||
isTerm(body_end + 11) == DTM_TRUE) {
|
||||
// Dangerously by successfully located what appears to be the end
|
||||
// of this attachment.
|
||||
//
|
||||
*end = body_end;
|
||||
_body_len = body_end - _body_text;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point we are at the end of the message and are without
|
||||
// the benefit of content lines or content length or the discovery
|
||||
// of what appears to be a message separator. Assume this body part
|
||||
// consumes the remainder of the message. Mark end of this body for
|
||||
// caller as 1 byte past end to prevent from being called again.
|
||||
//
|
||||
*end += 1;
|
||||
_body_len = *end - _body_text;
|
||||
return;
|
||||
}
|
||||
|
||||
V3BodyPart::~V3BodyPart(void)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef DEAD_WOOD
|
||||
DtMailChecksumState
|
||||
V3BodyPart::checksum(DtMailEnv & error)
|
||||
{
|
||||
error.clear();
|
||||
|
||||
return(DtMailCheckUnknown);
|
||||
}
|
||||
#endif /* DEAD_WOOD */
|
||||
|
||||
static int
|
||||
countTypes(char ** types)
|
||||
{
|
||||
if (NULL == types) return 0;
|
||||
|
||||
for (int count = 0; *types; types++, count++) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return(count);
|
||||
}
|
||||
|
||||
void
|
||||
V3BodyPart::getContentType(DtMailEnv &error, char **v3_type)
|
||||
{
|
||||
MutexLock lock_scope(_obj_mutex);
|
||||
MutexLock dt_lib_lock(_DtMutex);
|
||||
DtMailValueSeq value;
|
||||
|
||||
if (v3_type)
|
||||
*v3_type = (char *)0;
|
||||
|
||||
_body_env->getHeader(error, "Content-Type", DTM_FALSE, value);
|
||||
if (error.isNotSet())
|
||||
*v3_type = strdup(*(value[0]));
|
||||
else
|
||||
{
|
||||
error.clear();
|
||||
value.clear();
|
||||
_body_env->getHeader(error, "X-Sun-Data-Type", DTM_FALSE, value);
|
||||
if (error.isNotSet())
|
||||
*v3_type = strdup(*(value[0]));
|
||||
else
|
||||
{
|
||||
error.clear();
|
||||
*v3_type = strdup("text");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
V3BodyPart::getDtType(DtMailEnv & error)
|
||||
{
|
||||
MutexLock lock_scope(_obj_mutex);
|
||||
MutexLock dt_lib_lock(_DtMutex);
|
||||
|
||||
// No need to clear the error object here because it should have
|
||||
// been cleared by the object that instantiated it.
|
||||
// error.clear();
|
||||
|
||||
char * v3_type;
|
||||
DtMailValueSeq value;
|
||||
_body_env->getHeader(error, "Content-Type", DTM_FALSE, value);
|
||||
if (error.isNotSet()) {
|
||||
v3_type = strdup(*(value[0]));
|
||||
}
|
||||
else {
|
||||
error.clear();
|
||||
value.clear();
|
||||
_body_env->getHeader(error, "X-Sun-Data-Type", DTM_FALSE,
|
||||
value);
|
||||
if (error.isNotSet()) {
|
||||
v3_type = strdup(*(value[0]));
|
||||
}
|
||||
else {
|
||||
error.clear();
|
||||
v3_type = strdup("text");
|
||||
}
|
||||
}
|
||||
|
||||
char ** types = DtDtsFindAttribute("SUNV3_TYPE", v3_type);
|
||||
|
||||
if (NULL != types)
|
||||
{
|
||||
if (countTypes(types) == 1) {
|
||||
// We will use the first name. It may be wrong, but
|
||||
// it is the best we can do at this point.
|
||||
//
|
||||
_body_type = strdup(types[0]);
|
||||
DtDtsFreeDataTypeNames(types);
|
||||
free(v3_type);
|
||||
return;
|
||||
}
|
||||
DtDtsFreeDataTypeNames(types);
|
||||
}
|
||||
|
||||
// We need the bits so we can type the buffer and get
|
||||
// a type for the object. This is where things can get
|
||||
// very slow for the user.
|
||||
//
|
||||
loadBody(error);
|
||||
if (error.isSet()) {
|
||||
return;
|
||||
}
|
||||
|
||||
char * name = getName(error);
|
||||
if (error.isSet()) {
|
||||
// don't care about error conditions returned by getName().
|
||||
error.clear();
|
||||
}
|
||||
|
||||
// If the name is "Attachment" and the type is "text", then
|
||||
// use the type as the name to avoid the buffer being called
|
||||
// generic data.
|
||||
//
|
||||
if (name && strcasecmp(name, "attachment") == 0 &&
|
||||
strcasecmp(v3_type, "text") == 0) {
|
||||
free(name);
|
||||
name = strdup(v3_type);
|
||||
}
|
||||
|
||||
char * type = DtDtsBufferToDataType(_body, _body_decoded_len, name);
|
||||
_body_type = strdup(type);
|
||||
DtDtsFreeDataType(type);
|
||||
free(name);
|
||||
free(v3_type);
|
||||
// error.clear();
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
V3BodyPart::loadBody(DtMailEnv &)
|
||||
{
|
||||
// For CHARSET
|
||||
char *to_cs = NULL, *from_cs = NULL;
|
||||
char *cs = new char[64];
|
||||
|
||||
// End of For CHARSET
|
||||
int do_decode = 1;
|
||||
|
||||
if (_body) {
|
||||
delete [] cs;
|
||||
return;
|
||||
}
|
||||
|
||||
// See if we are using an encoding.
|
||||
DtMailValueSeq value;
|
||||
DtMailEnv lerror;
|
||||
_body_env->getHeader(lerror, "X-Sun-Encoding-Info",
|
||||
DTM_FALSE, value);
|
||||
if (lerror.isSet() || (_body_len == 0)) {
|
||||
// No Encodings.
|
||||
lerror.clear();
|
||||
_body = (char *)_body_text;
|
||||
_must_free_body = DTM_FALSE;
|
||||
_body_decoded_len = _body_len;
|
||||
// Before the codeset conversion code was put in, return here.
|
||||
do_decode = 0;
|
||||
}
|
||||
|
||||
if (do_decode) {
|
||||
_body_decoded_len = 0;
|
||||
_must_free_body = DTM_TRUE;
|
||||
|
||||
SunV3::decode(*(value[0]), &_body, _body_decoded_len,
|
||||
_body_text, _body_len);
|
||||
}
|
||||
|
||||
// For CHARSET
|
||||
// If Content-Type is text, then get charset and do conversion.
|
||||
// If Content-Type is X-sun-attachment, then if X-Sun-Data-Type is text,
|
||||
// then get charset and do conversion.
|
||||
// If Content-Type is missing, check if X-Sun-Data-Type exists, and
|
||||
// if X-Sun-Data-Type exists and is text, then get charset and do conversion.
|
||||
// If Content-Type and X-Sun-Data-Type are missing, then assume text so get
|
||||
// charset and do conversion.
|
||||
// Else don't need codeset conversion.
|
||||
const char *cstmp = NULL;
|
||||
const char *ct = NULL;
|
||||
value.clear();
|
||||
_body_env->getHeader(lerror, "Content-Type", DTM_FALSE, value);
|
||||
if (lerror.isNotSet()) {
|
||||
// Sun's V.3 Mail File Format requires a Content-Type header field to be
|
||||
// "X-sun-attachment" for attachment message. X-Sun-Data-Type header
|
||||
// field is also required for attachment message.
|
||||
ct = *(value[0]);
|
||||
if ( strcasecmp(ct, "X-sun-attachment") == 0 ) {
|
||||
value.clear();
|
||||
_body_env->getHeader(lerror, "X-Sun-Data-Type", DTM_FALSE, value);
|
||||
if (lerror.isNotSet()) {
|
||||
ct = *(value[0]);
|
||||
if ( strcasecmp(ct, "text") == 0 ) {
|
||||
// Get charset from X-Sun-Text-Type which contains the name of
|
||||
// the codeset for the text-based body part. It is a mandatory
|
||||
// header iff X-Sun-Data-Type is of type text.
|
||||
value.clear();
|
||||
_body_env->getHeader(lerror, "X-Sun-Text-Type", DTM_FALSE, value);
|
||||
if (lerror.isNotSet()) {
|
||||
cstmp = *(value[0]);
|
||||
strcpy(cs, cstmp);
|
||||
} else {
|
||||
// We are not returning yet. We'll be flexible here.
|
||||
// Some mailers may not set this field. We'll try to obtain charset
|
||||
// from X-Sun-Charset or use the locale default. See below.
|
||||
lerror.clear();
|
||||
}
|
||||
} else { // Attachment not text type
|
||||
delete [] cs;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Required field for attachment message not set -- return!
|
||||
delete [] cs;
|
||||
return;
|
||||
}
|
||||
} else if ( strcasecmp(ct, "text") != 0 ) {
|
||||
delete [] cs;
|
||||
return;
|
||||
}
|
||||
} else { // Content-Type does not exist!
|
||||
lerror.clear();
|
||||
value.clear();
|
||||
_body_env->getHeader(lerror, "X-Sun-Data-Type", DTM_FALSE, value);
|
||||
if (lerror.isNotSet()) {
|
||||
ct = *(value[0]);
|
||||
if ( strcasecmp(ct, "text") == 0 ) {
|
||||
// Get charset from X-Sun-Text-Type which contains the name of
|
||||
// the codeset for the text-based body part. It is a mandatory
|
||||
// header iff X-Sun-Data-Type is of type text.
|
||||
value.clear();
|
||||
_body_env->getHeader(lerror, "X-Sun-Text-Type", DTM_FALSE, value);
|
||||
if (lerror.isNotSet()) {
|
||||
cstmp = *(value[0]);
|
||||
strcpy(cs, cstmp);
|
||||
} else {
|
||||
// We are not returning yet. We'll be flexible here.
|
||||
// Some mailers may not set this field. We'll try to obtain charset
|
||||
// from X-Sun-Charset or use the locale default. See below.
|
||||
lerror.clear();
|
||||
}
|
||||
} else { // Attachment not text type
|
||||
delete [] cs;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Base on Sun's V.3 Mail File Format version 1.9: If no Content-Type,
|
||||
// and X-Sun-Data-Type is not set (means no/not attachment,
|
||||
// then assume text. Proceed with getting charset.
|
||||
lerror.clear();
|
||||
}
|
||||
}
|
||||
if (cstmp == NULL) {
|
||||
value.clear();
|
||||
// Get charset from charset field
|
||||
_body_env->getHeader(lerror, "X-Sun-Charset", DTM_FALSE, value);
|
||||
if (lerror.isNotSet()) {
|
||||
cstmp = *(value[0]);
|
||||
strcpy(cs, cstmp);
|
||||
} else { // No Charset
|
||||
// We'll be flexible here. If Content-Type is missing or if Content-Type
|
||||
// is text, then we get charset but if charset is missing, we'll try to
|
||||
// convert from the locale specific default codeset.
|
||||
lerror.clear();
|
||||
char *ret = NULL;
|
||||
strcpy(cs, "DEFAULT");
|
||||
DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
|
||||
setlocale(LC_CTYPE, NULL),
|
||||
NULL,
|
||||
NULL,
|
||||
&ret);
|
||||
strcpy(cs, "DEFAULT");
|
||||
strcat(cs, ".");
|
||||
strcat(cs, ret);
|
||||
if ( ret )
|
||||
free( ret );
|
||||
}
|
||||
} // If cstmp is NULL
|
||||
|
||||
// Handle ISO-2022-INT, RFC approved, or private encoding names
|
||||
if ( strcasecmp(cs, "ISO-2022-INT-1") == 0 ) {
|
||||
// Need to obtain charset from encoding
|
||||
} // RFC approved and private names are not treated differently.
|
||||
|
||||
// Get iconv name from charset - this is the "from" name.
|
||||
from_cs = NULL;
|
||||
from_cs = csToConvName(cs);
|
||||
|
||||
// Get current locale's iconv name - this is the "to" name.
|
||||
to_cs = NULL;
|
||||
to_cs = locToConvName();
|
||||
|
||||
if ( from_cs && to_cs ) {
|
||||
if ( strcasecmp(from_cs, to_cs) != 0 ) {
|
||||
unsigned long tmp_len = (unsigned long) _body_decoded_len;
|
||||
if (csConvert(&_body, tmp_len, (int)_must_free_body, from_cs, to_cs)) {
|
||||
_must_free_body = DTM_TRUE;
|
||||
_body_decoded_len = (int) tmp_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( from_cs )
|
||||
free( from_cs );
|
||||
if ( to_cs )
|
||||
free ( to_cs );
|
||||
// End of For CHARSET
|
||||
delete [] cs;
|
||||
}
|
||||
|
||||
char *
|
||||
V3BodyPart::getDescription(DtMailEnv & error)
|
||||
{
|
||||
// No need to clear the error object here, whoever created it, cleared it.
|
||||
// error.clear();
|
||||
|
||||
DtMailValueSeq value;
|
||||
_body_env->getHeader(error, "X-Sun-Data-Description",
|
||||
DTM_FALSE, value);
|
||||
if (error.isNotSet()) {
|
||||
return(strdup(*(value[0])));
|
||||
}
|
||||
error.clear(); // clear error so it doesn't propogate back up.
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
char *
|
||||
V3BodyPart::getName(DtMailEnv & error)
|
||||
{
|
||||
// No need to clear the error object here, it should be passed in already
|
||||
// cleared.
|
||||
// error.clear();
|
||||
|
||||
DtMailValueSeq value;
|
||||
_body_env->getHeader(error, "X-Sun-Data-Name",
|
||||
DTM_FALSE, value);
|
||||
if (error.isNotSet()) {
|
||||
return(strdup(*(value[0])));
|
||||
}
|
||||
|
||||
// Since we are returning a valid name in spite of the call to
|
||||
// getHeader returning an error, we should clear the error.
|
||||
//
|
||||
error.clear();
|
||||
return(strdup("Attachment"));
|
||||
}
|
||||
|
||||
void
|
||||
V3BodyPart::setName(DtMailEnv & error, const char * name)
|
||||
{
|
||||
_body_env->setHeader(error, "X-Sun-Data-Name", DTM_TRUE, name);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
V3BodyPart::getLength(DtMailEnv & error)
|
||||
{
|
||||
MutexLock lock_scope(_obj_mutex);
|
||||
|
||||
loadBody(error);
|
||||
if (error.isSet()) {
|
||||
// propogate the error back to the caller
|
||||
return (0);
|
||||
}
|
||||
|
||||
return(_body_decoded_len);
|
||||
}
|
||||
|
||||
int
|
||||
V3BodyPart::rfcSize(const char *, DtMailBoolean &)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
char *
|
||||
V3BodyPart::writeBodyParts(char *buf)
|
||||
{
|
||||
return(buf);
|
||||
}
|
||||
|
||||
// Do not need to implement this method because getHeader already
|
||||
// returns the X-Sun-Charset value.
|
||||
char *
|
||||
V3BodyPart::csFromContentType(DtMailValueSeq&)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
Reference in New Issue
Block a user