cdesktopenv/cde/programs/dtmail/libDtMail/RFC/MIMEPartial.C

561 lines
14 KiB
C

/*
* CDE - Common Desktop Environment
*
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these libraries and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/*
*+SNOTICE
*
*
* $TOG: 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;
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 = NULL;
char * stringEnd;
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 = const_cast <char *> (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)
{
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;
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 = const_cast <char *> (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.
}