gecko-dev/lib/mailto/msgsendp.cpp
1998-10-09 02:48:53 +00:00

809 lines
19 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "msg.h"
#include "ntypes.h"
#include "xlate.h" // Needed to compile msgsend.h
#include "msgsend.h"
#include "msgsendp.h"
#include "libi18n.h"
#ifdef MOZ_ENDER_MIME
#include "secrng.h" /* for RNG_GenerateGlobalRandomBytes() */
#endif /*MOZ_ENDER_MIME*/
extern "C"
{
extern int MK_OUT_OF_MEMORY;
}
static char *mime_mailto_stream_read_buffer = 0;
int32 MSG_SendPart::M_counter = 0;
MSG_SendPart::MSG_SendPart(MSG_SendMimeDeliveryState* state, int16 part_csid)
{
m_type=NULL;
m_parent = NULL;
m_filename = NULL;
m_buffer = NULL;
m_other = NULL;
m_strip_sensitive_headers = FALSE;
m_encoder_data = FALSE;
m_children = NULL;
m_numchildren = 0;
m_firstBlock = FALSE;
m_needIntlConversion = FALSE;
m_mainpart = FALSE;
m_just_hit_CR = FALSE;
m_csid = part_csid;
SetMimeDeliveryState(state);
}
MSG_SendPart::~MSG_SendPart()
{
if (m_encoder_data) {
MimeEncoderDestroy(m_encoder_data, FALSE);
m_encoder_data = NULL;
}
for (int i=0 ; i<m_numchildren ; i++) {
delete m_children[i];
}
delete [] m_children;
delete [] m_buffer;
delete [] m_other;
if (m_filename) delete [] m_filename;
FREEIF(m_type);
}
int MSG_SendPart::CopyString(char** dest, const char* src)
{
XP_ASSERT(src);
if (!src) src = "";
delete [] *dest;
*dest = new char [XP_STRLEN(src) + 1];
if (!*dest) return MK_OUT_OF_MEMORY;
XP_STRCPY(*dest, src);
return 0;
}
int MSG_SendPart::SetFile(const char* filename, XP_FileType type)
{
XP_ASSERT(m_buffer == NULL);
int status = CopyString(&m_filename, filename);
if (status < 0) return status;
m_filetype = type;
return status;
}
int MSG_SendPart::SetBuffer(const char* buffer)
{
XP_ASSERT(m_filename == NULL);
return CopyString(&m_buffer, buffer);
}
int MSG_SendPart::SetType(const char* type)
{
FREEIF(m_type);
m_type = XP_STRDUP(type);
return m_type ? 0 : MK_OUT_OF_MEMORY;
}
int MSG_SendPart::SetOtherHeaders(const char* other)
{
return CopyString(&m_other, other);
}
int MSG_SendPart::SetMimeDeliveryState(MSG_SendMimeDeliveryState *state)
{
m_state = state;
if (GetNumChildren() > 0)
{
for(int i = 0; i < GetNumChildren(); i++)
{
MSG_SendPart *part = GetChild(i);
if (part)
part->SetMimeDeliveryState(state);
}
}
return 0;
}
int MSG_SendPart::AppendOtherHeaders(const char* more)
{
if (!m_other) return SetOtherHeaders(more);
if (!more || !*more) return 0;
char* tmp = new char[XP_STRLEN(m_other) + XP_STRLEN(more) + 2];
if (!tmp) return MK_OUT_OF_MEMORY;
XP_STRCPY(tmp, m_other);
XP_STRCAT(tmp, more);
delete [] m_other;
m_other = tmp;
return 0;
}
int MSG_SendPart::SetEncoderData(MimeEncoderData* data)
{
m_encoder_data = data;
return 0;
}
int MSG_SendPart::SetMainPart(XP_Bool value)
{
m_mainpart = value;
return 0;
}
int MSG_SendPart::AddChild(MSG_SendPart* child)
{
m_numchildren++;
MSG_SendPart** tmp = new MSG_SendPart* [m_numchildren];
if (tmp == NULL) return MK_OUT_OF_MEMORY;
for (int i=0 ; i<m_numchildren-1 ; i++) {
tmp[i] = m_children[i];
}
delete [] m_children;
m_children = tmp;
m_children[m_numchildren - 1] = child;
child->m_parent = this;
return 0;
}
MSG_SendPart *
MSG_SendPart::DetachChild(int32 whichOne)
{
MSG_SendPart *returnValue = NULL;
XP_ASSERT(whichOne >= 0 && whichOne < m_numchildren);
if (whichOne >= 0 && whichOne < m_numchildren)
{
returnValue = m_children[whichOne];
if (m_numchildren > 1)
{
MSG_SendPart** tmp = new MSG_SendPart* [m_numchildren-1];
if (tmp != NULL)
{
// move all the other kids over
for (int i=0 ; i<m_numchildren-1 ; i++)
{
if (i >= whichOne)
tmp[i] = m_children[i+1];
else
tmp[i] = m_children[i];
}
delete [] m_children;
m_children = tmp;
m_numchildren--;
}
}
else
{
delete [] m_children;
m_children = NULL;
m_numchildren = 0;
}
}
if (returnValue)
returnValue->m_parent = NULL;
return returnValue;
}
MSG_SendPart* MSG_SendPart::GetChild(int32 which)
{
XP_ASSERT(which >= 0 && which < m_numchildren);
if (which >= 0 && which < m_numchildren) {
return m_children[which];
}
return NULL;
}
int MSG_SendPart::PushBody(char* buffer, int32 length)
{
#ifndef MOZ_ENDER_MIME
int status = 0;
char* encoded_data = buffer;
/* if this is the first block, create the conversion object
*/
if (m_firstBlock) {
if (m_needIntlConversion) {
m_intlDocToMailConverter =
INTL_CreateDocToMailConverter(m_state->GetContext(),
(!strcasecomp(m_type,
TEXT_HTML)),
(unsigned char*) buffer,
length);
// No conversion is done when mail_csid (ToCSID for the converter) is JIS
// and type is HTML (usually csid is SJIS or EUC for Japanese HTML)
// in order to avoid mismatch META_TAG (bug#104255).
if (m_intlDocToMailConverter != NULL) {
XP_Bool Base64HtmlNoChconv = ((INTL_GetCCCToCSID(m_intlDocToMailConverter) == CS_JIS) &&
!strcasecomp(m_type, TEXT_HTML) &&
(m_encoder_data != NULL));
if (Base64HtmlNoChconv) {
INTL_DestroyCharCodeConverter(m_intlDocToMailConverter);
m_intlDocToMailConverter = NULL;
}
}
}
m_firstBlock = FALSE; /* No longer the first block */
}
if (m_intlDocToMailConverter) {
encoded_data =
(char*)INTL_CallCharCodeConverter(m_intlDocToMailConverter,
(unsigned char*)buffer,
length);
/* the return buffer is different from the */
/* origional one. The size needs to be change */
if(encoded_data && encoded_data != buffer) {
length = XP_STRLEN(encoded_data);
}
}
if (m_encoder_data) {
status = MimeEncoderWrite(m_encoder_data, encoded_data, length);
} else {
// Merely translate all linebreaks to CRLF.
int status = 0;
const char *in = encoded_data;
const char *end = in + length;
char *buffer, *out;
buffer = mime_get_stream_write_buffer();
if (!buffer) return MK_OUT_OF_MEMORY;
XP_ASSERT(encoded_data != buffer);
out = buffer;
for (; in < end; in++) {
if (m_just_hit_CR) {
m_just_hit_CR = FALSE;
if (*in == LF) {
// The last thing we wrote was a CRLF from hitting a CR.
// So, we don't want to do anything from a following LF;
// we want to ignore it.
continue;
}
}
if (*in == CR || *in == LF) {
/* Write out the newline. */
*out++ = CR;
*out++ = LF;
status = mime_write_message_body(m_state, buffer,
out - buffer);
if (status < 0) return status;
out = buffer;
if (*in == CR) {
m_just_hit_CR = TRUE;
}
out = buffer;
} else {
/* Fix for bug #95985. We can't assume that all lines are shorter
than 4096 chars (MIME_BUFFER_SIZE), so we need to test
for this here. sfraser.
*/
if (out - buffer >= MIME_BUFFER_SIZE)
{
status = mime_write_message_body(m_state, buffer, out - buffer);
if (status < 0) return status;
out = buffer;
}
*out++ = *in;
}
}
/* Flush the last line. */
if (out > buffer) {
status = mime_write_message_body(m_state, buffer, out - buffer);
if (status < 0) return status;
out = buffer;
}
}
if (encoded_data && encoded_data != buffer) {
XP_FREE(encoded_data);
}
return status;
#else
return 0;
#endif //MOZ_ENDER_MIME
}
/* Partition the headers into those which apply to the message as a whole;
those which apply to the message's contents; and the Content-Type header
itself. (This relies on the fact that all body-related headers begin with
"Content-".)
(How many header parsers are in this program now?)
*/
static int divide_content_headers(const char *headers,
char **message_headers,
char **content_headers,
char **content_type_header)
{
const char *tail;
char *message_tail, *content_tail, *type_tail;
int L = 0;
if (headers)
L = XP_STRLEN(headers);
if (L == 0)
return 0;
*message_headers = (char *)XP_ALLOC(L+1);
if (!*message_headers)
return MK_OUT_OF_MEMORY;
*content_headers = (char *)XP_ALLOC(L+1);
if (!*content_headers)
{
XP_FREE(*message_headers);
return MK_OUT_OF_MEMORY;
}
*content_type_header = (char *)XP_ALLOC(L+1);
if (!*content_type_header)
{
XP_FREE(*message_headers);
XP_FREE(*content_headers);
return MK_OUT_OF_MEMORY;
}
message_tail = *message_headers;
content_tail = *content_headers;
type_tail = *content_type_header;
tail = headers;
while (*tail)
{
const char *head = tail;
char **out;
while(1)
{
/* Loop until we reach a newline that is not followed by whitespace.
*/
if (tail[0] == 0 ||
((tail[0] == CR || tail[0] == LF) &&
!(tail[1] == ' ' || tail[1] == '\t' || tail[1] == LF)))
{
/* Swallow the whole newline. */
if (tail[0] == CR && tail[1] == LF)
tail++;
if (*tail)
tail++;
break;
}
tail++;
}
/* Decide which block this header goes into.
*/
if (!strncasecomp(head, "Content-Type:", 13))
out = &type_tail;
else if (!strncasecomp(head, "Content-", 8))
out = &content_tail;
else
out = &message_tail;
XP_MEMCPY(*out, head, (tail-head));
*out += (tail-head);
}
*message_tail = 0;
*content_tail = 0;
*type_tail = 0;
if (!**message_headers)
{
XP_FREE(*message_headers);
*message_headers = 0;
}
if (!**content_headers)
{
XP_FREE(*content_headers);
*content_headers = 0;
}
if (!**content_type_header)
{
XP_FREE(*content_type_header);
*content_type_header = 0;
}
#ifdef DEBUG
// ### mwelch Because of the extreme difficulty we've had with
// duplicate part headers, I'm going to put in an
// ASSERT here which makes sure that no duplicate
// Content-Type or Content-Transfer-Encoding headers
// leave here undetected.
const char* tmp;
if (*content_type_header)
{
tmp = XP_STRSTR(*content_type_header, "Content-Type");
if (tmp)
{
tmp++; // get past the first occurrence
XP_ASSERT(!XP_STRSTR(tmp, "Content-Type"));
}
}
if (*content_headers)
{
tmp = XP_STRSTR(*content_headers, "Content-Transfer-Encoding");
if (tmp)
{
tmp++; // get past the first occurrence
XP_ASSERT(!XP_STRSTR(tmp, "Content-Transfer-Encoding"));
}
}
#endif // DEBUG
return 0;
}
extern "C" {
extern char *mime_make_separator(const char *prefix);
}
#ifdef MOZ_ENDER_MIME
//moved this here
extern "C" char *
mime_make_separator(const char *prefix)
{
unsigned char rand_buf[13];
RNG_GenerateGlobalRandomBytes((void *) rand_buf, 12);
return PR_smprintf("------------%s"
"%02X%02X%02X%02X"
"%02X%02X%02X%02X"
"%02X%02X%02X%02X",
prefix,
rand_buf[0], rand_buf[1], rand_buf[2], rand_buf[3],
rand_buf[4], rand_buf[5], rand_buf[6], rand_buf[7],
rand_buf[8], rand_buf[9], rand_buf[10], rand_buf[11]);
}
#endif
int MSG_SendPart::Write()
{
#ifndef MOZ_ENDER_MIME
int status = 0;
char *separator = 0;
XP_File file = NULL;
#define PUSHLEN(str, length) \
do { \
status = mime_write_message_body(m_state, str, length); \
if (status < 0) goto FAIL; \
} while (0) \
#define PUSH(str) PUSHLEN(str, XP_STRLEN(str))
if (m_mainpart && m_type && XP_STRCMP(m_type, TEXT_HTML) == 0) {
if (m_filename) {
// The "insert HTML links" code requires a memory buffer,
// so read the file into memory.
XP_ASSERT(m_buffer == NULL);
XP_StatStruct st;
st.st_size = 0;
XP_Stat (m_filename, &st, m_filetype);
int32 length = st.st_size;
m_buffer = new char[length + 1];
if (m_buffer) {
file = XP_FileOpen(m_filename, m_filetype, XP_FILE_READ_BIN);
if (file) {
XP_FileRead(m_buffer, length, file);
XP_FileClose(file);
m_buffer[length] = '\0';
file = NULL;
if (m_filename) delete [] m_filename;
m_filename = NULL;
} else {
delete [] m_buffer;
m_buffer = NULL;
}
}
}
if (m_buffer) {
char* tmp = NET_ScanHTMLForURLs(m_buffer);
if (tmp) {
SetBuffer(tmp);
XP_FREE(tmp);
}
}
}
if (m_parent && m_parent->m_type &&
!strcasecomp(m_parent->m_type, MULTIPART_DIGEST) &&
m_type &&
(!strcasecomp(m_type, MESSAGE_RFC822) ||
!strcasecomp(m_type, MESSAGE_NEWS))) {
/* If we're in a multipart/digest, and this document is of type
message/rfc822, then it's appropriate to emit no
headers.
*/
} else {
char *message_headers = 0;
char *content_headers = 0;
char *content_type_header = 0;
status = divide_content_headers(m_other,
&message_headers,
&content_headers,
&content_type_header);
if (status < 0)
goto FAIL;
/* First, write out all of the headers that refer to the message
itself (From, Subject, MIME-Version, etc.)
*/
if (message_headers)
{
PUSH(message_headers);
XP_FREE(message_headers);
message_headers = 0;
}
/* Now make sure there's a Content-Type header.
*/
if (!content_type_header)
{
XP_ASSERT(m_type && *m_type);
XP_Bool needsCharset = mime_type_needs_charset(m_type ? m_type : TEXT_PLAIN);
if (needsCharset)
{
char tmpCSName[64];
tmpCSName[0] = '\0';
INTL_CharSetIDToName(m_csid, tmpCSName);
content_type_header =
PR_smprintf("Content-Type: %s; charset=%s" CRLF,
(m_type ? m_type : TEXT_PLAIN), tmpCSName);
}
else
content_type_header =
PR_smprintf("Content-Type: %s" CRLF,
(m_type ? m_type : TEXT_PLAIN));
if (!content_type_header)
{
if (content_headers)
XP_FREE(content_headers);
status = MK_OUT_OF_MEMORY;
goto FAIL;
}
}
/* If this is a compound object, tack a boundary string onto the
Content-Type header.
*/
if (m_numchildren > 0)
{
int L;
char *ct2;
XP_ASSERT(m_type);
if (!separator)
{
separator = mime_make_separator("");
if (!separator)
{
status = MK_OUT_OF_MEMORY;
goto FAIL;
}
}
L = XP_STRLEN(content_type_header);
if (content_type_header[L-1] == LF)
content_type_header[--L] = 0;
if (content_type_header[L-1] == CR)
content_type_header[--L] = 0;
ct2 = PR_smprintf("%s;\r\n boundary=\"%s\"" CRLF,
content_type_header, separator);
XP_FREE(content_type_header);
if (!ct2)
{
if (content_headers)
XP_FREE(content_headers);
status = MK_OUT_OF_MEMORY;
goto FAIL;
}
content_type_header = ct2;
}
/* Now write out the Content-Type header...
*/
XP_ASSERT(content_type_header && *content_type_header);
PUSH(content_type_header);
XP_FREE(content_type_header);
content_type_header = 0;
/* ...followed by all of the other headers that refer to the body of
the message (Content-Transfer-Encoding, Content-Dispositon, etc.)
*/
if (content_headers)
{
PUSH(content_headers);
XP_FREE(content_headers);
content_headers = 0;
}
}
PUSH(CRLF); // A blank line, to mark the end of headers.
m_firstBlock = TRUE;
/* only convert if we need to tag charset */
m_needIntlConversion = mime_type_needs_charset(m_type);
m_intlDocToMailConverter = NULL;
if (m_buffer) {
XP_ASSERT(!m_filename);
status = PushBody(m_buffer, XP_STRLEN(m_buffer));
if (status < 0) goto FAIL;
} else if (m_filename) {
file = XP_FileOpen(m_filename, m_filetype, XP_FILE_READ_BIN);
if (!file) {
status = -1; // ### Better error code for a temp file
// mysteriously disappearing?
goto FAIL;
}
/* Hack to avoid having to allocate memory... */
if (!mime_mailto_stream_read_buffer) {
mime_mailto_stream_read_buffer = (char *)
XP_ALLOC(MIME_BUFFER_SIZE);
if (!mime_mailto_stream_read_buffer)
{
status = MK_OUT_OF_MEMORY;
goto FAIL;
}
}
char* buffer = mime_mailto_stream_read_buffer;
if (m_strip_sensitive_headers) {
/* We are attaching a message, so we should be careful to
strip out certain sensitive internal header fields.
*/
XP_Bool skipping = FALSE;
XP_ASSERT(MIME_BUFFER_SIZE > 1000); /* SMTP (RFC821) limit */
while (1) {
char *line = XP_FileReadLine(buffer, MIME_BUFFER_SIZE-3, file);
if (!line) break; /* EOF */
if (skipping) {
if (*line == ' ' || *line == '\t') {
continue;
} else {
skipping = FALSE;
}
}
int hdrLen = XP_STRLEN(buffer);
if ((hdrLen < 2) || (buffer[hdrLen-2] != CR)) // if the line doesn't end with CRLF,
{
// ... make it end with CRLF.
if ( (hdrLen == 0)
|| ((buffer[hdrLen-1] != CR) && (buffer[hdrLen-1] != LF)) )
hdrLen++;
buffer[hdrLen-1] = '\015';
buffer[hdrLen] = '\012';
buffer[hdrLen+1] = '\0';
}
if (!strncasecomp(line, "BCC:", 4) ||
!strncasecomp(line, "FCC:", 4) ||
!strncasecomp(line, CONTENT_LENGTH ":",
CONTENT_LENGTH_LEN + 1) ||
!strncasecomp(line, "Lines:", 6) ||
!strncasecomp(line, "Status:", 7) ||
!strncasecomp(line, X_MOZILLA_STATUS ":",
X_MOZILLA_STATUS_LEN+1) ||
!strncasecomp(line, X_MOZILLA_NEWSHOST ":",
X_MOZILLA_NEWSHOST_LEN+1) ||
!strncasecomp(line, X_UIDL ":", X_UIDL_LEN+1) ||
!strncasecomp(line, "X-VM-", 5))
{
skipping = TRUE;
continue;
}
PUSH(line);
if (*line == CR || *line == LF) {
break; // Now can do normal reads for the body.
}
}
}
while (1) {
status = XP_FileRead(buffer, MIME_BUFFER_SIZE, file);
if (status < 0) {
goto FAIL;
} else if (status == 0) {
break;
}
status = PushBody(buffer, status);
if (status < 0) goto FAIL;
}
}
if (m_encoder_data) {
status = MimeEncoderDestroy(m_encoder_data, FALSE);
m_encoder_data = NULL;
if (status < 0) goto FAIL;
}
if (m_numchildren > 0) {
XP_ASSERT(separator);
for (int i=0 ; i<m_numchildren ; i++) {
PUSH(CRLF);
PUSH("--");
PUSH(separator);
PUSH(CRLF);
status = m_children[i]->Write();
if (status < 0) goto FAIL;
}
PUSH(CRLF);
PUSH("--");
PUSH(separator);
PUSH("--");
PUSH(CRLF);
}
FAIL:
FREEIF(separator);
if (file) XP_FileClose(file);
if (m_intlDocToMailConverter) {
INTL_DestroyCharCodeConverter(m_intlDocToMailConverter);
m_intlDocToMailConverter = NULL;
}
return status;
#else
return 0;
#endif //MOZ_ENDER_MIME
}